forked from ltt1598/--Diffuse
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdiffuse_implicit.py
166 lines (138 loc) · 4.18 KB
/
diffuse_implicit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import taichi as ti
ti.init(arch = ti.cpu)
# control
paused = True
save_images = False
# problem setting
n = 64
scatter = 8
res = n * scatter
# physical parameters
# time-integration related
h = 1e-3 # time-step size
substep = 1 # number of substeps
dx = 1 # finite difference step size (in space)
# heat-source related
t_max = 300 # max temperature (in Celsius)
t_min = 0 # min temperature
heat_center = (n//2, n//2)
heat_radius = 5.1
k = 2000.0 # rate of heat diffusion
# visualization
pixels = ti.Vector.field(3, ti.f32, shape = (res, res))
# diffuse matrix
n2 = n**2
D_builder = ti.linalg.SparseMatrixBuilder(n2, n2, max_num_triplets=n2*5)
I_builder = ti.linalg.SparseMatrixBuilder(n2, n2, max_num_triplets=n2)
# temperature now and temperature next_time
t_n = ti.field(ti.f32, shape = n2) # unrolled to 1-d array
t_np1 = ti.field(ti.f32, shape = n2) # unrolled to 1-d array
ind = lambda i, j: i*n+j
@ti.kernel
def fillDiffusionMatrixBuilder(A: ti.linalg.sparse_matrix_builder()):
for i,j in ti.ndrange(n, n):
count = 0
if i-1 >= 0:
A[ind(i,j), ind(i-1,j)] += 1
count += 1
if i+1 < n:
A[ind(i,j), ind(i+1,j)] += 1
count += 1
if j-1 >= 0:
A[ind(i,j), ind(i,j-1)] += 1
count += 1
if j+1 < n:
A[ind(i,j), ind(i,j+1)] += 1
count += 1
A[ind(i,j), ind(i,j)] += -count
@ti.kernel
def fillEyeMatrixBuilder(A: ti.linalg.sparse_matrix_builder()):
for i,j in ti.ndrange(n, n):
A[ind(i,j), ind(i,j)] += 1
def buildMatrices():
fillDiffusionMatrixBuilder(D_builder)
fillEyeMatrixBuilder(I_builder)
D = D_builder.build()
I = I_builder.build()
return D, I
@ti.kernel
def init():
for i,j in ti.ndrange(n, n):
if (float(i)-heat_center[0])**2 + (float(j)-heat_center[1])**2 <= heat_radius**2:
t_n[ind(i, j)] = t_max # source
t_np1[ind(i, j)] = t_max # source
else:
t_n[ind(i, j)] = t_min
t_np1[ind(i, j)] = t_min
@ti.kernel
def update_source():
for i,j in ti.ndrange(n, n):
if (float(i)-heat_center[0])**2 + (float(j)-heat_center[1])**2 <= heat_radius**2:
t_np1[ind(i, j)] = t_max
def diffuse(dt: ti.f32):
c = dt * k / dx**2
ImcD = I - c*D
# linear solve: factorize
solver = ti.linalg.SparseSolver(solver_type="LLT")
solver.analyze_pattern(ImcD)
solver.factorize(ImcD)
# linear solve: solve
t_np1.from_numpy(solver.solve(t_n)) # t_np1 = t_n + c*D*t_np1
def update_source_and_commit():
update_source()
t_n.copy_from(t_np1)
@ti.func
def get_color(v, vmin, vmax):
c = ti.Vector([1.0, 1.0, 1.0]) # white
if v < vmin:
v = vmin
if v > vmax:
v = vmax
dv = vmax - vmin
if v < (vmin + 0.25 * dv):
c[0] = 0
c[1] = 4 * (v-vmin) / dv
elif v < (vmin + 0.5 * dv):
c[0] = 0
c[2] = 1 + 4 * (vmin + 0.25*dv -v) / dv
elif v < (vmin + 0.75*dv):
c[0] = 4 * (v - vmin -0.5 * dv) / dv
c[2] = 0
else:
c[1] = 1 + 4 * (vmin + 0.75 * dv - v) / dv
c[2] = 0
return c
@ti.kernel
def temperature_to_color(t: ti.template(), color: ti.template(), tmin: ti.f32, tmax: ti.f32):
for i,j in ti.ndrange(n, n):
for k,l in ti.ndrange(scatter, scatter):
color[i*scatter+k,j*scatter+l] = get_color(t[ind(i,j)], tmin, tmax)
# GUI
my_gui = ti.GUI("Diffuse", (res, res))
my_gui.show()
init()
D,I = buildMatrices()
i = 0
while my_gui.running:
for e in my_gui.get_events(ti.GUI.PRESS):
if e.key == ti.GUI.ESCAPE:
exit()
elif e.key == ti.GUI.SPACE:
paused = not paused
elif e.key == 'i':
save_images = not save_images
print(f"Exporting images to images\output_{i:05}.png")
elif e.key == 'r':
init()
i = 0
if not paused:
for sub in range(substep):
diffuse(h/substep)
update_source_and_commit()
temperature_to_color(t_np1, pixels, t_min, t_max)
my_gui.set_image(pixels)
if save_images and not paused:
my_gui.show(f"images\output_{i:05}.png")
i += 1
else:
my_gui.show()