Skip to content

Commit a38447c

Browse files
committed
feat(amr): IGR solver support with restriction-only coarse/fine coupling - the fine block runs its own fixed-iteration sigma solve seeded and Dirichlet-bounded by the converged coarse sigma (piecewise-constant parent injection over the full buffered range; per-iteration BC/halo populate skipped under amr_in_fine_advance; coarse jac/jac_old warm-start state bounced across the fine advance), fine RK update uses the IGR coefficient form (dt embedded in the rhs), flux-register capture/apply return early under igr (no face fluxes exposed by the fused IGR kernels - seam conservation is truncation-order, documented), amr_subcycle gated. Validated: free-stream through block EXACTLY preserved, AMR-vs-reference at resolution scale (rho 1.3e-4 rel-L2, static and dynamic regrid), transverse seam artifact 3e-4 absolute from the coarse/fine sigma jump (truncation-level, documented), conservation drift at the reference's own outflow scale. Goldens 6C20B752 (static) 660FFBFE (dynamic regrid)
1 parent 6f518df commit a38447c

12 files changed

Lines changed: 529 additions & 14 deletions

File tree

docs/documentation/amr.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ a diagnostic message for unsupported combinations.
217217
| QBMM bubbles (non-polytropic) | Supported (static block, no subcycle) | Each block carries its own `pb`/`mv` quadrature side-state: prolonged piecewise-constant (realizability), advanced with the block's own rhs scratch, restricted back with the moments; dynamic regrid and `amr_subcycle` remain gated |
218218
| Lagrangian bubbles (`bubbles_lagrange = T`) | Supported (cloud excluded from blocks) | Two-way coupling lives on the coarse grid: regrid suppresses tags and clips candidate boxes around the cloud's padded bbox (positions + `mapCells` smearing + stencil + drift margin, recomputed collectively each regrid), the fine advance skips the EL hooks, EL volume fractions prolong WITHOUT the sum-to-one closure (their sum is the local liquid fraction), and a per-stage guard aborts if the cloud reaches an active block |
219219
| Immersed boundaries (`ib = T`; one or more non-STL bodies, static or prescribed-motion `moving_ibm=1`) | Supported | Per-block fine-grid IB markers/ghost points, rebuilt each fine substage at the body's sub-time position for a moving body; non-conservative ghost-cell forcing at the body; with dynamic regrid candidate boxes expand to fully contain each body at its live position plus margin, the fine IB state rebuilds after every regrid, and a per-substage guard aborts if a moving body reaches its block boundary between regrids; force-driven (`moving_ibm=2`)/STL gated; a body spanning a rank seam is rejected at startup |
220-
| IGR solver | **Not supported** | Unvalidated with the fine-level advance |
220+
| IGR solver (`igr = T`) | Supported (restriction-only coupling) | The fine block runs its own fixed-iteration sigma solve, seeded and Dirichlet-bounded by the converged coarse sigma (frozen ghost ring; the per-iteration BC populate is skipped); the coarse warm-start state is saved/restored across the fine advance. The Berger-Colella reflux is NOT captured from the fused IGR flux kernels, so seam conservation is truncation-order rather than exact; free-stream preservation is exact. `amr_subcycle` is gated |
221221
| 2D axisymmetric (`cyl_coord = T`, `p = 0`) | Supported | Geometric sources read the live (swapped) grid; the axis half-width cell's per-cell WENO coefficients are recomputed for each block on swap/restore; blocks stay `buff_size` off the axis (domain-edge clamp); the axis-singularity viscous treatment runs on the coarse pass only |
222222
| 3D cylindrical (`cyl_coord = T`, `p > 0`) | **Not supported** | The per-stage azimuthal Fourier filter is a global operation incompatible with the block-local fine advance |
223223
| Grid stretching (`stretch_x[y,z] = T`) | Supported | Fine ghost-shell coordinates extend by exact parent-cell bisection, and the spacing-dependent WENO coefficients are recomputed for the active grid on every block swap/restore (`amr_weno_coef_recompute`, armed automatically when the grid is nonuniform); prolongation stays conservative but its slope estimate is first-order on nonuniform parents |

