FrontISTR  5.9.0
Large-scale structural analysis program with finit element method
hecmw_precond_SAAMG_prolongation.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 !-------------------------------------------------------------------------------
23  implicit none
24 
25  private
26  public :: hecmw_saamg_tentative
28 
29 contains
30 
32  subroutine hecmw_saamg_tentative(bfine, n, m, nb, aggr, naggr, phat, bcoarse)
33  implicit none
34  integer(kind=kint), intent(in) :: n, m, nb, naggr
35  real(kind=kreal), intent(in) :: bfine(n, m)
36  integer(kind=kint), intent(in) :: aggr(:)
37  type(hecmwst_saamg_bcsr), intent(out) :: phat
38  real(kind=kreal), allocatable, intent(out) :: bcoarse(:,:)
39  integer(kind=kint), allocatable :: memcnt(:), aggptr(:), aggnodes(:), pos(:)
40  integer(kind=kint), allocatable :: bi(:), bj(:)
41  real(kind=kreal), allocatable :: bv(:,:), bk(:,:), tau(:), work(:)
42  integer(kind=kint) :: nnode, ncoarse, inode, k, l, ii, c, r, node
43  integer(kind=kint) :: nk, rows, nt, maxt, lwork, info, rwork
44  real(kind=kreal) :: wq(1)
45 #ifdef HECMW_WITH_LAPACK
46  external :: dgeqrf, dorgqr
47 
48  nnode = n / nb
49  ncoarse = naggr * m
50  allocate(bcoarse(ncoarse, m)); bcoarse = 0.0d0
51 
52  ! member node list per aggregate (CSR-like)
53  allocate(memcnt(naggr)); memcnt = 0
54  do inode = 1, nnode
55  if (aggr(inode) > 0) memcnt(aggr(inode)) = memcnt(aggr(inode)) + 1
56  end do
57  allocate(aggptr(naggr+1)); aggptr(1) = 0
58  do k = 1, naggr
59  aggptr(k+1) = aggptr(k) + memcnt(k)
60  end do
61  allocate(aggnodes(aggptr(naggr+1)), pos(naggr))
62  do k = 1, naggr
63  pos(k) = aggptr(k)
64  end do
65  do inode = 1, nnode
66  k = aggr(inode)
67  if (k > 0) then
68  pos(k) = pos(k) + 1
69  aggnodes(pos(k)) = inode
70  end if
71  end do
72 
73  ! block-triplet buffer (one nb x m block per assigned node)
74  maxt = aggptr(naggr+1)
75  allocate(bi(maxt), bj(maxt), bv(nb*m, maxt)); nt = 0
76 
77  do k = 1, naggr
78  nk = memcnt(k); rows = nk * nb
79  if (rows < m) then
80  write(*,'(a,i0,a,i0,a,i0)') 'hecmw_saamg_tentative: aggregate ', k, &
81  ' has rows=', rows, ' < m=', m
82  call hecmw_saamg_abort('tentative: aggregate has fewer rows than near-kernel size m')
83  end if
84  allocate(bk(rows, m), tau(m))
85  do l = 0, nk-1
86  node = aggnodes(aggptr(k)+l+1)
87  do ii = 1, nb
88  do c = 1, m
89  bk(l*nb+ii, c) = bfine((node-1)*nb+ii, c)
90  end do
91  end do
92  end do
93 
94  ! workspace query (max of dgeqrf / dorgqr)
95  call dgeqrf(rows, m, bk, rows, tau, wq, -1, info); lwork = int(wq(1))
96  call dorgqr(rows, m, m, bk, rows, tau, wq, -1, info); rwork = int(wq(1))
97  lwork = max(lwork, rwork, 1)
98  allocate(work(lwork))
99 
100  call dgeqrf(rows, m, bk, rows, tau, work, lwork, info)
101  if (info /= 0) then; write(*,*) 'dgeqrf info=', info
102  call hecmw_saamg_abort('tentative: per-aggregate QR failed (dgeqrf)'); end if
103  ! Extract R into B_coarse block row k. R may be (near-)singular when the
104  ! aggregate is geometrically degenerate (e.g. collinear nodes -> a rotation
105  ! axis is unrepresented): that aggregate then carries fewer than m
106  ! independent near-kernel modes. This is admissible -- dorgqr still returns
107  ! an orthonormal Q (all m columns), so P-hat^T P-hat = I and the exact
108  ! identity P-hat * B_coarse = B_fine both hold; only B_coarse is rank
109  ! deficient, which the next-coarser level tolerates.
110  do r = 1, m
111  do c = r, m
112  bcoarse((k-1)*m+r, c) = bk(r, c)
113  end do
114  end do
115 
116  call dorgqr(rows, m, m, bk, rows, tau, work, lwork, info)
117  if (info /= 0) then; write(*,*) 'dorgqr info=', info
118  call hecmw_saamg_abort('tentative: per-aggregate Q formation failed (dorgqr)'); end if
119 
120  ! emit one P-hat block per member node (rows = node dofs, cols = coarse node k)
121  do l = 0, nk-1
122  node = aggnodes(aggptr(k)+l+1)
123  nt = nt + 1; bi(nt) = node; bj(nt) = k
124  do c = 1, m
125  do ii = 1, nb
126  bv((c-1)*nb+ii, nt) = bk(l*nb+ii, c)
127  end do
128  end do
129  end do
130 
131  deallocate(bk, tau, work)
132  end do
133 
134  call hecmw_saamg_bcsr_from_block_triplets(nnode, naggr, nb, m, bi, bj, bv, nt, phat)
135 
136  deallocate(memcnt, aggptr, aggnodes, pos, bi, bj, bv)
137 #else
138  call hecmw_saamg_require_lapack('per-aggregate tentative QR (dgeqrf/dorgqr)')
139 #endif
140  end subroutine hecmw_saamg_tentative
141 
146  subroutine hecmw_saamg_smooth_prolongator(A, D, omega, phat, p)
147  implicit none
148  type(hecmwst_saamg_bcsr), intent(in) :: a
149  type(hecmwst_saamg_blockdiag), intent(in) :: d
150  real(kind=kreal), intent(in) :: omega
151  type(hecmwst_saamg_bcsr), intent(in) :: phat
152  type(hecmwst_saamg_bcsr), intent(out) :: p
153  type(hecmwst_saamg_bcsr) :: ap
154  integer(kind=kint), allocatable :: bi(:), bj(:)
155  real(kind=kreal), allocatable :: bv(:,:)
156  integer(kind=kint) :: nb, m, bs, nrow, i, t, nt
157 
158  nb = phat%nb; m = phat%mb; bs = nb*m
159  ! P has only the INTERNAL fine rows (A%nbrow): for the distributed operator A
160  ! is rectangular (nint x NP) and phat is the halo-extended P-hat (NP rows);
161  ! only the first A%nbrow block rows of phat are kept. Sequential: A%nbrow ==
162  ! phat%nbrow, so this is a no-op restriction.
163  nrow = a%nbrow
164  call hecmw_saamg_spgemm_blk(a, phat, ap) ! ap = A P-hat (block, nint rows)
165  call hecmw_saamg_blockdiag_apply_bcsr_inplace(d, ap) ! ap := D^{-1} A P-hat (in place)
166 
167  ! P = P-hat - omega * ap : sum the two block sets (from_block_triplets sums dups)
168  allocate(bi(phat%nnzb + ap%nnzb), bj(phat%nnzb + ap%nnzb), bv(bs, phat%nnzb + ap%nnzb))
169  nt = 0
170  do i = 1, nrow ! + P-hat (internal rows only)
171  do t = phat%browptr(i), phat%browptr(i+1)-1
172  nt = nt + 1; bi(nt) = i; bj(nt) = phat%bcol(t)
173  bv(1:bs, nt) = phat%bval((t-1)*bs+1:t*bs)
174  end do
175  end do
176  do i = 1, ap%nbrow ! - omega D^{-1} A P-hat
177  do t = ap%browptr(i), ap%browptr(i+1)-1
178  nt = nt + 1; bi(nt) = i; bj(nt) = ap%bcol(t)
179  bv(1:bs, nt) = -omega * ap%bval((t-1)*bs+1:t*bs)
180  end do
181  end do
182 
183  call hecmw_saamg_bcsr_from_block_triplets(nrow, phat%nbcol, nb, m, bi, bj, bv, nt, p)
184 
185  deallocate(bi, bj, bv)
186  call hecmw_saamg_bcsr_free(ap)
187  end subroutine hecmw_saamg_smooth_prolongator
188 
Smoothed Aggregation AMG preconditioner : lightweight comm table.
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....
Smoothed Aggregation AMG preconditioner : internal block-CSR matrix.
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_free(A)
Release the storage held by a hecmwST_saamg_bcsr.
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,...
Smoothed Aggregation AMG preconditioner : prolongation.
subroutine, public hecmw_saamg_smooth_prolongator(A, D, omega, phat, p)
Smoothed prolongator P = P-hat - omega * D^{-1} (A P-hat), via SpGEMM. Computes A*P-hat once as a spa...
subroutine, public hecmw_saamg_tentative(bfine, n, m, nb, aggr, naggr, phat, bcoarse)
Build P-hat (n x naggr*m block-CSR) and B_coarse (naggr*m x m) from B_fine.
Smoothed Aggregation AMG preconditioner : smoother building blocks.
subroutine, public hecmw_saamg_blockdiag_apply_bcsr_inplace(D, C)
Apply C <- D^{-1} C in place (each nb x mb block solved against the node's LU). Same as blockdiag_app...
Smoothed Aggregation AMG preconditioner : kind parameters.
integer(kind=4), parameter kreal