@@ -53,7 +53,7 @@ module m_amr
5353 !> Block/slot state and fine-distribution services consumed by m_amr_regrid and m_amr_restart (the drivers split out of this
5454 !! module). State stays HERE - only the drivers moved.
5555 public :: amr_rg_gather, s_amr_fine_stage_advance_batched
56- public :: s_amr_build_gather_plan, amr_gpl_valid
56+ public :: s_amr_build_gather_plan, amr_gpl_valid, amr_gpk, amr_n_gpk, amr_slot_live
5757 public :: s_amr_gather_chunk_post, s_amr_gather_chunk_send, s_amr_gather_consume_box, amr_gath_chunk, s_amr_cov_note, &
5858 & amr_cad_tot, amr_cad_esc, amr_cad_armed
5959 public :: amr_slots, amr_cons_st, amr_stor_st, amr_loc_of, amr_seam_pairs_dirty, amr_mesh_epoch, amr_tag_base, &
@@ -330,6 +330,15 @@ module m_amr
330330 !! reproduces today's message set exactly.
331331 integer, allocatable :: amr_gpl_nsrc(:), amr_gpl_src(:,:), amr_gpl_sz(:,:), amr_gpl_psrc(:), amr_gpl_psz(:)
332332 logical :: amr_gpl_valid = .false. !< true only between plan build and the end of the rebuild box loop
333+ !> The rebuild's PARTICIPANT list: the ascending union of amr_my_blk (owner - posts, consumes), amr_fch_blk (owner of a foreign
334+ !! child's parent - the level>=2 send) and amr_l1p_blk (level-1 contributor - the send phase and the pb/mv gather), fine band
335+ !! only; the consumers keep their original per-box predicates, the list only drops boxes they would have cycled. The box loop
336+ !! used to visit every box in the machine on every rank - rb:gath calls/rank 4282 -> 43816 over np64 -> np512 with ms/call flat
337+ !! (the constant-density ladder, ledger 53) - and a box this rank has no role in touches nothing of its own but the replicated
338+ !! non-owner geometry, which the rebuild now fills in one plain pass. Built by s_amr_build_gather_plan from the epoch-keyed
339+ !! lists; valid exactly as long as amr_gpl_valid.
340+ integer, allocatable :: amr_gpk(:)
341+ integer :: amr_n_gpk = 0
333342 !> Chunked rebuild gather (step 2, amr_regrid_gather_batching.md): the rebuild box loop runs in chunks of amr_gath_chunk boxes -
334343 !! every owned box's recvs (level-1 contributor slices AND split level>=2 parent patches) are pre-posted from the plan into one
335344 !! flat pool, this rank's sends are issued (level>=2 only when the parent was consumed in an EARLIER chunk - a same-chunk
@@ -1126,28 +1135,54 @@ contains
11261135 !! (amr_region_*_all, amr_ovl_gather, amr_block_owner, rank coarse ranges, s_amr_parent_foot). Exchange behavior is UNCHANGED by
11271136 !! this step: the per-box gather asserts against the plan, box by box (see amr_gpl_* declarations). Caller
11281137 !! (s_amr_regrid_rebuild_slots) clears amr_gpl_valid when its box loop ends.
1129- impure subroutine s_amr_build_gather_plan(nboxes )
1138+ impure subroutine s_amr_build_gather_plan()
11301139
1131- integer, intent(in) :: nboxes
1132- integer :: k, ks, idx, r, nsrc, mo, pblk
1133- integer :: v1hi, v2hi, v3hi, plo(3), phi(3), crlo(3), crhi(3), bl(3), bh(3), w(3)
1140+ integer :: i, ks, idx, r, nsrc, mo, pblk, im, ifc, ip, km, kf, kp
1141+ integer :: v1hi, v2hi, v3hi, plo(3), phi(3), crlo(3), crhi(3), bl(3), bh(3), w(3)
11341142
11351143 ! the per-box path lazily rebuilds the overlap lists inside the first gather; force the SAME rebuild here so the plan
11361144 ! and the boxes read identical lists
11371145
11381146 if (amr_seam_pairs_dirty .or. amr_seam_pairs_nblk /= amr_num_blocks) call s_amr_build_seam_pairs()
1147+ ! the migrate step installed the new regions/levels/owners and bumped the epoch, so the lists rebuild on the NEW mesh here
1148+ ! (amr_own_blk reads amr_owns_all, still the previous generation until the geometry pass - nothing in the rebuild reads
1149+ ! it, and the reconcile's epoch bump rebuilds it before the first stage does)
1150+ call s_amr_refresh_my_blocks()
1151+ call s_amr_refresh_lists()
1152+ if (allocated(amr_gpk)) then
1153+ if (size(amr_gpk) < amr_max_blocks) deallocate (amr_gpk)
1154+ end if
1155+ if (.not. allocated(amr_gpk)) allocate (amr_gpk(amr_max_blocks))
1156+ ! three-cursor ascending merge; the L0 tile prefix (slots <= l0_slot_off, owned like any block) carries no regrid box
1157+ amr_n_gpk = 0
1158+ im = 1; ifc = 1; ip = 1
1159+ do
1160+ km = huge(1); kf = huge(1); kp = huge(1)
1161+ if (im <= amr_n_my) km = amr_my_blk(im)
1162+ if (ifc <= amr_n_fch) kf = amr_fch_blk(ifc)
1163+ if (ip <= amr_n_l1p) kp = amr_l1p_blk(ip)
1164+ ks = min(km, kf, kp)
1165+ if (ks == huge(1)) exit
1166+ if (km == ks) im = im + 1
1167+ if (kf == ks) ifc = ifc + 1
1168+ if (kp == ks) ip = ip + 1
1169+ if (ks <= l0_slot_off) cycle
1170+ amr_n_gpk = amr_n_gpk + 1
1171+ amr_gpk(amr_n_gpk) = ks
1172+ end do
11391173 mo = size(amr_ovl_gather, 1)
11401174 if (allocated(amr_gpl_src)) then
11411175 if (size(amr_gpl_src, 1) < mo) deallocate (amr_gpl_src, amr_gpl_sz)
11421176 end if
11431177 if (.not. allocated(amr_gpl_nsrc)) allocate (amr_gpl_nsrc(amr_max_blocks), amr_gpl_psrc(amr_max_blocks), &
11441178 & amr_gpl_psz(amr_max_blocks))
11451179 if (.not. allocated(amr_gpl_src)) allocate (amr_gpl_src(mo, amr_max_blocks), amr_gpl_sz(mo, amr_max_blocks))
1146- do k = 1, nboxes
1147- ks = f_l0_slot(k)
1180+ ! plan entries for the participants only: they are the only boxes the chunk post/send/consume below ever look up
1181+ do i = 1, amr_n_gpk
1182+ ks = amr_gpk(i)
11481183 amr_gpl_nsrc(ks) = 0; amr_gpl_psrc(ks) = -1; amr_gpl_psz(ks) = 0
11491184 if (amr_block_level(ks) >= 2) then
1150- pblk = f_amr_parent_block (ks)
1185+ pblk = amr_parent_blk (ks)
11511186 if (amr_block_owner(pblk) /= amr_block_owner(ks)) then
11521187 call s_amr_parent_foot(ks, pblk, plo, phi)
11531188 w = 0
@@ -1189,17 +1224,18 @@ contains
11891224 !! parent patches - straight from the plan into the flat chunk pool, tag = slot, appended in box order so box k's requests are
11901225 !! one contiguous run. Ownership from amr_block_owner ONLY (amr_owns_all / amr_rank_owns_block still mirror the previous
11911226 !! generation until the consume phase's geometry call). Contains no MPI waits; reallocating the pool here is safe because every
1192- !! recv posted for the previous chunk was completed inside that chunk's consume phase.
1193- impure subroutine s_amr_gather_chunk_post(c_lo, c_hi)
1227+ !! recv posted for the previous chunk was completed inside that chunk's consume phase. The chunk's boxes this rank has a role in
1228+ !! are amr_gpk(i0:i1); c_lo is the chunk's first box (chunk-local indexing of the request runs).
1229+ impure subroutine s_amr_gather_chunk_post(c_lo, i0, i1)
11941230
1195- integer, intent(in) :: c_lo, c_hi
1196- integer :: k , ks, cb, idx, need, nreq, off, ierr
1231+ integer, intent(in) :: c_lo, i0, i1
1232+ integer :: i , ks, cb, idx, need, nreq, off, ierr
11971233
11981234 @:ASSERT(amr_gpl_valid, "chunk gather: no plan")
11991235 call s_phase_tic(PH_RBPOST)
12001236 need = 0; nreq = 0
1201- do k = c_lo, c_hi
1202- ks = f_l0_slot(k )
1237+ do i = i0, i1
1238+ ks = amr_gpk(i )
12031239 if (amr_block_owner(ks) /= proc_rank) cycle
12041240 ! + XA_NH per message: the I1b identity header rides ahead of each payload (zero in production)
12051241 if (amr_block_level(ks) >= 2) then
@@ -1223,12 +1259,12 @@ contains
12231259 if (nreq > 0 .and. .not. allocated(amr_gcr_req)) allocate (amr_gcr_req(nreq), amr_gcr_off(nreq))
12241260
12251261 amr_gcr_n = 0; off = 0
1226- amr_gcr_nr(:) = 0; amr_gcr_sent(:) = .false.
1227- do k = c_lo, c_hi
1228- ks = f_l0_slot(k)
1229- cb = k - c_lo + 1
1230- amr_gcr_r0(cb) = amr_gcr_n + 1
1262+ amr_gcr_r0(:) = 1; amr_gcr_nr(:) = 0; amr_gcr_sent(:) = .false.
1263+ do i = i0, i1
1264+ ks = amr_gpk(i)
12311265 if (amr_block_owner(ks) /= proc_rank) cycle
1266+ cb = ks - l0_slot_off - c_lo + 1
1267+ amr_gcr_r0(cb) = amr_gcr_n + 1
12321268#ifdef MFC_MPI
12331269 if (amr_block_level(ks) >= 2) then
12341270 if (amr_gpl_psrc(ks) >= 0) then
@@ -1262,24 +1298,25 @@ contains
12621298 !! split pairs: send ONLY when the parent was consumed in an earlier chunk (pblk < f_l0_slot(c_lo), monotone slot map) - a
12631299 !! same-chunk parent's new-generation store is not built until its own consume iteration, so that send stays at the child's
12641300 !! consume position, where parents-first ordering guarantees the parent is complete. Geometry from the replicated caches only:
1265- !! no s_set_amr_fine_geometry swap, no amr_cur.
1266- impure subroutine s_amr_gather_chunk_send(q_coarse, c_lo, c_hi)
1301+ !! no s_set_amr_fine_geometry swap, no amr_cur. Walks the chunk's participants amr_gpk(i0:i1) with the ORIGINAL per-box
1302+ !! predicates intact: the list only drops boxes that would have cycled (a sender is a parent-owner or a level-1 contributor).
1303+ impure subroutine s_amr_gather_chunk_send(q_coarse, c_lo, i0, i1)
12671304
12681305 type(scalar_field), dimension(sys_size), intent(in) :: q_coarse
1269- integer, intent(in) :: c_lo, c_hi
1270- integer :: k, ks, cb, idx, i, g1, g2, g3, o1, o2, o3, boxsz, maxsz, pblk, ierr
1306+ integer, intent(in) :: c_lo, i0, i1
1307+ integer :: ks, cb, idx, i, ii , g1, g2, g3, o1, o2, o3, boxsz, maxsz, pblk, ierr
12711308 integer :: v1hi, v2hi, v3hi, plo(3), phi(3), crlo(3), crhi(3), bl(3), bh(3)
12721309 logical :: contrib
12731310
12741311 o1 = start_idx(1); o2 = 0; o3 = 0
12751312 if (n_glb > 0) o2 = start_idx(2)
12761313 if (p_glb > 0) o3 = start_idx(3)
1277- do k = c_lo, c_hi
1278- ks = f_l0_slot(k )
1279- cb = k - c_lo + 1
1314+ do ii = i0, i1
1315+ ks = amr_gpk(ii )
1316+ cb = ks - l0_slot_off - c_lo + 1
12801317 if (amr_block_level(ks) >= 2) then
12811318 if (amr_gpl_psrc(ks) < 0) cycle ! co-located: no message
1282- pblk = f_amr_parent_block (ks)
1319+ pblk = amr_parent_blk (ks)
12831320 if (amr_block_owner(pblk) /= proc_rank) cycle ! not the sender
12841321 if (pblk >= f_l0_slot(c_lo)) cycle ! same-chunk parent: send at the child's consume position
12851322 call s_phase_tic(PH_PGSEND)
@@ -1342,8 +1379,8 @@ contains
13421379 !! copy, one WAITALL on this box's contiguous request run, host unpack per contributor (plan order = posting order), device
13431380 !! push. Level>=2: co-located parent = local device copy; split parent = the parent owner packs and sends HERE when the parent
13441381 !! shares this chunk (amr_gcr_sent marks the ones phase B already covered), the child owner waits and device-unpacks its single
1345- !! pre-posted recv. Every rank passes through for every box (the caller's owner-cycle comes after), which is what makes the
1346- !! request arrays reusable next chunk.
1382+ !! pre-posted recv. Called for the boxes this rank owns or parents (the caller's owner-cycle comes after); every owned box's
1383+ !! requests are waited unconditionally inside its own chunk, which is what makes the request arrays reusable next chunk.
13471384 impure subroutine s_amr_gather_consume_box(q_coarse, k, c_lo)
13481385
13491386 type(scalar_field), dimension(sys_size), intent(in) :: q_coarse
@@ -1356,7 +1393,7 @@ contains
13561393
13571394 if (amr_block_level(amr_cur) >= 2) then
13581395 call s_phase_tic(PH_PGALL)
1359- pblk = f_amr_parent_block (amr_cur)
1396+ pblk = amr_parent_blk (amr_cur)
13601397 ! the deferred same-chunk send below reads the parent's store, valid ONLY because parents-first ordering already
13611398 ! consumed the parent (D1 in amr_regrid_gather_batching.md) - trip immediately if the ordering is ever violated
13621399 @:ASSERT(pblk < f_l0_slot(k), "chunk gather: parent box not before child")
@@ -1494,14 +1531,15 @@ contains
14941531 !! words vs words prolonged into cells the same-level carry-forward overwrites (old same-level regions are disjoint, so the
14951532 !! intersections sum exactly; each shrunk by 1 per face for the minmod stencil - conservative). Level>=2: patch words only - no
14961533 !! carry-forward exists, the patch is live by construction.
1497- impure subroutine s_amr_cov_note(old_np , old_ilo, old_ext, old_level)
1534+ impure subroutine s_amr_cov_note(nh, held , old_ilo, old_ext, old_level)
14981535
1499- integer, intent(in) :: old_np, old_ilo(:,:), old_ext(:,:), old_level(:)
1500- integer :: kk, pblk, plo(3), phi(3), olo(3), ohi(3), bl(3), bh(3), r1, r2, r3
1536+ !> held(1:nh): the old blocks this rank holds a stash of - the only ones an owned new box can overlap (migration invariant)
1537+ integer, intent(in) :: nh, held(:), old_ilo(:,:), old_ext(:,:), old_level(:)
1538+ integer :: hh, kk, pblk, plo(3), phi(3), olo(3), ohi(3), bl(3), bh(3), r1, r2, r3
15011539 integer(8) :: words
15021540
15031541 if (amr_block_level(amr_cur) >= 2) then
1504- pblk = f_amr_parent_block (amr_cur)
1542+ pblk = amr_parent_blk (amr_cur)
15051543 call s_amr_parent_foot(amr_cur, pblk, plo, phi)
15061544 r1 = phi(1) - plo(1) + 1
15071545 r2 = 1; r3 = 1
@@ -1517,7 +1555,8 @@ contains
15171555 if (p_glb > 0) r3 = amr_region_hi_all(3, amr_cur) - amr_region_lo_all(3, amr_cur) + 1
15181556 amr_cov_tot(2) = amr_cov_tot(2) + int(sys_size, 8)*int(r1 + 2*amr_cpat_mar, 8)*int(merge(r2 + 2*amr_cpat_mar, 1, &
15191557 & n_glb > 0), 8)*int(merge(r3 + 2*amr_cpat_mar, 1, p_glb > 0), 8)
1520- do kk = 1, old_np
1558+ do hh = 1, nh
1559+ kk = held(hh)
15211560 if (old_level(kk) /= amr_block_level(amr_cur)) cycle
15221561 olo = old_ilo(:,kk)
15231562 ohi = olo
@@ -10589,7 +10628,7 @@ contains
1058910628 if (allocated(amr_fw_rp)) deallocate (amr_fw_rp)
1059010629 if (allocated(amr_fw_req)) deallocate (amr_fw_req, amr_fw_reqw)
1059110630 #:for A in ['amr_my_blk', 'amr_l1r_blk', 'amr_l1p_blk', 'amr_fch_blk', 'amr_own_blk', 'amr_parent_blk', &
10592- 'amr_child_ptr', 'amr_child_idx']
10631+ 'amr_child_ptr', 'amr_child_idx', 'amr_gpk' ]
1059310632 if (allocated(${A}$)) deallocate (${A}$)
1059410633 #:endfor
1059510634 do i = 1, sys_size
0 commit comments