Afivo  0.3
poisson_helmholtz_cyl.f90
1 
6 program helmholtz_cyl
7  use m_a2_all
8  use m_gaussians
9 
10  implicit none
11 
12  integer, parameter :: box_size = 8
13  integer, parameter :: n_boxes_base = 1
14  integer, parameter :: n_iterations = 10
15  integer, parameter :: n_var_cell = 4
16  integer, parameter :: i_phi = 1
17  integer, parameter :: i_rhs = 2
18  integer, parameter :: i_err = 3
19  integer, parameter :: i_tmp = 4
20  real(dp), parameter :: lambda2 = 1000.0_dp
21 
22  type(a2_t) :: tree
23  type(ref_info_t) :: ref_info
24  integer :: mg_iter
25  integer :: ix_list(2, n_boxes_base)
26  real(dp) :: dr, residu(2), anal_err(2)
27  character(len=100) :: fname
28  type(mg2_t) :: mg
29  type(gauss_t) :: gs
30  integer :: count_rate,t_start, t_end
31 
32  print *, "Running helmholtz_cyl"
33  print *, "Number of threads", af_get_max_threads()
34 
35  ! The manufactured solution exists of two Gaussians, which are stored in gs
36  call gauss_init(gs, [1.0_dp, 1.0_dp], [0.01_dp, 0.04_dp], &
37  reshape([0.0_dp, 0.25_dp, 0.1_dp, 0.75_dp], [2,2]))
38 
39  ! The cell spacing at the coarsest grid level
40  dr = 1.0_dp / box_size
41 
42  ! Initialize tree
43  call a2_init(tree, & ! Tree to initialize
44  box_size, & ! A box contains box_size**DIM cells
45  n_var_cell, & ! Number of cell-centered variables
46  0, & ! Number of face-centered variables
47  dr, & ! Distance between cells on base level
48  coarsen_to=2, & ! Add coarsened levels for multigrid
49  coord=af_cyl, & ! Cylindrical coordinates
50  cc_names=["phi", "rhs", "err", "tmp"]) ! Variable names
51 
52  ! Set up geometry. These indices are used to define the coordinates of a box,
53  ! by default the box at [1,1] touches the origin (x,y) = (0,0)
54  ix_list(:, 1) = [1,1] ! Set index of box 1
55 
56  ! Create the base mesh, using the box indices and their neighbor information
57  call a2_set_base(tree, 1, ix_list)
58  call a2_print_info(tree)
59 
60  call system_clock(t_start, count_rate)
61  do
62  ! For each box, set the initial conditions
63  call a2_loop_box(tree, set_init_cond)
64 
65  ! This updates the refinement of the tree, by at most one level per call.
66  call a2_adjust_refinement(tree, ref_routine, ref_info)
67 
68  ! If no new boxes have been added, exit the loop
69  if (ref_info%n_add == 0) exit
70  end do
71  call system_clock(t_end, count_rate)
72 
73  write(*,"(A,Es10.3,A)") " Wall-clock time generating AMR grid: ", &
74  (t_end-t_start) / real(count_rate,dp), " seconds"
75 
76  call a2_print_info(tree)
77 
78  ! Set the multigrid options.
79  mg%i_phi = i_phi ! Solution variable
80  mg%i_rhs = i_rhs ! Right-hand side variable
81  mg%i_tmp = i_tmp ! Variable for temporary space
82  mg%sides_bc => sides_bc ! Method for boundary conditions Because we use
83 
84  ! Automatically detect the right methods
85  mg%box_op => helmholtz_cyl_operator
86  mg%box_gsrb => helmholtz_cyl_gsrb
87  mg%box_corr => mg2_auto_corr
88 
89  ! Initialize the multigrid options. This performs some basics checks and sets
90  ! default values where necessary.
91  ! This routine does not initialize the multigrid variables i_phi, i_rhs
92  ! and i_tmp. These variables will be initialized at the first call of mg2_fas_fmg
93  call mg2_init_mg(mg)
94 
95  print *, "Multigrid iteration | max residual | max error"
96  call system_clock(t_start, count_rate)
97 
98  do mg_iter = 1, n_iterations
99  ! Perform a FAS-FMG cycle (full approximation scheme, full multigrid). The
100  ! third argument controls whether the residual is stored in i_tmp. The
101  ! fourth argument controls whether to improve the current solution.
102  call mg2_fas_fmg(tree, mg, .true., mg_iter>1)
103 
104  ! Compute the error compared to the analytic solution
105  call a2_loop_box(tree, set_err)
106 
107  ! Determine the minimum and maximum residual and error
108  call a2_tree_min_cc(tree, i_tmp, residu(1))
109  call a2_tree_max_cc(tree, i_tmp, residu(2))
110  call a2_tree_min_cc(tree, i_err, anal_err(1))
111  call a2_tree_max_cc(tree, i_err, anal_err(2))
112  write(*,"(I8,2Es14.5)") mg_iter, maxval(abs(residu)), &
113  maxval(abs(anal_err))
114 
115  write(fname, "(A,I0)") "helmholtz_cyl_", mg_iter
116  call a2_write_vtk(tree, trim(fname), dir="output")
117  end do
118  call system_clock(t_end, count_rate)
119 
120  write(*, "(A,I0,A,E10.3,A)") &
121  " Wall-clock time after ", n_iterations, &
122  " iterations: ", (t_end-t_start) / real(count_rate, dp), &
123  " seconds"
124 
125  ! This call is not really necessary here, but cleaning up the data in a tree
126  ! is important if your program continues with other tasks.
127  call a2_destroy(tree)
128 
129 contains
130 
131  ! Set refinement flags for box
132  subroutine ref_routine(box, cell_flags)
133  type(box2_t), intent(in) :: box
134  integer, intent(out) :: cell_flags(box%n_cell, box%n_cell)
135  integer :: i, j, nc
136  real(dp) :: crv
137 
138  nc = box%n_cell
139 
140  ! Compute the "curvature" in phi
141  do j = 1, nc
142  do i = 1, nc
143  crv = box%dr**2 * abs(box%cc(i, j, i_rhs))
144 
145  ! And refine if it exceeds a threshold
146  if (crv > 5.0e-4_dp) then
147  cell_flags(i, j) = af_do_ref
148  else
149  cell_flags(i, j) = af_keep_ref
150  end if
151  ! if (box%lvl < 4) then
152  ! cell_flags(i, j) = af_do_ref
153  ! else
154  ! cell_flags(i, j) = af_keep_ref
155  ! end if
156  end do
157  end do
158  end subroutine ref_routine
159 
160  ! This routine sets the initial conditions for each box
161  subroutine set_init_cond(box)
162  type(box2_t), intent(inout) :: box
163  integer :: i, j, nc
164  real(dp) :: rz(2)
165 
166  nc = box%n_cell
167 
168  do j = 0, nc+1
169  do i = 0, nc+1
170  rz = a2_r_cc(box, [i,j])
171  box%cc(i, j, i_rhs) = gauss_laplacian_cyl(gs, rz) - &
172  lambda2 * gauss_value(gs, rz)
173  end do
174  end do
175  end subroutine set_init_cond
176 
177  ! Compute error compared to the analytic solution
178  subroutine set_err(box)
179  type(box2_t), intent(inout) :: box
180  integer :: i, j, nc
181  real(dp) :: rz(2)
182 
183  nc = box%n_cell
184  do j = 1, nc
185  do i = 1, nc
186  rz = a2_r_cc(box, [i,j])
187  box%cc(i, j, i_err) = box%cc(i, j, i_phi) - gauss_value(gs, rz)
188  end do
189  end do
190  end subroutine set_err
191 
192  ! This routine sets boundary conditions for a box, by filling its ghost cells
193  ! with approriate values. Note that on the axis (a boundary in the lower-x
194  ! direction) we should use a Neumann zero condition in cylindrical
195  ! coordinates.
196  subroutine sides_bc(box, nb, iv, bc_type)
197  type(box2_t), intent(inout) :: box
198  integer, intent(in) :: nb ! Direction for the boundary condition
199  integer, intent(in) :: iv ! Index of variable
200  integer, intent(out) :: bc_type ! Type of boundary condition
201  real(dp) :: rz(2)
202  integer :: n, nc
203 
204  nc = box%n_cell
205 
206  select case (nb)
207  case (a2_neighb_lowx) ! Neumann zero on axis
208  bc_type = af_bc_neumann
209  box%cc(0, 1:nc, iv) = 0
210  case (a2_neighb_highx) ! Use solution on other boundaries
211  bc_type = af_bc_dirichlet
212  do n = 1, nc
213  rz = a2_rr_cc(box, [nc+0.5_dp, real(n, dp)])
214  box%cc(nc+1, n, iv) = gauss_value(gs, rz)
215  end do
216  case (a2_neighb_lowy)
217  bc_type = af_bc_dirichlet
218  do n = 1, nc
219  rz = a2_rr_cc(box, [real(n, dp), 0.5_dp])
220  box%cc(n, 0, iv) = gauss_value(gs, rz)
221  end do
222  case (a2_neighb_highy)
223  bc_type = af_bc_dirichlet
224  do n = 1, nc
225  rz = a2_rr_cc(box, [real(n, dp), nc+0.5_dp])
226  box%cc(n, nc+1, iv) = gauss_value(gs, rz)
227  end do
228  end select
229  end subroutine sides_bc
230 
232  subroutine helmholtz_cyl_operator(box, i_out, mg)
233  type(box2_t), intent(inout) :: box
234  integer, intent(in) :: i_out
235  type(mg2_t), intent(in) :: mg
236  integer :: i, j, nc, i_phi, ioff
237  real(dp) :: inv_dr_sq, rfac(2)
238 
239  nc = box%n_cell
240  inv_dr_sq = 1 / box%dr**2
241  i_phi = mg%i_phi
242  ioff = (box%ix(1)-1) * nc
243 
244  do j = 1, nc
245  do i = 1, nc
246  rfac = [i+ioff-1, i+ioff] / (i+ioff-0.5_dp)
247  box%cc(i, j, i_out) = ( &
248  rfac(1) * box%cc(i-1, j, i_phi) + &
249  rfac(2) * box%cc(i+1, j, i_phi) + &
250  box%cc(i, j-1, i_phi) + box%cc(i, j+1, i_phi) - &
251  4 * box%cc(i, j, i_phi)) * inv_dr_sq - &
252  lambda2 * box%cc(i, j, i_phi)
253  end do
254  end do
255  end subroutine helmholtz_cyl_operator
256 
258  subroutine helmholtz_cyl_gsrb(box, redblack_cntr, mg)
259  type(box2_t), intent(inout) :: box
260  integer, intent(in) :: redblack_cntr
261  type(mg2_t), intent(in) :: mg
262  integer :: i, i0, j, nc, i_phi, i_rhs, ioff
263  real(dp) :: dx2, rfac(2)
264 
265  dx2 = box%dr**2
266  nc = box%n_cell
267  i_phi = mg%i_phi
268  i_rhs = mg%i_rhs
269  ioff = (box%ix(1)-1) * nc
270 
271  ! The parity of redblack_cntr determines which cells we use. If
272  ! redblack_cntr is even, we use the even cells and vice versa.
273  do j = 1, nc
274  i0 = 2 - iand(ieor(redblack_cntr, j), 1)
275  do i = i0, nc, 2
276  rfac = [i+ioff-1, i+ioff] / (i+ioff-0.5_dp)
277  box%cc(i, j, i_phi) = 1/(4 + lambda2 * dx2) * ( &
278  rfac(1) * box%cc(i-1, j, i_phi) + &
279  rfac(2) * box%cc(i+1, j, i_phi) + &
280  box%cc(i, j+1, i_phi) + box%cc(i, j-1, i_phi) - &
281  dx2 * box%cc(i, j, i_rhs))
282  end do
283  end do
284  end subroutine helmholtz_cyl_gsrb
285 
286 end program helmholtz_cyl
This module can be used to construct solutions consisting of one or more Gaussians.
Definition: m_gaussians.f90:3
Module which contains all Afivo modules, so that a user does not have to include them separately...
Definition: m_a2_all.f90:4