Afivo  0.3
poisson_cyl.f90

Example showing how to use multigrid and compare with an analytic solution, using the method of manufactured solutions. A standard 5-point Laplacian is used in cylindrical coordinates.

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