Skip to content

adam_flux_register_object

ADAM, flux register class definition

Berger-Colella reflux machinery for coarse-fine interfaces. Accumulates per-stage fluxes on registered seam faces from both the coarse and the fine side; at end-of-stage, applies the stage-weighted correction (Δt/Δx)·(F_coarse_used − Σ F_fine) to the coarse-side conserved variables.

Integrator-agnostic vocabulary: the per-face accumulator's third axis is nominally sized by n_stages (the realm's stages_per_step_forest() return value). For RK realms this happens to equal rk%nrk, but the register API uses the neutral name so a non-RK integrator can register / accumulate / correct against the same machinery without any vocabulary mismatch.

α.r1 (current operating mode): third-axis collapsed to 1. Under the α end-of-step barrier seam coupling, per-stage RK-weighted reflux refinement (Wang 2018) is dropped to admit asymmetric per-realm stage counts (K_realm(is) /= K_max). The accumulator collapses to a single end-of-step bucket: F_coarse(:,:,1) and F_fine_sum(:,:,1) hold the realm-final-stage face flux, and the correction is applied once per realm per step at stage == stages_per_step_forest(). The n_stages argument on register_face is retained for API stability but the third-axis allocation is forced to 1 regardless of the value passed; the per-stage discipline at the call site lives in the caller (the forest passes n_stages=1, PRISM's accumulate_seam_fluxes_fv passes stage=1 and gates the accumulation on stage_idx == rk%nrk). This is the AMReX Reflux cadence; same-K and asymmetric-K both reduce to the same Berger-Colella end-of-step correction.

See docs/guide/forest.md for the conceptual overview of seam coupling cadence and src/lib/common/README.md → "Forest orchestration" for the contract surface.

Two seam kinds share the same accumulator topology:

  • SEAM_KIND_INTRA_REALM_AMR — fine-coarse adjacency between two AMR levels inside one realm. Fine and coarse sides typically co-resident on the same rank (ADAM keeps levels co-resident by construction).
  • SEAM_KIND_INTER_REALM — face coupling between two realms in a forest. Fine and coarse sides may live on different ranks; cross- rank seam transport is not yet implemented.

Lifecycle (driven by forest_object):

  • init / regrid: register_face populates face(:)
  • per step (top): reset zeroes accumulators
  • per stage: accumulate_*_flux collect contributions from compute_residuals_*
  • end of stage: apply_reflux reduces (MPI) and corrects the coarse q

Memory cost is O(seam-area × nv × n_stages), bounded by the surface area of the AMR boundaries — independent of the interior cell count.

Architectural invariants:

  • All accumulators are intrinsic-typed arrays; no derived-type pointer components reachable from kernels.
  • The register owns no field data — only the per-face flux skin. Field pointers (q, dq) are threaded in by callers, not stored.

Seam-kind codes (see flux_register_face_t%seam_kind).

Encoded as small integers (rather than a Fortran enum) for the same reason the FACE_* codes in adam_maps_object are: they participate in checkpoint dumps and INI-level diagnostics, so stable integer values are wanted.

Source: src/lib/common/adam_flux_register_object.F90

Dependencies

Contents

Variables

NameTypeAttributesDescription
SEAM_KIND_INTRA_REALM_AMRinteger(kind=I4P)parameterCoarse-fine AMR jump inside one realm.
SEAM_KIND_INTER_REALMinteger(kind=I4P)parameterInter-realm seam between two realms in a forest.

Derived Types

flux_register_face_t

Per-seam-face flux accumulator.

One entry per coarse face touching a fine-side neighbourhood: either a fine-level patch (SEAM_KIND_INTRA_REALM_AMR) or another realm (SEAM_KIND_INTER_REALM). For a 2:1 refinement in 3D, the coarse face is covered by 4 fine faces; fine_block(:) lists them.

Both accumulators (F_coarse, F_fine_sum) are sized (nv, nface_cells, 1) under the α.r1 single-stage end-of-step contract (see module-level note). The third axis is retained dimensionally so the accumulate_*_flux(face_index, stage, flux_face) API survives unchanged; α-mode callers always pass stage = 1. The fine side accumulates into the same coarse-face skin via 2:1 averaging at the time of accumulation, so the two arrays are pointwise-comparable.

Components

NameTypeAttributesDescription
seam_kindinteger(kind=I4P)SEAM_KIND_* (set by register_face).
coarse_realminteger(kind=I4P)Realm owning the coarse side (1-based; 0 sentinel = unset).
coarse_blockinteger(kind=I4P)Block index on the coarse side.
coarse_faceinteger(kind=I4P)Face code (FACE_X_MAX..FACE_Z_MIN from adam_maps_object).
fine_realminteger(kind=I4P)Realm owning the fine side (1-based; 0 sentinel = unset).
fine_blockinteger(kind=I4P)allocatableFine blocks covering this coarse face (size 4 for 2:1 refinement in 3D).
nface_cellsinteger(kind=I4P)Coarse cells covered by this face (coarse-side count).
F_coarsereal(kind=R8P)allocatableCoarse-side end-of-step flux: (nv, nface_cells, 1) under α.r1.
F_fine_sumreal(kind=R8P)allocatableFine-side end-of-step accumulator: (nv, nface_cells, 1) under α.r1.

flux_register_object

Flux register class definition.

Owns the collection of registered seam faces. Lives on the forest (one instance per forest, shared across realms) so that the cross-realm reduce in apply_reflux has a single ownership point. Intra-realm AMR faces and inter-realm faces share the same face(:) array — they differ only in their seam_kind tag.

Components

NameTypeAttributesDescription
is_initialized_logicalFlag: register has been initialized.
nfacesinteger(kind=I4P)Number of registered faces (size of face(:) after initialize).
facetype(flux_register_face_t)allocatableAll registered seam faces; unallocated until initialize.

Type-Bound Procedures

NameAttributesDescription
initializepass(self)Allocate face(:) with nfaces capacity (called once after topology is
register_facepass(self)Populate one entry in face(:); called by topology pass at init/regrid.
accumulate_coarse_fluxpass(self)Add a coarse-side per-stage flux to F_coarse on the matching face
accumulate_fine_fluxpass(self)Add a fine-side per-stage flux to F_fine_sum on the matching face
reduce_fine_sumspass(self)MPI-reduce F_fine_sum across ranks so coarse owner has complete sum (Phase
resetpass(self)Zero F_coarse and F_fine_sum on every face; called at top-of-step.
destroypass(self)Deallocate face(:) and reset state to uninitialized.

Subroutines

initialize

Allocate face(:) with nfaces capacity.

Called once after the topology pass has counted how many seam faces exist. register_face is then called nfaces times to populate the entries.

Idempotent: a re-initialization with the same forest topology yields the same allocation; under a regrid that changes block layouts, the caller is expected to call destroy first, then initialize with the new face count. (No regrid hook yet; this is the schema-reserved path for AMR-active follow-ups.)

Calling with nfaces == 0 is valid: it flips is_initialized_ so the lifecycle hooks (reset, apply_reflux) become safe no-ops on the empty register. This is the N=1 single-realm path.

fortran
subroutine initialize(self, nfaces)

Arguments

NameTypeIntentAttributesDescription
selfclass(flux_register_object)inoutThe register.
nfacesinteger(kind=I4P)inNumber of seam faces to register.

Call graph

register_face

Populate one entry in face(:); called by topology pass at init/regrid.

For each coarse-fine adjacency discovered during topology population (in forest_object%populate_inter_realm_topology for inter-realm seams, and in the analogous AMR-jump walk for intra-realm seams), the topology pass calls this routine to fill in the corresponding face(face_index) entry. The accumulator arrays F_coarse and F_fine_sum are allocated here with shape (nv, nface_cells, n_stages) and zeroed.

Same-resolution case (current rmf-2realm with COUPLING_MIRROR and no intra-realm AMR jumps): coarse_realm and fine_realm are the two sides of the seam; the labels are conventional — both sides carry the same resolution, both fluxes are nominally equal, and the resulting correction is expected to be round-off zero in expectation. The accumulator structure is identical to the true coarse-fine AMR case; the same-resolution case just exercises the value-zero branch.

α.r1 storage convention. The n_stages argument is validated (>= 1) for API hygiene but the third-axis allocation is forced to 1 regardless of the value passed: the single end-of-step Berger- Colella correction is the same expression whether the realm uses SSP-RK-3 or SSP-RK-5, and same-K Wang-2018 per-stage refinement is deferred (see module-level note). Callers under α should pass n_stages = 1_I4P to make intent explicit, but passing the realm's stages_per_step_forest() is also accepted (the surplus depth is silently dropped). When per-stage storage is reintroduced (γ research RFC #17 or a future Wang-2018 path), this routine grows a sizing branch and the contract goes back to per-stage.

fortran
subroutine register_face(self, face_index, seam_kind, coarse_realm, coarse_block, coarse_face, fine_realm, fine_block, nface_cells, nv, n_stages)

Arguments

NameTypeIntentAttributesDescription
selfclass(flux_register_object)inoutThe register.
face_indexinteger(kind=I4P)inIndex into face(:) to populate (1-based, 1..nfaces).
seam_kindinteger(kind=I4P)inSEAM_KIND_*.
coarse_realminteger(kind=I4P)inRealm owning the coarse side (1-based).
coarse_blockinteger(kind=I4P)inBlock index on the coarse side.
coarse_faceinteger(kind=I4P)inFace code (FACE_X_MAX..FACE_Z_MIN).
fine_realminteger(kind=I4P)inRealm owning the fine side (1-based).
fine_blockinteger(kind=I4P)inoptionalFine blocks covering the coarse face;
nface_cellsinteger(kind=I4P)inCoarse cells covered by this face.
nvinteger(kind=I4P)inNumber of variables (state-vector width).
n_stagesinteger(kind=I4P)inRealm's stages_per_step_forest() (advisory under α.r1:

Call graph

accumulate_coarse_flux

Add a coarse-side per-stage flux contribution to F_coarse(:,:,stage).

Called by PRISM's compute_residuals_fv_centered when processing a face that has been identified as a coarse-side seam face. The face_index is looked up via a precomputed seam_face_index(b, face_code) table owned by the residual routine; the lookup is O(1).

Accumulation is additive: a face may receive contributions from multiple accumulate_*_flux calls within one stage (e.g. if the FV scheme processes the same face from both the cell-i and cell-(i+1) side); the register holds the running sum.

fortran
subroutine accumulate_coarse_flux(self, face_index, stage, flux_face)

Arguments

NameTypeIntentAttributesDescription
selfclass(flux_register_object)inoutThe register.
face_indexinteger(kind=I4P)inIndex into face(:) of the seam face being updated.
stageinteger(kind=I4P)inIntegrator stage 1..n_stages.
flux_facereal(kind=R8P)inPer-face flux contribution shaped (nv, nface_cells).

Call graph

accumulate_fine_flux

Add a fine-side per-stage flux contribution to F_fine_sum(:,:,stage).

Called by PRISM's compute_residuals_fv_centered when processing a face identified as a fine-side seam face. The contribution is assumed to be already mapped to the coarse-face skin (same-resolution case: identity mapping; AMR case: 2:1 averaging of 4 fine faces into one coarse slot, performed by the caller before this call). Storing the coarse-skin-shaped contribution directly keeps the register topology simple at the cost of caller-side averaging discipline for AMR.

fortran
subroutine accumulate_fine_flux(self, face_index, stage, flux_face)

Arguments

NameTypeIntentAttributesDescription
selfclass(flux_register_object)inoutThe register.
face_indexinteger(kind=I4P)inIndex into face(:) of the seam face being updated.
stageinteger(kind=I4P)inIntegrator stage 1..n_stages.
flux_facereal(kind=R8P)inPer-face flux contribution shaped (nv, nface_cells); already

Call graph

reduce_fine_sums

MPI-reduce F_fine_sum across ranks so the coarse-side rank has the full sum for the correction. Under the current replicated-forest layout (both ranks own both realms) every accumulator is already complete on every rank — this routine is a no-op in that case. Cross-rank reduce is left to a follow-up commit that adds disjoint-rank carve-out.

The reduce happens here (not in apply_reflux_corrections on forest_object) because it is purely register-internal: it exchanges accumulator data between ranks owning copies of the same face entry, without any realm-side q access.

fortran
subroutine reduce_fine_sums(self)

Arguments

NameTypeIntentAttributesDescription
selfclass(flux_register_object)inoutThe register.

Call graph

reset

Zero F_coarse and F_fine_sum on every registered face.

Called by forest_object%evolve_one_step at top-of-step. After this call, every face's accumulators are zero; per-stage flux contributions land into clean slots.

Skeleton commit: safe no-op when face(:) is unallocated (the case in this commit, since initialize does not allocate yet).

fortran
subroutine reset(self)

Arguments

NameTypeIntentAttributesDescription
selfclass(flux_register_object)inoutThe register.

Call graph

destroy

Deallocate face(:) and reset state to uninitialized.

fortran
subroutine destroy(self)

Arguments

NameTypeIntentAttributesDescription
selfclass(flux_register_object)inoutThe register.

Call graph