FrontISTR  5.9.0
Large-scale structural analysis program with finit element method
hecmw_precond_SAAMG_comm.F90
Go to the documentation of this file.
1 !-------------------------------------------------------------------------------
2 ! Copyright (c) 2026 FrontISTR Commons
3 ! This software is released under the MIT License, see License.txt
4 !-------------------------------------------------------------------------------
19 #ifndef HECMW_SERIAL
20  ! HEC-MW convention: go through the comm wrappers, not raw MPI. Guarded so the
21  ! standalone (-DHECMW_SERIAL) keeps the self-contained no-op stubs and needs no
22  ! HEC-MW comm layer.
30 #ifdef _OPENACC
31  ! GPU halo exchange: explicit device buffers + host_data use_device (CUDA-aware MPI),
32  ! mirroring hecmw_solver_SR.F90. acc_malloc/acc_map_data/acc_free + c_devptr.
33  use openacc
34 #endif
35 #endif
36  implicit none
37 
38  private
39  public :: hecmwst_saamg_comm
40  public :: hecmw_saamg_comm_update
57  public :: hecmw_saamg_comm_size
58  public :: hecmw_saamg_comm_copy
59  public :: hecmw_saamg_comm_free
60  public :: hecmw_saamg_abort
61  public :: hecmw_saamg_check_alloc
65 
67  type hecmwst_saamg_comm
68  integer(kind=kint) :: comm = 0
69  integer(kind=kint) :: my_rank = 0
70  integer(kind=kint) :: nint = 0
71  integer(kind=kint) :: nnode = 0
72  integer(kind=kint) :: nb = 0
73  integer(kind=kint) :: n_neighbor = 0
74  integer(kind=kint), allocatable :: neighbor(:)
75  integer(kind=kint), allocatable :: import_index(:)
76  integer(kind=kint), allocatable :: import_item(:)
77  integer(kind=kint), allocatable :: export_index(:)
78  integer(kind=kint), allocatable :: export_item(:)
79  integer(kind=kint), allocatable :: gnode(:)
80  end type hecmwst_saamg_comm
81 
82 contains
83 
89  subroutine hecmw_saamg_abort(msg)
90  character(len=*), intent(in) :: msg
91  write(*,'(a)') '#### SA-AMG fatal error: '//trim(msg)
92 #ifndef HECMW_SERIAL
94 #else
95  stop 1
96 #endif
97  end subroutine hecmw_saamg_abort
98 
102  logical function hecmw_saamg_lapack_available()
103 #ifdef HECMW_WITH_LAPACK
105 #else
107 #endif
108  end function hecmw_saamg_lapack_available
109 
113  subroutine hecmw_saamg_require_lapack(site)
114  character(len=*), intent(in) :: site
115  call hecmw_saamg_abort('SA-AMG (PRECOND=22) requires a LAPACK-enabled build ['//trim(site)// &
116  ']; rebuild with --with-lapack (setup.sh) or -DWITH_LAPACK=ON (cmake), or select another preconditioner')
117  end subroutine hecmw_saamg_require_lapack
118 
125  subroutine hecmw_saamg_check_alloc(ier, what)
126  integer(kind=kint), intent(in) :: ier
127  character(len=*), intent(in) :: what
128  if (ier /= 0) call hecmw_saamg_abort('memory allocation failed: '//trim(what))
129  end subroutine hecmw_saamg_check_alloc
130 
136  subroutine hecmw_saamg_comm_init_serial(cmt, nnode, nb)
137  type(hecmwst_saamg_comm), intent(out) :: cmt
138  integer(kind=kint), intent(in) :: nnode, nb
139  integer(kind=kint) :: i
140  call hecmw_saamg_comm_free(cmt)
141 #ifndef HECMW_SERIAL
142  cmt%comm = hecmw_comm_get_comm() ! valid single-process communicator for global reductions
143 #else
144  cmt%comm = 0
145 #endif
146  cmt%my_rank = 0
147  cmt%nint = nnode
148  cmt%nnode = nnode
149  cmt%nb = nb
150  cmt%n_neighbor = 0
151  allocate(cmt%gnode(nnode))
152  do i = 1, nnode
153  cmt%gnode(i) = i
154  end do
155  end subroutine hecmw_saamg_comm_init_serial
156 
159  subroutine hecmw_saamg_comm_update(cmt, nb, X)
160  type(hecmwst_saamg_comm), intent(in) :: cmt
161  integer(kind=kint), intent(in) :: nb
162  real(kind=kreal), intent(inout) :: x(:)
163 #ifndef HECMW_SERIAL
164  real(kind=kreal), allocatable :: ws(:), wr(:)
165  integer(kind=kint), allocatable :: req1(:), req2(:)
166  integer(kind=kint), allocatable :: sta1(:,:), sta2(:,:)
167  integer(kind=kint) :: neib, istart, inum, k, kk, ii, nreq1, nreq2, ns, nr
168 #ifdef _OPENACC
169  type(c_devptr) :: ws_dev, wr_dev
170 #endif
171 
172  if (cmt%n_neighbor == 0) return
173  ns = cmt%export_index(cmt%n_neighbor)
174  nr = cmt%import_index(cmt%n_neighbor)
175  allocate(ws(nb*ns), wr(nb*nr))
176  allocate(req1(cmt%n_neighbor), req2(cmt%n_neighbor))
177  allocate(sta1(hecmw_status_size, cmt%n_neighbor), sta2(hecmw_status_size, cmt%n_neighbor))
178 #ifdef _OPENACC
179  ! Map WS/WR to explicit device buffers so pack/unpack run on device and
180  ! host_data use_device passes a true device pointer to CUDA-aware MPI (SR.F90 idiom).
181  ws_dev = acc_malloc(kreal * nb * ns)
182  wr_dev = acc_malloc(kreal * nb * nr)
183  call acc_map_data(ws, ws_dev, kreal * nb * ns)
184  call acc_map_data(wr, wr_dev, kreal * nb * nr)
185 #endif
186 
187  ! pack + post sends
188  nreq1 = 0
189  do neib = 1, cmt%n_neighbor
190  istart = cmt%export_index(neib-1)
191  inum = cmt%export_index(neib) - istart
192  if (inum == 0) cycle
193 #ifdef _OPENACC
194  !$acc kernels
195  !$acc loop independent collapse(2)
196  do k = istart+1, istart+inum
197  do kk = 1, nb
198  ii = cmt%export_item(k)
199  ws(nb*(k-1)+kk) = x(nb*(ii-1)+kk)
200  end do
201  end do
202  !$acc end kernels
203  nreq1 = nreq1 + 1
204  !$acc host_data use_device(WS)
205  call hecmw_isend_r(ws(nb*istart+1:nb*istart+nb*inum), nb*inum, &
206  cmt%neighbor(neib), 0, cmt%comm, req1(nreq1))
207  !$acc end host_data
208 #else
209  do k = istart+1, istart+inum
210  ii = cmt%export_item(k)
211  do kk = 1, nb
212  ws(nb*(k-1)+kk) = x(nb*(ii-1)+kk)
213  end do
214  end do
215  nreq1 = nreq1 + 1
216  call hecmw_isend_r(ws(nb*istart+1:nb*istart+nb*inum), nb*inum, &
217  cmt%neighbor(neib), 0, cmt%comm, req1(nreq1))
218 #endif
219  end do
220 
221  ! post receives
222  nreq2 = 0
223  do neib = 1, cmt%n_neighbor
224  istart = cmt%import_index(neib-1)
225  inum = cmt%import_index(neib) - istart
226  if (inum == 0) cycle
227  nreq2 = nreq2 + 1
228 #ifdef _OPENACC
229  !$acc host_data use_device(WR)
230  call hecmw_irecv_r(wr(nb*istart+1:nb*istart+nb*inum), nb*inum, &
231  cmt%neighbor(neib), 0, cmt%comm, req2(nreq2))
232  !$acc end host_data
233 #else
234  call hecmw_irecv_r(wr(nb*istart+1:nb*istart+nb*inum), nb*inum, &
235  cmt%neighbor(neib), 0, cmt%comm, req2(nreq2))
236 #endif
237  end do
238 
239  call hecmw_waitall(nreq2, req2, sta2)
240 
241  ! unpack into halo region
242  do neib = 1, cmt%n_neighbor
243  istart = cmt%import_index(neib-1)
244  inum = cmt%import_index(neib) - istart
245 #ifdef _OPENACC
246  !$acc kernels
247  !$acc loop independent collapse(2)
248  do k = istart+1, istart+inum
249  do kk = 1, nb
250  ii = cmt%import_item(k)
251  x(nb*(ii-1)+kk) = wr(nb*(k-1)+kk)
252  end do
253  end do
254  !$acc end kernels
255 #else
256  do k = istart+1, istart+inum
257  ii = cmt%import_item(k)
258  do kk = 1, nb
259  x(nb*(ii-1)+kk) = wr(nb*(k-1)+kk)
260  end do
261  end do
262 #endif
263  end do
264 
265  call hecmw_waitall(nreq1, req1, sta1)
266 #ifdef _OPENACC
267  call acc_unmap_data(ws)
268  call acc_unmap_data(wr)
269  call acc_free(ws_dev)
270  call acc_free(wr_dev)
271 #endif
272  deallocate(ws, wr, req1, req2, sta1, sta2)
273 #else
274  ! serial: no halo, nothing to exchange
275  if (nb < 0) x(1) = x(1) ! silence unused-arg warnings without effect
276 #endif
277  end subroutine hecmw_saamg_comm_update
278 
284  subroutine hecmw_saamg_comm_reverse_add(cmt, nb, X)
285  type(hecmwst_saamg_comm), intent(in) :: cmt
286  integer(kind=kint), intent(in) :: nb
287  real(kind=kreal), intent(inout) :: x(:)
288 #ifndef HECMW_SERIAL
289  real(kind=kreal), allocatable :: ws(:), wr(:)
290  integer(kind=kint), allocatable :: req1(:), req2(:), sta1(:,:), sta2(:,:)
291  integer(kind=kint) :: neib, istart, inum, k, kk, ii, nreq1, nreq2, ns, nr
292 #ifdef _OPENACC
293  type(c_devptr) :: ws_dev, wr_dev
294 #endif
295 
296  if (cmt%n_neighbor == 0) return
297  ns = cmt%import_index(cmt%n_neighbor) ! sending halo (import) values
298  nr = cmt%export_index(cmt%n_neighbor) ! receiving into owned (export) nodes
299  allocate(ws(nb*ns), wr(nb*nr))
300  allocate(req1(cmt%n_neighbor), req2(cmt%n_neighbor))
301  allocate(sta1(hecmw_status_size, cmt%n_neighbor), sta2(hecmw_status_size, cmt%n_neighbor))
302 #ifdef _OPENACC
303  ws_dev = acc_malloc(kreal * nb * ns)
304  wr_dev = acc_malloc(kreal * nb * nr)
305  call acc_map_data(ws, ws_dev, kreal * nb * ns)
306  call acc_map_data(wr, wr_dev, kreal * nb * nr)
307 #endif
308 
309  nreq1 = 0
310  do neib = 1, cmt%n_neighbor
311  istart = cmt%import_index(neib-1)
312  inum = cmt%import_index(neib) - istart
313  if (inum == 0) cycle
314 #ifdef _OPENACC
315  !$acc kernels
316  !$acc loop independent collapse(2)
317  do k = istart+1, istart+inum
318  do kk = 1, nb
319  ii = cmt%import_item(k)
320  ws(nb*(k-1)+kk) = x(nb*(ii-1)+kk)
321  end do
322  end do
323  !$acc end kernels
324  nreq1 = nreq1 + 1
325  !$acc host_data use_device(WS)
326  call hecmw_isend_r(ws(nb*istart+1:nb*istart+nb*inum), nb*inum, &
327  cmt%neighbor(neib), 2, cmt%comm, req1(nreq1))
328  !$acc end host_data
329 #else
330  do k = istart+1, istart+inum
331  ii = cmt%import_item(k)
332  do kk = 1, nb
333  ws(nb*(k-1)+kk) = x(nb*(ii-1)+kk)
334  end do
335  end do
336  nreq1 = nreq1 + 1
337  call hecmw_isend_r(ws(nb*istart+1:nb*istart+nb*inum), nb*inum, &
338  cmt%neighbor(neib), 2, cmt%comm, req1(nreq1))
339 #endif
340  end do
341  nreq2 = 0
342  do neib = 1, cmt%n_neighbor
343  istart = cmt%export_index(neib-1)
344  inum = cmt%export_index(neib) - istart
345  if (inum == 0) cycle
346  nreq2 = nreq2 + 1
347 #ifdef _OPENACC
348  !$acc host_data use_device(WR)
349  call hecmw_irecv_r(wr(nb*istart+1:nb*istart+nb*inum), nb*inum, &
350  cmt%neighbor(neib), 2, cmt%comm, req2(nreq2))
351  !$acc end host_data
352 #else
353  call hecmw_irecv_r(wr(nb*istart+1:nb*istart+nb*inum), nb*inum, &
354  cmt%neighbor(neib), 2, cmt%comm, req2(nreq2))
355 #endif
356  end do
357  call hecmw_waitall(nreq2, req2, sta2)
358  ! unpack: accumulate halo contributions into owned nodes. Per-neighbor export_item
359  ! values are distinct, so collapse(2) within one neighbor has no += race; different
360  ! neighbors accumulate in separate (sequential) kernels.
361  do neib = 1, cmt%n_neighbor
362  istart = cmt%export_index(neib-1)
363  inum = cmt%export_index(neib) - istart
364 #ifdef _OPENACC
365  !$acc kernels
366  !$acc loop independent collapse(2)
367  do k = istart+1, istart+inum
368  do kk = 1, nb
369  ii = cmt%export_item(k)
370  x(nb*(ii-1)+kk) = x(nb*(ii-1)+kk) + wr(nb*(k-1)+kk)
371  end do
372  end do
373  !$acc end kernels
374 #else
375  do k = istart+1, istart+inum
376  ii = cmt%export_item(k)
377  do kk = 1, nb
378  x(nb*(ii-1)+kk) = x(nb*(ii-1)+kk) + wr(nb*(k-1)+kk)
379  end do
380  end do
381 #endif
382  end do
383  call hecmw_waitall(nreq1, req1, sta1)
384 #ifdef _OPENACC
385  call acc_unmap_data(ws)
386  call acc_unmap_data(wr)
387  call acc_free(ws_dev)
388  call acc_free(wr_dev)
389 #endif
390  deallocate(ws, wr, req1, req2, sta1, sta2)
391 #else
392  if (nb < 0) x(1) = x(1)
393 #endif
394  end subroutine hecmw_saamg_comm_reverse_add
395 
399  subroutine hecmw_saamg_comm_update_i(cmt, nb, IX)
400  type(hecmwst_saamg_comm), intent(in) :: cmt
401  integer(kind=kint), intent(in) :: nb
402  integer(kind=kint), intent(inout) :: ix(:)
403 #ifndef HECMW_SERIAL
404  integer(kind=kint), allocatable :: ws(:), wr(:)
405  integer(kind=kint), allocatable :: req1(:), req2(:)
406  integer(kind=kint), allocatable :: sta1(:,:), sta2(:,:)
407  integer(kind=kint) :: neib, istart, inum, k, kk, ii, nreq1, nreq2, ns, nr
408 
409  if (cmt%n_neighbor == 0) return
410  ns = cmt%export_index(cmt%n_neighbor)
411  nr = cmt%import_index(cmt%n_neighbor)
412  allocate(ws(nb*ns), wr(nb*nr))
413  allocate(req1(cmt%n_neighbor), req2(cmt%n_neighbor))
414  allocate(sta1(hecmw_status_size, cmt%n_neighbor), sta2(hecmw_status_size, cmt%n_neighbor))
415 
416  nreq1 = 0
417  do neib = 1, cmt%n_neighbor
418  istart = cmt%export_index(neib-1)
419  inum = cmt%export_index(neib) - istart
420  if (inum == 0) cycle
421  do k = istart+1, istart+inum
422  ii = cmt%export_item(k)
423  do kk = 1, nb
424  ws(nb*(k-1)+kk) = ix(nb*(ii-1)+kk)
425  end do
426  end do
427  nreq1 = nreq1 + 1
428  call hecmw_isend_int(ws(nb*istart+1:nb*istart+nb*inum), nb*inum, &
429  cmt%neighbor(neib), 0, cmt%comm, req1(nreq1))
430  end do
431  nreq2 = 0
432  do neib = 1, cmt%n_neighbor
433  istart = cmt%import_index(neib-1)
434  inum = cmt%import_index(neib) - istart
435  if (inum == 0) cycle
436  nreq2 = nreq2 + 1
437  call hecmw_irecv_int(wr(nb*istart+1:nb*istart+nb*inum), nb*inum, &
438  cmt%neighbor(neib), 0, cmt%comm, req2(nreq2))
439  end do
440  call hecmw_waitall(nreq2, req2, sta2)
441  do neib = 1, cmt%n_neighbor
442  istart = cmt%import_index(neib-1)
443  inum = cmt%import_index(neib) - istart
444  do k = istart+1, istart+inum
445  ii = cmt%import_item(k)
446  do kk = 1, nb
447  ix(nb*(ii-1)+kk) = wr(nb*(k-1)+kk)
448  end do
449  end do
450  end do
451  call hecmw_waitall(nreq1, req1, sta1)
452  deallocate(ws, wr, req1, req2, sta1, sta2)
453 #else
454  if (nb < 0) ix(1) = ix(1)
455 #endif
456  end subroutine hecmw_saamg_comm_update_i
457 
465  subroutine hecmw_saamg_comm_update_var(cmt, pb, cnt, off, X)
466  type(hecmwst_saamg_comm), intent(in) :: cmt
467  integer(kind=kint), intent(in) :: pb
468  integer(kind=kint), intent(in) :: cnt(:)
469  integer(kind=kint), intent(in) :: off(:)
470  real(kind=kreal), intent(inout) :: x(:)
471 #ifndef HECMW_SERIAL
472  real(kind=kreal), allocatable :: ws(:), wr(:)
473  integer(kind=kint), allocatable :: req1(:), req2(:)
474  integer(kind=kint), allocatable :: sta1(:,:), sta2(:,:)
475  integer(kind=kint), allocatable :: sdisp(:), rdisp(:)
476  integer(kind=kint) :: neib, k, ii, c, o, wp, nreq1, nreq2, b, nblk, astat
477 
478  if (cmt%n_neighbor == 0) return
479  allocate(sdisp(0:cmt%n_neighbor), rdisp(0:cmt%n_neighbor))
480  sdisp(0) = 0; rdisp(0) = 0
481  do neib = 1, cmt%n_neighbor
482  nblk = 0
483  do k = cmt%export_index(neib-1)+1, cmt%export_index(neib)
484  nblk = nblk + cnt(cmt%export_item(k))
485  end do
486  sdisp(neib) = sdisp(neib-1) + nblk
487  nblk = 0
488  do k = cmt%import_index(neib-1)+1, cmt%import_index(neib)
489  nblk = nblk + cnt(cmt%import_item(k))
490  end do
491  rdisp(neib) = rdisp(neib-1) + nblk
492  end do
493  allocate(ws(sdisp(cmt%n_neighbor)*pb), wr(rdisp(cmt%n_neighbor)*pb), stat=astat)
494  call hecmw_saamg_check_alloc(astat, 'comm_update_var (WS/WR)')
495  allocate(req1(cmt%n_neighbor), req2(cmt%n_neighbor))
496  allocate(sta1(hecmw_status_size, cmt%n_neighbor), sta2(hecmw_status_size, cmt%n_neighbor))
497 
498  ! pack + post sends
499  nreq1 = 0
500  do neib = 1, cmt%n_neighbor
501  if (sdisp(neib) == sdisp(neib-1)) cycle
502  wp = sdisp(neib-1)
503  do k = cmt%export_index(neib-1)+1, cmt%export_index(neib)
504  ii = cmt%export_item(k); c = cnt(ii); o = off(ii)
505  do b = 1, c*pb
506  ws(wp*pb+b) = x(o*pb+b)
507  end do
508  wp = wp + c
509  end do
510  nreq1 = nreq1 + 1
511  call hecmw_isend_r(ws(sdisp(neib-1)*pb+1:sdisp(neib)*pb), &
512  (sdisp(neib)-sdisp(neib-1))*pb, cmt%neighbor(neib), 0, cmt%comm, req1(nreq1))
513  end do
514 
515  ! post receives
516  nreq2 = 0
517  do neib = 1, cmt%n_neighbor
518  if (rdisp(neib) == rdisp(neib-1)) cycle
519  nreq2 = nreq2 + 1
520  call hecmw_irecv_r(wr(rdisp(neib-1)*pb+1:rdisp(neib)*pb), &
521  (rdisp(neib)-rdisp(neib-1))*pb, cmt%neighbor(neib), 0, cmt%comm, req2(nreq2))
522  end do
523 
524  call hecmw_waitall(nreq2, req2, sta2)
525 
526  ! unpack into halo region
527  do neib = 1, cmt%n_neighbor
528  wp = rdisp(neib-1)
529  do k = cmt%import_index(neib-1)+1, cmt%import_index(neib)
530  ii = cmt%import_item(k); c = cnt(ii); o = off(ii)
531  do b = 1, c*pb
532  x(o*pb+b) = wr(wp*pb+b)
533  end do
534  wp = wp + c
535  end do
536  end do
537 
538  call hecmw_waitall(nreq1, req1, sta1)
539  deallocate(ws, wr, req1, req2, sta1, sta2, sdisp, rdisp)
540 #else
541  if (pb < 0) x(1) = x(1) ! silence unused-arg warnings without effect
542 #endif
543  end subroutine hecmw_saamg_comm_update_var
544 
548  subroutine hecmw_saamg_comm_update_var_i(cmt, cnt, off, IX)
549  type(hecmwst_saamg_comm), intent(in) :: cmt
550  integer(kind=kint), intent(in) :: cnt(:)
551  integer(kind=kint), intent(in) :: off(:)
552  integer(kind=kint), intent(inout) :: ix(:)
553 #ifndef HECMW_SERIAL
554  integer(kind=kint), allocatable :: ws(:), wr(:)
555  integer(kind=kint), allocatable :: req1(:), req2(:)
556  integer(kind=kint), allocatable :: sta1(:,:), sta2(:,:)
557  integer(kind=kint), allocatable :: sdisp(:), rdisp(:)
558  integer(kind=kint) :: neib, k, ii, c, o, wp, nreq1, nreq2, b, nblk, astat
559 
560  if (cmt%n_neighbor == 0) return
561  allocate(sdisp(0:cmt%n_neighbor), rdisp(0:cmt%n_neighbor))
562  sdisp(0) = 0; rdisp(0) = 0
563  do neib = 1, cmt%n_neighbor
564  nblk = 0
565  do k = cmt%export_index(neib-1)+1, cmt%export_index(neib)
566  nblk = nblk + cnt(cmt%export_item(k))
567  end do
568  sdisp(neib) = sdisp(neib-1) + nblk
569  nblk = 0
570  do k = cmt%import_index(neib-1)+1, cmt%import_index(neib)
571  nblk = nblk + cnt(cmt%import_item(k))
572  end do
573  rdisp(neib) = rdisp(neib-1) + nblk
574  end do
575  allocate(ws(sdisp(cmt%n_neighbor)), wr(rdisp(cmt%n_neighbor)), stat=astat)
576  call hecmw_saamg_check_alloc(astat, 'comm_update_var_i (WS/WR)')
577  allocate(req1(cmt%n_neighbor), req2(cmt%n_neighbor))
578  allocate(sta1(hecmw_status_size, cmt%n_neighbor), sta2(hecmw_status_size, cmt%n_neighbor))
579 
580  nreq1 = 0
581  do neib = 1, cmt%n_neighbor
582  if (sdisp(neib) == sdisp(neib-1)) cycle
583  wp = sdisp(neib-1)
584  do k = cmt%export_index(neib-1)+1, cmt%export_index(neib)
585  ii = cmt%export_item(k); c = cnt(ii); o = off(ii)
586  do b = 1, c
587  ws(wp+b) = ix(o+b)
588  end do
589  wp = wp + c
590  end do
591  nreq1 = nreq1 + 1
592  call hecmw_isend_int(ws(sdisp(neib-1)+1:sdisp(neib)), &
593  sdisp(neib)-sdisp(neib-1), cmt%neighbor(neib), 0, cmt%comm, req1(nreq1))
594  end do
595 
596  nreq2 = 0
597  do neib = 1, cmt%n_neighbor
598  if (rdisp(neib) == rdisp(neib-1)) cycle
599  nreq2 = nreq2 + 1
600  call hecmw_irecv_int(wr(rdisp(neib-1)+1:rdisp(neib)), &
601  rdisp(neib)-rdisp(neib-1), cmt%neighbor(neib), 0, cmt%comm, req2(nreq2))
602  end do
603 
604  call hecmw_waitall(nreq2, req2, sta2)
605 
606  do neib = 1, cmt%n_neighbor
607  wp = rdisp(neib-1)
608  do k = cmt%import_index(neib-1)+1, cmt%import_index(neib)
609  ii = cmt%import_item(k); c = cnt(ii); o = off(ii)
610  do b = 1, c
611  ix(o+b) = wr(wp+b)
612  end do
613  wp = wp + c
614  end do
615  end do
616 
617  call hecmw_waitall(nreq1, req1, sta1)
618  deallocate(ws, wr, req1, req2, sta1, sta2, sdisp, rdisp)
619 #else
620  if (size(cnt) < 0) ix(1) = ix(1) ! silence unused-arg warnings without effect
621 #endif
622  end subroutine hecmw_saamg_comm_update_var_i
623 
625  function hecmw_saamg_comm_size(cmt) result(np)
626  type(hecmwst_saamg_comm), intent(in) :: cmt
627  integer(kind=kint) :: np
628 #ifndef HECMW_SERIAL
629  call hecmw_comm_size(cmt%comm, np)
630 #else
631  np = 1
632  if (cmt%my_rank < 0) np = 1
633 #endif
634  end function hecmw_saamg_comm_size
635 
637  subroutine hecmw_saamg_comm_allgather_int(cmt, sval, rbuf)
638  type(hecmwst_saamg_comm), intent(in) :: cmt
639  integer(kind=kint), intent(in) :: sval
640  integer(kind=kint), intent(out) :: rbuf(:)
641 #ifndef HECMW_SERIAL
642  call hecmw_allgather_int_1(sval, rbuf, cmt%comm)
643 #else
644  rbuf(1) = sval
645 #endif
646  end subroutine hecmw_saamg_comm_allgather_int
647 
650  subroutine hecmw_saamg_comm_exchange_neighbor_int(cmt, sendvals, recvvals)
651  type(hecmwst_saamg_comm), intent(in) :: cmt
652  integer(kind=kint), intent(in) :: sendvals(:)
653  integer(kind=kint), intent(out) :: recvvals(:)
654 #ifndef HECMW_SERIAL
655  integer(kind=kint), allocatable :: req(:), sta(:,:)
656  integer(kind=kint) :: neib, nreq
657  if (cmt%n_neighbor == 0) return
658  allocate(req(2*cmt%n_neighbor), sta(hecmw_status_size, 2*cmt%n_neighbor))
659  nreq = 0
660  do neib = 1, cmt%n_neighbor
661  nreq = nreq + 1
662  call hecmw_irecv_int(recvvals(neib:neib), 1, cmt%neighbor(neib), 1, cmt%comm, req(nreq))
663  end do
664  do neib = 1, cmt%n_neighbor
665  nreq = nreq + 1
666  call hecmw_isend_int(sendvals(neib:neib), 1, cmt%neighbor(neib), 1, cmt%comm, req(nreq))
667  end do
668  call hecmw_waitall(nreq, req, sta)
669  deallocate(req, sta)
670 #else
671  if (size(sendvals) > 0) recvvals = sendvals
672 #endif
674 
677  type(hecmwst_saamg_comm), intent(in) :: cmt
678  real(kind=kreal), intent(inout) :: s
679 #ifndef HECMW_SERIAL
680  real(kind=kreal) :: buf(1)
681  buf(1) = s; call hecmw_allreduce_r_comm(buf, 1, hecmw_max, cmt%comm); s = buf(1)
682 #else
683  if (cmt%my_rank < 0) s = s
684 #endif
685  end subroutine hecmw_saamg_comm_allreduce_max_r
686 
689  type(hecmwst_saamg_comm), intent(in) :: cmt
690  integer(kind=kint), intent(inout) :: n
691 #ifndef HECMW_SERIAL
692  integer(kind=kint) :: buf(1)
693  buf(1) = n; call hecmw_allreduce_i_comm(buf, 1, hecmw_max, cmt%comm); n = buf(1)
694 #else
695  if (cmt%my_rank < 0) n = n
696 #endif
698 
701  type(hecmwst_saamg_comm), intent(in) :: cmt
702  integer(kind=kint), intent(inout) :: n
703 #ifndef HECMW_SERIAL
704  integer(kind=kint) :: buf(1)
705  buf(1) = n; call hecmw_allreduce_i_comm(buf, 1, hecmw_sum, cmt%comm); n = buf(1)
706 #else
707  if (cmt%my_rank < 0) n = n
708 #endif
710 
713  type(hecmwst_saamg_comm), intent(in) :: cmt
714  real(kind=kreal), intent(inout) :: s
715 #ifndef HECMW_SERIAL
716  real(kind=kreal) :: buf(1)
717  buf(1) = s; call hecmw_allreduce_r_comm(buf, 1, hecmw_sum, cmt%comm); s = buf(1)
718 #else
719  if (cmt%my_rank < 0) s = s
720 #endif
721  end subroutine hecmw_saamg_comm_allreduce_sum_r
722 
726  subroutine hecmw_saamg_comm_allgatherv_triplets(cmt, nloc, ti, tj, tv, ntot, gti, gtj, gtv)
727  type(hecmwst_saamg_comm), intent(in) :: cmt
728  integer(kind=kint), intent(in) :: nloc
729  integer(kind=kint), intent(in) :: ti(:), tj(:)
730  real(kind=kreal), intent(in) :: tv(:)
731  integer(kind=kint), intent(out) :: ntot
732  integer(kind=kint), allocatable, intent(out) :: gti(:), gtj(:)
733  real(kind=kreal), allocatable, intent(out) :: gtv(:)
734 #ifndef HECMW_SERIAL
735  integer(kind=kint), allocatable :: counts(:), displs(:)
736  integer(kind=kint) :: nprocs, p
737  call hecmw_comm_size(cmt%comm, nprocs)
738  allocate(counts(nprocs), displs(nprocs))
739  call hecmw_allgather_int_1(nloc, counts, cmt%comm)
740  displs(1) = 0
741  do p = 2, nprocs
742  displs(p) = displs(p-1) + counts(p-1)
743  end do
744  ntot = displs(nprocs) + counts(nprocs)
745  allocate(gti(max(ntot,1)), gtj(max(ntot,1)), gtv(max(ntot,1)))
746  call hecmw_allgatherv_int(ti, nloc, gti, counts, displs, cmt%comm)
747  call hecmw_allgatherv_int(tj, nloc, gtj, counts, displs, cmt%comm)
748  call hecmw_allgatherv_real(tv, nloc, gtv, counts, displs, cmt%comm)
749  deallocate(counts, displs)
750 #else
751  ntot = nloc
752  allocate(gti(max(ntot,1)), gtj(max(ntot,1)), gtv(max(ntot,1)))
753  gti(1:nloc) = ti(1:nloc); gtj(1:nloc) = tj(1:nloc); gtv(1:nloc) = tv(1:nloc)
754 #endif
756 
759  subroutine hecmw_saamg_comm_allgatherv_real(cmt, nloc, vloc, ntot, gv)
760  type(hecmwst_saamg_comm), intent(in) :: cmt
761  integer(kind=kint), intent(in) :: nloc
762  real(kind=kreal), intent(in) :: vloc(:)
763  integer(kind=kint), intent(out) :: ntot
764  real(kind=kreal), allocatable, intent(out) :: gv(:)
765 #ifndef HECMW_SERIAL
766  integer(kind=kint), allocatable :: counts(:), displs(:)
767  integer(kind=kint) :: nprocs, p
768  call hecmw_comm_size(cmt%comm, nprocs)
769  allocate(counts(nprocs), displs(nprocs))
770  call hecmw_allgather_int_1(nloc, counts, cmt%comm)
771  displs(1) = 0
772  do p = 2, nprocs
773  displs(p) = displs(p-1) + counts(p-1)
774  end do
775  ntot = displs(nprocs) + counts(nprocs)
776  allocate(gv(max(ntot,1)))
777  call hecmw_allgatherv_real(vloc, nloc, gv, counts, displs, cmt%comm)
778  deallocate(counts, displs)
779 #else
780  ntot = nloc
781  allocate(gv(max(ntot,1)))
782  gv(1:nloc) = vloc(1:nloc)
783 #endif
784  end subroutine hecmw_saamg_comm_allgatherv_real
785 
788  subroutine hecmw_saamg_comm_alltoall_int(cmt, scnt, rcnt)
789  type(hecmwst_saamg_comm), intent(in) :: cmt
790  integer(kind=kint), intent(in) :: scnt(:)
791  integer(kind=kint), intent(out) :: rcnt(:)
792 #ifndef HECMW_SERIAL
793  call hecmw_alltoall_int(scnt, 1, rcnt, 1, cmt%comm)
794 #else
795  rcnt(1) = scnt(1)
796 #endif
797  end subroutine hecmw_saamg_comm_alltoall_int
798 
802  subroutine hecmw_saamg_comm_alltoallv_int(cmt, scnt, sbuf, ntot, rbuf)
803  type(hecmwst_saamg_comm), intent(in) :: cmt
804  integer(kind=kint), intent(in) :: scnt(:), sbuf(:)
805  integer(kind=kint), intent(out) :: ntot
806  integer(kind=kint), allocatable, intent(out) :: rbuf(:)
807 #ifndef HECMW_SERIAL
808  integer(kind=kint), allocatable :: rcnt(:), sdis(:), rdis(:)
809  integer(kind=kint) :: nprocs, p
810  call hecmw_comm_size(cmt%comm, nprocs)
811  allocate(rcnt(nprocs), sdis(nprocs), rdis(nprocs))
812  call hecmw_alltoall_int(scnt, 1, rcnt, 1, cmt%comm)
813  sdis(1) = 0; rdis(1) = 0
814  do p = 2, nprocs
815  sdis(p) = sdis(p-1) + scnt(p-1); rdis(p) = rdis(p-1) + rcnt(p-1)
816  end do
817  ntot = rdis(nprocs) + rcnt(nprocs)
818  allocate(rbuf(max(ntot,1)))
819  call hecmw_alltoallv_int(sbuf, scnt, sdis, rbuf, rcnt, rdis, cmt%comm)
820  deallocate(rcnt, sdis, rdis)
821 #else
822  ntot = scnt(1); allocate(rbuf(max(ntot,1)))
823  if (ntot > 0) rbuf(1:ntot) = sbuf(1:ntot)
824 #endif
825  end subroutine hecmw_saamg_comm_alltoallv_int
826 
831  subroutine hecmw_saamg_comm_alltoallv_triplets(cmt, scnt, si, sj, sv, ntot, ri, rj, rv)
832  type(hecmwst_saamg_comm), intent(in) :: cmt
833  integer(kind=kint), intent(in) :: scnt(:), si(:), sj(:)
834  real(kind=kreal), intent(in) :: sv(:)
835  integer(kind=kint), intent(out) :: ntot
836  integer(kind=kint), allocatable, intent(out) :: ri(:), rj(:)
837  real(kind=kreal), allocatable, intent(out) :: rv(:)
838 #ifndef HECMW_SERIAL
839  integer(kind=kint), allocatable :: rcnt(:), sdis(:), rdis(:)
840  integer(kind=kint) :: nprocs, p
841  call hecmw_comm_size(cmt%comm, nprocs)
842  allocate(rcnt(nprocs), sdis(nprocs), rdis(nprocs))
843  call hecmw_alltoall_int(scnt, 1, rcnt, 1, cmt%comm)
844  sdis(1) = 0; rdis(1) = 0
845  do p = 2, nprocs
846  sdis(p) = sdis(p-1) + scnt(p-1); rdis(p) = rdis(p-1) + rcnt(p-1)
847  end do
848  ntot = rdis(nprocs) + rcnt(nprocs)
849  allocate(ri(max(ntot,1)), rj(max(ntot,1)), rv(max(ntot,1)))
850  call hecmw_alltoallv_int(si, scnt, sdis, ri, rcnt, rdis, cmt%comm)
851  call hecmw_alltoallv_int(sj, scnt, sdis, rj, rcnt, rdis, cmt%comm)
852  call hecmw_alltoallv_real(sv, scnt, sdis, rv, rcnt, rdis, cmt%comm)
853  deallocate(rcnt, sdis, rdis)
854 #else
855  ntot = scnt(1); allocate(ri(max(ntot,1)), rj(max(ntot,1)), rv(max(ntot,1)))
856  if (ntot > 0) then; ri(1:ntot) = si(1:ntot); rj(1:ntot) = sj(1:ntot); rv(1:ntot) = sv(1:ntot); end if
857 #endif
859 
864  subroutine hecmw_saamg_comm_alltoallv_real(cmt, scnt, sval, ntot, rval)
865  type(hecmwst_saamg_comm), intent(in) :: cmt
866  integer(kind=kint), intent(in) :: scnt(:)
867  real(kind=kreal), intent(in) :: sval(:)
868  integer(kind=kint), intent(out) :: ntot
869  real(kind=kreal), allocatable, intent(out) :: rval(:)
870 #ifndef HECMW_SERIAL
871  integer(kind=kint), allocatable :: rcnt(:), sdis(:), rdis(:)
872  integer(kind=kint) :: nprocs, p
873  call hecmw_comm_size(cmt%comm, nprocs)
874  allocate(rcnt(nprocs), sdis(nprocs), rdis(nprocs))
875  call hecmw_alltoall_int(scnt, 1, rcnt, 1, cmt%comm)
876  sdis(1) = 0; rdis(1) = 0
877  do p = 2, nprocs
878  sdis(p) = sdis(p-1) + scnt(p-1); rdis(p) = rdis(p-1) + rcnt(p-1)
879  end do
880  ntot = rdis(nprocs) + rcnt(nprocs)
881  allocate(rval(max(ntot,1)))
882  call hecmw_alltoallv_real(sval, scnt, sdis, rval, rcnt, rdis, cmt%comm)
883  deallocate(rcnt, sdis, rdis)
884 #else
885  ntot = scnt(1); allocate(rval(max(ntot,1)))
886  if (ntot > 0) rval(1:ntot) = sval(1:ntot)
887 #endif
888  end subroutine hecmw_saamg_comm_alltoallv_real
889 
891  subroutine hecmw_saamg_comm_copy(src, dst)
892  type(hecmwst_saamg_comm), intent(in) :: src
893  type(hecmwst_saamg_comm), intent(out) :: dst
894  integer(kind=kint) :: nnb
895  call hecmw_saamg_comm_free(dst)
896  dst%comm = src%comm; dst%my_rank = src%my_rank
897  dst%nint = src%nint; dst%nnode = src%nnode; dst%nb = src%nb
898  dst%n_neighbor = src%n_neighbor
899  if (allocated(src%gnode)) then
900  allocate(dst%gnode(size(src%gnode))); dst%gnode = src%gnode
901  end if
902  nnb = src%n_neighbor
903  if (nnb == 0) return
904  allocate(dst%neighbor(nnb), dst%import_index(0:nnb), dst%export_index(0:nnb))
905  dst%neighbor = src%neighbor
906  dst%import_index = src%import_index
907  dst%export_index = src%export_index
908  allocate(dst%import_item(size(src%import_item)), dst%export_item(size(src%export_item)))
909  dst%import_item = src%import_item
910  dst%export_item = src%export_item
911  end subroutine hecmw_saamg_comm_copy
912 
913  subroutine hecmw_saamg_comm_free(cmt)
914  type(hecmwst_saamg_comm), intent(inout) :: cmt
915  if (allocated(cmt%neighbor)) deallocate(cmt%neighbor)
916  if (allocated(cmt%import_index)) deallocate(cmt%import_index)
917  if (allocated(cmt%import_item)) deallocate(cmt%import_item)
918  if (allocated(cmt%export_index)) deallocate(cmt%export_index)
919  if (allocated(cmt%export_item)) deallocate(cmt%export_item)
920  if (allocated(cmt%gnode)) deallocate(cmt%gnode)
921  cmt%n_neighbor = 0; cmt%nint = 0; cmt%nnode = 0
922  end subroutine hecmw_saamg_comm_free
923 
924 end module hecmw_precond_saamg_comm
Smoothed Aggregation AMG preconditioner : lightweight comm table.
subroutine, public hecmw_saamg_comm_update_var_i(cmt, cnt, off, IX)
Variable-length integer halo exchange (1 int per block). Companion to comm_update_var for the per-blo...
subroutine, public hecmw_saamg_comm_init_serial(cmt, nnode, nb)
Build a trivial single-rank communication table for nnode nodes of block size nb: no neighbors,...
subroutine, public hecmw_saamg_comm_allgatherv_triplets(cmt, nloc, ti, tj, tv, ntot, gti, gtj, gtv)
Allgatherv a triplet stream (ti,tj,tv)[1:nloc] from every rank into the globally-concatenated (gti,...
logical function, public hecmw_saamg_lapack_available()
Whether this build links LAPACK (SA-AMG's setup needs it: dense coarsest factorization,...
subroutine, public hecmw_saamg_comm_update(cmt, nb, X)
Halo exchange of a block vector X (length nb*nnode): fill the halo region nb*nint+1....
subroutine, public hecmw_saamg_check_alloc(ier, what)
Report a failed allocation (stat /= 0) with a clear message and a collective abort,...
subroutine, public hecmw_saamg_comm_allgather_int(cmt, sval, rbuf)
Allgather one integer from every rank into rbuf (size must be #ranks).
subroutine, public hecmw_saamg_comm_allreduce_sum_r(cmt, s)
Global sum-reduction of a scalar over the communicator (no-op when serial).
subroutine, public hecmw_saamg_comm_allreduce_max_r(cmt, s)
Global max-reduction of a scalar over the communicator (no-op when serial).
subroutine, public hecmw_saamg_abort(msg)
Fatal-error termination with a clear message. Under MPI this is a COLLECTIVE abort (hecmw_abort -> MP...
subroutine, public hecmw_saamg_require_lapack(site)
Abort with a uniform message when a LAPACK-only code path is reached in a build without LAPACK....
subroutine, public hecmw_saamg_comm_allreduce_sum_int(cmt, n)
Global sum-reduction of an integer over the communicator (no-op when serial).
subroutine, public hecmw_saamg_comm_alltoall_int(cmt, scnt, rcnt)
MPI_Alltoall of one integer per rank: scnt(r) (count this rank sends to rank r-1) -> rcnt(r) (count t...
subroutine, public hecmw_saamg_comm_alltoallv_triplets(cmt, scnt, si, sj, sv, ntot, ri, rj, rv)
MPI_Alltoallv of triplets (2 int + 1 real). scnt(#ranks) = send counts per destination; si/sj/sv orde...
subroutine, public hecmw_saamg_comm_alltoallv_int(cmt, scnt, sbuf, ntot, rbuf)
MPI_Alltoallv of integers. scnt(#ranks) = send counts per destination rank; sbuf must already be orde...
subroutine, public hecmw_saamg_comm_update_i(cmt, nb, IX)
Integer halo exchange of a block vector IX (length nb*cmtnnode): fill the halo region from owners....
subroutine, public hecmw_saamg_comm_allreduce_max_int(cmt, n)
Global max-reduction of an integer over the communicator (no-op when serial).
subroutine, public hecmw_saamg_comm_exchange_neighbor_int(cmt, sendvals, recvvals)
Exchange one integer with each neighbor: sendvals(k) is sent to neighbor(k), recvvals(k) is received ...
subroutine, public hecmw_saamg_comm_free(cmt)
subroutine, public hecmw_saamg_comm_update_var(cmt, pb, cnt, off, X)
Variable-length real halo exchange. Node i carries cnt(i) blocks of pb reals each,...
subroutine, public hecmw_saamg_comm_allgatherv_real(cmt, nloc, vloc, ntot, gv)
Allgatherv a real vector vloc[1:nloc] from every rank into gv[1:ntot] (allocated here)....
integer(kind=kint) function, public hecmw_saamg_comm_size(cmt)
Number of ranks in this level's communicator (1 when serial).
subroutine, public hecmw_saamg_comm_copy(src, dst)
Deep copy a communication table: dst = src.
subroutine, public hecmw_saamg_comm_alltoallv_real(cmt, scnt, sval, ntot, rval)
MPI_Alltoallv of reals only, reusing a fixed routing (same scnt as a prior _alltoallv_triplets call)....
subroutine, public hecmw_saamg_comm_reverse_add(cmt, nb, X)
Reverse halo exchange with accumulation: send each halo (import) node's value to its owner and ADD it...
Smoothed Aggregation AMG preconditioner : kind parameters.
integer(kind=4), parameter kreal
I/O and Utility.
Definition: hecmw_util_f.F90:7
integer(kind=kint), parameter hecmw_sum
subroutine hecmw_abort(comm, code)
integer(kind=kint) function hecmw_comm_get_comm()
integer(kind=kint), parameter hecmw_max
integer(kind=kint), parameter hecmw_status_size
subroutine hecmw_isend_int(sbuf, sc, dest, tag, comm, req)
subroutine hecmw_allgatherv_int(sbuf, sc, rbuf, rcs, disp, comm)
subroutine hecmw_alltoallv_real(sbuf, scs, sdisp, rbuf, rcs, rdisp, comm)
subroutine hecmw_allgatherv_real(sbuf, sc, rbuf, rcs, disp, comm)
subroutine hecmw_isend_r(sbuf, sc, dest, tag, comm, req)
subroutine hecmw_allgather_int_1(sval, rbuf, comm)
subroutine hecmw_waitall(cnt, reqs, stats)
subroutine hecmw_comm_size(comm, isize)
Number of ranks in an explicit communicator (1 when serial).
subroutine hecmw_alltoallv_int(sbuf, scs, sdisp, rbuf, rcs, rdisp, comm)
subroutine hecmw_irecv_int(rbuf, rc, source, tag, comm, req)
subroutine hecmw_irecv_r(rbuf, rc, source, tag, comm, req)
subroutine hecmw_allreduce_r_comm(val, n, ntag, comm)
subroutine hecmw_alltoall_int(sbuf, sc, rbuf, rc, comm)
subroutine hecmw_allreduce_i_comm(val, n, ntag, comm)
Allreduce over an explicit communicator (ntag = hecmw_sum / hecmw_max / hecmw_min)....