Afivo  0.3
check_prolongation.f90
1 #include "../src/cpp_macros.h"
2 !> \example check_prolongation
3 !>
4 !> This example checks whether prolongation behaves as expected
5 program check_prolongation
6  use m_af_all
7  implicit none
8 
9  integer, parameter :: box_size = 8
10  integer, parameter :: n_runs = 50
11  integer :: i_phi
12  real(dp) :: gradient(NDIM)
13  logical, parameter :: write_silo = .false.
14 
15  write(*,'(A,I0,A)') 'program check_prolongation_', ndim, "d"
16  print *, "Number of threads", af_get_max_threads()
17 
18  ! Check whether prolongation is exact for a linear gradient
19  call test_gradient(n_runs, af_prolong_linear, "linear")
20  call test_gradient(n_runs, af_prolong_sparse, "sparse")
21  call test_gradient(n_runs, af_prolong_limit, "limit")
22 
23  ! Check whether values lie in a valid range after prolongation
24  call test_valid_range(n_runs, af_prolong_linear, "linear")
25  call test_valid_range(n_runs, af_prolong_sparse, "sparse")
26  call test_valid_range(n_runs, af_prolong_zeroth, "zeroth")
27  call test_valid_range(n_runs, af_prolong_limit, "limit")
28 
29  print *, "Tests passed"
30 
31 contains
32 
33  subroutine test_valid_range(max_iter, prolongation_method, name)
34  integer, intent(in) :: max_iter
35  procedure(af_subr_prolong) :: prolongation_method
36  character(len=*), intent(in) :: name
37  type(af_t) :: tree
38  integer :: iter
39  type(ref_info_t) :: ref_info
40  character(len=100) :: fname
41 
42  print *, "Testing range of values (", name, ")"
43 
44  call af_add_cc_variable(tree, "phi", ix=i_phi)
45  call af_set_cc_methods(tree, i_phi, af_bc_dirichlet_zero, &
46  prolong=prolongation_method)
47 
48  call af_init(tree, box_size, [dtimes(1.0_dp)], &
49  [dtimes(box_size)], periodic=[dtimes(.true.)], &
50  mem_limit_gb=0.5_dp)
51 
52  do iter = 1, max_iter
53  call af_loop_box(tree, set_random_state)
54  call af_restrict_tree(tree, [i_phi])
55  call af_gc_tree(tree, [i_phi])
56 
57  call af_adjust_refinement(tree, ref_routine, ref_info, ref_buffer=0)
58 
59  call af_loop_tree(tree, check_range_box, leaves_only=.true.)
60 
61  if (write_silo) then
62  write(fname, "(A,I0)") "output/check_prolongation_" // name &
63  // "_" // dimname // "_", iter
64  call af_write_silo(tree, trim(fname), n_cycle=iter)
65  end if
66  end do
67 
68  end subroutine test_valid_range
69 
70  subroutine test_gradient(max_iter, prolongation_method, name)
71  integer, intent(in) :: max_iter
72  procedure(af_subr_prolong) :: prolongation_method
73  character(len=*), intent(in) :: name
74  type(af_t) :: tree
75  integer :: iter
76  type(ref_info_t) :: ref_info
77  character(len=100) :: fname
78 
79  print *, "Testing linear gradient (", name, ")"
80 
81  call af_add_cc_variable(tree, "phi", ix=i_phi)
82  call af_set_cc_methods(tree, i_phi, bc_gradient, &
83  prolong=prolongation_method)
84 
85  call af_init(tree, box_size, [dtimes(1.0_dp)], &
86  [dtimes(box_size)], periodic=[dtimes(.false.)], &
87  mem_limit_gb=0.5_dp)
88 
89  do iter = 1, max_iter
90  call random_number(gradient)
91  call af_loop_box(tree, set_solution_gradient)
92  call af_restrict_tree(tree, [i_phi])
93  call af_gc_tree(tree, [i_phi])
94 
95  call af_adjust_refinement(tree, ref_routine, ref_info, ref_buffer=0)
96 
97  call af_loop_tree(tree, check_gradient_box, leaves_only=.true.)
98 
99  if (write_silo) then
100  write(fname, "(A,I0)") "output/check_prolongation_gradient_" &
101  // name // "_" // dimname // "_", iter
102  call af_write_silo(tree, trim(fname), n_cycle=iter)
103  end if
104  end do
105  end subroutine test_gradient
106 
107  subroutine ref_routine(box, cell_flags)
108  type(box_t), intent(in) :: box ! A list of all boxes in the tree
109  integer, intent(out) :: cell_flags(DTIMES(box%n_cell))
110  real(dp) :: rr
111 
112  call random_number(rr)
113 
114  if (rr < 0.5_dp**ndim .and. box%lvl < 5) then
115  cell_flags = af_do_ref
116  else
117  cell_flags = af_rm_ref
118  end if
119  end subroutine ref_routine
120 
121  subroutine set_random_state(box)
122  type(box_t), intent(inout) :: box
123  integer :: nc
124  nc = box%n_cell
125  call random_number(box%cc(dtimes(1:nc), i_phi))
126  end subroutine set_random_state
127 
128  subroutine set_solution_gradient(box)
129  type(box_t), intent(inout) :: box
130  integer :: nc, IJK
131  real(dp) :: r(NDIM)
132 
133  nc = box%n_cell
134 
135  do kji_do(1,nc)
136  r = af_r_cc(box, [ijk])
137  box%cc(ijk, i_phi) = sum(gradient * r)
138  end do; close_do
139  end subroutine set_solution_gradient
140 
141  subroutine check_range_box(tree, id)
142  type(af_t), intent(inout) :: tree
143  integer, intent(in) :: id
144  integer :: nc
145  real(dp) :: min_phi, max_phi
146  logical :: mask(DTIMES(0:tree%n_cell+1))
147  integer :: minloc_phi(NDIM), maxloc_phi(NDIM)
148 
149  nc = tree%n_cell
150 
151  ! Set the mask for the interior and the sides (excluding corners)
152  mask = .false.
153  mask(dtimes(1:nc)) = .true.
154 #if NDIM == 1
155  mask(:0) = .true.
156  mask(nc+1:) = .true.
157 #elif NDIM == 2
158  mask(:0, 1:nc) = .true.
159  mask(nc+1:, 1:nc) = .true.
160  mask(1:nc, :0) = .true.
161  mask(1:nc, nc+1:) = .true.
162 #elif NDIM == 3
163  mask(:0, 1:nc, 1:nc) = .true.
164  mask(nc+1:, 1:nc, 1:nc) = .true.
165  mask(1:nc, :0, 1:nc) = .true.
166  mask(1:nc, nc+1:, 1:nc) = .true.
167  mask(1:nc, 1:nc, :0) = .true.
168  mask(1:nc, 1:nc, nc+1:) = .true.
169 #endif
170 
171  associate(cc => tree%boxes(id)%cc)
172  min_phi = minval(cc(dtimes(:), 1), mask=mask)
173  minloc_phi = minloc(cc(dtimes(:), 1), mask=mask)
174  max_phi = maxval(cc(dtimes(:), 1), mask=mask)
175  maxloc_phi = maxloc(cc(dtimes(:), 1), mask=mask)
176 
177  if (min_phi < 0 .or. max_phi > 1) then
178  print *, "minimum of phi: ", min_phi, minloc_phi - 1
179  print *, "maximum of phi: ", max_phi, maxloc_phi - 1
180  error stop "min/max not in range [0, 1]"
181  end if
182  end associate
183  end subroutine check_range_box
184 
185  subroutine check_gradient_box(tree, id)
186  type(af_t), intent(inout) :: tree
187  integer, intent(in) :: id
188  integer :: IJK, nc
189  logical :: mask(DTIMES(0:tree%n_cell+1))
190  real(dp) :: diff(DTIMES(0:tree%n_cell+1))
191  real(dp) :: max_deviation, r(NDIM)
192  real(dp), parameter :: threshold = 1e-13_dp
193 
194  nc = tree%n_cell
195 
196  ! Set the mask for the interior and the sides (excluding corners)
197  mask = .false.
198  mask(dtimes(1:nc)) = .true.
199 #if NDIM == 1
200  mask(:0) = .true.
201  mask(nc+1:) = .true.
202 #elif NDIM == 2
203  mask(:0, 1:nc) = .true.
204  mask(nc+1:, 1:nc) = .true.
205  mask(1:nc, :0) = .true.
206  mask(1:nc, nc+1:) = .true.
207 #elif NDIM == 3
208  mask(:0, 1:nc, 1:nc) = .true.
209  mask(nc+1:, 1:nc, 1:nc) = .true.
210  mask(1:nc, :0, 1:nc) = .true.
211  mask(1:nc, nc+1:, 1:nc) = .true.
212  mask(1:nc, 1:nc, :0) = .true.
213  mask(1:nc, 1:nc, nc+1:) = .true.
214 #endif
215 
216  associate(cc => tree%boxes(id)%cc)
217  ! Set solution
218  diff = 0.0_dp
219  do kji_do(0, nc+1)
220  if (mask(ijk)) then
221  r = af_r_cc(tree%boxes(id), [ijk])
222  diff(ijk) = abs(sum(gradient * r) - cc(ijk, 1))
223  end if
224  end do; close_do
225 
226  max_deviation = maxval(diff)
227 
228  if (max_deviation > threshold) then
229  print *, "max deviation from linear solution: ", max_deviation
230  print *, "location: ", maxloc(diff) - 1
231  error stop "max deviation larger than threshold"
232  end if
233  end associate
234  end subroutine check_gradient_box
235 
236  !> Set boundary conditions for a linear gradient solution
237  subroutine bc_gradient(box, nb, iv, coords, bc_val, bc_type)
238  type(box_t), intent(in) :: box
239  integer, intent(in) :: nb
240  integer, intent(in) :: iv
241  real(dp), intent(in) :: coords(NDIM, box%n_cell**(NDIM-1))
242  real(dp), intent(out) :: bc_val(box%n_cell**(NDIM-1))
243  integer, intent(out) :: bc_type
244  integer :: nc
245 
246  nc = box%n_cell
247  bc_type = af_bc_dirichlet
248  bc_val = matmul(gradient, coords)
249  end subroutine bc_gradient
250 
251 end program
Module which contains all Afivo modules, so that a user does not have to include them separately.
Definition: m_af_all.f90:3