Skip to content

Commit 9e0765a

Browse files
committed
fix(amr): apply review round-3 findings - (1) the Lagrangian-cloud re-clip verification after body expansion/merge ran only under acoustic_source, so an expanded box could silently regrow over the cloud in ib+lagrangian configs (fail-closed check now covers both exclusions); (2) the per-stage cloud guard ran only on block-owner ranks, missing bubbles whose smearing support reaches a block across a rank seam (now every rank checks its local bubbles against the global block box); (3) the moving-body containment guard was skipped entirely on static blocks (now always on for moving bodies, margin 0 there since validated static configs sit tighter than the regrid margin); (4) the position-to-cell-index conversions (int((x-beg)/dx(0))) in the IB expansion/guard and cloud exclusion assume uniform spacing and dx(0) is rank-local on stretched grids (rank-inconsistent regrid boxes) - stretched+lagrangian and stretched+ib-dynamic-regrid now fail closed at init (checker+validator). Comment/docs truth pass: fill_fine_ghosts_pbmv doc described the reverted selector design, nonpoly QBMM rows still said regrid/subcycle gated (both supported), amr.md mandatory settings said WENO-only (IGR allowed), recompute-flag comments still axisym-only. New goldens close the stretched coverage gaps: 79B334C7 (2-rank stretched: first exercise of the start_idx offset terms where a wrong offset changes coordinates), B7704247 (2D stretch_y: first exercise of the y-direction bisection formula; 2 regrids, block spans 65% y-spacing variation). 20-golden battery green incl. all touched features
1 parent 4794356 commit 9e0765a

9 files changed

Lines changed: 541 additions & 41 deletions

File tree

