Afivo  0.3
advection_3d.f90
1 
6 program advection_3d
7  use m_a3_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(a3_t) :: tree
20  type(ref_info_t) :: refine_info
21  integer :: ix_list(3, 1)
22  integer :: nb_list(a3_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(3), dr_min(3)
29  character(len=100) :: fname
30  integer :: count_rate, t_start, t_end
31 
32  print *, "Running advection_3d"
33  print *, "Number of threads", af_get_max_threads()
34 
35  ! Initialize tree
36  call a3_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 a3_set_cc_methods(tree, i_phi, a3_bc_neumann_zero, a3_gc_interp_lim, &
44  prolong=a3_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 a3_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 a3_loop_box(tree, set_initial_condition)
71 
72  ! Fill ghost cells for variables i_phi on the sides of all boxes, using
73  ! a3_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  ! a3_bc_neumann_zero physical boundaries: fill ghost cells near physical
77  ! boundaries using Neumann zero
78  call a3_gc_tree(tree, i_phi, a3_gc_interp_lim, a3_bc_neumann_zero)
79 
80  ! Adjust the refinement of a tree using refine_routine (see below) for grid
81  ! refinement.
82  ! Routine a3_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 a3_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 a3_print_info(tree)
101 
102  ! Restrict the initial conditions Restrict the children of a box to the box
103  ! (e.g., in 3D, average the values at the four children to get the value for
104  ! the parent)
105  call a3_restrict_tree(tree, i_phi)
106 
107  ! Fill ghost cells for variables i_phi on the sides of all boxes, using
108  ! a3_gc_interp_lim on refinement boundaries and a3_bc_neumann_zero on
109  ! physical boundaries
110  call a3_gc_tree(tree, i_phi, a3_gc_interp_lim, a3_bc_neumann_zero)
111 
112  call system_clock(t_start, count_rate)
113  time_steps = 0
114 
115  call a3_tree_sum_cc(tree, i_phi, sum_phi_t0)
116 
117  ! Starting simulation
118  do
119  time_steps = time_steps + 1
120  dr_min = a3_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_3d_", output_cnt
129 
130  ! Call procedure set_error (see below) for each box in tree, with argument time
131  call a3_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 a3_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 a3_tree_max_cc(tree, i_err, p_err)
140  call a3_tree_min_cc(tree, i_err, n_err)
141  call a3_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 a3_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 a3_loop_boxes(tree, fluxes_koren)
157 
158  ! Restrict fluxes from children to parents on refinement boundaries.
159  call a3_consistent_fluxes(tree, [i_phi])
160 
161  ! Call procedure update_solution (see below) for each box in tree, with argument dt
162  call a3_loop_box_arg(tree, update_solution, [dt])
163 
164  ! Restrict variables i_phi to all parent boxes
165  call a3_restrict_tree(tree, i_phi)
166  end do
167 
168  ! Take average of phi_old and phi
169  call a3_loop_box(tree, average_phi)
170  time = time + dt
171  end do
172 
173  ! Fill ghost cells for variable i_phi
174  call a3_gc_tree(tree, i_phi, a3_gc_interp_lim, a3_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 a3_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 a3_destroy(tree)
194 
195 contains
196 
199  subroutine refine_routine(box, cell_flags)
200  type(box3_t), intent(in) :: box
201  integer, intent(out) :: cell_flags(box%n_cell, box%n_cell, box%n_cell)
202  real(dp) :: diff
203  integer :: i, j, k, nc
204 
205  nc = box%n_cell
206 
207  do k = 1, nc; do j = 1, nc; do i = 1, nc
208  diff = abs(box%cc(i+1, j, k, i_phi) + &
209  box%cc(i-1, j, k, i_phi) + &
210  box%cc(i, j+1, k, i_phi) + &
211  box%cc(i, j-1, k, i_phi) + &
212  box%cc(i, j, k+1, i_phi) + &
213  box%cc(i, j, k-1, i_phi) - &
214  6 * box%cc(i, j, k, i_phi)) * box%dr
215 
216  if (box%lvl < 2 .or. diff > 0.5e-3_dp .and. box%lvl < 5) then
217  cell_flags(i, j, k) = af_do_ref
218  else if (diff < 0.1_dp * 0.1e-3_dp) then
219  cell_flags(i, j, k) = af_rm_ref
220  else
221  cell_flags(i, j, k) = af_keep_ref
222  end if
223  end do; end do; end do
224  end subroutine refine_routine
226 
228  subroutine set_initial_condition(box)
229  type(box3_t), intent(inout) :: box
230  integer :: i, j, k, nc
231  real(dp) :: rr(3)
232 
233  nc = box%n_cell
234  do k = 0, nc+1; do j = 0, nc+1; do i = 0, nc+1
235  rr = a3_r_cc(box, [i, j, k])
236  box%cc(i, j, k, i_phi) = solution(rr, 0.0_dp)
237  end do; end do; end do
238  end subroutine set_initial_condition
239 
241  subroutine set_error(box, time)
242  type(box3_t), intent(inout) :: box
243  real(dp), intent(in) :: time(:)
244  integer :: i, j, k, nc
245  real(dp) :: rr(3)
246 
247  nc = box%n_cell
248  do k = 1, nc; do j = 1, nc; do i = 1, nc
249  rr = a3_r_cc(box, [i, j, k])
250  box%cc(i, j, k, i_err) = &
251  box%cc(i, j, k, i_phi) - solution(rr, time(1))
252  end do; end do; end do
253  end subroutine set_error
254 
256  function solution(rr, t) result(sol)
257  real(dp), intent(in) :: rr(3), t
258  real(dp) :: sol, rr_t(3)
259 
260  rr_t = rr - velocity * t
261 
262  select case (sol_type)
263  case (1)
264  sol = sin(rr_t(1))**4 * cos(rr_t(2))**4
265  case (2)
266  rr_t = modulo(rr_t, domain_len) / domain_len
267  if (norm2(rr_t - 0.5_dp) < 0.1_dp) then
268  sol = 1.0_dp
269  else
270  sol = 0.0_dp
271  end if
272  end select
273  end function solution
274 
277  subroutine fluxes_koren(boxes, id)
278  use m_flux_schemes
279  type(box3_t), intent(inout) :: boxes(:)
280  integer, intent(in) :: id
281  integer :: nc
282  real(dp), allocatable :: cc(:, :, :)
283  real(dp), allocatable :: v(:, :, :, :)
284 
285  nc = boxes(id)%n_cell
286  allocate(cc(-1:nc+2, -1:nc+2, -1:nc+2))
287  allocate(v(1:nc+1, 1:nc+1, 1:nc+1, 3))
288 
289  call a3_gc_box(boxes, id, i_phi, a3_gc_interp_lim, a3_bc_neumann_zero)
290 
291  ! Get a second layer of ghost cell data (the 'normal' routines give just one
292  ! layer of ghost cells). Use a3_gc2_prolong_linear on refinement boundaries and
293  ! a3_bc2_neumann_zero on physical boundaries.
294  call a3_gc2_box(boxes, & ! List of all the boxes
295  id, & ! Id of box for which we set ghost cells
296  i_phi, & ! Variable for which ghost cells are set
297  a3_gc2_prolong_linear, & ! Procedure called at refinement boundaries
298  a3_bc2_neumann_zero, & ! Procedure called at physical boundaries
299  cc, & ! The enlarged box with ghost cells
300  nc) ! box%n_cell
301 
302  v(:, :, :, 1) = velocity(1)
303  v(:, :, :, 2) = velocity(2)
304  v(:, :, :, 3) = velocity(3)
305 
306  call flux_koren_3d(cc, v, nc, 2)
307  boxes(id)%fc(:, :, :, :, i_phi) = v
308 
309  end subroutine fluxes_koren
310 
312  subroutine update_solution(box, dt)
313  type(box3_t), intent(inout) :: box
314  real(dp), intent(in) :: dt(:)
315  real(dp) :: inv_dr
316  integer :: i, j, k, nc
317 
318  nc = box%n_cell
319  inv_dr = 1/box%dr
320 
321  forall (i = 1:nc, j = 1:nc, k = 1:nc)
322  box%cc(i, j, k, i_phi) = box%cc(i, j, k, i_phi) + dt(1) * inv_dr * ( &
323  box%fc(i, j, k, 1, i_phi) - box%fc(i+1, j, k, 1, i_phi) + &
324  box%fc(i, j, k, 2, i_phi) - box%fc(i, j+1, k, 2, i_phi) + &
325  box%fc(i, j, k, 3, i_phi) - box%fc(i, j, k+1, 3, i_phi))
326  end forall
327  end subroutine update_solution
328 
330  subroutine average_phi(box)
331  type(box3_t), intent(inout) :: box
332 
333  box%cc(:, :, :, i_phi) = 0.5_dp * (box%cc(:, :, :, i_phi) + &
334  box%cc(:, :, :, i_phi_old))
335  end subroutine average_phi
336 
337 end program advection_3d
Module which contains all Afivo modules, so that a user does not have to include them separately...
Definition: m_a3_all.f90:4
Module containing a couple simple flux schemes for scalar advection/diffusion problems.