Appearance
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_facepopulatesface(:) - per step (top):
resetzeroes accumulators - per stage:
accumulate_*_fluxcollect contributions fromcompute_residuals_* - end of stage:
apply_refluxreduces (MPI) and corrects the coarseq
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
- flux_register_face_t
- flux_register_object
- initialize
- register_face
- accumulate_coarse_flux
- accumulate_fine_flux
- reduce_fine_sums
- reset
- destroy
Variables
| Name | Type | Attributes | Description |
|---|---|---|---|
SEAM_KIND_INTRA_REALM_AMR | integer(kind=I4P) | parameter | Coarse-fine AMR jump inside one realm. |
SEAM_KIND_INTER_REALM | integer(kind=I4P) | parameter | Inter-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
| Name | Type | Attributes | Description |
|---|---|---|---|
seam_kind | integer(kind=I4P) | SEAM_KIND_* (set by register_face). | |
coarse_realm | integer(kind=I4P) | Realm owning the coarse side (1-based; 0 sentinel = unset). | |
coarse_block | integer(kind=I4P) | Block index on the coarse side. | |
coarse_face | integer(kind=I4P) | Face code (FACE_X_MAX..FACE_Z_MIN from adam_maps_object). | |
fine_realm | integer(kind=I4P) | Realm owning the fine side (1-based; 0 sentinel = unset). | |
fine_block | integer(kind=I4P) | allocatable | Fine blocks covering this coarse face (size 4 for 2:1 refinement in 3D). |
nface_cells | integer(kind=I4P) | Coarse cells covered by this face (coarse-side count). | |
F_coarse | real(kind=R8P) | allocatable | Coarse-side end-of-step flux: (nv, nface_cells, 1) under α.r1. |
F_fine_sum | real(kind=R8P) | allocatable | Fine-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
| Name | Type | Attributes | Description |
|---|---|---|---|
is_initialized_ | logical | Flag: register has been initialized. | |
nfaces | integer(kind=I4P) | Number of registered faces (size of face(:) after initialize). | |
face | type(flux_register_face_t) | allocatable | All registered seam faces; unallocated until initialize. |
Type-Bound Procedures
| Name | Attributes | Description |
|---|---|---|
initialize | pass(self) | Allocate face(:) with nfaces capacity (called once after topology is |
register_face | pass(self) | Populate one entry in face(:); called by topology pass at init/regrid. |
accumulate_coarse_flux | pass(self) | Add a coarse-side per-stage flux to F_coarse on the matching face |
accumulate_fine_flux | pass(self) | Add a fine-side per-stage flux to F_fine_sum on the matching face |
reduce_fine_sums | pass(self) | MPI-reduce F_fine_sum across ranks so coarse owner has complete sum (Phase |
reset | pass(self) | Zero F_coarse and F_fine_sum on every face; called at top-of-step. |
destroy | pass(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
| Name | Type | Intent | Attributes | Description |
|---|---|---|---|---|
self | class(flux_register_object) | inout | The register. | |
nfaces | integer(kind=I4P) | in | Number 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
| Name | Type | Intent | Attributes | Description |
|---|---|---|---|---|
self | class(flux_register_object) | inout | The register. | |
face_index | integer(kind=I4P) | in | Index into face(:) to populate (1-based, 1..nfaces). | |
seam_kind | integer(kind=I4P) | in | SEAM_KIND_*. | |
coarse_realm | integer(kind=I4P) | in | Realm owning the coarse side (1-based). | |
coarse_block | integer(kind=I4P) | in | Block index on the coarse side. | |
coarse_face | integer(kind=I4P) | in | Face code (FACE_X_MAX..FACE_Z_MIN). | |
fine_realm | integer(kind=I4P) | in | Realm owning the fine side (1-based). | |
fine_block | integer(kind=I4P) | in | optional | Fine blocks covering the coarse face; |
nface_cells | integer(kind=I4P) | in | Coarse cells covered by this face. | |
nv | integer(kind=I4P) | in | Number of variables (state-vector width). | |
n_stages | integer(kind=I4P) | in | Realm'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
| Name | Type | Intent | Attributes | Description |
|---|---|---|---|---|
self | class(flux_register_object) | inout | The register. | |
face_index | integer(kind=I4P) | in | Index into face(:) of the seam face being updated. | |
stage | integer(kind=I4P) | in | Integrator stage 1..n_stages. | |
flux_face | real(kind=R8P) | in | Per-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
| Name | Type | Intent | Attributes | Description |
|---|---|---|---|---|
self | class(flux_register_object) | inout | The register. | |
face_index | integer(kind=I4P) | in | Index into face(:) of the seam face being updated. | |
stage | integer(kind=I4P) | in | Integrator stage 1..n_stages. | |
flux_face | real(kind=R8P) | in | Per-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
| Name | Type | Intent | Attributes | Description |
|---|---|---|---|---|
self | class(flux_register_object) | inout | The 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
| Name | Type | Intent | Attributes | Description |
|---|---|---|---|---|
self | class(flux_register_object) | inout | The register. |
Call graph
destroy
Deallocate face(:) and reset state to uninitialized.
fortran
subroutine destroy(self)Arguments
| Name | Type | Intent | Attributes | Description |
|---|---|---|---|---|
self | class(flux_register_object) | inout | The register. |
Call graph