docs/documentation/case.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -855,7 +855,11 @@ lives on the coarse grid, regrid suppresses tags and clips boxes around the clou
855855
bounding box, EL volume fractions prolong without the sum-to-one closure (their sum is the
856856
local liquid fraction, not 1), and a per-stage guard aborts if the cloud reaches an active
857857
block (reduce `amr_regrid_int` or increase `amr_buf`).
858-
AMR is incompatible with surface tension, IGR, 3D cylindrical
858+
The IGR solver is supported with restriction-only coarse/fine coupling: the fine block runs
859+
its own fixed-iteration sigma solve seeded and Dirichlet-bounded by the converged coarse
860+
sigma; seam conservation is truncation-order (no reflux capture from the fused IGR flux
861+
kernels), free-stream preservation is exact, and `amr_subcycle` is gated under IGR.
862+
AMR is incompatible with surface tension, 3D cylindrical
859863
coordinates (2D axisymmetric IS supported), MHD, hyperelasticity, Riemann-extrapolation
860864
boundaries (bc = -4), and `active_box`. `hybrid_weno`/`hybrid_riemann` are supported: each
861865
level recomputes the smoothness sensor over its own (swapped) bounds every RHS call.

src/simulation/m_amr.fpp

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ module m_amr
3030
use m_weno, only: s_compute_weno_coefficients
3131
use m_acoustic_src, only: acoustic_supp_lo, acoustic_supp_hi
3232
use m_bubbles_EL, only: s_lag_cloud_bbox_local
33+
use m_igr, only: jac, jac_old
3334

3435
implicit none
3536

@@ -93,6 +94,12 @@ module m_amr
9394
type(int_bounds_info) :: sw_idwint(3), sw_idwbuff(3)
9495
logical :: sw_acoustic_source
9596

