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