Skip to content

Commit d0af211

Browse files
larponBruno-Vdr
andauthored
examples: add an Lattice Boltzmann Method simulation (#888) (#891)
Co-authored-by: Bruno-Vdr <[email protected]>
1 parent 49a756d commit d0af211

11 files changed

+869
-0
lines changed

examples/lbm/cell.v

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
module main
2+
3+
import math
4+
import rand
5+
6+
// This file contains Code and data structures for a Lattice-Boltzmann-Method (LBM)
7+
// fluid flow simulation. The simulation is 2 Dimension, with 9 possible directions (D2Q9)
8+
//
9+
// 8 1 2
10+
//
11+
// 7 0 3
12+
//
13+
// 6 5 4
14+
15+
// Vi is an enum for direction vector of D2Q9 Lattice.
16+
pub enum Vi {
17+
center = 0
18+
north = 1
19+
north_east = 2
20+
//
21+
east = 3
22+
south_east = 4
23+
south = 5
24+
//
25+
south_west = 6
26+
west = 7
27+
north_west = 8
28+
}
29+
30+
// vfmt off
31+
const opp = [
32+
Vi.center, Vi.south, Vi.south_west,
33+
Vi.west, Vi.north_west, Vi.north,
34+
Vi.north_east, Vi.east, Vi.south_east,
35+
]!
36+
37+
// Array defined here in order to loop over a Vi enum.
38+
// Warning: This array must be coherent with Vi enum order !
39+
const vi = [
40+
Vi.center, Vi.north, Vi.north_east,
41+
Vi.east, Vi.south_east, Vi.south,
42+
Vi.south_west, Vi.west, Vi.north_west,
43+
]!
44+
45+
// wi is the weight of mini-cells.
46+
// Warning: This array must be coherent with Vi enum order !
47+
const wi = [
48+
f64(16.0 / 36.0), 4.0 / 36.0, 1.0 / 36.0,
49+
4.0 / 36.0, 1.0 / 36.0, 4.0 / 36.0,
50+
1.0 / 36.0, 4.0 / 36.0, 1.0 / 36.0,
51+
]!
52+
// vfmt on
53+
54+
// opposite returns vector of opposite direction. Yes Enum can have methods in V.
55+
// Warning: This array must be coherent with Vi enum order !
56+
fn (v Vi) opposite() Vi {
57+
return opp[int(v)]
58+
}
59+
60+
// Discrete velocity vectors, by component, with respect to SDL display orientation (North is negative on y axis)
61+
// f64 and int are provided to avoid unnecessary casts.
62+
// Warning: These vectors must be coherent with Vi enum order !
63+
const dvx_f = [0.0, 0.0, 1.0, 1.0, 1.0, 0.0, -1.0, -1.0, -1.0]!
64+
const dvy_f = [0.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, -1.0]!
65+
const dvx_i = [0, 0, 1, 1, 1, 0, -1, -1, -1]!
66+
const dvy_i = [0, -1, -1, 0, 1, 1, 1, 0, -1]!
67+
68+
struct Cell {
69+
mut:
70+
obstacle bool
71+
sp [9]f64
72+
}
73+
74+
// Cell.new built, and initializes a default Cell.
75+
// Warning: sp values must be coherent with Vi enum order !
76+
fn Cell.new(o bool) Cell {
77+
return Cell{
78+
obstacle: o
79+
sp: [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]!
80+
} // ! means fixed size array ! Will change a day !
81+
}
82+
83+
// Return a Cell with all mini-cell set to Zero
84+
fn Cell.zero(o bool) Cell {
85+
return Cell{
86+
obstacle: o
87+
sp: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]!
88+
}
89+
}
90+
91+
// normalize perform a normalisation on the cell so that average desnsity is rho0
92+
fn (mut c Cell) normalize() {
93+
rho := c.rho()
94+
for mut v in c.sp {
95+
v *= (rho0 / rho)
96+
}
97+
}
98+
99+
// randomize add some randomness on the cell.
100+
fn (mut c Cell) randomize(i f64) {
101+
for v in vi {
102+
r := rand.f64() * i
103+
c.sum(v, r)
104+
}
105+
}
106+
107+
// get returns mini-cell depending on the given speed vector.
108+
fn (c &Cell) get(v Vi) f64 {
109+
return c.sp[int(v)]
110+
}
111+
112+
// set forces a mini-cell value given its speed vector.
113+
fn (mut c Cell) set(v Vi, value f64) {
114+
c.sp[int(v)] = value
115+
}
116+
117+
// increase (add value) to a mini-cell value given its speed vector.
118+
fn (mut c Cell) sum(v Vi, value f64) {
119+
c.sp[int(v)] += value
120+
}
121+
122+
// rho computes whole cell's density
123+
fn (c &Cell) rho() f64 {
124+
mut sum := 0.0
125+
for v in c.sp {
126+
sum += v
127+
}
128+
assert math.is_nan(sum) == false
129+
return sum
130+
}
131+
132+
// ux computes x (horizontal) component of cell speed vector.
133+
fn (c &Cell) ux() f64 {
134+
rho := c.rho()
135+
r := 1.0 / rho * (c.sp[Vi.north_east] + c.sp[Vi.east] + c.sp[Vi.south_east] - c.sp[Vi.south_west] - c.sp[Vi.west] - c.sp[Vi.north_west])
136+
assert math.is_nan(r) == false
137+
return r
138+
}
139+
140+
// uy computes y (vertical) component of cell speed vector.
141+
fn (c &Cell) uy() f64 {
142+
rho := c.rho()
143+
r := 1.0 / rho * (-c.sp[Vi.north] - c.sp[Vi.north_east] - c.sp[Vi.north_west] +
144+
c.sp[Vi.south_east] + c.sp[Vi.south] + c.sp[Vi.south_west])
145+
assert math.is_nan(r) == false
146+
return r
147+
}
148+
149+
// ux_no_rho computes x (horizontal) component of cell speed vector, when rho is already known and passed as param.
150+
fn (c &Cell) ux_no_rho(rho f64) f64 {
151+
r := 1.0 / rho * (c.sp[Vi.north_east] + c.sp[Vi.east] + c.sp[Vi.south_east] - c.sp[Vi.south_west] - c.sp[Vi.west] - c.sp[Vi.north_west])
152+
return r
153+
}
154+
155+
// uy_no_rho computes y (vertical) component of cell speed vector, when rho is already known and passed as param.
156+
fn (c &Cell) uy_no_rho(rho f64) f64 {
157+
r := 1.0 / rho * (-c.sp[Vi.north] - c.sp[Vi.north_east] - c.sp[Vi.north_west] +
158+
c.sp[Vi.south_east] + c.sp[Vi.south] + c.sp[Vi.south_west])
159+
return r
160+
}
161+
162+
// equ computes result of equilibrium function.
163+
fn (c &Cell) equ(i Vi) f64 {
164+
rho := c.rho()
165+
ux := c.ux_no_rho(rho)
166+
uy := c.uy_no_rho(rho)
167+
168+
t1 := 3.0 * (ux * dvx_f[i] + uy * dvy_f[i])
169+
mut t2 := (ux * dvx_f[i] + uy * dvy_f[i])
170+
171+
t2 *= t2 // t2^2
172+
t2 *= (9.0 / 2.0)
173+
t3 := (3.0 / 2.0) * (ux * ux + uy * uy)
174+
r := wi[i] * rho * (1.0 + t1 + t2 - t3)
175+
return r
176+
}

