Afivo  0.3
poisson_cyl_dielectric.f90
1 
5 program poisson_cyl_dielectric
6  use m_a2_all
7  use m_gaussians
8 
9  implicit none
10 
11  integer, parameter :: box_size = 8
12  integer, parameter :: n_boxes_base = 1
13  integer, parameter :: n_iterations = 10
14  integer, parameter :: n_var_cell = 5
15  integer, parameter :: i_phi = 1
16  integer, parameter :: i_rhs = 2
17  integer, parameter :: i_err = 3
18  integer, parameter :: i_tmp = 4
19  integer, parameter :: i_eps = 5
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_dielectric"
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.25_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", "eps"]) ! 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%i_eps = i_eps ! Variable for epsilon coefficient
82  mg%sides_bc => sides_bc ! Method for boundary conditions Because we use
83 
84  ! Automatically detect the right methods
85  mg%box_op => mg2_auto_op
86  mg%box_gsrb => mg2_auto_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)") "poisson_cyl_dielectric_", 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)) / box%cc(i, j, i_eps)
144 
145  ! And refine if it exceeds a threshold
146  if (crv > 1.0e-3_dp) then
147  cell_flags(i, j) = af_do_ref
148  else
149  cell_flags(i, j) = af_keep_ref
150  end if
151  end do
152  end do
153  end subroutine ref_routine
154 
155  ! This routine sets the initial conditions for each box
156  subroutine set_init_cond(box)
157  type(box2_t), intent(inout) :: box
158  integer :: i, j, nc
159  real(dp) :: rz(2), grad(2), dr, qbnd, tmp
160 
161  nc = box%n_cell
162  box%cc(:, :, i_phi) = 0
163  dr = box%dr
164 
165  do j = 0, nc+1
166  do i = 0, nc+1
167  rz = a2_r_cc(box, [i,j])
168 
169  ! Change epsilon in part of the domain
170  if (rz(1) < 0.5_dp .and. rz(2) < 0.5_dp) then
171  box%cc(i, j, i_eps) = 100.0_dp
172  else
173  box%cc(i, j, i_eps) = 1.0_dp
174  end if
175 
176  ! Partially compute the right-hand side (see below)
177  box%cc(i, j, i_rhs) = gauss_laplacian_cyl(gs, rz) * box%cc(i, j, i_eps)
178  end do
179  end do
180 
181  ! We have to place surface charges where epsilon has a jump, this is first
182  ! done in the r-direction
183  do j = 1, nc
184  do i = 0, nc
185  rz = a2_rr_cc(box, [i + 0.5_dp, real(j, dp)])
186 
187  ! Determine amount of charge
188  call gauss_gradient(gs, rz, grad)
189  qbnd = (box%cc(i+1, j, i_eps) - box%cc(i, j, i_eps)) * &
190  grad(1) / dr
191 
192  ! Place surface charge weighted with eps
193  tmp = box%cc(i+1, j, i_eps) / &
194  (box%cc(i, j, i_eps) + box%cc(i+1, j, i_eps))
195  box%cc(i+1, j, i_rhs) = box%cc(i+1, j, i_rhs) + tmp * qbnd
196  box%cc(i, j, i_rhs) = box%cc(i, j, i_rhs) + (1-tmp) * qbnd
197  end do
198  end do
199 
200  ! Set surface charge in z-direction
201  do j = 0, nc
202  do i = 1, nc
203  rz = a2_rr_cc(box, [real(i, dp), j + 0.5_dp])
204 
205  ! Determine amount of charge
206  call gauss_gradient(gs, rz, grad)
207  qbnd = (box%cc(i, j+1, i_eps) - box%cc(i, j, i_eps)) * &
208  grad(2) / dr
209 
210  ! Place surface charge weighted with eps
211  tmp = box%cc(i, j+1, i_eps) / &
212  (box%cc(i, j, i_eps) + box%cc(i, j+1, i_eps))
213  box%cc(i, j+1, i_rhs) = box%cc(i, j+1, i_rhs) + tmp * qbnd
214  box%cc(i, j, i_rhs) = box%cc(i, j, i_rhs) + (1-tmp) * qbnd
215  end do
216  end do
217  end subroutine set_init_cond
218 
219  ! Compute error compared to the analytic solution
220  subroutine set_err(box)
221  type(box2_t), intent(inout) :: box
222  integer :: i, j, nc
223  real(dp) :: rz(2)
224 
225  nc = box%n_cell
226  do j = 1, nc
227  do i = 1, nc
228  rz = a2_r_cc(box, [i,j])
229  box%cc(i, j, i_err) = box%cc(i, j, i_phi) - gauss_value(gs, rz)
230  end do
231  end do
232  end subroutine set_err
233 
234  ! This routine sets boundary conditions for a box, by filling its ghost cells
235  ! with approriate values. Note that on the axis (a boundary in the lower-x
236  ! direction) we should use a Neumann zero condition in cylindrical
237  ! coordinates.
238  subroutine sides_bc(box, nb, iv, bc_type)
239  type(box2_t), intent(inout) :: box
240  integer, intent(in) :: nb ! Direction for the boundary condition
241  integer, intent(in) :: iv ! Index of variable
242  integer, intent(out) :: bc_type ! Type of boundary condition
243  real(dp) :: rz(2)
244  integer :: n, nc
245 
246  nc = box%n_cell
247 
248  select case (nb)
249  case (a2_neighb_lowx) ! Neumann zero on axis
250  bc_type = af_bc_neumann
251  box%cc(0, 1:nc, iv) = 0
252  case (a2_neighb_highx) ! Use solution on other boundaries
253  bc_type = af_bc_dirichlet
254  do n = 1, nc
255  rz = a2_rr_cc(box, [nc+0.5_dp, real(n, dp)])
256  box%cc(nc+1, n, iv) = gauss_value(gs, rz)
257  end do
258  case (a2_neighb_lowy)
259  bc_type = af_bc_dirichlet
260  do n = 1, nc
261  rz = a2_rr_cc(box, [real(n, dp), 0.5_dp])
262  box%cc(n, 0, iv) = gauss_value(gs, rz)
263  end do
264  case (a2_neighb_highy)
265  bc_type = af_bc_dirichlet
266  do n = 1, nc
267  rz = a2_rr_cc(box, [real(n, dp), nc+0.5_dp])
268  box%cc(n, nc+1, iv) = gauss_value(gs, rz)
269  end do
270  end select
271  end subroutine sides_bc
272 
273 end program poisson_cyl_dielectric
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