Afivo  0.3
anisotropic_diffusion_2d.f90
1 
3 program anisotropic_diffusion_2d
4  use m_a2_all
5 
6  implicit none
7 
8  integer, parameter :: box_size = 8
9  integer, parameter :: i_phi = 1
10  integer, parameter :: i_phi_old = 2
11  integer, parameter :: i_bx = 3
12  integer, parameter :: i_by = 4
13  real(dp), parameter :: domain_len = 2 * acos(-1.0_dp)
14  real(dp), parameter :: dr = domain_len / box_size
15 
16  type(a2_t) :: tree
17  type(ref_info_t) :: refine_info
18  integer :: ix_list(2, 1)
19  integer :: nb_list(a2_num_neighbors, 1)
20  integer :: time_steps, output_cnt
21  integer :: i, id, n, n_steps
22  real(dp) :: dt, time, end_time
23  real(dp) :: dt_adapt, dt_output
24  real(dp) :: velocity(2), dr_min(2)
25  character(len=100) :: fname
26  integer :: count_rate, t_start, t_end
27 
28  print *, "Running anisotropic_diffusion_2d"
29  print *, "Number of threads", af_get_max_threads()
30 
31  ! Initialize tree
32  call a2_init(tree, & ! Tree to initialize
33  box_size, & ! A box contains box_size**DIM cells
34  n_var_cell=4, & ! Number of cell-centered variables
35  n_var_face=1, & ! Number of face-centered variables
36  dr=dr, & ! Distance between cells on base level
37  r_min=[-0.5_dp, -0.5_dp] * domain_len, &
38  cc_names=["phi", "old", "Bx ", "By "]) ! Variable names
39 
40  call a2_set_cc_methods(tree, i_phi, a2_bc_neumann_zero, a2_gc_interp_lim)
41 
42  ! Set up geometry
43  id = 1
44  ix_list(:, id) = 1 ! Set index of box
45  nb_list(:, id) = id ! Box is periodic, so its own neighbor
46 
47  ! Create the base mesh, using the box indices and their neighbor information
48  call a2_set_base(tree, 1, ix_list, nb_list)
49 
50  output_cnt = 0
51  time = 0
52  dt_adapt = 0.25_dp
53  dt_output = 0.25_dp
54  end_time = 10_dp
55  velocity(:) = 0.0_dp
56  velocity(1) = 1.0_dp
57  velocity(2) = -1.0_dp
58 
59  ! Set up the initial conditions
60  call system_clock(t_start,count_rate)
61 
62  do
63  call a2_loop_box(tree, set_initial_condition)
64  call a2_gc_tree(tree, i_phi, a2_gc_interp_lim, a2_bc_neumann_zero)
65  call a2_adjust_refinement(tree, & ! tree
66  refine_routine, & ! Refinement function
67  refine_info, & ! Information about refinement
68  1) ! Buffer width (in cells)
69  if (refine_info%n_add == 0) exit
70  end do
71 
72  call a2_gc_tree(tree, i_phi, a2_gc_interp_lim, a2_bc_neumann_zero)
73  call a2_print_info(tree)
74 
75  time_steps = 0
76 
77  ! Starting simulation
78  do
79  time_steps = time_steps + 1
80  dr_min = a2_min_dr(tree)
81  dt = 0.25_dp * minval(dr_min)**2
82 
83  n_steps = ceiling(dt_adapt/dt)
84  dt = dt_adapt / n_steps
85 
86  if (output_cnt * dt_output <= time) then
87  output_cnt = output_cnt + 1
88  write(fname, "(A,I0)") "anisotropic_diffusion_2d_", output_cnt
89  call a2_write_silo(tree, trim(fname), output_cnt, time, dir="output")
90  end if
91 
92  if (time > end_time) exit
93 
94  do n = 1, n_steps
95  ! Copy previous solution
96  call a2_tree_copy_cc(tree, i_phi, i_phi_old)
97 
98  ! Two forward Euler steps over dt
99  do i = 1, 2
100  ! Call procedure get_fluxes for each id in tree, giving the list of boxes
101  call a2_loop_boxes(tree, get_fluxes)
102 
103  ! Restrict fluxes from children to parents on refinement boundaries.
104  call a2_consistent_fluxes(tree, [i_phi])
105 
106  ! Call procedure update_solution (see below) for each box in tree, with argument dt
107  call a2_loop_box_arg(tree, update_solution, [dt])
108 
109  ! Restrict variables i_phi to all parent boxes
110  call a2_restrict_tree(tree, i_phi)
111  end do
112 
113  ! Take average of phi_old and phi
114  call a2_loop_box(tree, average_phi)
115  time = time + dt
116  end do
117 
118  ! Fill ghost cells for variable i_phi
119  call a2_gc_tree(tree, i_phi, a2_gc_interp_lim, a2_bc_neumann_zero)
120 
122  ! Adjust the refinement of a tree using refine_routine (see below) for grid
123  ! refinement. On input, the tree should be balanced. On output, the tree is
124  ! still balanced, and its refinement is updated (with at most one level per
125  ! call).
126  ! call a2_adjust_refinement(tree, refine_routine, refine_info, 1)
128 
129  ! Prolongation of i_phi values to new children (see below)
130  ! call prolong_to_new_children(tree, refine_info)
131  end do
132 
133  call system_clock(t_end,count_rate)
134  write(*, "(A,I0,A,Es10.3,A)") &
135  " Wall-clock time after ",time_steps, &
136  " time steps: ", (t_end-t_start) / real(count_rate, dp), &
137  " seconds"
138 
139  ! This call is not really necessary here, but cleaning up the data in a tree
140  ! is important if your program continues with other tasks.
141  call a2_destroy(tree)
142 
143 contains
144 
146  subroutine refine_routine(box, cell_flags)
147  type(box2_t), intent(in) :: box
148  integer, intent(out) :: cell_flags(box%n_cell, box%n_cell)
149  ! real(dp) :: diff
150  ! integer :: i, j, nc
151 
152  ! nc = box%n_cell
153 
154  ! do j = 1, nc; do i = 1, nc
155  ! diff = abs(box%cc(i+1, j, i_phi) + &
156  ! box%cc(i-1, j, i_phi) + &
157  ! box%cc(i, j+1, i_phi) + &
158  ! box%cc(i, j-1, i_phi) - &
159  ! 4 * box%cc(i, j, i_phi)) * box%dr
160 
161  ! if (box%lvl < 2 .or. diff > 0.5e-3_dp .and. box%lvl < 5) then
162  ! cell_flags(i, j) = af_do_ref
163  ! else if (diff < 0.1_dp * 0.1e-3_dp) then
164  ! cell_flags(i, j) = af_rm_ref
165  ! else
166  ! cell_flags(i, j) = af_keep_ref
167  ! end if
168  ! end do; end do
169 
170  if (box%lvl < 5) then
171  cell_flags(:, :) = af_do_ref
172  else
173  cell_flags(:, :) = af_keep_ref
174  end if
175  end subroutine refine_routine
176 
178  subroutine set_initial_condition(box)
179  type(box2_t), intent(inout) :: box
180  integer :: i, j, nc
181  real(dp) :: rr(2)
182 
183  nc = box%n_cell
184  do j = 0, nc+1; do i = 0, nc+1
185  rr = a2_r_cc(box, [i, j])
186  box%cc(i, j, i_phi) = solution(rr)
187  box%cc(i, j, i_bx:i_by) = get_bvec(rr)
188  end do; end do
189  end subroutine set_initial_condition
190 
192  function solution(rr) result(sol)
193  real(dp), intent(in) :: rr(2)
194  real(dp) :: sol
195 
196  if (all(abs(rr - domain_len * [0.25_dp, 0.0_dp]) < 0.2_dp)) then
197  sol = 1.0_dp
198  else
199  sol = 0.0_dp
200  end if
201 
202  end function solution
203 
204  function get_bvec(rr) result(bvec)
205  real(dp), intent(in) :: rr(2)
206  real(dp) :: bvec(2), phi
207 
208  ! phi = atan2(rr(2), rr(1))
209  ! bvec = [-sin(phi), cos(phi)]
210  bvec = [1.0_dp, 1.0_dp] * sqrt(0.5_dp)
211  end function get_bvec
212 
215  subroutine get_fluxes(boxes, id)
216  use m_flux_schemes
217  type(box2_t), intent(inout) :: boxes(:)
218  integer, intent(in) :: id
219  integer :: nc, n, m
220  real(dp) :: rr(2), bvec(2), grad(2), tmp(2), inv_dx
221 
222  nc = boxes(id)%n_cell
223  inv_dx = 1/boxes(id)%dr
224 
225  call a2_gc_box(boxes, id, i_phi, a2_gc_interp_lim, a2_bc_neumann_zero)
226 
227  do n = 1, nc+1
228  do m = 1, nc
229  rr = a2_rr_cc(boxes(id), [n-0.5_dp, m+0.0_dp])
230  bvec = get_bvec(rr)
231 
232  grad(1) = boxes(id)%cc(n-1, m, i_phi) - boxes(id)%cc(n, m, i_phi)
233  grad(2) = 0.25_dp * (&
234  boxes(id)%cc(n-1, m-1, i_phi) - boxes(id)%cc(n-1, m+1, i_phi) + &
235  boxes(id)%cc(n, m-1, i_phi) - boxes(id)%cc(n, m+1, i_phi))
236 
237  tmp = bvec(1) * inv_dx * bvec * grad
238  boxes(id)%fc(n, m, 1, i_phi) = sum(tmp)
239 
240  if (boxes(id)%fc(n, m, 1, i_phi) > inv_dx * boxes(id)%cc(n-1, m, i_phi)) then
241  boxes(id)%fc(n, m, 1, i_phi) = inv_dx * boxes(id)%cc(n-1, m, i_phi)
242  else if (boxes(id)%fc(n, m, 1, i_phi) < -inv_dx * boxes(id)%cc(n, m, i_phi)) then
243  boxes(id)%fc(n, m, 1, i_phi) = -inv_dx * boxes(id)%cc(n, m, i_phi)
244  end if
245 
246  rr = a2_rr_cc(boxes(id), [m+0.0_dp, n-0.5_dp])
247  bvec = get_bvec(rr)
248 
249  grad(2) = boxes(id)%cc(m, n-1, i_phi) - boxes(id)%cc(m, n, i_phi)
250  grad(1) = 0.25_dp * (&
251  boxes(id)%cc(m-1, n-1, i_phi) - boxes(id)%cc(m+1, n-1, i_phi) + &
252  boxes(id)%cc(m-1, n, i_phi) - boxes(id)%cc(m+1, n, i_phi))
253 
254  tmp = bvec(2) * inv_dx * bvec * grad
255  boxes(id)%fc(m, n, 2, i_phi) = sum(tmp)
256 
257  if (boxes(id)%fc(m, n, 2, i_phi) > inv_dx * boxes(id)%cc(m, n-1, i_phi)) then
258  boxes(id)%fc(m, n, 2, i_phi) = inv_dx * boxes(id)%cc(m, n-1, i_phi)
259  else if (boxes(id)%fc(m, n, 2, i_phi) < -inv_dx * boxes(id)%cc(m, n, i_phi)) then
260  boxes(id)%fc(m, n, 2, i_phi) = -inv_dx * boxes(id)%cc(m, n, i_phi)
261  end if
262  end do
263  end do
264 
265  end subroutine get_fluxes
266 
268  subroutine update_solution(box, dt)
269  type(box2_t), intent(inout) :: box
270  real(dp), intent(in) :: dt(:)
271  real(dp) :: inv_dr, fx(2), fy(2), tmp
272  integer :: i, j, nc
273 
274  nc = box%n_cell
275  inv_dr = 1/box%dr
276 
277  do j = 1, nc
278  do i = 1, nc
279  fx = box%fc(i:i+1, j, 1, i_phi)
280  fy = box%fc(i, j:j+1, 2, i_phi)
281 
282  tmp = dt(1) * inv_dr * (fx(1) - fx(2) + fy(1) - fy(2))
283  ! if (tmp < -box%cc(i, j, i_phi)) tmp = -box%cc(i, j, i_phi)
284  box%cc(i, j, i_phi) = box%cc(i, j, i_phi) + tmp
285  end do
286  end do
287  end subroutine update_solution
288 
290  subroutine average_phi(box)
291  type(box2_t), intent(inout) :: box
292 
293  box%cc(:, :, i_phi) = 0.5_dp * (box%cc(:, :, i_phi) + &
294  box%cc(:, :, i_phi_old))
295  end subroutine average_phi
296 
297 end program anisotropic_diffusion_2d
Module containing a couple simple flux schemes for scalar advection/diffusion problems.
Module which contains all Afivo modules, so that a user does not have to include them separately...
Definition: m_a2_all.f90:4