FrontISTR  5.9.0
Large-scale structural analysis program with finit element method
hecmw_precond_SAAMG_verify.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 !-------------------------------------------------------------------------------
14  use hecmw_util
20  use hecmw_precond_saamg_param, only: hecmwst_saamg_params
21  implicit none
22 
23  private
29 
30 contains
31 
37  subroutine hecmw_saamg_verify_refresh(dh, A, cmt, B, m, prm, n)
38  implicit none
39  type(hecmwst_saamg_hier), intent(inout) :: dh
40  type(hecmwst_saamg_bcsr), intent(in) :: a
41  type(hecmwst_saamg_comm), intent(in) :: cmt
42  real(kind=kreal), intent(in) :: b(:,:)
43  integer(kind=kint), intent(in) :: m
44  type(hecmwst_saamg_params), intent(in) :: prm
45  integer(kind=kint), intent(in) :: n
46  type(hecmwst_saamg_bcsr) :: ap
47  type(hecmwst_saamg_hier) :: dh2
48  real(kind=kreal), allocatable :: r(:), z1(:), z2(:)
49  real(kind=kreal) :: d2, n2, rel_same, rel_pert
50  integer(kind=kint) :: i, node, dof, gid, ir, k, rn, cn, nb1, e
51  real(kind=kreal) :: fr, fc
52 
53  ! The finest-level matvec hook (if any) points at the REAL hecMAT operator,
54  ! which the perturbation Ap cannot reach. Disable it for the duration of this
55  ! self-check so every apply uses dh's own (refreshable) level operators -- the
56  ! comparison must be refresh-vs-build of the SAME operator, not hook-vs-Ap.
57  dh%hook_disabled = .true.
58 
59  nb1 = dh%lev(1)%nb_l
60  allocate(r(n), z1(n), z2(n))
61  do i = 1, n
62  node = (i-1)/nb1 + 1; dof = mod(i-1, nb1) + 1
63  gid = node; if (allocated(cmt%gnode)) gid = cmt%gnode(node)
64  r(i) = sin(0.17d0*real(gid, kreal) + 0.31d0*real(dof, kreal))
65  end do
66 
67  ! (a) same A: refresh must reproduce the full build, bit-for-bit
68  call hecmw_saamg_apply(dh, r, z1)
69  call hecmw_saamg_refresh(dh, a)
70  call hecmw_saamg_apply(dh, r, z2)
71  d2 = 0.0d0; n2 = 0.0d0
72  do i = 1, n; d2 = d2 + (z1(i)-z2(i))**2; n2 = n2 + z1(i)**2; end do
75  rel_same = sqrt(d2/max(n2,1.0d-300))
76 
77  ! (b) value perturbation A' = Df A Df (symmetric congruence: same pattern, still
78  ! SPD). A refresh from A' must match a FRESH full build from A' (S4 routing
79  ! re-sends only values). Df(node) = 1 + 0.3*sin(gid) > 0.
80  call hecmw_saamg_bcsr_copy(a, ap)
81  do ir = 1, ap%nbrow ! block row = node
82  gid = ir; if (allocated(cmt%gnode)) gid = cmt%gnode(ir)
83  fr = 1.0d0 + 0.3d0*sin(0.5d0*real(gid,kreal))
84  do k = ap%browptr(ir), ap%browptr(ir+1)-1
85  cn = ap%bcol(k)
86  gid = cn; if (allocated(cmt%gnode)) gid = cmt%gnode(cn)
87  fc = 1.0d0 + 0.3d0*sin(0.5d0*real(gid,kreal))
88  do e = 1, ap%nb*ap%mb ! scale the whole nb x mb block
89  ap%bval((k-1)*ap%nb*ap%mb+e) = ap%bval((k-1)*ap%nb*ap%mb+e) * fr * fc
90  end do
91  end do
92  end do
93  call hecmw_saamg_setup(ap, cmt, b, m, prm, dh2) ! fresh reference build
94  call hecmw_saamg_refresh(dh, ap) ! refresh existing hierarchy
95  call hecmw_saamg_apply(dh2, r, z1)
96  call hecmw_saamg_apply(dh, r, z2)
97  d2 = 0.0d0; n2 = 0.0d0
98  do i = 1, n; d2 = d2 + (z1(i)-z2(i))**2; n2 = n2 + z1(i)**2; end do
101  rel_pert = sqrt(d2/max(n2,1.0d-300))
102  call hecmw_saamg_free(dh2)
103  call hecmw_saamg_refresh(dh, a) ! restore the hierarchy to the real A
104  dh%hook_disabled = .false. ! re-enable the finest-level matvec hook
105 
106  if (cmt%my_rank == 0) then
107  write(*,'(a,es10.3)') &
108  '#### SA-AMG refresh verify (same A): ||z_refresh - z_build||/||z|| = ', rel_same
109  write(*,'(a,es10.3)') &
110  '#### SA-AMG refresh verify (perturb A): ||z_refresh - z_rebuild||/||z|| = ', rel_pert
111  end if
112  call hecmw_saamg_bcsr_free(ap)
113  deallocate(r, z1, z2)
114  end subroutine hecmw_saamg_verify_refresh
115 
121  subroutine hecmw_saamg_verify_matvec(hecMAT, hecMESH, cmt, ndof)
122  implicit none
123  type(hecmwst_matrix), intent(in) :: hecmat
124  type(hecmwst_local_mesh), intent(in) :: hecmesh
125  type(hecmwst_saamg_comm), intent(in) :: cmt
126  integer(kind=kint), intent(in) :: ndof
127  type(hecmwst_saamg_bcsr) :: afull
128  real(kind=kreal), allocatable :: x(:), y(:)
129  real(kind=kreal) :: nax2, nx2
130  integer(kind=kint) :: ni, np, i, d, gid
131 
132  call hecmw_saamg_from_hecmat(hecmat, afull, include_halo=.true.)
133  ni = hecmesh%nn_internal; np = hecmesh%n_node
134  allocate(x(np*ndof), y(ni*ndof)); x = 0.0d0
135  do i = 1, ni
136  gid = hecmesh%global_node_ID(i)
137  do d = 1, ndof
138  x(ndof*(i-1)+d) = sin(0.1d0*real(gid,kreal) + 0.37d0*real(d,kreal))
139  end do
140  end do
141  call hecmw_saamg_matvec_d(cmt, afull, x, y) ! halo exchange + local product
142  call hecmw_innerproduct_r(hecmesh, ndof, y, y, nax2) ! internal-only sum + allreduce
143  call hecmw_innerproduct_r(hecmesh, ndof, x, x, nx2)
144  if (hecmesh%my_rank == 0) write(*,'(a,es22.15,a,es22.15)') &
145  '#### SA-AMG F4a verify: global ||A x||^2= ', nax2, ' ||x||^2= ', nx2
146  deallocate(x, y)
147  call hecmw_saamg_bcsr_free(afull)
148  end subroutine hecmw_saamg_verify_matvec
149 
156  subroutine hecmw_saamg_verify_coarsen(hecMAT, cmt, m, prm)
157  implicit none
158  type(hecmwst_matrix), intent(in) :: hecmat
159  type(hecmwst_saamg_comm), intent(in) :: cmt
160  integer(kind=kint), intent(in) :: m
161  type(hecmwst_saamg_params), intent(in) :: prm
162  type(hecmwst_saamg_bcsr) :: afull
163  type(hecmwst_saamg_comm) :: cmt_c
164  integer(kind=kint), allocatable :: aggr(:), agg_loc(:), buf(:)
165  integer(kind=kint) :: naggr_local, ncnode, nprocs, tot_aggr, tot_halo, okint
166  logical :: ok
167 
168  call hecmw_saamg_from_hecmat(hecmat, afull, include_halo=.true.)
169  call hecmw_saamg_coarsen_struct(afull, cmt, prm%min_size, prm%max_size, &
170  prm%theta, m, aggr, naggr_local, agg_loc, ncnode, cmt_c, agg_order=prm%agg_order)
171  call hecmw_saamg_verify_commtable(cmt_c, ok)
172 
173  nprocs = hecmw_saamg_comm_size(cmt)
174  allocate(buf(nprocs))
175  call hecmw_saamg_comm_allgather_int(cmt, naggr_local, buf); tot_aggr = sum(buf(1:nprocs))
176  call hecmw_saamg_comm_allgather_int(cmt, ncnode-naggr_local, buf); tot_halo = sum(buf(1:nprocs))
177  okint = 0; if (ok) okint = 1
178  call hecmw_saamg_comm_allgather_int(cmt, okint, buf)
179  ok = (minval(buf(1:nprocs)) == 1)
180  if (cmt%my_rank == 0) write(*,'(a,i0,a,i0,a,l1)') &
181  '#### SA-AMG F4b verify: total coarse nodes= ', tot_aggr, &
182  ' coarse-halo refs= ', tot_halo, ' commtable symmetric= ', ok
183 
184  deallocate(buf, aggr, agg_loc)
185  call hecmw_saamg_comm_free(cmt_c)
186  call hecmw_saamg_bcsr_free(afull)
187  end subroutine hecmw_saamg_verify_coarsen
188 
195  subroutine hecmw_saamg_verify_prolong(hecMAT, hecMESH, cmt, ndof, m, prm)
196  implicit none
197  type(hecmwst_matrix), intent(in) :: hecmat
198  type(hecmwst_local_mesh), intent(in) :: hecmesh
199  type(hecmwst_saamg_comm), intent(in) :: cmt
200  integer(kind=kint), intent(in) :: ndof, m
201  type(hecmwst_saamg_params), intent(in) :: prm
202  type(hecmwst_saamg_bcsr) :: afull, phat_ext
203  type(hecmwst_saamg_comm) :: cmt_c
204  integer(kind=kint), allocatable :: aggr(:), agg_loc(:)
205  real(kind=kreal), allocatable :: bext(:,:), bcoarse(:,:), bc(:), y(:)
206  integer(kind=kint) :: naggr_local, ncnode, np, ni, nrow, i, base, c, j
207  real(kind=kreal) :: xx, yy, zz, resid
208 
209  call hecmw_saamg_from_hecmat(hecmat, afull, include_halo=.true.)
210  call hecmw_saamg_coarsen_struct(afull, cmt, prm%min_size, prm%max_size, &
211  prm%theta, m, aggr, naggr_local, agg_loc, ncnode, cmt_c, agg_order=prm%agg_order)
212 
213  np = hecmesh%n_node; ni = hecmesh%nn_internal; nrow = np*ndof
214  ! rigid-body near-kernel over ALL nodes, ZERO centroid (rank-consistent)
215  allocate(bext(nrow, m)); bext = 0.0d0
216  do i = 1, np
217  xx = hecmesh%node(3*i-2); yy = hecmesh%node(3*i-1); zz = hecmesh%node(3*i)
218  base = ndof*(i-1)
219  bext(base+1,1)=1.0d0; bext(base+1,5)= zz; bext(base+1,6)=-yy
220  bext(base+2,2)=1.0d0; bext(base+2,4)=-zz; bext(base+2,6)= xx
221  bext(base+3,3)=1.0d0; bext(base+3,4)= yy; bext(base+3,5)=-xx
222  if (ndof == 6) then
223  bext(base+4,4)=1.0d0; bext(base+5,5)=1.0d0; bext(base+6,6)=1.0d0
224  end if
225  end do
226 
227  call hecmw_saamg_tentative_ext(cmt, ndof, m, bext(1:ni*ndof, :), aggr, &
228  naggr_local, agg_loc, ncnode, phat_ext, bcoarse)
229 
230  allocate(bc(ncnode*m), y(nrow))
231  resid = 0.0d0
232  do c = 1, m
233  bc = 0.0d0
234  do j = 1, naggr_local*m
235  bc(j) = bcoarse(j, c)
236  end do
237  call hecmw_saamg_comm_update(cmt_c, m, bc) ! fill halo coarse rows from owners
238  call hecmw_saamg_matvec(phat_ext, bc, y) ! y = P-hat_ext * bc (all rows)
239  do i = 1, nrow
240  resid = max(resid, abs(y(i) - bext(i, c)))
241  end do
242  end do
243  call hecmw_saamg_comm_allreduce_max_r(cmt, resid)
244  if (cmt%my_rank == 0) write(*,'(a,es10.3)') &
245  '#### SA-AMG F4b-2 verify: max |P-hat_ext B_c - B_fine| (all rows)= ', resid
246 
247  deallocate(aggr, agg_loc, bext, bcoarse, bc, y)
248  call hecmw_saamg_comm_free(cmt_c)
249  call hecmw_saamg_bcsr_free(phat_ext)
250  call hecmw_saamg_bcsr_free(afull)
251  end subroutine hecmw_saamg_verify_prolong
252 
260  subroutine hecmw_saamg_verify_smoothp(hecMAT, hecMESH, cmt, ndof, m, prm)
261  implicit none
262  type(hecmwst_matrix), intent(in) :: hecmat
263  type(hecmwst_local_mesh), intent(in) :: hecmesh
264  type(hecmwst_saamg_comm), intent(in) :: cmt
265  integer(kind=kint), intent(in) :: ndof, m
266  type(hecmwst_saamg_params), intent(in) :: prm
267  type(hecmwst_saamg_bcsr) :: afull, p, ac_global
268  type(hecmwst_saamg_comm) :: cmt_c
269  integer(kind=kint), allocatable :: aggr(:), agg_loc(:), chalo_gid(:)
270  real(kind=kreal), allocatable :: bext(:,:), bcoarse(:,:), bc(:), y(:)
271  integer(kind=kint) :: naggr_local, ncnode, np, ni, nir, i, base, c, j, my_off
272  real(kind=kreal) :: xx, yy, zz, omega, res2, prows_mism, asym
273 
274  call hecmw_saamg_from_hecmat(hecmat, afull, include_halo=.true.)
275  call hecmw_saamg_coarsen_struct(afull, cmt, prm%min_size, prm%max_size, &
276  prm%theta, m, aggr, naggr_local, agg_loc, ncnode, cmt_c, my_off, chalo_gid, &
277  agg_order=prm%agg_order)
278 
279  np = hecmesh%n_node; ni = hecmesh%nn_internal; nir = ni*ndof
280  allocate(bext(np*ndof, m)); bext = 0.0d0
281  do i = 1, np
282  xx = hecmesh%node(3*i-2); yy = hecmesh%node(3*i-1); zz = hecmesh%node(3*i)
283  base = ndof*(i-1)
284  bext(base+1,1)=1.0d0; bext(base+1,5)= zz; bext(base+1,6)=-yy
285  bext(base+2,2)=1.0d0; bext(base+2,4)=-zz; bext(base+2,6)= xx
286  bext(base+3,3)=1.0d0; bext(base+3,4)= yy; bext(base+3,5)=-xx
287  if (ndof == 6) then
288  bext(base+4,4)=1.0d0; bext(base+5,5)=1.0d0; bext(base+6,6)=1.0d0
289  end if
290  end do
291 
292  call hecmw_saamg_prolongator(afull, cmt, m, prm%safety, prm%lanczos_iter, &
293  bext(1:nir, :), aggr, naggr_local, agg_loc, ncnode, p, bcoarse, omega)
294 
295  allocate(bc(ncnode*m), y(nir))
296  res2 = 0.0d0
297  do c = 1, m
298  bc = 0.0d0
299  do j = 1, naggr_local*m
300  bc(j) = bcoarse(j, c)
301  end do
302  call hecmw_saamg_comm_update(cmt_c, m, bc)
303  call hecmw_saamg_matvec(p, bc, y) ! y = P bc (internal rows)
304  do i = 1, nir
305  res2 = res2 + (y(i) - bext(i, c))**2
306  end do
307  end do
308  call hecmw_saamg_comm_allreduce_sum_r(cmt, res2)
309  if (cmt%my_rank == 0) write(*,'(a,es22.15,a,es12.5)') &
310  '#### SA-AMG F4c-1 verify: global ||P B_c - B_f||^2= ', res2, ' omega= ', omega
311 
312  ! F4c-2a: verify the smoothed-P halo row exchange (round-trip checksum)
313  call hecmw_saamg_verify_prows(cmt, m, p, naggr_local, my_off, chalo_gid, prows_mism)
314  if (cmt%my_rank == 0) write(*,'(a,es10.3)') &
315  '#### SA-AMG F4c-2a verify: max P halo-row exchange mismatch= ', prows_mism
316 
317  ! F4c-2b: build the global coarse operator (redundant assembly) and check it
318  ! is symmetric -- the partition-invariant correctness test of the Galerkin.
319  call hecmw_saamg_galerkin_global(afull, cmt, p, naggr_local, ncnode, &
320  my_off, chalo_gid, m, ac_global)
321  asym = hecmw_saamg_is_symmetric(ac_global)
322  if (cmt%my_rank == 0) write(*,'(a,i0,a,es10.3)') &
323  '#### SA-AMG F4c-2b verify: global coarse op size= ', ac_global%n/m, &
324  ' relative asymmetry= ', asym
325 
326  ! S1: distributed Galerkin (row-routed) gathered == redundant Galerkin; cmt_op symmetric
327  block
328  real(kind=kreal) :: g_reldiff; logical :: g_sym
329  call hecmw_saamg_verify_galerkin(afull, cmt, p, naggr_local, ncnode, my_off, &
330  chalo_gid, m, g_reldiff, g_sym)
331  if (cmt%my_rank == 0) write(*,'(a,es10.3,a,l1)') &
332  '#### SA-AMG S1 verify: |Ac_dist - Ac_global|/|Ac| = ', g_reldiff, &
333  ' cmt_op symmetric= ', g_sym
334  end block
335 
336  deallocate(aggr, agg_loc, chalo_gid, bext, bcoarse, bc, y)
337  call hecmw_saamg_comm_free(cmt_c)
338  call hecmw_saamg_bcsr_free(p)
339  call hecmw_saamg_bcsr_free(ac_global)
340  call hecmw_saamg_bcsr_free(afull)
341  end subroutine hecmw_saamg_verify_smoothp
342 
Smoothed Aggregation AMG preconditioner : FrontISTR adapter.
subroutine, public hecmw_saamg_from_hecmat(hecMAT, A, include_halo)
Convert hecmwST_matrix (FrontISTR block storage) -> internal block-CSR. Rows are always internal only...
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_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_comm_free(cmt)
integer(kind=kint) function, public hecmw_saamg_comm_size(cmt)
Number of ranks in this level's communicator (1 when serial).
Smoothed Aggregation AMG preconditioner : distributed coarsening.
subroutine, public hecmw_saamg_prolongator(A, cmt, m, safety, lanczos_iter, bfine_int, aggr, naggr_local, agg_loc, ncnode, P, bcoarse, omega)
Distributed smoothed prolongator P = (I - omega D^{-1} A) P-hat_ext. Builds the halo-extended tentati...
subroutine, public hecmw_saamg_tentative_ext(cmt, nb, m, bfine_int, aggr, naggr_local, agg_loc, ncnode, phat_ext, bcoarse)
Distributed tentative prolongator with halo-extended rows. Builds P-hat over this rank's internal agg...
subroutine, public hecmw_saamg_free(dh)
subroutine, public hecmw_saamg_verify_prows(cmt, m, P, naggr_local, my_off, chalo_gid, mism)
Halo-exchange the smoothed prolongator's rows (F4c Galerkin needs P rows for halo fine nodes,...
subroutine, public hecmw_saamg_verify_galerkin(A, cmt, P, naggr_local, ncnode, my_off, chalo_gid, m, reldiff, sym_ok)
S1 self-check: build the coarse operator BOTH ways – distributed (hecmw_saamg_galerkin,...
subroutine, public hecmw_saamg_setup(A, cmt, bfine_int, m, prm, dh, fine_matvec, move_in)
Build a fully-distributed multilevel SA-AMG hierarchy. bfine_int is the near-kernel on internal nodes...
subroutine, public hecmw_saamg_refresh(dh, A_new)
Numeric-only refresh (Newton): reuse each level's aggregation / comm table / tentative P-hat AND the ...
subroutine, public hecmw_saamg_coarsen_struct(A, cmt, min_size, max_size, theta, m, aggr, naggr_local, agg_loc, ncnode, cmt_c, my_coarse_off, chalo_gid, agg_order)
Uncoupled aggregation + coarse communication table for one distributed level. Inputs: A = rectangular...
subroutine, public hecmw_saamg_galerkin_global(A, cmt, P, naggr_local, ncnode, my_off, chalo_gid, m, Ac_global, fuse_this_level)
Redundant Galerkin: gather every rank's partial triplets and sum -> the full global coarse operator (...
subroutine, public hecmw_saamg_apply(dh, r, z)
Apply one distributed multilevel V-cycle: z = M^{-1} r (internal, length nint*nb).
subroutine, public hecmw_saamg_verify_commtable(cmt_c, ok)
Check coarse comm-table symmetry: for each neighbor, the number of coarse nodes it exports to me must...
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_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_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...
Smoothed Aggregation AMG preconditioner : tunable parameters.
Smoothed Aggregation AMG preconditioner : distributed self-checks.
subroutine, public hecmw_saamg_verify_coarsen(hecMAT, cmt, m, prm)
F4b self-check: build the uncoupled aggregation and the coarse comm table on the distributed finest o...
subroutine, public hecmw_saamg_verify_prolong(hecMAT, hecMESH, cmt, ndof, m, prm)
F4b-2 self-check: build the halo-extended tentative prolongator P-hat_ext on the distributed operator...
subroutine, public hecmw_saamg_verify_refresh(dh, A, cmt, B, m, prm, n)
Verify the distributed numeric refresh: refreshing with the SAME operator A must reproduce the freshl...
subroutine, public hecmw_saamg_verify_matvec(hecMAT, hecMESH, cmt, ndof)
F4a self-check: the distributed (halo-aware) matvec applied to a globally consistent vector x (define...
subroutine, public hecmw_saamg_verify_smoothp(hecMAT, hecMESH, cmt, ndof, m, prm)
F4c-1 self-check: build the distributed SMOOTHED prolongator P and confirm the global near-kernel res...
subroutine hecmw_innerproduct_r(hecMESH, ndof, X, Y, sum, COMMtime)
I/O and Utility.
Definition: hecmw_util_f.F90:7
integer(kind=4), parameter kreal