Afivo  0.3
poisson_basic_2d.f90

Example showing how to use multigrid and compare with an analytic solution. A standard 5-point Laplacian is used.

1 
6 program poisson_basic_2d
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 = 6
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  integer, parameter :: i_gradx = 5
21  integer, parameter :: i_egradx = 6
22 
23  type(a2_t) :: tree
24  type(ref_info_t) :: refine_info
25  integer :: mg_iter
26  integer :: ix_list(2, n_boxes_base)
27  real(dp) :: dr, residu(2), anal_err(2)
28  character(len=100) :: fname
29  type(mg2_t) :: mg
30  type(gauss_t) :: gs
31  integer :: count_rate,t_start,t_end
32 
33  print *, "Running poisson_basic_2d"
34  print *, "Number of threads", af_get_max_threads()
35 
37  ! The manufactured solution exists of Gaussians
38  ! Amplitudes: [1.0_dp, 1.0_dp]
39  ! Sigmas : [0.04_dp, 0.04_dp]
40  ! Locations : x, y, z = 0.25 or x, y, z = 0.75
41  call gauss_init(gs, [1.0_dp, 1.0_dp], [0.04_dp, 0.04_dp], &
42  reshape([0.25_dp, 0.25_dp, &
43  0.75_dp, 0.75_dp], [2,2]))
45 
46  ! The cell spacing at the coarsest grid level
47  dr = 1.0_dp / box_size
48 
50  ! Initialize tree
51  call a2_init(tree, & ! Tree to initialize
52  box_size, & ! A box contains box_size**DIM cells
53  n_var_cell, & ! Number of cell-centered variables
54  0, & ! Number of face-centered variables
55  dr, & ! Distance between cells on base level
56  coarsen_to=2, & ! Add coarsened levels for multigrid
57  cc_names=["phi", "rhs", "err", "tmp", "Dx ", "eDx"]) ! Variable names
59 
61  ! Set up geometry. These indices are used to define the coordinates of a box,
62  ! by default the box at [1,1] touches the origin (x,y) = (0,0)
63  ix_list(:, 1) = [1, 1] ! Set index of box 1
64 
65  ! Create the base mesh, using the box indices and their neighbor information
66  call a2_set_base(tree, 1, ix_list)
68 
69  call system_clock(t_start, count_rate)
71  do
72  ! For each box, set the initial conditions
73  call a2_loop_box(tree, set_initial_condition)
74 
75  ! This updates the refinement of the tree, by at most one level per call.
76  ! The second argument is a subroutine that is called for each box that can
77  ! be refined or derefined, and it should set refinement flags. Information
78  ! about the changes in refinement are returned in the third argument.
79  call a2_adjust_refinement(tree, refine_routine, refine_info)
80 
81  ! If no new boxes have been added, exit the loop
82  if (refine_info%n_add == 0) exit
83  end do
85  call system_clock(t_end, count_rate)
86 
87  write(*,"(A,Es10.3,A)") " Wall-clock time generating AMR grid: ", &
88  (t_end-t_start) / real(count_rate,dp), " seconds"
89 
90  call a2_print_info(tree)
91 
92  ! Initialize the multigrid options. This performs some basics checks and sets
93  ! default values where necessary.
94  mg%i_phi = i_phi ! Solution variable
95  mg%i_rhs = i_rhs ! Right-hand side variable
96  mg%i_tmp = i_tmp ! Variable for temporary space
97  mg%sides_bc => sides_bc ! Method for boundary conditions
98 
99  ! This routine does not initialize the multigrid fields boxes%i_phi,
100  ! boxes%i_rhs and boxes%i_tmp. These fileds will be initialized at the
101  ! first call of mg2_fas_fmg
102  call mg2_init_mg(mg)
103 
104  print *, "Multigrid iteration | max residual | max error"
105  call system_clock(t_start, count_rate)
106 
107  do mg_iter = 1, n_iterations
108  ! Perform a FAS-FMG cycle (full approximation scheme, full multigrid). The
109  ! third argument controls whether the residual is stored in i_tmp. The
110  ! fourth argument controls whether to improve the current solution.
111  call mg2_fas_fmg(tree, mg, set_residual=.true., have_guess=(mg_iter>1))
112 
113  ! Compute the error compared to the analytic solution
114  call a2_loop_box(tree, set_error)
115 
116  ! Determine the minimum and maximum residual and error
117  call a2_tree_min_cc(tree, i_tmp, residu(1))
118  call a2_tree_max_cc(tree, i_tmp, residu(2))
119  call a2_tree_min_cc(tree, i_err, anal_err(1))
120  call a2_tree_max_cc(tree, i_err, anal_err(2))
121  write(*,"(I8,13x,2(Es14.5))") mg_iter, maxval(abs(residu)), &
122  maxval(abs(anal_err))
123 
125  ! This writes a Silo output file containing the cell-centered values of the
126  ! leaves of the tree (the boxes not covered by refinement).
127  write(fname, "(A,I0)") "poisson_basic_2d_", mg_iter
128  call a2_write_silo(tree, trim(fname), dir="output")
130  end do
131  call system_clock(t_end, count_rate)
132 
133  write(*, "(A,I0,A,E10.3,A)") &
134  " Wall-clock time after ", n_iterations, &
135  " iterations: ", (t_end-t_start) / real(count_rate, dp), &
136  " seconds"
137 
138  ! This call is not really necessary here, but cleaning up the data in a tree
139  ! is important if your program continues with other tasks.
140  call a2_destroy(tree)
141 
142 contains
143 
145  ! Return the refinement flags for box
146  subroutine refine_routine(box, cell_flags)
147  type(box2_t), intent(in) :: box
148  integer, intent(out) :: cell_flags(box%n_cell, box%n_cell)
149  integer :: i, j, nc
150  real(dp) :: rr(2), dr2, drhs
151 
152  nc = box%n_cell
153  dr2 = box%dr**2
154 
155  do j = 1, nc; do i = 1, nc
156  rr = a2_r_cc(box, [i, j])
157 
158  ! This is an estimate of the truncation error in the right-hand side,
159  ! which is related to the fourth derivative of the solution.
160  drhs = dr2 * box%cc(i, j, i_rhs)
161 
162  if (abs(drhs) > 1e-3_dp .and. box%lvl < 5) then
163  cell_flags(i, j) = af_do_ref
164  else
165  cell_flags(i, j) = af_keep_ref
166  end if
167  end do; end do
168  end subroutine refine_routine
170 
172  ! This routine sets the initial conditions for each box
173  subroutine set_initial_condition(box)
174  type(box2_t), intent(inout) :: box
175  integer :: i, j, nc
176  real(dp) :: rr(2), grad(2)
177 
178  nc = box%n_cell
179 
180  do j = 1, nc; do i = 1, nc
181  ! Get the coordinate of the cell center at i, j
182  rr = a2_r_cc(box, [i, j])
183 
184  ! And set the rhs values
185  box%cc(i, j, i_rhs) = gauss_laplacian(gs, rr)
186  call gauss_gradient(gs, rr, grad)
187  box%cc(i, j, i_gradx) = grad(1)
188  end do; end do
189  end subroutine set_initial_condition
191 
193  ! Set the error compared to the analytic solution
194  subroutine set_error(box)
195  type(box2_t), intent(inout) :: box
196  integer :: i, j, nc
197  real(dp) :: rr(2), dx, gradx
198 
199  nc = box%n_cell
200  dx = box%dr
201 
202  do j = 1, nc; do i = 1, nc
203  rr = a2_r_cc(box, [i, j])
204  box%cc(i, j, i_err) = box%cc(i, j, i_phi) - gauss_value(gs, rr)
205  gradx = 0.5_dp * (box%cc(i+1, j, i_phi) - &
206  box%cc(i-1, j, i_phi)) / dx
207  box%cc(i, j, i_egradx) = gradx - box%cc(i, j, i_gradx)
208 
209  end do; end do
210  end subroutine set_error
212 
213  ! This routine sets boundary conditions for a box, by filling its ghost cells
214  ! with approriate values.
215  subroutine sides_bc(box, nb, iv, bc_type)
216  type(box2_t), intent(inout) :: box
217  integer, intent(in) :: nb ! Direction for the boundary condition
218  integer, intent(in) :: iv ! Index of variable
219  integer, intent(out) :: bc_type ! Type of boundary condition
220  real(dp) :: rr(2)
221  integer :: n, nc
222 
223  nc = box%n_cell
224 
225  ! We use dirichlet boundary conditions
226  bc_type = af_bc_dirichlet
227 
228  ! Below the solution is specified in the approriate ghost cells
229  select case (nb)
230  case (a2_neighb_lowx) ! Lower-x direction
231  do n = 1, nc
232  rr = a2_rr_cc(box, [0.5_dp, real(n, dp)])
233  box%cc(0, n, iv) = gauss_value(gs, rr)
234  end do
235  case (a2_neighb_highx) ! Higher-x direction
236  do n = 1, nc
237  rr = a2_rr_cc(box, [nc+0.5_dp, real(n, dp)])
238  box%cc(nc+1, n, iv) = gauss_value(gs, rr)
239  end do
240  case (a2_neighb_lowy) ! Lower-y direction
241  do n = 1, nc
242  rr = a2_rr_cc(box, [real(n, dp), 0.5_dp])
243  box%cc(n, 0, iv) = gauss_value(gs, rr)
244  end do
245  case (a2_neighb_highy) ! Higher-y direction
246  do n = 1, nc
247  rr = a2_rr_cc(box, [real(n, dp), nc+0.5_dp])
248  box%cc(n, nc+1, iv) = gauss_value(gs, rr)
249  end do
250  end select
251  end subroutine sides_bc
252 
253 end program poisson_basic_2d