97+
!> IGR sigma-state bounce (igr only): the fine solve reuses the module jac/jac_old arrays at fine indices (the extent guard
98+
!! keeps fine bounds inside), so the coarse contents - jac_old is the Jacobi warm start persisting across steps - are saved here
99+
!! across the fine advance.
100+
real(wp), allocatable :: sw_jac(:,:,:), sw_jac_old(:,:,:)
101+
$:GPU_DECLARE(create='[sw_jac, sw_jac_old]')
102+
96103
!> Lagrangian bubble-cloud exclusion support: padded global coarse-index bbox of the cloud (positions + mapCells smearing +
97104
!! stencil headroom [+ drift margin at regrid]). Blocks and regrid boxes stay clear of it: a bubble inside a block loses two-way
98105
!! coupling (the fine advance skips the EL hooks and the coarse result under the block is discarded by restriction). Recomputed
@@ -222,6 +229,10 @@ contains
222229
allocate (sw_z_cc(-buff_size:p + buff_size))
223230
allocate (sw_dz(-buff_size:p + buff_size))
224231
end if
232+
if (igr) then
233+
@:ALLOCATE(sw_jac(idwbuff(1)%beg:idwbuff(1)%end, idwbuff(2)%beg:idwbuff(2)%end, idwbuff(3)%beg:idwbuff(3)%end))
234+
@:ALLOCATE(sw_jac_old(idwbuff(1)%beg:idwbuff(1)%end, idwbuff(2)%beg:idwbuff(2)%end, idwbuff(3)%beg:idwbuff(3)%end))
235+
end if
225236
226237
! Grid uniformity policy. The two spacing-uniformity consumers are both handled exactly:
227238
! the fine-block ghost-shell coordinates extend by exact parent-cell bisection (reads
@@ -244,7 +255,7 @@ contains
244255
amr_weno_coef_recompute = .true.
245256
end if
246257
end if
247-
if (weno_order == 1) amr_weno_coef_recompute = .false. ! order 1 has no grid-dependent coefficients
258+
if (weno_order == 1 .or. igr) amr_weno_coef_recompute = .false. ! order 1 / IGR: no grid-dependent WENO coefficients
248259

249260
! preallocate every slot at max buffered extents (each sized exactly as the single legacy block): coords (host) +
250261
! fields (device-resident @:ALLOCATE so s_compute_rhs kernels can run on the fine block; q_cons/q_prim/rhs get the
@@ -1247,6 +1258,13 @@ contains
12471258
! must be rebuilt for the block's own uniform grid (no-op flag on uniform grids)
12481259
if (amr_weno_coef_recompute) call s_amr_recompute_weno_coefs()
12491260
1261+
! IGR: save the coarse sigma state and seed the fine solve. jac holds THIS stage's
1262+
! converged coarse sigma (the coarse RHS ran first), so its parent values are both the
1263+
! best initial guess and the frozen Dirichlet ghost data for the block-local Jacobi
1264+
! solve (the per-iteration BC/halo populate is skipped under amr_in_fine_advance).
1265+
! Piecewise-constant parent injection over the full buffered fine range.
1266+
if (igr) call s_amr_igr_swap_sigma()
1267+
12501268
end subroutine s_amr_swap_to_fine
12511269

12521270
!> Restore the global grid state saved by s_amr_swap_to_fine.
@@ -1265,9 +1283,65 @@ contains
12651283
call s_amr_sync_grid_state_to_device()
12661284
if (hypoelasticity) call s_hypoelastic_update_fd_coeffs()
12671285
if (amr_weno_coef_recompute) call s_amr_recompute_weno_coefs()
1286+
if (igr) call s_amr_igr_restore_sigma()
12681287

12691288
end subroutine s_amr_restore_coarse
12701289

1290+
!> Save the coarse jac/jac_old and seed the (already swapped-in) fine block's sigma state by piecewise-constant parent injection
1291+
!! from the saved coarse sigma: interior = initial guess, ghost shell = frozen Dirichlet coupling data for the block-local
1292+
!! iterative solve.
1293+
impure subroutine s_amr_igr_swap_sigma()
1294+
1295+
integer :: j, k, l, ci, cj, ck
1296+
1297+
$:GPU_PARALLEL_LOOP(collapse=3, private='[j, k, l]')
1298+
do l = sw_idwbuff(3)%beg, sw_idwbuff(3)%end
1299+
do k = sw_idwbuff(2)%beg, sw_idwbuff(2)%end
1300+
do j = sw_idwbuff(1)%beg, sw_idwbuff(1)%end
1301+
sw_jac(j, k, l) = jac(j, k, l)
1302+
sw_jac_old(j, k, l) = jac_old(j, k, l)
1303+
end do
1304+
end do
1305+
end do
1306+
$:END_GPU_PARALLEL_LOOP()
1307+
$:GPU_PARALLEL_LOOP(collapse=3, private='[j, k, l, ci, cj, ck]')
1308+
do l = idwbuff(3)%beg, idwbuff(3)%end
1309+
do k = idwbuff(2)%beg, idwbuff(2)%end
1310+
do j = idwbuff(1)%beg, idwbuff(1)%end
1311+
ci = amr_isect_lo(1) + floor(real(j, wp)/2._wp) - start_idx(1)
1312+
cj = 0; ck = 0
1313+
if (n_glb > 0) cj = amr_isect_lo(2) + floor(real(k, wp)/2._wp) - start_idx(2)
1314+
if (p_glb > 0) ck = amr_isect_lo(3) + floor(real(l, wp)/2._wp) - start_idx(3)
1315+
ci = min(max(ci, sw_idwbuff(1)%beg), sw_idwbuff(1)%end)
1316+
cj = min(max(cj, sw_idwbuff(2)%beg), sw_idwbuff(2)%end)
1317+
ck = min(max(ck, sw_idwbuff(3)%beg), sw_idwbuff(3)%end)
1318+
jac(j, k, l) = sw_jac(ci, cj, ck)
1319+
jac_old(j, k, l) = sw_jac(ci, cj, ck)
1320+
end do
1321+
end do
1322+
end do
1323+
$:END_GPU_PARALLEL_LOOP()
1324+
1325+
end subroutine s_amr_igr_swap_sigma
1326+
1327+
!> Restore the coarse jac/jac_old saved by s_amr_igr_swap_sigma (bounds already restored).
1328+
impure subroutine s_amr_igr_restore_sigma()
1329+
1330+
integer :: j, k, l
1331+
1332+
$:GPU_PARALLEL_LOOP(collapse=3, private='[j, k, l]')
1333+
do l = idwbuff(3)%beg, idwbuff(3)%end
1334+
do k = idwbuff(2)%beg, idwbuff(2)%end
1335+
do j = idwbuff(1)%beg, idwbuff(1)%end
1336+
jac(j, k, l) = sw_jac(j, k, l)
1337+
jac_old(j, k, l) = sw_jac_old(j, k, l)
1338+
end do
1339+
end do
1340+
end do
1341+
$:END_GPU_PARALLEL_LOOP()
1342+
1343+
end subroutine s_amr_igr_restore_sigma
1344+
12711345
!> Recompute the WENO reconstruction coefficient arrays from the CURRENT grid globals (the fine block's after a swap, the coarse
12721346
!! grid's after a restore). s_compute_weno_coefficients reads the live cell-boundary arrays, refreshes uniform_grid, and pushes
12731347
!! its own device updates; the coefficient arrays were sized to the coarse local ranges at init, which the fine ranges never
@@ -1642,9 +1716,10 @@ contains
16421716
call s_amr_restore_coarse()
16431717
amr_in_fine_advance = .false.
16441718
1645-
! RK stage update (device kernel; mirror of the coarse non-IGR form)
1719+
! RK stage update (device kernel; mirror of the coarse form - under IGR the rhs already
1720+
! embeds dt, matching the coarse igr update, so the dt factor is 1)
16461721
call s_amr_fine_rk_update(amr_slots(amr_cur)%q_cons, amr_slots(amr_cur)%q_cons_stor, amr_slots(amr_cur)%rhs, coefs(1), &
1647-
& coefs(2), coefs(3), coefs(4), dt)
1722+
& coefs(2), coefs(3), coefs(4), merge(1._wp, dt, igr))
16481723
if (qbmm .and. .not. polytropic) call s_amr_fine_rk_update_pbmv(coefs(1), coefs(2), coefs(3), coefs(4), dt)
16491724
! 6-equation model: per-stage pressure relaxation on the block (before IB correct, coarse order)
16501725
if (model_eqns == model_eqns_6eq .and. (.not. relax)) call s_amr_pressure_relax_fine()
@@ -2990,6 +3065,10 @@ contains
29903065
if (allocated(sw_x_cb)) deallocate (sw_x_cb, sw_x_cc, sw_dx)
29913066
if (allocated(sw_y_cb)) deallocate (sw_y_cb, sw_y_cc, sw_dy)
29923067
if (allocated(sw_z_cb)) deallocate (sw_z_cb, sw_z_cc, sw_dz)
3068+
if (igr) then
3069+
@:DEALLOCATE(sw_jac)
3070+
@:DEALLOCATE(sw_jac_old)
3071+
end if
29933072
29943073
end subroutine s_finalize_amr_module
29953074

src/simulation/m_amr_registers.fpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ contains
145145
logical :: accum
146146
147147
if (.not. amr) return
148+
if (igr) return ! stage-1 IGR coupling is restriction-only: the fused IGR flux kernels do not expose face fluxes to capture
148149
if (amr_in_fine_advance .and. .not. amr_rank_owns_block) return
149150
islot = amr_cur ! working block slot (local => captured by value in the device kernels below)
150151
! flux data was just written by device kernels; the face reads below run as device kernels too
@@ -471,6 +472,7 @@ contains
471472
real(wp) :: fblo, fbhi, mlo, mhi
472473

473474
if (.not. amr) return
475+
if (igr) return ! stage-1 IGR: restriction-only coupling (no captured fluxes)
474476
islot = amr_cur ! working block slot (local => captured by value in the device kernels below)
475477
! per-face participation: each face's correction runs on the rank owning its OUTSIDE cell layer
476478
! (all faces at np=1); freg slices from a rank-boundary face's fine side arrive via
@@ -593,6 +595,7 @@ contains
593595
integer :: d, eq, t1, t2, t1_hi, t2_hi, islot
594596
595597
if (.not. amr) return
598+
if (igr) return ! stage-1 IGR: restriction-only coupling (no captured fluxes)
596599
if (.not. amr_rank_owns_block) return
597600
islot = amr_cur ! working block slot (local => captured by value in the device kernels below)
598601
do d = 1, 3
@@ -626,6 +629,7 @@ contains
626629
real(wp) :: fblo, fbhi, mlo, mhi, dtl
627630
628631
if (.not. amr) return
632+
if (igr) return ! stage-1 IGR: restriction-only coupling (no captured fluxes)
629633
islot = amr_cur ! working block slot (local => captured by value in the device kernels below)
630634
! per-face participation and index conventions: see s_amr_apply_reflux
631635
call s_amr_reflux_face_flags(sidx, ext, own_lo, own_hi)

src/simulation/m_checker.fpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ contains
9393
& "hybrid_riemann (cheap central/Rusanov flux) is incompatible with the low_Mach correction")
9494

9595
if (amr) then
96-
@:PROHIBIT(recon_type /= recon_type_weno, "amr requires WENO reconstruction")
96+
@:PROHIBIT((.not. igr) .and. recon_type /= recon_type_weno, "amr requires WENO reconstruction (or the IGR solver)")
9797
@:PROHIBIT(time_stepper /= time_stepper_rk3, "amr requires time_stepper = 3 (SSP-RK3)")
9898
! 6-equation support: the internal-energy equations prolong/restrict on the generic
9999
! conservative path and the per-stage pressure relaxation (cell-local) also runs on
@@ -113,7 +113,11 @@ contains
113113
! hypoelasticity is supported: stress components prolong via the generic conservative-linear
114114
! path and the swap/restore recomputes the spacing-dependent FD coefficients per grid
115115
@:PROHIBIT(hyperelasticity .or. mhd, "amr does not support hyperelasticity/MHD")
116-
@:PROHIBIT(igr, "amr does not support the IGR solver")
116+
! IGR is supported with restriction-only coarse/fine coupling (stage 1): the fine
117+
! block runs its own fixed-iteration sigma solve seeded and Dirichlet-bounded by the
118+
! converged coarse sigma; the Berger-Colella reflux is not yet captured from the
119+
! fused IGR flux kernels, so seam conservation is truncation-order, not exact
120+
@:PROHIBIT(igr .and. amr_subcycle, "amr_subcycle with the IGR solver is not yet supported (lockstep only)")
117121
! Lagrangian bubbles are supported with the cloud EXCLUDED from fine blocks (two-way
118122
! coupling lives on the coarse grid): regrid suppresses tags and clips boxes around
119123
! the cloud's padded bbox, and a per-stage guard aborts if the cloud reaches a block

src/simulation/m_igr.fpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module m_igr
1818
implicit none
1919

2020
private; public :: s_initialize_igr_module, s_igr_iterative_solve, s_igr_riemann_solver, s_igr_sigma_x, s_igr_flux_add, &
21-
& s_finalize_igr_module
21+
& s_finalize_igr_module, jac, jac_old
2222

2323
!> @cond
2424
#ifdef __NVCOMPILER_GPU_UNIFIED_MEM
@@ -300,7 +300,10 @@ contains
300300
end do
301301
$:END_GPU_PARALLEL_LOOP()
302302

303-
call s_populate_F_igr_buffers(bc_type, jac_sf)
303+
! AMR fine advance: the block's jac ghost shell holds frozen Dirichlet data prolonged
304+
! from the converged coarse sigma (seeded by s_amr_igr_swap_sigma); the physical-BC/halo
305+
! populate would write wrong (coarse-indexed) data on the swapped block grid
306+
if (.not. amr_in_fine_advance) call s_populate_F_igr_buffers(bc_type, jac_sf)
304307
305308
if (igr_iter_solver == 1) then ! Jacobi iteration
306309
$:GPU_PARALLEL_LOOP(private='[j, k, l]', collapse=3)

0 commit comments

Comments
 (0)