FrontISTR  6.0.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  !$omp critical
92  write(*,'(a)') '#### SA-AMG fatal error: '//trim(msg)
93  !$omp end critical
94 #ifndef HECMW_SERIAL
96 #else
97  stop 1
98 #endif
99  end subroutine hecmw_saamg_abort
100 
104  logical function hecmw_saamg_lapack_available()
105 #ifdef HECMW_WITH_LAPACK
107 #else
109 #endif
110  end function hecmw_saamg_lapack_available
111 
115  subroutine hecmw_saamg_require_lapack(site)
116  character(len=*), intent(in) :: site
117  call hecmw_saamg_abort('SA-AMG (PRECOND=22) requires a LAPACK-enabled build ['//trim(site)// &
118  ']; rebuild with --with-lapack (setup.sh) or -DWITH_LAPACK=ON (cmake), or select another preconditioner')
119  end subroutine hecmw_saamg_require_lapack
120 
127  subroutine hecmw_saamg_check_alloc(ier, what)
128  integer(kind=kint), intent(in) :: ier
129  character(len=*), intent(in) :: what
130  if (ier /= 0) call hecmw_saamg_abort('memory allocation failed: '//trim(what))
131  end subroutine hecmw_saamg_check_alloc
132 
138  subroutine hecmw_saamg_comm_init_serial(cmt, nnode, nb)
139  type(hecmwst_saamg_comm), intent(out) :: cmt
140  integer(kind=kint), intent(in) :: nnode, nb
141  integer(kind=kint) :: i
142  call hecmw_saamg_comm_free(cmt)
143 #ifndef HECMW_SERIAL
144  cmt%comm = hecmw_comm_get_comm() ! valid single-process communicator for global reductions
145 #else
146  cmt%comm = 0
147 #endif
148  cmt%my_rank = 0
149  cmt%nint = nnode
150  cmt%nnode = nnode
151  cmt%nb = nb
152  cmt%n_neighbor = 0
153  allocate(cmt%gnode(nnode))
154  do i = 1, nnode
155  cmt%gnode(i) = i
156  end do
157  end subroutine hecmw_saamg_comm_init_serial
158 
161  subroutine hecmw_saamg_comm_update(cmt, nb, X)
162  type(hecmwst_saamg_comm), intent(in) :: cmt
163  integer(kind=kint), intent(in) :: nb
164  real(kind=kreal), intent(inout) :: x(:)
165 #ifndef HECMW_SERIAL
166  real(kind=kreal), allocatable :: ws(:), wr(:)
167  integer(kind=kint), allocatable :: req1(:), req2(:)
168  integer(kind=kint), allocatable :: sta1(:,:), sta2(:,:)
169  integer(kind=kint) :: neib, istart, inum, k, kk, ii, nreq1, nreq2, ns, nr
170 #ifdef _OPENACC
171  type(c_devptr) :: ws_dev, wr_dev
172 #endif
173 
174  if (cmt%n_neighbor == 0) return
175  ns = cmt%export_index(cmt%n_neighbor)
176  nr = cmt%import_index(cmt%n_neighbor)
177  allocate(ws(nb*ns), wr(nb*nr))
178  allocate(req1(cmt%n_neighbor), req2(cmt%n_neighbor))
179  allocate(sta1(hecmw_status_size, cmt%n_neighbor), sta2(hecmw_status_size, cmt%n_neighbor))
180 #ifdef _OPENACC
181  ! Map WS/WR to explicit device buffers so pack/unpack run on device and
182  ! host_data use_device passes a true device pointer to CUDA-aware MPI (SR.F90 idiom).
183  ws_dev = acc_malloc(kreal * nb * ns)
184  wr_dev = acc_malloc(kreal * nb * nr)
185  call acc_map_data(ws, ws_dev, kreal * nb * ns)
186  call acc_map_data(wr, wr_dev, kreal * nb * nr)
187 #endif
188 
189  ! pack + post sends
190  nreq1 = 0
191  do neib = 1, cmt%n_neighbor
192  istart = cmt%export_index(neib-1)
193  inum = cmt%export_index(neib) - istart
194  if (inum == 0) cycle
195 #ifdef _OPENACC
196  !$acc kernels
197  !$acc loop independent collapse(2)
198  do k = istart+1, istart+inum
199  do kk = 1, nb
200  ii = cmt%export_item(k)
201  ws(nb*(k-1)+kk) = x(nb*(ii-1)+kk)
202  end do
203  end do
204  !$acc end kernels
205  nreq1 = nreq1 + 1
206  !$acc host_data use_device(WS)
207  call hecmw_isend_r(ws(nb*istart+1:nb*istart+nb*inum), nb*inum, &
208  cmt%neighbor(neib), 0, cmt%comm, req1(nreq1))
209  !$acc end host_data
210 #else
211  do k = istart+1, istart+inum
212  ii = cmt%export_item(k)
213  do kk = 1, nb
214  ws(nb*(k-1)+kk) = x(nb*(ii-1)+kk)
215  end do
216  end do
217  nreq1 = nreq1 + 1
218  call hecmw_isend_r(ws(nb*istart+1:nb*istart+nb*inum), nb*inum, &
219  cmt%neighbor(neib), 0, cmt%comm, req1(nreq1))
220 #endif
221  end do
222 
223  ! post receives
224  nreq2 = 0
225  do neib = 1, cmt%n_neighbor
226  istart = cmt%import_index(neib-1)
227  inum = cmt%import_index(neib) - istart
228  if (inum == 0) cycle
229  nreq2 = nreq2 + 1
230 #ifdef _OPENACC
231  !$acc host_data use_device(WR)
232  call hecmw_irecv_r(wr(nb*istart+1:nb*istart+nb*inum), nb*inum, &
233  cmt%neighbor(neib), 0, cmt%comm, req2(nreq2))
234  !$acc end host_data
235 #else
236  call hecmw_irecv_r(wr(nb*istart+1:nb*istart+nb*inum), nb*inum, &
237  cmt%neighbor(neib), 0, cmt%comm, req2(nreq2))
238 #endif
239  end do
240 
241  call hecmw_waitall(nreq2, req2, sta2)
242 
243  ! unpack into halo region
244  do neib = 1, cmt%n_neighbor
245  istart = cmt%import_index(neib-1)
246  inum = cmt%import_index(neib) - istart
247 #ifdef _OPENACC
248  !$acc kernels
249  !$acc loop independent collapse(2)
250  do k = istart+1, istart+inum
251  do kk = 1, nb
252  ii = cmt%import_item(k)
253  x(nb*(ii-1)+kk) = wr(nb*(k-1)+kk)
254  end do
255  end do
256  !$acc end kernels
257 #else
258  do k = istart+1, istart+inum
259  ii = cmt%import_item(k)
260  do kk = 1, nb
261  x(nb*(ii-1)+kk) = wr(nb*(k-1)+kk)
262  end do
263  end do
264 #endif
265  end do
266 
267  call hecmw_waitall(nreq1, req1, sta1)
268 #ifdef _OPENACC
269  call acc_unmap_data(ws)
270  call acc_unmap_data(wr)
271  call acc_free(ws_dev)
272  call acc_free(wr_dev)
273 #endif
274  deallocate(ws, wr, req1, req2, sta1, sta2)
275 #else
276  ! serial: no halo, nothing to exchange
277  if (nb < 0) x(1) = x(1) ! silence unused-arg warnings without effect
278 #endif
279  end subroutine hecmw_saamg_comm_update
280 
286  subroutine hecmw_saamg_comm_reverse_add(cmt, nb, X)
287  type(hecmwst_saamg_comm), intent(in) :: cmt
288  integer(kind=kint), intent(in) :: nb
289  real(kind=kreal), intent(inout) :: x(:)
290 #ifndef HECMW_SERIAL
291  real(kind=kreal), allocatable :: ws(:), wr(:)
292  integer(kind=kint), allocatable :: req1(:), req2(:), sta1(:,:), sta2(:,:)
293  integer(kind=kint) :: neib, istart, inum, k, kk, ii, nreq1, nreq2, ns, nr
294 #ifdef _OPENACC
295  type(c_devptr) :: ws_dev, wr_dev
296 #endif
297 
298  if (cmt%n_neighbor == 0) return
299  ns = cmt%import_index(cmt%n_neighbor) ! sending halo (import) values
300  nr = cmt%export_index(cmt%n_neighbor) ! receiving into owned (export) nodes
301  allocate(ws(nb*ns), wr(nb*nr))
302  allocate(req1(cmt%n_neighbor), req2(cmt%n_neighbor))
303  allocate(sta1(hecmw_status_size, cmt%n_neighbor), sta2(hecmw_status_size, cmt%n_neighbor))
304 #ifdef _OPENACC
305  ws_dev = acc_malloc(kreal * nb * ns)
306  wr_dev = acc_malloc(kreal * nb * nr)
307  call acc_map_data(ws, ws_dev, kreal * nb * ns)
308  call acc_map_data(wr, wr_dev, kreal * nb * nr)
309 #endif
310 
311  nreq1 = 0
312  do neib = 1, cmt%n_neighbor
313  istart = cmt%import_index(neib-1)
314  inum = cmt%import_index(neib) - istart
315  if (inum == 0) cycle
316 #ifdef _OPENACC
317  !$acc kernels
318  !$acc loop independent collapse(2)
319  do k = istart+1, istart+inum
320  do kk = 1, nb
321  ii = cmt%import_item(k)
322  ws(nb*(k-1)+kk) = x(nb*(ii-1)+kk)
323  end do
324  end do
325  !$acc end kernels
326  nreq1 = nreq1 + 1
327  !$acc host_data use_device(WS)
328  call hecmw_isend_r(ws(nb*istart+1:nb*istart+nb*inum), nb*inum, &
329  cmt%neighbor(neib), 2, cmt%comm, req1(nreq1))
330  !$acc end host_data
331 #else
332  do k = istart+1, istart+inum
333  ii = cmt%import_item(k)
334  do kk = 1, nb
335  ws(nb*(k-1)+kk) = x(nb*(ii-1)+kk)
336  end do
337  end do
338  nreq1 = nreq1 + 1
339  call hecmw_isend_r(ws(nb*istart+1:nb*istart+nb*inum), nb*inum, &
340  cmt%neighbor(neib), 2, cmt%comm, req1(nreq1))
341 #endif
342  end do
343  nreq2 = 0
344  do neib = 1, cmt%n_neighbor
345  istart = cmt%export_index(neib-1)
346  inum = cmt%export_index(neib) - istart
347  if (inum == 0) cycle
348  nreq2 = nreq2 + 1
349 #ifdef _OPENACC
350  !$acc host_data use_device(WR)
351  call hecmw_irecv_r(wr(nb*istart+1:nb*istart+nb*inum), nb*inum, &
352  cmt%neighbor(neib), 2, cmt%comm, req2(nreq2))
353  !$acc end host_data
354 #else
355  call hecmw_irecv_r(wr(nb*istart+1:nb*istart+nb*inum), nb*inum, &
356  cmt%neighbor(neib), 2, cmt%comm, req2(nreq2))
357 #endif
358  end do
359  call hecmw_waitall(nreq2, req2, sta2)
360  ! unpack: accumulate halo contributions into owned nodes. Per-neighbor export_item
361  ! values are distinct, so collapse(2) within one neighbor has no += race; different
362  ! neighbors accumulate in separate (sequential) kernels.
363  do neib = 1, cmt%n_neighbor
364  istart = cmt%export_index(neib-1)
365  inum = cmt%export_index(neib) - istart
366 #ifdef _OPENACC
367  !$acc kernels
368  !$acc loop independent collapse(2)
369  do k = istart+1, istart+inum
370  do kk = 1, nb
371  ii = cmt%export_item(k)
372  x(nb*(ii-1)+kk) = x(nb*(ii-1)+kk) + wr(nb*(k-1)+kk)
373  end do
374  end do
375  !$acc end kernels
376 #else
377  do k = istart+1, istart+inum
378  ii = cmt%export_item(k)
379  do kk = 1, nb
380  x(nb*(ii-1)+kk) = x(nb*(ii-1)+kk) + wr(nb*(k-1)+kk)
381  end do
382  end do
383 #endif
384  end do
385  call hecmw_waitall(nreq1, req1, sta1)
386 #ifdef _OPENACC
387  call acc_unmap_data(ws)
388  call acc_unmap_data(wr)
389  call acc_free(ws_dev)
390  call acc_free(wr_dev)
391 #endif
392  deallocate(ws, wr, req1, req2, sta1, sta2)
393 #else
394  if (nb < 0) x(1) = x(1)
395 #endif
396  end subroutine hecmw_saamg_comm_reverse_add
397 
401  subroutine hecmw_saamg_comm_update_i(cmt, nb, IX)
402  type(hecmwst_saamg_comm), intent(in) :: cmt
403  integer(kind=kint), intent(in) :: nb
404  integer(kind=kint), intent(inout) :: ix(:)
405 #ifndef HECMW_SERIAL
406  integer(kind=kint), allocatable :: ws(:), wr(:)
407  integer(kind=kint), allocatable :: req1(:), req2(:)
408  integer(kind=kint), allocatable :: sta1(:,:), sta2(:,:)
409  integer(kind=kint) :: neib, istart, inum, k, kk, ii, nreq1, nreq2, ns, nr
410 
411  if (cmt%n_neighbor == 0) return
412  ns = cmt%export_index(cmt%n_neighbor)
413  nr = cmt%import_index(cmt%n_neighbor)
414  allocate(ws(nb*ns), wr(nb*nr))
415  allocate(req1(cmt%n_neighbor), req2(cmt%n_neighbor))
416  allocate(sta1(hecmw_status_size, cmt%n_neighbor), sta2(hecmw_status_size, cmt%n_neighbor))
417 
418  nreq1 = 0
419  do neib = 1, cmt%n_neighbor
420  istart = cmt%export_index(neib-1)
421  inum = cmt%export_index(neib) - istart
422  if (inum == 0) cycle
423  do k = istart+1, istart+inum
424  ii = cmt%export_item(k)
425  do kk = 1, nb
426  ws(nb*(k-1)+kk) = ix(nb*(ii-1)+kk)
427  end do
428  end do
429  nreq1 = nreq1 + 1
430  call hecmw_isend_int(ws(nb*istart+1:nb*istart+nb*inum), nb*inum, &
431  cmt%neighbor(neib), 0, cmt%comm, req1(nreq1))
432  end do
433  nreq2 = 0
434  do neib = 1, cmt%n_neighbor
435  istart = cmt%import_index(neib-1)
436  inum = cmt%import_index(neib) - istart
437  if (inum == 0) cycle
438  nreq2 = nreq2 + 1
439  call hecmw_irecv_int(wr(nb*istart+1:nb*istart+nb*inum), nb*inum, &
440  cmt%neighbor(neib), 0, cmt%comm, req2(nreq2))
441  end do
442  call hecmw_waitall(nreq2, req2, sta2)
443  do neib = 1, cmt%n_neighbor
444  istart = cmt%import_index(neib-1)
445  inum = cmt%import_index(neib) - istart
446  do k = istart+1, istart+inum
447  ii = cmt%import_item(k)
448  do kk = 1, nb
449  ix(nb*(ii-1)+kk) = wr(nb*(k-1)+kk)
450  end do
451  end do
452  end do
453  call hecmw_waitall(nreq1, req1, sta1)
454  deallocate(ws, wr, req1, req2, sta1, sta2)
455 #else
456  if (nb < 0) ix(1) = ix(1)
457 #endif
458  end subroutine hecmw_saamg_comm_update_i
459 
467  subroutine hecmw_saamg_comm_update_var(cmt, pb, cnt, off, X)
468  type(hecmwst_saamg_comm), intent(in) :: cmt
469  integer(kind=kint), intent(in) :: pb
470  integer(kind=kint), intent(in) :: cnt(:)
471  integer(kind=kint), intent(in) :: off(:)
472  real(kind=kreal), intent(inout) :: x(:)
473 #ifndef HECMW_SERIAL
474  real(kind=kreal), allocatable :: ws(:), wr(:)
475  integer(kind=kint), allocatable :: req1(:), req2(:)
476  integer(kind=kint), allocatable :: sta1(:,:), sta2(:,:)
477  integer(kind=kint), allocatable :: sdisp(:), rdisp(:)
478  integer(kind=kint) :: neib, k, ii, c, o, wp, nreq1, nreq2, b, nblk, astat
479 
480  if (cmt%n_neighbor == 0) return
481  allocate(sdisp(0:cmt%n_neighbor), rdisp(0:cmt%n_neighbor))
482  sdisp(0) = 0; rdisp(0) = 0
483  do neib = 1, cmt%n_neighbor
484  nblk = 0
485  do k = cmt%export_index(neib-1)+1, cmt%export_index(neib)
486  nblk = nblk + cnt(cmt%export_item(k))
487  end do
488  sdisp(neib) = sdisp(neib-1) + nblk
489  nblk = 0
490  do k = cmt%import_index(neib-1)+1, cmt%import_index(neib)
491  nblk = nblk + cnt(cmt%import_item(k))
492  end do
493  rdisp(neib) = rdisp(neib-1) + nblk
494  end do
495  allocate(ws(sdisp(cmt%n_neighbor)*pb), wr(rdisp(cmt%n_neighbor)*pb), stat=astat)
496  call hecmw_saamg_check_alloc(astat, 'comm_update_var (WS/WR)')
497  allocate(req1(cmt%n_neighbor), req2(cmt%n_neighbor))
498  allocate(sta1(hecmw_status_size, cmt%n_neighbor), sta2(hecmw_status_size, cmt%n_neighbor))
499 
500  ! pack + post sends
501  nreq1 = 0
502  do neib = 1, cmt%n_neighbor
503  if (sdisp(neib) == sdisp(neib-1)) cycle
504  wp = sdisp(neib-1)
505  do k = cmt%export_index(neib-1)+1, cmt%export_index(neib)
506  ii = cmt%export_item(k); c = cnt(ii); o = off(ii)
507  do b = 1, c*pb
508  ws(wp*pb+b) = x(o*pb+b)
509  end do
510  wp = wp + c
511  end do
512  nreq1 = nreq1 + 1
513  call hecmw_isend_r(ws(sdisp(neib-1)*pb+1:sdisp(neib)*pb), &
514  (sdisp(neib)-sdisp(neib-1))*pb, cmt%neighbor(neib), 0, cmt%comm, req1(nreq1))
515  end do
516 
517  ! post receives
518  nreq2 = 0
519  do neib = 1, cmt%n_neighbor
520  if (rdisp(neib) == rdisp(neib-1)) cycle
521  nreq2 = nreq2 + 1
522  call hecmw_irecv_r(wr(rdisp(neib-1)*pb+1:rdisp(neib)*pb), &
523  (rdisp(neib)-rdisp(neib-1))*pb, cmt%neighbor(neib), 0, cmt%comm, req2(nreq2))
524  end do
525 
526  call hecmw_waitall(nreq2, req2, sta2)
527 
528  ! unpack into halo region
529  do neib = 1, cmt%n_neighbor
530  wp = rdisp(neib-1)
531  do k = cmt%import_index(neib-1)+1, cmt%import_index(neib)
532  ii = cmt%import_item(k); c = cnt(ii); o = off(ii)
533  do b = 1, c*pb
534  x(o*pb+b) = wr(wp*pb+b)
535  end do
536  wp = wp + c
537  end do
538  end do
539 
540  call hecmw_waitall(nreq1, req1, sta1)
541  deallocate(ws, wr, req1, req2, sta1, sta2, sdisp, rdisp)
542 #else
543  if (pb < 0) x(1) = x(1) ! silence unused-arg warnings without effect
544 #endif
545  end subroutine hecmw_saamg_comm_update_var
546 
550  subroutine hecmw_saamg_comm_update_var_i(cmt, cnt, off, IX)
551  type(hecmwst_saamg_comm), intent(in) :: cmt
552  integer(kind=kint), intent(in) :: cnt(:)
553  integer(kind=kint), intent(in) :: off(:)
554  integer(kind=kint), intent(inout) :: ix(:)
555 #ifndef HECMW_SERIAL
556  integer(kind=kint), allocatable :: ws(:), wr(:)
557  integer(kind=kint), allocatable :: req1(:), req2(:)
558  integer(kind=kint), allocatable :: sta1(:,:), sta2(:,:)
559  integer(kind=kint), allocatable :: sdisp(:), rdisp(:)
560  integer(kind=kint) :: neib, k, ii, c, o, wp, nreq1, nreq2, b, nblk, astat
561 
562  if (cmt%n_neighbor == 0) return
563  allocate(sdisp(0:cmt%n_neighbor), rdisp(0:cmt%n_neighbor))
564  sdisp(0) = 0; rdisp(0) = 0
565  do neib = 1, cmt%n_neighbor
566  nblk = 0
567  do k = cmt%export_index(neib-1)+1, cmt%export_index(neib)
568  nblk = nblk + cnt(cmt%export_item(k))
569  end do
570  sdisp(neib) = sdisp(neib-1) + nblk
571  nblk = 0
572  do k = cmt%import_index(neib-1)+1, cmt%import_index(neib)
573  nblk = nblk + cnt(cmt%import_item(k))
574  end do
575  rdisp(neib) = rdisp(neib-1) + nblk
576  end do
577  allocate(ws(sdisp(cmt%n_neighbor)), wr(rdisp(cmt%n_neighbor)), stat=astat)
578  call hecmw_saamg_check_alloc(astat, 'comm_update_var_i (WS/WR)')
579  allocate(req1(cmt%n_neighbor), req2(cmt%n_neighbor))
580  allocate(sta1(hecmw_status_size, cmt%n_neighbor), sta2(hecmw_status_size, cmt%n_neighbor))
581 
582  nreq1 = 0
583  do neib = 1, cmt%n_neighbor
584  if (sdisp(neib) == sdisp(neib-1)) cycle
585  wp = sdisp(neib-1)
586  do k = cmt%export_index(neib-1)+1, cmt%export_index(neib)
587  ii = cmt%export_item(k); c = cnt(ii); o = off(ii)
588  do b = 1, c
589  ws(wp+b) = ix(o+b)
590  end do
591  wp = wp + c
592  end do
593  nreq1 = nreq1 + 1
594  call hecmw_isend_int(ws(sdisp(neib-1)+1:sdisp(neib)), &
595  sdisp(neib)-sdisp(neib-1), cmt%neighbor(neib), 0, cmt%comm, req1(nreq1))
596  end do
597 
598  nreq2 = 0
599  do neib = 1, cmt%n_neighbor
600  if (rdisp(neib) == rdisp(neib-1)) cycle
601  nreq2 = nreq2 + 1
602  call hecmw_irecv_int(wr(rdisp(neib-1)+1:rdisp(neib)), &
603  rdisp(neib)-rdisp(neib-1), cmt%neighbor(neib), 0, cmt%comm, req2(nreq2))
604  end do
605 
606  call hecmw_waitall(nreq2, req2, sta2)
607 
608  do neib = 1, cmt%n_neighbor
609  wp = rdisp(neib-1)
610  do k = cmt%import_index(neib-1)+1, cmt%import_index(neib)
611  ii = cmt%import_item(k); c = cnt(ii); o = off(ii)
612  do b = 1, c
613  ix(o+b) = wr(wp+b)
614  end do
615  wp = wp + c
616  end do
617  end do
618 
619  call hecmw_waitall(nreq1, req1, sta1)
620  deallocate(ws, wr, req1, req2, sta1, sta2, sdisp, rdisp)
621 #else
622  if (size(cnt) < 0) ix(1) = ix(1) ! silence unused-arg warnings without effect
623 #endif
624  end subroutine hecmw_saamg_comm_update_var_i
625 
627  function hecmw_saamg_comm_size(cmt) result(np)
628  type(hecmwst_saamg_comm), intent(in) :: cmt
629  integer(kind=kint) :: np
630 #ifndef HECMW_SERIAL
631  call hecmw_comm_size(cmt%comm, np)
632 #else
633  np = 1
634  if (cmt%my_rank < 0) np = 1
635 #endif
636  end function hecmw_saamg_comm_size
637 
639  subroutine hecmw_saamg_comm_allgather_int(cmt, sval, rbuf)
640  type(hecmwst_saamg_comm), intent(in) :: cmt
641  integer(kind=kint), intent(in) :: sval
642  integer(kind=kint), intent(out) :: rbuf(:)
643 #ifndef HECMW_SERIAL
644  call hecmw_allgather_int_1(sval, rbuf, cmt%comm)
645 #else
646  rbuf(1) = sval
647 #endif
648  end subroutine hecmw_saamg_comm_allgather_int
649 
652  subroutine hecmw_saamg_comm_exchange_neighbor_int(cmt, sendvals, recvvals)
653  type(hecmwst_saamg_comm), intent(in) :: cmt
654  integer(kind=kint), intent(in) :: sendvals(:)
655  integer(kind=kint), intent(out) :: recvvals(:)
656 #ifndef HECMW_SERIAL
657  integer(kind=kint), allocatable :: req(:), sta(:,:)
658  integer(kind=kint) :: neib, nreq
659  if (cmt%n_neighbor == 0) return
660  allocate(req(2*cmt%n_neighbor), sta(hecmw_status_size, 2*cmt%n_neighbor))
661  nreq = 0
662  do neib = 1, cmt%n_neighbor
663  nreq = nreq + 1
664  call hecmw_irecv_int(recvvals(neib:neib), 1, cmt%neighbor(neib), 1, cmt%comm, req(nreq))
665  end do
666  do neib = 1, cmt%n_neighbor
667  nreq = nreq + 1
668  call hecmw_isend_int(sendvals(neib:neib), 1, cmt%neighbor(neib), 1, cmt%comm, req(nreq))
669  end do
670  call hecmw_waitall(nreq, req, sta)
671  deallocate(req, sta)
672 #else
673  if (size(sendvals) > 0) recvvals = sendvals
674 #endif
676 
679  type(hecmwst_saamg_comm), intent(in) :: cmt
680  real(kind=kreal), intent(inout) :: s
681 #ifndef HECMW_SERIAL
682  real(kind=kreal) :: buf(1)
683  buf(1) = s; call hecmw_allreduce_r_comm(buf, 1, hecmw_max, cmt%comm); s = buf(1)
684 #else
685  if (cmt%my_rank < 0) s = s
686 #endif
687  end subroutine hecmw_saamg_comm_allreduce_max_r
688 
691  type(hecmwst_saamg_comm), intent(in) :: cmt
692  integer(kind=kint), intent(inout) :: n
693 #ifndef HECMW_SERIAL
694  integer(kind=kint) :: buf(1)
695  buf(1) = n; call hecmw_allreduce_i_comm(buf, 1, hecmw_max, cmt%comm); n = buf(1)
696 #else
697  if (cmt%my_rank < 0) n = n
698 #endif
700 
703  type(hecmwst_saamg_comm), intent(in) :: cmt
704  integer(kind=kint), intent(inout) :: n
705 #ifndef HECMW_SERIAL
706  integer(kind=kint) :: buf(1)
707  buf(1) = n; call hecmw_allreduce_i_comm(buf, 1, hecmw_sum, cmt%comm); n = buf(1)
708 #else
709  if (cmt%my_rank < 0) n = n
710 #endif
712 
715  type(hecmwst_saamg_comm), intent(in) :: cmt
716  real(kind=kreal), intent(inout) :: s
717 #ifndef HECMW_SERIAL
718  real(kind=kreal) :: buf(1)
719  buf(1) = s; call hecmw_allreduce_r_comm(buf, 1, hecmw_sum, cmt%comm); s = buf(1)
720 #else
721  if (cmt%my_rank < 0) s = s
722 #endif
723  end subroutine hecmw_saamg_comm_allreduce_sum_r
724 
728  subroutine hecmw_saamg_comm_allgatherv_triplets(cmt, nloc, ti, tj, tv, ntot, gti, gtj, gtv)
729  type(hecmwst_saamg_comm), intent(in) :: cmt
730  integer(kind=kint), intent(in) :: nloc
731  integer(kind=kint), intent(in) :: ti(:), tj(:)
732  real(kind=kreal), intent(in) :: tv(:)
733  integer(kind=kint), intent(out) :: ntot
734  integer(kind=kint), allocatable, intent(out) :: gti(:), gtj(:)
735  real(kind=kreal), allocatable, intent(out) :: gtv(:)
736 #ifndef HECMW_SERIAL
737  integer(kind=kint), allocatable :: counts(:), displs(:)
738  integer(kind=kint) :: nprocs, p
739  call hecmw_comm_size(cmt%comm, nprocs)
740  allocate(counts(nprocs), displs(nprocs))
741  call hecmw_allgather_int_1(nloc, counts, cmt%comm)
742  displs(1) = 0
743  do p = 2, nprocs
744  displs(p) = displs(p-1) + counts(p-1)
745  end do
746  ntot = displs(nprocs) + counts(nprocs)
747  allocate(gti(max(ntot,1)), gtj(max(ntot,1)), gtv(max(ntot,1)))
748  call hecmw_allgatherv_int(ti, nloc, gti, counts, displs, cmt%comm)
749  call hecmw_allgatherv_int(tj, nloc, gtj, counts, displs, cmt%comm)
750  call hecmw_allgatherv_real(tv, nloc, gtv, counts, displs, cmt%comm)
751  deallocate(counts, displs)
752 #else
753  ntot = nloc
754  allocate(gti(max(ntot,1)), gtj(max(ntot,1)), gtv(max(ntot,1)))
755  gti(1:nloc) = ti(1:nloc); gtj(1:nloc) = tj(1:nloc); gtv(1:nloc) = tv(1:nloc)
756 #endif
758 
761  subroutine hecmw_saamg_comm_allgatherv_real(cmt, nloc, vloc, ntot, gv)
762  type(hecmwst_saamg_comm), intent(in) :: cmt
763  integer(kind=kint), intent(in) :: nloc
764  real(kind=kreal), intent(in) :: vloc(:)
765  integer(kind=kint), intent(out) :: ntot
766  real(kind=kreal), allocatable, intent(out) :: gv(:)
767 #ifndef HECMW_SERIAL
768  integer(kind=kint), allocatable :: counts(:), displs(:)
769  integer(kind=kint) :: nprocs, p
770  call hecmw_comm_size(cmt%comm, nprocs)
771  allocate(counts(nprocs), displs(nprocs))
772  call hecmw_allgather_int_1(nloc, counts, cmt%comm)
773  displs(1) = 0
774  do p = 2, nprocs
775  displs(p) = displs(p-1) + counts(p-1)
776  end do
777  ntot = displs(nprocs) + counts(nprocs)
778  allocate(gv(max(ntot,1)))
779  call hecmw_allgatherv_real(vloc, nloc, gv, counts, displs, cmt%comm)
780  deallocate(counts, displs)
781 #else
782  ntot = nloc
783  allocate(gv(max(ntot,1)))
784  gv(1:nloc) = vloc(1:nloc)
785 #endif
786  end subroutine hecmw_saamg_comm_allgatherv_real
787 
790  subroutine hecmw_saamg_comm_alltoall_int(cmt, scnt, rcnt)
791  type(hecmwst_saamg_comm), intent(in) :: cmt
792  integer(kind=kint), intent(in) :: scnt(:)
793  integer(kind=kint), intent(out) :: rcnt(:)
794 #ifndef HECMW_SERIAL
795  call hecmw_alltoall_int(scnt, 1, rcnt, 1, cmt%comm)
796 #else
797  rcnt(1) = scnt(1)
798 #endif
799  end subroutine hecmw_saamg_comm_alltoall_int
800 
804  subroutine hecmw_saamg_comm_alltoallv_int(cmt, scnt, sbuf, ntot, rbuf)
805  type(hecmwst_saamg_comm), intent(in) :: cmt
806  integer(kind=kint), intent(in) :: scnt(:), sbuf(:)
807  integer(kind=kint), intent(out) :: ntot
808  integer(kind=kint), allocatable, intent(out) :: rbuf(:)
809 #ifndef HECMW_SERIAL
810  integer(kind=kint), allocatable :: rcnt(:), sdis(:), rdis(:)
811  integer(kind=kint) :: nprocs, p
812  call hecmw_comm_size(cmt%comm, nprocs)
813  allocate(rcnt(nprocs), sdis(nprocs), rdis(nprocs))
814  call hecmw_alltoall_int(scnt, 1, rcnt, 1, cmt%comm)
815  sdis(1) = 0; rdis(1) = 0
816  do p = 2, nprocs
817  sdis(p) = sdis(p-1) + scnt(p-1); rdis(p) = rdis(p-1) + rcnt(p-1)
818  end do
819  ntot = rdis(nprocs) + rcnt(nprocs)
820  allocate(rbuf(max(ntot,1)))
821  call hecmw_alltoallv_int(sbuf, scnt, sdis, rbuf, rcnt, rdis, cmt%comm)
822  deallocate(rcnt, sdis, rdis)
823 #else
824  ntot = scnt(1); allocate(rbuf(max(ntot,1)))
825  if (ntot > 0) rbuf(1:ntot) = sbuf(1:ntot)
826 #endif
827  end subroutine hecmw_saamg_comm_alltoallv_int
828 
833  subroutine hecmw_saamg_comm_alltoallv_triplets(cmt, scnt, si, sj, sv, ntot, ri, rj, rv)
834  type(hecmwst_saamg_comm), intent(in) :: cmt
835  integer(kind=kint), intent(in) :: scnt(:), si(:), sj(:)
836  real(kind=kreal), intent(in) :: sv(:)
837  integer(kind=kint), intent(out) :: ntot
838  integer(kind=kint), allocatable, intent(out) :: ri(:), rj(:)
839  real(kind=kreal), allocatable, intent(out) :: rv(:)
840 #ifndef HECMW_SERIAL
841  integer(kind=kint), allocatable :: rcnt(:), sdis(:), rdis(:)
842  integer(kind=kint) :: nprocs, p
843  call hecmw_comm_size(cmt%comm, nprocs)
844  allocate(rcnt(nprocs), sdis(nprocs), rdis(nprocs))
845  call hecmw_alltoall_int(scnt, 1, rcnt, 1, cmt%comm)
846  sdis(1) = 0; rdis(1) = 0
847  do p = 2, nprocs
848  sdis(p) = sdis(p-1) + scnt(p-1); rdis(p) = rdis(p-1) + rcnt(p-1)
849  end do
850  ntot = rdis(nprocs) + rcnt(nprocs)
851  allocate(ri(max(ntot,1)), rj(max(ntot,1)), rv(max(ntot,1)))
852  call hecmw_alltoallv_int(si, scnt, sdis, ri, rcnt, rdis, cmt%comm)
853  call hecmw_alltoallv_int(sj, scnt, sdis, rj, rcnt, rdis, cmt%comm)
854  call hecmw_alltoallv_real(sv, scnt, sdis, rv, rcnt, rdis, cmt%comm)
855  deallocate(rcnt, sdis, rdis)
856 #else
857  ntot = scnt(1); allocate(ri(max(ntot,1)), rj(max(ntot,1)), rv(max(ntot,1)))
858  if (ntot > 0) then; ri(1:ntot) = si(1:ntot); rj(1:ntot) = sj(1:ntot); rv(1:ntot) = sv(1:ntot); end if
859 #endif
861 
866  subroutine hecmw_saamg_comm_alltoallv_real(cmt, scnt, sval, ntot, rval)
867  type(hecmwst_saamg_comm), intent(in) :: cmt
868  integer(kind=kint), intent(in) :: scnt(:)
869  real(kind=kreal), intent(in) :: sval(:)
870  integer(kind=kint), intent(out) :: ntot
871  real(kind=kreal), allocatable, intent(out) :: rval(:)
872 #ifndef HECMW_SERIAL
873  integer(kind=kint), allocatable :: rcnt(:), sdis(:), rdis(:)
874  integer(kind=kint) :: nprocs, p
875  call hecmw_comm_size(cmt%comm, nprocs)
876  allocate(rcnt(nprocs), sdis(nprocs), rdis(nprocs))
877  call hecmw_alltoall_int(scnt, 1, rcnt, 1, cmt%comm)
878  sdis(1) = 0; rdis(1) = 0
879  do p = 2, nprocs
880  sdis(p) = sdis(p-1) + scnt(p-1); rdis(p) = rdis(p-1) + rcnt(p-1)
881  end do
882  ntot = rdis(nprocs) + rcnt(nprocs)
883  allocate(rval(max(ntot,1)))
884  call hecmw_alltoallv_real(sval, scnt, sdis, rval, rcnt, rdis, cmt%comm)
885  deallocate(rcnt, sdis, rdis)
886 #else
887  ntot = scnt(1); allocate(rval(max(ntot,1)))
888  if (ntot > 0) rval(1:ntot) = sval(1:ntot)
889 #endif
890  end subroutine hecmw_saamg_comm_alltoallv_real
891 
893  subroutine hecmw_saamg_comm_copy(src, dst)
894  type(hecmwst_saamg_comm), intent(in) :: src
895  type(hecmwst_saamg_comm), intent(out) :: dst
896  integer(kind=kint) :: nnb
897  call hecmw_saamg_comm_free(dst)
898  dst%comm = src%comm; dst%my_rank = src%my_rank
899  dst%nint = src%nint; dst%nnode = src%nnode; dst%nb = src%nb
900  dst%n_neighbor = src%n_neighbor
901  if (allocated(src%gnode)) then
902  allocate(dst%gnode(size(src%gnode))); dst%gnode = src%gnode
903  end if
904  nnb = src%n_neighbor
905  if (nnb == 0) return
906  allocate(dst%neighbor(nnb), dst%import_index(0:nnb), dst%export_index(0:nnb))
907  dst%neighbor = src%neighbor
908  dst%import_index = src%import_index
909  dst%export_index = src%export_index
910  allocate(dst%import_item(size(src%import_item)), dst%export_item(size(src%export_item)))
911  dst%import_item = src%import_item
912  dst%export_item = src%export_item
913  end subroutine hecmw_saamg_comm_copy
914 
915  subroutine hecmw_saamg_comm_free(cmt)
916  type(hecmwst_saamg_comm), intent(inout) :: cmt
917  if (allocated(cmt%neighbor)) deallocate(cmt%neighbor)
918  if (allocated(cmt%import_index)) deallocate(cmt%import_index)
919  if (allocated(cmt%import_item)) deallocate(cmt%import_item)
920  if (allocated(cmt%export_index)) deallocate(cmt%export_index)
921  if (allocated(cmt%export_item)) deallocate(cmt%export_item)
922  if (allocated(cmt%gnode)) deallocate(cmt%gnode)
923  cmt%n_neighbor = 0; cmt%nint = 0; cmt%nnode = 0
924  end subroutine hecmw_saamg_comm_free
925 
926 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)....