Skip to content

adam_forest_object ​

ADAM, forest class definition — orchestrator of a forest of realms.

The forest tends an array of realms (realm_object extensions, e.g. prism_cpu_object, prism_fnl_object). It is a behavior-only class: it owns no derived-type state. Each realm lives in the program driver as a concrete monomorphic array (type(prism_cpu_object) :: realm(N)); the forest receives the array as class(realm_object), intent(inout) :: realm(:) and orchestrates inter-realm operations:

  • sequence per-realm initialize / finalize calls
  • reduce per-realm dt to a global dt (min reduction)
  • iterate per-realm timestep advance
  • iterate per-realm post-step diagnostics / IO
  • reduce per-realm termination predicate to a global done (AND reduction)

The forest NEVER reaches inside a realm's private state; it only invokes the realm-side TBPs that carry the _forest suffix. Together these TBPs form the orchestrator contract (see adam_realm_object).

Class-with-TBPs (not module-of-routines) so future forest-level configuration (MPI sub-communicator topology, inter-realm connectivity descriptor) can be added as intrinsic-typed state without a module API break.

See docs/guide/forest.md for the conceptual overview of the multi-realm machinery (manifest schema, α/β cadence trade-offs, phase cycle) and src/lib/common/README.md → "Forest orchestration" for the library-developer contract surface.

Source: src/lib/common/adam_forest_object.F90

Dependencies

Contents ​

Derived Types ​

forest_object ​

Behavior-only orchestrator of an array of realms.

Components ​

NameTypeAttributesDescription
ninteger(kind=I4P)Number of realms in the forest (set by initialize from size(realm)).
flux_registertype(flux_register_object)Coarse-fine interface reflux machinery.
timinglogical
wtimereal(kind=R8P)

Type-Bound Procedures ​

