This example shows how to solve reaction-diffusion equations with different time step methods.
1 #include "../src/cpp_macros.h"
6 program reaction_diffusion
11 integer,
parameter :: box_size = 8
12 integer :: nx_min = 100
15 integer :: i_phi1, i_phi2
16 integer :: i_rhs1, i_rhs2
19 real(dp) :: domain_len = 1.0_dp
21 type(mg_t) :: mg1, mg2
22 type(ref_info_t) :: refine_info
23 integer :: it, output_cnt
26 real(dp) :: end_time = 2.0_dp
27 real(dp) :: dt = 1e-3_dp
28 real(dp) :: dt_output = 1e-2_dp
29 real(dp) :: u_rng_ampl = 0.0_dp
30 real(dp) :: v_rng_ampl = 0.0_dp
31 logical :: periodic(3) = .false.
32 character(len=100) :: fname
33 logical :: file_exists
35 character(len=20) :: time_integrator =
"imex"
39 character(len=20) :: equation_type =
"schnakenberg"
44 real(dp) :: alpha = 0.1305_dp
45 real(dp) :: beta = 0.7695_dp
46 real(dp) :: D1 = 0.05_dp
47 real(dp) :: D2 = 1.0_dp
48 real(dp) :: kappa = 100.0_dp
51 real(dp) :: gs_F = 0.046d0
52 real(dp) :: gs_k = 0.063d0
54 namelist /settings/ alpha, beta, d1, d2, kappa, end_time, dt, &
55 dt_output, nx_min, time_integrator, periodic, u_rng_ampl, &
56 v_rng_ampl, equation_type, domain_len, gs_f, gs_k
58 print *,
"Running reaction_diffusion_" // dimname //
""
59 print *,
"Number of threads", af_get_max_threads()
61 inquire(file=
"reaction_diffusion.txt", exist=file_exists)
63 open(newunit=my_unit, file=
"reaction_diffusion.txt", status=
"old")
64 read(my_unit, settings)
67 print *,
"No input file reaction_diffusion.txt found; default settings"
70 call af_add_cc_variable(tree,
"u", ix=i_u, n_copies=2)
71 call af_add_cc_variable(tree,
"v", ix=i_v, n_copies=2)
72 call af_add_cc_variable(tree,
"phi1", ix=i_phi1)
73 call af_add_cc_variable(tree,
"phi2", ix=i_phi2)
74 call af_add_cc_variable(tree,
"rhs1", ix=i_rhs1)
75 call af_add_cc_variable(tree,
"rhs2", ix=i_rhs2)
76 call af_add_cc_variable(tree,
"tmp", ix=i_tmp)
78 call af_set_cc_methods(tree, i_u, af_bc_neumann_zero)
79 call af_set_cc_methods(tree, i_v, af_bc_neumann_zero)
84 [dtimes(domain_len)], &
88 call af_print_info(tree)
92 call af_loop_box(tree, set_initial_condition)
93 call af_gc_tree(tree, [i_u, i_v])
94 call af_adjust_refinement(tree, &
97 if (refine_info%n_add == 0)
exit
104 mg1%sides_bc => af_bc_neumann_zero
109 mg2%sides_bc => af_bc_neumann_zero
116 select case (time_integrator)
120 if (dt > af_min_dr(tree)**2/(2*ndim*max(d1, d2)))
then
121 error stop
"dt is too large for explicit integration"
126 mg1%helmholtz_lambda = 1/(0.5_dp * dt * d1)
127 mg2%helmholtz_lambda = 1/(0.5_dp * dt * d2)
129 call mg_init(tree, mg1)
130 call mg_init(tree, mg2)
136 if (output_cnt * dt_output <= time)
then
137 output_cnt = output_cnt + 1
138 write(fname,
"(A,I0)")
"output/reaction_diffusion_" &
139 // dimname //
"_", output_cnt
140 call af_write_silo(tree, trim(fname), output_cnt, time)
143 select case (time_integrator)
146 case (
"forward_euler")
147 call af_loop_box(tree, forward_euler)
148 case (
"midpoint_method")
149 call af_loop_box(tree, midpoint_method_step1)
150 call af_gc_tree(tree, [i_u+1, i_v+1])
151 call af_loop_box(tree, midpoint_method_step2)
154 call af_gc_tree(tree, [i_u, i_v])
156 if (time > end_time)
exit
162 subroutine forward_euler(box)
163 type(box_t),
intent(inout) :: box
164 call step_f(box, dt, [i_u, i_v], [i_u, i_v], [i_u, i_v])
165 end subroutine forward_euler
167 subroutine midpoint_method_step1(box)
168 type(box_t),
intent(inout) :: box
169 call step_f(box, 0.5_dp * dt, [i_u, i_v], [i_u, i_v], [i_u+1, i_v+1])
170 end subroutine midpoint_method_step1
172 subroutine midpoint_method_step2(box)
173 type(box_t),
intent(inout) :: box
174 call step_f(box, dt, [i_u+1, i_v+1], [i_u, i_v], [i_u, i_v])
175 end subroutine midpoint_method_step2
180 subroutine rd_imex(tree)
181 type(af_t),
intent(inout) :: tree
183 real(dp) :: max_res, max_rhs
184 real(dp),
parameter :: max_rel_residual = 1e-6_dp
186 call af_loop_box(tree, rd_imex_step1)
189 call af_tree_maxabs_cc(tree, mg1%i_rhs, max_rhs)
190 call mg_fas_fmg(tree, mg1, .true., .true.)
192 call af_tree_maxabs_cc(tree, mg1%i_tmp, max_res)
193 if (max_res/max_rhs < max_rel_residual)
exit
197 call af_tree_maxabs_cc(tree, mg2%i_rhs, max_rhs)
198 call mg_fas_fmg(tree, mg2, .true., .true.)
200 call af_tree_maxabs_cc(tree, mg2%i_tmp, max_res)
201 if (max_res/max_rhs < max_rel_residual)
exit
204 call af_loop_box(tree, rd_imex_step2)
205 end subroutine rd_imex
207 subroutine rd_imex_step1(box)
208 type(box_t),
intent(inout) :: box
214 call step_f0(box, dt, [i_u, i_v], [i_u, i_v], [mg1%i_rhs, mg2%i_rhs])
215 call step_f1(box, 0.5_dp * dt, [i_u, i_v], [mg1%i_rhs, mg2%i_rhs], &
216 [mg1%i_rhs, mg2%i_rhs])
218 box%cc(dtimes(1:nc), mg1%i_rhs) = -box%cc(dtimes(1:nc), mg1%i_rhs) * &
220 box%cc(dtimes(1:nc), mg2%i_rhs) = -box%cc(dtimes(1:nc), mg2%i_rhs) * &
223 end subroutine rd_imex_step1
225 subroutine rd_imex_step2(box)
226 type(box_t),
intent(inout) :: box
228 call step_f(box, 0.5_dp * dt, [i_u, i_v], [i_u, i_v], [i_u, i_v])
229 call step_f(box, 0.5_dp * dt, [mg1%i_phi, mg2%i_phi], &
230 [i_u, i_v], [i_u, i_v])
231 end subroutine rd_imex_step2
234 subroutine step_f0(box, dt, i_deriv, i_prev, i_out)
235 type(box_t),
intent(inout) :: box
236 real(dp),
intent(in) :: dt
237 integer,
intent(in) :: i_deriv(2)
238 integer,
intent(in) :: i_prev(2)
239 integer,
intent(in) :: i_out(2)
240 real(dp) :: tmp(DTIMES(box%n_cell), 2)
246 select case (equation_type)
248 tmp(dtimes(:), 1) = -box%cc(dtimes(1:nc), i_deriv(1)) * &
249 box%cc(dtimes(1:nc), i_deriv(2))**2 + &
250 gs_f * (1 - box%cc(dtimes(1:nc), i_deriv(1)))
251 tmp(dtimes(:), 2) = box%cc(dtimes(1:nc), i_deriv(1)) * &
252 box%cc(dtimes(1:nc), i_deriv(2))**2 - &
253 (gs_f + gs_k) * box%cc(dtimes(1:nc), i_deriv(2))
254 case (
"schnakenberg")
255 tmp(dtimes(:), 1) = kappa * (alpha - box%cc(dtimes(1:nc), i_deriv(1)) + &
256 box%cc(dtimes(1:nc), i_deriv(1))**2 * box%cc(dtimes(1:nc), i_deriv(2)))
257 tmp(dtimes(:), 2) = kappa * (beta - &
258 box%cc(dtimes(1:nc), i_deriv(1))**2 * box%cc(dtimes(1:nc), i_deriv(2)))
260 error stop
"Invalid equation type"
263 box%cc(dtimes(1:nc), i_out) = &
264 box%cc(dtimes(1:nc), i_prev) + dt * tmp
265 end subroutine step_f0
268 subroutine step_f1(box, dt, i_deriv, i_prev, i_out)
269 type(box_t),
intent(inout) :: box
270 real(dp),
intent(in) :: dt
271 integer,
intent(in) :: i_deriv(2)
272 integer,
intent(in) :: i_prev(2)
273 integer,
intent(in) :: i_out(2)
274 real(dp) :: tmp(DTIMES(box%n_cell), 2)
278 call laplacian(box, i_deriv(1), tmp(dtimes(:), 1))
279 call laplacian(box, i_deriv(2), tmp(dtimes(:), 2))
280 tmp(dtimes(:), 1) = tmp(dtimes(:), 1) * d1
281 tmp(dtimes(:), 2) = tmp(dtimes(:), 2) * d2
283 box%cc(dtimes(1:nc), i_out) = &
284 box%cc(dtimes(1:nc), i_prev) + dt * tmp
285 end subroutine step_f1
287 subroutine step_f(box, dt, i_deriv, i_prev, i_out)
288 type(box_t),
intent(inout) :: box
289 real(dp),
intent(in) :: dt
290 integer,
intent(in) :: i_deriv(2)
291 integer,
intent(in) :: i_prev(2)
292 integer,
intent(in) :: i_out(2)
294 real(dp) :: tmp(DTIMES(box%n_cell), 2)
298 call laplacian(box, i_deriv(1), tmp(dtimes(:), 1))
299 call laplacian(box, i_deriv(2), tmp(dtimes(:), 2))
300 tmp(dtimes(:), 1) = tmp(dtimes(:), 1) * d1
301 tmp(dtimes(:), 2) = tmp(dtimes(:), 2) * d2
304 select case (equation_type)
306 tmp(dtimes(:), 1) = tmp(dtimes(:), 1) - &
307 box%cc(dtimes(1:nc), i_deriv(1)) * &
308 box%cc(dtimes(1:nc), i_deriv(2))**2 + &
309 gs_f * (1 - box%cc(dtimes(1:nc), i_deriv(1)))
310 tmp(dtimes(:), 2) = tmp(dtimes(:), 2) + &
311 box%cc(dtimes(1:nc), i_deriv(1)) * &
312 box%cc(dtimes(1:nc), i_deriv(2))**2 - &
313 (gs_f + gs_k) * box%cc(dtimes(1:nc), i_deriv(2))
314 case (
"schnakenberg")
315 tmp(dtimes(:), 1) = tmp(dtimes(:), 1) + &
316 kappa * (alpha - box%cc(dtimes(1:nc), i_deriv(1)) + &
317 box%cc(dtimes(1:nc), i_deriv(1))**2 * box%cc(dtimes(1:nc), i_deriv(2)))
318 tmp(dtimes(:), 2) = tmp(dtimes(:), 2) + &
320 box%cc(dtimes(1:nc), i_deriv(1))**2 * box%cc(dtimes(1:nc), i_deriv(2)))
322 error stop
"Invalid equation type"
325 box%cc(dtimes(1:nc), i_out) = box%cc(dtimes(1:nc), i_prev) + dt * tmp
326 end subroutine step_f
329 subroutine refine_routine(box, cell_flags)
330 type(box_t),
intent(in) :: box
331 integer,
intent(out) :: cell_flags(DTIMES(box%n_cell))
333 if (maxval(box%dr) > domain_len/nx_min)
then
334 cell_flags = af_do_ref
336 cell_flags = af_keep_ref
338 end subroutine refine_routine
341 subroutine set_initial_condition(box)
342 type(box_t),
intent(inout) :: box
344 real(dp) :: rr(NDIM), r0(NDIM), u, v
348 select case (equation_type)
351 rr = af_r_cc(box, [ijk])
353 if (all(abs(rr - 0.5_dp * domain_len) < 10.0_dp/256))
then
354 call random_number(u)
355 call random_number(v)
356 box%cc(ijk, i_u) = 0.5_dp + u_rng_ampl * (u - 0.5_dp)
357 box%cc(ijk, i_v) = 0.25_dp + v_rng_ampl * (v - 0.5_dp)
359 box%cc(ijk, i_u) = 1.0_dp
360 box%cc(ijk, i_v) = 0.0_dp
363 case (
"schnakenberg")
368 rr = af_r_cc(box, [ijk])
369 call random_number(u)
370 call random_number(v)
371 box%cc(ijk, i_u) = alpha + beta + 1e-3_dp * &
372 exp(-100_dp * sum((rr - r0)**2)) + u_rng_ampl * (u - 0.5_dp)
373 box%cc(ijk, i_v) = beta / (alpha + beta)**2 + v_rng_ampl * (v - 0.5_dp)
376 error stop
"Unknown equation type"
378 end subroutine set_initial_condition
381 subroutine laplacian(box, i_in, lpl)
382 type(box_t),
intent(inout) :: box
383 integer,
intent(in) :: i_in
384 real(dp),
intent(out) :: lpl(DTIMES(1:box%n_cell))
386 real(dp) :: idr2(NDIM)
391 associate(cc => box%cc, n => i_in)
394 lpl(i) = idr2(1) * (cc(i-1, n) + cc(i+1, n) - 2 * cc(i, n))
397 idr2(1) * (cc(i-1, j, n) + cc(i+1, j, n) - 2 * cc(i, j, n)) + &
398 idr2(2) * (cc(i, j-1, n) + cc(i, j+1, n) - 2 * cc(i, j, n))
401 idr2(1) * (cc(i-1, j, k, n) + cc(i+1, j, k, n) &
402 - 2 * cc(i, j, k, n)) &
403 + idr2(2) * (cc(i, j-1, k, n) + cc(i, j+1, k, n) &
404 - 2 * cc(i, j, k, n)) &
405 + idr2(3) * (cc(i, j, k-1, n) + cc(i, j, k+1, n) &
406 - 2 * cc(i, j, k, n))
410 end subroutine laplacian
412 end program reaction_diffusion
Module which contains all Afivo modules, so that a user does not have to include them separately.