FrontISTR  5.9.0
Large-scale structural analysis program with finit element method
hecmw_precond_SAAMG_matrix.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 !-------------------------------------------------------------------------------
17  use hecmw_precond_saamg_comm, only: hecmwst_saamg_comm, hecmw_saamg_comm_update, &
19  implicit none
20 
21  private
22  public :: hecmwst_saamg_bcsr
23  public :: hecmw_saamg_bcsr_free
24  public :: hecmw_saamg_bcsr_copy
25  public :: hecmw_saamg_bcsr_move
31  public :: hecmw_saamg_matvec
32  public :: hecmw_saamg_matvec_d
33  public :: hecmw_saamg_spgemm
35  public :: hecmw_saamg_is_symmetric
36  ! block-CSR kernels (also reachable directly; the names above delegate to these)
37  public :: hecmw_saamg_to_dense_blk
38  public :: hecmw_saamg_matvec_blk
40  public :: hecmw_saamg_spgemm_blk
41  public :: hecmw_saamg_galerkin_blk
42  public :: hecmw_saamg_triple_blk
44 
46  type hecmwst_saamg_bcsr
47  integer(kind=kint) :: n = 0
48  integer(kind=kint) :: ncol = 0
49  integer(kind=kint) :: nb = 0
50  integer(kind=kint) :: mb = 0
51  integer(kind=kint) :: nnz = 0
52  integer(kind=kint) :: nbrow = 0
53  integer(kind=kint) :: nbcol = 0
54  integer(kind=kint) :: nnzb = 0
55  integer(kind=kint), allocatable :: browptr(:)
56  integer(kind=kint), allocatable :: bcol(:)
57  real(kind=kreal), allocatable :: bval(:)
58  end type hecmwst_saamg_bcsr
59 
60 contains
61 
63  subroutine hecmw_saamg_bcsr_free(A)
64  implicit none
65  type(hecmwst_saamg_bcsr), intent(inout) :: a
66  if (allocated(a%browptr)) deallocate(a%browptr)
67  if (allocated(a%bcol)) deallocate(a%bcol)
68  if (allocated(a%bval)) deallocate(a%bval)
69  a%n = 0; a%ncol = 0; a%nb = 0; a%mb = 0; a%nnz = 0
70  a%nbrow = 0; a%nbcol = 0; a%nnzb = 0
71  end subroutine hecmw_saamg_bcsr_free
72 
74  subroutine hecmw_saamg_bcsr_copy(A, B)
75  implicit none
76  type(hecmwst_saamg_bcsr), intent(in) :: a
77  type(hecmwst_saamg_bcsr), intent(out) :: b
78  integer(kind=kint) :: astat
79  call hecmw_saamg_bcsr_free(b)
80  b%n = a%n; b%ncol = a%ncol; b%nb = a%nb; b%mb = a%mb; b%nnz = a%nnz
81  b%nbrow = a%nbrow; b%nbcol = a%nbcol; b%nnzb = a%nnzb
82  if (allocated(a%browptr)) then
83  allocate(b%browptr(a%nbrow+1), b%bcol(a%nnzb), b%bval(a%nb*a%mb*a%nnzb), stat=astat)
84  call hecmw_saamg_check_alloc(astat, 'bcsr_copy (browptr/bcol/bval)')
85  b%browptr = a%browptr; b%bcol = a%bcol; b%bval = a%bval
86  end if
87  end subroutine hecmw_saamg_bcsr_copy
88 
91  subroutine hecmw_saamg_bcsr_move(A, B)
92  implicit none
93  type(hecmwst_saamg_bcsr), intent(inout) :: a
94  type(hecmwst_saamg_bcsr), intent(out) :: b
95  call hecmw_saamg_bcsr_free(b)
96  b%n = a%n; b%ncol = a%ncol; b%nb = a%nb; b%mb = a%mb; b%nnz = a%nnz
97  b%nbrow = a%nbrow; b%nbcol = a%nbcol; b%nnzb = a%nnzb
98  if (allocated(a%browptr)) call move_alloc(a%browptr, b%browptr)
99  if (allocated(a%bcol)) call move_alloc(a%bcol, b%bcol)
100  if (allocated(a%bval)) call move_alloc(a%bval, b%bval)
101  call hecmw_saamg_bcsr_free(a)
102  end subroutine hecmw_saamg_bcsr_move
103 
107  subroutine hecmw_saamg_bcsr_from_dense(dense, n, nb, A, mb)
108  implicit none
109  integer(kind=kint), intent(in) :: n, nb
110  real(kind=kreal), intent(in) :: dense(n,n)
111  type(hecmwst_saamg_bcsr), intent(out) :: a
112  integer(kind=kint), optional, intent(in) :: mb
113  real(kind=kreal), allocatable :: blk(:)
114  integer(kind=kint) :: mb_, nbrow, nbcol, bs, i, j, ii, jj, kout, rbase, cbase
115  logical :: any_nz
116 
117  mb_ = nb; if (present(mb)) mb_ = mb
118  call hecmw_saamg_bcsr_free(a)
119  a%n = n; a%ncol = n; a%nb = nb; a%mb = mb_
120  nbrow = n / nb; nbcol = n / mb_; bs = nb*mb_
121  a%nbrow = nbrow; a%nbcol = nbcol
122  allocate(a%browptr(nbrow+1), blk(bs))
123 
124  ! pass 1: count nonzero blocks per block row
125  a%browptr(1) = 1; a%nnzb = 0
126  do i = 1, nbrow
127  do j = 1, nbcol
128  if (block_nonzero(i, j)) a%nnzb = a%nnzb + 1
129  end do
130  a%browptr(i+1) = a%nnzb + 1
131  end do
132  allocate(a%bcol(a%nnzb), a%bval(bs*a%nnzb)); a%nnz = bs*a%nnzb
133 
134  ! pass 2: copy each nonzero block
135  kout = 0
136  do i = 1, nbrow
137  rbase = (i-1)*nb
138  do j = 1, nbcol
139  cbase = (j-1)*mb_; any_nz = .false.
140  do jj = 1, mb_
141  do ii = 1, nb
142  blk((jj-1)*nb+ii) = dense(rbase+ii, cbase+jj)
143  if (blk((jj-1)*nb+ii) /= 0.0d0) any_nz = .true.
144  end do
145  end do
146  if (any_nz) then
147  kout = kout + 1; a%bcol(kout) = j
148  a%bval((kout-1)*bs+1:kout*bs) = blk(1:bs)
149  end if
150  end do
151  end do
152  deallocate(blk)
153  contains
154  logical function block_nonzero(ib, jb) result(nz)
155  integer(kind=kint), intent(in) :: ib, jb
156  integer(kind=kint) :: a, b
157  nz = .false.
158  do b = 1, mb_
159  do a = 1, nb
160  if (dense((ib-1)*nb+a, (jb-1)*mb_+b) /= 0.0d0) then; nz = .true.; return; end if
161  end do
162  end do
163  end function block_nonzero
164  end subroutine hecmw_saamg_bcsr_from_dense
165 
167  subroutine hecmw_saamg_matvec(A, x, y)
168  implicit none
169  type(hecmwst_saamg_bcsr), intent(in) :: a
170  real(kind=kreal), intent(in) :: x(:)
171  real(kind=kreal), intent(out) :: y(:)
172  call hecmw_saamg_matvec_blk(a, x, y)
173  end subroutine hecmw_saamg_matvec
174 
179  subroutine hecmw_saamg_matvec_d(cmt, A, x, y)
180  implicit none
181  type(hecmwst_saamg_comm), intent(in) :: cmt
182  type(hecmwst_saamg_bcsr), intent(in) :: a
183  real(kind=kreal), intent(inout) :: x(:)
184  real(kind=kreal), intent(out) :: y(:)
185  call hecmw_saamg_comm_update(cmt, a%nb, x)
186  call hecmw_saamg_matvec(a, x, y)
187  end subroutine hecmw_saamg_matvec_d
188 
191  function hecmw_saamg_is_symmetric(A) result(rel)
192  implicit none
193  type(hecmwst_saamg_bcsr), intent(in) :: a
194  real(kind=kreal) :: rel
195  real(kind=kreal) :: nrm, dif, aij, aji
196  integer(kind=kint) :: i, j, t, tt, ii, jj, m, boff, foff
197  logical :: found
198 
199  m = a%nb ! square operator: nb == mb
200  nrm = 0.0d0; dif = 0.0d0
201  do i = 1, a%nbrow
202  do t = a%browptr(i), a%browptr(i+1)-1
203  j = a%bcol(t); boff = (t-1)*m*m
204  ! locate the transposed block A(j,i)
205  found = .false.; foff = 0
206  do tt = a%browptr(j), a%browptr(j+1)-1
207  if (a%bcol(tt) == i) then; found = .true.; foff = (tt-1)*m*m; exit; end if
208  end do
209  do jj = 1, m
210  do ii = 1, m
211  aij = a%bval(boff + (jj-1)*m + ii)
212  nrm = nrm + aij*aij
213  aji = 0.0d0
214  if (found) aji = a%bval(foff + (ii-1)*m + jj) ! A(j,i) entry (jj,ii)
215  dif = dif + (aij - aji)**2
216  end do
217  end do
218  end do
219  end do
220  if (nrm > 0.0d0) then
221  rel = sqrt(dif) / sqrt(nrm)
222  else
223  rel = 0.0d0
224  end if
225  end function hecmw_saamg_is_symmetric
226 
231  subroutine hecmw_saamg_bcsr_from_triplets(nrow, ncol, nb, ti, tj, tv, nt, A, mb)
232  implicit none
233  integer(kind=kint), intent(in) :: nrow, ncol, nb, nt
234  integer(kind=kint), intent(in) :: ti(:), tj(:)
235  real(kind=kreal), intent(in) :: tv(:)
236  type(hecmwst_saamg_bcsr), intent(out) :: a
237  integer(kind=kint), optional, intent(in) :: mb
238  integer(kind=kint), allocatable :: rcnt(:), rpos(:), jtmp(:), otmp(:), mark(:), touched(:)
239  real(kind=kreal), allocatable :: vtmp(:)
240  integer(kind=kint) :: mb_, nbrow, nbcol, bs, t, i, br, rr, bc, cc, j, p, q, ntouch, kout, blk, astat
241 
242  mb_ = nb; if (present(mb)) mb_ = mb
243  call hecmw_saamg_bcsr_free(a)
244  a%n = nrow; a%ncol = ncol; a%nb = nb; a%mb = mb_
245  nbrow = nrow / nb; nbcol = ncol / mb_; bs = nb*mb_
246  a%nbrow = nbrow; a%nbcol = nbcol
247 
248  ! bucket triplets by block row, storing (block col, in-block offset, value)
249  allocate(rcnt(nbrow+1)); rcnt = 0
250  do t = 1, nt
251  br = (ti(t)-1)/nb + 1; rcnt(br+1) = rcnt(br+1) + 1
252  end do
253  do i = 1, nbrow
254  rcnt(i+1) = rcnt(i+1) + rcnt(i)
255  end do
256  allocate(jtmp(nt), otmp(nt), vtmp(nt), rpos(nbrow), stat=astat)
257  call hecmw_saamg_check_alloc(astat, 'bcsr_from_triplets (jtmp/otmp/vtmp)')
258  do i = 1, nbrow
259  rpos(i) = rcnt(i)
260  end do
261  do t = 1, nt
262  br = (ti(t)-1)/nb + 1; rr = ti(t) - (br-1)*nb
263  bc = (tj(t)-1)/mb_ + 1; cc = tj(t) - (bc-1)*mb_
264  rpos(br) = rpos(br) + 1
265  jtmp(rpos(br)) = bc; otmp(rpos(br)) = (cc-1)*nb + rr; vtmp(rpos(br)) = tv(t)
266  end do
267 
268  ! count distinct block columns per block row
269  allocate(mark(nbcol), touched(nbcol)); mark = 0
270  a%nnzb = 0
271  do i = 1, nbrow
272  ntouch = 0
273  do p = rcnt(i)+1, rcnt(i+1)
274  if (mark(jtmp(p)) == 0) then
275  ntouch = ntouch + 1; touched(ntouch) = jtmp(p); mark(jtmp(p)) = 1
276  end if
277  end do
278  a%nnzb = a%nnzb + ntouch
279  do q = 1, ntouch
280  mark(touched(q)) = 0
281  end do
282  end do
283 
284  allocate(a%browptr(nbrow+1), a%bcol(a%nnzb), a%bval(bs*a%nnzb), stat=astat)
285  call hecmw_saamg_check_alloc(astat, 'from_triplets (bcol/bval)')
286  a%bval = 0.0d0; a%browptr(1) = 1; kout = 0
287  do i = 1, nbrow
288  ntouch = 0
289  do p = rcnt(i)+1, rcnt(i+1)
290  j = jtmp(p)
291  if (mark(j) == 0) then
292  ntouch = ntouch + 1; touched(ntouch) = j; mark(j) = ntouch
293  kout = kout + 1; a%bcol(kout) = j
294  end if
295  blk = kout - ntouch + mark(j)
296  a%bval((blk-1)*bs + otmp(p)) = a%bval((blk-1)*bs + otmp(p)) + vtmp(p)
297  end do
298  do q = 1, ntouch
299  mark(touched(q)) = 0
300  end do
301  a%browptr(i+1) = kout + 1
302  end do
303  a%nnz = bs*a%nnzb
304 
305  deallocate(rcnt, rpos, jtmp, otmp, vtmp, mark, touched)
306  end subroutine hecmw_saamg_bcsr_from_triplets
307 
312  subroutine hecmw_saamg_bcsr_from_block_triplets(nbrow, nbcol, nb, mb, bi, bj, bv, nt, A)
313  implicit none
314  integer(kind=kint), intent(in) :: nbrow, nbcol, nb, mb, nt
315  integer(kind=kint), intent(in) :: bi(:), bj(:)
316  real(kind=kreal), intent(in) :: bv(:,:) ! (nb*mb, nt)
317  type(hecmwst_saamg_bcsr), intent(out) :: a
318  integer(kind=kint), allocatable :: rcnt(:), rpos(:), btmp(:), mark(:), touched(:)
319  real(kind=kreal), allocatable :: vtmp(:,:)
320  integer(kind=kint) :: t, i, j, p, q, r, ntouch, kout, bs, astat
321 
322  bs = nb*mb
323  call hecmw_saamg_bcsr_free(a)
324  a%n = nbrow*nb; a%ncol = nbcol*mb; a%nb = nb; a%mb = mb
325  a%nbrow = nbrow; a%nbcol = nbcol
326 
327  ! bucket triplets by block row (unsorted, with duplicates)
328  allocate(rcnt(nbrow+1)); rcnt = 0
329  do t = 1, nt
330  rcnt(bi(t)+1) = rcnt(bi(t)+1) + 1
331  end do
332  do i = 1, nbrow
333  rcnt(i+1) = rcnt(i+1) + rcnt(i)
334  end do
335  allocate(btmp(nt), vtmp(bs, nt), rpos(nbrow), stat=astat)
336  call hecmw_saamg_check_alloc(astat, 'bcsr_from_block_triplets (btmp/vtmp)')
337  do i = 1, nbrow
338  rpos(i) = rcnt(i)
339  end do
340  do t = 1, nt
341  i = bi(t)
342  rpos(i) = rpos(i) + 1
343  btmp(rpos(i)) = bj(t)
344  vtmp(1:bs, rpos(i)) = bv(1:bs, t)
345  end do
346 
347  ! count distinct block columns per row
348  allocate(mark(nbcol), touched(nbcol)); mark = 0
349  a%nnzb = 0
350  do i = 1, nbrow
351  ntouch = 0
352  do p = rcnt(i)+1, rcnt(i+1)
353  if (mark(btmp(p)) == 0) then
354  ntouch = ntouch + 1; touched(ntouch) = btmp(p); mark(btmp(p)) = 1
355  end if
356  end do
357  a%nnzb = a%nnzb + ntouch
358  do q = 1, ntouch
359  mark(touched(q)) = 0
360  end do
361  end do
362 
363  allocate(a%browptr(nbrow+1), a%bcol(a%nnzb), a%bval(bs*a%nnzb), stat=astat)
364  call hecmw_saamg_check_alloc(astat, 'from_triplets (bcol/bval)')
365  a%bval = 0.0d0; a%browptr(1) = 1; kout = 0
366  do i = 1, nbrow
367  ntouch = 0
368  do p = rcnt(i)+1, rcnt(i+1)
369  j = btmp(p)
370  if (mark(j) == 0) then
371  ntouch = ntouch + 1; touched(ntouch) = j; mark(j) = ntouch
372  kout = kout + 1
373  a%bcol(kout) = j
374  a%bval((kout-1)*bs+1:kout*bs) = vtmp(1:bs, p)
375  else
376  r = kout - ntouch + mark(j)
377  a%bval((r-1)*bs+1:r*bs) = a%bval((r-1)*bs+1:r*bs) + vtmp(1:bs, p)
378  end if
379  end do
380  do q = 1, ntouch
381  mark(touched(q)) = 0
382  end do
383  a%browptr(i+1) = kout + 1
384  end do
385  a%nnz = bs*a%nnzb
386 
387  deallocate(rcnt, rpos, btmp, vtmp, mark, touched)
389 
391  subroutine hecmw_saamg_bcsr_transpose(A, At)
392  implicit none
393  type(hecmwst_saamg_bcsr), intent(in) :: a
394  type(hecmwst_saamg_bcsr), intent(out) :: at
395  call hecmw_saamg_transpose_blk(a, at)
396  end subroutine hecmw_saamg_bcsr_transpose
397 
399  subroutine hecmw_saamg_spgemm(A, B, C)
400  implicit none
401  type(hecmwst_saamg_bcsr), intent(in) :: a, b
402  type(hecmwst_saamg_bcsr), intent(out) :: c
403  call hecmw_saamg_spgemm_blk(a, b, c)
404  end subroutine hecmw_saamg_spgemm
405 
407  subroutine hecmw_saamg_galerkin_local(A, P, mblk, Ac)
408  implicit none
409  type(hecmwst_saamg_bcsr), intent(in) :: a, p
410  integer(kind=kint), intent(in) :: mblk
411  type(hecmwst_saamg_bcsr), intent(out) :: ac
412  call hecmw_saamg_galerkin_blk(a, p, mblk, ac)
413  end subroutine hecmw_saamg_galerkin_local
414 
416  subroutine hecmw_saamg_bcsr_to_dense(A, dense)
417  implicit none
418  type(hecmwst_saamg_bcsr), intent(in) :: a
419  real(kind=kreal), intent(out) :: dense(:,:)
420  call hecmw_saamg_to_dense_blk(a, dense)
421  end subroutine hecmw_saamg_bcsr_to_dense
422 
423  !-----------------------------------------------------------------------------
424  ! block-CSR kernels
425  !-----------------------------------------------------------------------------
426 
428  subroutine hecmw_saamg_to_dense_blk(A, dense)
429  implicit none
430  type(hecmwst_saamg_bcsr), intent(in) :: a
431  real(kind=kreal), intent(out) :: dense(:,:)
432  integer(kind=kint) :: i, t, j, ii, jj, rbase, cbase, boff
433  dense = 0.0d0
434  do i = 1, a%nbrow
435  rbase = (i-1)*a%nb
436  do t = a%browptr(i), a%browptr(i+1)-1
437  j = a%bcol(t); cbase = (j-1)*a%mb; boff = (t-1)*a%nb*a%mb
438  do jj = 1, a%mb
439  do ii = 1, a%nb
440  dense(rbase+ii, cbase+jj) = dense(rbase+ii, cbase+jj) + a%bval(boff+(jj-1)*a%nb+ii)
441  end do
442  end do
443  end do
444  end do
445  end subroutine hecmw_saamg_to_dense_blk
446 
448  subroutine hecmw_saamg_matvec_blk(A, x, y)
449  implicit none
450  type(hecmwst_saamg_bcsr), intent(in) :: a
451  real(kind=kreal), intent(in) :: x(:)
452  real(kind=kreal), intent(out) :: y(:)
453  integer(kind=kint) :: i, t, j, ii, jj, rbase, cbase, boff, nb, mb
454  real(kind=kreal) :: xj
455  nb = a%nb; mb = a%mb
456  ! GPU (OpenACC) vs CPU (OpenMP) selected by #ifdef _OPENACC (mutually exclusive,
457  ! so a build with both -acc and -mp is safe -- matches upstream las_33.F90).
458  ! Arrays are unified (-gpu=mem:managed) so no data clauses; inner block-GEMV loops
459  ! stay seq (accumulation, bitwise-stable).
460 #ifdef _OPENACC
461  !$acc parallel loop gang vector private(t,j,ii,jj,rbase,cbase,boff,xj)
462 #else
463  !$omp parallel do default(none) shared(A, x, y, nb, mb) private(i,t,j,ii,jj,rbase,cbase,boff,xj)
464 #endif
465  do i = 1, a%nbrow
466  rbase = (i-1)*nb
467  y(rbase+1:rbase+nb) = 0.0d0
468  !$acc loop seq
469  do t = a%browptr(i), a%browptr(i+1)-1
470  j = a%bcol(t); cbase = (j-1)*mb; boff = (t-1)*nb*mb
471  !$acc loop seq
472  do jj = 1, mb
473  xj = x(cbase+jj)
474  !$acc loop seq
475  do ii = 1, nb
476  y(rbase+ii) = y(rbase+ii) + a%bval(boff+(jj-1)*nb+ii) * xj
477  end do
478  end do
479  end do
480  end do
481 #ifndef _OPENACC
482  !$omp end parallel do
483 #endif
484  end subroutine hecmw_saamg_matvec_blk
485 
487  subroutine hecmw_saamg_transpose_blk(A, At)
488  implicit none
489  type(hecmwst_saamg_bcsr), intent(in) :: a
490  type(hecmwst_saamg_bcsr), intent(out) :: at
491  integer(kind=kint), allocatable :: pos(:)
492  integer(kind=kint) :: i, t, j, ii, jj, nb, mb, p, boff, aoff, astat
493  nb = a%nb; mb = a%mb
494  call hecmw_saamg_bcsr_free(at)
495  at%n = a%ncol; at%ncol = a%n; at%nb = mb; at%mb = nb
496  at%nbrow = a%nbcol; at%nbcol = a%nbrow; at%nnzb = a%nnzb; at%nnz = a%nnz
497  allocate(at%browptr(at%nbrow+1), at%bcol(at%nnzb), at%bval(nb*mb*at%nnzb), pos(at%nbrow), stat=astat)
498  call hecmw_saamg_check_alloc(astat, 'transpose_blk (At)')
499  at%browptr = 0
500  do t = 1, a%nnzb
501  at%browptr(a%bcol(t)+1) = at%browptr(a%bcol(t)+1) + 1
502  end do
503  at%browptr(1) = 1
504  do j = 1, at%nbrow
505  at%browptr(j+1) = at%browptr(j+1) + at%browptr(j)
506  end do
507  do j = 1, at%nbrow
508  pos(j) = at%browptr(j)
509  end do
510  do i = 1, a%nbrow
511  do t = a%browptr(i), a%browptr(i+1)-1
512  j = a%bcol(t); p = pos(j)
513  at%bcol(p) = i
514  boff = (p-1)*mb*nb; aoff = (t-1)*nb*mb
515  do jj = 1, mb ! A block (ii,jj) -> At block (jj,ii)
516  do ii = 1, nb
517  at%bval(boff + (ii-1)*mb + jj) = a%bval(aoff + (jj-1)*nb + ii)
518  end do
519  end do
520  pos(j) = pos(j) + 1
521  end do
522  end do
523  deallocate(pos)
524  end subroutine hecmw_saamg_transpose_blk
525 
528  subroutine hecmw_saamg_spgemm_blk(A, B, C)
529  implicit none
530  type(hecmwst_saamg_bcsr), intent(in) :: a, b
531  type(hecmwst_saamg_bcsr), intent(out) :: c
532  integer(kind=kint), allocatable :: mark(:), clist(:)
533  real(kind=kreal), allocatable :: spa(:,:)
534  integer(kind=kint) :: rb, kb, cb, ncb, i, ta, kk, tb, l, ii, jj, pp, cnt, nci, t, pos
535  integer(kind=kint) :: aoff, boff, coff, astat
536  real(kind=kreal) :: bkl
537 
538  if (a%mb /= b%nb .or. a%nbcol /= b%nbrow) then
539  write(*,*) 'hecmw_saamg_spgemm_blk: inner dim mismatch (mb,nbcol)=', a%mb, a%nbcol, &
540  ' vs (nb,nbrow)=', b%nb, b%nbrow
541  call hecmw_saamg_abort('spgemm_blk: inner dimension mismatch')
542  end if
543  rb = a%nb; kb = a%mb; cb = b%mb; ncb = b%nbcol
544  call hecmw_saamg_bcsr_free(c)
545  c%n = a%n; c%ncol = b%ncol; c%nb = rb; c%mb = cb
546  c%nbrow = a%nbrow; c%nbcol = ncb
547  allocate(c%browptr(a%nbrow+1), stat=astat)
548  call hecmw_saamg_check_alloc(astat, 'spgemm_blk (browptr)')
549 
550  ! pass 1: count nonzero blocks per row (rows independent; per-thread mark). The
551  ! mark(l)/=i trick avoids re-zeroing: mark(l) holds the last row that touched l
552  ! (distinct per row), so it works unchanged with a thread-private mark.
553  !$omp parallel default(none) shared(A, B, C, ncb) private(mark, i, cnt, ta, kk, tb, l) if(A%nbrow > 256)
554  allocate(mark(ncb)); mark = 0
555  !$omp do
556  do i = 1, a%nbrow
557  cnt = 0
558  do ta = a%browptr(i), a%browptr(i+1)-1
559  kk = a%bcol(ta)
560  do tb = b%browptr(kk), b%browptr(kk+1)-1
561  l = b%bcol(tb)
562  if (mark(l) /= i) then; mark(l) = i; cnt = cnt + 1; end if
563  end do
564  end do
565  c%browptr(i+1) = cnt
566  end do
567  !$omp end do
568  deallocate(mark)
569  !$omp end parallel
570 
571  c%browptr(1) = 1 ! prefix-sum counts -> row pointers
572  do i = 1, a%nbrow
573  c%browptr(i+1) = c%browptr(i) + c%browptr(i+1)
574  end do
575  c%nnzb = c%browptr(a%nbrow+1) - 1
576  allocate(c%bcol(c%nnzb), c%bval(rb*cb*c%nnzb), stat=astat)
577  call hecmw_saamg_check_alloc(astat, 'spgemm_blk (C bcol/bval)')
578  c%nnz = rb*cb*c%nnzb
579 
580  ! pass 2: accumulate block products (rows independent; each row writes its own
581  ! browptr(i)..browptr(i+1)-1 slice; per-thread mark/clist/spa accumulator)
582  !$omp parallel default(none) shared(A, B, C, rb, kb, cb, ncb) if(A%nbrow > 256) &
583  !$omp& private(mark, clist, spa, i, nci, ta, kk, aoff, tb, l, boff, jj, pp, bkl, ii, t, pos, coff)
584  allocate(mark(ncb), clist(ncb), spa(rb*cb, ncb)); mark = 0
585  !$omp do
586  do i = 1, a%nbrow
587  nci = 0
588  do ta = a%browptr(i), a%browptr(i+1)-1
589  kk = a%bcol(ta); aoff = (ta-1)*rb*kb
590  do tb = b%browptr(kk), b%browptr(kk+1)-1
591  l = b%bcol(tb); boff = (tb-1)*kb*cb
592  if (mark(l) /= i) then
593  mark(l) = i; nci = nci + 1; clist(nci) = l; spa(1:rb*cb, l) = 0.0d0
594  end if
595  do jj = 1, cb
596  do pp = 1, kb
597  bkl = b%bval(boff + (jj-1)*kb + pp)
598  if (bkl == 0.0d0) cycle
599  do ii = 1, rb
600  spa((jj-1)*rb+ii, l) = spa((jj-1)*rb+ii, l) + a%bval(aoff+(pp-1)*rb+ii) * bkl
601  end do
602  end do
603  end do
604  end do
605  end do
606  pos = c%browptr(i) - 1
607  do t = 1, nci
608  pos = pos + 1
609  l = clist(t)
610  c%bcol(pos) = l
611  coff = (pos-1)*rb*cb
612  c%bval(coff+1:coff+rb*cb) = spa(1:rb*cb, l)
613  end do
614  end do
615  !$omp end do
616  deallocate(mark, clist, spa)
617  !$omp end parallel
618  end subroutine hecmw_saamg_spgemm_blk
619 
622  subroutine hecmw_saamg_galerkin_blk(A, P, mblk, Ac)
623  implicit none
624  type(hecmwst_saamg_bcsr), intent(in) :: a, p
625  integer(kind=kint), intent(in) :: mblk
626  type(hecmwst_saamg_bcsr), intent(out) :: ac
627  type(hecmwst_saamg_bcsr) :: pt, c
628  call hecmw_saamg_transpose_blk(p, pt) ! Pt = P^T
629  call hecmw_saamg_spgemm_blk(a, p, c) ! C = A P
630  call hecmw_saamg_spgemm_blk(pt, c, ac) ! Ac = P^T C
631  ac%nb = mblk; ac%mb = mblk
632  call hecmw_saamg_bcsr_free(pt)
633  call hecmw_saamg_bcsr_free(c)
634  end subroutine hecmw_saamg_galerkin_blk
635 
639  subroutine hecmw_saamg_transpose_blk_rows(A, nbrow_keep, At)
640  implicit none
641  type(hecmwst_saamg_bcsr), intent(in) :: a
642  integer(kind=kint), intent(in) :: nbrow_keep
643  type(hecmwst_saamg_bcsr), intent(out) :: at
644  integer(kind=kint), allocatable :: pos(:)
645  integer(kind=kint) :: i, t, j, ii, jj, nb, mb, p, boff, aoff, nnzb, astat
646  nb = a%nb; mb = a%mb
647  nnzb = a%browptr(nbrow_keep+1) - 1
648  call hecmw_saamg_bcsr_free(at)
649  at%n = a%ncol; at%ncol = nbrow_keep*nb; at%nb = mb; at%mb = nb
650  at%nbrow = a%nbcol; at%nbcol = nbrow_keep; at%nnzb = nnzb; at%nnz = nnzb*nb*mb
651  allocate(at%browptr(at%nbrow+1), at%bcol(nnzb), at%bval(nb*mb*nnzb), pos(at%nbrow), stat=astat)
652  call hecmw_saamg_check_alloc(astat, 'transpose_blk_rows (At)')
653  at%browptr = 0
654  do t = 1, nnzb
655  at%browptr(a%bcol(t)+1) = at%browptr(a%bcol(t)+1) + 1
656  end do
657  at%browptr(1) = 1
658  do j = 1, at%nbrow
659  at%browptr(j+1) = at%browptr(j+1) + at%browptr(j)
660  end do
661  do j = 1, at%nbrow
662  pos(j) = at%browptr(j)
663  end do
664  do i = 1, nbrow_keep
665  do t = a%browptr(i), a%browptr(i+1)-1
666  j = a%bcol(t); p = pos(j)
667  at%bcol(p) = i
668  boff = (p-1)*mb*nb; aoff = (t-1)*nb*mb
669  do jj = 1, mb
670  do ii = 1, nb
671  at%bval(boff + (ii-1)*mb + jj) = a%bval(aoff + (jj-1)*nb + ii)
672  end do
673  end do
674  pos(j) = pos(j) + 1
675  end do
676  end do
677  deallocate(pos)
678  end subroutine hecmw_saamg_transpose_blk_rows
679 
686  subroutine hecmw_saamg_triple_blk(L, A, R, Ac)
687  implicit none
688  type(hecmwst_saamg_bcsr), intent(in) :: l, a, r
689  type(hecmwst_saamg_bcsr), intent(out) :: ac
690  integer(kind=kint), allocatable :: mark(:), clist(:)
691  real(kind=kreal), allocatable :: spa(:,:), la(:)
692  integer(kind=kint) :: rb, k1, k2, cb, ncr, i, jt, j, kt, k, lt, ll, p, q, rr, c
693  integer(kind=kint) :: cnt, nci, t, pos, loff, aoff, roff, coff, astat
694  real(kind=kreal) :: av, rv
695 
696  if (l%mb /= a%nb .or. a%mb /= r%nb .or. l%nbcol /= a%nbrow .or. a%nbcol /= r%nbrow) then
697  call hecmw_saamg_abort('triple_blk: dimension mismatch')
698  end if
699  rb = l%nb; k1 = l%mb; k2 = a%mb; cb = r%mb; ncr = r%nbcol
700  call hecmw_saamg_bcsr_free(ac)
701  ac%n = l%n; ac%ncol = r%ncol; ac%nb = rb; ac%mb = cb
702  ac%nbrow = l%nbrow; ac%nbcol = ncr
703  allocate(ac%browptr(l%nbrow+1), stat=astat)
704  call hecmw_saamg_check_alloc(astat, 'triple_blk (browptr)')
705 
706  ! pass 1: count nonzero blocks per row of Ac (rows independent; per-thread mark)
707  !$omp parallel default(none) shared(L, A, R, Ac, ncr) private(mark, i, cnt, jt, j, kt, k, lt, ll) if(L%nbrow > 256)
708  allocate(mark(ncr)); mark = 0
709  !$omp do
710  do i = 1, l%nbrow
711  cnt = 0
712  do jt = l%browptr(i), l%browptr(i+1)-1
713  j = l%bcol(jt)
714  do kt = a%browptr(j), a%browptr(j+1)-1
715  k = a%bcol(kt)
716  do lt = r%browptr(k), r%browptr(k+1)-1
717  ll = r%bcol(lt)
718  if (mark(ll) /= i) then; mark(ll) = i; cnt = cnt + 1; end if
719  end do
720  end do
721  end do
722  ac%browptr(i+1) = cnt
723  end do
724  !$omp end do
725  deallocate(mark)
726  !$omp end parallel
727 
728  ac%browptr(1) = 1 ! prefix-sum counts -> row pointers
729  do i = 1, l%nbrow
730  ac%browptr(i+1) = ac%browptr(i) + ac%browptr(i+1)
731  end do
732  ac%nnzb = ac%browptr(l%nbrow+1) - 1
733  allocate(ac%bcol(ac%nnzb), ac%bval(rb*cb*ac%nnzb), stat=astat); ac%nnz = rb*cb*ac%nnzb
734  call hecmw_saamg_check_alloc(astat, 'triple_blk (Ac bcol/bval)')
735 
736  ! pass 2: accumulate L(i,j) A(j,k) R(k,ll) into Ac(i,ll) (rows independent;
737  ! each row writes its own slice; per-thread mark/clist/spa/la scratch)
738  !$omp parallel default(none) shared(L, A, R, Ac, rb, k1, k2, cb, ncr) if(L%nbrow > 256) &
739  !$omp& private(mark, clist, spa, la, i, nci, jt, j, loff, kt, k, aoff, q, p, av, rr, &
740  !$omp& lt, ll, roff, c, rv, t, pos, coff)
741  allocate(mark(ncr), clist(ncr), spa(rb*cb, ncr), la(rb*k2)); mark = 0
742  !$omp do
743  do i = 1, l%nbrow
744  nci = 0
745  do jt = l%browptr(i), l%browptr(i+1)-1
746  j = l%bcol(jt); loff = (jt-1)*rb*k1
747  do kt = a%browptr(j), a%browptr(j+1)-1
748  k = a%bcol(kt); aoff = (kt-1)*k1*k2
749  ! la = L(i,j) * A(j,k) (rb x k1) * (k1 x k2) = rb x k2
750  la(1:rb*k2) = 0.0d0
751  do q = 1, k2
752  do p = 1, k1
753  av = a%bval(aoff + (q-1)*k1 + p)
754  if (av == 0.0d0) cycle
755  do rr = 1, rb
756  la((q-1)*rb+rr) = la((q-1)*rb+rr) + l%bval(loff+(p-1)*rb+rr) * av
757  end do
758  end do
759  end do
760  ! spa(:,ll) += la * R(k,ll) (rb x k2) * (k2 x cb) = rb x cb
761  do lt = r%browptr(k), r%browptr(k+1)-1
762  ll = r%bcol(lt); roff = (lt-1)*k2*cb
763  if (mark(ll) /= i) then
764  mark(ll) = i; nci = nci + 1; clist(nci) = ll; spa(1:rb*cb, ll) = 0.0d0
765  end if
766  do c = 1, cb
767  do q = 1, k2
768  rv = r%bval(roff + (c-1)*k2 + q)
769  if (rv == 0.0d0) cycle
770  do rr = 1, rb
771  spa((c-1)*rb+rr, ll) = spa((c-1)*rb+rr, ll) + la((q-1)*rb+rr) * rv
772  end do
773  end do
774  end do
775  end do
776  end do
777  end do
778  pos = ac%browptr(i) - 1
779  do t = 1, nci
780  pos = pos + 1
781  ll = clist(t); ac%bcol(pos) = ll
782  coff = (pos-1)*rb*cb
783  ac%bval(coff+1:coff+rb*cb) = spa(1:rb*cb, ll)
784  end do
785  end do
786  !$omp end do
787  deallocate(mark, clist, spa, la)
788  !$omp end parallel
789  end subroutine hecmw_saamg_triple_blk
790 
Smoothed Aggregation AMG preconditioner : lightweight comm table.
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_abort(msg)
Fatal-error termination with a clear message. Under MPI this is a COLLECTIVE abort (hecmw_abort -> MP...
Smoothed Aggregation AMG preconditioner : internal block-CSR matrix.
subroutine, public hecmw_saamg_matvec_d(cmt, A, x, y)
Distributed matvec y = A x : refresh x's halo region from the owning ranks (via the level comm table)...
subroutine, public hecmw_saamg_bcsr_transpose(A, At)
Transpose: At = A^T (At is ncol x n).
subroutine, public hecmw_saamg_spgemm_blk(A, B, C)
Block SpGEMM C = A * B : block Gustavson with dense block GEMM accumulation. Requires Amb == Bnb (inn...
subroutine, public hecmw_saamg_bcsr_to_dense(A, dense)
Densify a matrix into a (n x ncol) array (verification helper).
subroutine, public hecmw_saamg_bcsr_from_dense(dense, n, nb, A, mb)
Build a block-CSR matrix from a dense n x n array. A block is stored when any of its entries is nonze...
subroutine, public hecmw_saamg_bcsr_from_triplets(nrow, ncol, nb, ti, tj, tv, nt, A, mb)
Assemble a block-CSR (nrow x ncol) from a scalar triplet list (i,j,v): each entry is scattered into i...
subroutine, public hecmw_saamg_bcsr_move(A, B)
Move: B = A, transferring A's storage (move_alloc, no copy); A is emptied. Use when the source is no ...
subroutine, public hecmw_saamg_galerkin_local(A, P, mblk, Ac)
Galerkin coarse operator Ac = P^T A P. mblk = coarse block size.
subroutine, public hecmw_saamg_transpose_blk(A, At)
Block transpose At = A^T : swap block dims and transpose each block.
subroutine, public hecmw_saamg_matvec_blk(A, x, y)
Block matvec y = A x (x indexed by column blocks, y by row blocks).
subroutine, public hecmw_saamg_to_dense_blk(A, dense)
Densify from the block-CSR storage.
subroutine, public hecmw_saamg_triple_blk(L, A, R, Ac)
FUSED block triple product Ac = L * A * R, computed WITHOUT materializing the intermediate A*R (or L*...
subroutine, public hecmw_saamg_transpose_blk_rows(A, nbrow_keep, At)
Transpose only the first nbrow_keep block rows of A : At = (A[1:nbrow_keep])^T. Used to form the rest...
subroutine, public hecmw_saamg_bcsr_free(A)
Release the storage held by a hecmwST_saamg_bcsr.
subroutine, public hecmw_saamg_bcsr_copy(A, B)
Copy: B = A.
subroutine, public hecmw_saamg_galerkin_blk(A, P, mblk, Ac)
Block Galerkin Ac = P^T A P (block transpose + two block SpGEMM). mblk = coarse block size,...
subroutine, public hecmw_saamg_matvec(A, x, y)
Sparse matrix-vector product : y = A x.
real(kind=kreal) function, public hecmw_saamg_is_symmetric(A)
Relative asymmetry ||A - A^T||_F / ||A||_F (debug / verification helper). Returns 0 for a structurall...
subroutine, public hecmw_saamg_bcsr_from_block_triplets(nbrow, nbcol, nb, mb, bi, bj, bv, nt, A)
Assemble a block-CSR (nbrow x nbcol block grid, nb x mb blocks) from a list of block triplets (bi,...
subroutine, public hecmw_saamg_spgemm(A, B, C)
Sparse matrix-matrix product C = A * B. Requires Amb == Bnb.
Smoothed Aggregation AMG preconditioner : kind parameters.
integer(kind=4), parameter kreal