Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \note
10 : !> Basic type for real space grid methods
11 : !> \par History
12 : !> JGH (22-May-2002) : New routine rs_grid_zero
13 : !> JGH (12-Jun-2002) : Bug fix for mpi groups
14 : !> JGH (19-Jun-2003) : Added routine for task distribution
15 : !> JGH (23-Nov-2003) : Added routine for task loop separation
16 : !> \author JGH (18-Mar-2001)
17 : ! **************************************************************************************************
18 : MODULE realspace_grid_types
19 : USE cp_array_utils, ONLY: cp_1d_r_p_type
20 : USE cp_log_handling, ONLY: cp_to_string
21 : USE kahan_sum, ONLY: accurate_sum
22 : USE kinds, ONLY: dp,&
23 : int_8
24 : USE machine, ONLY: m_memory
25 : USE mathlib, ONLY: det_3x3
26 : USE message_passing, ONLY: mp_comm_null,&
27 : mp_comm_type,&
28 : mp_request_null,&
29 : mp_request_type,&
30 : mp_waitall,&
31 : mp_waitany
32 : USE offload_api, ONLY: offload_buffer_type,&
33 : offload_create_buffer,&
34 : offload_free_buffer
35 : USE pw_grid_types, ONLY: PW_MODE_LOCAL,&
36 : pw_grid_type
37 : USE pw_grids, ONLY: pw_grid_release,&
38 : pw_grid_retain
39 : USE pw_methods, ONLY: pw_integrate_function
40 : USE pw_types, ONLY: pw_r3d_rs_type
41 : USE util, ONLY: get_limit
42 :
43 : !$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num, omp_get_num_threads
44 :
45 : #include "../base/base_uses.f90"
46 :
47 : IMPLICIT NONE
48 :
49 : PRIVATE
50 : PUBLIC :: realspace_grid_type, &
51 : realspace_grid_desc_type, &
52 : realspace_grid_p_type, &
53 : realspace_grid_desc_p_type, &
54 : realspace_grid_input_type
55 :
56 : PUBLIC :: transfer_rs2pw, &
57 : transfer_pw2rs, &
58 : rs_grid_zero, &
59 : rs_grid_set_box, &
60 : rs_grid_create, &
61 : rs_grid_create_descriptor, &
62 : rs_grid_retain_descriptor, &
63 : rs_grid_release, &
64 : rs_grid_release_descriptor, &
65 : rs_grid_reorder_ranks, &
66 : rs_grid_print, &
67 : rs_grid_locate_rank, &
68 : rs_grid_max_ngpts, &
69 : rs_grid_mult_and_add, &
70 : map_gaussian_here
71 :
72 : INTEGER, PARAMETER, PUBLIC :: rsgrid_distributed = 0, &
73 : rsgrid_replicated = 1, &
74 : rsgrid_automatic = 2
75 :
76 : LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .FALSE.
77 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'realspace_grid_types'
78 :
79 : ! **************************************************************************************************
80 : TYPE realspace_grid_input_type
81 : INTEGER :: distribution_type = rsgrid_replicated
82 : INTEGER :: distribution_layout(3) = -1
83 : REAL(KIND=dp) :: memory_factor = 0.0_dp
84 : LOGICAL :: lock_distribution = .FALSE.
85 : INTEGER :: nsmax = -1
86 : REAL(KIND=dp) :: halo_reduction_factor = 1.0_dp
87 : END TYPE realspace_grid_input_type
88 :
89 : ! **************************************************************************************************
90 : TYPE realspace_grid_desc_type
91 : TYPE(pw_grid_type), POINTER :: pw => NULL() ! the pw grid
92 :
93 : INTEGER :: ref_count = 0 ! reference count
94 :
95 : INTEGER(int_8) :: ngpts = 0_int_8 ! # grid points
96 : INTEGER, DIMENSION(3) :: npts = 0 ! # grid points per dimension
97 : INTEGER, DIMENSION(3) :: lb = 0 ! lower bounds
98 : INTEGER, DIMENSION(3) :: ub = 0 ! upper bounds
99 :
100 : INTEGER :: border = 0 ! border points
101 :
102 : INTEGER, DIMENSION(3) :: perd = -1 ! periodicity enforced
103 : REAL(KIND=dp), DIMENSION(3, 3) :: dh = 0.0_dp ! incremental grid matrix
104 : REAL(KIND=dp), DIMENSION(3, 3) :: dh_inv = 0.0_dp ! inverse incremental grid matrix
105 : LOGICAL :: orthorhombic = .TRUE. ! grid symmetry
106 :
107 : LOGICAL :: parallel = .TRUE. ! whether the corresponding pw grid is distributed
108 : LOGICAL :: distributed = .TRUE. ! whether the rs grid is distributed
109 : ! these MPI related quantities are only meaningful depending on how the grid has been laid out
110 : ! they are most useful for fully distributed grids, where they reflect the topology of the grid
111 : TYPE(mp_comm_type) :: group = mp_comm_null
112 : INTEGER :: my_pos = -1
113 : INTEGER :: group_size = 0
114 : INTEGER, DIMENSION(3) :: group_dim = -1
115 : INTEGER, DIMENSION(3) :: group_coor = -1
116 : INTEGER, DIMENSION(3) :: neighbours = -1
117 : ! only meaningful on distributed grids
118 : ! a list of bounds for each CPU
119 : INTEGER, DIMENSION(:, :), ALLOCATABLE :: lb_global
120 : INTEGER, DIMENSION(:, :), ALLOCATABLE :: ub_global
121 : ! a mapping from linear rank to 3d coord
122 : INTEGER, DIMENSION(:, :), ALLOCATABLE :: rank2coord
123 : INTEGER, DIMENSION(:, :, :), ALLOCATABLE :: coord2rank
124 : ! a mapping from index to rank (which allows to figure out easily on which rank a given point of the grid is)
125 : INTEGER, DIMENSION(:), ALLOCATABLE :: x2coord
126 : INTEGER, DIMENSION(:), ALLOCATABLE :: y2coord
127 : INTEGER, DIMENSION(:), ALLOCATABLE :: z2coord
128 :
129 : INTEGER :: my_virtual_pos = -1
130 : INTEGER, DIMENSION(3) :: virtual_group_coor = -1
131 :
132 : INTEGER, DIMENSION(:), ALLOCATABLE :: virtual2real, real2virtual
133 :
134 : END TYPE realspace_grid_desc_type
135 :
136 : TYPE realspace_grid_type
137 :
138 : TYPE(realspace_grid_desc_type), POINTER :: desc => NULL()
139 :
140 : INTEGER :: ngpts_local = -1 ! local dimensions
141 : INTEGER, DIMENSION(3) :: npts_local = -1
142 : INTEGER, DIMENSION(3) :: lb_local = -1
143 : INTEGER, DIMENSION(3) :: ub_local = -1
144 : INTEGER, DIMENSION(3) :: lb_real = -1 ! lower bounds of the real local data
145 : INTEGER, DIMENSION(3) :: ub_real = -1 ! upper bounds of the real local data
146 :
147 : INTEGER, DIMENSION(:), ALLOCATABLE :: px, py, pz ! index translators
148 : TYPE(offload_buffer_type) :: buffer = offload_buffer_type() ! owner of the grid's memory
149 : REAL(KIND=dp), DIMENSION(:, :, :), CONTIGUOUS, POINTER :: r => NULL() ! the grid (pointer to buffer%host_buffer)
150 :
151 : END TYPE realspace_grid_type
152 :
153 : ! **************************************************************************************************
154 : TYPE realspace_grid_p_type
155 : TYPE(realspace_grid_type), POINTER :: rs_grid => NULL()
156 : END TYPE realspace_grid_p_type
157 :
158 : TYPE realspace_grid_desc_p_type
159 : TYPE(realspace_grid_desc_type), POINTER :: rs_desc => NULL()
160 : END TYPE realspace_grid_desc_p_type
161 :
162 : CONTAINS
163 :
164 : ! **************************************************************************************************
165 : !> \brief returns the 1D rank of the task which is a cartesian shift away from 1D rank rank_in
166 : !> only possible if rs_grid is a distributed grid
167 : !> \param rs_desc ...
168 : !> \param rank_in ...
169 : !> \param shift ...
170 : !> \return ...
171 : ! **************************************************************************************************
172 2346 : PURE FUNCTION rs_grid_locate_rank(rs_desc, rank_in, shift) RESULT(rank_out)
173 : TYPE(realspace_grid_desc_type), INTENT(IN) :: rs_desc
174 : INTEGER, INTENT(IN) :: rank_in
175 : INTEGER, DIMENSION(3), INTENT(IN) :: shift
176 : INTEGER :: rank_out
177 :
178 : INTEGER :: coord(3)
179 :
180 9384 : coord = MODULO(rs_desc%rank2coord(:, rank_in) + shift, rs_desc%group_dim)
181 2346 : rank_out = rs_desc%coord2rank(coord(1), coord(2), coord(3))
182 2346 : END FUNCTION rs_grid_locate_rank
183 :
184 : ! **************************************************************************************************
185 : !> \brief Determine the setup of real space grids - this is divided up into the
186 : !> creation of a descriptor and the actual grid itself (see rs_grid_create)
187 : !> \param desc ...
188 : !> \param pw_grid ...
189 : !> \param input_settings ...
190 : !> \param border_points ...
191 : !> \par History
192 : !> JGH (08-Jun-2003) : nsmax <= 0 indicates fully replicated grid
193 : !> Iain Bethune (05-Sep-2008) : modified cut heuristic
194 : !> (c) The Numerical Algorithms Group (NAG) Ltd, 2008 on behalf of the HECToR project
195 : !> - Create a descriptor for realspace grids with a number of border
196 : !> points as exactly given by the optional argument border_points.
197 : !> These grids are always distributed.
198 : !> (27.11.2013, Matthias Krack)
199 : !> \author JGH (18-Mar-2001)
200 : ! **************************************************************************************************
201 41330 : SUBROUTINE rs_grid_create_descriptor(desc, pw_grid, input_settings, border_points)
202 : TYPE(realspace_grid_desc_type), POINTER :: desc
203 : TYPE(pw_grid_type), INTENT(INOUT), TARGET :: pw_grid
204 : TYPE(realspace_grid_input_type), INTENT(IN) :: input_settings
205 : INTEGER, INTENT(IN), OPTIONAL :: border_points
206 :
207 : CHARACTER(LEN=*), PARAMETER :: routineN = 'rs_grid_create_descriptor'
208 :
209 : INTEGER :: border_size, dir, handle, i, j, k, l, &
210 : lb(2), min_npts_real, n_slices(3), &
211 : n_slices_tmp(3), nmin
212 : LOGICAL :: overlap
213 : REAL(KIND=dp) :: ratio, ratio_best, volume, volume_dist
214 :
215 41330 : CALL timeset(routineN, handle)
216 :
217 41330 : IF (PRESENT(border_points)) THEN
218 128 : border_size = border_points
219 : ELSE
220 : border_size = 0
221 : END IF
222 :
223 2025170 : ALLOCATE (desc)
224 :
225 41330 : CALL pw_grid%para%group%sync()
226 :
227 41330 : desc%pw => pw_grid
228 41330 : CALL pw_grid_retain(desc%pw)
229 :
230 537290 : desc%dh = pw_grid%dh
231 537290 : desc%dh_inv = pw_grid%dh_inv
232 41330 : desc%orthorhombic = pw_grid%orthorhombic
233 41330 : desc%ref_count = 1
234 :
235 41330 : IF (pw_grid%para%mode == PW_MODE_LOCAL) THEN
236 : ! The corresponding group has dimension 1
237 : ! All operations will be done locally
238 16912 : desc%npts = pw_grid%npts
239 16912 : desc%ngpts = PRODUCT(INT(desc%npts, KIND=int_8))
240 16912 : desc%lb = pw_grid%bounds(1, :)
241 16912 : desc%ub = pw_grid%bounds(2, :)
242 4228 : desc%border = border_size
243 4228 : IF (border_size == 0) THEN
244 16912 : desc%perd = 1
245 : ELSE
246 0 : desc%perd = 0
247 : END IF
248 4228 : desc%parallel = .FALSE.
249 4228 : desc%distributed = .FALSE.
250 4228 : desc%group = mp_comm_null
251 4228 : desc%group_size = 1
252 16912 : desc%group_dim = 1
253 16912 : desc%group_coor = 0
254 4228 : desc%my_pos = 0
255 : ELSE
256 : ! group size of desc grid
257 : ! global grid dimensions are still the same
258 37102 : desc%group_size = pw_grid%para%group%num_pe
259 148408 : desc%npts = pw_grid%npts
260 148408 : desc%ngpts = PRODUCT(INT(desc%npts, KIND=int_8))
261 148408 : desc%lb = pw_grid%bounds(1, :)
262 148408 : desc%ub = pw_grid%bounds(2, :)
263 :
264 : ! this is the eventual border size
265 37102 : IF (border_size == 0) THEN
266 36974 : nmin = (input_settings%nsmax + 1)/2
267 36974 : nmin = MAX(0, NINT(nmin*input_settings%halo_reduction_factor))
268 : ELSE
269 : ! Set explicitly the requested border size
270 : nmin = border_size
271 : END IF
272 :
273 37102 : IF (input_settings%distribution_type == rsgrid_replicated) THEN
274 :
275 57992 : n_slices = 1
276 14498 : IF (border_size > 0) THEN
277 : CALL cp_abort(__LOCATION__, &
278 : "An explicit border size > 0 is not yet working for "// &
279 : "replicated realspace grids. Request DISTRIBUTION_TYPE "// &
280 0 : "distributed for RS_GRID explicitly.")
281 : END IF
282 :
283 : ELSE
284 :
285 90416 : n_slices = 1
286 22604 : ratio_best = -HUGE(ratio_best)
287 :
288 : ! don't allow distributions with more processors than real grid points
289 67812 : DO k = 1, MIN(desc%npts(3), desc%group_size)
290 158228 : DO j = 1, MIN(desc%npts(2), desc%group_size)
291 90416 : i = MIN(desc%npts(1), desc%group_size/(j*k))
292 361664 : n_slices_tmp = [i, j, k]
293 :
294 : ! we don't match the actual number of CPUs
295 361664 : IF (PRODUCT(n_slices_tmp) /= desc%group_size) CYCLE
296 :
297 : ! we see if there has been a input constraint
298 : ! i.e. if the layout is not -1 we need to fullfil it
299 542754 : IF (.NOT. ALL(PACK(n_slices_tmp == input_settings%distribution_layout, &
300 : [-1, -1, -1] /= input_settings%distribution_layout) &
301 67812 : )) CYCLE
302 :
303 : ! We can not work with a grid that has more local than global grid points.
304 : ! This can happen when a halo region wraps around and overlaps with the other halo.
305 67586 : overlap = .FALSE.
306 270344 : DO dir = 1, 3
307 270344 : IF (n_slices_tmp(dir) > 1) THEN
308 202758 : DO l = 0, n_slices_tmp(dir) - 1
309 135172 : lb = get_limit(desc%npts(dir), n_slices_tmp(dir), l)
310 202758 : IF (lb(2) - lb(1) + 1 + 2*nmin > desc%npts(dir)) overlap = .TRUE.
311 : END DO
312 : END IF
313 : END DO
314 67586 : IF (overlap) CYCLE
315 :
316 : ! a heuristic optimisation to reduce the memory usage
317 : ! we go for the smallest local to real volume
318 : ! volume of the box without the wings / volume of the box with the wings
319 : ! with prefactodesc to promote less cuts in Z dimension
320 : ratio = PRODUCT(REAL(desc%npts, KIND=dp)/n_slices_tmp)/ &
321 : PRODUCT(REAL(desc%npts, KIND=dp)/n_slices_tmp + &
322 71414 : MERGE([0.0_dp, 0.0_dp, 0.0_dp], 2*[1.06_dp*nmin, 1.05_dp*nmin, 1.03_dp*nmin], n_slices_tmp == [1, 1, 1]))
323 55410 : IF (ratio > ratio_best) THEN
324 10172 : ratio_best = ratio
325 10172 : n_slices = n_slices_tmp
326 : END IF
327 :
328 : END DO
329 : END DO
330 :
331 : ! if automatic we can still decide this is a replicated grid
332 : ! if the memory gain (or the gain is messages) is too small.
333 22604 : IF (input_settings%distribution_type == rsgrid_automatic) THEN
334 88216 : volume = PRODUCT(REAL(desc%npts, KIND=dp))
335 : volume_dist = PRODUCT(REAL(desc%npts, KIND=dp)/n_slices + &
336 88216 : MERGE([0, 0, 0], 2*[nmin, nmin, nmin], n_slices == [1, 1, 1]))
337 22054 : IF (volume < volume_dist*input_settings%memory_factor) THEN
338 88216 : n_slices = 1
339 : END IF
340 : END IF
341 :
342 : END IF
343 :
344 148408 : desc%group_dim(:) = n_slices(:)
345 37102 : CALL desc%group%from_dup(pw_grid%para%group)
346 37102 : desc%group_size = desc%group%num_pe
347 37102 : desc%my_pos = desc%group%mepos
348 :
349 148230 : IF (ALL(n_slices == 1)) THEN
350 : ! CASE 1 : only one slice: we do not need overlapping regions and special
351 : ! recombination of the total density
352 36944 : desc%border = border_size
353 36944 : IF (border_size == 0) THEN
354 147776 : desc%perd = 1
355 : ELSE
356 0 : desc%perd = 0
357 : END IF
358 36944 : desc%distributed = .FALSE.
359 36944 : desc%parallel = .TRUE.
360 147776 : desc%group_coor(:) = 0
361 36944 : desc%my_virtual_pos = 0
362 :
363 110832 : ALLOCATE (desc%virtual2real(0:desc%group_size - 1))
364 110832 : ALLOCATE (desc%real2virtual(0:desc%group_size - 1))
365 : ! Start with no reordering
366 110832 : DO i = 0, desc%group_size - 1
367 73888 : desc%virtual2real(i) = i
368 110832 : desc%real2virtual(i) = i
369 : END DO
370 : ELSE
371 : ! CASE 2 : general case
372 : ! periodicity is no longer enforced arbritary directions
373 158 : IF (border_size == 0) THEN
374 120 : desc%perd = 1
375 120 : DO dir = 1, 3
376 120 : IF (n_slices(dir) > 1) desc%perd(dir) = 0
377 : END DO
378 : ELSE
379 512 : desc%perd(:) = 0
380 : END IF
381 : ! we keep a border of nmin points
382 158 : desc%border = nmin
383 : ! we are going parallel on the real space grid
384 158 : desc%parallel = .TRUE.
385 158 : desc%distributed = .TRUE.
386 :
387 : ! set up global info about the distribution
388 474 : ALLOCATE (desc%rank2coord(3, 0:desc%group_size - 1))
389 790 : ALLOCATE (desc%coord2rank(0:desc%group_dim(1) - 1, 0:desc%group_dim(2) - 1, 0:desc%group_dim(3) - 1))
390 474 : ALLOCATE (desc%lb_global(3, 0:desc%group_size - 1))
391 474 : ALLOCATE (desc%ub_global(3, 0:desc%group_size - 1))
392 474 : ALLOCATE (desc%x2coord(desc%lb(1):desc%ub(1)))
393 474 : ALLOCATE (desc%y2coord(desc%lb(2):desc%ub(2)))
394 474 : ALLOCATE (desc%z2coord(desc%lb(3):desc%ub(3)))
395 :
396 474 : DO i = 0, desc%group_size - 1
397 : ! Calculate coordinates in a row-major order (to be SMP-friendly)
398 316 : desc%rank2coord(1, i) = i/(desc%group_dim(2)*desc%group_dim(3))
399 : desc%rank2coord(2, i) = MODULO(i, desc%group_dim(2)*desc%group_dim(3)) &
400 316 : /desc%group_dim(3)
401 316 : desc%rank2coord(3, i) = MODULO(i, desc%group_dim(3))
402 :
403 316 : IF (i == desc%my_pos) THEN
404 632 : desc%group_coor = desc%rank2coord(:, i)
405 : END IF
406 :
407 316 : desc%coord2rank(desc%rank2coord(1, i), desc%rank2coord(2, i), desc%rank2coord(3, i)) = i
408 : ! the lb_global and ub_global correspond to lb_real and ub_real of each task
409 1264 : desc%lb_global(:, i) = desc%lb
410 1264 : desc%ub_global(:, i) = desc%ub
411 1422 : DO dir = 1, 3
412 1264 : IF (desc%group_dim(dir) > 1) THEN
413 316 : lb = get_limit(desc%npts(dir), desc%group_dim(dir), desc%rank2coord(dir, i))
414 316 : desc%lb_global(dir, i) = lb(1) + desc%lb(dir) - 1
415 316 : desc%ub_global(dir, i) = lb(2) + desc%lb(dir) - 1
416 : END IF
417 : END DO
418 : END DO
419 :
420 : ! map a grid point to a CPU coord
421 632 : DO dir = 1, 3
422 1264 : DO l = 0, desc%group_dim(dir) - 1
423 632 : IF (desc%group_dim(dir) > 1) THEN
424 316 : lb = get_limit(desc%npts(dir), desc%group_dim(dir), l)
425 948 : lb = lb + desc%lb(dir) - 1
426 : ELSE
427 316 : lb(1) = desc%lb(dir)
428 316 : lb(2) = desc%ub(dir)
429 : END IF
430 474 : SELECT CASE (dir)
431 : CASE (1)
432 11696 : desc%x2coord(lb(1):lb(2)) = l
433 : CASE (2)
434 12104 : desc%y2coord(lb(1):lb(2)) = l
435 : CASE (3)
436 12200 : desc%z2coord(lb(1):lb(2)) = l
437 : END SELECT
438 : END DO
439 : END DO
440 :
441 : ! an upper bound for the number of neighbours the border is overlapping with
442 632 : DO dir = 1, 3
443 474 : desc%neighbours(dir) = 0
444 632 : IF ((n_slices(dir) > 1) .OR. (border_size > 0)) THEN
445 414 : min_npts_real = HUGE(0)
446 986 : DO l = 0, n_slices(dir) - 1
447 572 : lb = get_limit(desc%npts(dir), n_slices(dir), l)
448 986 : min_npts_real = MIN(lb(2) - lb(1) + 1, min_npts_real)
449 : END DO
450 414 : desc%neighbours(dir) = (desc%border + min_npts_real - 1)/min_npts_real
451 : END IF
452 : END DO
453 :
454 474 : ALLOCATE (desc%virtual2real(0:desc%group_size - 1))
455 474 : ALLOCATE (desc%real2virtual(0:desc%group_size - 1))
456 : ! Start with no reordering
457 474 : DO i = 0, desc%group_size - 1
458 316 : desc%virtual2real(i) = i
459 474 : desc%real2virtual(i) = i
460 : END DO
461 :
462 158 : desc%my_virtual_pos = desc%real2virtual(desc%my_pos)
463 632 : desc%virtual_group_coor(:) = desc%rank2coord(:, desc%my_virtual_pos)
464 :
465 : END IF
466 : END IF
467 :
468 41330 : CALL timestop(handle)
469 :
470 41330 : END SUBROUTINE rs_grid_create_descriptor
471 :
472 : ! **************************************************************************************************
473 : !> \brief ...
474 : !> \param rs ...
475 : !> \param desc ...
476 : ! **************************************************************************************************
477 6066438 : SUBROUTINE rs_grid_create(rs, desc)
478 : TYPE(realspace_grid_type), INTENT(OUT) :: rs
479 : TYPE(realspace_grid_desc_type), INTENT(INOUT), &
480 : TARGET :: desc
481 :
482 : CHARACTER(LEN=*), PARAMETER :: routineN = 'rs_grid_create'
483 :
484 : INTEGER :: handle
485 :
486 288878 : CALL timeset(routineN, handle)
487 :
488 288878 : rs%desc => desc
489 288878 : CALL rs_grid_retain_descriptor(rs%desc)
490 :
491 288878 : IF (desc%pw%para%mode == PW_MODE_LOCAL) THEN
492 : ! The corresponding group has dimension 1
493 : ! All operations will be done locally
494 70976 : rs%lb_real = desc%lb
495 70976 : rs%ub_real = desc%ub
496 70976 : rs%lb_local = rs%lb_real - desc%border*(1 - desc%perd)
497 70976 : rs%ub_local = rs%ub_real + desc%border*(1 - desc%perd)
498 70976 : rs%npts_local = rs%ub_local - rs%lb_local + 1
499 70976 : rs%ngpts_local = PRODUCT(rs%npts_local)
500 : END IF
501 :
502 1153116 : IF (ALL(rs%desc%group_dim == 1)) THEN
503 : ! CASE 1 : only one slice: we do not need overlapping regions and special
504 : ! recombination of the total density
505 1148408 : rs%lb_real = desc%lb
506 1148408 : rs%ub_real = desc%ub
507 1148408 : rs%lb_local = rs%lb_real - desc%border*(1 - desc%perd)
508 1148408 : rs%ub_local = rs%ub_real + desc%border*(1 - desc%perd)
509 1148408 : rs%npts_local = rs%ub_local - rs%lb_local + 1
510 1148408 : rs%ngpts_local = PRODUCT(rs%npts_local)
511 : ELSE
512 : ! CASE 2 : general case
513 : ! extract some more derived quantities about the local grid
514 7104 : rs%lb_real = desc%lb_global(:, desc%my_virtual_pos)
515 7104 : rs%ub_real = desc%ub_global(:, desc%my_virtual_pos)
516 7104 : rs%lb_local = rs%lb_real - desc%border*(1 - desc%perd)
517 7104 : rs%ub_local = rs%ub_real + desc%border*(1 - desc%perd)
518 7104 : rs%npts_local = rs%ub_local - rs%lb_local + 1
519 7104 : rs%ngpts_local = PRODUCT(rs%npts_local)
520 : END IF
521 :
522 288878 : CALL offload_create_buffer(rs%ngpts_local, rs%buffer)
523 : rs%r(rs%lb_local(1):rs%ub_local(1), &
524 : rs%lb_local(2):rs%ub_local(2), &
525 288878 : rs%lb_local(3):rs%ub_local(3)) => rs%buffer%host_buffer
526 :
527 866634 : ALLOCATE (rs%px(desc%npts(1)))
528 866634 : ALLOCATE (rs%py(desc%npts(2)))
529 866634 : ALLOCATE (rs%pz(desc%npts(3)))
530 :
531 288878 : CALL timestop(handle)
532 :
533 288878 : END SUBROUTINE rs_grid_create
534 :
535 : ! **************************************************************************************************
536 : !> \brief Defines a new ordering of ranks on this realspace grid, recalculating
537 : !> the data bounds and reallocating the grid. As a result, each MPI process
538 : !> now has a real rank (i.e., its rank in the MPI communicator from the pw grid)
539 : !> and a virtual rank (the rank of the process where the data now owned by this
540 : !> process would reside in an ordinary cartesian distribution).
541 : !> NB. Since the grid size required may change, the caller should be sure to release
542 : !> and recreate the corresponding rs_grids
543 : !> The desc%real2virtual and desc%virtual2real arrays can be used to map
544 : !> a physical rank to the 'rank' of data owned by that process and vice versa
545 : !> \param desc ...
546 : !> \param real2virtual ...
547 : !> \par History
548 : !> 04-2009 created [Iain Bethune]
549 : !> (c) The Numerical Algorithms Group (NAG) Ltd, 2009 on behalf of the HECToR project
550 : ! **************************************************************************************************
551 6 : PURE SUBROUTINE rs_grid_reorder_ranks(desc, real2virtual)
552 :
553 : TYPE(realspace_grid_desc_type), INTENT(INOUT) :: desc
554 : INTEGER, DIMENSION(:), INTENT(IN) :: real2virtual
555 :
556 : INTEGER :: i
557 :
558 18 : desc%real2virtual(:) = real2virtual
559 :
560 18 : DO i = 0, desc%group_size - 1
561 18 : desc%virtual2real(desc%real2virtual(i)) = i
562 : END DO
563 :
564 6 : desc%my_virtual_pos = desc%real2virtual(desc%my_pos)
565 :
566 12 : IF (.NOT. ALL(desc%group_dim == 1)) THEN
567 24 : desc%virtual_group_coor(:) = desc%rank2coord(:, desc%my_virtual_pos)
568 : END IF
569 :
570 6 : END SUBROUTINE rs_grid_reorder_ranks
571 :
572 : ! **************************************************************************************************
573 : !> \brief Print information on grids to output
574 : !> \param rs ...
575 : !> \param iounit ...
576 : !> \author JGH (17-May-2007)
577 : ! **************************************************************************************************
578 17512 : SUBROUTINE rs_grid_print(rs, iounit)
579 : TYPE(realspace_grid_type), INTENT(IN) :: rs
580 : INTEGER, INTENT(in) :: iounit
581 :
582 : INTEGER :: dir, i, nn
583 : REAL(KIND=dp) :: pp(3)
584 :
585 17512 : IF (rs%desc%parallel) THEN
586 17220 : IF (iounit > 0) THEN
587 : WRITE (iounit, '(/,A,T71,I10)') &
588 7767 : " RS_GRID| Information for grid number ", rs%desc%pw%id_nr
589 31068 : DO i = 1, 3
590 23301 : WRITE (iounit, '(A,I3,T30,2I8,T62,A,T71,I10)') " RS_GRID| Bounds ", &
591 54369 : i, rs%desc%lb(i), rs%desc%ub(i), "Points:", rs%desc%npts(i)
592 : END DO
593 7767 : IF (.NOT. rs%desc%distributed) THEN
594 7752 : WRITE (iounit, '(A)') " RS_GRID| Real space fully replicated"
595 : WRITE (iounit, '(A,T71,I10)') &
596 7752 : " RS_GRID| Group size ", rs%desc%group_dim(2)
597 : ELSE
598 60 : DO dir = 1, 3
599 60 : IF (rs%desc%perd(dir) /= 1) THEN
600 : WRITE (iounit, '(A,T71,I3,A)') &
601 15 : " RS_GRID| Real space distribution over ", rs%desc%group_dim(dir), " groups"
602 : WRITE (iounit, '(A,T71,I10)') &
603 15 : " RS_GRID| Real space distribution along direction ", dir
604 : WRITE (iounit, '(A,T71,I10)') &
605 15 : " RS_GRID| Border size ", rs%desc%border
606 : END IF
607 : END DO
608 : END IF
609 : END IF
610 17220 : IF (rs%desc%distributed) THEN
611 120 : DO dir = 1, 3
612 120 : IF (rs%desc%perd(dir) /= 1) THEN
613 30 : nn = rs%npts_local(dir)
614 30 : CALL rs%desc%group%sum(nn)
615 120 : pp(1) = REAL(nn, KIND=dp)/REAL(PRODUCT(rs%desc%group_dim), KIND=dp)
616 30 : nn = rs%npts_local(dir)
617 30 : CALL rs%desc%group%max(nn)
618 30 : pp(2) = REAL(nn, KIND=dp)
619 30 : nn = rs%npts_local(dir)
620 30 : CALL rs%desc%group%min(nn)
621 30 : pp(3) = REAL(nn, KIND=dp)
622 30 : IF (iounit > 0) THEN
623 15 : WRITE (iounit, '(A,T48,A)') " RS_GRID| Distribution", &
624 30 : " Average Max Min"
625 15 : WRITE (iounit, '(A,T45,F12.1,2I12)') " RS_GRID| Planes ", &
626 30 : pp(1), NINT(pp(2)), NINT(pp(3))
627 : END IF
628 : END IF
629 : END DO
630 : ! WRITE ( iounit, '(/)' )
631 : END IF
632 : ELSE
633 292 : IF (iounit > 0) THEN
634 : WRITE (iounit, '(/,A,T71,I10)') &
635 180 : " RS_GRID| Information for grid number ", rs%desc%pw%id_nr
636 720 : DO i = 1, 3
637 540 : WRITE (iounit, '(A,I3,T30,2I8,T62,A,T71,I10)') " RS_GRID| Bounds ", &
638 1260 : i, rs%desc%lb(i), rs%desc%ub(i), "Points:", rs%desc%npts(i)
639 : END DO
640 : ! WRITE ( iounit, '(/)' )
641 : END IF
642 : END IF
643 :
644 17512 : END SUBROUTINE rs_grid_print
645 :
646 : ! **************************************************************************************************
647 : !> \brief ...
648 : !> \param rs ...
649 : !> \param pw ...
650 : ! **************************************************************************************************
651 1368160 : SUBROUTINE transfer_rs2pw(rs, pw)
652 : TYPE(realspace_grid_type), INTENT(IN) :: rs
653 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pw
654 :
655 : CHARACTER(len=*), PARAMETER :: routineN = 'transfer_rs2pw'
656 :
657 : INTEGER :: handle, handle2, i
658 :
659 1368160 : CALL timeset(routineN, handle2)
660 1368160 : CALL timeset(routineN//"_"//TRIM(ADJUSTL(cp_to_string(CEILING(pw%pw_grid%cutoff/10)*10))), handle)
661 :
662 1368160 : IF (.NOT. ASSOCIATED(rs%desc%pw, pw%pw_grid)) THEN
663 0 : CPABORT("Different rs and pw indentifiers")
664 : END IF
665 :
666 1368160 : IF (rs%desc%distributed) THEN
667 1852 : CALL transfer_rs2pw_distributed(rs, pw)
668 1366308 : ELSE IF (rs%desc%parallel) THEN
669 1150150 : CALL transfer_rs2pw_replicated(rs, pw)
670 : ELSE ! treat simple serial case locally
671 216158 : IF (rs%desc%border == 0) THEN
672 864632 : CALL dcopy(SIZE(rs%r), rs%r, 1, pw%array, 1)
673 : ELSE
674 0 : CPASSERT(LBOUND(pw%array, 3) == rs%lb_real(3))
675 0 : !$OMP PARALLEL DO DEFAULT(NONE) SHARED(pw,rs)
676 : DO i = rs%lb_real(3), rs%ub_real(3)
677 : pw%array(:, :, i) = rs%r(rs%lb_real(1):rs%ub_real(1), &
678 : rs%lb_real(2):rs%ub_real(2), i)
679 : END DO
680 : !$OMP END PARALLEL DO
681 : END IF
682 : END IF
683 :
684 1368160 : CALL timestop(handle)
685 1368160 : CALL timestop(handle2)
686 :
687 1368160 : END SUBROUTINE transfer_rs2pw
688 :
689 : ! **************************************************************************************************
690 : !> \brief ...
691 : !> \param rs ...
692 : !> \param pw ...
693 : ! **************************************************************************************************
694 1293163 : SUBROUTINE transfer_pw2rs(rs, pw)
695 :
696 : TYPE(realspace_grid_type), INTENT(IN) :: rs
697 : TYPE(pw_r3d_rs_type), INTENT(IN) :: pw
698 :
699 : CHARACTER(len=*), PARAMETER :: routineN = 'transfer_pw2rs'
700 :
701 : INTEGER :: handle, handle2, i, im, j, jm, k, km
702 :
703 1293163 : CALL timeset(routineN, handle2)
704 1293163 : CALL timeset(routineN//"_"//TRIM(ADJUSTL(cp_to_string(CEILING(pw%pw_grid%cutoff/10)*10))), handle)
705 :
706 1293163 : IF (.NOT. ASSOCIATED(rs%desc%pw, pw%pw_grid)) THEN
707 0 : CPABORT("Different rs and pw indentifiers")
708 : END IF
709 :
710 1293163 : IF (rs%desc%distributed) THEN
711 872 : CALL transfer_pw2rs_distributed(rs, pw)
712 1292291 : ELSE IF (rs%desc%parallel) THEN
713 1039516 : CALL transfer_pw2rs_replicated(rs, pw)
714 : ELSE ! treat simple serial case locally
715 252775 : IF (rs%desc%border == 0) THEN
716 1011100 : CALL dcopy(SIZE(rs%r), pw%array, 1, rs%r, 1)
717 : ELSE
718 : !$OMP PARALLEL DO DEFAULT(NONE) &
719 : !$OMP PRIVATE(i,im,j,jm,k,km) &
720 0 : !$OMP SHARED(pw,rs)
721 : DO k = rs%lb_local(3), rs%ub_local(3)
722 : IF (k < rs%lb_real(3)) THEN
723 : km = k + rs%desc%npts(3)
724 : ELSE IF (k > rs%ub_real(3)) THEN
725 : km = k - rs%desc%npts(3)
726 : ELSE
727 : km = k
728 : END IF
729 : DO j = rs%lb_local(2), rs%ub_local(2)
730 : IF (j < rs%lb_real(2)) THEN
731 : jm = j + rs%desc%npts(2)
732 : ELSE IF (j > rs%ub_real(2)) THEN
733 : jm = j - rs%desc%npts(2)
734 : ELSE
735 : jm = j
736 : END IF
737 : DO i = rs%lb_local(1), rs%ub_local(1)
738 : IF (i < rs%lb_real(1)) THEN
739 : im = i + rs%desc%npts(1)
740 : ELSE IF (i > rs%ub_real(1)) THEN
741 : im = i - rs%desc%npts(1)
742 : ELSE
743 : im = i
744 : END IF
745 : rs%r(i, j, k) = pw%array(im, jm, km)
746 : END DO
747 : END DO
748 : END DO
749 : !$OMP END PARALLEL DO
750 : END IF
751 : END IF
752 :
753 1293163 : CALL timestop(handle)
754 1293163 : CALL timestop(handle2)
755 :
756 1293163 : END SUBROUTINE transfer_pw2rs
757 :
758 : ! **************************************************************************************************
759 : !> \brief transfer from a realspace grid to a planewave grid
760 : !> \param rs ...
761 : !> \param pw ...
762 : ! **************************************************************************************************
763 1150150 : SUBROUTINE transfer_rs2pw_replicated(rs, pw)
764 : TYPE(realspace_grid_type), INTENT(IN) :: rs
765 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pw
766 :
767 : INTEGER :: dest, ii, ip, ix, iy, iz, nma, nn, s(3), &
768 : source
769 1150150 : INTEGER, ALLOCATABLE, DIMENSION(:) :: rcount
770 : INTEGER, DIMENSION(3) :: lb, ub
771 1150150 : INTEGER, DIMENSION(:, :, :, :), POINTER :: bounds_by_rank
772 1150150 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: recvbuf, sendbuf, swaparray
773 :
774 : ! Avoid nested component array sections: GCC 16 can generate invalid bounds checks.
775 1150150 : bounds_by_rank => pw%pw_grid%para%bo
776 : ASSOCIATE (np => pw%pw_grid%para%group%num_pe, bo => bounds_by_rank(:, :, :, 1), &
777 : pbo => pw%pw_grid%bounds, group => pw%pw_grid%para%group, mepos => pw%pw_grid%para%group%mepos, &
778 : grid => rs%r)
779 3450450 : ALLOCATE (rcount(0:np - 1))
780 3450450 : DO ip = 1, np
781 10351350 : rcount(ip - 1) = PRODUCT(bo(2, :, ip) - bo(1, :, ip) + 1)
782 : END DO
783 3450450 : nma = MAXVAL(rcount(0:np - 1))
784 4600600 : ALLOCATE (sendbuf(nma), recvbuf(nma))
785 43424367600 : sendbuf = 1.0E99_dp; recvbuf = 1.0E99_dp ! init mpi'ed buffers to silence warnings under valgrind
786 :
787 : !sample peak memory
788 1150150 : CALL m_memory()
789 :
790 1150150 : dest = MODULO(mepos + 1, np)
791 1150150 : source = MODULO(mepos - 1, np)
792 1150150 : sendbuf = 0.0_dp
793 :
794 2300300 : DO ip = 1, np
795 :
796 9201200 : lb = pbo(1, :) + bo(1, :, MODULO(mepos - ip, np) + 1) - 1
797 9201200 : ub = pbo(1, :) + bo(2, :, MODULO(mepos - ip, np) + 1) - 1
798 : ! this loop takes about the same time as the message passing call
799 : ! notice that the range of ix is only a small fraction of the first index of grid
800 : ! therefore it seems faster to have the second index as the innermost loop
801 : ! if this runs on many cpus
802 : ! tested on itanium, pentium4, opteron, ultrasparc...
803 9201200 : s = ub - lb + 1
804 57770480 : DO iz = lb(3), ub(3)
805 1021915492 : DO ix = lb(1), ub(1)
806 964145012 : ii = (iz - lb(3))*s(1)*s(2) + (ix - lb(1)) + 1
807 44139472380 : DO iy = lb(2), ub(2)
808 43119857188 : sendbuf(ii) = sendbuf(ii) + grid(ix, iy, iz)
809 44084002200 : ii = ii + s(1)
810 : END DO
811 : END DO
812 : END DO
813 2300300 : IF (ip == np) EXIT
814 1150150 : CALL group%sendrecv(sendbuf, dest, recvbuf, source, 13)
815 1150150 : CALL MOVE_ALLOC(sendbuf, swaparray)
816 1150150 : CALL MOVE_ALLOC(recvbuf, sendbuf)
817 2300300 : CALL MOVE_ALLOC(swaparray, recvbuf)
818 : END DO
819 1150150 : nn = rcount(mepos)
820 : END ASSOCIATE
821 :
822 1150150 : CALL dcopy(nn, sendbuf, 1, pw%array, 1)
823 :
824 1150150 : DEALLOCATE (rcount)
825 1150150 : DEALLOCATE (sendbuf)
826 1150150 : DEALLOCATE (recvbuf)
827 :
828 1150150 : END SUBROUTINE transfer_rs2pw_replicated
829 :
830 : ! **************************************************************************************************
831 : !> \brief transfer from a planewave grid to a realspace grid
832 : !> \param rs ...
833 : !> \param pw ...
834 : ! **************************************************************************************************
835 1039516 : SUBROUTINE transfer_pw2rs_replicated(rs, pw)
836 : TYPE(realspace_grid_type), INTENT(IN) :: rs
837 : TYPE(pw_r3d_rs_type), INTENT(IN) :: pw
838 :
839 : INTEGER :: dest, i, ii, im, ip, ix, iy, iz, j, jm, &
840 : k, km, nma, nn, source
841 1039516 : INTEGER, ALLOCATABLE, DIMENSION(:) :: rcount
842 : INTEGER, DIMENSION(3) :: lb, ub
843 1039516 : INTEGER, DIMENSION(:, :, :, :), POINTER :: bounds_by_rank
844 1039516 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: recvbuf, sendbuf, swaparray
845 3118548 : TYPE(mp_request_type), DIMENSION(2) :: req
846 :
847 : ! Keep the reverse transfer on the same compiler-safe array-section path.
848 1039516 : bounds_by_rank => pw%pw_grid%para%bo
849 : ASSOCIATE (np => pw%pw_grid%para%group%num_pe, bo => bounds_by_rank(:, :, :, 1), &
850 : pbo => pw%pw_grid%bounds, group => pw%pw_grid%para%group, mepos => pw%pw_grid%para%group%mepos, &
851 : grid => rs%r)
852 3118548 : ALLOCATE (rcount(0:np - 1))
853 3118548 : DO ip = 1, np
854 9355644 : rcount(ip - 1) = PRODUCT(bo(2, :, ip) - bo(1, :, ip) + 1)
855 : END DO
856 3118548 : nma = MAXVAL(rcount(0:np - 1))
857 4158064 : ALLOCATE (sendbuf(nma), recvbuf(nma))
858 39984648968 : sendbuf = 1.0E99_dp; recvbuf = 1.0E99_dp ! init mpi'ed buffers to silence warnings under valgrind
859 :
860 : !sample peak memory
861 1039516 : CALL m_memory()
862 :
863 1039516 : nn = rcount(mepos)
864 1039516 : CALL dcopy(nn, pw%array, 1, sendbuf, 1)
865 :
866 1039516 : dest = MODULO(mepos + 1, np)
867 1039516 : source = MODULO(mepos - 1, np)
868 :
869 3118548 : DO ip = 0, np - 1
870 : ! we must shift the buffer only np-1 times around
871 2079032 : IF (ip /= np - 1) THEN
872 : CALL group%isendrecv(sendbuf, dest, recvbuf, source, &
873 1039516 : req(1), req(2), 13)
874 : END IF
875 8316128 : lb = pbo(1, :) + bo(1, :, MODULO(mepos - ip, np) + 1) - 1
876 8316128 : ub = pbo(1, :) + bo(2, :, MODULO(mepos - ip, np) + 1) - 1
877 2079032 : ii = 0
878 : ! this loop takes about the same time as the message passing call
879 : ! If I read the code correctly then:
880 52822572 : DO iz = lb(3), ub(3)
881 1813325704 : DO iy = lb(2), ub(2)
882 41519440252 : DO ix = lb(1), ub(1)
883 39708193580 : ii = ii + 1
884 41468696712 : grid(ix, iy, iz) = sendbuf(ii)
885 : END DO
886 : END DO
887 : END DO
888 2079032 : IF (ip /= np - 1) THEN
889 1039516 : CALL mp_waitall(req)
890 : END IF
891 2079032 : CALL MOVE_ALLOC(sendbuf, swaparray)
892 2079032 : CALL MOVE_ALLOC(recvbuf, sendbuf)
893 3118548 : CALL MOVE_ALLOC(swaparray, recvbuf)
894 : END DO
895 2079032 : IF (rs%desc%border > 0) THEN
896 : !$OMP PARALLEL DO DEFAULT(NONE) &
897 : !$OMP PRIVATE(i,im,j,jm,k,km) &
898 0 : !$OMP SHARED(rs)
899 : DO k = rs%lb_local(3), rs%ub_local(3)
900 : IF (k < rs%lb_real(3)) THEN
901 : km = k + rs%desc%npts(3)
902 : ELSE IF (k > rs%ub_real(3)) THEN
903 : km = k - rs%desc%npts(3)
904 : ELSE
905 : km = k
906 : END IF
907 : DO j = rs%lb_local(2), rs%ub_local(2)
908 : IF (j < rs%lb_real(2)) THEN
909 : jm = j + rs%desc%npts(2)
910 : ELSE IF (j > rs%ub_real(2)) THEN
911 : jm = j - rs%desc%npts(2)
912 : ELSE
913 : jm = j
914 : END IF
915 : DO i = rs%lb_local(1), rs%ub_local(1)
916 : IF (i < rs%lb_real(1)) THEN
917 : im = i + rs%desc%npts(1)
918 : ELSE IF (i > rs%ub_real(1)) THEN
919 : im = i - rs%desc%npts(1)
920 : ELSE
921 : im = i
922 : END IF
923 : rs%r(i, j, k) = rs%r(im, jm, km)
924 : END DO
925 : END DO
926 : END DO
927 : !$OMP END PARALLEL DO
928 : END IF
929 : END ASSOCIATE
930 :
931 1039516 : DEALLOCATE (rcount)
932 1039516 : DEALLOCATE (sendbuf)
933 1039516 : DEALLOCATE (recvbuf)
934 :
935 1039516 : END SUBROUTINE transfer_pw2rs_replicated
936 :
937 : ! **************************************************************************************************
938 : !> \brief does the rs2pw transfer in the case where the rs grid is
939 : !> distributed (3D domain decomposition)
940 : !> \param rs ...
941 : !> \param pw ...
942 : !> \par History
943 : !> 12.2007 created [Matt Watkins]
944 : !> 9.2008 reduced amount of halo data sent [Iain Bethune]
945 : !> 10.2008 added non-blocking communication [Iain Bethune]
946 : !> 4.2009 added support for rank-reordering on the grid [Iain Bethune]
947 : !> 12.2009 added OMP and sparse alltoall [Iain Bethune]
948 : !> (c) The Numerical Algorithms Group (NAG) Ltd, 2008-2009 on behalf of the HECToR project
949 : !> \note
950 : !> the transfer is a two step procedure. For example, for the rs2pw transfer:
951 : !>
952 : !> 1) Halo-exchange in 3D so that the local part of the rs_grid contains the full data
953 : !> 2) an alltoall communication to redistribute the local rs_grid to the local pw_grid
954 : !>
955 : !> the halo exchange is most expensive on a large number of CPUs. Particular in this halo
956 : !> exchange is that the border region is rather large (e.g. 20 points) and that it might overlap
957 : !> with the central domain of several CPUs (i.e. next nearest neighbors)
958 : ! **************************************************************************************************
959 1852 : SUBROUTINE transfer_rs2pw_distributed(rs, pw)
960 : TYPE(realspace_grid_type), INTENT(IN) :: rs
961 : TYPE(pw_r3d_rs_type), INTENT(IN) :: pw
962 :
963 : CHARACTER(LEN=200) :: error_string
964 : INTEGER :: completed, dest_down, dest_up, i, idir, j, k, lb, my_id, my_pw_rank, my_rs_rank, &
965 : n_shifts, nn, num_threads, position, source_down, source_up, ub, x, y, z
966 1852 : INTEGER, ALLOCATABLE, DIMENSION(:) :: dshifts, recv_disps, recv_sizes, &
967 1852 : send_disps, send_sizes, ushifts
968 3704 : INTEGER, ALLOCATABLE, DIMENSION(:, :) :: bounds, recv_tasks, send_tasks
969 : INTEGER, DIMENSION(2) :: neighbours, pos
970 : INTEGER, DIMENSION(3) :: coords, lb_recv, lb_recv_down, lb_recv_up, lb_send, lb_send_down, &
971 : lb_send_up, ub_recv, ub_recv_down, ub_recv_up, ub_send, ub_send_down, ub_send_up
972 : LOGICAL, DIMENSION(3) :: halo_swapped
973 : REAL(KIND=dp) :: pw_sum, rs_sum
974 1852 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: recv_buf_3d_down, recv_buf_3d_up, &
975 1852 : send_buf_3d_down, send_buf_3d_up
976 3704 : TYPE(cp_1d_r_p_type), ALLOCATABLE, DIMENSION(:) :: recv_bufs, send_bufs
977 1852 : TYPE(mp_request_type), ALLOCATABLE, DIMENSION(:) :: recv_reqs, send_reqs
978 9260 : TYPE(mp_request_type), DIMENSION(4) :: req
979 :
980 1852 : num_threads = 1
981 1852 : my_id = 0
982 :
983 : ! safety check, to be removed once we're absolute sure the routine is correct
984 : IF (debug_this_module) THEN
985 : rs_sum = accurate_sum(rs%r)*ABS(det_3x3(rs%desc%dh))
986 : CALL rs%desc%group%sum(rs_sum)
987 : END IF
988 :
989 1852 : halo_swapped = .FALSE.
990 : ! We don't need to send the 'edges' of the halos that have already been sent
991 : ! Halos are contiguous in memory in z-direction only, so swap these first,
992 : ! and send less data in the y and x directions which are more expensive
993 :
994 7408 : DO idir = 3, 1, -1
995 :
996 5556 : IF (rs%desc%perd(idir) /= 1) THEN
997 :
998 14412 : ALLOCATE (dshifts(0:rs%desc%neighbours(idir)))
999 9608 : ALLOCATE (ushifts(0:rs%desc%neighbours(idir)))
1000 :
1001 4804 : ushifts = 0
1002 4804 : dshifts = 0
1003 :
1004 : ! check that we don't try to send data to ourself
1005 6656 : DO n_shifts = 1, MIN(rs%desc%neighbours(idir), rs%desc%group_dim(idir) - 1)
1006 :
1007 : ! need to take into account the possible varying widths of neighbouring cells
1008 : ! offset_up and offset_down hold the real size of the neighbouring cells
1009 1852 : position = MODULO(rs%desc%virtual_group_coor(idir) - n_shifts, rs%desc%group_dim(idir))
1010 1852 : neighbours = get_limit(rs%desc%npts(idir), rs%desc%group_dim(idir), position)
1011 1852 : dshifts(n_shifts) = dshifts(n_shifts - 1) + (neighbours(2) - neighbours(1) + 1)
1012 :
1013 1852 : position = MODULO(rs%desc%virtual_group_coor(idir) + n_shifts, rs%desc%group_dim(idir))
1014 1852 : neighbours = get_limit(rs%desc%npts(idir), rs%desc%group_dim(idir), position)
1015 1852 : ushifts(n_shifts) = ushifts(n_shifts - 1) + (neighbours(2) - neighbours(1) + 1)
1016 :
1017 : ! The border data has to be send/received from the neighbours
1018 : ! First we calculate the source and destination processes for the shift
1019 : ! We do both shifts at once to allow for more overlap of communication and buffer packing/unpacking
1020 :
1021 1852 : CALL cart_shift(rs, idir, -1*n_shifts, source_down, dest_down)
1022 :
1023 7408 : lb_send_down(:) = rs%lb_local(:)
1024 7408 : lb_recv_down(:) = rs%lb_local(:)
1025 7408 : ub_recv_down(:) = rs%ub_local(:)
1026 7408 : ub_send_down(:) = rs%ub_local(:)
1027 :
1028 1852 : IF (dshifts(n_shifts - 1) <= rs%desc%border) THEN
1029 1852 : ub_send_down(idir) = lb_send_down(idir) + rs%desc%border - 1 - dshifts(n_shifts - 1)
1030 : lb_send_down(idir) = MAX(lb_send_down(idir), &
1031 1852 : lb_send_down(idir) + rs%desc%border - dshifts(n_shifts))
1032 :
1033 1852 : ub_recv_down(idir) = ub_recv_down(idir) - rs%desc%border
1034 : lb_recv_down(idir) = MAX(lb_recv_down(idir) + rs%desc%border, &
1035 1852 : ub_recv_down(idir) - rs%desc%border + 1 + ushifts(n_shifts - 1))
1036 : ELSE
1037 0 : lb_send_down(idir) = 0
1038 0 : ub_send_down(idir) = -1
1039 0 : lb_recv_down(idir) = 0
1040 0 : ub_recv_down(idir) = -1
1041 : END IF
1042 :
1043 7408 : DO i = 1, 3
1044 7408 : IF (halo_swapped(i)) THEN
1045 554 : lb_send_down(i) = rs%lb_real(i)
1046 554 : ub_send_down(i) = rs%ub_real(i)
1047 554 : lb_recv_down(i) = rs%lb_real(i)
1048 554 : ub_recv_down(i) = rs%ub_real(i)
1049 : END IF
1050 : END DO
1051 :
1052 : ! post the receive
1053 0 : ALLOCATE (recv_buf_3d_down(lb_recv_down(1):ub_recv_down(1), &
1054 9260 : lb_recv_down(2):ub_recv_down(2), lb_recv_down(3):ub_recv_down(3)))
1055 1852 : CALL rs%desc%group%irecv(recv_buf_3d_down, source_down, req(1))
1056 :
1057 : ! now allocate, pack and send the send buffer
1058 7408 : nn = PRODUCT(ub_send_down - lb_send_down + 1)
1059 0 : ALLOCATE (send_buf_3d_down(lb_send_down(1):ub_send_down(1), &
1060 9260 : lb_send_down(2):ub_send_down(2), lb_send_down(3):ub_send_down(3)))
1061 :
1062 : !$OMP PARALLEL DEFAULT(NONE), &
1063 : !$OMP PRIVATE(lb,ub,my_id,NUM_THREADS), &
1064 1852 : !$OMP SHARED(send_buf_3d_down,rs,lb_send_down,ub_send_down)
1065 : !$ num_threads = MIN(omp_get_max_threads(), ub_send_down(3) - lb_send_down(3) + 1)
1066 : !$ my_id = omp_get_thread_num()
1067 : IF (my_id < num_threads) THEN
1068 : lb = lb_send_down(3) + ((ub_send_down(3) - lb_send_down(3) + 1)*my_id)/num_threads
1069 : ub = lb_send_down(3) + ((ub_send_down(3) - lb_send_down(3) + 1)*(my_id + 1))/num_threads - 1
1070 :
1071 : send_buf_3d_down(lb_send_down(1):ub_send_down(1), lb_send_down(2):ub_send_down(2), &
1072 : lb:ub) = rs%r(lb_send_down(1):ub_send_down(1), &
1073 : lb_send_down(2):ub_send_down(2), lb:ub)
1074 : END IF
1075 : !$OMP END PARALLEL
1076 :
1077 1852 : CALL rs%desc%group%isend(send_buf_3d_down, dest_down, req(3))
1078 :
1079 : ! Now for the other direction
1080 1852 : CALL cart_shift(rs, idir, n_shifts, source_up, dest_up)
1081 :
1082 7408 : lb_send_up(:) = rs%lb_local(:)
1083 7408 : lb_recv_up(:) = rs%lb_local(:)
1084 7408 : ub_recv_up(:) = rs%ub_local(:)
1085 7408 : ub_send_up(:) = rs%ub_local(:)
1086 :
1087 1852 : IF (ushifts(n_shifts - 1) <= rs%desc%border) THEN
1088 :
1089 1852 : lb_send_up(idir) = ub_send_up(idir) - rs%desc%border + 1 + ushifts(n_shifts - 1)
1090 : ub_send_up(idir) = MIN(ub_send_up(idir), &
1091 1852 : ub_send_up(idir) - rs%desc%border + ushifts(n_shifts))
1092 :
1093 1852 : lb_recv_up(idir) = lb_recv_up(idir) + rs%desc%border
1094 : ub_recv_up(idir) = MIN(ub_recv_up(idir) - rs%desc%border, &
1095 1852 : lb_recv_up(idir) + rs%desc%border - 1 - dshifts(n_shifts - 1))
1096 : ELSE
1097 0 : lb_send_up(idir) = 0
1098 0 : ub_send_up(idir) = -1
1099 0 : lb_recv_up(idir) = 0
1100 0 : ub_recv_up(idir) = -1
1101 : END IF
1102 :
1103 7408 : DO i = 1, 3
1104 7408 : IF (halo_swapped(i)) THEN
1105 554 : lb_send_up(i) = rs%lb_real(i)
1106 554 : ub_send_up(i) = rs%ub_real(i)
1107 554 : lb_recv_up(i) = rs%lb_real(i)
1108 554 : ub_recv_up(i) = rs%ub_real(i)
1109 : END IF
1110 : END DO
1111 :
1112 : ! post the receive
1113 0 : ALLOCATE (recv_buf_3d_up(lb_recv_up(1):ub_recv_up(1), &
1114 9260 : lb_recv_up(2):ub_recv_up(2), lb_recv_up(3):ub_recv_up(3)))
1115 1852 : CALL rs%desc%group%irecv(recv_buf_3d_up, source_up, req(2))
1116 :
1117 : ! now allocate,pack and send the send buffer
1118 7408 : nn = PRODUCT(ub_send_up - lb_send_up + 1)
1119 0 : ALLOCATE (send_buf_3d_up(lb_send_up(1):ub_send_up(1), &
1120 9260 : lb_send_up(2):ub_send_up(2), lb_send_up(3):ub_send_up(3)))
1121 :
1122 : !$OMP PARALLEL DEFAULT(NONE), &
1123 : !$OMP PRIVATE(lb,ub,my_id,NUM_THREADS), &
1124 1852 : !$OMP SHARED(send_buf_3d_up,rs,lb_send_up,ub_send_up)
1125 : !$ num_threads = MIN(omp_get_max_threads(), ub_send_up(3) - lb_send_up(3) + 1)
1126 : !$ my_id = omp_get_thread_num()
1127 : IF (my_id < num_threads) THEN
1128 : lb = lb_send_up(3) + ((ub_send_up(3) - lb_send_up(3) + 1)*my_id)/num_threads
1129 : ub = lb_send_up(3) + ((ub_send_up(3) - lb_send_up(3) + 1)*(my_id + 1))/num_threads - 1
1130 :
1131 : send_buf_3d_up(lb_send_up(1):ub_send_up(1), lb_send_up(2):ub_send_up(2), &
1132 : lb:ub) = rs%r(lb_send_up(1):ub_send_up(1), &
1133 : lb_send_up(2):ub_send_up(2), lb:ub)
1134 : END IF
1135 : !$OMP END PARALLEL
1136 :
1137 1852 : CALL rs%desc%group%isend(send_buf_3d_up, dest_up, req(4))
1138 :
1139 : ! wait for a recv to complete, then we can unpack
1140 :
1141 5556 : DO i = 1, 2
1142 :
1143 3704 : CALL mp_waitany(req(1:2), completed)
1144 :
1145 5556 : IF (completed == 1) THEN
1146 :
1147 : ! only some procs may need later shifts
1148 1852 : IF (ub_recv_down(idir) >= lb_recv_down(idir)) THEN
1149 : ! Sum the data in the RS Grid
1150 : !$OMP PARALLEL DEFAULT(NONE), &
1151 : !$OMP PRIVATE(lb,ub,my_id,NUM_THREADS), &
1152 1852 : !$OMP SHARED(recv_buf_3d_down,rs,lb_recv_down,ub_recv_down)
1153 : !$ num_threads = MIN(omp_get_max_threads(), ub_recv_down(3) - lb_recv_down(3) + 1)
1154 : !$ my_id = omp_get_thread_num()
1155 : IF (my_id < num_threads) THEN
1156 : lb = lb_recv_down(3) + ((ub_recv_down(3) - lb_recv_down(3) + 1)*my_id)/num_threads
1157 : ub = lb_recv_down(3) + ((ub_recv_down(3) - lb_recv_down(3) + 1)*(my_id + 1))/num_threads - 1
1158 :
1159 : rs%r(lb_recv_down(1):ub_recv_down(1), &
1160 : lb_recv_down(2):ub_recv_down(2), lb:ub) = &
1161 : rs%r(lb_recv_down(1):ub_recv_down(1), &
1162 : lb_recv_down(2):ub_recv_down(2), lb:ub) + &
1163 : recv_buf_3d_down(:, :, lb:ub)
1164 : END IF
1165 : !$OMP END PARALLEL
1166 : END IF
1167 1852 : DEALLOCATE (recv_buf_3d_down)
1168 : ELSE
1169 :
1170 : ! only some procs may need later shifts
1171 1852 : IF (ub_recv_up(idir) >= lb_recv_up(idir)) THEN
1172 : ! Sum the data in the RS Grid
1173 : !$OMP PARALLEL DEFAULT(NONE), &
1174 : !$OMP PRIVATE(lb,ub,my_id,NUM_THREADS), &
1175 1852 : !$OMP SHARED(recv_buf_3d_up,rs,lb_recv_up,ub_recv_up)
1176 : !$ num_threads = MIN(omp_get_max_threads(), ub_recv_up(3) - lb_recv_up(3) + 1)
1177 : !$ my_id = omp_get_thread_num()
1178 : IF (my_id < num_threads) THEN
1179 : lb = lb_recv_up(3) + ((ub_recv_up(3) - lb_recv_up(3) + 1)*my_id)/num_threads
1180 : ub = lb_recv_up(3) + ((ub_recv_up(3) - lb_recv_up(3) + 1)*(my_id + 1))/num_threads - 1
1181 :
1182 : rs%r(lb_recv_up(1):ub_recv_up(1), &
1183 : lb_recv_up(2):ub_recv_up(2), lb:ub) = &
1184 : rs%r(lb_recv_up(1):ub_recv_up(1), &
1185 : lb_recv_up(2):ub_recv_up(2), lb:ub) + &
1186 : recv_buf_3d_up(:, :, lb:ub)
1187 : END IF
1188 : !$OMP END PARALLEL
1189 : END IF
1190 1852 : DEALLOCATE (recv_buf_3d_up)
1191 : END IF
1192 :
1193 : END DO
1194 :
1195 : ! make sure the sends have completed before we deallocate
1196 :
1197 1852 : CALL mp_waitall(req(3:4))
1198 :
1199 1852 : DEALLOCATE (send_buf_3d_down)
1200 8508 : DEALLOCATE (send_buf_3d_up)
1201 : END DO
1202 :
1203 4804 : DEALLOCATE (dshifts)
1204 4804 : DEALLOCATE (ushifts)
1205 :
1206 : END IF
1207 :
1208 7408 : halo_swapped(idir) = .TRUE.
1209 :
1210 : END DO
1211 :
1212 : ! This is the real redistribution
1213 7408 : ALLOCATE (bounds(0:pw%pw_grid%para%group%num_pe - 1, 1:4))
1214 :
1215 : ! work out the pw grid points each proc holds
1216 5556 : DO i = 0, pw%pw_grid%para%group%num_pe - 1
1217 11112 : bounds(i, 1:2) = pw%pw_grid%para%bo(1:2, 1, i, 1)
1218 11112 : bounds(i, 3:4) = pw%pw_grid%para%bo(1:2, 2, i, 1)
1219 11112 : bounds(i, 1:2) = bounds(i, 1:2) - pw%pw_grid%npts(1)/2 - 1
1220 12964 : bounds(i, 3:4) = bounds(i, 3:4) - pw%pw_grid%npts(2)/2 - 1
1221 : END DO
1222 :
1223 7408 : ALLOCATE (send_tasks(0:pw%pw_grid%para%group%num_pe - 1, 1:6))
1224 5556 : ALLOCATE (send_sizes(0:pw%pw_grid%para%group%num_pe - 1))
1225 3704 : ALLOCATE (send_disps(0:pw%pw_grid%para%group%num_pe - 1))
1226 3704 : ALLOCATE (recv_tasks(0:pw%pw_grid%para%group%num_pe - 1, 1:6))
1227 3704 : ALLOCATE (recv_sizes(0:pw%pw_grid%para%group%num_pe - 1))
1228 3704 : ALLOCATE (recv_disps(0:pw%pw_grid%para%group%num_pe - 1))
1229 5556 : send_tasks(:, 1) = 1
1230 5556 : send_tasks(:, 2) = 0
1231 5556 : send_tasks(:, 3) = 1
1232 5556 : send_tasks(:, 4) = 0
1233 5556 : send_tasks(:, 5) = 1
1234 5556 : send_tasks(:, 6) = 0
1235 1852 : send_sizes = 0
1236 1852 : recv_sizes = 0
1237 :
1238 1852 : my_rs_rank = rs%desc%my_pos
1239 1852 : my_pw_rank = pw%pw_grid%para%group%mepos
1240 :
1241 : ! find the processors that should hold our data
1242 : ! should be part of the rs grid type
1243 : ! this is a loop over real ranks (i.e. the in-order cartesian ranks)
1244 : ! do the recv and send tasks in two separate loops which will
1245 : ! load balance better for OpenMP with large numbers of MPI tasks
1246 :
1247 : !$OMP PARALLEL DO DEFAULT(NONE), &
1248 : !$OMP PRIVATE(coords,idir,pos,lb_send,ub_send), &
1249 1852 : !$OMP SHARED(rs,bounds,my_rs_rank,recv_tasks,recv_sizes)
1250 : DO i = 0, rs%desc%group_size - 1
1251 :
1252 : coords(:) = rs%desc%rank2coord(:, rs%desc%real2virtual(i))
1253 : !calculate the rs grid points on each processor
1254 : !coords is the part of the grid that rank i actually holds
1255 : DO idir = 1, 3
1256 : pos(:) = get_limit(rs%desc%npts(idir), rs%desc%group_dim(idir), coords(idir))
1257 : pos(:) = pos(:) - rs%desc%npts(idir)/2 - 1
1258 : lb_send(idir) = pos(1)
1259 : ub_send(idir) = pos(2)
1260 : END DO
1261 :
1262 : IF (lb_send(1) > bounds(my_rs_rank, 2)) CYCLE
1263 : IF (ub_send(1) < bounds(my_rs_rank, 1)) CYCLE
1264 : IF (lb_send(2) > bounds(my_rs_rank, 4)) CYCLE
1265 : IF (ub_send(2) < bounds(my_rs_rank, 3)) CYCLE
1266 :
1267 : recv_tasks(i, 1) = MAX(lb_send(1), bounds(my_rs_rank, 1))
1268 : recv_tasks(i, 2) = MIN(ub_send(1), bounds(my_rs_rank, 2))
1269 : recv_tasks(i, 3) = MAX(lb_send(2), bounds(my_rs_rank, 3))
1270 : recv_tasks(i, 4) = MIN(ub_send(2), bounds(my_rs_rank, 4))
1271 : recv_tasks(i, 5) = lb_send(3)
1272 : recv_tasks(i, 6) = ub_send(3)
1273 : recv_sizes(i) = (recv_tasks(i, 2) - recv_tasks(i, 1) + 1)* &
1274 : (recv_tasks(i, 4) - recv_tasks(i, 3) + 1)*(recv_tasks(i, 6) - recv_tasks(i, 5) + 1)
1275 :
1276 : END DO
1277 : !$OMP END PARALLEL DO
1278 :
1279 7408 : coords(:) = rs%desc%rank2coord(:, rs%desc%real2virtual(my_rs_rank))
1280 7408 : DO idir = 1, 3
1281 5556 : pos(:) = get_limit(rs%desc%npts(idir), rs%desc%group_dim(idir), coords(idir))
1282 16668 : pos(:) = pos(:) - rs%desc%npts(idir)/2 - 1
1283 5556 : lb_send(idir) = pos(1)
1284 7408 : ub_send(idir) = pos(2)
1285 : END DO
1286 :
1287 1852 : lb_recv(:) = lb_send(:)
1288 1852 : ub_recv(:) = ub_send(:)
1289 : !$OMP PARALLEL DO DEFAULT(NONE), &
1290 1852 : !$OMP SHARED(pw,lb_send,ub_send,bounds,send_tasks,send_sizes)
1291 : DO j = 0, pw%pw_grid%para%group%num_pe - 1
1292 :
1293 : IF (lb_send(1) > bounds(j, 2)) CYCLE
1294 : IF (ub_send(1) < bounds(j, 1)) CYCLE
1295 : IF (lb_send(2) > bounds(j, 4)) CYCLE
1296 : IF (ub_send(2) < bounds(j, 3)) CYCLE
1297 :
1298 : send_tasks(j, 1) = MAX(lb_send(1), bounds(j, 1))
1299 : send_tasks(j, 2) = MIN(ub_send(1), bounds(j, 2))
1300 : send_tasks(j, 3) = MAX(lb_send(2), bounds(j, 3))
1301 : send_tasks(j, 4) = MIN(ub_send(2), bounds(j, 4))
1302 : send_tasks(j, 5) = lb_send(3)
1303 : send_tasks(j, 6) = ub_send(3)
1304 : send_sizes(j) = (send_tasks(j, 2) - send_tasks(j, 1) + 1)* &
1305 : (send_tasks(j, 4) - send_tasks(j, 3) + 1)*(send_tasks(j, 6) - send_tasks(j, 5) + 1)
1306 :
1307 : END DO
1308 : !$OMP END PARALLEL DO
1309 :
1310 1852 : send_disps(0) = 0
1311 1852 : recv_disps(0) = 0
1312 3704 : DO i = 1, pw%pw_grid%para%group%num_pe - 1
1313 1852 : send_disps(i) = send_disps(i - 1) + send_sizes(i - 1)
1314 3704 : recv_disps(i) = recv_disps(i - 1) + recv_sizes(i - 1)
1315 : END DO
1316 :
1317 12964 : CPASSERT(SUM(send_sizes) == PRODUCT(ub_recv - lb_recv + 1))
1318 :
1319 9260 : ALLOCATE (send_bufs(0:rs%desc%group_size - 1))
1320 11112 : ALLOCATE (recv_bufs(0:rs%desc%group_size - 1))
1321 :
1322 5556 : DO i = 0, rs%desc%group_size - 1
1323 3704 : IF (send_sizes(i) /= 0) THEN
1324 10374 : ALLOCATE (send_bufs(i)%array(send_sizes(i)))
1325 : ELSE
1326 246 : NULLIFY (send_bufs(i)%array)
1327 : END IF
1328 5556 : IF (recv_sizes(i) /= 0) THEN
1329 10374 : ALLOCATE (recv_bufs(i)%array(recv_sizes(i)))
1330 : ELSE
1331 246 : NULLIFY (recv_bufs(i)%array)
1332 : END IF
1333 : END DO
1334 :
1335 9260 : ALLOCATE (recv_reqs(0:rs%desc%group_size - 1))
1336 5556 : recv_reqs = mp_request_null
1337 :
1338 5556 : DO i = 0, rs%desc%group_size - 1
1339 5556 : IF (recv_sizes(i) /= 0) THEN
1340 3458 : CALL rs%desc%group%irecv(recv_bufs(i)%array, i, recv_reqs(i))
1341 : END IF
1342 : END DO
1343 :
1344 : ! do packing
1345 : !$OMP PARALLEL DO DEFAULT(NONE), &
1346 : !$OMP PRIVATE(k,z,y,x), &
1347 1852 : !$OMP SHARED(rs,send_tasks,send_bufs,send_disps)
1348 : DO i = 0, rs%desc%group_size - 1
1349 : k = 0
1350 : DO z = send_tasks(i, 5), send_tasks(i, 6)
1351 : DO y = send_tasks(i, 3), send_tasks(i, 4)
1352 : DO x = send_tasks(i, 1), send_tasks(i, 2)
1353 : k = k + 1
1354 : send_bufs(i)%array(k) = rs%r(x, y, z)
1355 : END DO
1356 : END DO
1357 : END DO
1358 : END DO
1359 : !$OMP END PARALLEL DO
1360 :
1361 9260 : ALLOCATE (send_reqs(0:rs%desc%group_size - 1))
1362 5556 : send_reqs = mp_request_null
1363 :
1364 5556 : DO i = 0, rs%desc%group_size - 1
1365 5556 : IF (send_sizes(i) /= 0) THEN
1366 3458 : CALL rs%desc%group%isend(send_bufs(i)%array, i, send_reqs(i))
1367 : END IF
1368 : END DO
1369 :
1370 : ! do unpacking
1371 : ! no OMP here so we can unpack each message as it arrives
1372 5556 : DO i = 0, rs%desc%group_size - 1
1373 3704 : IF (recv_sizes(i) == 0) CYCLE
1374 :
1375 3458 : CALL mp_waitany(recv_reqs, completed)
1376 3458 : k = 0
1377 143780 : DO z = recv_tasks(completed - 1, 5), recv_tasks(completed - 1, 6)
1378 10754290 : DO y = recv_tasks(completed - 1, 3), recv_tasks(completed - 1, 4)
1379 452245022 : DO x = recv_tasks(completed - 1, 1), recv_tasks(completed - 1, 2)
1380 441494436 : k = k + 1
1381 452106552 : pw%array(x, y, z) = recv_bufs(completed - 1)%array(k)
1382 : END DO
1383 : END DO
1384 : END DO
1385 : END DO
1386 :
1387 1852 : CALL mp_waitall(send_reqs)
1388 :
1389 1852 : DEALLOCATE (recv_reqs)
1390 1852 : DEALLOCATE (send_reqs)
1391 :
1392 5556 : DO i = 0, rs%desc%group_size - 1
1393 3704 : IF (ASSOCIATED(send_bufs(i)%array)) THEN
1394 3458 : DEALLOCATE (send_bufs(i)%array)
1395 : END IF
1396 5556 : IF (ASSOCIATED(recv_bufs(i)%array)) THEN
1397 3458 : DEALLOCATE (recv_bufs(i)%array)
1398 : END IF
1399 : END DO
1400 :
1401 1852 : DEALLOCATE (send_bufs)
1402 1852 : DEALLOCATE (recv_bufs)
1403 1852 : DEALLOCATE (send_tasks)
1404 1852 : DEALLOCATE (send_sizes)
1405 1852 : DEALLOCATE (send_disps)
1406 1852 : DEALLOCATE (recv_tasks)
1407 1852 : DEALLOCATE (recv_sizes)
1408 1852 : DEALLOCATE (recv_disps)
1409 :
1410 : IF (debug_this_module) THEN
1411 : ! safety check, to be removed once we're absolute sure the routine is correct
1412 : pw_sum = pw_integrate_function(pw)
1413 : IF (ABS(pw_sum - rs_sum)/MAX(1.0_dp, ABS(pw_sum), ABS(rs_sum)) > EPSILON(rs_sum)*1000) THEN
1414 : WRITE (error_string, '(A,6(1X,I4.4),3F25.16)') "rs_pw_transfer_distributed", &
1415 : rs%desc%npts, rs%desc%group_dim, pw_sum, rs_sum, ABS(pw_sum - rs_sum)
1416 : CALL cp_abort(__LOCATION__, &
1417 : error_string//" Please report this bug ... quick workaround: use "// &
1418 : "DISTRIBUTION_TYPE REPLICATED")
1419 : END IF
1420 : END IF
1421 :
1422 1852 : END SUBROUTINE transfer_rs2pw_distributed
1423 :
1424 : ! **************************************************************************************************
1425 : !> \brief does the pw2rs transfer in the case where the rs grid is
1426 : !> distributed (3D domain decomposition)
1427 : !> \param rs ...
1428 : !> \param pw ...
1429 : !> \par History
1430 : !> 12.2007 created [Matt Watkins]
1431 : !> 9.2008 reduced amount of halo data sent [Iain Bethune]
1432 : !> 10.2008 added non-blocking communication [Iain Bethune]
1433 : !> 4.2009 added support for rank-reordering on the grid [Iain Bethune]
1434 : !> 12.2009 added OMP and sparse alltoall [Iain Bethune]
1435 : !> (c) The Numerical Algorithms Group (NAG) Ltd, 2008-2009 on behalf of the HECToR project
1436 : !> \note
1437 : !> the transfer is a two step procedure. For example, for the rs2pw transfer:
1438 : !>
1439 : !> 1) Halo-exchange in 3D so that the local part of the rs_grid contains the full data
1440 : !> 2) an alltoall communication to redistribute the local rs_grid to the local pw_grid
1441 : !>
1442 : !> the halo exchange is most expensive on a large number of CPUs. Particular in this halo
1443 : !> exchange is that the border region is rather large (e.g. 20 points) and that it might overlap
1444 : !> with the central domain of several CPUs (i.e. next nearest neighbors)
1445 : ! **************************************************************************************************
1446 872 : SUBROUTINE transfer_pw2rs_distributed(rs, pw)
1447 : TYPE(realspace_grid_type), INTENT(IN) :: rs
1448 : TYPE(pw_r3d_rs_type), INTENT(IN) :: pw
1449 :
1450 : INTEGER :: completed, dest_down, dest_up, i, idir, j, k, lb, my_id, my_pw_rank, my_rs_rank, &
1451 : n_shifts, nn, num_threads, position, source_down, source_up, ub, x, y, z
1452 872 : INTEGER, ALLOCATABLE, DIMENSION(:) :: dshifts, recv_disps, recv_sizes, &
1453 872 : send_disps, send_sizes, ushifts
1454 1744 : INTEGER, ALLOCATABLE, DIMENSION(:, :) :: bounds, recv_tasks, send_tasks
1455 : INTEGER, DIMENSION(2) :: neighbours, pos
1456 : INTEGER, DIMENSION(3) :: coords, lb_recv, lb_recv_down, lb_recv_up, lb_send, lb_send_down, &
1457 : lb_send_up, ub_recv, ub_recv_down, ub_recv_up, ub_send, ub_send_down, ub_send_up
1458 : LOGICAL, DIMENSION(3) :: halo_swapped
1459 872 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: recv_buf_3d_down, recv_buf_3d_up, &
1460 872 : send_buf_3d_down, send_buf_3d_up
1461 1744 : TYPE(cp_1d_r_p_type), ALLOCATABLE, DIMENSION(:) :: recv_bufs, send_bufs
1462 872 : TYPE(mp_request_type), ALLOCATABLE, DIMENSION(:) :: recv_reqs, send_reqs
1463 4360 : TYPE(mp_request_type), DIMENSION(4) :: req
1464 :
1465 872 : num_threads = 1
1466 872 : my_id = 0
1467 :
1468 872 : CALL rs_grid_zero(rs)
1469 :
1470 : ! This is the real redistribution
1471 :
1472 3488 : ALLOCATE (bounds(0:pw%pw_grid%para%group%num_pe - 1, 1:4))
1473 :
1474 2616 : DO i = 0, pw%pw_grid%para%group%num_pe - 1
1475 5232 : bounds(i, 1:2) = pw%pw_grid%para%bo(1:2, 1, i, 1)
1476 5232 : bounds(i, 3:4) = pw%pw_grid%para%bo(1:2, 2, i, 1)
1477 5232 : bounds(i, 1:2) = bounds(i, 1:2) - pw%pw_grid%npts(1)/2 - 1
1478 6104 : bounds(i, 3:4) = bounds(i, 3:4) - pw%pw_grid%npts(2)/2 - 1
1479 : END DO
1480 :
1481 3488 : ALLOCATE (send_tasks(0:pw%pw_grid%para%group%num_pe - 1, 1:6))
1482 2616 : ALLOCATE (send_sizes(0:pw%pw_grid%para%group%num_pe - 1))
1483 1744 : ALLOCATE (send_disps(0:pw%pw_grid%para%group%num_pe - 1))
1484 1744 : ALLOCATE (recv_tasks(0:pw%pw_grid%para%group%num_pe - 1, 1:6))
1485 1744 : ALLOCATE (recv_sizes(0:pw%pw_grid%para%group%num_pe - 1))
1486 1744 : ALLOCATE (recv_disps(0:pw%pw_grid%para%group%num_pe - 1))
1487 :
1488 872 : send_tasks = 0
1489 2616 : send_tasks(:, 1) = 1
1490 2616 : send_tasks(:, 2) = 0
1491 2616 : send_tasks(:, 3) = 1
1492 2616 : send_tasks(:, 4) = 0
1493 2616 : send_tasks(:, 5) = 1
1494 2616 : send_tasks(:, 6) = 0
1495 872 : send_sizes = 0
1496 :
1497 872 : recv_tasks = 0
1498 2616 : recv_tasks(:, 1) = 1
1499 2616 : recv_tasks(:, 2) = 0
1500 2616 : send_tasks(:, 3) = 1
1501 2616 : send_tasks(:, 4) = 0
1502 2616 : send_tasks(:, 5) = 1
1503 2616 : send_tasks(:, 6) = 0
1504 872 : recv_sizes = 0
1505 :
1506 872 : my_rs_rank = rs%desc%my_pos
1507 872 : my_pw_rank = pw%pw_grid%para%group%mepos
1508 :
1509 : ! find the processors that should hold our data
1510 : ! should be part of the rs grid type
1511 : ! this is a loop over real ranks (i.e. the in-order cartesian ranks)
1512 : ! do the recv and send tasks in two separate loops which will
1513 : ! load balance better for OpenMP with large numbers of MPI tasks
1514 :
1515 : ! this is the reverse of rs2pw: what were the sends are now the recvs
1516 :
1517 : !$OMP PARALLEL DO DEFAULT(NONE), &
1518 : !$OMP PRIVATE(coords,idir,pos,lb_send,ub_send), &
1519 872 : !$OMP SHARED(rs,bounds,my_rs_rank,send_tasks,send_sizes,pw)
1520 : DO i = 0, pw%pw_grid%para%group%num_pe - 1
1521 :
1522 : coords(:) = rs%desc%rank2coord(:, rs%desc%real2virtual(i))
1523 : !calculate the real rs grid points on each processor
1524 : !coords is the part of the grid that rank i actually holds
1525 : DO idir = 1, 3
1526 : pos(:) = get_limit(rs%desc%npts(idir), rs%desc%group_dim(idir), coords(idir))
1527 : pos(:) = pos(:) - rs%desc%npts(idir)/2 - 1
1528 : lb_send(idir) = pos(1)
1529 : ub_send(idir) = pos(2)
1530 : END DO
1531 :
1532 : IF (ub_send(1) < bounds(my_rs_rank, 1)) CYCLE
1533 : IF (lb_send(1) > bounds(my_rs_rank, 2)) CYCLE
1534 : IF (ub_send(2) < bounds(my_rs_rank, 3)) CYCLE
1535 : IF (lb_send(2) > bounds(my_rs_rank, 4)) CYCLE
1536 :
1537 : send_tasks(i, 1) = MAX(lb_send(1), bounds(my_rs_rank, 1))
1538 : send_tasks(i, 2) = MIN(ub_send(1), bounds(my_rs_rank, 2))
1539 : send_tasks(i, 3) = MAX(lb_send(2), bounds(my_rs_rank, 3))
1540 : send_tasks(i, 4) = MIN(ub_send(2), bounds(my_rs_rank, 4))
1541 : send_tasks(i, 5) = lb_send(3)
1542 : send_tasks(i, 6) = ub_send(3)
1543 : send_sizes(i) = (send_tasks(i, 2) - send_tasks(i, 1) + 1)* &
1544 : (send_tasks(i, 4) - send_tasks(i, 3) + 1)*(send_tasks(i, 6) - send_tasks(i, 5) + 1)
1545 :
1546 : END DO
1547 : !$OMP END PARALLEL DO
1548 :
1549 3488 : coords(:) = rs%desc%rank2coord(:, rs%desc%real2virtual(my_rs_rank))
1550 3488 : DO idir = 1, 3
1551 2616 : pos(:) = get_limit(rs%desc%npts(idir), rs%desc%group_dim(idir), coords(idir))
1552 7848 : pos(:) = pos(:) - rs%desc%npts(idir)/2 - 1
1553 2616 : lb_send(idir) = pos(1)
1554 3488 : ub_send(idir) = pos(2)
1555 : END DO
1556 :
1557 872 : lb_recv(:) = lb_send(:)
1558 872 : ub_recv(:) = ub_send(:)
1559 :
1560 : !$OMP PARALLEL DO DEFAULT(NONE), &
1561 872 : !$OMP SHARED(pw,lb_send,ub_send,bounds,recv_tasks,recv_sizes)
1562 : DO j = 0, pw%pw_grid%para%group%num_pe - 1
1563 :
1564 : IF (ub_send(1) < bounds(j, 1)) CYCLE
1565 : IF (lb_send(1) > bounds(j, 2)) CYCLE
1566 : IF (ub_send(2) < bounds(j, 3)) CYCLE
1567 : IF (lb_send(2) > bounds(j, 4)) CYCLE
1568 :
1569 : recv_tasks(j, 1) = MAX(lb_send(1), bounds(j, 1))
1570 : recv_tasks(j, 2) = MIN(ub_send(1), bounds(j, 2))
1571 : recv_tasks(j, 3) = MAX(lb_send(2), bounds(j, 3))
1572 : recv_tasks(j, 4) = MIN(ub_send(2), bounds(j, 4))
1573 : recv_tasks(j, 5) = lb_send(3)
1574 : recv_tasks(j, 6) = ub_send(3)
1575 : recv_sizes(j) = (recv_tasks(j, 2) - recv_tasks(j, 1) + 1)* &
1576 : (recv_tasks(j, 4) - recv_tasks(j, 3) + 1)*(recv_tasks(j, 6) - recv_tasks(j, 5) + 1)
1577 :
1578 : END DO
1579 : !$OMP END PARALLEL DO
1580 :
1581 872 : send_disps(0) = 0
1582 872 : recv_disps(0) = 0
1583 1744 : DO i = 1, pw%pw_grid%para%group%num_pe - 1
1584 872 : send_disps(i) = send_disps(i - 1) + send_sizes(i - 1)
1585 1744 : recv_disps(i) = recv_disps(i - 1) + recv_sizes(i - 1)
1586 : END DO
1587 :
1588 6104 : CPASSERT(SUM(recv_sizes) == PRODUCT(ub_recv - lb_recv + 1))
1589 :
1590 4360 : ALLOCATE (send_bufs(0:rs%desc%group_size - 1))
1591 5232 : ALLOCATE (recv_bufs(0:rs%desc%group_size - 1))
1592 :
1593 2616 : DO i = 0, rs%desc%group_size - 1
1594 1744 : IF (send_sizes(i) /= 0) THEN
1595 4986 : ALLOCATE (send_bufs(i)%array(send_sizes(i)))
1596 : ELSE
1597 82 : NULLIFY (send_bufs(i)%array)
1598 : END IF
1599 2616 : IF (recv_sizes(i) /= 0) THEN
1600 4986 : ALLOCATE (recv_bufs(i)%array(recv_sizes(i)))
1601 : ELSE
1602 82 : NULLIFY (recv_bufs(i)%array)
1603 : END IF
1604 : END DO
1605 :
1606 4360 : ALLOCATE (recv_reqs(0:rs%desc%group_size - 1))
1607 2616 : recv_reqs = mp_request_null
1608 :
1609 2616 : DO i = 0, rs%desc%group_size - 1
1610 2616 : IF (recv_sizes(i) /= 0) THEN
1611 1662 : CALL rs%desc%group%irecv(recv_bufs(i)%array, i, recv_reqs(i))
1612 : END IF
1613 : END DO
1614 :
1615 : ! do packing
1616 : !$OMP PARALLEL DO DEFAULT(NONE), &
1617 : !$OMP PRIVATE(k,z,y,x), &
1618 872 : !$OMP SHARED(pw,rs,send_tasks,send_bufs,send_disps)
1619 : DO i = 0, rs%desc%group_size - 1
1620 : k = 0
1621 : DO z = send_tasks(i, 5), send_tasks(i, 6)
1622 : DO y = send_tasks(i, 3), send_tasks(i, 4)
1623 : DO x = send_tasks(i, 1), send_tasks(i, 2)
1624 : k = k + 1
1625 : send_bufs(i)%array(k) = pw%array(x, y, z)
1626 : END DO
1627 : END DO
1628 : END DO
1629 : END DO
1630 : !$OMP END PARALLEL DO
1631 :
1632 4360 : ALLOCATE (send_reqs(0:rs%desc%group_size - 1))
1633 2616 : send_reqs = mp_request_null
1634 :
1635 2616 : DO i = 0, rs%desc%group_size - 1
1636 2616 : IF (send_sizes(i) /= 0) THEN
1637 1662 : CALL rs%desc%group%isend(send_bufs(i)%array, i, send_reqs(i))
1638 : END IF
1639 : END DO
1640 :
1641 : ! do unpacking
1642 : ! no OMP here so we can unpack each message as it arrives
1643 :
1644 2616 : DO i = 0, rs%desc%group_size - 1
1645 1744 : IF (recv_sizes(i) == 0) CYCLE
1646 :
1647 1662 : CALL mp_waitany(recv_reqs, completed)
1648 1662 : k = 0
1649 66556 : DO z = recv_tasks(completed - 1, 5), recv_tasks(completed - 1, 6)
1650 4763300 : DO y = recv_tasks(completed - 1, 3), recv_tasks(completed - 1, 4)
1651 194725231 : DO x = recv_tasks(completed - 1, 1), recv_tasks(completed - 1, 2)
1652 189963675 : k = k + 1
1653 194661209 : rs%r(x, y, z) = recv_bufs(completed - 1)%array(k)
1654 : END DO
1655 : END DO
1656 : END DO
1657 : END DO
1658 :
1659 872 : CALL mp_waitall(send_reqs)
1660 :
1661 872 : DEALLOCATE (recv_reqs)
1662 872 : DEALLOCATE (send_reqs)
1663 :
1664 2616 : DO i = 0, rs%desc%group_size - 1
1665 1744 : IF (ASSOCIATED(send_bufs(i)%array)) THEN
1666 1662 : DEALLOCATE (send_bufs(i)%array)
1667 : END IF
1668 2616 : IF (ASSOCIATED(recv_bufs(i)%array)) THEN
1669 1662 : DEALLOCATE (recv_bufs(i)%array)
1670 : END IF
1671 : END DO
1672 :
1673 872 : DEALLOCATE (send_bufs)
1674 872 : DEALLOCATE (recv_bufs)
1675 872 : DEALLOCATE (send_tasks)
1676 872 : DEALLOCATE (send_sizes)
1677 872 : DEALLOCATE (send_disps)
1678 872 : DEALLOCATE (recv_tasks)
1679 872 : DEALLOCATE (recv_sizes)
1680 872 : DEALLOCATE (recv_disps)
1681 :
1682 : ! now pass wings around
1683 872 : halo_swapped = .FALSE.
1684 :
1685 3488 : DO idir = 1, 3
1686 :
1687 2616 : IF (rs%desc%perd(idir) /= 1) THEN
1688 :
1689 5568 : ALLOCATE (dshifts(0:rs%desc%neighbours(idir)))
1690 3712 : ALLOCATE (ushifts(0:rs%desc%neighbours(idir)))
1691 1856 : ushifts = 0
1692 1856 : dshifts = 0
1693 :
1694 3712 : DO n_shifts = 1, rs%desc%neighbours(idir)
1695 :
1696 : ! need to take into account the possible varying widths of neighbouring cells
1697 : ! ushifts and dshifts hold the real size of the neighbouring cells
1698 :
1699 1856 : position = MODULO(rs%desc%virtual_group_coor(idir) - n_shifts, rs%desc%group_dim(idir))
1700 1856 : neighbours = get_limit(rs%desc%npts(idir), rs%desc%group_dim(idir), position)
1701 1856 : dshifts(n_shifts) = dshifts(n_shifts - 1) + (neighbours(2) - neighbours(1) + 1)
1702 :
1703 1856 : position = MODULO(rs%desc%virtual_group_coor(idir) + n_shifts, rs%desc%group_dim(idir))
1704 1856 : neighbours = get_limit(rs%desc%npts(idir), rs%desc%group_dim(idir), position)
1705 1856 : ushifts(n_shifts) = ushifts(n_shifts - 1) + (neighbours(2) - neighbours(1) + 1)
1706 :
1707 : ! The border data has to be send/received from the neighbors
1708 : ! First we calculate the source and destination processes for the shift
1709 : ! The first shift is "downwards"
1710 :
1711 1856 : CALL cart_shift(rs, idir, -1*n_shifts, source_down, dest_down)
1712 :
1713 7424 : lb_send_down(:) = rs%lb_local(:)
1714 7424 : ub_send_down(:) = rs%ub_local(:)
1715 7424 : lb_recv_down(:) = rs%lb_local(:)
1716 7424 : ub_recv_down(:) = rs%ub_local(:)
1717 :
1718 1856 : IF (dshifts(n_shifts - 1) <= rs%desc%border) THEN
1719 1856 : lb_send_down(idir) = lb_send_down(idir) + rs%desc%border
1720 : ub_send_down(idir) = MIN(ub_send_down(idir) - rs%desc%border, &
1721 1856 : lb_send_down(idir) + rs%desc%border - 1 - dshifts(n_shifts - 1))
1722 :
1723 1856 : lb_recv_down(idir) = ub_recv_down(idir) - rs%desc%border + 1 + ushifts(n_shifts - 1)
1724 : ub_recv_down(idir) = MIN(ub_recv_down(idir), &
1725 1856 : ub_recv_down(idir) - rs%desc%border + ushifts(n_shifts))
1726 : ELSE
1727 0 : lb_send_down(idir) = 0
1728 0 : ub_send_down(idir) = -1
1729 0 : lb_recv_down(idir) = 0
1730 0 : ub_recv_down(idir) = -1
1731 : END IF
1732 :
1733 7424 : DO i = 1, 3
1734 7424 : IF (.NOT. (halo_swapped(i) .OR. i == idir)) THEN
1735 1560 : lb_send_down(i) = rs%lb_real(i)
1736 1560 : ub_send_down(i) = rs%ub_real(i)
1737 1560 : lb_recv_down(i) = rs%lb_real(i)
1738 1560 : ub_recv_down(i) = rs%ub_real(i)
1739 : END IF
1740 : END DO
1741 :
1742 : ! allocate the recv buffer
1743 7424 : nn = PRODUCT(ub_recv_down - lb_recv_down + 1)
1744 0 : ALLOCATE (recv_buf_3d_down(lb_recv_down(1):ub_recv_down(1), &
1745 9280 : lb_recv_down(2):ub_recv_down(2), lb_recv_down(3):ub_recv_down(3)))
1746 :
1747 : ! recv buffer is now ready, so post the receive
1748 1856 : CALL rs%desc%group%irecv(recv_buf_3d_down, source_down, req(1))
1749 :
1750 : ! now allocate,pack and send the send buffer
1751 7424 : nn = PRODUCT(ub_send_down - lb_send_down + 1)
1752 0 : ALLOCATE (send_buf_3d_down(lb_send_down(1):ub_send_down(1), &
1753 9280 : lb_send_down(2):ub_send_down(2), lb_send_down(3):ub_send_down(3)))
1754 :
1755 : !$OMP PARALLEL DEFAULT(NONE), &
1756 : !$OMP PRIVATE(lb,ub,my_id,NUM_THREADS), &
1757 1856 : !$OMP SHARED(send_buf_3d_down,rs,lb_send_down,ub_send_down)
1758 : !$ num_threads = MIN(omp_get_max_threads(), ub_send_down(3) - lb_send_down(3) + 1)
1759 : !$ my_id = omp_get_thread_num()
1760 : IF (my_id < num_threads) THEN
1761 : lb = lb_send_down(3) + ((ub_send_down(3) - lb_send_down(3) + 1)*my_id)/num_threads
1762 : ub = lb_send_down(3) + ((ub_send_down(3) - lb_send_down(3) + 1)*(my_id + 1))/num_threads - 1
1763 :
1764 : send_buf_3d_down(lb_send_down(1):ub_send_down(1), lb_send_down(2):ub_send_down(2), &
1765 : lb:ub) = rs%r(lb_send_down(1):ub_send_down(1), &
1766 : lb_send_down(2):ub_send_down(2), lb:ub)
1767 : END IF
1768 : !$OMP END PARALLEL
1769 :
1770 1856 : CALL rs%desc%group%isend(send_buf_3d_down, dest_down, req(3))
1771 :
1772 : ! Now for the other direction
1773 :
1774 1856 : CALL cart_shift(rs, idir, n_shifts, source_up, dest_up)
1775 :
1776 7424 : lb_send_up(:) = rs%lb_local(:)
1777 7424 : ub_send_up(:) = rs%ub_local(:)
1778 7424 : lb_recv_up(:) = rs%lb_local(:)
1779 7424 : ub_recv_up(:) = rs%ub_local(:)
1780 :
1781 1856 : IF (ushifts(n_shifts - 1) <= rs%desc%border) THEN
1782 1856 : ub_send_up(idir) = ub_send_up(idir) - rs%desc%border
1783 : lb_send_up(idir) = MAX(lb_send_up(idir) + rs%desc%border, &
1784 1856 : ub_send_up(idir) - rs%desc%border + 1 + ushifts(n_shifts - 1))
1785 :
1786 1856 : ub_recv_up(idir) = lb_recv_up(idir) + rs%desc%border - 1 - dshifts(n_shifts - 1)
1787 : lb_recv_up(idir) = MAX(lb_recv_up(idir), &
1788 1856 : lb_recv_up(idir) + rs%desc%border - dshifts(n_shifts))
1789 : ELSE
1790 0 : lb_send_up(idir) = 0
1791 0 : ub_send_up(idir) = -1
1792 0 : lb_recv_up(idir) = 0
1793 0 : ub_recv_up(idir) = -1
1794 : END IF
1795 :
1796 7424 : DO i = 1, 3
1797 7424 : IF (.NOT. (halo_swapped(i) .OR. i == idir)) THEN
1798 1560 : lb_send_up(i) = rs%lb_real(i)
1799 1560 : ub_send_up(i) = rs%ub_real(i)
1800 1560 : lb_recv_up(i) = rs%lb_real(i)
1801 1560 : ub_recv_up(i) = rs%ub_real(i)
1802 : END IF
1803 : END DO
1804 :
1805 : ! allocate the recv buffer
1806 7424 : nn = PRODUCT(ub_recv_up - lb_recv_up + 1)
1807 0 : ALLOCATE (recv_buf_3d_up(lb_recv_up(1):ub_recv_up(1), &
1808 9280 : lb_recv_up(2):ub_recv_up(2), lb_recv_up(3):ub_recv_up(3)))
1809 :
1810 : ! recv buffer is now ready, so post the receive
1811 :
1812 1856 : CALL rs%desc%group%irecv(recv_buf_3d_up, source_up, req(2))
1813 :
1814 : ! now allocate,pack and send the send buffer
1815 7424 : nn = PRODUCT(ub_send_up - lb_send_up + 1)
1816 0 : ALLOCATE (send_buf_3d_up(lb_send_up(1):ub_send_up(1), &
1817 9280 : lb_send_up(2):ub_send_up(2), lb_send_up(3):ub_send_up(3)))
1818 :
1819 : !$OMP PARALLEL DEFAULT(NONE), &
1820 : !$OMP PRIVATE(lb,ub,my_id,NUM_THREADS), &
1821 1856 : !$OMP SHARED(send_buf_3d_up,rs,lb_send_up,ub_send_up)
1822 : !$ num_threads = MIN(omp_get_max_threads(), ub_send_up(3) - lb_send_up(3) + 1)
1823 : !$ my_id = omp_get_thread_num()
1824 : IF (my_id < num_threads) THEN
1825 : lb = lb_send_up(3) + ((ub_send_up(3) - lb_send_up(3) + 1)*my_id)/num_threads
1826 : ub = lb_send_up(3) + ((ub_send_up(3) - lb_send_up(3) + 1)*(my_id + 1))/num_threads - 1
1827 :
1828 : send_buf_3d_up(lb_send_up(1):ub_send_up(1), lb_send_up(2):ub_send_up(2), &
1829 : lb:ub) = rs%r(lb_send_up(1):ub_send_up(1), &
1830 : lb_send_up(2):ub_send_up(2), lb:ub)
1831 : END IF
1832 : !$OMP END PARALLEL
1833 :
1834 1856 : CALL rs%desc%group%isend(send_buf_3d_up, dest_up, req(4))
1835 :
1836 : ! wait for a recv to complete, then we can unpack
1837 :
1838 5568 : DO i = 1, 2
1839 :
1840 3712 : CALL mp_waitany(req(1:2), completed)
1841 :
1842 5568 : IF (completed == 1) THEN
1843 :
1844 : ! only some procs may need later shifts
1845 1856 : IF (ub_recv_down(idir) >= lb_recv_down(idir)) THEN
1846 :
1847 : ! Add the data to the RS Grid
1848 : !$OMP PARALLEL DEFAULT(NONE), &
1849 : !$OMP PRIVATE(lb,ub,my_id,NUM_THREADS), &
1850 1856 : !$OMP SHARED(recv_buf_3d_down,rs,lb_recv_down,ub_recv_down)
1851 : !$ num_threads = MIN(omp_get_max_threads(), ub_recv_down(3) - lb_recv_down(3) + 1)
1852 : !$ my_id = omp_get_thread_num()
1853 : IF (my_id < num_threads) THEN
1854 : lb = lb_recv_down(3) + ((ub_recv_down(3) - lb_recv_down(3) + 1)*my_id)/num_threads
1855 : ub = lb_recv_down(3) + ((ub_recv_down(3) - lb_recv_down(3) + 1)*(my_id + 1))/num_threads - 1
1856 :
1857 : rs%r(lb_recv_down(1):ub_recv_down(1), lb_recv_down(2):ub_recv_down(2), &
1858 : lb:ub) = recv_buf_3d_down(:, :, lb:ub)
1859 : END IF
1860 : !$OMP END PARALLEL
1861 : END IF
1862 :
1863 1856 : DEALLOCATE (recv_buf_3d_down)
1864 : ELSE
1865 :
1866 : ! only some procs may need later shifts
1867 1856 : IF (ub_recv_up(idir) >= lb_recv_up(idir)) THEN
1868 :
1869 : ! Add the data to the RS Grid
1870 : !$OMP PARALLEL DEFAULT(NONE), &
1871 : !$OMP PRIVATE(lb,ub,my_id,NUM_THREADS), &
1872 1856 : !$OMP SHARED(recv_buf_3d_up,rs,lb_recv_up,ub_recv_up)
1873 : !$ num_threads = MIN(omp_get_max_threads(), ub_recv_up(3) - lb_recv_up(3) + 1)
1874 : !$ my_id = omp_get_thread_num()
1875 : IF (my_id < num_threads) THEN
1876 : lb = lb_recv_up(3) + ((ub_recv_up(3) - lb_recv_up(3) + 1)*my_id)/num_threads
1877 : ub = lb_recv_up(3) + ((ub_recv_up(3) - lb_recv_up(3) + 1)*(my_id + 1))/num_threads - 1
1878 :
1879 : rs%r(lb_recv_up(1):ub_recv_up(1), lb_recv_up(2):ub_recv_up(2), &
1880 : lb:ub) = recv_buf_3d_up(:, :, lb:ub)
1881 : END IF
1882 : !$OMP END PARALLEL
1883 : END IF
1884 :
1885 1856 : DEALLOCATE (recv_buf_3d_up)
1886 : END IF
1887 : END DO
1888 :
1889 1856 : CALL mp_waitall(req(3:4))
1890 :
1891 1856 : DEALLOCATE (send_buf_3d_down)
1892 5568 : DEALLOCATE (send_buf_3d_up)
1893 : END DO
1894 :
1895 1856 : DEALLOCATE (ushifts)
1896 1856 : DEALLOCATE (dshifts)
1897 : END IF
1898 :
1899 3488 : halo_swapped(idir) = .TRUE.
1900 :
1901 : END DO
1902 :
1903 872 : END SUBROUTINE transfer_pw2rs_distributed
1904 :
1905 : ! **************************************************************************************************
1906 : !> \brief Initialize grid to zero
1907 : !> \param rs ...
1908 : !> \par History
1909 : !> none
1910 : !> \author JGH (23-Mar-2002)
1911 : ! **************************************************************************************************
1912 412122 : SUBROUTINE rs_grid_zero(rs)
1913 :
1914 : TYPE(realspace_grid_type), INTENT(IN) :: rs
1915 :
1916 : CHARACTER(len=*), PARAMETER :: routineN = 'rs_grid_zero'
1917 :
1918 : INTEGER :: handle, i, j, k, l(3), u(3)
1919 :
1920 412122 : CALL timeset(routineN, handle)
1921 1236366 : l(1) = LBOUND(rs%r, 1); l(2) = LBOUND(rs%r, 2); l(3) = LBOUND(rs%r, 3)
1922 1236366 : u(1) = UBOUND(rs%r, 1); u(2) = UBOUND(rs%r, 2); u(3) = UBOUND(rs%r, 3)
1923 : !$OMP PARALLEL DO DEFAULT(NONE) COLLAPSE(3) &
1924 : !$OMP PRIVATE(i,j,k) &
1925 412122 : !$OMP SHARED(rs,l,u)
1926 : DO k = l(3), u(3)
1927 : DO j = l(2), u(2)
1928 : DO i = l(1), u(1)
1929 : rs%r(i, j, k) = 0.0_dp
1930 : END DO
1931 : END DO
1932 : END DO
1933 : !$OMP END PARALLEL DO
1934 412122 : CALL timestop(handle)
1935 :
1936 412122 : END SUBROUTINE rs_grid_zero
1937 :
1938 : ! **************************************************************************************************
1939 : !> \brief rs1(i) = rs1(i) + rs2(i)*rs3(i)
1940 : !> \param rs1 ...
1941 : !> \param rs2 ...
1942 : !> \param rs3 ...
1943 : !> \param scalar ...
1944 : !> \par History
1945 : !> none
1946 : !> \author
1947 : ! **************************************************************************************************
1948 1404 : SUBROUTINE rs_grid_mult_and_add(rs1, rs2, rs3, scalar)
1949 :
1950 : TYPE(realspace_grid_type), INTENT(IN) :: rs1, rs2, rs3
1951 : REAL(dp), INTENT(IN) :: scalar
1952 :
1953 : CHARACTER(len=*), PARAMETER :: routineN = 'rs_grid_mult_and_add'
1954 :
1955 : INTEGER :: handle, i, j, k, l(3), u(3)
1956 :
1957 : !-----------------------------------------------------------------------------!
1958 :
1959 1404 : CALL timeset(routineN, handle)
1960 1404 : IF (scalar /= 0.0_dp) THEN
1961 4212 : l(1) = LBOUND(rs1%r, 1); l(2) = LBOUND(rs1%r, 2); l(3) = LBOUND(rs1%r, 3)
1962 4212 : u(1) = UBOUND(rs1%r, 1); u(2) = UBOUND(rs1%r, 2); u(3) = UBOUND(rs1%r, 3)
1963 : !$OMP PARALLEL DO DEFAULT(NONE) COLLAPSE(3) &
1964 : !$OMP PRIVATE(i,j,k) &
1965 1404 : !$OMP SHARED(rs1,rs2,rs3,scalar,l,u)
1966 : DO k = l(3), u(3)
1967 : DO j = l(2), u(2)
1968 : DO i = l(1), u(1)
1969 : rs1%r(i, j, k) = rs1%r(i, j, k) + scalar*rs2%r(i, j, k)*rs3%r(i, j, k)
1970 : END DO
1971 : END DO
1972 : END DO
1973 : !$OMP END PARALLEL DO
1974 : END IF
1975 1404 : CALL timestop(handle)
1976 1404 : END SUBROUTINE rs_grid_mult_and_add
1977 :
1978 : ! **************************************************************************************************
1979 : !> \brief Set box matrix info for real space grid
1980 : !> This is needed for variable cell simulations
1981 : !> \param pw_grid ...
1982 : !> \param rs ...
1983 : !> \par History
1984 : !> none
1985 : !> \author JGH (15-May-2007)
1986 : ! **************************************************************************************************
1987 207980 : SUBROUTINE rs_grid_set_box(pw_grid, rs)
1988 :
1989 : TYPE(pw_grid_type), INTENT(IN), TARGET :: pw_grid
1990 : TYPE(realspace_grid_type), INTENT(IN) :: rs
1991 :
1992 207980 : CPASSERT(ASSOCIATED(rs%desc%pw, pw_grid))
1993 2703740 : rs%desc%dh = pw_grid%dh
1994 2703740 : rs%desc%dh_inv = pw_grid%dh_inv
1995 :
1996 207980 : END SUBROUTINE rs_grid_set_box
1997 :
1998 : ! **************************************************************************************************
1999 : !> \brief retains the given rs grid descriptor (see doc/ReferenceCounting.html)
2000 : !> \param rs_desc the grid descriptor to retain
2001 : !> \par History
2002 : !> 04.2009 created [Iain Bethune]
2003 : !> (c) The Numerical Algorithms Group (NAG) Ltd, 2009 on behalf of the HECToR project
2004 : ! **************************************************************************************************
2005 291514 : SUBROUTINE rs_grid_retain_descriptor(rs_desc)
2006 : TYPE(realspace_grid_desc_type), INTENT(INOUT) :: rs_desc
2007 :
2008 291514 : CPASSERT(rs_desc%ref_count > 0)
2009 291514 : rs_desc%ref_count = rs_desc%ref_count + 1
2010 291514 : END SUBROUTINE rs_grid_retain_descriptor
2011 :
2012 : ! **************************************************************************************************
2013 : !> \brief releases the given rs grid (see doc/ReferenceCounting.html)
2014 : !> \param rs_grid the rs grid to release
2015 : !> \par History
2016 : !> 03.2003 created [fawzi]
2017 : !> \author fawzi
2018 : ! **************************************************************************************************
2019 290630 : SUBROUTINE rs_grid_release(rs_grid)
2020 : TYPE(realspace_grid_type), INTENT(INOUT) :: rs_grid
2021 :
2022 290630 : CALL rs_grid_release_descriptor(rs_grid%desc)
2023 :
2024 290630 : CALL offload_free_buffer(rs_grid%buffer)
2025 290630 : NULLIFY (rs_grid%r)
2026 :
2027 290630 : IF (ALLOCATED(rs_grid%px)) DEALLOCATE (rs_grid%px)
2028 290630 : IF (ALLOCATED(rs_grid%py)) DEALLOCATE (rs_grid%py)
2029 290630 : IF (ALLOCATED(rs_grid%pz)) DEALLOCATE (rs_grid%pz)
2030 290630 : END SUBROUTINE rs_grid_release
2031 :
2032 : ! **************************************************************************************************
2033 : !> \brief releases the given rs grid descriptor (see doc/ReferenceCounting.html)
2034 : !> \param rs_desc the rs grid descriptor to release
2035 : !> \par History
2036 : !> 04.2009 created [Iain Bethune]
2037 : !> (c) The Numerical Algorithms Group (NAG) Ltd, 2009 on behalf of the HECToR project
2038 : ! **************************************************************************************************
2039 336597 : SUBROUTINE rs_grid_release_descriptor(rs_desc)
2040 : TYPE(realspace_grid_desc_type), POINTER :: rs_desc
2041 :
2042 336597 : IF (ASSOCIATED(rs_desc)) THEN
2043 332844 : CPASSERT(rs_desc%ref_count > 0)
2044 332844 : rs_desc%ref_count = rs_desc%ref_count - 1
2045 332844 : IF (rs_desc%ref_count == 0) THEN
2046 :
2047 41330 : CALL pw_grid_release(rs_desc%pw)
2048 :
2049 41330 : IF (rs_desc%parallel) THEN
2050 : ! release the group communicator
2051 37102 : CALL rs_desc%group%free()
2052 :
2053 37102 : DEALLOCATE (rs_desc%virtual2real)
2054 37102 : DEALLOCATE (rs_desc%real2virtual)
2055 : END IF
2056 :
2057 41330 : IF (rs_desc%distributed) THEN
2058 158 : DEALLOCATE (rs_desc%rank2coord)
2059 158 : DEALLOCATE (rs_desc%coord2rank)
2060 158 : DEALLOCATE (rs_desc%lb_global)
2061 158 : DEALLOCATE (rs_desc%ub_global)
2062 158 : DEALLOCATE (rs_desc%x2coord)
2063 158 : DEALLOCATE (rs_desc%y2coord)
2064 158 : DEALLOCATE (rs_desc%z2coord)
2065 : END IF
2066 :
2067 41330 : DEALLOCATE (rs_desc)
2068 : END IF
2069 : END IF
2070 336597 : NULLIFY (rs_desc)
2071 336597 : END SUBROUTINE rs_grid_release_descriptor
2072 :
2073 : ! **************************************************************************************************
2074 : !> \brief emulates the function of an MPI_cart_shift operation, but the shift is
2075 : !> done in virtual coordinates, and the corresponding real ranks are returned
2076 : !> \param rs_grid ...
2077 : !> \param dir ...
2078 : !> \param disp ...
2079 : !> \param source ...
2080 : !> \param dest ...
2081 : !> \par History
2082 : !> 04.2009 created [Iain Bethune]
2083 : !> (c) The Numerical Algorithms Group (NAG) Ltd, 2009 on behalf of the HECToR project
2084 : ! **************************************************************************************************
2085 7416 : PURE SUBROUTINE cart_shift(rs_grid, dir, disp, source, dest)
2086 :
2087 : TYPE(realspace_grid_type), INTENT(IN) :: rs_grid
2088 : INTEGER, INTENT(IN) :: dir, disp
2089 : INTEGER, INTENT(OUT) :: source, dest
2090 :
2091 : INTEGER, DIMENSION(3) :: shift_coords
2092 :
2093 29664 : shift_coords = rs_grid%desc%virtual_group_coor
2094 7416 : shift_coords(dir) = MODULO(shift_coords(dir) + disp, rs_grid%desc%group_dim(dir))
2095 7416 : dest = rs_grid%desc%virtual2real(rs_grid%desc%coord2rank(shift_coords(1), shift_coords(2), shift_coords(3)))
2096 29664 : shift_coords = rs_grid%desc%virtual_group_coor
2097 7416 : shift_coords(dir) = MODULO(shift_coords(dir) - disp, rs_grid%desc%group_dim(dir))
2098 7416 : source = rs_grid%desc%virtual2real(rs_grid%desc%coord2rank(shift_coords(1), shift_coords(2), shift_coords(3)))
2099 :
2100 7416 : END SUBROUTINE cart_shift
2101 :
2102 : ! **************************************************************************************************
2103 : !> \brief returns the maximum number of points in the local grid of any process
2104 : !> to account for the case where the grid may later be reordered
2105 : !> \param desc ...
2106 : !> \return ...
2107 : !> \par History
2108 : !> 10.2011 created [Iain Bethune]
2109 : ! **************************************************************************************************
2110 0 : FUNCTION rs_grid_max_ngpts(desc) RESULT(max_ngpts)
2111 : TYPE(realspace_grid_desc_type), INTENT(IN) :: desc
2112 : INTEGER :: max_ngpts
2113 :
2114 : CHARACTER(len=*), PARAMETER :: routineN = 'rs_grid_max_ngpts'
2115 :
2116 : INTEGER :: handle, i
2117 : INTEGER, DIMENSION(3) :: lb, ub
2118 :
2119 0 : CALL timeset(routineN, handle)
2120 :
2121 0 : max_ngpts = 0
2122 0 : IF ((desc%pw%para%mode == PW_MODE_LOCAL) .OR. &
2123 : (ALL(desc%group_dim == 1))) THEN
2124 0 : CPASSERT(PRODUCT(INT(desc%npts, KIND=int_8)) < HUGE(1))
2125 0 : max_ngpts = PRODUCT(desc%npts)
2126 : ELSE
2127 0 : DO i = 0, desc%group_size - 1
2128 0 : lb = desc%lb_global(:, i)
2129 0 : ub = desc%ub_global(:, i)
2130 0 : lb = lb - desc%border*(1 - desc%perd)
2131 0 : ub = ub + desc%border*(1 - desc%perd)
2132 0 : CPASSERT(PRODUCT(INT(ub - lb + 1, KIND=int_8)) < HUGE(1))
2133 0 : max_ngpts = MAX(max_ngpts, PRODUCT(ub - lb + 1))
2134 : END DO
2135 : END IF
2136 :
2137 0 : CALL timestop(handle)
2138 :
2139 0 : END FUNCTION rs_grid_max_ngpts
2140 :
2141 : ! **************************************************************************************************
2142 : !> \brief ...
2143 : !> \param rs_grid ...
2144 : !> \param h_inv ...
2145 : !> \param ra ...
2146 : !> \param offset ...
2147 : !> \param group_size ...
2148 : !> \param my_pos ...
2149 : !> \return ...
2150 : ! **************************************************************************************************
2151 1611955 : PURE LOGICAL FUNCTION map_gaussian_here(rs_grid, h_inv, ra, offset, group_size, my_pos) RESULT(res)
2152 : TYPE(realspace_grid_type), INTENT(IN) :: rs_grid
2153 : REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN) :: h_inv
2154 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: ra
2155 : INTEGER, INTENT(IN), OPTIONAL :: offset, group_size, my_pos
2156 :
2157 : INTEGER :: dir, lb(3), location(3), tp(3), ub(3)
2158 :
2159 1611955 : res = .FALSE.
2160 :
2161 6447812 : IF (.NOT. ALL(rs_grid%desc%perd == 1)) THEN
2162 32 : DO dir = 1, 3
2163 : ! bounds of local grid (i.e. removing the 'wings'), if periodic
2164 96 : tp(dir) = FLOOR(DOT_PRODUCT(h_inv(dir, :), ra)*rs_grid%desc%npts(dir))
2165 24 : tp(dir) = MODULO(tp(dir), rs_grid%desc%npts(dir))
2166 24 : IF (rs_grid%desc%perd(dir) /= 1) THEN
2167 8 : lb(dir) = rs_grid%lb_local(dir) + rs_grid%desc%border
2168 8 : ub(dir) = rs_grid%ub_local(dir) - rs_grid%desc%border
2169 : ELSE
2170 16 : lb(dir) = rs_grid%lb_local(dir)
2171 16 : ub(dir) = rs_grid%ub_local(dir)
2172 : END IF
2173 : ! distributed grid, only map if it is local to the grid
2174 32 : location(dir) = tp(dir) + rs_grid%desc%lb(dir)
2175 : END DO
2176 60 : IF (ALL(lb(:) <= location(:)) .AND. ALL(location(:) <= ub(:))) THEN
2177 4 : res = .TRUE.
2178 : END IF
2179 : ELSE
2180 1611947 : IF (PRESENT(offset) .AND. PRESENT(group_size) .AND. PRESENT(my_pos)) THEN
2181 : ! not distributed, just a round-robin distribution over the full set of CPUs
2182 1611947 : IF (MODULO(offset, group_size) == my_pos) res = .TRUE.
2183 : END IF
2184 : END IF
2185 :
2186 1611955 : END FUNCTION map_gaussian_here
2187 :
2188 0 : END MODULE realspace_grid_types
|