Afivo  0.3
advection_2d.f90

An advection example using the Koren flux limiter. Time stepping is done with the explicit trapezoidal rule.

1 
6 program advection_2d
7  use m_a2_all
8 
9  implicit none
10 
11  integer, parameter :: box_size = 8
12  integer, parameter :: i_phi = 1
13  integer, parameter :: i_phi_old = 2
14  integer, parameter :: i_err = 3
15  integer, parameter :: sol_type = 1
16  real(dp), parameter :: domain_len = 2 * acos(-1.0_dp)
17  real(dp), parameter :: dr = domain_len / box_size
18 
19  type(a2_t) :: tree
20  type(ref_info_t) :: refine_info
21  integer :: ix_list(2, 1)
22  integer :: nb_list(a2_num_neighbors, 1)
23  integer :: refine_steps, time_steps, output_cnt
24  integer :: i, id, n, n_steps
25  real(dp) :: dt, time, end_time, p_err, n_err
26  real(dp) :: sum_phi, sum_phi_t0
27  real(dp) :: dt_adapt, dt_output
28  real(dp) :: velocity(2), dr_min(2)
29  character(len=100) :: fname
30  integer :: count_rate, t_start, t_end
31 
32  print *, "Running advection_2d"
33  print *, "Number of threads", af_get_max_threads()
34 
35  ! Initialize tree
36  call a2_init(tree, & ! Tree to initialize
37  box_size, & ! A box contains box_size**DIM cells
38  n_var_cell=3, & ! Number of cell-centered variables
39  n_var_face=1, & ! Number of face-centered variables
40  dr=dr, & ! Distance between cells on base level
41  cc_names=["phi", "old", "err"]) ! Variable names
42 
43  call a2_set_cc_methods(tree, i_phi, a2_bc_neumann_zero, a2_gc_interp_lim, &
44  prolong=a2_prolong_limit)
45 
46  ! Set up geometry
47  id = 1
48  ix_list(:, id) = 1 ! Set index of box
49  nb_list(:, id) = id ! Box is periodic, so its own neighbor
50 
51  ! Create the base mesh, using the box indices and their neighbor information
52  call a2_set_base(tree, 1, ix_list, nb_list)
53 
54  output_cnt = 0
55  time = 0
56  dt_adapt = 0.01_dp
57  dt_output = 0.1_dp
58  end_time = 5.0_dp
59  velocity(:) = 0.0_dp
60  velocity(1) = 1.0_dp
61  velocity(2) = -1.0_dp
62 
63  ! Set up the initial conditions
64  call system_clock(t_start,count_rate)
65  refine_steps=0
66 
67  do
68  refine_steps=refine_steps+1
69  ! We should only set the finest level, but this also works
70  call a2_loop_box(tree, set_initial_condition)
71 
72  ! Fill ghost cells for variables i_phi on the sides of all boxes, using
73  ! a2_gc_interp_lim on refinement boundaries: Interpolation between fine
74  ! points and coarse neighbors to fill ghost cells near refinement
75  ! boundaries. The ghost values are less than twice the coarse values. and
76  ! a2_bc_neumann_zero physical boundaries: fill ghost cells near physical
77  ! boundaries using Neumann zero
78  call a2_gc_tree(tree, i_phi, a2_gc_interp_lim, a2_bc_neumann_zero)
79 
80  ! Adjust the refinement of a tree using refine_routine (see below) for grid
81  ! refinement.
82  ! Routine a2_adjust_refinement sets the bit af_bit_new_children for each box
83  ! that is refined. On input, the tree should be balanced. On output,
84  ! the tree is still balanced, and its refinement is updated (with at most
85  ! one level per call).
86  call a2_adjust_refinement(tree, & ! tree
87  refine_routine, & ! Refinement function
88  refine_info, & ! Information about refinement
89  1) ! Buffer width (in cells)
90 
91  ! If no new boxes have been added, exit the loop
92  if (refine_info%n_add == 0) exit
93  end do
94  call system_clock(t_end, count_rate)
95 
96  write(*, "(A,i0,A,Es10.3,A)") " Wall-clock time for ", &
97  refine_steps, " refinement steps: ", &
98  (t_end-t_start) / real(count_rate, dp), " seconds"
99 
100  call a2_print_info(tree)
101 
102  ! Restrict the initial conditions Restrict the children of a box to the box
103  ! (e.g., in 2D, average the values at the four children to get the value for
104  ! the parent)
105  call a2_restrict_tree(tree, i_phi)
106 
107  ! Fill ghost cells for variables i_phi on the sides of all boxes, using
108  ! a2_gc_interp_lim on refinement boundaries and a2_bc_neumann_zero on
109  ! physical boundaries
110  call a2_gc_tree(tree, i_phi, a2_gc_interp_lim, a2_bc_neumann_zero)
111 
112  call system_clock(t_start, count_rate)
113  time_steps = 0
114 
115  call a2_tree_sum_cc(tree, i_phi, sum_phi_t0)
116 
117  ! Starting simulation
118  do
119  time_steps = time_steps + 1
120  dr_min = a2_min_dr(tree)
121  dt = 0.5_dp / (sum(abs(velocity/dr_min)) + epsilon(1.0_dp))
122 
123  n_steps = ceiling(dt_adapt/dt)
124  dt = dt_adapt / n_steps
125 
126  if (output_cnt * dt_output <= time) then
127  output_cnt = output_cnt + 1
128  write(fname, "(A,I0)") "advection_2d_", output_cnt
129 
130  ! Call procedure set_error (see below) for each box in tree, with argument time
131  call a2_loop_box_arg(tree, set_error, [time])
132 
133  ! Write the cell centered data of tree to a vtk unstructured file fname.
134  ! Only the leaves of the tree are used
135  call a2_write_silo(tree, trim(fname), output_cnt, time, dir="output")
136 
137  ! Find maximum and minimum values of cc(..., i_err) and cc(..., i_phi).
138  ! By default, only loop over leaves, and ghost cells are not used.
139  call a2_tree_max_cc(tree, i_err, p_err)
140  call a2_tree_min_cc(tree, i_err, n_err)
141  call a2_tree_sum_cc(tree, i_phi, sum_phi)
142  write(*,"(2(A,1x,Es12.4,2x))") &
143  " max error:", max(p_err, abs(n_err)), &
144  "conservation error: ", (sum_phi - sum_phi_t0) / sum_phi_t0
145  end if
146 
147  if (time > end_time) exit
148 
149  do n = 1, n_steps
150  ! Copy previous solution
151  call a2_tree_copy_cc(tree, i_phi, i_phi_old)
152 
153  ! Two forward Euler steps over dt
154  do i = 1, 2
155  ! Call procedure fluxes_koren for each id in tree, giving the list of boxes
156  call a2_loop_boxes(tree, fluxes_koren)
157 
158  ! Restrict fluxes from children to parents on refinement boundaries.
159  call a2_consistent_fluxes(tree, [i_phi])
160 
161  ! Call procedure update_solution (see below) for each box in tree, with argument dt
162  call a2_loop_box_arg(tree, update_solution, [dt])
163 
164  ! Restrict variables i_phi to all parent boxes
165  call a2_restrict_tree(tree, i_phi)
166  end do
167 
168  ! Take average of phi_old and phi
169  call a2_loop_box(tree, average_phi)
170  time = time + dt
171  end do
172 
173  ! Fill ghost cells for variable i_phi
174  call a2_gc_tree(tree, i_phi, a2_gc_interp_lim, a2_bc_neumann_zero)
175 
177  ! Adjust the refinement of a tree using refine_routine (see below) for grid
178  ! refinement. On input, the tree should be balanced. On output, the tree is
179  ! still balanced, and its refinement is updated (with at most one level per
180  ! call).
181  call a2_adjust_refinement(tree, refine_routine, refine_info, 1)
183  end do
184 
185  call system_clock(t_end,count_rate)
186  write(*, "(A,I0,A,Es10.3,A)") &
187  " Wall-clock time after ",time_steps, &
188  " time steps: ", (t_end-t_start) / real(count_rate, dp), &
189  " seconds"
190 
191  ! This call is not really necessary here, but cleaning up the data in a tree
192  ! is important if your program continues with other tasks.
193  call a2_destroy(tree)
194 
195 contains
196 
199  subroutine refine_routine(box, cell_flags)
200  type(box2_t), intent(in) :: box
201  integer, intent(out) :: cell_flags(box%n_cell, box%n_cell)
202  real(dp) :: diff
203  integer :: i, j, nc
204 
205  nc = box%n_cell
206 
207  do j = 1, nc; do i = 1, nc
208  diff = abs(box%cc(i+1, j, i_phi) + &
209  box%cc(i-1, j, i_phi) + &
210  box%cc(i, j+1, i_phi) + &
211  box%cc(i, j-1, i_phi) - &
212  4 * box%cc(i, j, i_phi)) * box%dr
213 
214  if (box%lvl < 2 .or. diff > 0.5e-3_dp .and. box%lvl < 5) then
215  cell_flags(i, j) = af_do_ref
216  else if (diff < 0.1_dp * 0.1e-3_dp) then
217  cell_flags(i, j) = af_rm_ref
218  else
219  cell_flags(i, j) = af_keep_ref
220  end if
221  end do; end do
222  end subroutine refine_routine
224 
226  subroutine set_initial_condition(box)
227  type(box2_t), intent(inout) :: box
228  integer :: i, j, nc
229  real(dp) :: rr(2)
230 
231  nc = box%n_cell
232  do j = 0, nc+1; do i = 0, nc+1
233  rr = a2_r_cc(box, [i, j])
234  box%cc(i, j, i_phi) = solution(rr, 0.0_dp)
235  end do; end do
236  end subroutine set_initial_condition
237 
239  subroutine set_error(box, time)
240  type(box2_t), intent(inout) :: box
241  real(dp), intent(in) :: time(:)
242  integer :: i, j, nc
243  real(dp) :: rr(2)
244 
245  nc = box%n_cell
246  do j = 1, nc; do i = 1, nc
247  rr = a2_r_cc(box, [i, j])
248  box%cc(i, j, i_err) = &
249  box%cc(i, j, i_phi) - solution(rr, time(1))
250  end do; end do
251  end subroutine set_error
252 
254  function solution(rr, t) result(sol)
255  real(dp), intent(in) :: rr(2), t
256  real(dp) :: sol, rr_t(2)
257 
258  rr_t = rr - velocity * t
259 
260  select case (sol_type)
261  case (1)
262  sol = sin(rr_t(1))**4 * cos(rr_t(2))**4
263  case (2)
264  rr_t = modulo(rr_t, domain_len) / domain_len
265  if (norm2(rr_t - 0.5_dp) < 0.1_dp) then
266  sol = 1.0_dp
267  else
268  sol = 0.0_dp
269  end if
270  end select
271  end function solution
272 
275  subroutine fluxes_koren(boxes, id)
276  use m_flux_schemes
277  type(box2_t), intent(inout) :: boxes(:)
278  integer, intent(in) :: id
279  integer :: nc
280  real(dp), allocatable :: cc(:, :)
281  real(dp), allocatable :: v(:, :, :)
282 
283  nc = boxes(id)%n_cell
284  allocate(cc(-1:nc+2, -1:nc+2))
285  allocate(v(1:nc+1, 1:nc+1, 2))
286 
287  call a2_gc_box(boxes, id, i_phi, a2_gc_interp_lim, a2_bc_neumann_zero)
288 
289  ! Get a second layer of ghost cell data (the 'normal' routines give just one
290  ! layer of ghost cells). Use a2_gc2_prolong_linear on refinement boundaries and
291  ! a2_bc2_neumann_zero on physical boundaries.
292  call a2_gc2_box(boxes, & ! List of all the boxes
293  id, & ! Id of box for which we set ghost cells
294  i_phi, & ! Variable for which ghost cells are set
295  a2_gc2_prolong_linear, & ! Procedure called at refinement boundaries
296  a2_bc2_neumann_zero, & ! Procedure called at physical boundaries
297  cc, & ! The enlarged box with ghost cells
298  nc) ! box%n_cell
299 
300  v(:, :, 1) = velocity(1)
301  v(:, :, 2) = velocity(2)
302 
303  call flux_koren_2d(cc, v, nc, 2)
304  boxes(id)%fc(:, :, :, i_phi) = v
305 
306  end subroutine fluxes_koren
307 
309  subroutine update_solution(box, dt)
310  type(box2_t), intent(inout) :: box
311  real(dp), intent(in) :: dt(:)
312  real(dp) :: inv_dr
313  integer :: i, j, nc
314 
315  nc = box%n_cell
316  inv_dr = 1/box%dr
317 
318  forall (i = 1:nc, j = 1:nc)
319  box%cc(i, j, i_phi) = box%cc(i, j, i_phi) + dt(1) * inv_dr * ( &
320  box%fc(i, j, 1, i_phi) - box%fc(i+1, j, 1, i_phi) + &
321  box%fc(i, j, 2, i_phi) - box%fc(i, j+1, 2, i_phi))
322  end forall
323  end subroutine update_solution
324 
326  subroutine average_phi(box)
327  type(box2_t), intent(inout) :: box
328 
329  box%cc(:, :, i_phi) = 0.5_dp * (box%cc(:, :, i_phi) + &
330  box%cc(:, :, i_phi_old))
331  end subroutine average_phi
332 
333 end program advection_2d