MPI-AMRVAC 3.1
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
m_octree_mg_2d.t
Go to the documentation of this file.
1
2
3! Single module generated from the octree-mg sources.
4! This file can be easier to include in existing projects.
5!
6! Notes:
7! 1. The module name is here extended by _1d, _2d or _3d
8! 2. The free space Poisson solver is not included here.
9! 3. It is best to make changes in the original repository at
10! https://github.com/jannisteunissen/octree-mg
11! 4. This file can be generated as follows:
12! cd octree-mg/single_module
13! ./to_single_module.sh
14!
15! The modules can be compiled with:
16! mpif90 -c m_octree_mg_1d.f90 [other options]
17! mpif90 -c m_octree_mg_2d.f90 [other options]
18! mpif90 -c m_octree_mg_3d.f90 [other options]
19! mpif90 -c m_octree_mg.f90 -cpp -DNDIM=<1,2,3> [other options]
20
21
23 use mpi
24 implicit none
25 private
26
27 !! File ../src/m_data_structures.f90
28
29 !> Type of reals
30 integer, parameter, public :: dp = kind(0.0d0)
31
32 !> Type for 64-bit integers
33 integer, parameter, public :: i8 = selected_int_kind(18)
34
35 !> Indicates a standard Laplacian
36 integer, parameter, public :: mg_laplacian = 1
37
38 !> Indicates a variable-coefficient Laplacian
39 integer, parameter, public :: mg_vlaplacian = 2
40
41 !> Indicates a constant-coefficient Helmholtz equation
42 integer, parameter, public :: mg_helmholtz = 3
43
44 !> Indicates a variable-coefficient Helmholtz equation
45 integer, parameter, public :: mg_vhelmholtz = 4
46
47 !> Indicates a anisotropic-coefficient Helmholtz equation
48 integer, parameter, public :: mg_ahelmholtz = 5
49
50 !> Cartesian coordinate system
51 integer, parameter, public :: mg_cartesian = 1
52 !> Cylindrical coordinate system
53 integer, parameter, public :: mg_cylindrical = 2
54 !> Spherical coordinate system
55 integer, parameter, public :: mg_spherical = 3
56
57 integer, parameter, public :: mg_smoother_gs = 1
58 integer, parameter, public :: mg_smoother_gsrb = 2
59 integer, parameter, public :: mg_smoother_jacobi = 3
60
61 !> Problem dimension
62 integer, parameter, public :: mg_ndim = 2
63
64 !> Number of predefined multigrid variables
65 integer, parameter, public :: mg_num_vars = 4
66 !> Maximum number of variables
67 integer, parameter, public :: mg_max_num_vars = 10
68 !> Index of solution
69 integer, parameter, public :: mg_iphi = 1
70 !> Index of right-hand side
71 integer, parameter, public :: mg_irhs = 2
72 !> Index of previous solution (used for correction)
73 integer, parameter, public :: mg_iold = 3
74 !> Index of residual
75 integer, parameter, public :: mg_ires = 4
76
77 !> Index of the variable coefficient (at cell centers)
78 integer, parameter, public :: mg_iveps = 5
79
80 !> Indexes of anisotropic variable coefficients
81 integer, parameter, public :: mg_iveps1 = 5
82 integer, parameter, public :: mg_iveps2 = 6
83 integer, parameter, public :: mg_iveps3 = 7
84
85 !> Minimum allowed grid level
86 integer, parameter, public :: mg_lvl_lo = -20
87 !> Maximum allowed grid level
88 integer, parameter, public :: mg_lvl_hi = 20
89
90 !> Value to indicate a Dirichlet boundary condition
91 integer, parameter, public :: mg_bc_dirichlet = -10
92
93 !> Value to indicate a Neumann boundary condition
94 integer, parameter, public :: mg_bc_neumann = -11
95
96 !> Value to indicate a continuous boundary condition
97 integer, parameter, public :: mg_bc_continuous = -12
98
99 !> Special value that indicates there is no box
100 integer, parameter, public :: mg_no_box = 0
101 !> Special value that indicates there is a physical boundary
102 integer, parameter, public :: mg_physical_boundary = -1
103
104 !> Maximum number of timers to use
105 integer, parameter, public :: mg_max_timers = 20
106
107 ! Numbering of children (same location as **corners**)
108 integer, parameter, public :: mg_num_children = 4
109
110 ! Index offset for each child
111 integer, parameter, public :: mg_child_dix(2, 4) = reshape([0,0,1,0,0,1,1,1], [2,4])
112 ! Reverse child index in each direction
113 integer, parameter, public :: mg_child_rev(4, 2) = reshape([2,1,4,3,3,4,1,2], [4,2])
114 ! Children adjacent to a neighbor
115 integer, parameter, public :: mg_child_adj_nb(2, 4) = reshape([1,3,2,4,1,2,3,4], [2,4])
116 ! Which children have a low index per dimension
117 logical, parameter, public :: mg_child_low(2, 4) = reshape([.true., .true., &
118 .false., .true., .true., .false., .false., .false.], [2, 4])
119
120 ! Neighbor topology information
121 integer, parameter, public :: mg_num_neighbors = 4
122 integer, parameter, public :: mg_neighb_lowx = 1
123 integer, parameter, public :: mg_neighb_highx = 2
124 integer, parameter, public :: mg_neighb_lowy = 3
125 integer, parameter, public :: mg_neighb_highy = 4
126
127 ! Index offsets of neighbors
128 integer, parameter, public :: mg_neighb_dix(2, 4) = reshape([-1,0,1,0,0,-1,0,1], [2,4])
129 ! Which neighbors have a lower index
130 logical, parameter, public :: mg_neighb_low(4) = [.true., .false., .true., .false.]
131 ! Opposite of nb_low, but now as -1,1 integers
132 integer, parameter, public :: mg_neighb_high_pm(4) = [-1, 1, -1, 1]
133
134 ! Reverse neighbors
135 integer, parameter, public :: mg_neighb_rev(4) = [2, 1, 4, 3]
136 ! Direction (dimension) for a neighbor
137 integer, parameter, public :: mg_neighb_dim(4) = [1, 1, 2, 2]
138
139 !> Lists of blocks per refinement level
140 type, public :: mg_lvl_t
141 integer, allocatable :: leaves(:)
142 integer, allocatable :: parents(:)
143 integer, allocatable :: ref_bnds(:)
144 integer, allocatable :: ids(:)
145 integer, allocatable :: my_leaves(:)
146 integer, allocatable :: my_parents(:)
147 integer, allocatable :: my_ref_bnds(:)
148 integer, allocatable :: my_ids(:)
149 end type mg_lvl_t
150
151 !> Box data structure
152 type, public :: mg_box_t
153 integer :: rank !< Which process owns this box
154 integer :: id !< Box id (index in boxes(:) array)
155 integer :: lvl !< Refinement level
156 integer :: ix(2) !< Spatial index
157 integer :: parent !< Id of parent
158 integer :: children(2**2) !< Ids of children
159 integer :: neighbors(2*2) !< Ids of neighbors
160 real(dp) :: r_min(2) !< Minimum coordinate
161 real(dp) :: dr(2) !< Grid spacing
162 !> Cell-centered data
163 real(dp), allocatable :: cc(:, :, :)
164 end type mg_box_t
165
166 !> Buffer type (one is used for each pair of communicating processes)
167 type, public :: mg_buf_t
168 integer :: i_send !< Index in send array
169 integer :: i_recv
170 integer :: i_ix
171 integer, allocatable :: ix(:) !< Will be used to sort the data
172 real(dp), allocatable :: send(:)
173 real(dp), allocatable :: recv(:)
174 end type mg_buf_t
175
176 type, public :: mg_comm_t
177 integer, allocatable :: n_send(:, :)
178 integer, allocatable :: n_recv(:, :)
179 end type mg_comm_t
180
181 type, public :: mg_bc_t
182 integer :: bc_type = mg_bc_dirichlet !< Type of boundary condition
183 real(dp) :: bc_value = 0.0_dp !< Value (for e.g. Dirichlet or Neumann)
184 !> To set user-defined boundary conditions (overrides bc(:))
185 procedure(mg_subr_bc), pointer, nopass :: boundary_cond => null()
186 !> To set a user-defined refinement boundary method
187 procedure(mg_subr_rb), pointer, nopass :: refinement_bnd => null()
188 end type mg_bc_t
189
190 type, public :: mg_timer_t
191 character(len=20) :: name
192 real(dp) :: t = 0.0_dp
193 real(dp) :: t0
194 end type mg_timer_t
195
196 type, public :: mg_t
197 !> Whether the multigrid tree structure has been created
198 logical :: tree_created = .false.
199 !> Whether storage has been allocated for boxes
200 logical :: is_allocated = .false.
201 !> Number of extra cell-centered variable (e.g., for coefficients)
202 integer :: n_extra_vars = 0
203 !> MPI communicator
204 integer :: comm = -1
205 !> Number of MPI tasks
206 integer :: n_cpu = -1
207 !> MPI rank of this task
208 integer :: my_rank = -1
209 !> Size of boxes in cells, be equal for all dimensions
210 integer :: box_size = -1
211 !> Highest grid level in the tree
212 integer :: highest_lvl = -1
213 !> Lowest grid level in the tree
214 integer :: lowest_lvl = -1
215 !> First normal level of the quadtree/octree, at coarser levels parents
216 !> have only one child
217 integer :: first_normal_lvl = -1
218 !> Total number of boxes in the tree (among all processes)
219 integer :: n_boxes = 0
220 !> Size of boxes per level (differs for coarsest levels)
221 integer :: box_size_lvl(mg_lvl_lo:mg_lvl_hi)
222 !> Size of domain per level (if uniformly refined)
223 integer :: domain_size_lvl(2, mg_lvl_lo:mg_lvl_hi)
224 !> Grid spacing per level
225 real(dp) :: dr(2, mg_lvl_lo:mg_lvl_hi)
226 !> Minimum coordinates
227 real(dp) :: r_min(2)
228 !> List of all levels
230 !> Array with all boxes in the tree. Only boxes owned by this task are
231 !> actually allocated
232 type(mg_box_t), allocatable :: boxes(:)
233 !> Buffer for communication with each other task
234 type(mg_buf_t), allocatable :: buf(:)
235
236 !> Communication info for restriction
237 type(mg_comm_t) :: comm_restrict
238 !> Communication info for prolongation
239 type(mg_comm_t) :: comm_prolong
240 !> Communication info for ghost cell filling
241 type(mg_comm_t) :: comm_ghostcell
242
243 !> Whether boundary condition data has been stored for mg solution
244 logical :: phi_bc_data_stored = .false.
245
246 !> Whether a dimension is periodic
247 logical :: periodic(2) = .false.
248
249 !> To store pre-defined boundary conditions per direction per variable
251
252 !> Type of operator
253 integer :: operator_type = mg_laplacian
254 !> Type of grid geometry
255 integer :: geometry_type = mg_cartesian
256
257 !> Whether the mean has to be subtracted from the multigrid solution
258 logical :: subtract_mean = .false.
259 !> Type of multigrid smoother
260 integer :: smoother_type = mg_smoother_gs
261 !> Number of substeps for the smoother (for GSRB this is 2)
262 integer :: n_smoother_substeps = 1
263 !> Number of cycles when doing downwards relaxation
264 integer :: n_cycle_down = 2
265 !> Number of cycles when doing upwards relaxation
266 integer :: n_cycle_up = 2
267 !> Maximum number of cycles on the coarse grid
268 integer :: max_coarse_cycles = 1000
269 integer :: coarsest_grid(2) = 2
270 !> Stop coarse grid when max. residual is smaller than this
271 real(dp) :: residual_coarse_abs = 1e-8_dp
272 !> Stop coarse grid when residual has been reduced by this factor
273 real(dp) :: residual_coarse_rel = 1e-8_dp
274
275 !> Multigrid operator (e.g., Laplacian)
276 procedure(mg_box_op), pointer, nopass :: box_op => null()
277
278 !> Multigrid smoother
279 procedure(mg_box_gsrb), pointer, nopass :: box_smoother => null()
280
281 !> Multigrid prolongation method
282 procedure(mg_box_prolong), pointer, nopass :: box_prolong => null()
283
284 !> Number of timers
285 integer :: n_timers = 0
286 !> Values for the timers
287 type(mg_timer_t) :: timers(mg_max_timers)
288 end type mg_t
289
290 interface
291 !> To fill ghost cells near physical boundaries
292 subroutine mg_subr_bc(box, nc, iv, nb, bc_type, bc)
293 import
294 type(mg_box_t), intent(in) :: box
295 integer, intent(in) :: nc
296 integer, intent(in) :: iv !< Index of variable
297 integer, intent(in) :: nb !< Direction
298 integer, intent(out) :: bc_type !< Type of b.c.
299 !> Boundary values
300 real(dp), intent(out) :: bc(nc)
301 end subroutine mg_subr_bc
302
303 !> To fill ghost cells near refinement boundaries
304 subroutine mg_subr_rb(box, nc, iv, nb, cgc)
305 import
306 type(mg_box_t), intent(inout) :: box
307 integer, intent(in) :: nc
308 integer, intent(in) :: iv !< Index of variable
309 integer, intent(in) :: nb !< Direction
310 !> Coarse data
311 real(dp), intent(in) :: cgc(nc)
312 end subroutine mg_subr_rb
313
314 !> Subroutine that performs A * cc(..., i_in) = cc(..., i_out)
315 subroutine mg_box_op(mg, id, nc, i_out)
316 import
317 type(mg_t), intent(inout) :: mg
318 integer, intent(in) :: id
319 integer, intent(in) :: nc
320 integer, intent(in) :: i_out
321 end subroutine mg_box_op
322
323 !> Subroutine that performs Gauss-Seidel relaxation
324 subroutine mg_box_gsrb(mg, id, nc, redblack_cntr)
325 import
326 type(mg_t), intent(inout) :: mg
327 integer, intent(in) :: id
328 integer, intent(in) :: nc
329 integer, intent(in) :: redblack_cntr
330 end subroutine mg_box_gsrb
331
332 !> Subroutine that performs prolongation to a single child
333 subroutine mg_box_prolong(mg, p_id, dix, nc, iv, fine)
334 import
335 type(mg_t), intent(inout) :: mg
336 integer, intent(in) :: p_id !< Id of parent
337 integer, intent(in) :: dix(2) !< Offset of child in parent grid
338 integer, intent(in) :: nc !< Child grid size
339 integer, intent(in) :: iv !< Prolong from this variable
340 real(dp), intent(out) :: fine(nc, nc) !< Prolonged values
341 end subroutine mg_box_prolong
342 end interface
343
344 ! Public methods
345 public :: mg_subr_bc
346 public :: mg_subr_rb
347 public :: mg_box_op
348 public :: mg_box_gsrb
349 public :: mg_box_prolong
350 public :: mg_has_children
351 public :: mg_ix_to_ichild
352 public :: mg_get_child_offset
353 public :: mg_highest_uniform_lvl
354 public :: mg_number_of_unknowns
355 public :: mg_get_face_coords
356 public :: mg_add_timer
357 public :: mg_timer_start
358 public :: mg_timer_end
359 public :: mg_timers_show
360
361 !! File ../src/m_allocate_storage.f90
362
363 public :: mg_allocate_storage
364 public :: mg_deallocate_storage
365
366 !! File ../src/m_diffusion.f90
367
368!> Module for implicitly solving diffusion equations
369
370 public :: diffusion_solve
371 public :: diffusion_solve_vcoeff
372 public :: diffusion_solve_acoeff
373
374 !! File ../src/m_laplacian.f90
375
376!> Module which contains multigrid procedures for a Laplacian operator
377
378 public :: laplacian_set_methods
379
380 !! File ../src/m_vhelmholtz.f90
381
382!> Module which contains multigrid procedures for a Helmholtz operator of the
383!> form: div(D grad(phi)) - lambda*phi = f, where D has a smooth spatial
384!> variation
385
386 !> The lambda used for the Helmholtz equation (should be >= 0)
387 real(dp), public, protected :: vhelmholtz_lambda = 0.0_dp
388
389 public :: vhelmholtz_set_methods
390 public :: vhelmholtz_set_lambda
391
392 !! File ../src/m_build_tree.f90
393
394 ! Public methods
395 public :: mg_build_rectangle
396 public :: mg_add_children
397 public :: mg_set_leaves_parents
398 public :: mg_set_next_level_ids
400 public :: mg_set_neighbors_lvl
401
402 !! File ../src/m_load_balance.f90
403!> Module for load balancing a tree (that already has been constructed). The
404!> load balancing determines which ranks (MPI processes) allocated physical
405!> storage for boxes. The tree structure itself is present on all processes.
406
407 ! Public methods
408 public :: mg_load_balance_simple
409 public :: mg_load_balance
411
412 !! File ../src/m_vlaplacian.f90
413
414!> Module which contains multigrid procedures for a variable-coefficient
415!> Laplacian operator, assuming the variation is smooth
416
417 public :: vlaplacian_set_methods
418
419 !! File ../src/m_communication.f90
420
421 ! Public methods
422 public :: mg_comm_init
424
425 !! File ../src/m_ghost_cells.f90
426
427 ! Public methods
429 public :: mg_fill_ghost_cells
431 public :: mg_phi_bc_store
432
433 !! File ../src/m_prolong.f90
434
435 ! Public methods
436 public :: mg_prolong
437 public :: mg_prolong_buffer_size
438 public :: mg_prolong_sparse
439
440 !! File ../src/m_helmholtz.f90
441
442!> Module which contains multigrid procedures for a Helmholtz operator of the
443!> form: laplacian(phi) - lambda*phi = f
444
445 !> The lambda used for the Helmholtz equation (should be >= 0)
446 real(dp), public, protected :: helmholtz_lambda = 0.0_dp
447
448 public :: helmholtz_set_methods
449 public :: helmholtz_set_lambda
450
451 !! File ../src/m_multigrid.f90
452
453 integer :: timer_total_vcycle = -1
454 integer :: timer_total_fmg = -1
455 integer :: timer_smoother = -1
456 integer :: timer_smoother_gc = -1
457 integer :: timer_coarse = -1
458 integer :: timer_correct = -1
459 integer :: timer_update_coarse = -1
460
461 ! Public methods
462 public :: mg_fas_vcycle
463 public :: mg_fas_fmg
464 public :: mg_set_methods
465 public :: mg_apply_op
466
467 !! File ../src/m_restrict.f90
468
469 ! Public methods
470 public :: mg_restrict
471 public :: mg_restrict_lvl
473
474 !! File ../src/m_mrgrnk.f90
475
476 Integer, Parameter :: kdp = selected_real_kind(15)
477 public :: mrgrnk
478 private :: kdp
479 private :: i_mrgrnk
480 interface mrgrnk
481 module procedure i_mrgrnk
482 end interface mrgrnk
483
484 !! File ../src/m_ahelmholtz.f90
485
486!> Module which contains multigrid procedures for a Helmholtz operator of the
487!> form: div(D grad(phi)) - lambda*phi = f, where D has a smooth spatial
488!> variation and a component in each spatial direction
489
490 !> The lambda used for the Helmholtz equation (should be >= 0)
491 real(dp), public, protected :: ahelmholtz_lambda = 0.0_dp
492
493 public :: ahelmholtz_set_methods
494 public :: ahelmholtz_set_lambda
495contains
496
497 !! File ../src/m_data_structures.f90
498
499 !> Return .true. if a box has children
500 elemental logical function mg_has_children(box)
501 type(mg_box_t), intent(in) :: box
502
503 ! Boxes are either fully refined or not, so we only need to check one of the
504 ! children
505 mg_has_children = (box%children(1) /= mg_no_box)
506 end function mg_has_children
507
508 !> Compute the child index for a box with spatial index ix. With child index
509 !> we mean the index in the children(:) array of its parent.
510 integer function mg_ix_to_ichild(ix)
511 integer, intent(in) :: ix(2) !< Spatial index of the box
512 ! The index can range from 1 (all ix odd) and 2**$D (all ix even)
513 mg_ix_to_ichild = 4 - 2 * iand(ix(2), 1) - iand(ix(1), 1)
514 end function mg_ix_to_ichild
515
516 !> Get the offset of a box with respect to its parent (e.g. in 2d, there can
517 !> be a child at offset 0,0, one at n_cell/2,0, one at 0,n_cell/2 and one at
518 !> n_cell/2, n_cell/2)
519 pure function mg_get_child_offset(mg, id) result(ix_offset)
520 type(mg_t), intent(in) :: mg
521 integer, intent(in) :: id
522 integer :: ix_offset(2)
523
524 if (mg%boxes(id)%lvl <= mg%first_normal_lvl) then
525 ix_offset(:) = 0
526 else
527 ix_offset = iand(mg%boxes(id)%ix-1, 1) * &
528 ishft(mg%box_size, -1) ! * n_cell / 2
529 end if
530 end function mg_get_child_offset
531
532 pure function mg_highest_uniform_lvl(mg) result(lvl)
533 type(mg_t), intent(in) :: mg
534 integer :: lvl
535
536 do lvl = mg%first_normal_lvl, mg%highest_lvl-1
537 ! Exit if a grid is partially refined
538 if (size(mg%lvls(lvl)%leaves) /= 0 .and. &
539 size(mg%lvls(lvl)%parents) /= 0) exit
540 end do
541 ! If the loop did not exit, we get lvl equals mg%highest_lvl
542 end function mg_highest_uniform_lvl
543
544 !> Determine total number of unknowns (on leaves)
545 function mg_number_of_unknowns(mg) result(n_unknowns)
546 type(mg_t), intent(in) :: mg
547 integer :: lvl
548 integer(i8) :: n_unknowns
549
550 n_unknowns = 0
551 do lvl = mg%first_normal_lvl, mg%highest_lvl
552 n_unknowns = n_unknowns + size(mg%lvls(lvl)%leaves)
553 end do
554 n_unknowns = n_unknowns * int(mg%box_size**3, i8)
555 end function mg_number_of_unknowns
556
557 !> Get coordinates at the face of a box
558 subroutine mg_get_face_coords(box, nb, nc, x)
559 type(mg_box_t), intent(in) :: box
560 integer, intent(in) :: nb
561 integer, intent(in) :: nc
562 real(dp), intent(out) :: x(nc, 2)
563 integer :: i, ixs(2-1)
564 integer :: nb_dim
565 real(dp) :: rmin(2)
566
567 nb_dim = mg_neighb_dim(nb)
568
569 ! Determine directions perpendicular to neighbor
570 ixs = [(i, i = 1, 2-1)]
571 ixs(nb_dim:) = ixs(nb_dim:) + 1
572
573 rmin = box%r_min
574 if (.not. mg_neighb_low(nb)) then
575 rmin(nb_dim) = rmin(nb_dim) + box%dr(nb_dim) * nc
576 end if
577
578 do i = 1, nc
579 x(i, :) = rmin
580 x(i, ixs(1)) = x(i, ixs(1)) + (i-0.5d0) * box%dr(ixs(1))
581 end do
582 end subroutine mg_get_face_coords
583
584 integer function mg_add_timer(mg, name)
585 type(mg_t), intent(inout) :: mg
586 character(len=*), intent(in) :: name
587
588 mg%n_timers = mg%n_timers + 1
589 mg_add_timer = mg%n_timers
590 mg%timers(mg_add_timer)%name = name
591 end function mg_add_timer
592
593 subroutine mg_timer_start(timer)
594 use mpi
595 type(mg_timer_t), intent(inout) :: timer
596 timer%t0 = mpi_wtime()
597 end subroutine mg_timer_start
598
599 subroutine mg_timer_end(timer)
600 use mpi
601 type(mg_timer_t), intent(inout) :: timer
602 timer%t = timer%t + mpi_wtime() - timer%t0
603 end subroutine mg_timer_end
604
605 subroutine mg_timers_show(mg)
606 use mpi
607 type(mg_t), intent(in) :: mg
608 integer :: n, ierr
609 real(dp) :: tmin(mg%n_timers)
610 real(dp) :: tmax(mg%n_timers)
611 real(dp), allocatable :: tmp_array(:)
612
613 allocate(tmp_array(mg%n_timers))
614 tmp_array(:) = mg%timers(1:mg%n_timers)%t
615
616 call mpi_reduce(tmp_array, tmin, mg%n_timers, &
617 mpi_double, mpi_min, 0, mg%comm, ierr)
618 call mpi_reduce(tmp_array, tmax, mg%n_timers, &
619 mpi_double, mpi_max, 0, mg%comm, ierr)
620
621 if (mg%my_rank == 0) then
622 write(*, "(A20,2A16)") "name ", "min(s)", "max(s)"
623 do n = 1, mg%n_timers
624 write(*, "(A20,2F16.6)") mg%timers(n)%name, &
625 tmin(n), tmax(n)
626 end do
627 end if
628 end subroutine mg_timers_show
629
630 !! File ../src/m_allocate_storage.f90
631
632 !> Deallocate all allocatable arrays
634 type(mg_t), intent(inout) :: mg
635 integer :: lvl
636
637 if (.not. mg%is_allocated) &
638 error stop "deallocate_storage: tree is not allocated"
639
640 deallocate(mg%boxes)
641 deallocate(mg%buf)
642
643 deallocate(mg%comm_restrict%n_send)
644 deallocate(mg%comm_restrict%n_recv)
645
646 deallocate(mg%comm_prolong%n_send)
647 deallocate(mg%comm_prolong%n_recv)
648
649 deallocate(mg%comm_ghostcell%n_send)
650 deallocate(mg%comm_ghostcell%n_recv)
651
652 do lvl = mg%lowest_lvl, mg%highest_lvl
653 deallocate(mg%lvls(lvl)%ids)
654 deallocate(mg%lvls(lvl)%leaves)
655 deallocate(mg%lvls(lvl)%parents)
656 deallocate(mg%lvls(lvl)%ref_bnds)
657 deallocate(mg%lvls(lvl)%my_ids)
658 deallocate(mg%lvls(lvl)%my_leaves)
659 deallocate(mg%lvls(lvl)%my_parents)
660 deallocate(mg%lvls(lvl)%my_ref_bnds)
661 end do
662
663 mg%is_allocated = .false.
664 mg%n_boxes = 0
665 mg%phi_bc_data_stored = .false.
666 end subroutine mg_deallocate_storage
667
668 !> Allocate communication buffers and local boxes for a tree that has already
669 !> been created
670 subroutine mg_allocate_storage(mg)
671
672 type(mg_t), intent(inout) :: mg
673 integer :: i, id, lvl, nc
674 integer :: n_send(0:mg%n_cpu-1, 3)
675 integer :: n_recv(0:mg%n_cpu-1, 3)
676 integer :: dsize(3)
677 integer :: n_in, n_out, n_id
678
679 if (.not. mg%tree_created) &
680 error stop "allocate_storage: tree is not yet created"
681
682 if (mg%is_allocated) &
683 error stop "allocate_storage: tree is already allocated"
684
685 do lvl = mg%lowest_lvl, mg%highest_lvl
686 nc = mg%box_size_lvl(lvl)
687 do i = 1, size(mg%lvls(lvl)%my_ids)
688 id = mg%lvls(lvl)%my_ids(i)
689 allocate(mg%boxes(id)%cc(0:nc+1, 0:nc+1, &
690 mg_num_vars + mg%n_extra_vars))
691
692 ! Set all initial values to zero
693 mg%boxes(id)%cc(:, :, :) = 0.0_dp
694 end do
695 end do
696
697 allocate(mg%buf(0:mg%n_cpu-1))
698
699 call mg_ghost_cell_buffer_size(mg, n_send(:, 1), &
700 n_recv(:, 1), dsize(1))
701 call mg_restrict_buffer_size(mg, n_send(:, 2), &
702 n_recv(:, 2), dsize(2))
703 call mg_prolong_buffer_size(mg, n_send(:, 3), &
704 n_recv(:, 3), dsize(3))
705
706 do i = 0, mg%n_cpu-1
707 n_out = maxval(n_send(i, :) * dsize(:))
708 n_in = maxval(n_recv(i, :) * dsize(:))
709 n_id = maxval(n_send(i, :))
710 allocate(mg%buf(i)%send(n_out))
711 allocate(mg%buf(i)%recv(n_in))
712 allocate(mg%buf(i)%ix(n_id))
713 end do
714
715 mg%is_allocated = .true.
716 end subroutine mg_allocate_storage
717
718 !! File ../src/m_diffusion.f90
719
720 !> Solve a diffusion equation implicitly, assuming a constant diffusion
721 !> coefficient. The solution at time t should be stored in mg_iphi, which is
722 !> on output replaced by the solution at time t+dt.
723 subroutine diffusion_solve(mg, dt, diffusion_coeff, order, max_res)
724
725 type(mg_t), intent(inout) :: mg
726 real(dp), intent(in) :: dt
727 real(dp), intent(in) :: diffusion_coeff
728 integer, intent(in) :: order
729 real(dp), intent(in) :: max_res
730 integer, parameter :: max_its = 10
731 integer :: n
732 real(dp) :: res
733
734 mg%operator_type = mg_helmholtz
735 call mg_set_methods(mg)
736
737 select case (order)
738 case (1)
739 call helmholtz_set_lambda(1/(dt * diffusion_coeff))
740 call set_rhs(mg, -1/(dt * diffusion_coeff), 0.0_dp)
741 case (2)
742 call helmholtz_set_lambda(0.0d0)
743 call mg_apply_op(mg, mg_irhs)
744 call helmholtz_set_lambda(2/(dt * diffusion_coeff))
745 call set_rhs(mg, -2/(dt * diffusion_coeff), -1.0_dp)
746 case default
747 error stop "diffusion_solve: order should be 1 or 2"
748 end select
749
750 ! Start with an FMG cycle
751 call mg_fas_fmg(mg, .true., max_res=res)
752
753 ! Add V-cycles if necessary
754 do n = 1, max_its
755 if (res <= max_res) exit
756 call mg_fas_vcycle(mg, max_res=res)
757 end do
758
759 if (n == max_its + 1) then
760 if (mg%my_rank == 0) &
761 print *, "Did you specify boundary conditions correctly?"
762 error stop "diffusion_solve: no convergence"
763 end if
764 end subroutine diffusion_solve
765
766 !> Solve a diffusion equation implicitly, assuming a variable diffusion
767 !> coefficient which has been stored in mg_iveps (also on coarse grids). The
768 !> solution at time t should be stored in mg_iphi, which is on output replaced
769 !> by the solution at time t+dt.
770 subroutine diffusion_solve_vcoeff(mg, dt, order, max_res)
771
772 type(mg_t), intent(inout) :: mg
773 real(dp), intent(in) :: dt
774 integer, intent(in) :: order
775 real(dp), intent(in) :: max_res
776 integer, parameter :: max_its = 10
777 integer :: n
778 real(dp) :: res
779
780 mg%operator_type = mg_vhelmholtz
781 call mg_set_methods(mg)
782
783 select case (order)
784 case (1)
785 call vhelmholtz_set_lambda(1/dt)
786 call set_rhs(mg, -1/dt, 0.0_dp)
787 case (2)
788 call vhelmholtz_set_lambda(0.0d0)
789 call mg_apply_op(mg, mg_irhs)
790 call vhelmholtz_set_lambda(2/dt)
791 call set_rhs(mg, -2/dt, -1.0_dp)
792 case default
793 error stop "diffusion_solve: order should be 1 or 2"
794 end select
795
796 ! Start with an FMG cycle
797 call mg_fas_fmg(mg, .true., max_res=res)
798
799 ! Add V-cycles if necessary
800 do n = 1, max_its
801 if (res <= max_res) exit
802 call mg_fas_vcycle(mg, max_res=res)
803 end do
804
805 if (n == max_its + 1) then
806 if (mg%my_rank == 0) then
807 print *, "Did you specify boundary conditions correctly?"
808 print *, "Or is the variation in diffusion too large?"
809 end if
810 error stop "diffusion_solve: no convergence"
811 end if
812 end subroutine diffusion_solve_vcoeff
813
814 !> Solve a diffusion equation implicitly, assuming anisotropic diffusion
815 !> coefficient which has been stored in mg_iveps1, mg_iveps2, mg_iveps3
816 !> (also on coarse grids). The
817 !> solution at time t should be stored in mg_iphi, which is on output replaced
818 !> by the solution at time t+dt.
819 subroutine diffusion_solve_acoeff(mg, dt, order, max_res)
820
821 type(mg_t), intent(inout) :: mg
822 real(dp), intent(in) :: dt
823 integer, intent(in) :: order
824 real(dp), intent(in) :: max_res
825 integer, parameter :: max_its = 100
826 integer :: n
827 real(dp) :: res
828
829 mg%operator_type = mg_ahelmholtz
830 call mg_set_methods(mg)
831
832 select case (order)
833 case (1)
834 call ahelmholtz_set_lambda(1/dt)
835 call set_rhs(mg, -1/dt, 0.0_dp)
836 case (2)
837 call ahelmholtz_set_lambda(0.0d0)
838 call mg_apply_op(mg, mg_irhs)
839 call ahelmholtz_set_lambda(2/dt)
840 call set_rhs(mg, -2/dt, -1.0_dp)
841 case default
842 error stop "diffusion_solve: order should be 1 or 2"
843 end select
844
845 ! Start with an FMG cycle
846 call mg_fas_fmg(mg, .true., max_res=res)
847
848 ! Add V-cycles if necessary
849 do n = 1, max_its
850 print*, n, res
851 if (res <= max_res) exit
852 call mg_fas_vcycle(mg, max_res=res)
853 end do
854
855 if (n == max_its + 1) then
856 if (mg%my_rank == 0) then
857 print *, "Did you specify boundary conditions correctly?"
858 print *, "Or is the variation in diffusion too large?"
859 end if
860 error stop "diffusion_solve: no convergence"
861 end if
862 end subroutine diffusion_solve_acoeff
863
864 subroutine set_rhs(mg, f1, f2)
865 type(mg_t), intent(inout) :: mg
866 real(dp), intent(in) :: f1, f2
867 integer :: lvl, i, id, nc
868
869 do lvl = 1, mg%highest_lvl
870 nc = mg%box_size_lvl(lvl)
871 do i = 1, size(mg%lvls(lvl)%my_leaves)
872 id = mg%lvls(lvl)%my_leaves(i)
873 mg%boxes(id)%cc(1:nc, 1:nc, mg_irhs) = &
874 f1 * mg%boxes(id)%cc(1:nc, 1:nc, mg_iphi) + &
875 f2 * mg%boxes(id)%cc(1:nc, 1:nc, mg_irhs)
876 end do
877 end do
878 end subroutine set_rhs
879
880 !! File ../src/m_laplacian.f90
881
883 type(mg_t), intent(inout) :: mg
884
885 if (all(mg%periodic)) then
886 ! For a fully periodic Laplacian, remove the mean from the rhs and phi so
887 ! that a unique and periodic solution can be found
888 mg%subtract_mean = .true.
889 end if
890
891 select case (mg%geometry_type)
892 case (mg_cartesian)
893 mg%box_op => box_lpl
894
895 select case (mg%smoother_type)
896 ! case (smoother_jacobi)
897 ! mg%box_smoother => box_jacobi_lpl
899 mg%box_smoother => box_gs_lpl
900 case default
901 error stop "laplacian_set_methods: unsupported smoother type"
902 end select
903 case (mg_cylindrical)
904 mg%box_op => box_clpl
905
906 select case (mg%smoother_type)
908 mg%box_smoother => box_gs_clpl
909 case default
910 error stop "laplacian_set_methods: unsupported smoother type"
911 end select
912 case default
913 error stop "laplacian_set_methods: unsupported geometry"
914 end select
915
916 end subroutine laplacian_set_methods
917
918 !> Perform Gauss-Seidel relaxation on box for a Laplacian operator
919 subroutine box_gs_lpl(mg, id, nc, redblack_cntr)
920 type(mg_t), intent(inout) :: mg
921 integer, intent(in) :: id
922 integer, intent(in) :: nc
923 integer, intent(in) :: redblack_cntr !< Iteration counter
924 integer :: i, j, i0, di
925 real(dp) :: idr2(2), fac
926 logical :: redblack
927
928 idr2 = 1/mg%dr(:, mg%boxes(id)%lvl)**2
929 fac = 0.5_dp / sum(idr2)
930 i0 = 1
931
932 redblack = (mg%smoother_type == mg_smoother_gsrb)
933 if (redblack) then
934 di = 2
935 else
936 di = 1
937 end if
938
939 ! The parity of redblack_cntr determines which cells we use. If
940 ! redblack_cntr is even, we use the even cells and vice versa.
941 associate(cc => mg%boxes(id)%cc, n => mg_iphi)
942 do j = 1, nc
943 if (redblack) &
944 i0 = 2 - iand(ieor(redblack_cntr, j), 1)
945
946 do i = i0, nc, di
947 cc(i, j, n) = fac * ( &
948 idr2(1) * (cc(i+1, j, n) + cc(i-1, j, n)) + &
949 idr2(2) * (cc(i, j+1, n) + cc(i, j-1, n)) - &
950 cc(i, j, mg_irhs))
951 end do
952 end do
953 end associate
954 end subroutine box_gs_lpl
955
956! !> Perform Jacobi relaxation on box for a Laplacian operator
957! subroutine box_jacobi_lpl(mg, id, nc, cntr)
958! type(mg_t), intent(inout) :: mg
959! integer, intent(in) :: id
960! integer, intent(in) :: nc
961! integer, intent(in) :: cntr !< Not used
962! integer :: i, j
963! real(dp), parameter :: w = 2.0_dp / 3
964! real(dp) :: tmp(0:nc+1, 0:nc+1)
965! real(dp) :: dr2
966! #if 2 == 3
967! real(dp), parameter :: sixth = 1/6.0_dp
968! #endif
969
970! dr2 = mg%dr(mg%boxes(id)%lvl)**2
971
972! associate (box => mg%boxes(id))
973! tmp = box%cc(:, :, mg_iphi)
974! do j=1, nc; do i=1, nc
975! #if 2 == 2
976! box%cc(i, j, mg_iphi) = (1-w) * box%cc(i, j, mg_iphi) + &
977! 0.25_dp * w * ( &
978! tmp(i+1, j) + tmp(i-1, j) + &
979! tmp(i, j+1) + tmp(i, j-1) - &
980! dr2 * box%cc(i, j, mg_irhs))
981! #elif 2 == 3
982! box%cc(i, j, k, mg_iphi) = (1-w) * &
983! box%cc(i, j, k, mg_iphi) + &
984! sixth * w * ( &
985! tmp(i+1, j, k) + tmp(i-1, j, k) + &
986! tmp(i, j+1, k) + tmp(i, j-1, k) + &
987! tmp(i, j, k+1) + tmp(i, j, k-1) - &
988! dr2 * box%cc(i, j, k, mg_irhs))
989! #endif
990! end do; end do
991! end associate
992! end subroutine box_jacobi_lpl
993
994 !> Perform Laplacian operator on a box
995 subroutine box_lpl(mg, id, nc, i_out)
996 type(mg_t), intent(inout) :: mg
997 integer, intent(in) :: id
998 integer, intent(in) :: nc
999 integer, intent(in) :: i_out !< Index of variable to store Laplacian in
1000 integer :: i, j
1001 real(dp) :: idr2(2)
1002
1003 idr2 = 1 / mg%dr(:, mg%boxes(id)%lvl)**2
1004
1005 associate(cc => mg%boxes(id)%cc, n => mg_iphi)
1006 do j = 1, nc
1007 do i = 1, nc
1008 cc(i, j, i_out) = &
1009 idr2(1) * (cc(i-1, j, n) + cc(i+1, j, n) - 2 * cc(i, j, n)) + &
1010 idr2(2) * (cc(i, j-1, n) + cc(i, j+1, n) - 2 * cc(i, j, n))
1011 end do
1012 end do
1013 end associate
1014 end subroutine box_lpl
1015
1016 !> Perform Laplacian operator on a box in cylindrical geometry, using (r,z)
1017 !> and (r,phi,z) coordinates in 2D/3D.
1018 subroutine box_clpl(mg, id, nc, i_out)
1019 type(mg_t), intent(inout) :: mg
1020 integer, intent(in) :: id
1021 integer, intent(in) :: nc
1022 integer, intent(in) :: i_out !< Index of variable to store Laplacian in
1023 integer :: i, j
1024 real(dp) :: dr(2), idr2(2)
1025 real(dp) :: r_face(nc+1), r_inv(nc)
1026
1027 dr = mg%dr(:, mg%boxes(id)%lvl)
1028 idr2 = 1 / dr**2
1029 r_face = mg%boxes(id)%r_min(1) + dr(1) * [(i, i=0,nc)]
1030 r_inv = 1/(mg%boxes(id)%r_min(1) + dr(1) * [(i-0.5_dp, i=1,nc)])
1031
1032 associate(cc => mg%boxes(id)%cc, n => mg_iphi)
1033 do j = 1, nc
1034 do i = 1, nc
1035 cc(i, j, i_out) = idr2(1) * (&
1036 r_face(i) * r_inv(i) * cc(i-1, j, n) + &
1037 r_face(i+1) * r_inv(i) * cc(i+1, j, n) &
1038 - 2 * cc(i, j, n)) &
1039 + idr2(2) * (cc(i, j-1, n) + cc(i, j+1, n) - 2 * cc(i, j, n))
1040 end do
1041 end do
1042 end associate
1043 end subroutine box_clpl
1044
1045 !> Perform Gauss-Seidel relaxation on box for a Laplacian operator in
1046 !> cylindrical geometry. TODO: in 3D this does not converge well, maybe it
1047 !> will for a stretched grid.
1048 subroutine box_gs_clpl(mg, id, nc, redblack_cntr)
1049 type(mg_t), intent(inout) :: mg
1050 integer, intent(in) :: id
1051 integer, intent(in) :: nc
1052 integer, intent(in) :: redblack_cntr !< Iteration counter
1053 integer :: i, j, i0, di
1054 logical :: redblack
1055 real(dp) :: idr2(2), dr(2), dr2(2), fac
1056 real(dp) :: r_face(nc+1), r_inv(nc)
1057
1058 dr = mg%dr(:, mg%boxes(id)%lvl)
1059 dr2 = dr**2
1060 idr2 = 1/dr**2
1061 fac = 0.5_dp / sum(idr2)
1062 r_face = mg%boxes(id)%r_min(1) + dr(1) * [(i, i=0,nc)]
1063 r_inv = 1/(mg%boxes(id)%r_min(1) + dr(1) * [(i-0.5_dp, i=1,nc)])
1064
1065 i0 = 1
1066 redblack = (mg%smoother_type == mg_smoother_gsrb)
1067 if (redblack) then
1068 di = 2
1069 else
1070 di = 1
1071 end if
1072
1073 ! The parity of redblack_cntr determines which cells we use. If
1074 ! redblack_cntr is even, we use the even cells and vice versa.
1075 associate(cc => mg%boxes(id)%cc, n => mg_iphi)
1076 do j = 1, nc
1077 if (redblack) &
1078 i0 = 2 - iand(ieor(redblack_cntr, j), 1)
1079
1080 do i = i0, nc, di
1081 cc(i, j, n) = fac * (idr2(1) * &
1082 (r_face(i+1) * r_inv(i) * cc(i+1, j, n) + &
1083 r_face(i) * r_inv(i) * cc(i-1, j, n)) + &
1084 idr2(2) * (cc(i, j+1, n) + cc(i, j-1, n)) &
1085 - cc(i, j, mg_irhs))
1086 end do
1087 end do
1088 end associate
1089 end subroutine box_gs_clpl
1090
1091 !! File ../src/m_vhelmholtz.f90
1092
1094 type(mg_t), intent(inout) :: mg
1095
1096 if (mg%n_extra_vars == 0 .and. mg%is_allocated) then
1097 error stop "vhelmholtz_set_methods: mg%n_extra_vars == 0"
1098 else
1099 mg%n_extra_vars = max(1, mg%n_extra_vars)
1100 end if
1101
1102 mg%subtract_mean = .false.
1103
1104 ! Use Neumann zero boundary conditions for the variable coefficient, since
1105 ! it is needed in ghost cells.
1106 mg%bc(:, mg_iveps)%bc_type = mg_bc_neumann
1107 mg%bc(:, mg_iveps)%bc_value = 0.0_dp
1108
1109 select case (mg%geometry_type)
1110 case (mg_cartesian)
1111 mg%box_op => box_vhelmh
1112
1113 select case (mg%smoother_type)
1115 mg%box_smoother => box_gs_vhelmh
1116 case default
1117 error stop "vhelmholtz_set_methods: unsupported smoother type"
1118 end select
1119 case default
1120 error stop "vhelmholtz_set_methods: unsupported geometry"
1121 end select
1122
1123 end subroutine vhelmholtz_set_methods
1124
1125 subroutine vhelmholtz_set_lambda(lambda)
1126 real(dp), intent(in) :: lambda
1127
1128 if (lambda < 0) &
1129 error stop "vhelmholtz_set_lambda: lambda < 0 not allowed"
1130
1131 vhelmholtz_lambda = lambda
1132 end subroutine vhelmholtz_set_lambda
1133
1134 !> Perform Gauss-Seidel relaxation on box for a Helmholtz operator
1135 subroutine box_gs_vhelmh(mg, id, nc, redblack_cntr)
1136 type(mg_t), intent(inout) :: mg
1137 integer, intent(in) :: id
1138 integer, intent(in) :: nc
1139 integer, intent(in) :: redblack_cntr !< Iteration counter
1140 integer :: i, j, i0, di
1141 logical :: redblack
1142 real(dp) :: idr2(2*2), u(2*2)
1143 real(dp) :: a0, a(2*2), c(2*2)
1144
1145 ! Duplicate 1/dr^2 array to multiply neighbor values
1146 idr2(1:2*2:2) = 1/mg%dr(:, mg%boxes(id)%lvl)**2
1147 idr2(2:2*2:2) = idr2(1:2*2:2)
1148 i0 = 1
1149
1150 redblack = (mg%smoother_type == mg_smoother_gsrb)
1151 if (redblack) then
1152 di = 2
1153 else
1154 di = 1
1155 end if
1156
1157 ! The parity of redblack_cntr determines which cells we use. If
1158 ! redblack_cntr is even, we use the even cells and vice versa.
1159 associate(cc => mg%boxes(id)%cc, n => mg_iphi, &
1160 i_eps => mg_iveps)
1161 do j = 1, nc
1162 if (redblack) &
1163 i0 = 2 - iand(ieor(redblack_cntr, j), 1)
1164
1165 do i = i0, nc, di
1166 a0 = cc(i, j, i_eps)
1167 u(1:2) = cc(i-1:i+1:2, j, n)
1168 a(1:2) = cc(i-1:i+1:2, j, i_eps)
1169 u(3:4) = cc(i, j-1:j+1:2, n)
1170 a(3:4) = cc(i, j-1:j+1:2, i_eps)
1171 c(:) = 2 * a0 * a(:) / (a0 + a(:)) * idr2
1172
1173 cc(i, j, n) = &
1174 (sum(c(:) * u(:)) - cc(i, j, mg_irhs)) / &
1175 (sum(c(:)) + vhelmholtz_lambda)
1176 end do
1177 end do
1178 end associate
1179 end subroutine box_gs_vhelmh
1180
1181 !> Perform Helmholtz operator on a box
1182 subroutine box_vhelmh(mg, id, nc, i_out)
1183 type(mg_t), intent(inout) :: mg
1184 integer, intent(in) :: id
1185 integer, intent(in) :: nc
1186 integer, intent(in) :: i_out !< Index of variable to store Helmholtz in
1187 integer :: i, j
1188 real(dp) :: idr2(2*2), a0, u0, u(2*2), a(2*2)
1189
1190 ! Duplicate 1/dr^2 array to multiply neighbor values
1191 idr2(1:2*2:2) = 1/mg%dr(:, mg%boxes(id)%lvl)**2
1192 idr2(2:2*2:2) = idr2(1:2*2:2)
1193
1194 associate(cc => mg%boxes(id)%cc, n => mg_iphi, &
1195 i_eps => mg_iveps)
1196 do j = 1, nc
1197 do i = 1, nc
1198 a0 = cc(i, j, i_eps)
1199 a(1:2) = cc(i-1:i+1:2, j, i_eps)
1200 a(3:4) = cc(i, j-1:j+1:2, i_eps)
1201 u0 = cc(i, j, n)
1202 u(1:2) = cc(i-1:i+1:2, j, n)
1203 u(3:4) = cc(i, j-1:j+1:2, n)
1204
1205 cc(i, j, i_out) = sum(2 * idr2 * &
1206 a0*a(:)/(a0 + a(:)) * (u(:) - u0)) - &
1208 end do
1209 end do
1210 end associate
1211 end subroutine box_vhelmh
1212
1213 !! File ../src/m_build_tree.f90
1214
1215 subroutine mg_build_rectangle(mg, domain_size, box_size, dx, r_min, &
1216 periodic, n_finer)
1217 type(mg_t), intent(inout) :: mg
1218 integer, intent(in) :: domain_size(2)
1219 integer, intent(in) :: box_size
1220 real(dp), intent(in) :: dx(2)
1221 real(dp), intent(in) :: r_min(2)
1222 logical, intent(in) :: periodic(2)
1223 integer, intent(in) :: n_finer
1224 integer :: i, j, lvl, n, id, nx(2), ijk_vec(2), idim
1225 integer :: boxes_per_dim(2, mg_lvl_lo:1)
1226 integer :: periodic_offset(2)
1227
1228 if (modulo(box_size, 2) /= 0) &
1229 error stop "box_size should be even"
1230 if (any(modulo(domain_size, box_size) /= 0)) &
1231 error stop "box_size does not divide domain_size"
1232
1233 if (all(periodic)) then
1234 mg%subtract_mean = .true.
1235 end if
1236
1237 nx = domain_size
1238 mg%box_size = box_size
1239 mg%box_size_lvl(1) = box_size
1240 mg%domain_size_lvl(:, 1) = domain_size
1241 mg%first_normal_lvl = 1
1242 mg%dr(:, 1) = dx
1243 mg%r_min(:) = r_min
1244 mg%periodic = periodic
1245 boxes_per_dim(:, :) = 0
1246 boxes_per_dim(:, 1) = domain_size / box_size
1247
1248 do lvl = 1, mg_lvl_lo+1, -1
1249 ! For a Gauss-Seidel (non red-black) smoother, we should avoid boxes
1250 ! containing a single cell
1251 if (any(modulo(nx, 2) == 1 .or. nx == mg%coarsest_grid .or. &
1252 (mg%box_size_lvl(lvl) == mg%coarsest_grid .and. &
1253 mg%smoother_type == mg_smoother_gs))) exit
1254
1255 if (all(modulo(nx/mg%box_size_lvl(lvl), 2) == 0)) then
1256 mg%box_size_lvl(lvl-1) = mg%box_size_lvl(lvl)
1257 boxes_per_dim(:, lvl-1) = boxes_per_dim(:, lvl)/2
1258 mg%first_normal_lvl = lvl-1
1259 else
1260 mg%box_size_lvl(lvl-1) = mg%box_size_lvl(lvl)/2
1261 boxes_per_dim(:, lvl-1) = boxes_per_dim(:, lvl)
1262 end if
1263
1264 mg%dr(:, lvl-1) = mg%dr(:, lvl) * 2
1265 nx = nx / 2
1266 mg%domain_size_lvl(:, lvl-1) = nx
1267 end do
1268
1269 mg%lowest_lvl = lvl
1270 mg%highest_lvl = 1
1271
1272 do lvl = 2, mg_lvl_hi
1273 mg%dr(:, lvl) = mg%dr(:, lvl-1) * 0.5_dp
1274 mg%box_size_lvl(lvl) = box_size
1275 mg%domain_size_lvl(:, lvl) = 2 * mg%domain_size_lvl(:, lvl-1)
1276 end do
1277
1278 n = sum(product(boxes_per_dim, dim=1)) + n_finer
1279 allocate(mg%boxes(n))
1280
1281 ! Create lowest level
1282 nx = boxes_per_dim(:, mg%lowest_lvl)
1283 periodic_offset = [nx(1)-1, (nx(2)-1)*nx(1)]
1284
1285 do j=1,nx(2); do i=1,nx(1)
1286 mg%n_boxes = mg%n_boxes + 1
1287 n = mg%n_boxes
1288
1289 mg%boxes(n)%rank = 0
1290 mg%boxes(n)%id = n
1291 mg%boxes(n)%lvl = mg%lowest_lvl
1292 mg%boxes(n)%ix(:) = [i, j]
1293 mg%boxes(n)%r_min(:) = r_min + (mg%boxes(n)%ix(:) - 1) * &
1294 mg%box_size_lvl(mg%lowest_lvl) * mg%dr(:, mg%lowest_lvl)
1295 mg%boxes(n)%dr(:) = mg%dr(:, mg%lowest_lvl)
1296 mg%boxes(n)%parent = mg_no_box
1297 mg%boxes(n)%children(:) = mg_no_box
1298
1299 ! Set default neighbors
1300 mg%boxes(n)%neighbors(:) = [n-1, n+1, n-nx(1), n+nx(1)]
1301
1302 ! Handle boundaries
1303 ijk_vec = [i, j]
1304 do idim = 1, 2
1305 if (ijk_vec(idim) == 1) then
1306 if (periodic(idim)) then
1307 mg%boxes(n)%neighbors(2*idim-1) = n + periodic_offset(idim)
1308 else
1309 mg%boxes(n)%neighbors(2*idim-1) = mg_physical_boundary
1310 end if
1311 end if
1312
1313 if (ijk_vec(idim) == nx(idim)) then
1314 if (periodic(idim)) then
1315 mg%boxes(n)%neighbors(2*idim) = n - periodic_offset(idim)
1316 else
1317 mg%boxes(n)%neighbors(2*idim) = mg_physical_boundary
1318 end if
1319 end if
1320 end do
1321 end do; end do
1322
1323 mg%lvls(mg%lowest_lvl)%ids = [(n, n=1, mg%n_boxes)]
1324
1325 ! Add higher levels
1326 do lvl = mg%lowest_lvl, 0
1327 if (mg%box_size_lvl(lvl+1) == mg%box_size_lvl(lvl)) then
1328 do i = 1, size(mg%lvls(lvl)%ids)
1329 id = mg%lvls(lvl)%ids(i)
1330 call mg_add_children(mg, id)
1331 end do
1332
1333 call mg_set_leaves_parents(mg%boxes, mg%lvls(lvl))
1334 call mg_set_next_level_ids(mg, lvl)
1335 call mg_set_neighbors_lvl(mg, lvl+1)
1336 else
1337 do i = 1, size(mg%lvls(lvl)%ids)
1338 id = mg%lvls(lvl)%ids(i)
1339 call add_single_child(mg, id, size(mg%lvls(lvl)%ids))
1340 end do
1341
1342 call mg_set_leaves_parents(mg%boxes, mg%lvls(lvl))
1343 call mg_set_next_level_ids(mg, lvl)
1344 end if
1345 end do
1346
1347 call mg_set_leaves_parents(mg%boxes, mg%lvls(1))
1348
1349 ! No refinement boundaries
1350 do lvl = mg%lowest_lvl, 1
1351 if (allocated(mg%lvls(lvl)%ref_bnds)) &
1352 deallocate(mg%lvls(lvl)%ref_bnds)
1353 allocate(mg%lvls(lvl)%ref_bnds(0))
1354 end do
1355
1356 mg%tree_created = .true.
1357 end subroutine mg_build_rectangle
1358
1359 subroutine mg_set_neighbors_lvl(mg, lvl)
1360 type(mg_t), intent(inout) :: mg
1361 integer, intent(in) :: lvl
1362 integer :: i, id
1363
1364 do i = 1, size(mg%lvls(lvl)%ids)
1365 id = mg%lvls(lvl)%ids(i)
1366 call set_neighbs(mg%boxes, id)
1367 end do
1368 end subroutine mg_set_neighbors_lvl
1369
1370 subroutine mg_set_next_level_ids(mg, lvl)
1371 type(mg_t), intent(inout) :: mg
1372 integer, intent(in) :: lvl
1373 integer :: n, i, id
1374
1375 if (allocated(mg%lvls(lvl+1)%ids)) &
1376 deallocate(mg%lvls(lvl+1)%ids)
1377
1378 ! Set next level ids to children of this level
1379 if (mg%box_size_lvl(lvl+1) == mg%box_size_lvl(lvl)) then
1380 n = mg_num_children * size(mg%lvls(lvl)%parents)
1381 allocate(mg%lvls(lvl+1)%ids(n))
1382
1383 n = mg_num_children
1384 do i = 1, size(mg%lvls(lvl)%parents)
1385 id = mg%lvls(lvl)%parents(i)
1386 mg%lvls(lvl+1)%ids(n*(i-1)+1:n*i) = mg%boxes(id)%children
1387 end do
1388 else
1389 n = size(mg%lvls(lvl)%parents)
1390 allocate(mg%lvls(lvl+1)%ids(n))
1391
1392 n = 1
1393 do i = 1, size(mg%lvls(lvl)%parents)
1394 id = mg%lvls(lvl)%parents(i)
1395 mg%lvls(lvl+1)%ids(i) = mg%boxes(id)%children(1)
1396 end do
1397 end if
1398
1399 end subroutine mg_set_next_level_ids
1400
1401 ! Set the neighbors of id (using their parent)
1402 subroutine set_neighbs(boxes, id)
1403 type(mg_box_t), intent(inout) :: boxes(:)
1404 integer, intent(in) :: id
1405 integer :: nb, nb_id
1406
1407 do nb = 1, mg_num_neighbors
1408 if (boxes(id)%neighbors(nb) == mg_no_box) then
1409 nb_id = find_neighb(boxes, id, nb)
1410 if (nb_id > mg_no_box) then
1411 boxes(id)%neighbors(nb) = nb_id
1412 boxes(nb_id)%neighbors(mg_neighb_rev(nb)) = id
1413 end if
1414 end if
1415 end do
1416 end subroutine set_neighbs
1417
1418 !> Get the id of neighbor nb of boxes(id), through its parent
1419 function find_neighb(boxes, id, nb) result(nb_id)
1420 type(mg_box_t), intent(in) :: boxes(:) !< List with all the boxes
1421 integer, intent(in) :: id !< Box whose neighbor we are looking for
1422 integer, intent(in) :: nb !< Neighbor index
1423 integer :: nb_id, p_id, c_ix, d, old_pid
1424
1425 p_id = boxes(id)%parent
1426 old_pid = p_id
1427 c_ix = mg_ix_to_ichild(boxes(id)%ix)
1428 d = mg_neighb_dim(nb)
1429
1430 ! Check if neighbor is in same direction as ix is (low/high). If so,
1431 ! use neighbor of parent
1432 if (mg_child_low(d, c_ix) .eqv. mg_neighb_low(nb)) then
1433 p_id = boxes(p_id)%neighbors(nb)
1434 end if
1435
1436 ! The child ix of the neighbor is reversed in direction d
1437 nb_id = boxes(p_id)%children(mg_child_rev(c_ix, d))
1438 end function find_neighb
1439
1440 !> Create a list of leaves and a list of parents for a level
1441 subroutine mg_set_leaves_parents(boxes, level)
1442 type(mg_box_t), intent(in) :: boxes(:) !< List of boxes
1443 type(mg_lvl_t), intent(inout) :: level !< Level type which contains the indices of boxes
1444 integer :: i, id, i_leaf, i_parent
1445 integer :: n_parents, n_leaves
1446
1447 n_parents = count(mg_has_children(boxes(level%ids)))
1448 n_leaves = size(level%ids) - n_parents
1449
1450 if (.not. allocated(level%parents)) then
1451 allocate(level%parents(n_parents))
1452 else if (n_parents /= size(level%parents)) then
1453 deallocate(level%parents)
1454 allocate(level%parents(n_parents))
1455 end if
1456
1457 if (.not. allocated(level%leaves)) then
1458 allocate(level%leaves(n_leaves))
1459 else if (n_leaves /= size(level%leaves)) then
1460 deallocate(level%leaves)
1461 allocate(level%leaves(n_leaves))
1462 end if
1463
1464 i_leaf = 0
1465 i_parent = 0
1466 do i = 1, size(level%ids)
1467 id = level%ids(i)
1468 if (mg_has_children(boxes(id))) then
1469 i_parent = i_parent + 1
1470 level%parents(i_parent) = id
1471 else
1472 i_leaf = i_leaf + 1
1473 level%leaves(i_leaf) = id
1474 end if
1475 end do
1476 end subroutine mg_set_leaves_parents
1477
1478 !> Create a list of refinement boundaries (from the coarse side)
1479 subroutine mg_set_refinement_boundaries(boxes, level)
1480 type(mg_box_t), intent(in) :: boxes(:)
1481 type(mg_lvl_t), intent(inout) :: level
1482 integer, allocatable :: tmp(:)
1483 integer :: i, id, nb, nb_id, ix
1484
1485 if (allocated(level%ref_bnds)) deallocate(level%ref_bnds)
1486
1487 if (size(level%parents) == 0) then
1488 ! There are no refinement boundaries
1489 allocate(level%ref_bnds(0))
1490 else
1491 allocate(tmp(size(level%leaves)))
1492 ix = 0
1493 do i = 1, size(level%leaves)
1494 id = level%leaves(i)
1495
1496 do nb = 1, mg_num_neighbors
1497 nb_id = boxes(id)%neighbors(nb)
1498 if (nb_id > mg_no_box) then
1499 if (mg_has_children(boxes(nb_id))) then
1500 ix = ix + 1
1501 tmp(ix) = id
1502 exit
1503 end if
1504 end if
1505 end do
1506 end do
1507
1508 allocate(level%ref_bnds(ix))
1509 level%ref_bnds(:) = tmp(1:ix)
1510 end if
1511 end subroutine mg_set_refinement_boundaries
1512
1513 subroutine mg_add_children(mg, id)
1514 type(mg_t), intent(inout) :: mg
1515 integer, intent(in) :: id !< Id of box that gets children
1516 integer :: lvl, i, nb, child_nb(2**(2-1))
1517 integer :: c_ids(mg_num_children)
1518 integer :: c_id, c_ix_base(2)
1519
1520 if (mg%n_boxes + mg_num_children > size(mg%boxes)) then
1521 error stop "mg_add_children: not enough space"
1522 end if
1523
1524 c_ids = [(mg%n_boxes+i, i=1,mg_num_children)]
1525 mg%n_boxes = mg%n_boxes + mg_num_children
1526 mg%boxes(id)%children = c_ids
1527 c_ix_base = 2 * mg%boxes(id)%ix - 1
1528 lvl = mg%boxes(id)%lvl+1
1529
1530 do i = 1, mg_num_children
1531 c_id = c_ids(i)
1532 mg%boxes(c_id)%rank = mg%boxes(id)%rank
1533 mg%boxes(c_id)%ix = c_ix_base + mg_child_dix(:, i)
1534 mg%boxes(c_id)%lvl = lvl
1535 mg%boxes(c_id)%parent = id
1536 mg%boxes(c_id)%children = mg_no_box
1537 mg%boxes(c_id)%neighbors = mg_no_box
1538 mg%boxes(c_id)%r_min = mg%boxes(id)%r_min + &
1539 mg%dr(:, lvl) * mg_child_dix(:, i) * mg%box_size
1540 mg%boxes(c_id)%dr(:) = mg%dr(:, lvl)
1541 end do
1542
1543 ! Set boundary conditions at children
1544 do nb = 1, mg_num_neighbors
1545 if (mg%boxes(id)%neighbors(nb) < mg_no_box) then
1546 child_nb = c_ids(mg_child_adj_nb(:, nb)) ! Neighboring children
1547 mg%boxes(child_nb)%neighbors(nb) = mg%boxes(id)%neighbors(nb)
1548 end if
1549 end do
1550 end subroutine mg_add_children
1551
1552 subroutine add_single_child(mg, id, n_boxes_lvl)
1553 type(mg_t), intent(inout) :: mg
1554 integer, intent(in) :: id !< Id of box that gets children
1555 integer, intent(in) :: n_boxes_lvl
1556 integer :: lvl, c_id
1557
1558 c_id = mg%n_boxes + 1
1559 mg%n_boxes = mg%n_boxes + 1
1560 mg%boxes(id)%children(1) = c_id
1561 lvl = mg%boxes(id)%lvl+1
1562
1563 mg%boxes(c_id)%rank = mg%boxes(id)%rank
1564 mg%boxes(c_id)%ix = mg%boxes(id)%ix
1565 mg%boxes(c_id)%lvl = lvl
1566 mg%boxes(c_id)%parent = id
1567 mg%boxes(c_id)%children = mg_no_box
1568 where (mg%boxes(id)%neighbors == mg_physical_boundary)
1569 mg%boxes(c_id)%neighbors = mg%boxes(id)%neighbors
1570 elsewhere
1571 mg%boxes(c_id)%neighbors = mg%boxes(id)%neighbors + n_boxes_lvl
1572 end where
1573 mg%boxes(c_id)%r_min = mg%boxes(id)%r_min
1574 mg%boxes(c_id)%dr(:) = mg%dr(:, lvl)
1575
1576 end subroutine add_single_child
1577
1578 !! File ../src/m_load_balance.f90
1579
1580 !> Load balance all boxes in the multigrid tree, by simply distributing the
1581 !> load per grid level. This method will only work well for uniform grids.
1582 !>
1583 !> Note that in a typical application the load balancing of the leaves is
1584 !> already determined, then mg_load_balance_parents can be used.
1586 type(mg_t), intent(inout) :: mg
1587 integer :: i, id, lvl, single_cpu_lvl
1588 integer :: work_left, my_work, i_cpu
1589
1590 ! Up to this level, all boxes have to be on a single processor because they
1591 ! have a different size and the communication routines do not support this
1592 single_cpu_lvl = max(mg%first_normal_lvl-1, mg%lowest_lvl)
1593
1594 do lvl = mg%lowest_lvl, single_cpu_lvl
1595 do i = 1, size(mg%lvls(lvl)%ids)
1596 id = mg%lvls(lvl)%ids(i)
1597 mg%boxes(id)%rank = 0
1598 end do
1599 end do
1600
1601 ! Distribute the boxes equally. Due to the way the mesh is constructed, the
1602 ! mg%lvls(lvl)%ids array already contains a Morton-like ordering.
1603 do lvl = single_cpu_lvl+1, mg%highest_lvl
1604 work_left = size(mg%lvls(lvl)%ids)
1605 my_work = 0
1606 i_cpu = 0
1607
1608 do i = 1, size(mg%lvls(lvl)%ids)
1609 if ((mg%n_cpu - i_cpu - 1) * my_work >= work_left) then
1610 i_cpu = i_cpu + 1
1611 my_work = 0
1612 end if
1613
1614 my_work = my_work + 1
1615 work_left = work_left - 1
1616
1617 id = mg%lvls(lvl)%ids(i)
1618 mg%boxes(id)%rank = i_cpu
1619 end do
1620 end do
1621
1622 do lvl = mg%lowest_lvl, mg%highest_lvl
1623 call update_lvl_info(mg, mg%lvls(lvl))
1624 end do
1625
1626 end subroutine mg_load_balance_simple
1627
1628 !> Load balance all boxes in the multigrid tree. Compared to
1629 !> mg_load_balance_simple, this method does a better job of setting the ranks
1630 !> of parent boxes
1631 !>
1632 !> Note that in a typical application the load balancing of the leaves is
1633 !> already determined, then mg_load_balance_parents can be used.
1634 subroutine mg_load_balance(mg)
1635 type(mg_t), intent(inout) :: mg
1636 integer :: i, id, lvl, single_cpu_lvl
1637 integer :: work_left, my_work(0:mg%n_cpu), i_cpu
1638 integer :: c_ids(mg_num_children)
1639 integer :: c_ranks(mg_num_children)
1640 integer :: coarse_rank
1641
1642 ! Up to this level, all boxes have to be on a single processor because they
1643 ! have a different size and the communication routines do not support this
1644 single_cpu_lvl = max(mg%first_normal_lvl-1, mg%lowest_lvl)
1645
1646 ! Distribute the boxes equally. Due to the way the mesh is constructed, the
1647 ! mg%lvls(lvl)%ids array already contains a Morton-like ordering.
1648 do lvl = mg%highest_lvl, single_cpu_lvl+1, -1
1649 ! For parents determine the rank based on their child ranks
1650 my_work(:) = 0
1651
1652 do i = 1, size(mg%lvls(lvl)%parents)
1653 id = mg%lvls(lvl)%parents(i)
1654
1655 c_ids = mg%boxes(id)%children
1656 c_ranks = mg%boxes(c_ids)%rank
1657 i_cpu = most_popular(c_ranks, my_work, mg%n_cpu)
1658 mg%boxes(id)%rank = i_cpu
1659 my_work(i_cpu) = my_work(i_cpu) + 1
1660 end do
1661
1662 work_left = size(mg%lvls(lvl)%leaves)
1663 i_cpu = 0
1664
1665 do i = 1, size(mg%lvls(lvl)%leaves)
1666 ! Skip this CPU if it already has enough work
1667 if ((mg%n_cpu - i_cpu - 1) * my_work(i_cpu) >= &
1668 work_left + sum(my_work(i_cpu+1:))) then
1669 i_cpu = i_cpu + 1
1670 end if
1671
1672 my_work(i_cpu) = my_work(i_cpu) + 1
1673 work_left = work_left - 1
1674
1675 id = mg%lvls(lvl)%leaves(i)
1676 mg%boxes(id)%rank = i_cpu
1677 end do
1678 end do
1679
1680 ! Determine most popular CPU for coarse grids
1681 if (single_cpu_lvl < mg%highest_lvl) then
1682 coarse_rank = most_popular(mg%boxes(&
1683 mg%lvls(single_cpu_lvl+1)%ids)%rank, my_work, mg%n_cpu)
1684 else
1685 coarse_rank = 0
1686 end if
1687
1688 do lvl = mg%lowest_lvl, single_cpu_lvl
1689 do i = 1, size(mg%lvls(lvl)%ids)
1690 id = mg%lvls(lvl)%ids(i)
1691 mg%boxes(id)%rank = coarse_rank
1692 end do
1693 end do
1694
1695 do lvl = mg%lowest_lvl, mg%highest_lvl
1696 call update_lvl_info(mg, mg%lvls(lvl))
1697 end do
1698
1699 end subroutine mg_load_balance
1700
1701 !> Load balance the parents (non-leafs). Assign them to the rank that has most
1702 !> children.
1704 type(mg_t), intent(inout) :: mg
1705 integer :: i, id, lvl, n_boxes
1706 integer :: c_ids(mg_num_children)
1707 integer :: c_ranks(mg_num_children)
1708 integer :: single_cpu_lvl, coarse_rank
1709 integer :: my_work(0:mg%n_cpu), i_cpu
1710 integer, allocatable :: ranks(:)
1711
1712 ! Up to this level, all boxes have to be on a single processor because they
1713 ! have a different size and the communication routines do not support this
1714 single_cpu_lvl = max(mg%first_normal_lvl-1, mg%lowest_lvl)
1715
1716 do lvl = mg%highest_lvl-1, single_cpu_lvl+1, -1
1717 my_work(:) = 0
1718
1719 ! Determine amount of work for the leaves
1720 do i = 1, size(mg%lvls(lvl)%leaves)
1721 id = mg%lvls(lvl)%leaves(i)
1722 i_cpu = mg%boxes(id)%rank
1723 my_work(i_cpu) = my_work(i_cpu) + 1
1724 end do
1725
1726 do i = 1, size(mg%lvls(lvl)%parents)
1727 id = mg%lvls(lvl)%parents(i)
1728
1729 c_ids = mg%boxes(id)%children
1730 c_ranks = mg%boxes(c_ids)%rank
1731 i_cpu = most_popular(c_ranks, my_work, mg%n_cpu)
1732 mg%boxes(id)%rank = i_cpu
1733 my_work(i_cpu) = my_work(i_cpu) + 1
1734 end do
1735
1736 end do
1737
1738 ! Determine most popular CPU for coarse grids
1739 if (single_cpu_lvl < mg%highest_lvl) then
1740 ! Get ranks of boxes at single_cpu_lvl+1
1741 n_boxes = size(mg%lvls(single_cpu_lvl+1)%ids)
1742 allocate(ranks(n_boxes))
1743 ranks(:) = mg%boxes(mg%lvls(single_cpu_lvl+1)%ids)%rank
1744
1745 coarse_rank = most_popular(ranks, my_work, mg%n_cpu)
1746 deallocate(ranks)
1747 else
1748 coarse_rank = 0
1749 end if
1750
1751 do lvl = mg%lowest_lvl, single_cpu_lvl
1752 do i = 1, size(mg%lvls(lvl)%ids)
1753 id = mg%lvls(lvl)%ids(i)
1754 mg%boxes(id)%rank = coarse_rank
1755 end do
1756 end do
1757
1758 do lvl = mg%lowest_lvl, mg%highest_lvl
1759 call update_lvl_info(mg, mg%lvls(lvl))
1760 end do
1761
1762 end subroutine mg_load_balance_parents
1763
1764 !> Determine most popular rank in the list. In case of ties, assign the rank
1765 !> with the least work.
1766 pure integer function most_popular(list, work, n_cpu)
1767 integer, intent(in) :: list(:) !< List of MPI ranks
1768 integer, intent(in) :: n_cpu
1769 integer, intent(in) :: work(0:n_cpu-1) !< Existing work per rank
1770 integer :: i, best_count, current_count
1771 integer :: my_work, best_work
1772
1773 best_count = 0
1774 best_work = 0
1775 most_popular = -1
1776
1777 do i = 1, size(list)
1778 current_count = count(list == list(i))
1779 my_work = work(list(i))
1780
1781 ! In case of ties, select task with lowest work
1782 if (current_count > best_count .or. &
1783 current_count == best_count .and. my_work < best_work) then
1784 best_count = current_count
1785 best_work = my_work
1786 most_popular = list(i)
1787 end if
1788 end do
1789
1790 end function most_popular
1791
1792 subroutine update_lvl_info(mg, lvl)
1793 type(mg_t), intent(inout) :: mg
1794 type(mg_lvl_t), intent(inout) :: lvl
1795
1796 lvl%my_ids = pack(lvl%ids, &
1797 mg%boxes(lvl%ids)%rank == mg%my_rank)
1798 lvl%my_leaves = pack(lvl%leaves, &
1799 mg%boxes(lvl%leaves)%rank == mg%my_rank)
1800 lvl%my_parents = pack(lvl%parents, &
1801 mg%boxes(lvl%parents)%rank == mg%my_rank)
1802 lvl%my_ref_bnds = pack(lvl%ref_bnds, &
1803 mg%boxes(lvl%ref_bnds)%rank == mg%my_rank)
1804 end subroutine update_lvl_info
1805
1806 !! File ../src/m_vlaplacian.f90
1807
1809 type(mg_t), intent(inout) :: mg
1810
1811 if (mg%n_extra_vars == 0 .and. mg%is_allocated) then
1812 error stop "vlaplacian_set_methods: mg%n_extra_vars == 0"
1813 else
1814 mg%n_extra_vars = max(1, mg%n_extra_vars)
1815 end if
1816
1817 mg%subtract_mean = .false.
1818
1819 ! Use Neumann zero boundary conditions for the variable coefficient, since
1820 ! it is needed in ghost cells.
1821 mg%bc(:, mg_iveps)%bc_type = mg_bc_neumann
1822 mg%bc(:, mg_iveps)%bc_value = 0.0_dp
1823
1824 select case (mg%geometry_type)
1825 case (mg_cartesian)
1826 mg%box_op => box_vlpl
1827
1828 ! TODO: test whether a custom prolongation works better
1829 ! mg%box_prolong => vlpl_prolong
1830
1831 select case (mg%smoother_type)
1833 mg%box_smoother => box_gs_vlpl
1834 case default
1835 error stop "vlaplacian_set_methods: unsupported smoother type"
1836 end select
1837
1838 case default
1839 error stop "vlaplacian_set_methods: unsupported geometry"
1840 end select
1841
1842 end subroutine vlaplacian_set_methods
1843
1844 !> Perform Gauss-Seidel relaxation on box for a Laplacian operator
1845 subroutine box_gs_vlpl(mg, id, nc, redblack_cntr)
1846 type(mg_t), intent(inout) :: mg
1847 integer, intent(in) :: id
1848 integer, intent(in) :: nc
1849 integer, intent(in) :: redblack_cntr !< Iteration counter
1850 integer :: i, j, i0, di
1851 logical :: redblack
1852 real(dp) :: idr2(2*2), u(2*2)
1853 real(dp) :: a0, a(2*2), c(2*2)
1854
1855 ! Duplicate 1/dr^2 array to multiply neighbor values
1856 idr2(1:2*2:2) = 1/mg%dr(:, mg%boxes(id)%lvl)**2
1857 idr2(2:2*2:2) = idr2(1:2*2:2)
1858 i0 = 1
1859
1860 redblack = (mg%smoother_type == mg_smoother_gsrb)
1861 if (redblack) then
1862 di = 2
1863 else
1864 di = 1
1865 end if
1866
1867 ! The parity of redblack_cntr determines which cells we use. If
1868 ! redblack_cntr is even, we use the even cells and vice versa.
1869 associate(cc => mg%boxes(id)%cc, n => mg_iphi, &
1870 i_eps => mg_iveps)
1871 do j = 1, nc
1872 if (redblack) &
1873 i0 = 2 - iand(ieor(redblack_cntr, j), 1)
1874
1875 do i = i0, nc, di
1876 a0 = cc(i, j, i_eps)
1877 u(1:2) = cc(i-1:i+1:2, j, n)
1878 a(1:2) = cc(i-1:i+1:2, j, i_eps)
1879 u(3:4) = cc(i, j-1:j+1:2, n)
1880 a(3:4) = cc(i, j-1:j+1:2, i_eps)
1881 c(:) = 2 * a0 * a(:) / (a0 + a(:)) * idr2
1882
1883 cc(i, j, n) = &
1884 (sum(c(:) * u(:)) - cc(i, j, mg_irhs)) / sum(c(:))
1885 end do
1886 end do
1887 end associate
1888 end subroutine box_gs_vlpl
1889
1890 !> Perform Laplacian operator on a box
1891 subroutine box_vlpl(mg, id, nc, i_out)
1892 type(mg_t), intent(inout) :: mg
1893 integer, intent(in) :: id
1894 integer, intent(in) :: nc
1895 integer, intent(in) :: i_out !< Index of variable to store Laplacian in
1896 integer :: i, j
1897 real(dp) :: idr2(2*2), a0, u0, u(2*2), a(2*2)
1898
1899 ! Duplicate 1/dr^2 array to multiply neighbor values
1900 idr2(1:2*2:2) = 1/mg%dr(:, mg%boxes(id)%lvl)**2
1901 idr2(2:2*2:2) = idr2(1:2*2:2)
1902
1903 associate(cc => mg%boxes(id)%cc, n => mg_iphi, &
1904 i_eps => mg_iveps)
1905 do j = 1, nc
1906 do i = 1, nc
1907 a0 = cc(i, j, i_eps)
1908 a(1:2) = cc(i-1:i+1:2, j, i_eps)
1909 a(3:4) = cc(i, j-1:j+1:2, i_eps)
1910 u0 = cc(i, j, n)
1911 u(1:2) = cc(i-1:i+1:2, j, n)
1912 u(3:4) = cc(i, j-1:j+1:2, n)
1913
1914 cc(i, j, i_out) = sum(2 * idr2 * &
1915 a0*a(:)/(a0 + a(:)) * (u(:) - u0))
1916 end do
1917 end do
1918 end associate
1919 end subroutine box_vlpl
1920
1921 !> Prolong from a parent to a child with index offset dix. This method could
1922 !> sometimes work better than the default prolongation, which does not take
1923 !> the variation in epsilon into account
1924! subroutine vlpl_prolong(mg, p_id, dix, nc, iv, fine)
1925! type(mg_t), intent(inout) :: mg
1926! integer, intent(in) :: p_id !< Id of parent
1927! integer, intent(in) :: dix(2) !< Offset of child in parent grid
1928! integer, intent(in) :: nc !< Child grid size
1929! integer, intent(in) :: iv !< Prolong from this variable
1930! real(dp), intent(out) :: fine(nc, nc) !< Prolonged values
1931
1932! integer :: i, j, hnc
1933! #if 2 == 2
1934! integer :: ic, jc
1935! real(dp) :: f0, flx, fhx, fly, fhy
1936! real(dp) :: c0, clx, chx, cly, chy
1937! #elif 2 == 3
1938! integer :: ic, jc, kc
1939! real(dp) :: f0, flx, fhx, fly, fhy, flz, fhz
1940! real(dp) :: c0, clx, chx, cly, chy, clz, chz
1941! #endif
1942
1943! hnc = nc/2
1944
1945! associate (crs => mg%boxes(p_id)%cc, &
1946! i_eps => mg_iveps)
1947! #if 2 == 2
1948! do j = 1, hnc
1949! jc = j + dix(2)
1950! do i = 1, hnc
1951! ic = i + dix(1)
1952
1953! f0 = crs(ic, jc, iv)
1954! flx = crs(ic-1, jc, iv)
1955! fhx = crs(ic+1, jc, iv)
1956! fly = crs(ic, jc-1, iv)
1957! fhy = crs(ic, jc+1, iv)
1958
1959! c0 = 2 * crs(ic, jc, i_eps)
1960! clx = crs(ic-1, jc, i_eps)
1961! chx = crs(ic+1, jc, i_eps)
1962! cly = crs(ic, jc-1, i_eps)
1963! chy = crs(ic, jc+1, i_eps)
1964
1965! fine(2*i-1, 2*j-1) = (f0*c0 + flx*clx + fly*cly) / &
1966! (c0 + clx + cly)
1967! fine(2*i , 2*j-1) = (f0*c0 + fhx*chx + fly*cly) / &
1968! (c0 + chx + cly)
1969! fine(2*i-1, 2*j) = (f0*c0 + flx*clx + fhy*chy) / &
1970! (c0 + clx + chy)
1971! fine(2*i , 2*j) = (f0*c0 + fhx*chx + fhy*chy) / &
1972! (c0 + chx + chy)
1973! end do
1974! end do
1975! #elif 2 == 3
1976! do k = 1, hnc
1977! kc = k + dix(3)
1978! do j = 1, hnc
1979! jc = j + dix(2)
1980! do i = 1, hnc
1981! ic = i + dix(1)
1982
1983! f0 = crs(ic, jc, kc, iv)
1984! flx = crs(ic-1, jc, kc, iv)
1985! fhx = crs(ic+1, jc, kc, iv)
1986! fly = crs(ic, jc-1, kc, iv)
1987! fhy = crs(ic, jc+1, kc, iv)
1988! flz = crs(ic, jc, kc-1, iv)
1989! fhz = crs(ic, jc, kc+1, iv)
1990
1991! c0 = crs(ic, jc, kc, i_eps)
1992! clx = crs(ic-1, jc, kc, i_eps)
1993! chx = crs(ic+1, jc, kc, i_eps)
1994! cly = crs(ic, jc-1, kc, i_eps)
1995! chy = crs(ic, jc+1, kc, i_eps)
1996! clz = crs(ic, jc, kc-1, i_eps)
1997! chz = crs(ic, jc, kc+1, i_eps)
1998
1999! fine(2*i-1, 2*j-1, 2*k-1) = (f0*c0 + flx*clx + fly*cly + flz*clz) / &
2000! (c0 + clx + cly + clz)
2001! fine(2*i, 2*j-1, 2*k-1) = (f0*c0 + fhx*chx + fly*cly + flz*clz) / &
2002! (c0 + chx + cly + clz)
2003! fine(2*i-1, 2*j, 2*k-1) = (f0*c0 + flx*clx + fhy*chy + flz*clz) / &
2004! (c0 + clx + chy + clz)
2005! fine(2*i, 2*j, 2*k-1) = (f0*c0 + fhx*chx + fhy*chy + flz*clz) / &
2006! (c0 + chx + chy + clz)
2007! fine(2*i-1, 2*j-1, 2*k) = (f0*c0 + flx*clx + fly*cly + fhz*chz) / &
2008! (c0 + clx + cly + chz)
2009! fine(2*i, 2*j-1, 2*k) = (f0*c0 + fhx*chx + fly*cly + fhz*chz) / &
2010! (c0 + chx + cly + chz)
2011! fine(2*i-1, 2*j, 2*k) = (f0*c0 + flx*clx + fhy*chy + fhz*chz) / &
2012! (c0 + clx + chy + chz)
2013! fine(2*i, 2*j, 2*k) = (f0*c0 + fhx*chx + fhy*chy + fhz*chz) / &
2014! (c0 + chx + chy + chz)
2015! end do
2016! end do
2017! end do
2018! #endif
2019! end associate
2020! end subroutine vlpl_prolong
2021
2022 !! File ../src/m_communication.f90
2023
2024 !> Initialize MPI if needed, and store MPI information
2025 subroutine mg_comm_init(mg, comm)
2026 use mpi
2027 type(mg_t), intent(inout) :: mg
2028 !> MPI communicator (default: MPI_COMM_WORLD)
2029 integer, intent(in), optional :: comm
2030 integer :: ierr
2031 logical :: initialized
2032
2033 call mpi_initialized(initialized, ierr)
2034 if (.not. initialized) then
2035 call mpi_init(ierr)
2036 end if
2037
2038 if (present(comm)) then
2039 mg%comm = comm
2040 else
2041 mg%comm = mpi_comm_world
2042 end if
2043
2044 call mpi_comm_rank(mg%comm, mg%my_rank, ierr)
2045 call mpi_comm_size(mg%comm, mg%n_cpu, ierr)
2046 end subroutine mg_comm_init
2047
2048 subroutine sort_and_transfer_buffers(mg, dsize)
2049 use mpi
2050 type(mg_t), intent(inout) :: mg
2051 integer, intent(in) :: dsize
2052 integer :: i, n_send, n_recv
2053 integer :: send_req(mg%n_cpu)
2054 integer :: recv_req(mg%n_cpu)
2055 integer :: ierr
2056
2057 n_send = 0
2058 n_recv = 0
2059
2060 do i = 0, mg%n_cpu - 1
2061 if (mg%buf(i)%i_send > 0) then
2062 n_send = n_send + 1
2063 call sort_sendbuf(mg%buf(i), dsize)
2064 call mpi_isend(mg%buf(i)%send, mg%buf(i)%i_send, mpi_double, &
2065 i, 0, mg%comm, send_req(n_send), ierr)
2066 end if
2067 if (mg%buf(i)%i_recv > 0) then
2068 n_recv = n_recv + 1
2069 call mpi_irecv(mg%buf(i)%recv, mg%buf(i)%i_recv, mpi_double, &
2070 i, 0, mg%comm, recv_req(n_recv), ierr)
2071 end if
2072 end do
2073
2074 call mpi_waitall(n_recv, recv_req(1:n_recv), mpi_statuses_ignore, ierr)
2075 call mpi_waitall(n_send, send_req(1:n_send), mpi_statuses_ignore, ierr)
2076
2077 end subroutine sort_and_transfer_buffers
2078
2079 !> Sort send buffers according to the idbuf array
2080 subroutine sort_sendbuf(gc, dsize)
2081
2082 type(mg_buf_t), intent(inout) :: gc
2083 integer, intent(in) :: dsize !< Size of send buffer elements
2084 integer :: ix_sort(gc%i_ix)
2085 real(dp) :: buf_cpy(gc%i_send)
2086 integer :: i, j, n
2087
2088 call mrgrnk(gc%ix(1:gc%i_ix), ix_sort)
2089
2090 buf_cpy = gc%send(1:gc%i_send)
2091
2092 do n = 1, gc%i_ix
2093 i = (n-1) * dsize
2094 j = (ix_sort(n)-1) * dsize
2095 gc%send(i+1:i+dsize) = buf_cpy(j+1:j+dsize)
2096 end do
2097 gc%ix(1:gc%i_ix) = gc%ix(ix_sort)
2098
2099 end subroutine sort_sendbuf
2100
2101 !! File ../src/m_ghost_cells.f90
2102
2103 !> Specify minimum buffer size (per process) for communication
2104 subroutine mg_ghost_cell_buffer_size(mg, n_send, n_recv, dsize)
2105 type(mg_t), intent(inout) :: mg
2106 integer, intent(out) :: n_send(0:mg%n_cpu-1)
2107 integer, intent(out) :: n_recv(0:mg%n_cpu-1)
2108 integer, intent(out) :: dsize
2109 integer :: i, id, lvl, nc
2110
2111 allocate(mg%comm_ghostcell%n_send(0:mg%n_cpu-1, &
2112 mg%first_normal_lvl:mg%highest_lvl))
2113 allocate(mg%comm_ghostcell%n_recv(0:mg%n_cpu-1, &
2114 mg%first_normal_lvl:mg%highest_lvl))
2115
2116 dsize = mg%box_size**(2-1)
2117
2118 do lvl = mg%first_normal_lvl, mg%highest_lvl
2119 nc = mg%box_size_lvl(lvl)
2120 mg%buf(:)%i_send = 0
2121 mg%buf(:)%i_recv = 0
2122 mg%buf(:)%i_ix = 0
2123
2124 do i = 1, size(mg%lvls(lvl)%my_ids)
2125 id = mg%lvls(lvl)%my_ids(i)
2126 call buffer_ghost_cells(mg, id, nc, 1, dry_run=.true.)
2127 end do
2128
2129 if (lvl > 1) then
2130 do i = 1, size(mg%lvls(lvl-1)%my_ref_bnds)
2131 id = mg%lvls(lvl-1)%my_ref_bnds(i)
2132 call buffer_refinement_boundaries(mg, id, nc, 1, dry_run=.true.)
2133 end do
2134 end if
2135
2136 ! Set ghost cells to received data
2137 mg%buf(:)%i_recv = 0
2138 do i = 1, size(mg%lvls(lvl)%my_ids)
2139 id = mg%lvls(lvl)%my_ids(i)
2140 call set_ghost_cells(mg, id, nc, 1, dry_run=.true.)
2141 end do
2142
2143 mg%comm_ghostcell%n_send(:, lvl) = mg%buf(:)%i_send/dsize
2144 mg%comm_ghostcell%n_recv(:, lvl) = mg%buf(:)%i_recv/dsize
2145 end do
2146
2147 n_send = maxval(mg%comm_ghostcell%n_send, dim=2)
2148 n_recv = maxval(mg%comm_ghostcell%n_recv, dim=2)
2149 end subroutine mg_ghost_cell_buffer_size
2150
2151 !> Store boundary conditions for the solution variable, this can speed up
2152 !> calculations if the same boundary conditions are re-used.
2153 subroutine mg_phi_bc_store(mg)
2154 type(mg_t), intent(inout) :: mg
2155 integer :: lvl, nc
2156
2157 nc = mg%box_size
2158
2159 do lvl = mg%lowest_lvl, mg%highest_lvl
2160 nc = mg%box_size_lvl(lvl)
2161 call mg_phi_bc_store_lvl(mg, lvl, nc)
2162 end do
2163
2164 mg%phi_bc_data_stored = .true.
2165 end subroutine mg_phi_bc_store
2166
2167 subroutine mg_phi_bc_store_lvl(mg, lvl, nc)
2168 type(mg_t), intent(inout) :: mg
2169 integer, intent(in) :: lvl
2170 integer, intent(in) :: nc
2171 real(dp) :: bc(nc)
2172 integer :: i, id, nb, nb_id, bc_type
2173
2174 do i = 1, size(mg%lvls(lvl)%my_ids)
2175 id = mg%lvls(lvl)%my_ids(i)
2176 do nb = 1, mg_num_neighbors
2177 nb_id = mg%boxes(id)%neighbors(nb)
2178 if (nb_id < mg_no_box) then
2179 ! Physical boundary
2180 if (associated(mg%bc(nb, mg_iphi)%boundary_cond)) then
2181 call mg%bc(nb, mg_iphi)%boundary_cond(mg%boxes(id), nc, &
2182 mg_iphi, nb, bc_type, bc)
2183 else
2184 bc_type = mg%bc(nb, mg_iphi)%bc_type
2185 bc = mg%bc(nb, mg_iphi)%bc_value
2186 end if
2187
2188 ! Store the boundary condition type. This is not globally set in
2189 ! the tree, but all negative values are treated the same in
2190 ! other parts of the code
2191 mg%boxes(id)%neighbors(nb) = bc_type
2192
2193 ! Store ghost cell data in the right-hand side
2194 call box_set_gc(mg%boxes(id), nb, nc, mg_irhs, bc)
2195 end if
2196 end do
2197 end do
2198 end subroutine mg_phi_bc_store_lvl
2199
2200 !> Fill ghost cells at all grid levels
2201 subroutine mg_fill_ghost_cells(mg, iv)
2202 type(mg_t) :: mg
2203 integer, intent(in) :: iv !< Index of variable
2204 integer :: lvl
2205
2206 do lvl = mg%lowest_lvl, mg%highest_lvl
2207 call mg_fill_ghost_cells_lvl(mg, lvl, iv)
2208 end do
2209 end subroutine mg_fill_ghost_cells
2210
2211 !> Fill ghost cells at a grid level
2212 subroutine mg_fill_ghost_cells_lvl(mg, lvl, iv)
2213
2214 type(mg_t) :: mg
2215 integer, intent(in) :: lvl
2216 integer, intent(in) :: iv !< Index of variable
2217 integer :: i, id, dsize, nc
2218
2219 if (lvl < mg%lowest_lvl) &
2220 error stop "fill_ghost_cells_lvl: lvl < lowest_lvl"
2221 if (lvl > mg%highest_lvl) &
2222 error stop "fill_ghost_cells_lvl: lvl > highest_lvl"
2223
2224 nc = mg%box_size_lvl(lvl)
2225
2226 if (lvl >= mg%first_normal_lvl) then
2227 dsize = nc**(2-1)
2228 mg%buf(:)%i_send = 0
2229 mg%buf(:)%i_recv = 0
2230 mg%buf(:)%i_ix = 0
2231
2232 do i = 1, size(mg%lvls(lvl)%my_ids)
2233 id = mg%lvls(lvl)%my_ids(i)
2234 call buffer_ghost_cells(mg, id, nc, iv, .false.)
2235 end do
2236
2237 if (lvl > 1) then
2238 do i = 1, size(mg%lvls(lvl-1)%my_ref_bnds)
2239 id = mg%lvls(lvl-1)%my_ref_bnds(i)
2240 call buffer_refinement_boundaries(mg, id, nc, iv, .false.)
2241 end do
2242 end if
2243
2244 ! Transfer data between processes
2245 mg%buf(:)%i_recv = mg%comm_ghostcell%n_recv(:, lvl) * dsize
2246 call sort_and_transfer_buffers(mg, dsize)
2247
2248 ! Set ghost cells to received data
2249 mg%buf(:)%i_recv = 0
2250 end if
2251
2252 do i = 1, size(mg%lvls(lvl)%my_ids)
2253 id = mg%lvls(lvl)%my_ids(i)
2254 call set_ghost_cells(mg, id, nc, iv, .false.)
2255 end do
2256 end subroutine mg_fill_ghost_cells_lvl
2257
2258 subroutine buffer_ghost_cells(mg, id, nc, iv, dry_run)
2259 type(mg_t), intent(inout) :: mg
2260 integer, intent(in) :: id
2261 integer, intent(in) :: nc
2262 integer, intent(in) :: iv
2263 logical, intent(in) :: dry_run
2264 integer :: nb, nb_id, nb_rank
2265
2266 do nb = 1, mg_num_neighbors
2267 nb_id = mg%boxes(id)%neighbors(nb)
2268
2269 if (nb_id > mg_no_box) then
2270 ! There is a neighbor
2271 nb_rank = mg%boxes(nb_id)%rank
2272
2273 if (nb_rank /= mg%my_rank) then
2274 call buffer_for_nb(mg, mg%boxes(id), nc, iv, nb_id, nb_rank, &
2275 nb, dry_run)
2276 end if
2277 end if
2278 end do
2279 end subroutine buffer_ghost_cells
2280
2281 subroutine buffer_refinement_boundaries(mg, id, nc, iv, dry_run)
2282 type(mg_t), intent(inout) :: mg
2283 integer, intent(in) :: id
2284 integer, intent(in) :: nc
2285 integer, intent(in) :: iv
2286 logical, intent(in) :: dry_run
2287 integer :: nb, nb_id, c_ids(2**(2-1))
2288 integer :: n, c_id, c_rank
2289
2290 do nb = 1, mg_num_neighbors
2291 nb_id = mg%boxes(id)%neighbors(nb)
2292 if (nb_id > mg_no_box) then
2293 if (mg_has_children(mg%boxes(nb_id))) then
2294 c_ids = mg%boxes(nb_id)%children(&
2296
2297 do n = 1, mg_num_children/2
2298 c_id = c_ids(n)
2299 c_rank = mg%boxes(c_id)%rank
2300
2301 if (c_rank /= mg%my_rank) then
2302 ! Send all coarse ghost cells
2303 call buffer_for_fine_nb(mg, mg%boxes(id), nc, iv, c_id, &
2304 c_rank, nb, dry_run)
2305 end if
2306 end do
2307 end if
2308 end if
2309 end do
2310 end subroutine buffer_refinement_boundaries
2311
2312 !> The routine that actually fills the ghost cells
2313 subroutine set_ghost_cells(mg, id, nc, iv, dry_run)
2314 type(mg_t), intent(inout) :: mg
2315 integer, intent(in) :: id
2316 integer, intent(in) :: nc
2317 integer, intent(in) :: iv
2318 logical, intent(in) :: dry_run
2319 real(dp) :: bc(nc)
2320 integer :: nb, nb_id, nb_rank, bc_type
2321
2322 do nb = 1, mg_num_neighbors
2323 nb_id = mg%boxes(id)%neighbors(nb)
2324
2325 if (nb_id > mg_no_box) then
2326 ! There is a neighbor
2327 nb_rank = mg%boxes(nb_id)%rank
2328
2329 if (nb_rank /= mg%my_rank) then
2330 call fill_buffered_nb(mg, mg%boxes(id), nb_rank, &
2331 nb, nc, iv, dry_run)
2332 else if (.not. dry_run) then
2333 call copy_from_nb(mg%boxes(id), mg%boxes(nb_id), &
2334 nb, nc, iv)
2335 end if
2336 else if (nb_id == mg_no_box) then
2337 ! Refinement boundary
2338 call fill_refinement_bnd(mg, id, nb, nc, iv, dry_run)
2339 else if (.not. dry_run) then
2340 ! Physical boundary
2341 if (mg%phi_bc_data_stored .and. iv == mg_iphi) then
2342 ! Copy the boundary conditions stored in the ghost cells of the
2343 ! right-hand side
2344 call box_get_gc(mg%boxes(id), nb, nc, mg_irhs, bc)
2345 bc_type = nb_id
2346 else
2347 if (associated(mg%bc(nb, iv)%boundary_cond)) then
2348 call mg%bc(nb, iv)%boundary_cond(mg%boxes(id), nc, iv, &
2349 nb, bc_type, bc)
2350 else
2351 bc_type = mg%bc(nb, iv)%bc_type
2352 bc = mg%bc(nb, iv)%bc_value
2353 end if
2354 end if
2355
2356 call box_set_gc(mg%boxes(id), nb, nc, iv, bc)
2357 call bc_to_gc(mg, id, nc, iv, nb, bc_type)
2358 end if
2359 end do
2360 end subroutine set_ghost_cells
2361
2362 subroutine fill_refinement_bnd(mg, id, nb, nc, iv, dry_run)
2363 type(mg_t), intent(inout) :: mg
2364 integer, intent(in) :: id
2365 integer, intent(in) :: nc
2366 integer, intent(in) :: iv
2367 integer, intent(in) :: nb
2368 logical, intent(in) :: dry_run
2369 real(dp) :: gc(nc)
2370 integer :: p_id, p_nb_id, ix_offset(2)
2371 integer :: i, dsize, p_nb_rank
2372
2373 dsize = nc**(2-1)
2374 p_id = mg%boxes(id)%parent
2375 p_nb_id = mg%boxes(p_id)%neighbors(nb)
2376 p_nb_rank = mg%boxes(p_nb_id)%rank
2377
2378 if (p_nb_rank /= mg%my_rank) then
2379 i = mg%buf(p_nb_rank)%i_recv
2380 if (.not. dry_run) then
2381 gc = reshape(mg%buf(p_nb_rank)%recv(i+1:i+dsize), shape(gc))
2382 end if
2383 mg%buf(p_nb_rank)%i_recv = mg%buf(p_nb_rank)%i_recv + dsize
2384 else if (.not. dry_run) then
2385 ix_offset = mg_get_child_offset(mg, id)
2386 call box_gc_for_fine_neighbor(mg%boxes(p_nb_id), mg_neighb_rev(nb), &
2387 ix_offset, nc, iv, gc)
2388 end if
2389
2390 if (.not. dry_run) then
2391 if (associated(mg%bc(nb, iv)%refinement_bnd)) then
2392 call mg%bc(nb, iv)%refinement_bnd(mg%boxes(id), nc, iv, nb, gc)
2393 else
2394 call sides_rb(mg%boxes(id), nc, iv, nb, gc)
2395 end if
2396 end if
2397 end subroutine fill_refinement_bnd
2398
2399 subroutine copy_from_nb(box, box_nb, nb, nc, iv)
2400 type(mg_box_t), intent(inout) :: box
2401 type(mg_box_t), intent(in) :: box_nb
2402 integer, intent(in) :: nb
2403 integer, intent(in) :: nc
2404 integer, intent(in) :: iv
2405 real(dp) :: gc(nc)
2406
2407 call box_gc_for_neighbor(box_nb, mg_neighb_rev(nb), nc, iv, gc)
2408 call box_set_gc(box, nb, nc, iv, gc)
2409 end subroutine copy_from_nb
2410
2411 subroutine buffer_for_nb(mg, box, nc, iv, nb_id, nb_rank, nb, dry_run)
2412 type(mg_t), intent(inout) :: mg
2413 type(mg_box_t), intent(inout) :: box
2414 integer, intent(in) :: nc
2415 integer, intent(in) :: iv
2416 integer, intent(in) :: nb_id
2417 integer, intent(in) :: nb_rank
2418 integer, intent(in) :: nb
2419 logical, intent(in) :: dry_run
2420 integer :: i, dsize
2421 real(dp) :: gc(nc)
2422
2423 i = mg%buf(nb_rank)%i_send
2424 dsize = nc**(2-1)
2425
2426 if (.not. dry_run) then
2427 call box_gc_for_neighbor(box, nb, nc, iv, gc)
2428 mg%buf(nb_rank)%send(i+1:i+dsize) = pack(gc, .true.)
2429 end if
2430
2431 ! Later the buffer is sorted, using the fact that loops go from low to high
2432 ! box id, and we fill ghost cells according to the neighbor order
2433 i = mg%buf(nb_rank)%i_ix
2434 if (.not. dry_run) then
2435 mg%buf(nb_rank)%ix(i+1) = mg_num_neighbors * nb_id + mg_neighb_rev(nb)
2436 end if
2437
2438 mg%buf(nb_rank)%i_send = mg%buf(nb_rank)%i_send + dsize
2439 mg%buf(nb_rank)%i_ix = mg%buf(nb_rank)%i_ix + 1
2440 end subroutine buffer_for_nb
2441
2442 subroutine buffer_for_fine_nb(mg, box, nc, iv, fine_id, fine_rank, nb, dry_run)
2443 type(mg_t), intent(inout) :: mg
2444 type(mg_box_t), intent(inout) :: box
2445 integer, intent(in) :: nc
2446 integer, intent(in) :: iv
2447 integer, intent(in) :: fine_id
2448 integer, intent(in) :: fine_rank
2449 integer, intent(in) :: nb
2450 logical, intent(in) :: dry_run
2451 integer :: i, dsize, ix_offset(2)
2452 real(dp) :: gc(nc)
2453
2454 i = mg%buf(fine_rank)%i_send
2455 dsize = nc**(2-1)
2456
2457 if (.not. dry_run) then
2458 ix_offset = mg_get_child_offset(mg, fine_id)
2459 call box_gc_for_fine_neighbor(box, nb, ix_offset, nc, iv, gc)
2460 mg%buf(fine_rank)%send(i+1:i+dsize) = pack(gc, .true.)
2461 end if
2462
2463 ! Later the buffer is sorted, using the fact that loops go from low to high
2464 ! box id, and we fill ghost cells according to the neighbor order
2465 i = mg%buf(fine_rank)%i_ix
2466 if (.not. dry_run) then
2467 mg%buf(fine_rank)%ix(i+1) = mg_num_neighbors * fine_id + &
2468 mg_neighb_rev(nb)
2469 end if
2470
2471 mg%buf(fine_rank)%i_send = mg%buf(fine_rank)%i_send + dsize
2472 mg%buf(fine_rank)%i_ix = mg%buf(fine_rank)%i_ix + 1
2473 end subroutine buffer_for_fine_nb
2474
2475 subroutine fill_buffered_nb(mg, box, nb_rank, nb, nc, iv, dry_run)
2476 type(mg_t), intent(inout) :: mg
2477 type(mg_box_t), intent(inout) :: box
2478 integer, intent(in) :: nb_rank
2479 integer, intent(in) :: nb
2480 integer, intent(in) :: nc
2481 integer, intent(in) :: iv
2482 logical, intent(in) :: dry_run
2483 integer :: i, dsize
2484 real(dp) :: gc(nc)
2485
2486 i = mg%buf(nb_rank)%i_recv
2487 dsize = nc**(2-1)
2488
2489 if (.not. dry_run) then
2490 gc = reshape(mg%buf(nb_rank)%recv(i+1:i+dsize), shape(gc))
2491 call box_set_gc(box, nb, nc, iv, gc)
2492 end if
2493 mg%buf(nb_rank)%i_recv = mg%buf(nb_rank)%i_recv + dsize
2494
2495 end subroutine fill_buffered_nb
2496
2497 subroutine box_gc_for_neighbor(box, nb, nc, iv, gc)
2498 type(mg_box_t), intent(in) :: box
2499 integer, intent(in) :: nb, nc, iv
2500 real(dp), intent(out) :: gc(nc)
2501
2502 select case (nb)
2503 case (mg_neighb_lowx)
2504 gc = box%cc(1, 1:nc, iv)
2505 case (mg_neighb_highx)
2506 gc = box%cc(nc, 1:nc, iv)
2507 case (mg_neighb_lowy)
2508 gc = box%cc(1:nc, 1, iv)
2509 case (mg_neighb_highy)
2510 gc = box%cc(1:nc, nc, iv)
2511 end select
2512 end subroutine box_gc_for_neighbor
2513
2514 !> Get ghost cells for a fine neighbor
2515 subroutine box_gc_for_fine_neighbor(box, nb, di, nc, iv, gc)
2516 type(mg_box_t), intent(in) :: box
2517 integer, intent(in) :: nb !< Direction of fine neighbor
2518 integer, intent(in) :: di(2) !< Index offset of fine neighbor
2519 integer, intent(in) :: nc, iv
2520 real(dp), intent(out) :: gc(nc)
2521 real(dp) :: tmp(0:nc/2+1), grad(2-1)
2522 integer :: i, hnc
2523
2524 hnc = nc/2
2525
2526 ! First fill a temporary array with data next to the fine grid
2527 select case (nb)
2528 case (mg_neighb_lowx)
2529 tmp = box%cc(1, di(2):di(2)+hnc+1, iv)
2530 case (mg_neighb_highx)
2531 tmp = box%cc(nc, di(2):di(2)+hnc+1, iv)
2532 case (mg_neighb_lowy)
2533 tmp = box%cc(di(1):di(1)+hnc+1, 1, iv)
2534 case (mg_neighb_highy)
2535 tmp = box%cc(di(1):di(1)+hnc+1, nc, iv)
2536 case default
2537 error stop
2538 end select
2539
2540 ! Now interpolate the coarse grid data to obtain values 'straight' next to
2541 ! the fine grid points
2542 do i = 1, hnc
2543 grad(1) = 0.125_dp * (tmp(i+1) - tmp(i-1))
2544 gc(2*i-1) = tmp(i) - grad(1)
2545 gc(2*i) = tmp(i) + grad(1)
2546 end do
2547 end subroutine box_gc_for_fine_neighbor
2548
2549 subroutine box_get_gc(box, nb, nc, iv, gc)
2550 type(mg_box_t), intent(in) :: box
2551 integer, intent(in) :: nb, nc, iv
2552 real(dp), intent(out) :: gc(nc)
2553
2554 select case (nb)
2555 case (mg_neighb_lowx)
2556 gc = box%cc(0, 1:nc, iv)
2557 case (mg_neighb_highx)
2558 gc = box%cc(nc+1, 1:nc, iv)
2559 case (mg_neighb_lowy)
2560 gc = box%cc(1:nc, 0, iv)
2561 case (mg_neighb_highy)
2562 gc = box%cc(1:nc, nc+1, iv)
2563 end select
2564 end subroutine box_get_gc
2565
2566 subroutine box_set_gc(box, nb, nc, iv, gc)
2567 type(mg_box_t), intent(inout) :: box
2568 integer, intent(in) :: nb, nc, iv
2569 real(dp), intent(in) :: gc(nc)
2570
2571 select case (nb)
2572 case (mg_neighb_lowx)
2573 box%cc(0, 1:nc, iv) = gc
2574 case (mg_neighb_highx)
2575 box%cc(nc+1, 1:nc, iv) = gc
2576 case (mg_neighb_lowy)
2577 box%cc(1:nc, 0, iv) = gc
2578 case (mg_neighb_highy)
2579 box%cc(1:nc, nc+1, iv) = gc
2580 end select
2581 end subroutine box_set_gc
2582
2583 subroutine bc_to_gc(mg, id, nc, iv, nb, bc_type)
2584 type(mg_t), intent(inout) :: mg
2585 integer, intent(in) :: id
2586 integer, intent(in) :: nc
2587 integer, intent(in) :: iv
2588 integer, intent(in) :: nb !< Neighbor direction
2589 integer, intent(in) :: bc_type !< Type of b.c.
2590 real(dp) :: c0, c1, c2, dr
2591
2592 ! If we call the interior point x1, x2 and the ghost point x0, then a
2593 ! Dirichlet boundary value b can be imposed as:
2594 ! x0 = -x1 + 2*b
2595 ! A Neumann b.c. can be imposed as:
2596 ! x0 = x1 +/- dx * b
2597 ! A continuous boundary (same slope) as:
2598 ! x0 = 2 * x1 - x2
2599 ! Below, we set coefficients to handle these cases
2600 select case (bc_type)
2601 case (mg_bc_dirichlet)
2602 c0 = 2
2603 c1 = -1
2604 c2 = 0
2605 case (mg_bc_neumann)
2606 dr = mg%dr(mg_neighb_dim(nb), mg%boxes(id)%lvl)
2607 c0 = dr * mg_neighb_high_pm(nb) ! This gives a + or - sign
2608 c1 = 1
2609 c2 = 0
2610 case (mg_bc_continuous)
2611 c0 = 0
2612 c1 = 2
2613 c2 = -1
2614 case default
2615 error stop "bc_to_gc: unknown boundary condition"
2616 end select
2617
2618 select case (nb)
2619 case (mg_neighb_lowx)
2620 mg%boxes(id)%cc(0, 1:nc, iv) = &
2621 c0 * mg%boxes(id)%cc(0, 1:nc, iv) + &
2622 c1 * mg%boxes(id)%cc(1, 1:nc, iv) + &
2623 c2 * mg%boxes(id)%cc(2, 1:nc, iv)
2624 case (mg_neighb_highx)
2625 mg%boxes(id)%cc(nc+1, 1:nc, iv) = &
2626 c0 * mg%boxes(id)%cc(nc+1, 1:nc, iv) + &
2627 c1 * mg%boxes(id)%cc(nc, 1:nc, iv) + &
2628 c2 * mg%boxes(id)%cc(nc-1, 1:nc, iv)
2629 case (mg_neighb_lowy)
2630 mg%boxes(id)%cc(1:nc, 0, iv) = &
2631 c0 * mg%boxes(id)%cc(1:nc, 0, iv) + &
2632 c1 * mg%boxes(id)%cc(1:nc, 1, iv) + &
2633 c2 * mg%boxes(id)%cc(1:nc, 2, iv)
2634 case (mg_neighb_highy)
2635 mg%boxes(id)%cc(1:nc, nc+1, iv) = &
2636 c0 * mg%boxes(id)%cc(1:nc, nc+1, iv) + &
2637 c1 * mg%boxes(id)%cc(1:nc, nc, iv) + &
2638 c2 * mg%boxes(id)%cc(1:nc, nc-1, iv)
2639 end select
2640 end subroutine bc_to_gc
2641
2642 !> Fill ghost cells near refinement boundaries which preserves diffusive fluxes.
2643 subroutine sides_rb(box, nc, iv, nb, gc)
2644 type(mg_box_t), intent(inout) :: box
2645 integer, intent(in) :: nc
2646 integer, intent(in) :: iv
2647 integer, intent(in) :: nb !< Ghost cell direction
2648 !> Interpolated coarse grid ghost cell data (but not yet in the nb direction)
2649 real(dp), intent(in) :: gc(nc)
2650 integer :: di, dj
2651 integer :: i, j, ix, dix
2652
2653 if (mg_neighb_low(nb)) then
2654 ix = 1
2655 dix = 1
2656 else
2657 ix = nc
2658 dix = -1
2659 end if
2660
2661 select case (mg_neighb_dim(nb))
2662 case (1)
2663 i = ix
2664 di = dix
2665
2666 do j = 1, nc
2667 dj = -1 + 2 * iand(j, 1)
2668 box%cc(i-di, j, iv) = 0.5_dp * gc(j) &
2669 + 0.75_dp * box%cc(i, j, iv) &
2670 - 0.25_dp * box%cc(i+di, j, iv)
2671 end do
2672 case (2)
2673 j = ix
2674 dj = dix
2675 do i = 1, nc
2676 di = -1 + 2 * iand(i, 1)
2677 box%cc(i, j-dj, iv) = 0.5_dp * gc(i) &
2678 + 0.75_dp * box%cc(i, j, iv) &
2679 - 0.25_dp * box%cc(i, j+dj, iv)
2680 end do
2681 end select
2682
2683 end subroutine sides_rb
2684
2685 !! File ../src/m_prolong.f90
2686
2687 !> Specify minimum buffer size (per process) for communication
2688 subroutine mg_prolong_buffer_size(mg, n_send, n_recv, dsize)
2689 type(mg_t), intent(inout) :: mg
2690 integer, intent(out) :: n_send(0:mg%n_cpu-1)
2691 integer, intent(out) :: n_recv(0:mg%n_cpu-1)
2692 integer, intent(out) :: dsize
2693 integer :: lvl, min_lvl
2694
2695 if (.not. allocated(mg%comm_restrict%n_send)) then
2696 error stop "Call restrict_buffer_size before prolong_buffer_size"
2697 end if
2698
2699 min_lvl = max(mg%first_normal_lvl-1, mg%lowest_lvl)
2700 allocate(mg%comm_prolong%n_send(0:mg%n_cpu-1, &
2701 min_lvl:mg%highest_lvl))
2702 allocate(mg%comm_prolong%n_recv(0:mg%n_cpu-1, &
2703 min_lvl:mg%highest_lvl))
2704
2705 mg%comm_prolong%n_recv(:, mg%highest_lvl) = 0
2706 mg%comm_prolong%n_send(:, mg%highest_lvl) = 0
2707
2708 do lvl = min_lvl, mg%highest_lvl-1
2709 mg%comm_prolong%n_recv(:, lvl) = &
2710 mg%comm_restrict%n_send(:, lvl+1)
2711 mg%comm_prolong%n_send(:, lvl) = &
2712 mg%comm_restrict%n_recv(:, lvl+1)
2713 end do
2714
2715 ! Send fine grid points, because this is more flexible than sending coarse
2716 ! grid points (e.g., when multiple variables are used for interpolation)
2717 dsize = (mg%box_size)**2
2718 n_send = maxval(mg%comm_prolong%n_send, dim=2)
2719 n_recv = maxval(mg%comm_prolong%n_recv, dim=2)
2720 end subroutine mg_prolong_buffer_size
2721
2722 !> Prolong variable iv from lvl to variable iv_to at lvl+1
2723 subroutine mg_prolong(mg, lvl, iv, iv_to, method, add)
2724
2725 type(mg_t), intent(inout) :: mg
2726 integer, intent(in) :: lvl !< Level to prolong from
2727 integer, intent(in) :: iv !< Source variable
2728 integer, intent(in) :: iv_to !< Target variable
2729 procedure(mg_box_prolong) :: method !< Prolongation method
2730 logical, intent(in) :: add !< If true, add to current values
2731 integer :: i, id, dsize, nc
2732
2733 if (lvl == mg%highest_lvl) error stop "cannot prolong highest level"
2734 if (lvl < mg%lowest_lvl) error stop "cannot prolong below lowest level"
2735
2736 ! Below the first normal level, all boxes are on the same CPU
2737 if (lvl >= mg%first_normal_lvl-1) then
2738 dsize = mg%box_size**2
2739 mg%buf(:)%i_send = 0
2740 mg%buf(:)%i_ix = 0
2741
2742 do i = 1, size(mg%lvls(lvl)%my_ids)
2743 id = mg%lvls(lvl)%my_ids(i)
2744 call prolong_set_buffer(mg, id, mg%box_size, iv, method)
2745 end do
2746
2747 mg%buf(:)%i_recv = mg%comm_prolong%n_recv(:, lvl) * dsize
2748 call sort_and_transfer_buffers(mg, dsize)
2749 mg%buf(:)%i_recv = 0
2750 end if
2751
2752 nc = mg%box_size_lvl(lvl+1)
2753 do i = 1, size(mg%lvls(lvl+1)%my_ids)
2754 id = mg%lvls(lvl+1)%my_ids(i)
2755 call prolong_onto(mg, id, nc, iv, iv_to, add, method)
2756 end do
2757 end subroutine mg_prolong
2758
2759 !> In case the fine grid is on a different CPU, perform the prolongation and
2760 !> store the fine-grid values in the send buffer.
2761 !>
2762 subroutine prolong_set_buffer(mg, id, nc, iv, method)
2763 type(mg_t), intent(inout) :: mg
2764 integer, intent(in) :: id
2765 integer, intent(in) :: nc
2766 integer, intent(in) :: iv
2767 procedure(mg_box_prolong) :: method
2768 integer :: i, dix(2)
2769 integer :: i_c, c_id, c_rank, dsize
2770 real(dp) :: tmp(nc, nc)
2771
2772 dsize = nc**2
2773
2774 do i_c = 1, mg_num_children
2775 c_id = mg%boxes(id)%children(i_c)
2776 if (c_id > mg_no_box) then
2777 c_rank = mg%boxes(c_id)%rank
2778 if (c_rank /= mg%my_rank) then
2779 dix = mg_get_child_offset(mg, c_id)
2780 call method(mg, id, dix, nc, iv, tmp)
2781
2782 i = mg%buf(c_rank)%i_send
2783 mg%buf(c_rank)%send(i+1:i+dsize) = pack(tmp, .true.)
2784 mg%buf(c_rank)%i_send = mg%buf(c_rank)%i_send + dsize
2785
2786 i = mg%buf(c_rank)%i_ix
2787 mg%buf(c_rank)%ix(i+1) = c_id
2788 mg%buf(c_rank)%i_ix = mg%buf(c_rank)%i_ix + 1
2789 end if
2790 end if
2791 end do
2792 end subroutine prolong_set_buffer
2793
2794 !> Prolong onto a child box
2795 subroutine prolong_onto(mg, id, nc, iv, iv_to, add, method)
2796 type(mg_t), intent(inout) :: mg
2797 integer, intent(in) :: id
2798 integer, intent(in) :: nc
2799 integer, intent(in) :: iv !< Prolong from this variable
2800 integer, intent(in) :: iv_to !< Prolong to this variable
2801 logical, intent(in) :: add !< If true, add to current values
2802 procedure(mg_box_prolong) :: method
2803 integer :: hnc, p_id, p_rank, i, dix(2), dsize
2804 real(dp) :: tmp(nc, nc)
2805
2806 hnc = nc/2
2807 p_id = mg%boxes(id)%parent
2808 p_rank = mg%boxes(p_id)%rank
2809
2810 if (p_rank == mg%my_rank) then
2811 dix = mg_get_child_offset(mg, id)
2812 call method(mg, p_id, dix, nc, iv, tmp)
2813 else
2814 dsize = nc**2
2815 i = mg%buf(p_rank)%i_recv
2816 tmp = reshape(mg%buf(p_rank)%recv(i+1:i+dsize), [nc, nc])
2817 mg%buf(p_rank)%i_recv = mg%buf(p_rank)%i_recv + dsize
2818 end if
2819
2820 if (add) then
2821 mg%boxes(id)%cc(1:nc, 1:nc, iv_to) = &
2822 mg%boxes(id)%cc(1:nc, 1:nc, iv_to) + tmp
2823 else
2824 mg%boxes(id)%cc(1:nc, 1:nc, iv_to) = tmp
2825 end if
2826
2827 end subroutine prolong_onto
2828
2829 !> Prolong from a parent to a child with index offset dix
2830 subroutine mg_prolong_sparse(mg, p_id, dix, nc, iv, fine)
2831 type(mg_t), intent(inout) :: mg
2832 integer, intent(in) :: p_id !< Id of parent
2833 integer, intent(in) :: dix(2) !< Offset of child in parent grid
2834 integer, intent(in) :: nc !< Child grid size
2835 integer, intent(in) :: iv !< Prolong from this variable
2836 real(dp), intent(out) :: fine(nc, nc) !< Prolonged values
2837
2838 integer :: i, j, hnc
2839 integer :: ic, jc
2840 real(dp) :: f0, flx, fhx, fly, fhy
2841
2842 hnc = nc/2
2843
2844 associate(crs => mg%boxes(p_id)%cc)
2845 do j = 1, hnc
2846 jc = j + dix(2)
2847 do i = 1, hnc
2848 ic = i + dix(1)
2849
2850 f0 = 0.5_dp * crs(ic, jc, iv)
2851 flx = 0.25_dp * crs(ic-1, jc, iv)
2852 fhx = 0.25_dp * crs(ic+1, jc, iv)
2853 fly = 0.25_dp * crs(ic, jc-1, iv)
2854 fhy = 0.25_dp * crs(ic, jc+1, iv)
2855
2856 fine(2*i-1, 2*j-1) = f0 + flx + fly
2857 fine(2*i , 2*j-1) = f0 + fhx + fly
2858 fine(2*i-1, 2*j) = f0 + flx + fhy
2859 fine(2*i , 2*j) = f0 + fhx + fhy
2860 end do
2861 end do
2862 end associate
2863 end subroutine mg_prolong_sparse
2864
2865 !! File ../src/m_helmholtz.f90
2866
2868 type(mg_t), intent(inout) :: mg
2869
2870 mg%subtract_mean = .false.
2871
2872 select case (mg%geometry_type)
2873 case (mg_cartesian)
2874 mg%box_op => box_helmh
2875
2876 select case (mg%smoother_type)
2878 mg%box_smoother => box_gs_helmh
2879 case default
2880 error stop "helmholtz_set_methods: unsupported smoother type"
2881 end select
2882 case default
2883 error stop "helmholtz_set_methods: unsupported geometry"
2884 end select
2885
2886 end subroutine helmholtz_set_methods
2887
2888 subroutine helmholtz_set_lambda(lambda)
2889 real(dp), intent(in) :: lambda
2890
2891 if (lambda < 0) &
2892 error stop "helmholtz_set_lambda: lambda < 0 not allowed"
2893
2894 helmholtz_lambda = lambda
2895 end subroutine helmholtz_set_lambda
2896
2897 !> Perform Gauss-Seidel relaxation on box for a Helmholtz operator
2898 subroutine box_gs_helmh(mg, id, nc, redblack_cntr)
2899 type(mg_t), intent(inout) :: mg
2900 integer, intent(in) :: id
2901 integer, intent(in) :: nc
2902 integer, intent(in) :: redblack_cntr !< Iteration counter
2903 integer :: i, j, i0, di
2904 real(dp) :: idr2(2), fac
2905 logical :: redblack
2906
2907 idr2 = 1/mg%dr(:, mg%boxes(id)%lvl)**2
2908 fac = 1.0_dp / (2 * sum(idr2) + helmholtz_lambda)
2909 i0 = 1
2910
2911 redblack = (mg%smoother_type == mg_smoother_gsrb)
2912 if (redblack) then
2913 di = 2
2914 else
2915 di = 1
2916 end if
2917
2918 ! The parity of redblack_cntr determines which cells we use. If
2919 ! redblack_cntr is even, we use the even cells and vice versa.
2920 associate(cc => mg%boxes(id)%cc, n => mg_iphi)
2921 do j = 1, nc
2922 if (redblack) &
2923 i0 = 2 - iand(ieor(redblack_cntr, j), 1)
2924
2925 do i = i0, nc, di
2926 cc(i, j, n) = fac * ( &
2927 idr2(1) * (cc(i+1, j, n) + cc(i-1, j, n)) + &
2928 idr2(2) * (cc(i, j+1, n) + cc(i, j-1, n)) - &
2929 cc(i, j, mg_irhs))
2930 end do
2931 end do
2932 end associate
2933 end subroutine box_gs_helmh
2934
2935 !> Perform Helmholtz operator on a box
2936 subroutine box_helmh(mg, id, nc, i_out)
2937 type(mg_t), intent(inout) :: mg
2938 integer, intent(in) :: id
2939 integer, intent(in) :: nc
2940 integer, intent(in) :: i_out !< Index of variable to store Helmholtz in
2941 integer :: i, j
2942 real(dp) :: idr2(2)
2943
2944 idr2 = 1 / mg%dr(:, mg%boxes(id)%lvl)**2
2945
2946 associate(cc => mg%boxes(id)%cc, n => mg_iphi)
2947 do j = 1, nc
2948 do i = 1, nc
2949 cc(i, j, i_out) = &
2950 idr2(1) * (cc(i-1, j, n) + cc(i+1, j, n) - 2 * cc(i, j, n)) + &
2951 idr2(2) * (cc(i, j-1, n) + cc(i, j+1, n) - 2 * cc(i, j, n)) - &
2952 helmholtz_lambda * cc(i, j, n)
2953 end do
2954 end do
2955 end associate
2956 end subroutine box_helmh
2957
2958 !! File ../src/m_multigrid.f90
2959
2960 subroutine mg_set_methods(mg)
2961
2962 type(mg_t), intent(inout) :: mg
2963
2964 ! Set default prolongation method (routines below can override this)
2965 mg%box_prolong => mg_prolong_sparse
2966
2967 select case (mg%operator_type)
2968 case (mg_laplacian)
2969 call laplacian_set_methods(mg)
2970 case (mg_vlaplacian)
2971 call vlaplacian_set_methods(mg)
2972 case (mg_helmholtz)
2973 call helmholtz_set_methods(mg)
2974 case (mg_vhelmholtz)
2975 call vhelmholtz_set_methods(mg)
2976 case (mg_ahelmholtz)
2977 call ahelmholtz_set_methods(mg)
2978 case default
2979 error stop "mg_set_methods: unknown operator"
2980 end select
2981
2982 ! For red-black, perform two smoothing sub-steps so that all unknowns are
2983 ! updated per cycle
2984 if (mg%smoother_type == mg_smoother_gsrb) then
2985 mg%n_smoother_substeps = 2
2986 else
2987 mg%n_smoother_substeps = 1
2988 end if
2989 end subroutine mg_set_methods
2990
2991 subroutine check_methods(mg)
2992 type(mg_t), intent(inout) :: mg
2993
2994 if (.not. associated(mg%box_op) .or. &
2995 .not. associated(mg%box_smoother)) then
2996 call mg_set_methods(mg)
2997 end if
2998
2999 end subroutine check_methods
3000
3001 subroutine mg_add_timers(mg)
3002 type(mg_t), intent(inout) :: mg
3003 timer_total_vcycle = mg_add_timer(mg, "mg total V-cycle")
3004 timer_total_fmg = mg_add_timer(mg, "mg total FMG cycle")
3005 timer_smoother = mg_add_timer(mg, "mg smoother")
3006 timer_smoother_gc = mg_add_timer(mg, "mg smoother g.c.")
3007 timer_coarse = mg_add_timer(mg, "mg coarse")
3008 timer_correct = mg_add_timer(mg, "mg correct")
3009 timer_update_coarse = mg_add_timer(mg, "mg update coarse")
3010 end subroutine mg_add_timers
3011
3012 !> Perform FAS-FMG cycle (full approximation scheme, full multigrid).
3013 subroutine mg_fas_fmg(mg, have_guess, max_res)
3014 type(mg_t), intent(inout) :: mg
3015 logical, intent(in) :: have_guess !< If false, start from phi = 0
3016 real(dp), intent(out), optional :: max_res !< Store max(abs(residual))
3017 integer :: lvl, i, id
3018
3019 call check_methods(mg)
3020 if (timer_smoother == -1) call mg_add_timers(mg)
3021
3022 call mg_timer_start(mg%timers(timer_total_fmg))
3023
3024 if (.not. have_guess) then
3025 do lvl = mg%highest_lvl, mg%lowest_lvl, -1
3026 do i = 1, size(mg%lvls(lvl)%my_ids)
3027 id = mg%lvls(lvl)%my_ids(i)
3028 mg%boxes(id)%cc(:, :, mg_iphi) = 0.0_dp
3029 end do
3030 end do
3031 end if
3032
3033 ! Ensure ghost cells are filled correctly
3034 call mg_fill_ghost_cells_lvl(mg, mg%highest_lvl, mg_iphi)
3035
3036 do lvl = mg%highest_lvl, mg%lowest_lvl+1, -1
3037 ! Set rhs on coarse grid and restrict phi
3038 call mg_timer_start(mg%timers(timer_update_coarse))
3039 call update_coarse(mg, lvl)
3040 call mg_timer_end(mg%timers(timer_update_coarse))
3041 end do
3042
3043 if (mg%subtract_mean) then
3044 ! For fully periodic solutions, the mean source term has to be zero
3045 call subtract_mean(mg, mg_irhs, .false.)
3046 end if
3047
3048 do lvl = mg%lowest_lvl, mg%highest_lvl
3049 ! Store phi_old
3050 do i = 1, size(mg%lvls(lvl)%my_ids)
3051 id = mg%lvls(lvl)%my_ids(i)
3052 mg%boxes(id)%cc(:, :, mg_iold) = &
3053 mg%boxes(id)%cc(:, :, mg_iphi)
3054 end do
3055
3056 if (lvl > mg%lowest_lvl) then
3057 ! Correct solution at this lvl using lvl-1 data
3058 ! phi = phi + prolong(phi_coarse - phi_old_coarse)
3059 call mg_timer_start(mg%timers(timer_correct))
3060 call correct_children(mg, lvl-1)
3061 call mg_timer_end(mg%timers(timer_correct))
3062
3063 ! Update ghost cells
3064 call mg_fill_ghost_cells_lvl(mg, lvl, mg_iphi)
3065 end if
3066
3067 ! Perform V-cycle, possibly set residual on last iteration
3068 if (lvl == mg%highest_lvl) then
3069 call mg_fas_vcycle(mg, lvl, max_res, standalone=.false.)
3070 else
3071 call mg_fas_vcycle(mg, lvl, standalone=.false.)
3072 end if
3073 end do
3074
3075 call mg_timer_end(mg%timers(timer_total_fmg))
3076 end subroutine mg_fas_fmg
3077
3078 !> Perform FAS V-cycle (full approximation scheme).
3079 subroutine mg_fas_vcycle(mg, highest_lvl, max_res, standalone)
3080 use mpi
3081 type(mg_t), intent(inout) :: mg
3082 integer, intent(in), optional :: highest_lvl !< Maximum level for V-cycle
3083 real(dp), intent(out), optional :: max_res !< Store max(abs(residual))
3084 !> Whether the V-cycle is called by itself (default: true)
3085 logical, intent(in), optional :: standalone
3086 integer :: lvl, min_lvl, i, max_lvl, ierr
3087 real(dp) :: init_res, res
3088 logical :: is_standalone
3089
3090 is_standalone = .true.
3091 if (present(standalone)) is_standalone = standalone
3092
3093 call check_methods(mg)
3094 if (timer_smoother == -1) call mg_add_timers(mg)
3095
3096 if (is_standalone) &
3097 call mg_timer_start(mg%timers(timer_total_vcycle))
3098
3099 if (mg%subtract_mean .and. .not. present(highest_lvl)) then
3100 ! Assume that this is a stand-alone call. For fully periodic solutions,
3101 ! ensure the mean source term is zero.
3102 call subtract_mean(mg, mg_irhs, .false.)
3103 end if
3104
3105 min_lvl = mg%lowest_lvl
3106 max_lvl = mg%highest_lvl
3107 if (present(highest_lvl)) max_lvl = highest_lvl
3108
3109 ! Ensure ghost cells are filled correctly
3110 if (is_standalone) then
3111 call mg_fill_ghost_cells_lvl(mg, max_lvl, mg_iphi)
3112 end if
3113
3114 do lvl = max_lvl, min_lvl+1, -1
3115 ! Downwards relaxation
3116 call smooth_boxes(mg, lvl, mg%n_cycle_down)
3117
3118 ! Set rhs on coarse grid, restrict phi, and copy mg_iphi to mg_iold for the
3119 ! correction later
3120 call mg_timer_start(mg%timers(timer_update_coarse))
3121 call update_coarse(mg, lvl)
3122 call mg_timer_end(mg%timers(timer_update_coarse))
3123 end do
3124
3125 call mg_timer_start(mg%timers(timer_coarse))
3126 if (.not. all(mg%boxes(mg%lvls(min_lvl)%ids)%rank == &
3127 mg%boxes(mg%lvls(min_lvl)%ids(1))%rank)) then
3128 error stop "Multiple CPUs for coarse grid (not implemented yet)"
3129 end if
3130
3131 init_res = max_residual_lvl(mg, min_lvl)
3132 do i = 1, mg%max_coarse_cycles
3133 call smooth_boxes(mg, min_lvl, mg%n_cycle_up+mg%n_cycle_down)
3134 res = max_residual_lvl(mg, min_lvl)
3135 if (res < mg%residual_coarse_rel * init_res .or. &
3136 res < mg%residual_coarse_abs) exit
3137 end do
3138 call mg_timer_end(mg%timers(timer_coarse))
3139
3140 ! Do the upwards part of the v-cycle in the tree
3141 do lvl = min_lvl+1, max_lvl
3142 ! Correct solution at this lvl using lvl-1 data
3143 ! phi = phi + prolong(phi_coarse - phi_old_coarse)
3144 call mg_timer_start(mg%timers(timer_correct))
3145 call correct_children(mg, lvl-1)
3146
3147 ! Have to fill ghost cells after correction
3148 call mg_fill_ghost_cells_lvl(mg, lvl, mg_iphi)
3149 call mg_timer_end(mg%timers(timer_correct))
3150
3151 ! Upwards relaxation
3152 call smooth_boxes(mg, lvl, mg%n_cycle_up)
3153 end do
3154
3155 if (present(max_res)) then
3156 init_res = 0.0_dp
3157 do lvl = min_lvl, max_lvl
3158 res = max_residual_lvl(mg, lvl)
3159 init_res = max(res, init_res)
3160 end do
3161 call mpi_allreduce(init_res, max_res, 1, &
3162 mpi_double, mpi_max, mg%comm, ierr)
3163 end if
3164
3165 ! Subtract mean(phi) from phi
3166 if (mg%subtract_mean) then
3167 call subtract_mean(mg, mg_iphi, .true.)
3168 end if
3169
3170 if (is_standalone) &
3171 call mg_timer_end(mg%timers(timer_total_vcycle))
3172 end subroutine mg_fas_vcycle
3173
3174 subroutine subtract_mean(mg, iv, include_ghostcells)
3175 use mpi
3176 type(mg_t), intent(inout) :: mg
3177 integer, intent(in) :: iv
3178 logical, intent(in) :: include_ghostcells
3179 integer :: i, id, lvl, nc, ierr
3180 real(dp) :: sum_iv, mean_iv, volume
3181
3182 nc = mg%box_size
3183 sum_iv = get_sum(mg, iv)
3184 call mpi_allreduce(sum_iv, mean_iv, 1, &
3185 mpi_double, mpi_sum, mg%comm, ierr)
3186
3187 ! Divide by total grid volume to get mean
3188 volume = nc**2 * product(mg%dr(:, 1)) * size(mg%lvls(1)%ids)
3189 mean_iv = mean_iv / volume
3190
3191 do lvl = mg%lowest_lvl, mg%highest_lvl
3192 nc = mg%box_size_lvl(lvl)
3193
3194 do i = 1, size(mg%lvls(lvl)%my_ids)
3195 id = mg%lvls(lvl)%my_ids(i)
3196 if (include_ghostcells) then
3197 mg%boxes(id)%cc(:, :, iv) = &
3198 mg%boxes(id)%cc(:, :, iv) - mean_iv
3199 else
3200 mg%boxes(id)%cc(1:nc, 1:nc, iv) = &
3201 mg%boxes(id)%cc(1:nc, 1:nc, iv) - mean_iv
3202 end if
3203 end do
3204 end do
3205 end subroutine subtract_mean
3206
3207 real(dp) function get_sum(mg, iv)
3208 type(mg_t), intent(in) :: mg
3209 integer, intent(in) :: iv
3210 integer :: lvl, i, id, nc
3211 real(dp) :: w
3212
3213 get_sum = 0.0_dp
3214 do lvl = 1, mg%highest_lvl
3215 nc = mg%box_size_lvl(lvl)
3216 w = product(mg%dr(:, lvl)) ! Adjust for non-Cartesian cases
3217 do i = 1, size(mg%lvls(lvl)%my_leaves)
3218 id = mg%lvls(lvl)%my_leaves(i)
3219 get_sum = get_sum + w * &
3220 sum(mg%boxes(id)%cc(1:nc, 1:nc, iv))
3221 end do
3222 end do
3223 end function get_sum
3224
3225 real(dp) function max_residual_lvl(mg, lvl)
3226 type(mg_t), intent(inout) :: mg
3227 integer, intent(in) :: lvl
3228 integer :: i, id, nc
3229 real(dp) :: res
3230
3231 nc = mg%box_size_lvl(lvl)
3232 max_residual_lvl = 0.0_dp
3233
3234 do i = 1, size(mg%lvls(lvl)%my_ids)
3235 id = mg%lvls(lvl)%my_ids(i)
3236 call residual_box(mg, id, nc)
3237 res = maxval(abs(mg%boxes(id)%cc(1:nc, 1:nc, mg_ires)))
3238 max_residual_lvl = max(max_residual_lvl, res)
3239 end do
3240 end function max_residual_lvl
3241
3242 ! subroutine solve_coarse_grid(mg)
3243 ! use m_fishpack
3244 ! type(mg_t), intent(inout) :: mg
3245
3246 ! real(dp) :: rhs(mg%box_size, mg%box_size)
3247 ! real(dp) :: rmin(2), rmax(2)
3248 ! integer :: nc, nx(2), my_boxes, total_boxes
3249
3250 ! my_boxes = size(mg%lvls(1)%my_ids)
3251 ! total_boxes = size(mg%lvls(1)%ids)
3252 ! nc = mg%box_size
3253
3254 ! if (my_boxes == total_boxes) then
3255 ! nx(:) = nc
3256 ! rmin = [0.0_dp, 0.0_dp]
3257 ! rmax = mg%dr(1) * [nc, nc]
3258 ! rhs = mg%boxes(1)%cc(1:nc, 1:nc, mg_irhs)
3259
3260 ! #if 2 == 2
3261 ! call fishpack_2d(nx, rhs, mg%bc, rmin, rmax)
3262 ! #elif 2 == 3
3263 ! call fishpack_3d(nx, rhs, mg%bc, rmin, rmax)
3264 ! #endif
3265
3266 ! mg%boxes(1)%cc(1:nc, 1:nc, mg_iphi) = rhs
3267 ! else if (my_boxes > 0) then
3268 ! error stop "Boxes at level 1 at different processors"
3269 ! end if
3270
3271 ! call fill_ghost_cells_lvl(mg, 1)
3272 ! end subroutine solve_coarse_grid
3273
3274 ! Set rhs on coarse grid, restrict phi, and copy mg_iphi to mg_iold for the
3275 ! correction later
3276 subroutine update_coarse(mg, lvl)
3277 type(mg_t), intent(inout) :: mg !< Tree containing full grid
3278 integer, intent(in) :: lvl !< Update coarse values at lvl-1
3279 integer :: i, id, nc, nc_c
3280
3281 nc = mg%box_size_lvl(lvl)
3282 nc_c = mg%box_size_lvl(lvl-1)
3283
3284 ! Compute residual
3285 do i = 1, size(mg%lvls(lvl)%my_ids)
3286 id = mg%lvls(lvl)%my_ids(i)
3287 call residual_box(mg, id, nc)
3288 end do
3289
3290 ! Restrict phi and the residual
3291 call mg_restrict_lvl(mg, mg_iphi, lvl)
3292 call mg_restrict_lvl(mg, mg_ires, lvl)
3293
3294 call mg_fill_ghost_cells_lvl(mg, lvl-1, mg_iphi)
3295
3296 ! Set rhs_c = laplacian(phi_c) + restrict(res) where it is refined, and
3297 ! store current coarse phi in old.
3298 do i = 1, size(mg%lvls(lvl-1)%my_parents)
3299 id = mg%lvls(lvl-1)%my_parents(i)
3300
3301 ! Set rhs = L phi
3302 call mg%box_op(mg, id, nc_c, mg_irhs)
3303
3304 ! Add the fine grid residual to rhs
3305 mg%boxes(id)%cc(1:nc_c, 1:nc_c, mg_irhs) = &
3306 mg%boxes(id)%cc(1:nc_c, 1:nc_c, mg_irhs) + &
3307 mg%boxes(id)%cc(1:nc_c, 1:nc_c, mg_ires)
3308
3309 ! Story a copy of phi
3310 mg%boxes(id)%cc(:, :, mg_iold) = &
3311 mg%boxes(id)%cc(:, :, mg_iphi)
3312 end do
3313 end subroutine update_coarse
3314
3315 ! Sets phi = phi + prolong(phi_coarse - phi_old_coarse)
3316 subroutine correct_children(mg, lvl)
3317 type(mg_t), intent(inout) :: mg
3318 integer, intent(in) :: lvl
3319 integer :: i, id
3320
3321 do i = 1, size(mg%lvls(lvl)%my_parents)
3322 id = mg%lvls(lvl)%my_parents(i)
3323
3324 ! Store the correction in mg_ires
3325 mg%boxes(id)%cc(:, :, mg_ires) = &
3326 mg%boxes(id)%cc(:, :, mg_iphi) - &
3327 mg%boxes(id)%cc(:, :, mg_iold)
3328 end do
3329
3330 call mg_prolong(mg, lvl, mg_ires, mg_iphi, mg%box_prolong, add=.true.)
3331 end subroutine correct_children
3332
3333 subroutine smooth_boxes(mg, lvl, n_cycle)
3334 type(mg_t), intent(inout) :: mg
3335 integer, intent(in) :: lvl
3336 integer, intent(in) :: n_cycle !< Number of cycles to perform
3337 integer :: n, i, id, nc
3338
3339 nc = mg%box_size_lvl(lvl)
3340
3341 do n = 1, n_cycle * mg%n_smoother_substeps
3342 call mg_timer_start(mg%timers(timer_smoother))
3343 do i = 1, size(mg%lvls(lvl)%my_ids)
3344 id = mg%lvls(lvl)%my_ids(i)
3345 call mg%box_smoother(mg, id, nc, n)
3346 end do
3347 call mg_timer_end(mg%timers(timer_smoother))
3348
3349 call mg_timer_start(mg%timers(timer_smoother_gc))
3350 call mg_fill_ghost_cells_lvl(mg, lvl, mg_iphi)
3351 call mg_timer_end(mg%timers(timer_smoother_gc))
3352 end do
3353 end subroutine smooth_boxes
3354
3355 subroutine residual_box(mg, id, nc)
3356 type(mg_t), intent(inout) :: mg
3357 integer, intent(in) :: id
3358 integer, intent(in) :: nc
3359
3360 call mg%box_op(mg, id, nc, mg_ires)
3361
3362 mg%boxes(id)%cc(1:nc, 1:nc, mg_ires) = &
3363 mg%boxes(id)%cc(1:nc, 1:nc, mg_irhs) &
3364 - mg%boxes(id)%cc(1:nc, 1:nc, mg_ires)
3365 end subroutine residual_box
3366
3367 !> Apply operator to the tree and store in variable i_out
3368 subroutine mg_apply_op(mg, i_out, op)
3369 type(mg_t), intent(inout) :: mg
3370 integer, intent(in) :: i_out
3371 procedure(mg_box_op), optional :: op
3372 integer :: lvl, i, id, nc
3373
3374 do lvl = mg%lowest_lvl, mg%highest_lvl
3375 nc = mg%box_size_lvl(lvl)
3376 do i = 1, size(mg%lvls(lvl)%my_ids)
3377 id = mg%lvls(lvl)%my_ids(i)
3378 if (present(op)) then
3379 call op(mg, id, nc, i_out)
3380 else
3381 call mg%box_op(mg, id, nc, i_out)
3382 end if
3383 end do
3384 end do
3385 end subroutine mg_apply_op
3386
3387 !! File ../src/m_restrict.f90
3388
3389 !> Specify minimum buffer size (per process) for communication
3390 subroutine mg_restrict_buffer_size(mg, n_send, n_recv, dsize)
3391 type(mg_t), intent(inout) :: mg
3392 integer, intent(out) :: n_send(0:mg%n_cpu-1)
3393 integer, intent(out) :: n_recv(0:mg%n_cpu-1)
3394 integer, intent(out) :: dsize
3395 integer :: n_out(0:mg%n_cpu-1, mg%first_normal_lvl:mg%highest_lvl)
3396 integer :: n_in(0:mg%n_cpu-1, mg%first_normal_lvl:mg%highest_lvl)
3397 integer :: lvl, i, id, p_id, p_rank
3398 integer :: i_c, c_id, c_rank, min_lvl
3399
3400 n_out(:, :) = 0
3401 n_in(:, :) = 0
3402 min_lvl = max(mg%lowest_lvl+1, mg%first_normal_lvl)
3403
3404 do lvl = min_lvl, mg%highest_lvl
3405 ! Number of messages to receive (at lvl-1)
3406 do i = 1, size(mg%lvls(lvl-1)%my_parents)
3407 id = mg%lvls(lvl-1)%my_parents(i)
3408 do i_c = 1, mg_num_children
3409 c_id = mg%boxes(id)%children(i_c)
3410
3411 if (c_id > mg_no_box) then
3412 c_rank = mg%boxes(c_id)%rank
3413 if (c_rank /= mg%my_rank) then
3414 n_in(c_rank, lvl) = n_in(c_rank, lvl) + 1
3415 end if
3416 end if
3417 end do
3418 end do
3419
3420 ! Number of messages to send
3421 do i = 1, size(mg%lvls(lvl)%my_ids)
3422 id = mg%lvls(lvl)%my_ids(i)
3423
3424 p_id = mg%boxes(id)%parent
3425 p_rank = mg%boxes(p_id)%rank
3426 if (p_rank /= mg%my_rank) then
3427 n_out(p_rank, lvl) = n_out(p_rank, lvl) + 1
3428 end if
3429
3430 end do
3431 end do
3432
3433 allocate(mg%comm_restrict%n_send(0:mg%n_cpu-1, &
3434 mg%first_normal_lvl:mg%highest_lvl))
3435 allocate(mg%comm_restrict%n_recv(0:mg%n_cpu-1, &
3436 mg%first_normal_lvl:mg%highest_lvl))
3437 mg%comm_restrict%n_send = n_out
3438 mg%comm_restrict%n_recv = n_in
3439
3440 dsize = (mg%box_size/2)**2
3441 n_send = maxval(n_out, dim=2)
3442 n_recv = maxval(n_in, dim=2)
3443 end subroutine mg_restrict_buffer_size
3444
3445 !> Restrict all levels
3446 subroutine mg_restrict(mg, iv)
3447 type(mg_t), intent(inout) :: mg
3448 integer, intent(in) :: iv
3449 integer :: lvl
3450
3451 do lvl = mg%highest_lvl, mg%lowest_lvl+1, -1
3452 call mg_restrict_lvl(mg, iv, lvl)
3453 end do
3454 end subroutine mg_restrict
3455
3456 !> Restrict from lvl to lvl-1
3457 subroutine mg_restrict_lvl(mg, iv, lvl)
3458
3459 type(mg_t), intent(inout) :: mg
3460 integer, intent(in) :: iv
3461 integer, intent(in) :: lvl
3462 integer :: i, id, dsize, nc
3463
3464 if (lvl <= mg%lowest_lvl) error stop "cannot restrict lvl <= lowest_lvl"
3465
3466 nc = mg%box_size_lvl(lvl)
3467
3468 if (lvl >= mg%first_normal_lvl) then
3469 dsize = (nc/2)**2
3470
3471 mg%buf(:)%i_send = 0
3472 mg%buf(:)%i_ix = 0
3473
3474 do i = 1, size(mg%lvls(lvl)%my_ids)
3475 id = mg%lvls(lvl)%my_ids(i)
3476 call restrict_set_buffer(mg, id, iv)
3477 end do
3478
3479 mg%buf(:)%i_recv = mg%comm_restrict%n_recv(:, lvl) * dsize
3480 call sort_and_transfer_buffers(mg, dsize)
3481 mg%buf(:)%i_recv = 0
3482 end if
3483
3484 do i = 1, size(mg%lvls(lvl-1)%my_parents)
3485 id = mg%lvls(lvl-1)%my_parents(i)
3486 call restrict_onto(mg, id, nc, iv)
3487 end do
3488 end subroutine mg_restrict_lvl
3489
3490 subroutine restrict_set_buffer(mg, id, iv)
3491 type(mg_t), intent(inout) :: mg
3492 integer, intent(in) :: id
3493 integer, intent(in) :: iv
3494 integer :: i, j, n, hnc, p_id, p_rank
3495 real(dp) :: tmp(mg%box_size/2, mg%box_size/2)
3496
3497 hnc = mg%box_size/2
3498 p_id = mg%boxes(id)%parent
3499 p_rank = mg%boxes(p_id)%rank
3500
3501 if (p_rank /= mg%my_rank) then
3502 do j = 1, hnc
3503 do i = 1, hnc
3504 tmp(i, j) = 0.25_dp * &
3505 sum(mg%boxes(id)%cc(2*i-1:2*i, 2*j-1:2*j, iv))
3506 end do
3507 end do
3508
3509 ! Buffer
3510 n = size(tmp)
3511 i = mg%buf(p_rank)%i_send
3512 mg%buf(p_rank)%send(i+1:i+n) = pack(tmp, .true.)
3513 mg%buf(p_rank)%i_send = mg%buf(p_rank)%i_send + n
3514
3515 ! To later sort the send buffer according to parent order
3516 i = mg%buf(p_rank)%i_ix
3517 n = mg_ix_to_ichild(mg%boxes(id)%ix)
3518 mg%buf(p_rank)%ix(i+1) = mg_num_children * p_id + n
3519 mg%buf(p_rank)%i_ix = mg%buf(p_rank)%i_ix + 1
3520 end if
3521 end subroutine restrict_set_buffer
3522
3523 subroutine restrict_onto(mg, id, nc, iv)
3524 type(mg_t), intent(inout) :: mg
3525 integer, intent(in) :: id
3526 integer, intent(in) :: nc
3527 integer, intent(in) :: iv
3528 integer :: i, j, hnc, dsize, i_c, c_id
3529 integer :: c_rank, dix(2)
3530
3531 hnc = nc/2
3532 dsize = hnc**2
3533
3534 do i_c = 1, mg_num_children
3535 c_id = mg%boxes(id)%children(i_c)
3536 if (c_id == mg_no_box) cycle ! For coarsened grid
3537 c_rank = mg%boxes(c_id)%rank
3538 dix = mg_get_child_offset(mg, c_id)
3539
3540 if (c_rank == mg%my_rank) then
3541 do j=1, hnc; do i=1, hnc
3542 mg%boxes(id)%cc(dix(1)+i, dix(2)+j, iv) = 0.25_dp * &
3543 sum(mg%boxes(c_id)%cc(2*i-1:2*i, 2*j-1:2*j, iv))
3544 end do; end do
3545 else
3546 i = mg%buf(c_rank)%i_recv
3547 mg%boxes(id)%cc(dix(1)+1:dix(1)+hnc, &
3548 dix(2)+1:dix(2)+hnc, iv) = &
3549 reshape(mg%buf(c_rank)%recv(i+1:i+dsize), [hnc, hnc])
3550 mg%buf(c_rank)%i_recv = mg%buf(c_rank)%i_recv + dsize
3551 end if
3552 end do
3553
3554 end subroutine restrict_onto
3555
3556 !! File ../src/m_mrgrnk.f90
3557
3558 Subroutine i_mrgrnk (XDONT, IRNGT)
3559 ! __________________________________________________________
3560 ! MRGRNK = Merge-sort ranking of an array
3561 ! For performance reasons, the first 2 passes are taken
3562 ! out of the standard loop, and use dedicated coding.
3563 ! __________________________________________________________
3564 ! __________________________________________________________
3565 Integer, Dimension (:), Intent (In) :: XDONT
3566 Integer, Dimension (:), Intent (Out) :: IRNGT
3567 ! __________________________________________________________
3568 Integer :: XVALA, XVALB
3569 !
3570 Integer, Dimension (SIZE(IRNGT)) :: JWRKT
3571 Integer :: LMTNA, LMTNC, IRNG1, IRNG2
3572 Integer :: NVAL, IIND, IWRKD, IWRK, IWRKF, JINDA, IINDA, IINDB
3573 !
3574 nval = min(SIZE(xdont), SIZE(irngt))
3575 Select Case (nval)
3576 Case (:0)
3577 Return
3578 Case (1)
3579 irngt(1) = 1
3580 Return
3581 Case Default
3582 Continue
3583 End Select
3584 !
3585 ! Fill-in the index array, creating ordered couples
3586 !
3587 Do iind = 2, nval, 2
3588 If (xdont(iind-1) <= xdont(iind)) Then
3589 irngt(iind-1) = iind - 1
3590 irngt(iind) = iind
3591 Else
3592 irngt(iind-1) = iind
3593 irngt(iind) = iind - 1
3594 End If
3595 End Do
3596 If (modulo(nval, 2) /= 0) Then
3597 irngt(nval) = nval
3598 End If
3599 !
3600 ! We will now have ordered subsets A - B - A - B - ...
3601 ! and merge A and B couples into C - C - ...
3602 !
3603 lmtna = 2
3604 lmtnc = 4
3605 !
3606 ! First iteration. The length of the ordered subsets goes from 2 to 4
3607 !
3608 Do
3609 If (nval <= 2) Exit
3610 !
3611 ! Loop on merges of A and B into C
3612 !
3613 Do iwrkd = 0, nval - 1, 4
3614 If ((iwrkd+4) > nval) Then
3615 If ((iwrkd+2) >= nval) Exit
3616 !
3617 ! 1 2 3
3618 !
3619 If (xdont(irngt(iwrkd+2)) <= xdont(irngt(iwrkd+3))) Exit
3620 !
3621 ! 1 3 2
3622 !
3623 If (xdont(irngt(iwrkd+1)) <= xdont(irngt(iwrkd+3))) Then
3624 irng2 = irngt(iwrkd+2)
3625 irngt(iwrkd+2) = irngt(iwrkd+3)
3626 irngt(iwrkd+3) = irng2
3627 !
3628 ! 3 1 2
3629 !
3630 Else
3631 irng1 = irngt(iwrkd+1)
3632 irngt(iwrkd+1) = irngt(iwrkd+3)
3633 irngt(iwrkd+3) = irngt(iwrkd+2)
3634 irngt(iwrkd+2) = irng1
3635 End If
3636 Exit
3637 End If
3638 !
3639 ! 1 2 3 4
3640 !
3641 If (xdont(irngt(iwrkd+2)) <= xdont(irngt(iwrkd+3))) cycle
3642 !
3643 ! 1 3 x x
3644 !
3645 If (xdont(irngt(iwrkd+1)) <= xdont(irngt(iwrkd+3))) Then
3646 irng2 = irngt(iwrkd+2)
3647 irngt(iwrkd+2) = irngt(iwrkd+3)
3648 If (xdont(irng2) <= xdont(irngt(iwrkd+4))) Then
3649 ! 1 3 2 4
3650 irngt(iwrkd+3) = irng2
3651 Else
3652 ! 1 3 4 2
3653 irngt(iwrkd+3) = irngt(iwrkd+4)
3654 irngt(iwrkd+4) = irng2
3655 End If
3656 !
3657 ! 3 x x x
3658 !
3659 Else
3660 irng1 = irngt(iwrkd+1)
3661 irng2 = irngt(iwrkd+2)
3662 irngt(iwrkd+1) = irngt(iwrkd+3)
3663 If (xdont(irng1) <= xdont(irngt(iwrkd+4))) Then
3664 irngt(iwrkd+2) = irng1
3665 If (xdont(irng2) <= xdont(irngt(iwrkd+4))) Then
3666 ! 3 1 2 4
3667 irngt(iwrkd+3) = irng2
3668 Else
3669 ! 3 1 4 2
3670 irngt(iwrkd+3) = irngt(iwrkd+4)
3671 irngt(iwrkd+4) = irng2
3672 End If
3673 Else
3674 ! 3 4 1 2
3675 irngt(iwrkd+2) = irngt(iwrkd+4)
3676 irngt(iwrkd+3) = irng1
3677 irngt(iwrkd+4) = irng2
3678 End If
3679 End If
3680 End Do
3681 !
3682 ! The Cs become As and Bs
3683 !
3684 lmtna = 4
3685 Exit
3686 End Do
3687 !
3688 ! Iteration loop. Each time, the length of the ordered subsets
3689 ! is doubled.
3690 !
3691 Do
3692 If (lmtna >= nval) Exit
3693 iwrkf = 0
3694 lmtnc = 2 * lmtnc
3695 !
3696 ! Loop on merges of A and B into C
3697 !
3698 Do
3699 iwrk = iwrkf
3700 iwrkd = iwrkf + 1
3701 jinda = iwrkf + lmtna
3702 iwrkf = iwrkf + lmtnc
3703 If (iwrkf >= nval) Then
3704 If (jinda >= nval) Exit
3705 iwrkf = nval
3706 End If
3707 iinda = 1
3708 iindb = jinda + 1
3709 !
3710 ! Shortcut for the case when the max of A is smaller
3711 ! than the min of B. This line may be activated when the
3712 ! initial set is already close to sorted.
3713 !
3714 ! IF (XDONT(IRNGT(JINDA)) <= XDONT(IRNGT(IINDB))) CYCLE
3715 !
3716 ! One steps in the C subset, that we build in the final rank array
3717 !
3718 ! Make a copy of the rank array for the merge iteration
3719 !
3720 jwrkt(1:lmtna) = irngt(iwrkd:jinda)
3721 !
3722 xvala = xdont(jwrkt(iinda))
3723 xvalb = xdont(irngt(iindb))
3724 !
3725 Do
3726 iwrk = iwrk + 1
3727 !
3728 ! We still have unprocessed values in both A and B
3729 !
3730 If (xvala > xvalb) Then
3731 irngt(iwrk) = irngt(iindb)
3732 iindb = iindb + 1
3733 If (iindb > iwrkf) Then
3734 ! Only A still with unprocessed values
3735 irngt(iwrk+1:iwrkf) = jwrkt(iinda:lmtna)
3736 Exit
3737 End If
3738 xvalb = xdont(irngt(iindb))
3739 Else
3740 irngt(iwrk) = jwrkt(iinda)
3741 iinda = iinda + 1
3742 If (iinda > lmtna) exit! Only B still with unprocessed values
3743 xvala = xdont(jwrkt(iinda))
3744 End If
3745 !
3746 End Do
3747 End Do
3748 !
3749 ! The Cs become As and Bs
3750 !
3751 lmtna = 2 * lmtna
3752 End Do
3753 !
3754 Return
3755 !
3756 End Subroutine i_mrgrnk
3757
3758 !! File ../src/m_ahelmholtz.f90
3759
3761 type(mg_t), intent(inout) :: mg
3762
3763 if (mg%n_extra_vars == 0 .and. mg%is_allocated) then
3764 error stop "ahelmholtz_set_methods: mg%n_extra_vars == 0"
3765 else
3766 mg%n_extra_vars = max(2, mg%n_extra_vars)
3767 end if
3768
3769 ! Use Neumann zero boundary conditions for the variable coefficient, since
3770 ! it is needed in ghost cells.
3771 mg%bc(:, mg_iveps1)%bc_type = mg_bc_neumann
3772 mg%bc(:, mg_iveps1)%bc_value = 0.0_dp
3773
3774 mg%bc(:, mg_iveps2)%bc_type = mg_bc_neumann
3775 mg%bc(:, mg_iveps2)%bc_value = 0.0_dp
3776
3777
3778 select case (mg%geometry_type)
3779 case (mg_cartesian)
3780 mg%box_op => box_ahelmh
3781
3782 select case (mg%smoother_type)
3784 mg%box_smoother => box_gs_ahelmh
3785 case default
3786 error stop "ahelmholtz_set_methods: unsupported smoother type"
3787 end select
3788 case default
3789 error stop "ahelmholtz_set_methods: unsupported geometry"
3790 end select
3791
3792 end subroutine ahelmholtz_set_methods
3793
3794 subroutine ahelmholtz_set_lambda(lambda)
3795 real(dp), intent(in) :: lambda
3796
3797 if (lambda < 0) &
3798 error stop "ahelmholtz_set_lambda: lambda < 0 not allowed"
3799
3800 ahelmholtz_lambda = lambda
3801 end subroutine ahelmholtz_set_lambda
3802
3803 !> Perform Gauss-Seidel relaxation on box for a Helmholtz operator
3804 subroutine box_gs_ahelmh(mg, id, nc, redblack_cntr)
3805 type(mg_t), intent(inout) :: mg
3806 integer, intent(in) :: id
3807 integer, intent(in) :: nc
3808 integer, intent(in) :: redblack_cntr !< Iteration counter
3809 integer :: i, j, i0, di
3810 logical :: redblack
3811 real(dp) :: idr2(2*2), u(2*2)
3812 real(dp) :: a0(2*2), a(2*2), c(2*2)
3813
3814 ! Duplicate 1/dr^2 array to multiply neighbor values
3815 idr2(1:2*2:2) = 1/mg%dr(:, mg%boxes(id)%lvl)**2
3816 idr2(2:2*2:2) = idr2(1:2*2:2)
3817 i0 = 1
3818
3819 redblack = (mg%smoother_type == mg_smoother_gsrb)
3820 if (redblack) then
3821 di = 2
3822 else
3823 di = 1
3824 end if
3825
3826 ! The parity of redblack_cntr determines which cells we use. If
3827 ! redblack_cntr is even, we use the even cells and vice versa.
3828 associate(cc => mg%boxes(id)%cc, n => mg_iphi, &
3829 i_eps1 => mg_iveps1, &
3830 i_eps2 => mg_iveps2)
3831
3832 do j = 1, nc
3833 if (redblack) &
3834 i0 = 2 - iand(ieor(redblack_cntr, j), 1)
3835
3836 do i = i0, nc, di
3837 a0(1:2) = cc(i, j, i_eps1)
3838 a0(3:4) = cc(i, j, i_eps2)
3839 u(1:2) = cc(i-1:i+1:2, j, n)
3840 a(1:2) = cc(i-1:i+1:2, j, i_eps1)
3841 u(3:4) = cc(i, j-1:j+1:2, n)
3842 a(3:4) = cc(i, j-1:j+1:2, i_eps2)
3843 c(:) = 2 * a0(:) * a(:) / (a0(:) + a(:)) * idr2
3844
3845 cc(i, j, n) = &
3846 (sum(c(:) * u(:)) - cc(i, j, mg_irhs)) / &
3847 (sum(c(:)) + ahelmholtz_lambda)
3848 end do
3849 end do
3850 end associate
3851 end subroutine box_gs_ahelmh
3852
3853 !> Perform Helmholtz operator on a box
3854 subroutine box_ahelmh(mg, id, nc, i_out)
3855 type(mg_t), intent(inout) :: mg
3856 integer, intent(in) :: id
3857 integer, intent(in) :: nc
3858 integer, intent(in) :: i_out !< Index of variable to store Helmholtz in
3859 integer :: i, j
3860 real(dp) :: idr2(2*2), a0(2*2), u0, u(2*2), a(2*2)
3861
3862 ! Duplicate 1/dr^2 array to multiply neighbor values
3863 idr2(1:2*2:2) = 1/mg%dr(:, mg%boxes(id)%lvl)**2
3864 idr2(2:2*2:2) = idr2(1:2*2:2)
3865
3866 associate(cc => mg%boxes(id)%cc, n => mg_iphi, &
3867 i_eps1 => mg_iveps1, &
3868 i_eps2 => mg_iveps2)
3869 do j = 1, nc
3870 do i = 1, nc
3871 a0(1:2) = cc(i, j, i_eps1)
3872 a0(3:4) = cc(i, j, i_eps2)
3873 a(1:2) = cc(i-1:i+1:2, j, i_eps1)
3874 a(3:4) = cc(i, j-1:j+1:2, i_eps2)
3875 u0 = cc(i, j, n)
3876 u(1:2) = cc(i-1:i+1:2, j, n)
3877 u(3:4) = cc(i, j-1:j+1:2, n)
3878
3879 cc(i, j, i_out) = sum(2 * idr2 * &
3880 a0(:)*a(:)/(a0(:) + a(:)) * (u(:) - u0)) - &
3882 end do
3883 end do
3884 end associate
3885 end subroutine box_ahelmh
3886
3887end module m_octree_mg_2d
Subroutine that performs Gauss-Seidel relaxation.
Subroutine that performs A * cc(..., i_in) = cc(..., i_out)
Subroutine that performs prolongation to a single child.
To fill ghost cells near physical boundaries.
To fill ghost cells near refinement boundaries.
subroutine, public mg_comm_init(mg, comm)
Prolong from a parent to a child with index offset dix. This method could sometimes work better than ...
subroutine, public mg_fas_vcycle(mg, highest_lvl, max_res, standalone)
Perform FAS V-cycle (full approximation scheme).
subroutine, public vhelmholtz_set_methods(mg)
subroutine, public mg_ghost_cell_buffer_size(mg, n_send, n_recv, dsize)
Specify minimum buffer size (per process) for communication.
integer, parameter, public mg_num_children
subroutine, public mg_timer_end(timer)
subroutine, public mg_restrict(mg, iv)
Restrict all levels.
integer, parameter, public mg_vhelmholtz
Indicates a variable-coefficient Helmholtz equation.
integer, parameter, public mg_iveps1
Indexes of anisotropic variable coefficients.
integer, parameter, public mg_ahelmholtz
Indicates a anisotropic-coefficient Helmholtz equation.
integer, parameter, public mg_vlaplacian
Indicates a variable-coefficient Laplacian.
integer, dimension(2, 4), parameter, public mg_neighb_dix
integer, parameter, public mg_helmholtz
Indicates a constant-coefficient Helmholtz equation.
integer, parameter, public mg_physical_boundary
Special value that indicates there is a physical boundary.
subroutine, public mg_prolong_sparse(mg, p_id, dix, nc, iv, fine)
Prolong from a parent to a child with index offset dix.
subroutine, public mg_get_face_coords(box, nb, nc, x)
Get coordinates at the face of a box.
subroutine, public sort_and_transfer_buffers(mg, dsize)
integer, parameter, public dp
Type of reals.
pure integer function, public mg_highest_uniform_lvl(mg)
integer function, public mg_ix_to_ichild(ix)
Compute the child index for a box with spatial index ix. With child index we mean the index in the ch...
integer, parameter, public mg_ndim
Problem dimension.
subroutine, public mg_prolong_buffer_size(mg, n_send, n_recv, dsize)
Specify minimum buffer size (per process) for communication.
subroutine, public diffusion_solve_acoeff(mg, dt, order, max_res)
Solve a diffusion equation implicitly, assuming anisotropic diffusion coefficient which has been stor...
subroutine, public mg_fas_fmg(mg, have_guess, max_res)
Perform FAS-FMG cycle (full approximation scheme, full multigrid).
integer, parameter, public mg_ires
Index of residual.
integer, parameter, public mg_neighb_highy
subroutine, public mg_set_neighbors_lvl(mg, lvl)
integer, parameter, public mg_iveps3
subroutine, public mg_load_balance_simple(mg)
Load balance all boxes in the multigrid tree, by simply distributing the load per grid level....
subroutine, public helmholtz_set_methods(mg)
integer, parameter, public mg_bc_continuous
Value to indicate a continuous boundary condition.
subroutine, public mg_timers_show(mg)
integer, parameter, public mg_iphi
Index of solution.
subroutine, public mg_add_children(mg, id)
subroutine, public mg_prolong(mg, lvl, iv, iv_to, method, add)
Prolong variable iv from lvl to variable iv_to at lvl+1.
integer, dimension(4), parameter, public mg_neighb_dim
integer, dimension(2, 4), parameter, public mg_child_dix
integer, parameter, public mg_spherical
Spherical coordinate system.
subroutine, public mg_timer_start(timer)
integer(i8) function, public mg_number_of_unknowns(mg)
Determine total number of unknowns (on leaves)
integer, parameter, public mg_neighb_highx
integer, parameter, public mg_bc_dirichlet
Value to indicate a Dirichlet boundary condition.
subroutine, public helmholtz_set_lambda(lambda)
integer, parameter, public mg_laplacian
Indicates a standard Laplacian.
integer, dimension(4), parameter, public mg_neighb_rev
subroutine, public ahelmholtz_set_lambda(lambda)
real(dp), public, protected ahelmholtz_lambda
Module which contains multigrid procedures for a Helmholtz operator of the form: div(D grad(phi)) - l...
integer, parameter, public mg_lvl_hi
Maximum allowed grid level.
integer, dimension(4), parameter, public mg_neighb_high_pm
integer, parameter, public mg_bc_neumann
Value to indicate a Neumann boundary condition.
integer, parameter, public mg_irhs
Index of right-hand side.
logical, dimension(2, 4), parameter, public mg_child_low
integer, parameter, public mg_iveps2
elemental logical function, public mg_has_children(box)
Return .true. if a box has children.
subroutine, public diffusion_solve_vcoeff(mg, dt, order, max_res)
Solve a diffusion equation implicitly, assuming a variable diffusion coefficient which has been store...
integer, parameter, public mg_cylindrical
Cylindrical coordinate system.
real(dp), public, protected vhelmholtz_lambda
Module for implicitly solving diffusion equations.
subroutine, public mg_restrict_buffer_size(mg, n_send, n_recv, dsize)
Specify minimum buffer size (per process) for communication.
integer, parameter, public mg_smoother_jacobi
subroutine, public laplacian_set_methods(mg)
subroutine, public mg_deallocate_storage(mg)
Deallocate all allocatable arrays.
subroutine, public ahelmholtz_set_methods(mg)
subroutine, public mg_load_balance(mg)
Load balance all boxes in the multigrid tree. Compared to mg_load_balance_simple, this method does a ...
subroutine, public mg_fill_ghost_cells_lvl(mg, lvl, iv)
Fill ghost cells at a grid level.
subroutine, public vhelmholtz_set_lambda(lambda)
subroutine, public mg_set_next_level_ids(mg, lvl)
integer, parameter, public mg_num_neighbors
integer, parameter, public mg_cartesian
Cartesian coordinate system.
integer, dimension(2, 4), parameter, public mg_child_adj_nb
subroutine, public mg_set_refinement_boundaries(boxes, level)
Create a list of refinement boundaries (from the coarse side)
integer, parameter, public mg_neighb_lowx
integer, parameter, public mg_neighb_lowy
integer, parameter, public mg_num_vars
Number of predefined multigrid variables.
integer, parameter, public mg_lvl_lo
Minimum allowed grid level.
subroutine, public mg_set_leaves_parents(boxes, level)
Create a list of leaves and a list of parents for a level.
subroutine, public mg_load_balance_parents(mg)
Load balance the parents (non-leafs). Assign them to the rank that has most children.
subroutine, public mg_fill_ghost_cells(mg, iv)
Fill ghost cells at all grid levels.
subroutine, public mg_restrict_lvl(mg, iv, lvl)
Restrict from lvl to lvl-1.
subroutine, public vlaplacian_set_methods(mg)
integer, parameter, public mg_iveps
Index of the variable coefficient (at cell centers)
subroutine, public mg_allocate_storage(mg)
Allocate communication buffers and local boxes for a tree that has already been created.
subroutine, public mg_build_rectangle(mg, domain_size, box_size, dx, r_min, periodic, n_finer)
subroutine, public mg_apply_op(mg, i_out, op)
Apply operator to the tree and store in variable i_out.
integer, parameter, public i8
Type for 64-bit integers.
subroutine, public mg_set_methods(mg)
integer, parameter, public mg_max_num_vars
Maximum number of variables.
pure integer function, dimension(2), public mg_get_child_offset(mg, id)
Get the offset of a box with respect to its parent (e.g. in 2d, there can be a child at offset 0,...
integer, parameter, public mg_iold
Index of previous solution (used for correction)
logical, dimension(4), parameter, public mg_neighb_low
integer, parameter, public mg_max_timers
Maximum number of timers to use.
integer, parameter, public mg_smoother_gs
real(dp), public, protected helmholtz_lambda
Module for load balancing a tree (that already has been constructed). The load balancing determines w...
subroutine, public diffusion_solve(mg, dt, diffusion_coeff, order, max_res)
Solve a diffusion equation implicitly, assuming a constant diffusion coefficient. The solution at tim...
subroutine, public mg_phi_bc_store(mg)
Store boundary conditions for the solution variable, this can speed up calculations if the same bound...
integer, dimension(4, 2), parameter, public mg_child_rev
integer, parameter, public mg_no_box
Special value that indicates there is no box.
integer, parameter, public mg_smoother_gsrb
integer function, public mg_add_timer(mg, name)
Box data structure.
Buffer type (one is used for each pair of communicating processes)
Lists of blocks per refinement level.