Afivo  0.3
poisson_helmholtz_3d.f90
1 
6 program poisson_helmholtz_3d
7  use m_a3_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 :: lambda = 1.0e3_dp
21 
22  type(a3_t) :: tree
23  type(ref_info_t) :: refine_info
24  integer :: mg_iter
25  integer :: ix_list(3, n_boxes_base)
26  real(dp) :: dr, residu(2), anal_err(2)
27  character(len=100) :: fname
28  type(mg3_t) :: mg
29  type(gauss_t) :: gs
30  integer :: count_rate,t_start,t_end
31 
32  print *, "Running poisson_helmholtz_3d"
33  print *, "Number of threads", af_get_max_threads()
34 
35  call gauss_init(gs, [1.0_dp, 1.0_dp], [0.04_dp, 0.04_dp], &
36  reshape([0.25_dp, 0.25_dp, 0.25_dp, &
37  0.75_dp, 0.75_dp, 0.75_dp], [3,2]))
38 
39  ! The cell spacing at the coarsest grid level
40  dr = 1.0_dp / box_size
41 
42  ! Initialize tree
43  call a3_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  cc_names=["phi", "rhs", "err", "tmp"]) ! Variable names
50 
51  ix_list(:, 1) = [1, 1, 1] ! Set index of box 1
52  call a3_set_base(tree, 1, ix_list)
53 
54  do
55  call a3_loop_box(tree, set_initial_condition)
56  call a3_adjust_refinement(tree, refine_routine, refine_info)
57  if (refine_info%n_add == 0) exit
58  end do
59 
60  call a3_print_info(tree)
61 
62  mg%i_phi = i_phi ! Solution variable
63  mg%i_rhs = i_rhs ! Right-hand side variable
64  mg%i_tmp = i_tmp ! Variable for temporary space
65  mg%sides_bc => sides_bc ! Method for boundary conditions
66  mg%box_op => helmholtz_operator
67  mg%box_gsrb => helmholtz_gsrb
68 
69  call mg3_init_mg(mg)
70 
71  print *, "Multigrid iteration | max residual | max error"
72  call system_clock(t_start, count_rate)
73 
74  do mg_iter = 1, n_iterations
75  call mg3_fas_fmg(tree, mg, set_residual=.true., have_guess=(mg_iter>1))
76  call a3_loop_box(tree, set_error)
77  call a3_tree_min_cc(tree, i_tmp, residu(1))
78  call a3_tree_max_cc(tree, i_tmp, residu(2))
79  call a3_tree_min_cc(tree, i_err, anal_err(1))
80  call a3_tree_max_cc(tree, i_err, anal_err(2))
81  write(*,"(I8,13x,2(Es14.5))") mg_iter, maxval(abs(residu)), &
82  maxval(abs(anal_err))
83 
84  write(fname, "(A,I0)") "poisson_helmholtz_3d_", mg_iter
85  call a3_write_vtk(tree, trim(fname), dir="output")
86  end do
87  call system_clock(t_end, count_rate)
88 
89  write(*, "(A,I0,A,E10.3,A)") &
90  " Wall-clock time after ", n_iterations, &
91  " iterations: ", (t_end-t_start) / real(count_rate, dp), &
92  " seconds"
93 
94 contains
95 
96  ! Return the refinement flags for box
97  subroutine refine_routine(box, cell_flags)
98  type(box3_t), intent(in) :: box
99  integer, intent(out) :: cell_flags(box%n_cell, box%n_cell, box%n_cell)
100  integer :: i, j, k, nc
101  real(dp) :: rr(3), dr2, drhs
102 
103  nc = box%n_cell
104  dr2 = box%dr**2
105 
106  do k = 1, nc; do j = 1, nc; do i = 1, nc
107  rr = a3_r_cc(box, [i, j, k])
108 
109  ! This is an estimate of the truncation error in the right-hand side,
110  ! which is related to the fourth derivative of the solution.
111  drhs = dr2 * box%cc(i, j, k, i_rhs)
112 
113  if (abs(drhs) > 1e-3_dp .and. box%lvl < 5) then
114  cell_flags(i, j, k) = af_do_ref
115  else
116  cell_flags(i, j, k) = af_keep_ref
117  end if
118  end do; end do; end do
119  end subroutine refine_routine
120 
121  ! This routine sets the initial conditions for each box
122  subroutine set_initial_condition(box)
123  type(box3_t), intent(inout) :: box
124  integer :: i, j, k, nc
125  real(dp) :: rr(3)
126 
127  nc = box%n_cell
128 
129  do k = 1, nc; do j = 1, nc; do i = 1, nc
130  ! Get the coordinate of the cell center at i, j, k
131  rr = a3_r_cc(box, [i, j, k])
132 
133  ! And set the rhs values
134  box%cc(i, j, k, i_rhs) = gauss_laplacian(gs, rr) - &
135  lambda * gauss_value(gs, rr)
136  end do; end do; end do
137  end subroutine set_initial_condition
138 
139  ! Set the error compared to the analytic solution
140  subroutine set_error(box)
141  type(box3_t), intent(inout) :: box
142  integer :: i, j, k, nc
143  real(dp) :: rr(3)
144 
145  nc = box%n_cell
146  do k = 1, nc; do j = 1, nc; do i = 1, nc
147  rr = a3_r_cc(box, [i, j, k])
148  box%cc(i, j, k, i_err) = box%cc(i, j, k, i_phi) - gauss_value(gs, rr)
149  end do; end do; end do
150  end subroutine set_error
151 
152  ! This routine sets boundary conditions for a box, by filling its ghost cells
153  ! with approriate values.
154  subroutine sides_bc(box, nb, iv, bc_type)
155  type(box3_t), intent(inout) :: box
156  integer, intent(in) :: nb ! Direction for the boundary condition
157  integer, intent(in) :: iv ! Index of variable
158  integer, intent(out) :: bc_type ! Type of boundary condition
159  real(dp) :: rr(3)
160  integer :: i, j, k, ix, nc
161  real(dp) :: loc
162 
163  nc = box%n_cell
164 
165  ! We use dirichlet boundary conditions
166  bc_type = af_bc_dirichlet
167 
168  ! Below the solution is specified in the approriate ghost cells
169  ! Determine whether the direction nb is to "lower" or "higher" neighbors
170  if (a3_neighb_low(nb)) then
171  ix = 0
172  loc = 0.5_dp
173  else
174  ix = nc+1
175  loc = nc + 0.5_dp
176  end if
177 
178  ! Below the solution is specified in the approriate ghost cells
179  select case (a3_neighb_dim(nb))
180  case (1)
181  do k = 1, nc
182  do j = 1, nc
183  rr = a3_rr_cc(box, [loc, real(j, dp), real(k, dp)])
184  box%cc(ix, j, k, iv) = gauss_value(gs, rr)
185  end do
186  end do
187  case (2)
188  do k = 1, nc
189  do i = 1, nc
190  rr = a3_rr_cc(box, [real(i, dp), loc, real(k, dp)])
191  box%cc(i, ix, k, iv) = gauss_value(gs, rr)
192  end do
193  end do
194  case (3)
195  do j = 1, nc
196  do i = 1, nc
197  rr = a3_rr_cc(box, [real(i, dp), real(j, dp), loc])
198  box%cc(i, j, ix, iv) = gauss_value(gs, rr)
199  end do
200  end do
201  end select
202  end subroutine sides_bc
203 
205  subroutine helmholtz_operator(box, i_out, mg)
206  type(box3_t), intent(inout) :: box
207  integer, intent(in) :: i_out
208  type(mg3_t), intent(in) :: mg
209  integer :: i, j, nc, i_phi
210  real(dp) :: inv_dr_sq
211  integer :: k
212 
213  nc = box%n_cell
214  inv_dr_sq = 1 / box%dr**2
215  i_phi = mg%i_phi
216 
217  do k = 1, nc
218  do j = 1, nc
219  do i = 1, nc
220  box%cc(i, j, k, i_out) = inv_dr_sq * (box%cc(i-1, j, k, i_phi) + &
221  box%cc(i+1, j, k, i_phi) + box%cc(i, j-1, k, i_phi) + &
222  box%cc(i, j+1, k, i_phi) + box%cc(i, j, k-1, i_phi) + &
223  box%cc(i, j, k+1, i_phi) - 6 * box%cc(i, j, k, i_phi)) - &
224  lambda * box%cc(i, j, k, i_phi)
225  end do
226  end do
227  end do
228  end subroutine helmholtz_operator
229 
231  subroutine helmholtz_gsrb(box, redblack_cntr, mg)
232  type(box3_t), intent(inout) :: box
233  integer, intent(in) :: redblack_cntr
234  type(mg3_t), intent(in) :: mg
235  integer :: i, i0, j, nc, i_phi, i_rhs
236  real(dp) :: dx2
237  integer :: k
238 
239  dx2 = box%dr**2
240  nc = box%n_cell
241  i_phi = mg%i_phi
242  i_rhs = mg%i_rhs
243 
244  ! The parity of redblack_cntr determines which cells we use. If
245  ! redblack_cntr is even, we use the even cells and vice versa.
246  do k = 1, nc
247  do j = 1, nc
248  i0 = 2 - iand(ieor(redblack_cntr, k+j), 1)
249  do i = i0, nc, 2
250  box%cc(i, j, k, i_phi) = 1/(6 + lambda * dx2) * ( &
251  box%cc(i+1, j, k, i_phi) + box%cc(i-1, j, k, i_phi) + &
252  box%cc(i, j+1, k, i_phi) + box%cc(i, j-1, k, i_phi) + &
253  box%cc(i, j, k+1, i_phi) + box%cc(i, j, k-1, i_phi) - &
254  dx2 * box%cc(i, j, k, i_rhs))
255  end do
256  end do
257  end do
258  end subroutine helmholtz_gsrb
259 
260 end program poisson_helmholtz_3d
261 
Module which contains all Afivo modules, so that a user does not have to include them separately...
Definition: m_a3_all.f90:4
This module can be used to construct solutions consisting of one or more Gaussians.
Definition: m_gaussians.f90:3