Afivo 0.3
All Classes Namespaces Functions Variables Pages
m_af_multigrid.f90
1!> This module contains the geometric multigrid routines that come with Afivo
2!>
3!> @todo How to use box tag with different types of operators?
5#include "../src/cpp_macros.h"
6 use m_af_types
10
11 implicit none
12 private
13
14 public :: mg_t
15 public :: mg_init
16 public :: mg_use
17 public :: mg_destroy
18 public :: mg_fas_fmg
19 public :: mg_fas_vcycle
20
21 ! Methods for level set functions
22 public :: mg_lsf_dist_linear
23 public :: mg_lsf_dist_gss
24
25 ! Set tag for each box, depending on operator
26 public :: mg_set_box_tag
27 public :: mg_set_operators_lvl
28 public :: mg_set_operators_tree
29 public :: mg_update_operator_stencil
30
31 ! Methods for normal Laplacian
32 public :: mg_box_lpl_gradient
33 public :: mg_sides_rb
34
35 ! To compute the gradient of the solution
36 public :: mg_compute_phi_gradient
37 public :: mg_compute_field_norm
38 public :: mg_box_field_norm
39
40contains
41
42 !> Check multigrid options or set them to default
43 subroutine mg_init(tree, mg)
44 use m_af_core, only: af_set_cc_methods
45 type(af_t), intent(inout) :: tree !< Tree to do multigrid on
46 type(mg_t), intent(inout) :: mg !< Multigrid options
47
48 if (mg%i_phi < 0) stop "mg_init: i_phi not set"
49 if (mg%i_tmp < 0) stop "mg_init: i_tmp not set"
50 if (mg%i_rhs < 0) stop "mg_init: i_rhs not set"
51
52 if (.not. associated(mg%sides_bc)) stop "mg_init: sides_bc not set"
53 if (.not. tree%ready) error stop "mg_init: tree not initialized"
54
55 ! Check whether methods are set, otherwise use default (for laplacian)
56 if (.not. associated(mg%box_op)) mg%box_op => mg_auto_op
57 ! if (.not. associated(mg%box_stencil)) mg%box_stencil => mg_auto_stencil
58 if (.not. associated(mg%box_gsrb)) mg%box_gsrb => mg_auto_gsrb
59 if (.not. associated(mg%box_corr)) mg%box_corr => mg_auto_corr
60 if (.not. associated(mg%box_rstr)) mg%box_rstr => mg_auto_rstr
61 if (.not. associated(mg%sides_rb)) mg%sides_rb => mg_auto_rb
62
63 ! By default, store new operator and prolongation stencils
64 if (mg%operator_key == af_stencil_none) then
65 tree%n_stencil_keys_stored = tree%n_stencil_keys_stored + 1
66 mg%operator_key = tree%n_stencil_keys_stored
67 end if
68
69 if (mg%prolongation_key == af_stencil_none) then
70 tree%n_stencil_keys_stored = tree%n_stencil_keys_stored + 1
71 mg%prolongation_key = tree%n_stencil_keys_stored
72 end if
73
74 if (mg%i_lsf /= -1) then
75 error stop "Set tree%mg_i_lsf instead of mg%i_lsf"
76 else if (iand(mg%operator_mask, mg_lsf_box) > 0) then
77 mg%i_lsf = tree%mg_i_lsf
78 end if
79
80 if (mg%i_eps /= -1) then
81 error stop "Set tree%mg_i_eps instead of mg%i_eps"
82 else if (iand(mg%operator_mask, mg_veps_box+mg_ceps_box) > 0) then
83 mg%i_eps = tree%mg_i_eps
84 end if
85
86 if (mg%i_lsf /= -1) then
87 if (.not. associated(mg%lsf_dist)) &
88 mg%lsf_dist => mg_lsf_dist_linear
89 if (associated(mg%lsf_dist, mg_lsf_dist_gss) .and. .not. &
90 associated(mg%lsf)) then
91 error stop "mg_lsf_dist_gss requires mg%lsf to be set"
92 end if
93 end if
94
95 call mg_set_operators_lvl(tree, mg, 1)
96 call coarse_solver_initialize(tree, mg)
97
98 if (mg%i_lsf /= -1) then
99 call check_coarse_representation_lsf(tree, mg)
100 end if
101
102 if (.not. tree%has_cc_method(mg%i_phi)) then
103 ! Set the proper methods for the phi variable
104 call af_set_cc_methods(tree, mg%i_phi, mg%sides_bc, mg%sides_rb)
105 end if
106
107 mg%initialized = .true.
108
109 end subroutine mg_init
110
111 subroutine mg_destroy(mg)
112 type(mg_t), intent(inout) :: mg !< Multigrid options
113 if (.not. mg%initialized) error stop "mg%initialized is false"
114 call coarse_solver_destroy(mg%csolver)
115 end subroutine mg_destroy
116
117 !> Make sure box tags and operators are set
118 subroutine mg_use(tree, mg)
119 type(af_t), intent(inout) :: tree
120 type(mg_t), intent(in), target :: mg !< Multigrid options
121
122 if (.not. mg%initialized) error stop "mg%initialized is false"
123 tree%mg_current_operator_mask = mg%operator_mask
124
125 call mg_set_operators_tree(tree, mg)
126 end subroutine mg_use
127
128 !> To be called at the end of a multigrid solve
129 subroutine done_with_mg(tree)
130 type(af_t), intent(inout) :: tree
131 tree%mg_current_operator_mask = -1
132 end subroutine done_with_mg
133
134 !> Perform FAS-FMG cycle (full approximation scheme, full multigrid). Note
135 !> that this routine needs valid ghost cells (for i_phi) on input, and gives
136 !> back valid ghost cells on output
137 subroutine mg_fas_fmg(tree, mg, set_residual, have_guess)
138 use m_af_utils, only: af_boxes_copy_cc
139 type(af_t), intent(inout) :: tree !< Tree to do multigrid on
140 type(mg_t), intent(inout) :: mg !< Multigrid options
141 logical, intent(in) :: set_residual !< If true, store residual in i_tmp
142 logical, intent(in) :: have_guess !< If false, start from phi = 0
143 integer :: lvl
144
145 call mg_use(tree, mg)
146
147 if (have_guess) then
148 do lvl = tree%highest_lvl, 2, -1
149 ! Set rhs on coarse grid and restrict phi
150 call set_coarse_phi_rhs(tree, lvl, mg)
151 end do
152 else
153 call init_phi_rhs(tree, mg)
154 end if
155
156 ! Handle level 1 grid
157 lvl = 1
158 call af_boxes_copy_cc(tree%boxes, tree%lvls(lvl)%ids, &
159 mg%i_phi, mg%i_tmp)
160 call mg_fas_vcycle(tree, mg, set_residual .and. &
161 lvl == tree%highest_lvl, lvl, standalone=.false.)
162
163 do lvl = 2, tree%highest_lvl
164 ! Store phi_old in tmp
165 call af_boxes_copy_cc(tree%boxes, tree%lvls(lvl)%ids, &
166 mg%i_phi, mg%i_tmp)
167
168 ! Correct solution at this lvl using lvl-1 data
169 ! phi = phi + prolong(phi_coarse - phi_old_coarse)
170 call correct_children(tree%boxes, tree%lvls(lvl-1)%parents, mg)
171
172 ! Update ghost cells
173 call af_gc_lvl(tree, lvl, [mg%i_phi])
174
175 call mg_fas_vcycle(tree, mg, set_residual .and. &
176 lvl == tree%highest_lvl, lvl, standalone=.false.)
177 end do
178
179 call done_with_mg(tree)
180 end subroutine mg_fas_fmg
181
182 !> Perform FAS V-cycle (full approximation scheme). Note that this routine
183 !> needs valid ghost cells (for i_phi) on input, and gives back valid ghost
184 !> cells on output
185 subroutine mg_fas_vcycle(tree, mg, set_residual, highest_lvl, standalone)
186 use m_af_utils, only: af_tree_sum_cc
187 type(af_t), intent(inout) :: tree !< Tree to do multigrid on
188 type(mg_t), intent(in) :: mg !< Multigrid options
189 logical, intent(in) :: set_residual !< If true, store residual in i_tmp
190 integer, intent(in), optional :: highest_lvl !< Maximum level for V-cycle
191 logical, intent(in), optional :: standalone !< False if called by other cycle
192 integer :: lvl, i, id, max_lvl
193 logical :: by_itself
194 real(dp) :: sum_phi, mean_phi
195
196 by_itself = .true.; if (present(standalone)) by_itself = standalone
197
198 if (by_itself) then
199 call mg_use(tree, mg)
200 end if
201
202 max_lvl = tree%highest_lvl
203 if (present(highest_lvl)) max_lvl = highest_lvl
204
205 do lvl = max_lvl, 2, -1
206 ! Downwards relaxation
207 call gsrb_boxes(tree, lvl, mg, mg_cycle_down)
208
209 ! Set rhs on coarse grid, restrict phi, and copy i_phi to i_tmp for the
210 ! correction later
211 call update_coarse(tree, lvl, mg)
212 end do
213
214 call solve_coarse_grid(tree, mg)
215
216 ! Do the upwards part of the v-cycle in the tree
217 do lvl = 2, max_lvl
218 ! Correct solution at this lvl using lvl-1 data
219 ! phi = phi + prolong(phi_coarse - phi_old_coarse)
220 call correct_children(tree%boxes, tree%lvls(lvl-1)%parents, mg)
221
222 ! Have to fill ghost cells after correction
223 call af_gc_lvl(tree, lvl, [mg%i_phi])
224
225 ! Upwards relaxation
226 call gsrb_boxes(tree, lvl, mg, mg_cycle_up)
227 end do
228
229 if (set_residual) then
230 !$omp parallel private(lvl, i, id)
231 do lvl = 1, max_lvl
232 !$omp do
233 do i = 1, size(tree%lvls(lvl)%ids)
234 id = tree%lvls(lvl)%ids(i)
235 call residual_box(tree%boxes(id), mg)
236 end do
237 !$omp end do nowait
238 end do
239 !$omp end parallel
240 end if
241
242 ! Subtract the mean of phi, for example when doing a fully periodic
243 ! simulation. Note that the integrated r.h.s. term should also be zero then.
244 if (mg%subtract_mean) then
245 call af_tree_sum_cc(tree, mg%i_phi, sum_phi)
246 mean_phi = sum_phi / af_total_volume(tree)
247 !$omp parallel private(lvl, i, id)
248 do lvl = 1, max_lvl
249 !$omp do
250 do i = 1, size(tree%lvls(lvl)%ids)
251 id = tree%lvls(lvl)%ids(i)
252 tree%boxes(id)%cc(dtimes(:), mg%i_phi) = &
253 tree%boxes(id)%cc(dtimes(:), mg%i_phi) - mean_phi
254 end do
255 !$omp end do nowait
256 end do
257 !$omp end parallel
258 end if
259
260 if (by_itself) then
261 call done_with_mg(tree)
262 end if
263
264 end subroutine mg_fas_vcycle
265
266 subroutine solve_coarse_grid(tree, mg)
267 use omp_lib
268 type(af_t), intent(inout) :: tree !< Tree to do multigrid on
269 type(mg_t), intent(in) :: mg !< Multigrid options
270 integer :: num_iterations, max_threads
271 integer :: n_unknowns
272
273 call coarse_solver_set_rhs_phi(tree, mg)
274 n_unknowns = product(tree%coarse_grid_size)
275
276 if (n_unknowns < mg%csolver%min_unknowns_for_openmp) then
277 ! Use single thread if there are too few unknowns
278 max_threads = omp_get_max_threads()
279 call omp_set_num_threads(1)
280 end if
281
282 call coarse_solver(mg%csolver, num_iterations)
283 call coarse_solver_get_phi(tree, mg)
284
285 ! Set ghost cells for the new coarse grid solution
286 call af_gc_lvl(tree, 1, [mg%i_phi])
287
288 if (n_unknowns < mg%csolver%min_unknowns_for_openmp) then
289 call omp_set_num_threads(max_threads)
290 end if
291 end subroutine solve_coarse_grid
292
293 !> Fill ghost cells near refinement boundaries which preserves diffusive fluxes.
294 subroutine mg_sides_rb(boxes, id, nb, iv)
295 type(box_t), intent(inout) :: boxes(:) !< List of all boxes
296 integer, intent(in) :: id !< Id of box
297 integer, intent(in) :: nb !< Ghost cell direction
298 integer, intent(in) :: iv !< Ghost cell variable
299 integer :: nc, ix, dix, ijk, di, co(ndim)
300 integer :: hnc, p_id, p_nb_id
301#if NDIM == 1
302 real(dp) :: tmp
303 real(dp) :: gc
304#elif NDIM == 2
305 integer :: dj
306 real(dp) :: tmp(0:boxes(id)%n_cell/2+1)
307 real(dp) :: gc(boxes(id)%n_cell)
308#elif NDIM == 3
309 integer :: dj, dk
310 real(dp) :: tmp(0:boxes(id)%n_cell/2+1, 0:boxes(id)%n_cell/2+1)
311 real(dp) :: gc(boxes(id)%n_cell, boxes(id)%n_cell)
312#endif
313#if NDIM > 1
314 real(dp) :: grad(ndim-1)
315#endif
316
317 nc = boxes(id)%n_cell
318 hnc = nc/2
319
320 p_id = boxes(id)%parent
321 p_nb_id = boxes(p_id)%neighbors(nb)
322 co = af_get_child_offset(boxes(id))
323
324 associate(box => boxes(p_nb_id))
325 ! First fill a temporary array with data next to the fine grid
326 select case (nb)
327#if NDIM == 1
328 case (af_neighb_lowx)
329 tmp = box%cc(nc, iv)
330 case (af_neighb_highx)
331 tmp = box%cc(1, iv)
332#elif NDIM == 2
333 case (af_neighb_lowx)
334 tmp = box%cc(nc, co(2):co(2)+hnc+1, iv)
335 case (af_neighb_highx)
336 tmp = box%cc(1, co(2):co(2)+hnc+1, iv)
337 case (af_neighb_lowy)
338 tmp = box%cc(co(1):co(1)+hnc+1, nc, iv)
339 case (af_neighb_highy)
340 tmp = box%cc(co(1):co(1)+hnc+1, 1, iv)
341#elif NDIM == 3
342 case (af_neighb_lowx)
343 tmp = box%cc(nc, co(2):co(2)+hnc+1, co(3):co(3)+hnc+1, iv)
344 case (af_neighb_highx)
345 tmp = box%cc(1, co(2):co(2)+hnc+1, co(3):co(3)+hnc+1, iv)
346 case (af_neighb_lowy)
347 tmp = box%cc(co(1):co(1)+hnc+1, nc, co(3):co(3)+hnc+1, iv)
348 case (af_neighb_highy)
349 tmp = box%cc(co(1):co(1)+hnc+1, 1, co(3):co(3)+hnc+1, iv)
350 case (af_neighb_lowz)
351 tmp = box%cc(co(1):co(1)+hnc+1, co(2):co(2)+hnc+1, nc, iv)
352 case (af_neighb_highz)
353 tmp = box%cc(co(1):co(1)+hnc+1, co(2):co(2)+hnc+1, 1, iv)
354#endif
355 case default
356 error stop "mg_sides_rb: wrong argument for nb"
357 end select
358 end associate
359
360 ! Now interpolate the coarse grid data to obtain values 'straight' next to
361 ! the fine grid points
362#if NDIM == 1
363 gc = tmp
364#elif NDIM == 2
365 do i = 1, hnc
366 grad(1) = 0.125_dp * (tmp(i+1) - tmp(i-1))
367 gc(2*i-1) = tmp(i) - grad(1)
368 gc(2*i) = tmp(i) + grad(1)
369 end do
370#elif NDIM == 3
371 do j = 1, hnc
372 do i = 1, hnc
373 grad(1) = 0.125_dp * (tmp(i+1, j) - tmp(i-1, j))
374 grad(2) = 0.125_dp * (tmp(i, j+1) - tmp(i, j-1))
375 gc(2*i-1, 2*j-1) = tmp(i, j) - grad(1) - grad(2)
376 gc(2*i, 2*j-1) = tmp(i, j) + grad(1) - grad(2)
377 gc(2*i-1, 2*j) = tmp(i, j) - grad(1) + grad(2)
378 gc(2*i, 2*j) = tmp(i, j) + grad(1) + grad(2)
379 end do
380 end do
381#endif
382
383 if (af_neighb_low(nb)) then
384 ix = 1
385 dix = 1
386 else
387 ix = nc
388 dix = -1
389 end if
390
391 select case (af_neighb_dim(nb))
392#if NDIM == 1
393 case (1)
394 i = ix
395 di = dix
396 boxes(id)%cc(i-di, iv) = 0.5_dp * gc &
397 + 0.75_dp * boxes(id)%cc(i, iv) &
398 - 0.25_dp * boxes(id)%cc(i+di, iv)
399#elif NDIM == 2
400 case (1)
401 i = ix
402 di = dix
403 do j = 1, nc
404 dj = -1 + 2 * iand(j, 1)
405 boxes(id)%cc(i-di, j, iv) = 0.5_dp * gc(j) &
406 + 0.75_dp * boxes(id)%cc(i, j, iv) &
407 - 0.25_dp * boxes(id)%cc(i+di, j, iv)
408 end do
409 case (2)
410 j = ix
411 dj = dix
412 do i = 1, nc
413 di = -1 + 2 * iand(i, 1)
414 boxes(id)%cc(i, j-dj, iv) = 0.5_dp * gc(i) &
415 + 0.75_dp * boxes(id)%cc(i, j, iv) &
416 - 0.25_dp * boxes(id)%cc(i, j+dj, iv)
417 end do
418#elif NDIM == 3
419 case (1)
420 i = ix
421 di = dix
422 do k = 1, nc
423 dk = -1 + 2 * iand(k, 1)
424 do j = 1, nc
425 dj = -1 + 2 * iand(j, 1)
426 boxes(id)%cc(i-di, j, k, iv) = &
427 0.5_dp * gc(j, k) + &
428 0.75_dp * boxes(id)%cc(i, j, k, iv) - &
429 0.25_dp * boxes(id)%cc(i+di, j, k, iv)
430 end do
431 end do
432 case (2)
433 j = ix
434 dj = dix
435 do k = 1, nc
436 dk = -1 + 2 * iand(k, 1)
437 do i = 1, nc
438 di = -1 + 2 * iand(i, 1)
439 boxes(id)%cc(i, j-dj, k, iv) = &
440 0.5_dp * gc(i, k) + &
441 0.75_dp * boxes(id)%cc(i, j, k, iv) - &
442 0.25_dp * boxes(id)%cc(i, j+dj, k, iv)
443 end do
444 end do
445 case (3)
446 k = ix
447 dk = dix
448 do j = 1, nc
449 dj = -1 + 2 * iand(j, 1)
450 do i = 1, nc
451 di = -1 + 2 * iand(i, 1)
452 boxes(id)%cc(i, j, k-dk, iv) = &
453 0.5_dp * gc(i, j) + &
454 0.75_dp * boxes(id)%cc(i, j, k, iv) - &
455 0.25_dp * boxes(id)%cc(i, j, k+dk, iv)
456 end do
457 end do
458#endif
459 end select
460
461 end subroutine mg_sides_rb
462
463 !> Fill ghost cells near refinement boundaries which preserves diffusive
464 !> fluxes. This routine does not do interpolation of coarse grid values.
465 !> Basically, we extrapolate from the fine cells to a corner point, and then
466 !> take the average between this corner point and a coarse neighbor to fill
467 !> ghost cells for the fine cells.
468 subroutine mg_sides_rb_extrap(boxes, id, nb, iv)
469 type(box_t), intent(inout) :: boxes(:) !< List of all boxes
470 integer, intent(in) :: id !< Id of box
471 integer, intent(in) :: nb !< Ghost cell direction
472 integer, intent(in) :: iv !< Ghost cell variable
473 integer :: nc, ix, dix, IJK, di
474#if NDIM > 1
475 integer :: dj
476#endif
477#if NDIM > 2
478 integer :: dk
479#endif
480
481 nc = boxes(id)%n_cell
482
483 if (af_neighb_low(nb)) then
484 ix = 1
485 dix = 1
486 else
487 ix = nc
488 dix = -1
489 end if
490
491 call af_gc_prolong_copy(boxes, id, nb, iv, 0)
492
493 select case (af_neighb_dim(nb))
494#if NDIM == 1
495 case (1)
496 i = ix
497 di = dix
498 boxes(id)%cc(i-di, iv) = 0.5_dp * boxes(id)%cc(i-di, iv) &
499 + 0.75_dp * boxes(id)%cc(i, iv) &
500 - 0.25_dp * boxes(id)%cc(i+di, iv)
501#elif NDIM == 2
502 case (1)
503 i = ix
504 di = dix
505 do j = 1, nc
506 dj = -1 + 2 * iand(j, 1)
507
508 ! Bilinear extrapolation (using 4 points)
509 boxes(id)%cc(i-di, j, iv) = 0.5_dp * boxes(id)%cc(i-di, j, iv) + &
510 1.125_dp * boxes(id)%cc(i, j, iv) - 0.375_dp * &
511 (boxes(id)%cc(i+di, j, iv) + boxes(id)%cc(i, j+dj, iv)) &
512 + 0.125_dp * boxes(id)%cc(i+di, j+dj, iv)
513
514 ! Extrapolation using 3 points
515 ! boxes(id)%cc(i-di, j, iv) = 0.5_dp * boxes(id)%cc(i-di, j, iv) + &
516 ! boxes(id)%cc(i, j, iv) - 0.25_dp * &
517 ! (boxes(id)%cc(i+di, j, iv) + boxes(id)%cc(i, j+dj, iv))
518
519 ! Extrapolation using 2 points
520 ! boxes(id)%cc(i-di, j, iv) = 0.5_dp * boxes(id)%cc(i-di, j, iv) + &
521 ! 0.75_dp * boxes(id)%cc(i, j, iv) - 0.25_dp * &
522 ! boxes(id)%cc(i+di, j+dj, iv)
523 end do
524 case (2)
525 j = ix
526 dj = dix
527 do i = 1, nc
528 di = -1 + 2 * iand(i, 1)
529
530 ! Bilinear extrapolation (using 4 points)
531 boxes(id)%cc(i, j-dj, iv) = 0.5_dp * boxes(id)%cc(i, j-dj, iv) + &
532 1.125_dp * boxes(id)%cc(i, j, iv) - 0.375_dp * &
533 (boxes(id)%cc(i+di, j, iv) + boxes(id)%cc(i, j+dj, iv)) &
534 + 0.125_dp * boxes(id)%cc(i+di, j+dj, iv)
535
536 ! Extrapolation using 2 points
537 ! boxes(id)%cc(i, j-dj, iv) = 0.5_dp * boxes(id)%cc(i, j-dj, iv) + &
538 ! 0.75_dp * boxes(id)%cc(i, j, iv) - 0.25_dp * &
539 ! boxes(id)%cc(i+di, j+dj, iv)
540 end do
541#elif NDIM == 3
542 case (1)
543 i = ix
544 di = dix
545 do k = 1, nc
546 dk = -1 + 2 * iand(k, 1)
547 do j = 1, nc
548 dj = -1 + 2 * iand(j, 1)
549 ! Trilinear extrapolation (using 8 points)
550 ! boxes(id)%cc(i-di, j, k, iv) = &
551 ! 0.5_dp * boxes(id)%cc(i-di, j, k, iv) + 0.0625_dp * (&
552 ! 27 * boxes(id)%cc(i, j, k, iv) &
553 ! - 9 * boxes(id)%cc(i+di, j, k, iv) &
554 ! - 9 * boxes(id)%cc(i, j+dj, k, iv) &
555 ! - 9 * boxes(id)%cc(i, j, k+dk, iv) &
556 ! + 3 * boxes(id)%cc(i+di, j+dj, k, iv) &
557 ! + 3 * boxes(id)%cc(i+di, j, k+dk, iv) &
558 ! + 3 * boxes(id)%cc(i, j+dj, k+dk, iv) &
559 ! - 1 * boxes(id)%cc(i+di, j+dj, k+dk, iv))
560
561 ! Extrapolation using 2 points
562 boxes(id)%cc(i-di, j, k, iv) = &
563 0.5_dp * boxes(id)%cc(i-di, j, k, iv) + &
564 0.75_dp * boxes(id)%cc(i, j, k, iv) - &
565 0.25_dp * boxes(id)%cc(i+di, j+dj, k+dk, iv)
566 end do
567 end do
568 case (2)
569 j = ix
570 dj = dix
571 do k = 1, nc
572 dk = -1 + 2 * iand(k, 1)
573 do i = 1, nc
574 di = -1 + 2 * iand(i, 1)
575
576 ! boxes(id)%cc(i, j-dj, k, iv) = &
577 ! 0.5_dp * boxes(id)%cc(i, j-dj, k, iv) + 0.0625_dp * (&
578 ! 27 * boxes(id)%cc(i, j, k, iv) &
579 ! - 9 * boxes(id)%cc(i+di, j, k, iv) &
580 ! - 9 * boxes(id)%cc(i, j+dj, k, iv) &
581 ! - 9 * boxes(id)%cc(i, j, k+dk, iv) &
582 ! + 3 * boxes(id)%cc(i+di, j+dj, k, iv) &
583 ! + 3 * boxes(id)%cc(i+di, j, k+dk, iv) &
584 ! + 3 * boxes(id)%cc(i, j+dj, k+dk, iv) &
585 ! - 1 * boxes(id)%cc(i+di, j+dj, k+dk, iv))
586
587 boxes(id)%cc(i, j-dj, k, iv) = &
588 0.5_dp * boxes(id)%cc(i, j-dj, k, iv) + &
589 0.75_dp * boxes(id)%cc(i, j, k, iv) - &
590 0.25_dp * boxes(id)%cc(i+di, j+dj, k+dk, iv)
591 end do
592 end do
593 case (3)
594 k = ix
595 dk = dix
596 do j = 1, nc
597 dj = -1 + 2 * iand(j, 1)
598 do i = 1, nc
599 di = -1 + 2 * iand(i, 1)
600
601 ! boxes(id)%cc(i, j, k-dk, iv) = &
602 ! 0.5_dp * boxes(id)%cc(i, j, k-dk, iv) + 0.0625_dp * (&
603 ! 27 * boxes(id)%cc(i, j, k, iv) &
604 ! - 9 * boxes(id)%cc(i+di, j, k, iv) &
605 ! - 9 * boxes(id)%cc(i, j+dj, k, iv) &
606 ! - 9 * boxes(id)%cc(i, j, k+dk, iv) &
607 ! + 3 * boxes(id)%cc(i+di, j+dj, k, iv) &
608 ! + 3 * boxes(id)%cc(i+di, j, k+dk, iv) &
609 ! + 3 * boxes(id)%cc(i, j+dj, k+dk, iv) &
610 ! - 1 * boxes(id)%cc(i+di, j+dj, k+dk, iv))
611
612 boxes(id)%cc(i, j, k-dk, iv) = &
613 0.5_dp * boxes(id)%cc(i, j, k-dk, iv) + &
614 0.75_dp * boxes(id)%cc(i, j, k, iv) - &
615 0.25_dp * boxes(id)%cc(i+di, j+dj, k+dk, iv)
616 end do
617 end do
618#endif
619 end select
620
621 end subroutine mg_sides_rb_extrap
622
623 ! Sets phi = phi + prolong(phi_coarse - phi_old_coarse)
624 subroutine correct_children(boxes, ids, mg)
625 type(box_t), intent(inout) :: boxes(:) !< List of all boxes
626 integer, intent(in) :: ids(:) !< Operate on these boxes
627 type(mg_t), intent(in) :: mg !< Multigrid options
628 integer :: i, id, nc, i_c, c_id
629
630 !$omp parallel do private(id, nc, i_c, c_id)
631 do i = 1, size(ids)
632 id = ids(i)
633 nc = boxes(id)%n_cell
634
635 ! Store the correction in i_tmp
636 boxes(id)%cc(dtimes(:), mg%i_tmp) = boxes(id)%cc(dtimes(:), mg%i_phi) - &
637 boxes(id)%cc(dtimes(:), mg%i_tmp)
638
639 do i_c = 1, af_num_children
640 c_id = boxes(id)%children(i_c)
641 if (c_id == af_no_box) cycle
642 call mg%box_corr(boxes(id), boxes(c_id), mg)
643 end do
644 end do
645 !$omp end parallel do
646 end subroutine correct_children
647
648 subroutine gsrb_boxes(tree, lvl, mg, type_cycle)
649 type(af_t), intent(inout) :: tree !< Tree containing full grid
650 type(mg_t), intent(in) :: mg !< Multigrid options
651 integer, intent(in) :: lvl !< Operate on this refinement level
652 integer, intent(in) :: type_cycle !< Type of cycle to perform
653 integer :: n, i, n_cycle
654 logical :: use_corners
655
656 select case (type_cycle)
657 case (mg_cycle_down)
658 n_cycle = mg%n_cycle_down
659 case (mg_cycle_up)
660 n_cycle = mg%n_cycle_up
661 case default
662 error stop "gsrb_boxes: invalid cycle type"
663 end select
664
665 associate(ids => tree%lvls(lvl)%ids)
666 !$omp parallel private(n, i)
667 do n = 1, 2 * n_cycle
668 !$omp do
669 do i = 1, size(ids)
670 call mg%box_gsrb(tree%boxes(ids(i)), n, mg)
671 end do
672 !$omp end do
673
674 ! If corner ghost cells are not required, only store them during the
675 ! final upward cycle
676 use_corners = mg%use_corners .or. &
677 (type_cycle /= mg_cycle_down .and. n == 2 * n_cycle)
678
679 !$omp do
680 do i = 1, size(ids)
681 call af_gc_box(tree, ids(i), [mg%i_phi], use_corners)
682 end do
683 !$omp end do
684 end do
685 !$omp end parallel
686 end associate
687 end subroutine gsrb_boxes
688
689 ! Set rhs on coarse grid, restrict phi, and copy i_phi to i_tmp for the
690 ! correction later
691 subroutine update_coarse(tree, lvl, mg)
692 use m_af_utils, only: af_box_add_cc, af_box_copy_cc
693 type(af_t), intent(inout) :: tree !< Tree containing full grid
694 integer, intent(in) :: lvl !< Update coarse values at lvl-1
695 type(mg_t), intent(in) :: mg !< Multigrid options
696 integer :: i, id, p_id, nc
697 real(dp), allocatable :: tmp(DTIMES(:))
698
699 id = tree%lvls(lvl)%ids(1)
700 nc = tree%n_cell
701 allocate(tmp(dtimes(1:nc)))
702
703 ! Restrict phi and the residual
704 !$omp parallel do private(id, p_id, tmp)
705 do i = 1, size(tree%lvls(lvl)%ids)
706 id = tree%lvls(lvl)%ids(i)
707 p_id = tree%boxes(id)%parent
708
709 ! Copy the data currently in i_tmp, and restore it later (i_tmp holds the
710 ! previous state of i_phi)
711 tmp = tree%boxes(id)%cc(dtimes(1:nc), mg%i_tmp)
712 call residual_box(tree%boxes(id), mg)
713 call mg%box_rstr(tree%boxes(id), tree%boxes(p_id), mg%i_tmp, mg)
714 call mg%box_rstr(tree%boxes(id), tree%boxes(p_id), mg%i_phi, mg)
715 tree%boxes(id)%cc(dtimes(1:nc), mg%i_tmp) = tmp
716 end do
717 !$omp end parallel do
718
719 call af_gc_lvl(tree, lvl-1, [mg%i_phi])
720
721 ! Set rhs_c = laplacian(phi_c) + restrict(res) where it is refined, and
722 ! store current coarse phi in tmp.
723
724 !$omp parallel do private(id)
725 do i = 1, size(tree%lvls(lvl-1)%parents)
726 id = tree%lvls(lvl-1)%parents(i)
727
728 ! Set rhs = L phi
729 call mg%box_op(tree%boxes(id), mg%i_rhs, mg)
730
731 ! Add tmp (the fine grid residual) to rhs
732 call af_box_add_cc(tree%boxes(id), mg%i_tmp, mg%i_rhs)
733
734 ! Story a copy of phi in tmp
735 call af_box_copy_cc(tree%boxes(id), mg%i_phi, mg%i_tmp)
736 end do
737 !$omp end parallel do
738 end subroutine update_coarse
739
740 !> This routine performs the same as update_coarse, but it ignores the tmp
741 !> variable
742 subroutine set_coarse_phi_rhs(tree, lvl, mg)
743 use m_af_utils, only: af_box_add_cc
744 type(af_t), intent(inout) :: tree !< Tree containing full grid
745 integer, intent(in) :: lvl !< Update coarse values at lvl-1
746 type(mg_t), intent(in) :: mg !< Multigrid options
747 integer :: i, id, p_id
748
749 ! Fill ghost cells here to be sure
750 if (lvl == tree%highest_lvl) then
751 call af_gc_lvl(tree, lvl, [mg%i_phi])
752 end if
753
754 !$omp parallel do private(id, p_id)
755 do i = 1, size(tree%lvls(lvl)%ids)
756 id = tree%lvls(lvl)%ids(i)
757 p_id = tree%boxes(id)%parent
758
759 call residual_box(tree%boxes(id), mg)
760 call mg%box_rstr(tree%boxes(id), tree%boxes(p_id), mg%i_tmp, mg)
761 call mg%box_rstr(tree%boxes(id), tree%boxes(p_id), mg%i_phi, mg)
762 end do
763 !$omp end parallel do
764
765 call af_gc_lvl(tree, lvl-1, [mg%i_phi])
766
767 ! Set rhs_c = laplacian(phi_c) + restrict(res) where it is refined
768
769 !$omp parallel do private(id)
770 do i = 1, size(tree%lvls(lvl-1)%parents)
771 id = tree%lvls(lvl-1)%parents(i)
772 call mg%box_op(tree%boxes(id), mg%i_rhs, mg)
773 call af_box_add_cc(tree%boxes(id), mg%i_tmp, mg%i_rhs)
774 end do
775 !$omp end parallel do
776 end subroutine set_coarse_phi_rhs
777
778 !> Set the initial guess for phi and restrict the rhs
779 subroutine init_phi_rhs(tree, mg)
780 use m_af_utils, only: af_box_clear_cc
781 type(af_t), intent(inout) :: tree !< Full grid
782 type(mg_t), intent(in) :: mg !< Multigrid options
783 integer :: i, id, p_id, lvl
784
785 !$omp parallel private(lvl, i, id, p_id)
786 do lvl = tree%highest_lvl, 1+1, -1
787 !$omp do
788 do i = 1, size(tree%lvls(lvl)%ids)
789 id = tree%lvls(lvl)%ids(i)
790 call af_box_clear_cc(tree%boxes(id), mg%i_phi)
791 if (lvl > 1) then
792 p_id = tree%boxes(id)%parent
793 call mg%box_rstr(tree%boxes(id), tree%boxes(p_id), mg%i_rhs, mg)
794 end if
795 end do
796 !$omp end do
797 end do
798 !$omp end parallel
799 end subroutine init_phi_rhs
800
801 subroutine residual_box(box, mg)
802 type(box_t), intent(inout) :: box !< Operate on this box
803 type(mg_t), intent(in) :: mg !< Multigrid options
804 integer :: nc
805
806 call mg%box_op(box, mg%i_tmp, mg)
807 nc = box%n_cell
808 box%cc(dtimes(1:nc), mg%i_tmp) = box%cc(dtimes(1:nc), mg%i_rhs) &
809 - box%cc(dtimes(1:nc), mg%i_tmp)
810 end subroutine residual_box
811
812 !> Based on the box type, apply a Gauss-Seidel relaxation scheme
813 subroutine mg_auto_gsrb(box, redblack_cntr, mg)
814 type(box_t), intent(inout) :: box !< Box to operate on
815 integer, intent(in) :: redblack_cntr !< Iteration count
816 type(mg_t), intent(in) :: mg !< Multigrid options
817
818 call af_stencil_gsrb_box(box, mg%operator_key, redblack_cntr, &
819 mg%i_phi, mg%i_rhs)
820 end subroutine mg_auto_gsrb
821
822 !> Store operator stencil for a box
823 subroutine mg_store_operator_stencil(box, mg, ix)
824 type(box_t), intent(inout) :: box
825 type(mg_t), intent(in) :: mg
826 !> Index of the stencil. If equal to af_stencil_none, store a new stencil
827 integer, intent(inout) :: ix
828
829 if (ix == af_stencil_none) then
830 call af_stencil_prepare_store(box, mg%operator_key, ix)
831 end if
832
833 select case (mg%operator_type)
834 case (mg_normal_operator)
835 call mg_box_lpl_stencil(box, mg, ix)
836 case (mg_lsf_operator)
837 call mg_box_lsf_stencil(box, mg, ix)
838 case (mg_eps_operator)
839 call mg_box_lpld_stencil(box, mg, ix)
840 case (mg_auto_operator)
841 ! Use box tag to set operator
842 select case (iand(box%tag, mg%operator_mask))
843 case (mg_normal_box)
844 call mg_box_lpl_stencil(box, mg, ix)
845 case (mg_lsf_box)
846 call mg_box_lsf_stencil(box, mg, ix)
847 case (mg_veps_box, mg_ceps_box)
848 call mg_box_lpld_stencil(box, mg, ix)
849 case (mg_veps_box+mg_lsf_box, mg_ceps_box+mg_lsf_box)
850 call mg_box_lpld_lsf_stencil(box, mg, ix)
851 case default
852 error stop "mg_store_operator_stencil: unknown box tag"
853 end select
854 case default
855 error stop "mg_store_operator_stencil: unknown mg%operator_type"
856 end select
857
858 call af_stencil_check_box(box, mg%operator_key, ix)
859 end subroutine mg_store_operator_stencil
860
861 !> Store prolongation stencil for a box
862 subroutine mg_store_prolongation_stencil(tree, id, mg)
863 type(af_t), intent(inout) :: tree
864 integer, intent(in) :: id !< Id of box
865 type(mg_t), intent(in) :: mg
866 integer :: ix, p_id
867
868 call af_stencil_prepare_store(tree%boxes(id), mg%prolongation_key, ix)
869
870 p_id = tree%boxes(id)%parent
871 if (p_id <= af_no_box) error stop "Box does not have parent"
872
873 select case (mg%prolongation_type)
874 case (mg_prolong_linear)
875 call mg_box_prolong_linear_stencil(tree%boxes(id), &
876 tree%boxes(p_id), mg, ix)
877 case (mg_prolong_sparse)
878 call mg_box_prolong_sparse_stencil(tree%boxes(id), &
879 tree%boxes(p_id), mg, ix)
880 case (mg_prolong_auto)
881 ! Use box tag
882 associate(box=>tree%boxes(id), box_p=>tree%boxes(p_id))
883 select case (iand(box%tag, mg%operator_mask))
884 case (mg_normal_box, mg_ceps_box)
885 call mg_box_prolong_linear_stencil(box, box_p, mg, ix)
886 case (mg_lsf_box, mg_ceps_box+mg_lsf_box)
887 if (mg%lsf_use_custom_prolongation) then
888 call mg_box_prolong_lsf_stencil(box, box_p, mg, ix)
889 else
890 call mg_box_prolong_linear_stencil(box, box_p, mg, ix)
891 end if
892 case (mg_veps_box, mg_veps_box + mg_lsf_box)
893 call mg_box_prolong_eps_stencil(box, box_p, mg, ix)
894 case default
895 error stop "mg_store_prolongation_stencil: unknown box tag"
896 end select
897 end associate
898 case default
899 error stop "mg_store_prolongation_stencil: unknown mg%prolongation_type"
900 end select
901
902 call af_stencil_check_box(tree%boxes(id), mg%prolongation_key, ix)
903 end subroutine mg_store_prolongation_stencil
904
905 !> Based on the box type, apply the approriate operator
906 subroutine mg_auto_op(box, i_out, mg)
907 type(box_t), intent(inout) :: box !< Operate on this box
908 integer, intent(in) :: i_out !< Index of output variable
909 type(mg_t), intent(in) :: mg !< Multigrid options
910
911 call af_stencil_apply_box(box, mg%operator_key, mg%i_phi, i_out)
912 end subroutine mg_auto_op
913
914 !> Restriction operator
915 subroutine mg_auto_rstr(box_c, box_p, iv, mg)
916 type(box_t), intent(in) :: box_c !< Child box
917 type(box_t), intent(inout) :: box_p !< Parent box
918 integer, intent(in) :: iv !< Index of variable
919 type(mg_t), intent(in) :: mg !< Multigrid options
920
921 ! Always apply standard restriction
922 call mg_box_rstr_lpl(box_c, box_p, iv, mg)
923 end subroutine mg_auto_rstr
924
925 !> Set ghost cells near refinement boundaries
926 subroutine mg_auto_rb(boxes, id, nb, iv, op_mask)
927 type(box_t), intent(inout) :: boxes(:) !< List of all boxes
928 integer, intent(in) :: id !< Id of box
929 integer, intent(in) :: nb !< Ghost cell direction
930 integer, intent(in) :: iv !< Ghost cell variable
931 integer, intent(in) :: op_mask !< Operator mask
932
933 select case (iand(boxes(id)%tag, op_mask))
934 case (mg_veps_box)
935 ! With a variable coefficients, use local extrapolation for ghost cells
936 call mg_sides_rb_extrap(boxes, id, nb, iv)
937 case default
938 call mg_sides_rb(boxes, id, nb, iv)
939 end select
940 end subroutine mg_auto_rb
941
942 !> Based on the box type, correct the solution of the children
943 subroutine mg_auto_corr(box_p, box_c, mg)
944 type(box_t), intent(inout) :: box_c !< Child box
945 type(box_t), intent(in) :: box_p !< Parent box
946 type(mg_t), intent(in) :: mg !< Multigrid options
947
948 call af_stencil_prolong_box(box_p, box_c, mg%prolongation_key, &
949 mg%i_tmp, mg%i_phi, .true.)
950 end subroutine mg_auto_corr
951
952 !> Check where the level-set function could have a root by computing the
953 !> numerical gradient
954 subroutine get_possible_lsf_root_mask(box, nc, dmax, mg, root_mask)
955 type(box_t), intent(in) :: box !< Box to operate on
956 real(dp), intent(in) :: dmax !< Maximal distance to consider
957 integer, intent(in) :: nc !< Box size
958 type(mg_t), intent(in) :: mg !< Multigrid options
959 !> Whether there could be a root
960 logical, intent(out) :: root_mask(DTIMES(nc))
961 integer :: IJK
962 real(dp) :: gradnorm, rr(NDIM)
963
964 if (.not. associated(mg%lsf)) error stop "mg%lsf not set"
965
966 ! Compute gradient
967 do kji_do(1, nc)
968 rr = af_r_cc(box, [ijk])
969 gradnorm = norm2(numerical_gradient(mg%lsf, rr))
970 root_mask(ijk) = (abs(box%cc(ijk, mg%i_lsf)) < dmax * gradnorm * &
971 mg%lsf_gradient_safety_factor)
972 end do; close_do
973 end subroutine get_possible_lsf_root_mask
974
975 !> Check if the level set function could have zeros, and store distances for
976 !> the neighboring cells
977 subroutine store_lsf_distance_matrix(box, nc, mg, boundary)
978 type(box_t), intent(inout) :: box !< Box to operate on
979 integer, intent(in) :: nc !< Box size
980 type(mg_t), intent(in) :: mg !< Multigrid options
981 logical, intent(out) :: boundary !< Whether a boundary is found
982 logical :: root_mask(DTIMES(nc))
983 real(dp) :: dd(2*NDIM), a(NDIM)
984 integer :: ixs(NDIM, nc**NDIM), IJK, ix, n
985 integer :: n_mask, m
986 real(dp) :: v(2*NDIM, nc**NDIM)
987#if NDIM > 1
988 integer :: nb, dim, i_step, n_steps
989 real(dp) :: dist, gradient(NDIM), dvec(NDIM)
990 real(dp) :: x(NDIM), step_size(NDIM), min_dr
991
992 min_dr = minval(box%dr)
993#endif
994
995 n = 0
996 boundary = .false.
997
998 call get_possible_lsf_root_mask(box, nc, norm2(box%dr), mg, root_mask)
999 n_mask = count(root_mask)
1000
1001 if (n_mask > 0) then
1002 ! Store mask of where potential roots are
1003 call af_stencil_prepare_store(box, mg_lsf_mask_key, ix)
1004 box%stencils(ix)%stype = stencil_sparse
1005 box%stencils(ix)%shape = af_stencil_mask
1006 call af_stencil_allocate_coeff(box%stencils(ix), box%n_cell, &
1007 n_sparse=n_mask)
1008
1009 m = 0
1010 do kji_do(1, nc)
1011 if (root_mask(ijk)) then
1012 m = m + 1
1013 box%stencils(ix)%sparse_ix(:, m) = [ijk]
1014 end if
1015 end do; close_do
1016 else
1017 return
1018 end if
1019
1020 ! Compute distances
1021 do kji_do(1, nc)
1022 if (root_mask(ijk)) then
1023 a = af_r_cc(box, [ijk])
1024#if NDIM == 1
1025 dd(1) = mg%lsf_dist(a, af_r_cc(box, [i-1]), mg)
1026 dd(2) = mg%lsf_dist(a, af_r_cc(box, [i+1]), mg)
1027#elif NDIM == 2
1028 dd(1) = mg%lsf_dist(a, af_r_cc(box, [i-1, j]), mg)
1029 dd(2) = mg%lsf_dist(a, af_r_cc(box, [i+1, j]), mg)
1030 dd(3) = mg%lsf_dist(a, af_r_cc(box, [i, j-1]), mg)
1031 dd(4) = mg%lsf_dist(a, af_r_cc(box, [i, j+1]), mg)
1032#elif NDIM == 3
1033 dd(1) = mg%lsf_dist(a, af_r_cc(box, [i-1, j, k]), mg)
1034 dd(2) = mg%lsf_dist(a, af_r_cc(box, [i+1, j, k]), mg)
1035 dd(3) = mg%lsf_dist(a, af_r_cc(box, [i, j-1, k]), mg)
1036 dd(4) = mg%lsf_dist(a, af_r_cc(box, [i, j+1, k]), mg)
1037 dd(5) = mg%lsf_dist(a, af_r_cc(box, [i, j, k-1]), mg)
1038 dd(6) = mg%lsf_dist(a, af_r_cc(box, [i, j, k+1]), mg)
1039#endif
1040
1041#if NDIM > 1
1042 ! If no boundaries are found, search along the gradient of the level
1043 ! set function to check if there is a boundary nearby
1044 if (min_dr > mg%lsf_length_scale .and. all(dd >= 1)) then
1045 n_steps = ceiling(min_dr/mg%lsf_length_scale)
1046 step_size = sign(mg%lsf_length_scale, box%cc(ijk, mg%i_lsf))
1047 x = a
1048
1049 do i_step = 1, n_steps
1050 gradient = numerical_gradient(mg%lsf, x)
1051 gradient = gradient/max(norm2(gradient), 1e-50_dp) ! unit vector
1052
1053 ! Search in direction towards zero
1054 x = x - gradient * step_size
1055 if (mg%lsf(x) * box%cc(ijk, mg%i_lsf) <= 0) exit
1056 end do
1057
1058 dist = mg%lsf_dist(a, x, mg)
1059
1060 if (dist < 1) then
1061 ! Rescale
1062 dist = dist * norm2(x - a)/min_dr
1063 dvec = x - a
1064
1065 ! Select closest direction
1066 dim = maxloc(abs(dvec), dim=1)
1067 nb = 2 * dim - 1
1068 if (dvec(dim) > 0) nb = nb + 1
1069
1070 dd(nb) = dist
1071 end if
1072 end if
1073#endif
1074 else
1075 dd(:) = 1.0_dp
1076 end if
1077
1078 ! Only store distances for boundaries
1079 if (any(dd < 1.0_dp)) then
1080 n = n + 1
1081 ixs(:, n) = [ijk]
1082 v(:, n) = dd
1083 end if
1084 end do; close_do
1085
1086 if (n > 0) then
1087 boundary = .true.
1088 call af_stencil_prepare_store(box, mg_lsf_distance_key, ix)
1089 box%stencils(ix)%stype = stencil_sparse
1090 box%stencils(ix)%shape = af_stencil_246
1091 call af_stencil_allocate_coeff(box%stencils(ix), box%n_cell, &
1092 n_sparse=n)
1093 box%stencils(ix)%sparse_ix(:, :) = ixs(:, 1:n)
1094 box%stencils(ix)%sparse_v(:, :) = v(:, 1:n)
1095 end if
1096
1097 end subroutine store_lsf_distance_matrix
1098
1099 !> Set tag (box type) for a box in the tree
1100 subroutine mg_set_box_tag(tree, id, mg, new_lsf, new_eps)
1101 type(af_t), intent(inout) :: tree
1102 integer, intent(in) :: id
1103 type(mg_t), intent(in) :: mg
1104 logical, intent(in) :: new_lsf !< Whether the lsf has changed
1105 logical, intent(in) :: new_eps !< Whether epsilon has changed
1106 logical :: has_lsf, update_lsf, update_eps
1107 real(dp) :: a, b
1108
1109 associate(box => tree%boxes(id))
1110 if (box%tag == af_init_tag) then
1111 box%tag = mg_normal_box
1112 update_lsf = .true.
1113 update_eps = .true.
1114 else
1115 update_lsf = new_lsf
1116 update_eps = new_eps
1117
1118 ! Remove flags for updated variables
1119 if (new_lsf) box%tag = iand(box%tag, not(mg_lsf_box))
1120 if (new_eps) box%tag = iand(box%tag, not(ior(mg_veps_box, mg_ceps_box)))
1121 end if
1122
1123 if (tree%mg_i_lsf /= -1 .and. update_lsf) then
1124 call store_lsf_distance_matrix(box, box%n_cell, mg, has_lsf)
1125 if (has_lsf) then
1126 ! Add bits indicating a level set function
1127 box%tag = box%tag + mg_lsf_box
1128 end if
1129 end if
1130
1131 if (tree%mg_i_eps /= -1 .and. update_eps) then
1132 a = minval(box%cc(dtimes(:), tree%mg_i_eps))
1133 b = maxval(box%cc(dtimes(:), tree%mg_i_eps))
1134
1135 if (b > a) then
1136 ! Variable coefficient
1137 box%tag = box%tag + mg_veps_box
1138 else if (maxval(abs([a, b] - 1)) > 1e-8_dp) then
1139 ! Constant coefficient (but not equal to one)
1140 box%tag = box%tag + mg_ceps_box
1141 end if
1142 end if
1143 end associate
1144
1145 end subroutine mg_set_box_tag
1146
1147 subroutine mg_set_operators_lvl(tree, mg, lvl)
1148 type(af_t), intent(inout) :: tree
1149 type(mg_t), intent(in) :: mg
1150 integer, intent(in) :: lvl
1151 integer :: i, id, n
1152
1153 !$omp parallel do private(i, id, n)
1154 do i = 1, size(tree%lvls(lvl)%ids)
1155 id = tree%lvls(lvl)%ids(i)
1156 associate(box => tree%boxes(id))
1157
1158 if (box%tag == af_init_tag) then
1159 ! We could use the tag of the parent box, but sometimes a part of a
1160 ! sharp feature can only be detected in the children
1161 call mg_set_box_tag(tree, id, mg, .true., .true.)
1162 end if
1163
1164 ! Check if stencils are available
1165 n = af_stencil_index(box, mg%operator_key)
1166 if (n == af_stencil_none) then
1167 call mg_store_operator_stencil(box, mg, n)
1168 end if
1169
1170 ! Compute right-hand side correction for internal boundaries
1171 if (allocated(box%stencils(n)%f)) then
1172 box%stencils(n)%bc_correction = box%stencils(n)%f * &
1173 mg_lsf_boundary_value(box, mg)
1174 end if
1175
1176 if (lvl > 1) then
1177 n = af_stencil_index(box, mg%prolongation_key)
1178 if (n == af_stencil_none) then
1179 call mg_store_prolongation_stencil(tree, id, mg)
1180 end if
1181 end if
1182 end associate
1183 end do
1184 !$omp end parallel do
1185 end subroutine mg_set_operators_lvl
1186
1187 !> Update stencil for an operator, because (part of) the coefficients have changed
1188 subroutine mg_update_operator_stencil(tree, mg, new_lsf, new_eps)
1189 type(af_t), intent(inout) :: tree
1190 type(mg_t), intent(inout) :: mg
1191 logical, intent(in) :: new_lsf !< Whether the lsf has changed
1192 logical, intent(in) :: new_eps !< Whether epsilon has changed
1193 integer :: lvl, i, id, n
1194
1195 !$omp parallel private(lvl, i, id, n)
1196 do lvl = 1, tree%highest_lvl
1197 !$omp do
1198 do i = 1, size(tree%lvls(lvl)%ids)
1199 id = tree%lvls(lvl)%ids(i)
1200 associate(box => tree%boxes(id))
1201 n = af_stencil_index(box, mg%operator_key)
1202
1203 ! Update box tag
1204 call mg_set_box_tag(tree, id, mg, new_lsf, new_eps)
1205
1206 call mg_store_operator_stencil(box, mg, n)
1207 end associate
1208 end do
1209 !$omp end do
1210 end do
1211 !$omp end parallel
1212
1213 call coarse_solver_update_matrix(tree, mg)
1214 end subroutine mg_update_operator_stencil
1215
1216 subroutine mg_set_operators_tree(tree, mg)
1217 type(af_t), intent(inout) :: tree
1218 type(mg_t), intent(in) :: mg
1219 integer :: lvl
1220
1221 do lvl = 1, tree%highest_lvl
1222 call mg_set_operators_lvl(tree, mg, lvl)
1223 end do
1224 end subroutine mg_set_operators_tree
1225
1226 !> Restriction of child box (box_c) to its parent (box_p)
1227 subroutine mg_box_rstr_lpl(box_c, box_p, iv, mg)
1228 use m_af_restrict, only: af_restrict_box
1229 type(box_t), intent(in) :: box_c !< Child box to restrict
1230 type(box_t), intent(inout) :: box_p !< Parent box to restrict to
1231 integer, intent(in) :: iv !< Variable to restrict
1232 type(mg_t), intent(in) :: mg !< Multigrid options
1233
1234 if (iv == mg%i_phi) then
1235 ! Don't use geometry for restriction, since this is inconsistent with the
1236 ! filling of ghost cells near refinement boundaries
1237 call af_restrict_box(box_c, box_p, [iv], use_geometry=.false.)
1238 else
1239 ! For the right-hand side, use the geometry
1240 call af_restrict_box(box_c, box_p, [iv], use_geometry=.true.)
1241 end if
1242 end subroutine mg_box_rstr_lpl
1243
1244 !> Store the matrix stencil for each cell of the box. The order of the stencil
1245 !> is (i, j), (i-1, j), (i+1, j), (i, j-1), (i, j+1) (e.g., -4, 1, 1, 1, 1)
1246 subroutine mg_box_lpl_stencil(box, mg, ix)
1247 type(box_t), intent(inout) :: box
1248 type(mg_t), intent(in) :: mg
1249 integer, intent(in) :: ix !< Stencil index
1250 real(dp) :: inv_dr2(NDIM)
1251 integer :: idim
1252
1253 box%stencils(ix)%shape = af_stencil_357
1254 box%stencils(ix)%stype = stencil_constant
1255 box%stencils(ix)%cylindrical_gradient = (box%coord_t == af_cyl)
1256 inv_dr2 = 1 / box%dr**2
1257 call af_stencil_allocate_coeff(box%stencils(ix), box%n_cell)
1258
1259 do idim = 1, ndim
1260 box%stencils(ix)%c(2*idim:2*idim+1) = inv_dr2(idim)
1261 end do
1262 box%stencils(ix)%c(1) = -sum(box%stencils(ix)%c(2:)) - mg%helmholtz_lambda
1263
1264 end subroutine mg_box_lpl_stencil
1265
1266 !> Store linear prolongation stencil for standard Laplacian
1267 subroutine mg_box_prolong_linear_stencil(box, box_p, mg, ix)
1268 type(box_t), intent(inout) :: box !< Current box
1269 type(box_t), intent(in) :: box_p !< Parent box
1270 type(mg_t), intent(in) :: mg
1271 integer, intent(in) :: ix !< Stencil index
1272
1273 box%stencils(ix)%shape = af_stencil_p248
1274 box%stencils(ix)%stype = stencil_constant
1275 call af_stencil_allocate_coeff(box%stencils(ix), box%n_cell)
1276
1277#if NDIM == 1
1278 box%stencils(ix)%c(:) = [0.75_dp, 0.25_dp]
1279#elif NDIM == 2
1280 box%stencils(ix)%c(:) = [9, 3, 3, 1] / 16.0_dp
1281#elif NDIM == 3
1282 box%stencils(ix)%c(:) = [27, 9, 9, 3, 9, 3, 3, 1] / 64.0_dp
1283#endif
1284 end subroutine mg_box_prolong_linear_stencil
1285
1286 !> Store sparse linear prolongation stencil for standard Laplacian
1287 subroutine mg_box_prolong_sparse_stencil(box, box_p, mg, ix)
1288 type(box_t), intent(inout) :: box !< Current box
1289 type(box_t), intent(in) :: box_p !< Parent box
1290 type(mg_t), intent(in) :: mg
1291 integer, intent(in) :: ix !< Stencil index
1292
1293 box%stencils(ix)%shape = af_stencil_p234
1294 box%stencils(ix)%stype = stencil_constant
1295 call af_stencil_allocate_coeff(box%stencils(ix), box%n_cell)
1296
1297#if NDIM == 1
1298 box%stencils(ix)%c(:) = [0.75_dp, 0.25_dp]
1299#elif NDIM == 2
1300 box%stencils(ix)%c(:) = [0.5_dp, 0.25_dp, 0.25_dp]
1301#elif NDIM == 3
1302 box%stencils(ix)%c(:) = [0.25_dp, 0.25_dp, 0.25_dp, 0.25_dp]
1303#endif
1304 end subroutine mg_box_prolong_sparse_stencil
1305
1306 !> Store prolongation stencil for standard Laplacian with variable coefficient
1307 !> that can jump at cell faces
1308 subroutine mg_box_prolong_eps_stencil(box, box_p, mg, ix)
1309 type(box_t), intent(inout) :: box !< Current box
1310 type(box_t), intent(in) :: box_p !< Parent box
1311 type(mg_t), intent(in) :: mg
1312 integer, intent(in) :: ix !< Stencil index
1313 real(dp) :: a0, a(NDIM)
1314 integer :: i_eps, nc
1315 logical :: success
1316 integer :: IJK, IJK_(c1)
1317 integer :: IJK_(c2), ix_offset(NDIM)
1318#if NDIM == 3
1319 real(dp), parameter :: third = 1/3.0_dp
1320#endif
1321
1322 nc = box%n_cell
1323 box%stencils(ix)%shape = af_stencil_p234
1324 box%stencils(ix)%stype = stencil_variable
1325 ix_offset = af_get_child_offset(box)
1326 call af_stencil_allocate_coeff(box%stencils(ix), box%n_cell)
1327
1328 i_eps = mg%i_eps
1329
1330 associate(v => box%stencils(ix)%v)
1331 ! In these loops, we calculate the closest coarse index (_c1), and the
1332 ! one-but-closest (_c2). The fine cell lies in between.
1333#if NDIM == 1
1334 do i = 1, nc
1335 i_c1 = ix_offset(1) + ishft(i+1, -1) ! (i+1)/2
1336 i_c2 = i_c1 + 1 - 2 * iand(i, 1) ! even: +1, odd: -1
1337
1338 a0 = box_p%cc(i_c1, i_eps)
1339 a(1) = box_p%cc(i_c2, i_eps)
1340
1341 ! Get value of phi at coarse cell faces, and average
1342 v(:, ijk) = [a0, a(1)] / (a0 + a(1))
1343 end do
1344#elif NDIM == 2
1345 do j = 1, nc
1346 j_c1 = ix_offset(2) + ishft(j+1, -1) ! (j+1)/2
1347 j_c2 = j_c1 + 1 - 2 * iand(j, 1) ! even: +1, odd: -1
1348 do i = 1, nc
1349 i_c1 = ix_offset(1) + ishft(i+1, -1) ! (i+1)/2
1350 i_c2 = i_c1 + 1 - 2 * iand(i, 1) ! even: +1, odd: -1
1351
1352 a0 = box_p%cc(i_c1, j_c1, i_eps)
1353 a(1) = box_p%cc(i_c2, j_c1, i_eps)
1354 a(2) = box_p%cc(i_c1, j_c2, i_eps)
1355
1356 ! Get value of phi at coarse cell faces, and average
1357 v(1, ijk) = 0.5_dp * sum(a0 / (a0 + a(:)))
1358 v(2:, ijk) = 0.5_dp * a(:) / (a0 + a(:))
1359 end do
1360 end do
1361#elif NDIM == 3
1362 do k = 1, nc
1363 k_c1 = ix_offset(3) + ishft(k+1, -1) ! (k+1)/2
1364 k_c2 = k_c1 + 1 - 2 * iand(k, 1) ! even: +1, odd: -1
1365 do j = 1, nc
1366 j_c1 = ix_offset(2) + ishft(j+1, -1) ! (j+1)/2
1367 j_c2 = j_c1 + 1 - 2 * iand(j, 1) ! even: +1, odd: -1
1368 do i = 1, nc
1369 i_c1 = ix_offset(1) + ishft(i+1, -1) ! (i+1)/2
1370 i_c2 = i_c1 + 1 - 2 * iand(i, 1) ! even: +1, odd: -1
1371
1372 a0 = box_p%cc(i_c1, j_c1, k_c1, i_eps)
1373 a(1) = box_p%cc(i_c2, j_c1, k_c1, i_eps)
1374 a(2) = box_p%cc(i_c1, j_c2, k_c1, i_eps)
1375 a(3) = box_p%cc(i_c1, j_c1, k_c2, i_eps)
1376
1377 ! Get value of phi at coarse cell faces, and average
1378 v(1, ijk) = third * sum((a0 - 0.5_dp * a(:))/(a0 + a(:)))
1379 v(2:, ijk) = 0.5_dp * a(:) / (a0 + a(:))
1380 end do
1381 end do
1382 end do
1383#endif
1384 end associate
1385
1386 call af_stencil_try_constant(box, ix, epsilon(1.0_dp), success)
1387
1388 end subroutine mg_box_prolong_eps_stencil
1389
1390 !> Store prolongation stencil for standard Laplacian with level set function
1391 !> for internal boundaries
1392 subroutine mg_box_prolong_lsf_stencil(box, box_p, mg, ix)
1393 type(box_t), intent(inout) :: box !< Current box
1394 type(box_t), intent(in) :: box_p !< Parent box
1395 type(mg_t), intent(in) :: mg
1396 integer, intent(in) :: ix !< Stencil index
1397 real(dp) :: dd(NDIM+1), a(NDIM)
1398 integer :: i_lsf, nc, ix_mask
1399 logical :: success
1400 integer :: IJK, IJK_(c1), n, n_mask
1401 integer :: IJK_(c2), ix_offset(NDIM)
1402
1403 nc = box%n_cell
1404 box%stencils(ix)%shape = af_stencil_p234
1405 box%stencils(ix)%stype = stencil_variable
1406 ix_offset = af_get_child_offset(box)
1407 i_lsf = mg%i_lsf
1408 call af_stencil_allocate_coeff(box%stencils(ix), box%n_cell)
1409
1410 ix_mask = af_stencil_index(box, mg_lsf_mask_key)
1411 if (ix_mask == af_stencil_none) error stop "No LSF root mask stored"
1412 n_mask = size(box%stencils(ix_mask)%sparse_ix, 2)
1413
1414 associate(v => box%stencils(ix)%v)
1415 ! In these loops, we calculate the closest coarse index (_c1), and the
1416 ! one-but-closest (_c2). The fine cell lies in between.
1417#if NDIM == 1
1418 v(1, :) = 0.75_dp
1419 v(2, :) = 0.25_dp
1420
1421 do n = 1, n_mask
1422 i = box%stencils(ix_mask)%sparse_ix(1, n)
1423 i_c1 = ix_offset(1) + ishft(i+1, -1) ! (i+1)/2
1424 i_c2 = i_c1 + 1 - 2 * iand(i, 1) ! even: +1, odd: -1
1425
1426 a = af_r_cc(box, [ijk])
1427 dd(1) = mg%lsf_dist(a, af_r_cc(box_p, [i_c1]), mg)
1428 dd(2) = mg%lsf_dist(a, af_r_cc(box_p, [i_c2]), mg)
1429
1430 v(:, ijk) = [3 * dd(2), dd(1)]
1431 v(:, ijk) = v(:, ijk) / sum(v(:, ijk))
1432 where (dd < 1) v(:, ijk) = 0
1433 end do
1434#elif NDIM == 2
1435 v(1, :, :) = 0.5_dp
1436 v(2, :, :) = 0.25_dp
1437 v(3, :, :) = 0.25_dp
1438
1439 do n = 1, n_mask
1440 i = box%stencils(ix_mask)%sparse_ix(1, n)
1441 j = box%stencils(ix_mask)%sparse_ix(2, n)
1442
1443 j_c1 = ix_offset(2) + ishft(j+1, -1) ! (j+1)/2
1444 j_c2 = j_c1 + 1 - 2 * iand(j, 1) ! even: +1, odd: -1
1445 i_c1 = ix_offset(1) + ishft(i+1, -1) ! (i+1)/2
1446 i_c2 = i_c1 + 1 - 2 * iand(i, 1) ! even: +1, odd: -1
1447
1448 a = af_r_cc(box, [ijk])
1449 dd(1) = mg%lsf_dist(a, af_r_cc(box_p, [i_c1, j_c1]), mg)
1450 dd(2) = mg%lsf_dist(a, af_r_cc(box_p, [i_c2, j_c1]), mg)
1451 dd(3) = mg%lsf_dist(a, af_r_cc(box_p, [i_c1, j_c2]), mg)
1452
1453 v(:, ijk) = [2 * dd(2) * dd(3), dd(1) * dd(3), dd(1) * dd(2)]
1454 v(:, ijk) = v(:, ijk) / sum(v(:, ijk))
1455 where (dd < 1) v(:, ijk) = 0
1456 end do
1457#elif NDIM == 3
1458 v(:, :, :, :) = 0.25_dp
1459
1460 do n = 1, n_mask
1461 i = box%stencils(ix_mask)%sparse_ix(1, n)
1462 j = box%stencils(ix_mask)%sparse_ix(2, n)
1463 k = box%stencils(ix_mask)%sparse_ix(3, n)
1464
1465 k_c1 = ix_offset(3) + ishft(k+1, -1) ! (k+1)/2
1466 k_c2 = k_c1 + 1 - 2 * iand(k, 1) ! even: +1, odd: -1
1467 j_c1 = ix_offset(2) + ishft(j+1, -1) ! (j+1)/2
1468 j_c2 = j_c1 + 1 - 2 * iand(j, 1) ! even: +1, odd: -1
1469 i_c1 = ix_offset(1) + ishft(i+1, -1) ! (i+1)/2
1470 i_c2 = i_c1 + 1 - 2 * iand(i, 1) ! even: +1, odd: -1
1471
1472 a = af_r_cc(box, [ijk])
1473 dd(1) = mg%lsf_dist(a, af_r_cc(box_p, [i_c1, j_c1, k_c1]), mg)
1474 dd(2) = mg%lsf_dist(a, af_r_cc(box_p, [i_c2, j_c1, k_c1]), mg)
1475 dd(3) = mg%lsf_dist(a, af_r_cc(box_p, [i_c1, j_c2, k_c1]), mg)
1476 dd(4) = mg%lsf_dist(a, af_r_cc(box_p, [i_c1, j_c1, k_c2]), mg)
1477
1478 v(:, ijk) = [dd(2) * dd(3) * dd(4), &
1479 dd(1) * dd(3) * dd(4), dd(1) * dd(2) * dd(4), &
1480 dd(1) * dd(2) * dd(3)]
1481 v(:, ijk) = v(:, ijk) / sum(v(:, ijk))
1482 where (dd < 1) v(:, ijk) = 0
1483 end do
1484#endif
1485 end associate
1486
1487 call af_stencil_try_constant(box, ix, epsilon(1.0_dp), success)
1488
1489 end subroutine mg_box_prolong_lsf_stencil
1490
1491 !> Store the matrix stencil for each cell of the box. The order of the stencil
1492 !> is (i, j), (i-1, j), (i+1, j), (i, j-1), (i, j+1) (e.g., -4, 1, 1, 1, 1)
1493 subroutine mg_box_lpld_stencil(box, mg, ix)
1494 type(box_t), intent(inout) :: box
1495 type(mg_t), intent(in) :: mg
1496 integer, intent(in) :: ix !< Index of the stencil
1497 integer :: IJK, nc
1498 real(dp) :: idr2(2*NDIM), a0, a(2*NDIM)
1499 logical :: success
1500
1501 nc = box%n_cell
1502 idr2(1:2*ndim:2) = 1/box%dr**2
1503 idr2(2:2*ndim:2) = idr2(1:2*ndim:2)
1504
1505 box%stencils(ix)%shape = af_stencil_357
1506 box%stencils(ix)%stype = stencil_variable
1507 box%stencils(ix)%cylindrical_gradient = (box%coord_t == af_cyl)
1508 call af_stencil_allocate_coeff(box%stencils(ix), box%n_cell)
1509
1510 associate(cc => box%cc, n => mg%i_phi, i_eps => mg%i_eps)
1511 do kji_do(1, nc)
1512#if NDIM == 1
1513 a0 = box%cc(i, i_eps)
1514 a(1:2) = box%cc(i-1:i+1:2, i_eps)
1515#elif NDIM == 2
1516 a0 = box%cc(i, j, i_eps)
1517 a(1:2) = box%cc(i-1:i+1:2, j, i_eps)
1518 a(3:4) = box%cc(i, j-1:j+1:2, i_eps)
1519#elif NDIM == 3
1520 a0 = box%cc(i, j, k, i_eps)
1521 a(1:2) = box%cc(i-1:i+1:2, j, k, i_eps)
1522 a(3:4) = box%cc(i, j-1:j+1:2, k, i_eps)
1523 a(5:6) = box%cc(i, j, k-1:k+1:2, i_eps)
1524#endif
1525 box%stencils(ix)%v(2:, ijk) = idr2 * 2 * a0*a(:)/(a0 + a(:))
1526 box%stencils(ix)%v(1, ijk) = -sum(box%stencils(ix)%v(2:, ijk))
1527 end do; close_do
1528 end associate
1529
1530 call af_stencil_try_constant(box, ix, epsilon(1.0_dp), success)
1531
1532 end subroutine mg_box_lpld_stencil
1533
1534 !> Store stencil for a box with variable coefficient and level set function
1535 subroutine mg_box_lpld_lsf_stencil(box, mg, ix)
1536 type(box_t), intent(inout) :: box
1537 type(mg_t), intent(in) :: mg
1538 integer, intent(in) :: ix !< Index of the stencil
1539 integer :: IJK, nc
1540 real(dp) :: idr2(2*NDIM), a0, a(2*NDIM), dr2(NDIM)
1541 integer :: n, m, idim, s_ix(NDIM), ix_dist
1542 real(dp) :: dd(2*NDIM)
1543 real(dp), allocatable :: all_distances(:, DTIMES(:))
1544 logical :: success
1545#if NDIM == 2
1546 real(dp) :: tmp
1547#endif
1548
1549 nc = box%n_cell
1550 idr2(1:2*ndim:2) = 1/box%dr**2
1551 idr2(2:2*ndim:2) = idr2(1:2*ndim:2)
1552 dr2 = box%dr**2
1553
1554 box%stencils(ix)%shape = af_stencil_357
1555 box%stencils(ix)%stype = stencil_variable
1556 ! Perform a custom correction in cylindrical coordinates
1557 box%stencils(ix)%cylindrical_gradient = .false.
1558 call af_stencil_allocate_coeff(box%stencils(ix), box%n_cell, use_f=.true.)
1559 allocate(all_distances(2*ndim, dtimes(nc)))
1560
1561 box%stencils(ix)%f = 0.0_dp
1562 ! Distance 1 indicates no boundary
1563 all_distances = 1.0_dp
1564
1565 ix_dist = af_stencil_index(box, mg_lsf_distance_key)
1566 if (ix_dist == af_stencil_none) error stop "No distances stored"
1567
1568 ! Use sparse storage of boundary distances to update all_distances
1569 do n = 1, size(box%stencils(ix_dist)%sparse_ix, 2)
1570 s_ix = box%stencils(ix_dist)%sparse_ix(:, n)
1571 all_distances(:, dindex(s_ix)) = &
1572 box%stencils(ix_dist)%sparse_v(:, n)
1573 end do
1574
1575 associate(cc => box%cc, n => mg%i_phi, i_eps => mg%i_eps)
1576 do kji_do(1, nc)
1577 dd = all_distances(:, ijk)
1578
1579#if NDIM == 1
1580 a0 = box%cc(i, i_eps)
1581 a(1:2) = box%cc(i-1:i+1:2, i_eps)
1582#elif NDIM == 2
1583 a0 = box%cc(i, j, i_eps)
1584 a(1:2) = box%cc(i-1:i+1:2, j, i_eps)
1585 a(3:4) = box%cc(i, j-1:j+1:2, i_eps)
1586#elif NDIM == 3
1587 a0 = box%cc(i, j, k, i_eps)
1588 a(1:2) = box%cc(i-1:i+1:2, j, k, i_eps)
1589 a(3:4) = box%cc(i, j-1:j+1:2, k, i_eps)
1590 a(5:6) = box%cc(i, j, k-1:k+1:2, i_eps)
1591#endif
1592 ! Generalized Laplacian for neighbors at distance dd * dx
1593 do idim = 1, ndim
1594 box%stencils(ix)%v(1+2*idim-1, ijk) = 1 / &
1595 (0.5_dp * dr2(idim) * (dd(2*idim-1) + dd(2*idim)) * &
1596 dd(2*idim-1))
1597 box%stencils(ix)%v(1+2*idim, ijk) = 1 / &
1598 (0.5_dp * dr2(idim) * (dd(2*idim-1) + dd(2*idim)) * &
1599 dd(2*idim))
1600 end do
1601
1602 ! Account for permittivity. If there is an electrode boundary, assume
1603 ! the permittivity is constant up to the boundary.
1604 where (dd(:) < 1.0_dp) a(:) = a0
1605
1606 box%stencils(ix)%v(2:, ijk) = box%stencils(ix)%v(2:, ijk) * &
1607 2 * a0*a(:)/(a0 + a(:))
1608
1609#if NDIM == 2
1610 if (box%coord_t == af_cyl) then
1611 ! Add correction for cylindrical coordinate system. This is a
1612 ! discretization of eps/r * d/dr phi
1613 tmp = a0/(box%dr(1) * (dd(1) + dd(2)) * af_cyl_radius_cc(box, i))
1614 box%stencils(ix)%v(2, ijk) = box%stencils(ix)%v(2, ijk) - tmp
1615 box%stencils(ix)%v(3, ijk) = box%stencils(ix)%v(3, ijk) + tmp
1616 end if
1617#endif
1618
1619 box%stencils(ix)%v(1, ijk) = -sum(box%stencils(ix)%v(2:, ijk))
1620
1621 ! Move internal boundaries to right-hand side
1622 do m = 1, 2 * ndim
1623 if (dd(m) < 1.0_dp) then
1624 box%stencils(ix)%f(ijk) = box%stencils(ix)%f(ijk) - &
1625 box%stencils(ix)%v(m+1, ijk)
1626 box%stencils(ix)%v(m+1, ijk) = 0.0_dp
1627 end if
1628 end do
1629 end do; close_do
1630 end associate
1631
1632 call af_stencil_try_constant(box, ix, epsilon(1.0_dp), success)
1633
1634 end subroutine mg_box_lpld_lsf_stencil
1635
1636 !> Compute distance to boundary starting at point a going to point b, in
1637 !> the range from [0, 1], with 1 meaning there is no boundary
1638 function mg_lsf_dist_linear(a, b, mg) result(dist)
1639 real(dp), intent(in) :: a(ndim) !< Start point
1640 real(dp), intent(in) :: b(ndim) !< End point
1641 type(mg_t), intent(in) :: mg
1642 real(dp) :: dist, lsf_a, lsf_b
1643
1644 lsf_a = mg%lsf(a)
1645 lsf_b = mg%lsf(b)
1646
1647 if (lsf_a * lsf_b < 0) then
1648 ! There is a boundary between the points
1649 dist = lsf_a / (lsf_a - lsf_b)
1650 dist = max(dist, mg%lsf_min_rel_distance)
1651 else
1652 dist = 1.0_dp
1653 end if
1654 end function mg_lsf_dist_linear
1655
1656 !> Find root of f in the interval [a, b]. If f(a) and f(b) have different
1657 !> signs, apply bisection directly. Else, first find the (assumed to be)
1658 !> unique local minimum/maximum to determine a bracket. Return relative
1659 !> location of root, or 1 if there is no root.
1660 function mg_lsf_dist_gss(a, b, mg) result(dist)
1661 real(dp), intent(in) :: a(ndim) !< Start point
1662 real(dp), intent(in) :: b(ndim) !< End point
1663 type(mg_t), intent(in) :: mg
1664 real(dp) :: bracket(ndim, 2), b_new(ndim)
1665 real(dp) :: dist, r_root(ndim), lsf_a, lsf_b
1666 integer, parameter :: max_iter = 100
1667
1668 lsf_a = mg%lsf(a)
1669 lsf_b = mg%lsf(b)
1670 dist = 1.0_dp
1671
1672 if (lsf_a * lsf_b <= 0) then
1673 r_root = bisection(mg%lsf, a, b, mg%lsf_tol, max_iter)
1674 else
1675 ! Determine bracket using Golden section search
1676 bracket = gss(mg%lsf, a, b, minimization=(lsf_a >= 0), &
1677 tol=mg%lsf_tol, find_bracket=.true.)
1678
1679 ! Take one of the endpoints of the bracket
1680 if (mg%lsf(bracket(:, 1)) * lsf_a <= 0) then
1681 b_new = bracket(:, 1)
1682 else
1683 b_new = bracket(:, 2)
1684 end if
1685
1686 if (mg%lsf(b_new) * lsf_a > 0) then
1687 return ! No root
1688 else
1689 r_root = bisection(mg%lsf, a, b_new, mg%lsf_tol, max_iter)
1690 end if
1691 end if
1692
1693 dist = norm2(r_root - a)/norm2(b-a)
1694 dist = max(dist, mg%lsf_min_rel_distance)
1695 end function mg_lsf_dist_gss
1696
1697 !> Simple bisection
1698 function bisection(f, in_a, in_b, tol, max_iter) result(c)
1699 procedure(mg_func_lsf) :: f
1700 real(dp), intent(in) :: in_a(ndim), in_b(ndim), tol
1701 integer, intent(in) :: max_iter
1702 real(dp) :: a(ndim), b(ndim), c(ndim), fc
1703 integer :: n
1704
1705 a = in_a
1706 b = in_b
1707
1708 do n = 1, max_iter
1709 c = 0.5 * (a + b)
1710 fc = f(c)
1711 if (0.5 * norm2(b-a) < tol .or. abs(fc) <= 0) exit
1712
1713 if (fc * f(a) >= 0) then
1714 a = c
1715 else
1716 b = c
1717 end if
1718 end do
1719 end function bisection
1720
1721 !> Golden-section search on a line between a and b. Given a function f with a
1722 !> single local minimum/maximum in the interval [a,b], gss returns a subset
1723 !> interval [c,d] that contains the minimum/maximum with d-c <= tol. Adapted
1724 !> from https://en.wikipedia.org/wiki/Golden-section_search
1725 function gss(f, in_a, in_b, minimization, tol, find_bracket) result(bracket)
1726 procedure(mg_func_lsf) :: f !< Function to minimize/maximize
1727 real(dp), intent(in) :: in_a(ndim) !< Start coordinate
1728 real(dp), intent(in) :: in_b(ndim) !< End coordinate
1729 !> Whether to perform minimization or maximization
1730 logical, intent(in) :: minimization
1731 real(dp), intent(in) :: tol !< Absolute tolerance
1732 logical, intent(in) :: find_bracket !< Whether to search for a bracket
1733 real(dp) :: bracket(ndim, 2)
1734 real(dp) :: a(ndim), b(ndim), c(ndim), d(ndim)
1735 real(dp) :: h(ndim), yc, yd, ya
1736 real(dp) :: invphi, invphi2
1737 integer :: n, k
1738
1739 invphi = (sqrt(5.0_dp) - 1) / 2 ! 1 / phi
1740 invphi2 = (3 - sqrt(5.0_dp)) / 2 ! 1 / phi^2
1741
1742 a = in_a
1743 b = in_b
1744 h = b - a
1745
1746 if (norm2(h) <= tol) then
1747 bracket(:, 1) = a
1748 bracket(:, 2) = b
1749 return
1750 end if
1751
1752 ! Required steps to achieve tolerance
1753 n = int(ceiling(log(tol / norm2(h)) / log(invphi)))
1754
1755 c = a + invphi2 * h
1756 d = a + invphi * h
1757 ya = f(a) ! To search for bracket
1758 yc = f(c)
1759 yd = f(d)
1760
1761 do k = 1, n-1
1762 if ((yc < yd) .eqv. minimization) then
1763 b = d
1764 d = c
1765 yd = yc
1766 h = invphi * h
1767 c = a + invphi2 * h
1768 yc = f(c)
1769 else
1770 a = c
1771 c = d
1772 yc = yd
1773 h = invphi * h
1774 d = a + invphi * h
1775 yd = f(d)
1776 end if
1777
1778 ! Exit early if we are only searching for a bracket
1779 if (find_bracket .and. ya * yc <= 0 .and. ya * yd <= 0) exit
1780 end do
1781
1782 if ((yc < yd) .eqv. minimization) then
1783 bracket(:, 1) = a
1784 bracket(:, 2) = d
1785 else
1786 bracket(:, 1) = c
1787 bracket(:, 2) = b
1788 end if
1789 end function gss
1790
1791 !> Store the matrix stencil for each cell of the box. The order of the stencil
1792 !> is (i, j), (i-1, j), (i+1, j), (i, j-1), (i, j+1) (e.g., -4, 1, 1, 1, 1)
1793 subroutine mg_box_lsf_stencil(box, mg, ix)
1794 type(box_t), intent(inout) :: box
1795 type(mg_t), intent(in) :: mg
1796 integer, intent(in) :: ix !< Index of stencil
1797 integer :: IJK, n, nc, idim
1798 integer :: s_ix(NDIM), ix_dist
1799 real(dp) :: dd(2*NDIM), dr2(NDIM)
1800 real(dp), allocatable :: all_distances(:, DTIMES(:))
1801#if NDIM == 2
1802 real(dp) :: tmp
1803#endif
1804
1805 nc = box%n_cell
1806 dr2 = box%dr**2
1807
1808 ix_dist = af_stencil_index(box, mg_lsf_distance_key)
1809 if (ix_dist == af_stencil_none) error stop "No distances stored"
1810
1811 ! Use stored distances to construct stencil
1812 box%stencils(ix)%shape = af_stencil_357
1813 box%stencils(ix)%stype = stencil_variable
1814 ! Perform a custom correction in cylindrical coordinates
1815 box%stencils(ix)%cylindrical_gradient = .false.
1816 call af_stencil_allocate_coeff(box%stencils(ix), box%n_cell, use_f=.true.)
1817 box%stencils(ix)%f = 0.0_dp
1818
1819 allocate(all_distances(2*ndim, dtimes(nc)))
1820
1821 ! Distance 1 indicates no boundary
1822 all_distances = 1.0_dp
1823
1824 ! Use sparse storage of boundary distances to update all_distances
1825 do n = 1, size(box%stencils(ix_dist)%sparse_ix, 2)
1826 s_ix = box%stencils(ix_dist)%sparse_ix(:, n)
1827 all_distances(:, dindex(s_ix)) = &
1828 box%stencils(ix_dist)%sparse_v(:, n)
1829 end do
1830
1831 do kji_do(1, nc)
1832 dd = all_distances(:, ijk)
1833
1834 ! Generalized Laplacian for neighbors at distance dd * dx
1835 do idim = 1, ndim
1836 box%stencils(ix)%v(1+2*idim-1, ijk) = 1 / &
1837 (0.5_dp * dr2(idim) * (dd(2*idim-1) + dd(2*idim)) * &
1838 dd(2*idim-1))
1839 box%stencils(ix)%v(1+2*idim, ijk) = 1 / &
1840 (0.5_dp * dr2(idim) * (dd(2*idim-1) + dd(2*idim)) * &
1841 dd(2*idim))
1842 end do
1843
1844#if NDIM == 2
1845 if (box%coord_t == af_cyl) then
1846 ! Add correction for cylindrical coordinate system. This is a
1847 ! discretization of 1/r d/dr phi.
1848 tmp = 1/(box%dr(1) * (dd(1) + dd(2)) * af_cyl_radius_cc(box, i))
1849 box%stencils(ix)%v(2, ijk) = box%stencils(ix)%v(2, ijk) - tmp
1850 box%stencils(ix)%v(3, ijk) = box%stencils(ix)%v(3, ijk) + tmp
1851 end if
1852#endif
1853 box%stencils(ix)%v(1, ijk) = -sum(box%stencils(ix)%v(2:, ijk))
1854
1855 ! Move internal boundaries to right-hand side
1856 do n = 1, 2 * ndim
1857 if (dd(n) < 1.0_dp) then
1858 box%stencils(ix)%f(ijk) = box%stencils(ix)%f(ijk) - &
1859 box%stencils(ix)%v(n+1, ijk)
1860 box%stencils(ix)%v(n+1, ijk) = 0.0_dp
1861 end if
1862 end do
1863 end do; close_do
1864
1865 end subroutine mg_box_lsf_stencil
1866
1867 !> Compute the gradient of the potential and store in face-centered variables
1868 subroutine mg_compute_phi_gradient(tree, mg, i_fc, fac, i_norm)
1869 type(af_t), intent(inout) :: tree
1870 type(mg_t), intent(in) :: mg
1871 integer, intent(in) :: i_fc !< Face-centered indices
1872 real(dp), intent(in) :: fac !< Multiply with this factor
1873 !> If present, store norm in this cell-centered variable
1874 integer, intent(in), optional :: i_norm
1875 integer :: lvl, i, id
1876
1877 !$omp parallel private(lvl, i, id)
1878 do lvl = 1, tree%highest_lvl
1879 !$omp do
1880 do i = 1, size(tree%lvls(lvl)%ids)
1881 id = tree%lvls(lvl)%ids(i)
1882 select case (iand(tree%boxes(id)%tag, mg%operator_mask))
1883 case (mg_normal_box, mg_ceps_box)
1884 call mg_box_lpl_gradient(tree, id, mg, i_fc, fac)
1885 case (mg_lsf_box, mg_ceps_box+mg_lsf_box, mg_veps_box+mg_lsf_box)
1886 ! @todo is this okay for the case mg_veps_box+mg_lsf_box?
1887 if (af_has_children(tree%boxes(id))) then
1888 !> @todo Solution on coarse grid can lead to large gradient due
1889 !> to inconsistencies with level set function
1890 call mg_box_lpl_gradient(tree, id, mg, i_fc, fac)
1891 else
1892 call mg_box_lpllsf_gradient(tree, id, mg, i_fc, fac)
1893 end if
1894 case (mg_veps_box)
1895 ! Should call surface_correct_field_fc afterwards
1896 call mg_box_lpl_gradient(tree, id, mg, i_fc, fac)
1897 case (af_init_tag)
1898 error stop "box tag not set"
1899 case default
1900 error stop "unknown box tag"
1901 end select
1902
1903 if (present(i_norm)) then
1904 call mg_box_field_norm(tree, id, i_fc, i_norm)
1905 end if
1906 end do
1907 !$omp end do nowait
1908 end do
1909 !$omp end parallel
1910 end subroutine mg_compute_phi_gradient
1911
1912 !> Compute the gradient of the potential
1913 subroutine mg_box_lpl_gradient(tree, id, mg, i_fc, fac)
1914 type(af_t), intent(inout) :: tree
1915 integer, intent(in) :: id
1916 type(mg_t), intent(in) :: mg
1917 integer, intent(in) :: i_fc !< Face-centered indices
1918 real(dp), intent(in) :: fac !< Multiply with this factor
1919 integer :: nc, i_phi, i_eps
1920 real(dp) :: inv_dr(ndim)
1921
1922 associate(box => tree%boxes(id), cc => tree%boxes(id)%cc)
1923 nc = box%n_cell
1924 i_phi = mg%i_phi
1925 inv_dr = fac / box%dr
1926
1927#if NDIM == 1
1928 box%fc(1:nc+1, 1, i_fc) = inv_dr(1) * &
1929 (cc(1:nc+1, i_phi) - cc(0:nc, i_phi))
1930#elif NDIM == 2
1931 box%fc(1:nc+1, 1:nc, 1, i_fc) = inv_dr(1) * &
1932 (cc(1:nc+1, 1:nc, i_phi) - cc(0:nc, 1:nc, i_phi))
1933 box%fc(1:nc, 1:nc+1, 2, i_fc) = inv_dr(2) * &
1934 (cc(1:nc, 1:nc+1, i_phi) - cc(1:nc, 0:nc, i_phi))
1935#elif NDIM == 3
1936 box%fc(1:nc+1, 1:nc, 1:nc, 1, i_fc) = inv_dr(1) * &
1937 (cc(1:nc+1, 1:nc, 1:nc, i_phi) - &
1938 cc(0:nc, 1:nc, 1:nc, i_phi))
1939 box%fc(1:nc, 1:nc+1, 1:nc, 2, i_fc) = inv_dr(2) * &
1940 (cc(1:nc, 1:nc+1, 1:nc, i_phi) - &
1941 cc(1:nc, 0:nc, 1:nc, i_phi))
1942 box%fc(1:nc, 1:nc, 1:nc+1, 3, i_fc) = inv_dr(3) * &
1943 (cc(1:nc, 1:nc, 1:nc+1, i_phi) - &
1944 cc(1:nc, 1:nc, 0:nc, i_phi))
1945#endif
1946
1947 if (iand(box%tag, mg%operator_mask) == mg_veps_box) then
1948 ! Compute fields at the boundaries of the box, where eps can change
1949 i_eps = mg%i_eps
1950
1951#if NDIM == 1
1952 box%fc(1, 1, i_fc) = 2 * inv_dr(1) * &
1953 (cc(1, i_phi) - cc(0, i_phi)) * &
1954 cc(0, i_eps) / &
1955 (cc(1, i_eps) + cc(0, i_eps))
1956 box%fc(nc+1, 1, i_fc) = 2 * inv_dr(1) * &
1957 (cc(nc+1, i_phi) - cc(nc, i_phi)) * &
1958 cc(nc+1, i_eps) / &
1959 (cc(nc+1, i_eps) + cc(nc, i_eps))
1960#elif NDIM == 2
1961 box%fc(1, 1:nc, 1, i_fc) = 2 * inv_dr(1) * &
1962 (cc(1, 1:nc, i_phi) - cc(0, 1:nc, i_phi)) * &
1963 cc(0, 1:nc, i_eps) / &
1964 (cc(1, 1:nc, i_eps) + cc(0, 1:nc, i_eps))
1965 box%fc(nc+1, 1:nc, 1, i_fc) = 2 * inv_dr(1) * &
1966 (cc(nc+1, 1:nc, i_phi) - cc(nc, 1:nc, i_phi)) * &
1967 cc(nc+1, 1:nc, i_eps) / &
1968 (cc(nc+1, 1:nc, i_eps) + cc(nc, 1:nc, i_eps))
1969 box%fc(1:nc, 1, 2, i_fc) = 2 * inv_dr(2) * &
1970 (cc(1:nc, 1, i_phi) - cc(1:nc, 0, i_phi)) * &
1971 cc(1:nc, 0, i_eps) / &
1972 (cc(1:nc, 1, i_eps) + cc(1:nc, 0, i_eps))
1973 box%fc(1:nc, nc+1, 2, i_fc) = 2 * inv_dr(2) * &
1974 (cc(1:nc, nc+1, i_phi) - cc(1:nc, nc, i_phi)) * &
1975 cc(1:nc, nc+1, i_eps) / &
1976 (cc(1:nc, nc+1, i_eps) + cc(1:nc, nc, i_eps))
1977#elif NDIM == 3
1978 box%fc(1, 1:nc, 1:nc, 1, i_fc) = 2 * inv_dr(1) * &
1979 (cc(1, 1:nc, 1:nc, i_phi) - cc(0, 1:nc, 1:nc, i_phi)) * &
1980 cc(0, 1:nc, 1:nc, i_eps) / &
1981 (cc(1, 1:nc, 1:nc, i_eps) + cc(0, 1:nc, 1:nc, i_eps))
1982 box%fc(nc+1, 1:nc, 1:nc, 1, i_fc) = 2 * inv_dr(1) * &
1983 (cc(nc+1, 1:nc, 1:nc, i_phi) - cc(nc, 1:nc, 1:nc, i_phi)) * &
1984 cc(nc+1, 1:nc, 1:nc, i_eps) / &
1985 (cc(nc+1, 1:nc, 1:nc, i_eps) + cc(nc, 1:nc, 1:nc, i_eps))
1986 box%fc(1:nc, 1, 1:nc, 2, i_fc) = 2 * inv_dr(2) * &
1987 (cc(1:nc, 1, 1:nc, i_phi) - cc(1:nc, 0, 1:nc, i_phi)) * &
1988 cc(1:nc, 0, 1:nc, i_eps) / &
1989 (cc(1:nc, 1, 1:nc, i_eps) + cc(1:nc, 0, 1:nc, i_eps))
1990 box%fc(1:nc, nc+1, 1:nc, 2, i_fc) = 2 * inv_dr(2) * &
1991 (cc(1:nc, nc+1, 1:nc, i_phi) - cc(1:nc, nc, 1:nc, i_phi)) * &
1992 cc(1:nc, nc+1, 1:nc, i_eps) / &
1993 (cc(1:nc, nc+1, 1:nc, i_eps) + cc(1:nc, nc, 1:nc, i_eps))
1994 box%fc(1:nc, 1:nc, 1, 3, i_fc) = 2 * inv_dr(3) * &
1995 (cc(1:nc, 1:nc, 1, i_phi) - cc(1:nc, 1:nc, 0, i_phi)) * &
1996 cc(1:nc, 1:nc, 0, i_eps) / &
1997 (cc(1:nc, 1:nc, 1, i_eps) + cc(1:nc, 1:nc, 0, i_eps))
1998 box%fc(1:nc, 1:nc, nc+1, 3, i_fc) = 2 * inv_dr(3) * &
1999 (cc(1:nc, 1:nc, nc+1, i_phi) - cc(1:nc, 1:nc, nc, i_phi)) * &
2000 cc(1:nc, 1:nc, nc+1, i_eps) / &
2001 (cc(1:nc, 1:nc, nc+1, i_eps) + cc(1:nc, 1:nc, nc, i_eps))
2002#endif
2003 end if
2004 end associate
2005 end subroutine mg_box_lpl_gradient
2006
2007 !> Compute norm of face-centered variable
2008 subroutine mg_compute_field_norm(tree, i_fc, i_norm)
2009 type(af_t), intent(inout) :: tree
2010 integer, intent(in) :: i_fc !< Index of face-centered variable
2011 integer, intent(in) :: i_norm !< Index of cell-centered variable
2012 integer :: lvl, i, id
2013
2014 !$omp parallel private(lvl, i, id)
2015 do lvl = 1, tree%highest_lvl
2016 !$omp do
2017 do i = 1, size(tree%lvls(lvl)%ids)
2018 id = tree%lvls(lvl)%ids(i)
2019 call mg_box_field_norm(tree, id, i_fc, i_norm)
2020 end do
2021 !$omp end do nowait
2022 end do
2023 !$omp end parallel
2024 end subroutine mg_compute_field_norm
2025
2026 subroutine mg_box_field_norm(tree, id, i_fc, i_norm)
2027 type(af_t), intent(inout) :: tree
2028 integer, intent(in) :: id
2029 integer, intent(in) :: i_fc !< Face-centered indices
2030 !> Store norm in this cell-centered variable
2031 integer, intent(in) :: i_norm
2032 integer :: nc
2033
2034 associate(box => tree%boxes(id))
2035 nc = box%n_cell
2036#if NDIM == 1
2037 box%cc(1:nc, i_norm) = 0.5_dp * sqrt(&
2038 (box%fc(1:nc, 1, i_fc) + &
2039 box%fc(2:nc+1, 1, i_fc))**2)
2040#elif NDIM == 2
2041 box%cc(1:nc, 1:nc, i_norm) = 0.5_dp * sqrt(&
2042 (box%fc(1:nc, 1:nc, 1, i_fc) + &
2043 box%fc(2:nc+1, 1:nc, 1, i_fc))**2 + &
2044 (box%fc(1:nc, 1:nc, 2, i_fc) + &
2045 box%fc(1:nc, 2:nc+1, 2, i_fc))**2)
2046#elif NDIM == 3
2047 box%cc(1:nc, 1:nc, 1:nc, i_norm) = 0.5_dp * sqrt(&
2048 (box%fc(1:nc, 1:nc, 1:nc, 1, i_fc) + &
2049 box%fc(2:nc+1, 1:nc, 1:nc, 1, i_fc))**2 + &
2050 (box%fc(1:nc, 1:nc, 1:nc, 2, i_fc) + &
2051 box%fc(1:nc, 2:nc+1, 1:nc, 2, i_fc))**2 + &
2052 (box%fc(1:nc, 1:nc, 1:nc, 3, i_fc) + &
2053 box%fc(1:nc, 1:nc, 2:nc+1, 3, i_fc))**2)
2054#endif
2055 end associate
2056 end subroutine mg_box_field_norm
2057
2058 !> Compute the gradient of the potential with a level set function and store
2059 !> in face-centered variables. The gradients are computed from the positive
2060 !> side of the level set function.
2061 subroutine mg_box_lpllsf_gradient(tree, id, mg, i_fc, fac)
2062 type(af_t), intent(inout) :: tree
2063 integer, intent(in) :: id
2064 type(mg_t), intent(in) :: mg
2065 integer, intent(in) :: i_fc !< Face-centered indices
2066 real(dp), intent(in) :: fac !< Multiply with this factor
2067 integer :: IJK
2068 integer :: n, nc, i_phi, ix_dist
2069 real(dp) :: inv_dr(NDIM), dd(2*NDIM)
2070 real(dp) :: bc(DTIMES(tree%n_cell))
2071
2072 ! Compute regular values, correct part of them below
2073 call mg_box_lpl_gradient(tree, id, mg, i_fc, fac)
2074
2075 ix_dist = af_stencil_index(tree%boxes(id), mg_lsf_distance_key)
2076 if (ix_dist == af_stencil_none) error stop "No distances stored"
2077
2078 associate(box => tree%boxes(id), cc => tree%boxes(id)%cc)
2079 nc = box%n_cell
2080 i_phi = mg%i_phi
2081 inv_dr = fac / box%dr
2082 bc = mg_lsf_boundary_value(box, mg)
2083
2084 ! Use sparse storage of boundary distances
2085 do n = 1, size(box%stencils(ix_dist)%sparse_ix, 2)
2086 dd = box%stencils(ix_dist)%sparse_v(:, n)
2087
2088#if NDIM == 1
2089 i = box%stencils(ix_dist)%sparse_ix(1, n)
2090
2091 if (dd(1) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2092 box%fc(i, 1, i_fc) = inv_dr(1) * &
2093 (cc(i, i_phi) - bc(ijk)) / dd(1)
2094 end if
2095 if (dd(2) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2096 box%fc(i+1, 1, i_fc) = inv_dr(1) * &
2097 (bc(ijk) - cc(i, i_phi)) / dd(2)
2098 end if
2099#elif NDIM == 2
2100 i = box%stencils(ix_dist)%sparse_ix(1, n)
2101 j = box%stencils(ix_dist)%sparse_ix(2, n)
2102
2103 if (dd(1) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2104 box%fc(i, j, 1, i_fc) = inv_dr(1) * &
2105 (cc(i, j, i_phi) - bc(ijk)) / dd(1)
2106 end if
2107 if (dd(2) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2108 box%fc(i+1, j, 1, i_fc) = inv_dr(1) * &
2109 (bc(ijk) - cc(i, j, i_phi)) / dd(2)
2110 end if
2111 if (dd(3) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2112 box%fc(i, j, 2, i_fc) = inv_dr(2) * &
2113 (cc(i, j, i_phi) - bc(ijk)) / dd(3)
2114 end if
2115 if (dd(4) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2116 box%fc(i, j+1, 2, i_fc) = inv_dr(2) * &
2117 (bc(ijk) - cc(i, j, i_phi)) / dd(4)
2118 end if
2119#elif NDIM == 3
2120 i = box%stencils(ix_dist)%sparse_ix(1, n)
2121 j = box%stencils(ix_dist)%sparse_ix(2, n)
2122 k = box%stencils(ix_dist)%sparse_ix(3, n)
2123
2124 if (dd(1) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2125 box%fc(i, j, k, 1, i_fc) = inv_dr(1) * &
2126 (cc(ijk, i_phi) - bc(ijk)) / dd(1)
2127 end if
2128 if (dd(2) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2129 box%fc(i+1, j, k, 1, i_fc) = inv_dr(1) * &
2130 (bc(ijk) - cc(ijk, i_phi)) / dd(2)
2131 end if
2132 if (dd(3) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2133 box%fc(i, j, k, 2, i_fc) = inv_dr(2) * &
2134 (cc(ijk, i_phi) - bc(ijk)) / dd(3)
2135 end if
2136 if (dd(4) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2137 box%fc(i, j+1, k, 2, i_fc) = inv_dr(2) * &
2138 (bc(ijk) - cc(ijk, i_phi)) / dd(4)
2139 end if
2140 if (dd(5) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2141 box%fc(i, j, k, 3, i_fc) = inv_dr(3) * &
2142 (cc(ijk, i_phi) - bc(ijk)) / dd(5)
2143 end if
2144 if (dd(6) < 1 .and. cc(ijk, mg%i_lsf) >= 0) then
2145 box%fc(i, j, k+1, 3, i_fc) = inv_dr(3) * &
2146 (bc(ijk) - cc(ijk, i_phi)) / dd(6)
2147 end if
2148#endif
2149 end do
2150 end associate
2151 end subroutine mg_box_lpllsf_gradient
2152
2153 !> This method checks whether the level set function is properly defined on
2154 !> the coarse grid
2155 subroutine check_coarse_representation_lsf(tree, mg)
2156 type(af_t), intent(in) :: tree
2157 type(mg_t), intent(in) :: mg
2158 integer :: i, id, ix
2159
2160 do i = 1, size(tree%lvls(1)%ids)
2161 id = tree%lvls(1)%ids(i)
2162 ix = af_stencil_index(tree%boxes(id), mg_lsf_distance_key)
2163 if (ix > af_stencil_none) exit
2164 end do
2165
2166 if (i == size(tree%lvls(1)%ids)+1) then
2167 print *, "No roots found for level set function on coarse grid"
2168 print *, "you should probably use a finer coarse grid"
2169 error stop "level set function not resolved on coarse grid"
2170 end if
2171
2172 end subroutine check_coarse_representation_lsf
2173
2174 !> Get amplitude of numerical gradient of level set function
2175 function numerical_gradient(f, r) result(gradient)
2176 procedure(mg_func_lsf) :: f
2177 real(dp), intent(in) :: r(ndim)
2178 real(dp), parameter :: sqrteps = sqrt(epsilon(1.0_dp))
2179 real(dp), parameter :: min_stepsize = epsilon(1.0_dp)
2180 real(dp) :: r_eval(ndim), gradient(ndim)
2181 real(dp) :: stepsize(ndim), flo, fhi
2182 integer :: idim
2183
2184 stepsize = max(min_stepsize, sqrteps * abs(r))
2185 r_eval = r
2186
2187 do idim = 1, ndim
2188 ! Sample function at (r - step_idim) and (r + step_idim)
2189 r_eval(idim) = r(idim) - stepsize(idim)
2190 flo = f(r_eval)
2191
2192 r_eval(idim) = r(idim) + stepsize(idim)
2193 fhi = f(r_eval)
2194
2195 ! Use central difference scheme
2196 gradient(idim) = (fhi - flo)/(2 * stepsize(idim))
2197
2198 ! Reset to original coordinate
2199 r_eval(idim) = r(idim)
2200 end do
2201 end function numerical_gradient
2202
2203end module m_af_multigrid
This module contains the core routines of Afivo, namely those that deal with initializing and changin...
Definition: m_af_core.f90:3
This module contains routines related to the filling of ghost cells. Note that corner ghost cells are...
This module contains the geometric multigrid routines that come with Afivo.
This module contains routines for restriction: going from fine to coarse variables.
This module contains functionality for dealing with numerical stencils.
Definition: m_af_stencil.f90:2
This module contains the basic types and constants that are used in the NDIM-dimensional version of A...
Definition: m_af_types.f90:3
This module contains all kinds of different 'helper' routines for Afivo. If the number of routines fo...
Definition: m_af_utils.f90:4
Module to solve elliptic PDEs on the coarse grid. This module contains an interface to Hypre,...
Type which stores all the boxes and levels, as well as some information about the number of boxes,...
Definition: m_af_types.f90:326
The basic building block of afivo: a box with cell-centered and face centered data,...
Definition: m_af_types.f90:286
Type to store multigrid options in.
Definition: m_af_types.f90:572