docs/documentation/amr.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,13 @@ a diagnostic message for unsupported combinations.
214214
| Hypoelasticity (`hypoelasticity = T`, incl. continuum damage) | Supported | Stress components prolong on the generic conservative path; the fine swap recomputes the spacing-dependent FD coefficients |
215215
| Hyperelasticity / MHD | **Not supported** | Gated (hyperelasticity has no upstream test coverage to validate against; MHD needs divergence-preserving B prolongation) |
216216
| QBMM bubbles (polytropic) | Supported | Bubble moments live in `q_cons`, injected piecewise-constant at prolongation to preserve CHyQMOM realizability |
217-
| 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 |
217+
| QBMM bubbles (non-polytropic) | Supported | 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` are both supported (the side-state bounces through the regrid and time-lerps its ghost shell) |
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 |
220220
| 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 |
223-
| 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 |
223+
| 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. Stretched grids do NOT combine with Lagrangian bubbles or dynamic regrid with immersed bodies (their position-to-cell-index conversions assume uniform spacing; init abort) |
224224
| Riemann-extrapolation BCs (`bc = -4`) | **Not supported** | Boundary-adjusted WENO coefficient rows cannot be inherited by interior blocks (checker gate) |
225225
| `active_box` | **Not supported** | Unvalidated combination |
226226
| `hybrid_weno` / `hybrid_riemann` | Supported | The sensor arrays are sized to the coarse ghost-inclusive bounds (the fine extent guard keeps fine indices inside them) and the sensor is recomputed from the live bounds every RHS call, so each level evaluates its own sensor; conservative full-WENO defaults at buffer edges |
@@ -229,7 +229,7 @@ a diagnostic message for unsupported combinations.
229229
**Mandatory solver settings.**
230230

231231
AMR requires:
232-
- WENO reconstruction: `recon_type = 1` (any order)
232+
- WENO reconstruction (`recon_type = 1`, any order) or the IGR solver (`igr = T`)
233233
- SSP-RK3 time-stepping: `time_stepper = 3`
234234
- 5- or 6-equation model: `model_eqns = 2` or `3` (for 6-eq the per-stage pressure relaxation also runs on each fine block)
235235

docs/documentation/case.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -812,10 +812,10 @@ arrays are inert stubs when `polytropic = T`), and the whole moment block is pro
812812
piecewise-constant so every fine/ghost cell inherits the coarse cell's realizable moment set
813813
(the CHyQMOM inversion needs the radius variance c20 = m20/m00 - (m10/m00)^2 to stay positive, which a
814814
per-component minmod slope could break); the moments still reflux and restrict on the standard
815-
conservative path. Non-polytropic QBMM (`polytropic = F`) is supported on a static block without
816-
subcycling: each block carries its own per-quadrature-node internal pressure and vapor mass
815+
conservative path. Non-polytropic QBMM (`polytropic = F`) is fully supported: each block carries its own
816+
per-quadrature-node internal pressure and vapor mass
817817
(pb/mv), prolonged piecewise-constant for realizability, advanced with the block's own rhs
818-
scratch, and restricted back with the moments (dynamic regrid and `amr_subcycle` remain gated).
818+
scratch, and restricted back with the moments; dynamic regrid and `amr_subcycle` are both supported.
819819
Phase change (`relax`) is supported: the cell-local, mass/energy-conserving relaxation
820820
runs on the fine solution before restriction (matching the coarse once-per-step timing).
821821
Chemistry (`chemistry = T`) is supported for reactions and advection: the species partial

src/simulation/m_amr.fpp

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,11 @@ module m_amr
112112
integer :: lag_supp_lo(3), lag_supp_hi(3)
113113
logical :: lag_supp_on = .false.
114114
logical :: amr_swapped = .false. !< paired-swap guard: a nested swap would corrupt the bounce buffers silently
115-
!> True when the coarse grid is nonuniform in an allowed way (the 2D-axisymmetric half-width axis cell): the WENO reconstruction
116-
!! coefficients are then per-cell, so the fine advance must recompute them for the block's own (uniform) grid on every swap and
117-
!! restore the coarse ones after. False on fully uniform grids - the recompute is skipped and behavior is bit-identical.
118-
logical :: amr_weno_coef_recompute = .false.
115+
!> True when the coarse grid is nonuniform (stretched grids, or 2D-axisymmetric's half-width axis cell): the spacing-dependent
116+
!! WENO coefficients are then recomputed for the ACTIVE grid on every block swap (the fine block's grid is itself nonuniform
117+
!! under stretching) and restored after. False on fully uniform grids - the recompute is skipped and behavior is bit-identical.
118+
logical :: amr_weno_coef_recompute = .false.
119+
logical :: amr_grid_stretched = .false. !< stretched coarse spacing (beyond the axisym axis half-cell; set at init)
119120
real(wp), allocatable :: sw_x_cb(:), sw_x_cc(:), sw_dx(:)
120121
real(wp), allocatable :: sw_y_cb(:), sw_y_cc(:), sw_dy(:)
121122
real(wp), allocatable :: sw_z_cb(:), sw_z_cc(:), sw_dz(:)
@@ -248,19 +249,34 @@ contains
248249
! flags are pre_process-only, so the grid itself is checked (this also catches
249250
! externally generated grids).
250251
if (maxval(dx(0:m)) - minval(dx(0:m)) > 1.e-12_wp*maxval(dx(0:m))) then
251-
amr_weno_coef_recompute = .true.
252+
amr_weno_coef_recompute = .true.; amr_grid_stretched = .true.
252253
end if
253254
if (n_glb > 0) then
254-
if (maxval(dy(0:n)) - minval(dy(0:n)) > 1.e-12_wp*maxval(dy(0:n))) then
255+
! interior nonuniformity is stretching; a lone dy(0) deviation is stretching only
256+
! when it is NOT the axisymmetric half-width axis cell
257+
if (n > 0 .and. maxval(dy(1:n)) - minval(dy(1:n)) > 1.e-12_wp*maxval(dy(1:n))) then
258+
amr_weno_coef_recompute = .true.; amr_grid_stretched = .true.
259+
end if
260+
if (abs(dy(0) - dy(min(1, n))) > 1.e-12_wp*dy(min(1, n))) then
255261
amr_weno_coef_recompute = .true.
262+
if (.not. cyl_coord) amr_grid_stretched = .true.
256263
end if
257264
end if
258265
if (p_glb > 0) then
259266
if (maxval(dz(0:p)) - minval(dz(0:p)) > 1.e-12_wp*maxval(dz(0:p))) then
260-
amr_weno_coef_recompute = .true.
267+
amr_weno_coef_recompute = .true.; amr_grid_stretched = .true.
261268
end if
262269
end if
263270
if (weno_order == 1 .or. igr) amr_weno_coef_recompute = .false. ! order 1 / IGR: no grid-dependent WENO coefficients
271+
! The IB containment expansion / moving-body guard and the Lagrangian-cloud exclusion
272+
! convert physical positions to global cell indices as int((x - beg)/dx(0)) - valid only
273+
! for uniform spacing (and dx(0) is rank-local on a stretched grid, so ranks would build
274+
! DIFFERENT regrid box sets: silently rank-inconsistent fine geometry). Fail closed.
275+
if (amr_grid_stretched .and. (bubbles_lagrange .or. (ib .and. amr_regrid_int > 0))) then
276+
call s_mpi_abort('amr on a stretched grid does not support ' &
277+
& // 'Lagrangian bubbles or dynamic regrid with immersed bodies: their ' &
278+
& // 'position-to-cell-index conversions assume uniform spacing')
279+
end if
264280

265281
! preallocate every slot at max buffered extents (each sized exactly as the single legacy block): coords (host) +
266282
! fields (device-resident @:ALLOCATE so s_compute_rhs kernels can run on the fine block; q_cons/q_prim/rhs get the
@@ -838,16 +854,18 @@ contains
838854
839855
if (.not. ib) return
840856
if (.not. amr_rank_owns_block) return
841-
! Between regrids a moving body must stay inside its block: the regrid expansion
842-
! contained it with margin max(amr_buf,4), and here we require the body plus the
843-
! image-point stencil reach (2 coarse cells) to remain contained. Consecutive
844-
! contained positions keep every sub-time interpolate contained (axis-aligned
845-
! boxes are convex in the linearly-moving corners). A body that overlaps the
846-
! block edge would get silently clipped forcing - abort instead.
847-
if (amr_regrid_int > 0) then
857+
! A moving body must stay inside its block (a body overlapping the block edge gets
858+
! silently clipped forcing - abort instead). Under dynamic regrid the expansion
859+
! contained it with margin max(amr_buf,4) and we require body + image-point stencil
860+
! reach (2 coarse cells) to remain contained between regrids; on a STATIC block the
861+
! user's placement is authoritative (validated configs sit tighter than the regrid
862+
! margin), so only the body bbox itself must stay inside. Consecutive contained
863+
! positions keep every sub-time interpolate contained (axis-aligned boxes are
864+
! convex in the linearly-moving corners).
865+
if (any(patch_ib(1:num_ibs)%moving_ibm /= 0)) then
848866
do i = 1, num_ibs
849867
if (patch_ib(i)%moving_ibm == 0) cycle
850-
call s_amr_body_bbox(i, 2, blo, bhi)
868+
call s_amr_body_bbox(i, merge(2, 0, amr_regrid_int > 0), blo, bhi)
851869
ovl = blo(1) <= amr_slots(amr_cur)%region%hi(1) .and. bhi(1) >= amr_slots(amr_cur)%region%lo(1)
852870
if (n_glb > 0) ovl = ovl .and. blo(2) <= amr_slots(amr_cur)%region%hi(2) .and. bhi(2) &
853871
& >= amr_slots(amr_cur)%region%lo(2)
@@ -860,8 +878,9 @@ contains
860878
if (p_glb > 0) inside = inside .and. blo(3) >= amr_slots(amr_cur)%region%lo(3) .and. bhi(3) &
861879
& <= amr_slots(amr_cur)%region%hi(3)
862880
if (.not. inside) then
863-
call s_mpi_abort('amr dynamic regrid with moving ib: the body reached the fine-block ' &
864-
& // 'boundary between regrids; reduce amr_regrid_int or increase amr_buf')
881+
call s_mpi_abort('amr with moving ib: the body reached the fine-block boundary; ' &
882+
& // 'under dynamic regrid reduce amr_regrid_int or increase amr_buf, ' &
883+
& // 'for a static block enlarge it to contain the trajectory')
865884
end if
866885
end do
867886
end if
@@ -917,10 +936,9 @@ contains
917936
918937
end subroutine s_restrict_all_vars
919938
920-
!> Non-polytropic QBMM: piecewise-constant HOST prolongation of the coarse pb/mv side-state onto the block's fine interior, then
921-
!! push to the device. Piecewise-constant preserves the per-cell realizable quadrature set (mirroring the moments' injection).
922-
!! Called at init populate and at restart (the fine side-state is re-prolonged from the restored coarse fields - a one-time
923-
!! smoothing; q_cons is restored exactly).
939+
!> Non-polytropic QBMM: piecewise-constant prolongation of the block's pb/mv interior from the coarse side-state. HOST loops +
940+
!! device push; all three callers make the coarse host mirrors current first: init populate and restart read pb/mv on the host,
941+
!! the regrid path refreshes them from the device before re-prolonging.
924942
impure subroutine s_amr_prolong_pbmv()
925943

926944
integer :: fi, fj, fk, q, ib_, ci, cj, ck, rr, lo1, lo2, lo3, ox, oy, oz
@@ -957,10 +975,8 @@ contains
957975

958976
!> Non-polytropic QBMM: piecewise-constant prolongation of the fine pb/mv GHOST shell from the coarse side-state (device kernel;
959977
!! interior untouched). The ghosts feed the widened-idwint conversions and the qbmm rhs over the shell, mirroring the q_cons
960-
!! ghost fill. The fine target is selected by integer (1 = pb_f/mv_f, 2 = ghost_a, 3 = ghost_b) and the slot arrays are
961-
!! referenced DIRECTLY inside the kernels: passing an amr_slots(amr_cur) component as an assumed-shape actual makes nvfortran
962-
!! emit a component-section data clause that fails partially-present on device (the coarse sources pb_c/mv_c follow the proven
963-
!! pb_ts pattern).
978+
!! ghost fill. All four arrays are assumed-shape dummies with %sf pointer-member actuals (the device-proven pb_ts pattern); only
979+
!! raw derived-type 5D members as actuals tripped nvfortran's component-section data clauses on device.
964980
impure subroutine s_amr_fill_fine_ghosts_pbmv(pb_c, mv_c, pb_t, mv_t)
965981
966982
real(stp), dimension(idwbuff(1)%beg:,idwbuff(2)%beg:,idwbuff(3)%beg:,1:,1:), intent(in) :: pb_c, mv_c
@@ -1279,8 +1295,8 @@ contains
12791295
! hypoelastic stress sources use grid-spacing-dependent FD coefficients: recompute
12801296
! them from the (now fine) grid, else every fine velocity gradient is halved
12811297
if (hypoelasticity) call s_hypoelastic_update_fd_coeffs()
1282-
! nonuniform coarse grid (axisymmetric axis half-cell): the per-cell WENO coefficients
1283-
! must be rebuilt for the block's own uniform grid (no-op flag on uniform grids)
1298+
! nonuniform coarse grid (stretched, or the axisymmetric axis half-cell): the per-cell
1299+
! WENO coefficients must be rebuilt for the block's own grid (no-op flag on uniform grids)
12841300
if (amr_weno_coef_recompute) call s_amr_recompute_weno_coefs()
12851301

12861302
! IGR: save the coarse sigma state and seed the fine solve. jac holds THIS stage's
@@ -1698,8 +1714,10 @@ contains
16981714
if (.not. amr) return
16991715
! valid coarse CONS ghosts for the ghost prolongation below (ALL ranks call: pairwise halo)
17001716
if (amr_xchg_coarse_ghosts) call s_amr_exchange_coarse_cons_halo(q_cons_coarse)
1701-
if (.not. amr_rank_owns_block) return
1717+
! the Lagrangian-cloud guard runs on EVERY rank (rank-local bubbles vs the block's
1718+
! GLOBAL box): a non-owner rank's bubbles can reach into a block across a rank seam
17021719
call s_amr_check_lag_clear()
1720+
if (.not. amr_rank_owns_block) return
17031721
17041722
! ghost prolongation from the coarse stage-entry conservative state (device kernel reads the
17051723
! device-current coarse stage-entry state directly); rank_time brackets cover the fine-advance
@@ -1790,7 +1808,7 @@ contains
17901808
call s_amr_exchange_coarse_cons_halo(q_old)
17911809
call s_amr_exchange_coarse_cons_halo(q_new)
17921810
end if
1793-
if (amr_rank_owns_block) call s_amr_check_lag_clear()
1811+
call s_amr_check_lag_clear() ! EVERY rank: non-owner bubbles can reach the block across a seam
17941812
if (.not. amr_rank_owns_block) return
17951813
17961814
! fill both lerp sources once: ghost shells prolonged from coarse t^n and t^{n+1} (device kernels
@@ -2161,8 +2179,9 @@ contains
21612179
call s_mpi_abort('amr dynamic regrid with ib: unsupported body geometry for the ' &
21622180
& // 'containment bounding box (supported: circle/rectangle/sphere/box/cylinder)')
21632181
end select
2164-
! physical bbox -> global coarse indices (uniform spacing is enforced for amr; the
2165-
! axisymmetric half axis cell only shrinks dy(0), so the floor is still conservative)
2182+
! physical bbox -> global coarse indices: valid for uniform spacing only (stretched
2183+
! grids with ib-dynamic-regrid/Lagrangian are aborted at init; the axisymmetric half
2184+
! axis cell only shrinks dy(0), so the floor is still conservative)
21662185
blo(1) = int((c(1) - half(1) - x_domain%beg)/dx(0)) - mrg
21672186
bhi(1) = int((c(1) + half(1) - x_domain%beg)/dx(0)) + mrg
21682187
blo(2) = 0; bhi(2) = 0; blo(3) = 0; bhi(3) = 0
@@ -2455,12 +2474,13 @@ contains
24552474
end do
24562475
end do outer
24572476
end do
2458-
! the expansion may also have grown a box onto an acoustic source support: the two
2459-
! constraints (contain the body, exclude the source) cannot both hold - fail closed
2460-
if (acoustic_source) then
2477+
! the expansion may also have grown a box onto an acoustic source support or the
2478+
! Lagrangian cloud: the constraints (contain the body, exclude the source/cloud)
2479+
! cannot both hold - fail closed
2480+
if (acoustic_source .or. (bubbles_lagrange .and. lag_supp_on)) then
24612481
do k = 1, nboxes
24622482
lo = boxes(k)%lo; hi = boxes(k)%hi
2463-
call s_amr_clip_box_from_sources(lo, hi)
2483+
if (acoustic_source) call s_amr_clip_box_from_sources(lo, hi)
24642484
if (bubbles_lagrange .and. lag_supp_on) call s_amr_clip_box_from_supp(lo, hi, lag_supp_lo, lag_supp_hi)
24652485
if (any(lo /= boxes(k)%lo) .or. any(hi /= boxes(k)%hi)) then
24662486
call s_mpi_abort('amr regrid: a block must contain an immersed body AND stay ' &

0 commit comments

Comments
 (0)