examples/lbm/colormap.v

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
module main
2+
3+
import arrays
4+
5+
struct Colormap {}
6+
7+
// build a linear color map ARGB8888 from color c1 to c2, of steps colors
8+
pub fn Colormap.build(c1 u32, c2 u32, steps int) []u32 {
9+
assert steps > 1, 'Error, generating colormap needs at least two steps.'
10+
mut cm := []u32{cap: steps}
11+
12+
// split c1 & c2 to RGB channels.
13+
mut c1_r := f64((c1 & 0xFF0000) >> 16)
14+
mut c1_g := f64((c1 & 0xFF00) >> 8)
15+
mut c1_b := f64((c1 & 0xFF))
16+
17+
c2_r := f64((c2 & 0xFF0000) >> 16)
18+
c2_g := f64((c2 & 0xFF00) >> 8)
19+
c2_b := f64((c2 & 0xFF))
20+
21+
delta_r := (c2_r - c1_r) / f64(steps - 1)
22+
delta_g := (c2_g - c1_g) / f64(steps - 1)
23+
delta_b := (c2_b - c1_b) / f64(steps - 1)
24+
25+
cm << (0xFF000000 | c1) // Emit exact start color.
26+
for _ in 1 .. steps - 1 {
27+
c1_r += delta_r
28+
c1_g += delta_g
29+
c1_b += delta_b
30+
31+
// Recompose 3 channels to ARGB8888 color.
32+
mut c := u32(0xFF_00_00_00)
33+
c |= u32(c1_r) << 16
34+
c |= u32(c1_g) << 8
35+
c |= u32(c1_b)
36+
cm << c
37+
}
38+
cm << (0xFF000000 | c2) // Emit exact end color.
39+
return cm
40+
}
41+
42+
// dual creates a color map with start color, intermediate color and final color, equaly sized.
43+
fn Colormap.dual(c1 u32, c2 u32, c3 u32, steps int) []u32 {
44+
assert steps > 2, 'Error, generating dual-colormap needs at least three steps.'
45+
return arrays.append(Colormap.build(c1, c2, steps / 2), Colormap.build(c2, c3, steps - (steps / 2)))
46+
}

0 commit comments

Comments
 (0)