NameAttributesDescription
initializepass(self)Sequence each realm's initialize_forest at startup (single shared INI).
initialize_from_manifestpass(self)Like initialize, but each realm reads its own INI from a forest manifest.
finalizepass(self)Sequence each realm's finalize_forest at shutdown.
compute_global_dtpass(self)Min-reduce each realm's compute_local_dt_forest across the forest.
evolve_one_steppass(self)Iterate realm(😃%advance_one_step_forest(dt) for one global timestep.
is_donepass(self)AND-reduce each realm's is_done_forest across the forest.
post_steppass(self)Iterate realm(😃%post_step_forest for the per-step diagnostics/IO block.
regridpass(self)Iterate realm(😃%regrid_forest, rebuild the register on a change (#74).
simulatepass(self)Main entry point (single shared INI): drive the full simulation.
simulate_from_manifestpass(self)Main entry point (per-realm INIs via forest manifest).
populate_inter_realm_topologypass(self)Translate manifest face-pairs into maps of neighbors.
register_intra_realm_amr_seamspass(self)Register intra-realm AMR coarse-fine faces in the flux
apply_reflux_correctionspass(self)Apply Berger-Colella reflux to coarse-side.
share_flux_registerpass(self)Copy the register into every realm (#68 A2).
start_timingpass(self)Arm the timing report (issue #53).
report_timingpass(self)Print the timing report (issue #53).

seam_rows_t ​

Growable list of integer rows, used while the seam rows are enumerated.

Components ​

NameTypeAttributesDescription
rowinteger(kind=I4P)allocatableRows (n, columns).
ninteger(kind=I4P)Rows in use.

Subroutines ​

initialize ​

Initialize the forest and every realm it tends.

fortran
subroutine initialize(self, realm, filename)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmclass(realm_object)inoutThe realms to initialize.
filenamecharacter(len=*)inInput parameters file name (shared across realms for v1).

Call graph

initialize_from_manifest ​

Initialize the forest and every realm using per-realm INIs from a manifest.

Like initialize but each realm receives its OWN INI path (manifest%realm_ini(is)) instead of a shared filename. After all realms are initialized, translates the manifest's face-pair list into per-realm maps%inter_realm_neighbors entries.

The driver MUST allocate realm(size = manifest%realms_number) with the concrete app type before calling this — the forest does not !< allocate the realm array (it cannot, since realm_object is abstract and each app has its own extension).

fortran
subroutine initialize_from_manifest(self, realm, manifest)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmclass(realm_object)inoutThe realms to initialize.
manifesttype(forest_manifest_t)inParsed manifest (per-realm INI paths + topology).

Call graph

finalize ​

Shut down the forest and every realm it tends.

Iterates realm(is)%finalize_forest in increasing index order; each realm closes its IO files, releases its resources, and finalizes its MPI handler.

fortran
subroutine finalize(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inThe forest.
realmclass(realm_object)inoutThe realms to shut down.

Call graph

compute_global_dt ​

Compute the forest-global stability-limited dt.

Each realm reports its local dt via compute_local_dt_forest; the forest takes the min across all realms (intra-process) and then across all MPI ranks (MPI_ALLREDUCE on MPI_COMM_WORLD). Returns the bit-identical global min every rank should advance by.

fortran
subroutine compute_global_dt(self, realm, dt)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inThe forest.
realmclass(realm_object)inoutThe realms to query.
dtreal(kind=R8P)outGlobal stability-limited dt.

Call graph

evolve_one_step ​

Advance every realm by one global timestep — α end-of-step barrier semantics.

N=1 fast path: realm owns the whole step (advance_one_step_forest). No seam machinery is exercised (there are no peer seams to refresh).

N>1 multi-realm path: per-seam coupling cadence (α / β).

The forest drives an integrator-agnostic K-stage clock with K_realm(is) = realm(is)%stages_per_step_forest() and K_max = max(K_realm). Asymmetric per-realm K is a first-class operating mode under α; the per-stage TBPs (begin/end_stage_forest) are gated behind k <= K_realm(is) so a realm with K < K_max no-ops the trailing stages.

Each inter-realm seam carries a coupling_cadence from the manifest (cached on the realm as seam_local_cadence(p)):

α (CADENCE_END_OF_STEP, default; AMReX-aligned coarse-fine convention) Mid-step peer ghosts are INTENTIONALLY STALE-BY-ONE-STEP. During stages 1..K, each realm reads the peer ghosts established by the previous end-of-step exchange (or by the initial-condition seam fill at populate_inter_realm_topology time, for the first step). This is structurally identical to AMReX's FillCoarsePatch reading coarse t^n data during fine sub-steps (Berger-Oliger 1984; AMReX_Amr.cpp::timeStep). Phase 5 (below) is the α seam coherence boundary: once per step, after close_step_forest, every realm's stage_active == 0 and the receiver reads peer's committed q. α admits asymmetric per-realm K (the K-equality guard is removed). Cost: first-order seam coupling in time.

β (CADENCE_STAGE_COINCIDENT, opt-in) Peer ghosts are refreshed once per RK substage, inside the K loop (Phase 2 below), before end_stage_forest reads them. Admissible only when both endpoint realms agree on (scheme_time, rk_scheme, nv, K) — enforced at forest init by check_beta_admissibility. Recovers bit-equivalence to a monolithic single-realm run on the union grid when admissible. Seam coupling order matches the per-realm interior order. Spatial operator MAY differ per realm.

Per-seam selection: the same forest may carry α seams and β seams simultaneously. Phase 2 iterates seams and fires only for β; Phase 5 iterates seams and fires only for α. A seam is filled exactly once per step under either cadence (Phase 2 may fire K times for β, but at successive substages, never duplicating the same substage).

Reflux cadence (α.r1):

The flux register's third axis is collapsed to 1. PRISM realms gate apply_reflux_to_stage_forest and accumulate_seam_fluxes_fv on the realm's own final RK substage (stage == rk%nrk). Earlier substages no-op. The mid-step apply_reflux_corrections call below therefore fires for every k, but only the k == K_realm(is) invocation does real work on realm is. Independent of α/β: β does not restore Wang 2018 per-stage RK-weighted reflux (deferred).

Phase outline:

Phase 0 — open_step_forest (per-realm prologue) For k = 1..K_max: Phase 1 — begin_stage_forest (per-realm, gated by k <= K_realm(is)) Phase 2 — fill_seam_from_peer_forest (per-seam, β-gated) Phase 3 — end_stage_forest (per-realm, gated by k <= K_realm(is)) + reduce_fine_sums + apply_reflux_corrections (reflux body is α.r1 end-of-step gated inside the realm) Phase 4 — close_step_forest (per-realm epilogue) Phase 5 — fill_seam_from_peer_forest (per-seam, α-gated)

LOAD-BEARING INVARIANT (β): Phase 2 must complete on ALL realms before Phase 3 starts on ANY realm. The orchestrator's serial inner loops within a rank give this for free under the Phase-A replicated-forest layout. If a future refactor interleaves Phase 2 and Phase 3, the read-after-overwrite race returns.

Future per-realm-dt subcycling (Berger-Colella "case 4") and γ (dense-output peer reads) are deferred. β with asymmetric K would require γ-class interpolation; not in scope.

fortran
subroutine evolve_one_step(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmclass(realm_object)inouttargetThe realms to advance.

Call graph

is_done ​

Decide whether the whole forest has finished evolving.

Each realm reports its local predicate via is_done_forest; the forest AND-reduces across all realms (intra-process) and then across all MPI ranks (MPI_ALLREDUCE on MPI_COMM_WORLD). AND-reduction means the forest keeps evolving as long as ANY realm wants to — matching the legacy single-realm semantics for v1 (with one realm the global predicate equals that realm's local one).

fortran
subroutine is_done(self, realm, done)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inThe forest.
realmclass(realm_object)inoutThe realms to query.
donelogicaloutForest-global termination predicate.

Call graph

post_step ​

Run every realm's post-step diagnostics / IO / AMR block.

fortran
subroutine post_step(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inThe forest.
realmclass(realm_object)inoutThe realms to query.

Call graph

regrid ​

Let every realm regrid if its cadence asks for it now, then rebuild what the forest owns (issue #74, M5).

Runs after the step closes and before post_step, on committed q (no stage active): the fields, histories and restart files post_step saves then describe the grid the next step runs on, so a run restarted at a regrid step continues exactly as the uninterrupted one (saved before the regrid, the restart held the old grid and the restarted run diverged from the regrid step on). A realm that regrids changes its tree, so the intra-realm 2:1 faces of the flux register (and the per-block register index the positivity seam sync and the div(B) seam band read) describe the old grid: the register is rebuilt from the new trees. Runtime regridding is single-realm (refused on a multi-realm forest at initialisation, check_runtime_amr), so no inter-realm seam row or register face needs rebuilding. The fused fast path is chosen per step from flux_register%nfaces, so a run that gains or loses 2:1 faces switches path with the rebuilt register.

fortran
subroutine regrid(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmclass(realm_object)inoutThe realms.

Call graph

simulate ​

Drive the full simulation: initialize, time-loop, finalize.

Top-level entry point the program driver calls. The time loop is: initialize → loop {evolve_one_step → post_step → is_done} → finalize. Each step invokes the orchestrator-contract TBPs on every realm; the per-realm body decides what app-specific work to do internally.

fortran
subroutine simulate(self, realm, filename)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmclass(realm_object)inoutThe realms to evolve.
filenamecharacter(len=*)inInput parameters file name.

Call graph

simulate_from_manifest ​

Drive the full simulation using per-realm INIs from a manifest.

Like simulate but uses initialize_from_manifest to populate each realm from its own INI file, and (via that initialize) wires the inter-realm topology from the manifest. The time-loop body is identical to simulate.

fortran
subroutine simulate_from_manifest(self, realm, manifest)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmclass(realm_object)inoutThe realms to evolve.
manifesttype(forest_manifest_t)inParsed manifest.

Call graph

start_timing ​

Arm the timing report when the environment sets ADAM_SEAM_TIMING=1 (issue #53): the forest times its loop, steps and reflux, every realm the phases of the seam fills of its ghosts. Off by default, the output unchanged.

fortran
subroutine start_timing(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmclass(realm_object)inoutThe realms.

Call graph

report_timing ​

Print the timing report (issue #53): the maximum over the ranks of the forest timers and, per realm, of the seam fill phases (local copy, buffers, pack, MPI, unpack) and of their sum; fills and rows summed over the ranks.

fortran
subroutine report_timing(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inThe forest.
realmclass(realm_object)inThe realms.

Call graph

check_runtime_amr ​

Refuse runtime regridding on a multi-realm forest (issue #74, D-M5-4).

The inter-realm topology (seam rows, BC_SEAM crown overrides, inter-realm register faces) is built once, at initialisation; a realm that regrids would leave it describing the old grid, silently. Until it is rebuilt per regrid, a realm that regrids must be the only one.

fortran
subroutine check_runtime_amr(realm)

Arguments

NameTypeIntentAttributesDescription
realmclass(realm_object)inInitialized realms.

Call graph

check_beta_admissibility ​

Enforce β admissibility contract on every stage_coincident seam.

For each manifest face-pair with coupling_cadence == CADENCE_STAGE_COINCIDENT, query both endpoint realms' coupling_descriptor_forest and verify they agree on (scheme_time, rk_scheme, nv). On any mismatch error_stop with a precise diagnostic naming the offending pair and the specific descriptor field that disagrees. Per-realm K participation (stages_per_step_forest()) is verified too: under β both endpoints must report the same K (asymmetric-K is α's domain, not β's).

Runs once at initialize_from_manifest time, after every realm's initialize_forest has populated numerics, rk, physics.

fortran
subroutine check_beta_admissibility(realm, manifest)

Arguments

NameTypeIntentAttributesDescription
realmclass(realm_object)inoutThe initialized realms.
manifesttype(forest_manifest_t)inParsed manifest.

Call graph

populate_inter_realm_topology ​

Translate manifest face-pairs into per-realm maps%inter_realm_neighbors.

Each manifest face-pair becomes TWO entries — one in realm_a's adam%maps%inter_realm_neighbors (looking outward toward realm_b) and one in realm_b's (looking back). Both entries carry the SAME coupling kind. Block indices are NOT resolved at this stage; the manifest declares realm-level coupling (which realm-face touches which realm-face), and the realm-side override of exchange_inter_realm_halos_forest is responsible for enumerating per-block face cells at exchange time. This keeps the manifest small and avoids encoding block layouts that depend on AMR / decomposition state not known at INI parse time.

fortran
subroutine populate_inter_realm_topology(self, realm, manifest)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmclass(realm_object)inoutInitialized realms whose adam%maps gets populated.
manifesttype(forest_manifest_t)inParsed manifest.

Call graph

check_couplings ​

Validate the coupling of every manifest face pair against the realm geometry (issue #52); error_stop otherwise.

Walks the seam leaves of both sides on the replicated trees (the same result on every rank):

  • mirror: one cell size on each side's seam face, equal across the seam;
  • refined: one cell size per side with ratio exactly 2 (either side may be the coarse one), equal block cell counts along the seam, the same [amr] seam_ghost_fill, and nested blocks: every fine seam leaf covers one 2:1 quadrant of the face of one coarse seam leaf, and even block cell counts in the fine realm;
  • periodic, interpolate: schema-reserved, refused (before issue #52 they were accepted and run as mirror).
fortran
subroutine check_couplings(realm, manifest)

Arguments

NameTypeIntentAttributesDescription
realmclass(realm_object)inoutForest realms.
manifesttype(forest_manifest_t)inParsed manifest.

Call graph

seam_sides ​

The coarse and fine sides of a face pair: for a refined seam the realm with the larger seam cells is the coarse side (issue #52), for a mirror seam realm_a by convention.

fortran
subroutine seam_sides(realm, pair, rc, fc, rf, ff, refined)

Arguments

NameTypeIntentAttributesDescription
realmclass(realm_object)inForest realms.
pairtype(forest_face_pair_t)inFace pair.
rcinteger(kind=I4P)outCoarse-side realm and face.
fcinteger(kind=I4P)outCoarse-side realm and face.
rfinteger(kind=I4P)outFine-side realm and face.
ffinteger(kind=I4P)outFine-side realm and face.
refinedlogicalout2:1 seam.

Call graph

check_nested ​

Every fine seam leaf must cover one 2:1 quadrant of the seam face of one coarse leaf: half its tangential extent, starting at its origin or at its midpoint along each tangential axis.

fortran
subroutine check_nested(coarse, fine, face_f, pfx)

Arguments

NameTypeIntentAttributesDescription
coarseclass(realm_object)inoutCoarse realm.
fineclass(realm_object)inoutFine realm.
face_finteger(kind=I4P)inFine seam face.
pfxcharacter(len=*)inError message prefix.

Call graph

build_seam_rows ​

Build the seam ghost rows of every realm from the replicated trees (issue #40).

Every rank walks, for every face pair (side A then side B), every leaf of the realm owning the ghosts that lies on the seam face, owned or not, and every ghost of its slab (tangential edge and corner ghosts included), in the tree's own order: the same sequence on every rank. The peer cell is the interior cell of the leaf of the peer realm containing the ghost centre (get_closest_block on the peer's replicated tree); ghosts outside the peer domain are corners where the seam meets a physical boundary and are left to the physical BC. Each rank keeps the rows it takes part in:

  • local (ghost and cell here): [peer, b_cell, b_ghost, i_cell, j_cell, k_cell, i_ghost, j_ghost, k_ghost];
  • receive (ghost here, cell on rank r): [r, b_ghost, i_ghost, j_ghost, k_ghost] on the ghost realm;
  • send (cell here, ghost on rank r): [r, b_cell, i_cell, j_cell, k_cell] on the cell realm.

Since the order is shared, the rows rank A sends to rank B for a seam are, in order, the rows B receives from A. The peer cell centre must coincide with the ghost centre: mirror seams join cells of the same size.

fortran
subroutine build_seam_rows(realm, manifest)

Arguments

NameTypeIntentAttributesDescription
realmclass(realm_object)inoutForest realms.
manifesttype(forest_manifest_t)inParsed manifest.

Call graph

enumerate_seam_side ​

Enumerate the seam ghosts of realm is on face, whose peer is realm ip; append this rank's rows.

fortran
subroutine enumerate_seam_side(realm, is, ip, face, local, recv, send)

Arguments

NameTypeIntentAttributesDescription
realmclass(realm_object)inoutForest realms.
isinteger(kind=I4P)inRealm owning the ghosts.
ipinteger(kind=I4P)inRealm owning the cells.
faceinteger(kind=I4P)inSeam face of realm is (FACE_* code).
localtype(seam_rows_t)inoutLocal rows of realm is.
recvtype(seam_rows_t)inoutReceive rows of realm is.
sendtype(seam_rows_t)inoutSend rows of realm ip.

Call graph

store_seam_rows ​

Store the enumerated seam rows of realm is into its maps, grouped by peer slot (and by rank inside a slot).

fortran
subroutine store_seam_rows(realm, is, manifest, local, recv, send)

Arguments

NameTypeIntentAttributesDescription
realmclass(realm_object)inoutRealm is.
isinteger(kind=I4P)inRealm index.
manifesttype(forest_manifest_t)inParsed manifest.
localtype(seam_rows_t)inLocal rows (col 1 = peer realm).
recvtype(seam_rows_t)inReceive rows (col 1 = peer realm).
sendtype(seam_rows_t)inSend rows (col 1 = ghost realm).

Call graph

group_rows ​

Group cross-rank rows [peer, rank, b, i, j, k, ...] by peer slot and, inside a slot, by ascending rank, keeping the enumeration order within a rank; store them without the peer column ([rank, b, i, j, k], plus kind, meta for send rows).

fortran
subroutine group_rows(rows, peers, cell, row_start, row_count)

Arguments

NameTypeIntentAttributesDescription
rowstype(seam_rows_t)inEnumerated rows.
peersinteger(kind=I4P)inPeer realm of each slot.
cellinteger(kind=I4P)outallocatableGrouped rows.
row_startinteger(kind=I4P)outallocatableFirst row of each slot.
row_countinteger(kind=I4P)outallocatableRows of each slot.

Call graph

realm_face_leaves ​

Morton codes of the leaves of a realm that lie on one face of its domain, in tree order (issue #51).

fortran
subroutine realm_face_leaves(this_realm, face, codes)

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inRealm.
faceinteger(kind=I4P)inFace code (FACE_X_MIN..FACE_Z_MAX).
codesinteger(kind=I8P)outallocatableLeaves on the face.

Call graph

push_row ​

Append a row to a growable row list (the capacity doubles when full).

fortran
subroutine push_row(list, row)

Arguments

NameTypeIntentAttributesDescription
listtype(seam_rows_t)inoutRow list.
rowinteger(kind=I4P)inRow to append.

Call graph

leaf_metrics ​

Origin, cell size and (optionally) upper corner of the leaf code of a realm, from its tree and grid (valid on every rank, whichever rank owns the leaf).

fortran
subroutine leaf_metrics(this_realm, code, emin, dxyz, emax)

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inRealm.
codeinteger(kind=I8P)inLeaf Morton code.
eminreal(kind=R8P)outLeaf origin.
dxyzreal(kind=R8P)outCell size.
emaxreal(kind=R8P)outoptionalLeaf upper corner.

Call graph

ghost_slab_extents ​

The (i, j, k) bounds of a block's ghost slab on one face: ngc cells deep on the (axis, sgn) face, the two tangential axes over the full ghost-extended range [1-ngc, n+ngc] (edge and corner ghosts included).

Attributes: pure

fortran
subroutine ghost_slab_extents(this_realm, axis, sgn, imin_g, imax_g, jmin_g, jmax_g, kmin_g, kmax_g)

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inRealm.
axisinteger(kind=I4P)inFace axis and side.
sgninteger(kind=I4P)inFace axis and side.
imin_ginteger(kind=I4P)outSlab bounds.
imax_ginteger(kind=I4P)outSlab bounds.
jmin_ginteger(kind=I4P)outSlab bounds.
jmax_ginteger(kind=I4P)outSlab bounds.
kmin_ginteger(kind=I4P)outSlab bounds.
kmax_ginteger(kind=I4P)outSlab bounds.

Call graph

wrap_periodic_tangential ​

Wrap a seam ghost centre into the peer domain along every tangential axis periodic on both realms (issue #65 P4).

The edge and corner ghosts of a seam slab past a tangential boundary of the ghost realm lie outside the peer domain too. At a physical boundary they are the BC's; at a periodic one no BC fills them (the realm's own maps have no neighbour across the seam), and they kept their initial values, read by every stencil with tangential reach (the dissipative fluxes: NaN at the first step of a mirror split periodic in y). Their peer is the cell one period away, which the wrap makes get_closest_block find. Both realms span the same tangential extent at a mirror or refined seam (the faces cover each other), so the period is the peer's domain length.

Attributes: pure

fortran
subroutine wrap_periodic_tangential(realm_g, realm_c, axis, xg)

Arguments

NameTypeIntentAttributesDescription
realm_gclass(realm_object)inRealm owning the ghost.
realm_cclass(realm_object)inRealm owning the cells.
axisinteger(kind=I4P)inSeam normal axis.
xgreal(kind=R8P)inoutGhost centre.

Call graph

register_intra_realm_amr_seams ​

Register every intra-realm AMR coarse-fine face in the forest flux register.

Walks each realm's tree node neighborhood (already built by make_neighborhood at realm init) and, for every block whose face neighbor is MORE refined (NODE_MORE_REFINED — this block is the COARSE side of a 2:1 jump), adds one register entry. The fine-side blocks (the ratio/2 finer neighbors covering that coarse face) are recorded in fine_block(:).

Only the 6 faces (fec = 1..6) are registered — reflux is a face-flux correction; edge/corner adjacencies (fec = 7..26) carry no conserved face flux and are skipped.

Two-pass, mirroring register_inter_realm_seams:

  • Pass 1 counts coarse-side faces across all realms → register size.
  • Pass 2 calls register_face(seam_kind = SEAM_KIND_INTRA_REALM_AMR, ...) and fills the signed inter_realm_face_register_index lookup: +cursor on the coarse (block, bc_fec), -cursor on each fine neighbor block's opposite face. For intra-realm jumps coarse and fine are the SAME realm, so coarse_realm = fine_realm = is.

Register ownership. This routine is the sole initializer of the flux register: it calls flux_register%initialize with the intra-realm face count (possibly 0, which still flips the register's is_initialized_ so the per-step reset/reflux hooks are safe no-ops) plus extra_faces.

Composition with inter-realm seams (issue #37). The manifest path calls it first with extra_faces = the inter-realm seam face count, then register_inter_realm_seams appends those faces after the nfaces_intra intra-realm ones. Both lists are walked on the replicated trees (issue #40), so every rank holds the same faces under the same cursors, which is what the register's cross-rank reduction of the fine sums relies on.

fortran
subroutine register_intra_realm_amr_seams(self, realm, extra_faces, nfaces_intra)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmclass(realm_object)inoutInitialized realms whose trees are walked.
extra_facesinteger(kind=I4P)inoptionalRegister slots reserved for inter-realm faces.
nfaces_intrainteger(kind=I4P)outoptionalIntra-realm AMR faces registered (cursors 1..n).

Call graph

apply_reflux_corrections ​

Dispatch the Berger-Colella reflux correction to every realm.

Called once per step AFTER close_step_forest's update_q. The forest's role is purely orchestration: it iterates the realm array and invokes each realm's apply_reflux_to_stage_forest TBP, passing that realm's OWN final stage (stages_per_step_forest()) so the realm-side end-of-step gate (stage == self%rk%nrk) fires for every realm, asymmetric-K included. The realm-side body filters flux_register%face(:) by face%coarse_realm == self%realm_index and adds the per-cell end-of-step correction directly into its committed self%q (full dt/dx weight, no stage RK coefficient — AMReX Berger-Colella cadence).

The forest never reaches realm%rk/realm%q directly: the per-cell write lives realm-side. This is the integrator-agnostic split — see realm_object%apply_reflux_to_stage_forest for the contract, and prism_cpu_object for the RK-specific override.

Empty register fast path: when the register has no faces the per-realm calls each short-circuit on flux_register%nfaces == 0.

fortran
subroutine apply_reflux_corrections(self, realm, dt)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inThe forest (holds the flux register).
realmclass(realm_object)inoutRealms; each realm's reflux TBP fires once.
dtreal(kind=R8P)inTime step.

Call graph

share_flux_register ​

Copy the flux register into every realm (realm_object%flux_register_copy), after each build (init, regrid, manifest): the faces, their ownership and their accumulators (zeroed), so that a realm can accumulate one residual evaluation's seam fluxes for a diagnostic without touching the forest's step accumulators (issue #68 A2). The register is replicated (the same faces on every rank, issue #40), and so is the copy.

fortran
subroutine share_flux_register(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmclass(realm_object)inoutRealms.

Call graph

Functions ​

seam_cell_size ​

The cell size of the leaves of a realm on its seam face: one size for all of them, error_stop otherwise.

Returns: real(kind=R8P)

fortran
function seam_cell_size(this_realm, face, pfx) result(dxyz)

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inRealm.
faceinteger(kind=I4P)inSeam face (FACE_* code).
pfxcharacter(len=*)inError message prefix.

Call graph

size_pairs ​

Number of face pairs of a manifest.

Attributes: pure

Returns: integer(kind=I4P)

fortran
function size_pairs(manifest) result(n)

Arguments

NameTypeIntentAttributesDescription
manifesttype(forest_manifest_t)inParsed manifest.

leaf_on_realm_face ​

Return .true. iff the leaf code of a realm has its (axis, sgn) face on the realm boundary (any owner rank).

Returns: logical

fortran
function leaf_on_realm_face(this_realm, code, axis, sgn) result(yes)

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inRealm.
codeinteger(kind=I8P)inLeaf Morton code.
axisinteger(kind=I4P)inFace axis (1..3) and side (+1 max, -1 min).
sgninteger(kind=I4P)inFace axis (1..3) and side (+1 max, -1 min).

Call graph

inside_domain ​

Return .true. iff xc lies inside the domain of this_realm, at least tol away from its boundary.

Attributes: pure

Returns: logical

fortran
function inside_domain(this_realm, xc, tol) result(yes)

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inRealm to query.
xcreal(kind=R8P)inPoint.
tolreal(kind=R8P)inPer-axis margin.

Call graph

ghost_label ​

Name a seam ghost cell in an error message.

Returns: character(len=:)

fortran
function ghost_label(is, b, i, j, k) result(label)

Arguments

NameTypeIntentAttributesDescription
isinteger(kind=I4P)inRealm index.
binteger(kind=I4P)inOwner-local block and ghost cell indices.
iinteger(kind=I4P)inOwner-local block and ghost cell indices.
jinteger(kind=I4P)inOwner-local block and ghost cell indices.
kinteger(kind=I4P)inOwner-local block and ghost cell indices.

Call graph