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), target :: realm(N)); the driver points an array of realm_pointer at its elements and the forest receives that, type(realm_pointer), intent(inout) :: realm(:) (element is is realm(is)%ptr). It never receives a polymorphic array class(realm_object) :: realm(:): nvfortran miscompiles the indexing of one (issue #96). The forest 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/concepts/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)
rebuildsinteger(kind=I4P)
manifesttype(forest_manifest_t)
has_manifestlogical
rebuild_topologylogical

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 topology on a change.
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).
fill_all_seamspass(self)Fill every seam on committed q (issue #110).
regrid_realmspass(self)The phased multi-realm regrid (issue #110).
rebuild_topology_timedpass(self)Rebuild the topology, refill the seams (#110).
balance_seam_markspass(self)Balance the marks across the seams (#110).
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.

seam_register_face_t ​

One inter-realm register face (issue #110 P3, seam_register_faces): its coarse and fine sides and the coarse leaf.

Components ​

NameTypeAttributesDescription
rcinteger(kind=I4P)Coarse-side realm.
fcinteger(kind=I4P)Coarse-side face (FACE_* code).
rfinteger(kind=I4P)Fine-side realm.
ffinteger(kind=I4P)Fine-side face (FACE_* code).
code_cinteger(kind=I8P)The coarse seam leaf.
refinedlogicalA 2:1 face (else equal cells, realm_a coarse by convention).

Subroutines ​

initialize ​

Initialize the forest and every realm it tends.

fortran
subroutine initialize(self, realm, filename)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmtype(realm_pointer)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 manifest%realms_number realms of the concrete app type and point realm(:) at them before calling this — the forest does not allocate the realms (it cannot, since realm_object is abstract and each app has its own extension). Environment value. Environment status. MPI error.

fortran
subroutine initialize_from_manifest(self, realm, manifest)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmtype(realm_pointer)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.
realmtype(realm_pointer)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.
realmtype(realm_pointer)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 FROZEN AT t^n. Phase 0 (below) fills the seam ghosts of every realm's committed q from the peers' committed q, once per step before open_step_forest, and each realm's open_step_forest copies them into every stage buffer, so stages 1..K all read the peer state at the start of the 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). α admits asymmetric per-realm K (the K-equality guard is removed). Cost: first-order seam coupling in time (issue #113: before it the stages read seam ghosts no fill reached).

β (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 0 iterates seams and fires only for α. An α seam is filled once per step; a β seam once per substage (Phase 2 fires K times).

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 — fill_seam_from_peer_forest (per-seam, α-gated), then open_step_forest (per-realm prologue: the stages start from q, the α seam ghosts included) 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)

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.
realmtype(realm_pointer)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.
realmtype(realm_pointer)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.
realmtype(realm_pointer)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. 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.

On a multi-realm forest (issue #110) the whole inter-realm topology depends on the block layout too: the register faces of the seams, the seam ghost rows (local, MPI send and receive), the BC_SEAM rows of the boundary crown and their device copies. After any realm regridded, populate_inter_realm_topology rebuilds all of it from the manifest kept at initialisation (it is idempotent: every structure it builds is deallocated first), then every seam is filled from its peer on the committed state, both cadences: a new seam block has no seam ghost yet, and an end-of-step seam would otherwise enter the next step with the ghosts of the old layout. Before the realms mark, the seams are filled too, so markers that read ghosts (gradient, Löhner) see the peer realm across a seam. Both fills run only when a realm regrids at runtime (or under the rebuild hook): a checkpoint holds the ghost cells, so a run without runtime AMR must not see an extra fill.

fortran
subroutine regrid(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmtype(realm_pointer)inoutThe realms.

Call graph

rebuild_topology_timed ​

Rebuild the inter-realm topology from the kept manifest and refill every seam from its peer, timing both when ADAM_SEAM_TIMING=1 (issue #110 P6).

fortran
subroutine rebuild_topology_timed(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmtype(realm_pointer)inoutThe realms.

Call graph

regrid_realms ​

The regrid of a multi-realm forest (issue #110): every realm marks, the forest balances the marks across the seams, every realm adapts; sweep after sweep, until no realm marks or no tree changes.

regrid_forest marks and adapts a realm in one call, so the realm adapting first could not see its peers' marks. Here each sweep runs the realm phases one realm after the other: regrid_mark_forest (the marks gathered on the replicated tree; a realm that does not mark has its tree marks cleared), balance_seam_marks (on the replicated trees, identical on every rank), then regrid_apply_forest for every realm whose tree carries a mark, a realm a peer forces to refine included. After a sweep that changed a tree the inter-realm topology is rebuilt and the seams refilled, so the next sweep marks, and the next step runs, on the new grid (rebuilt).

fortran
subroutine regrid_realms(self, realm, any_regridded, rebuilt)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmtype(realm_pointer)inoutThe realms.
any_regriddedlogicaloutSome realm's tree changed.
rebuiltlogicaloutThe topology was rebuilt after the last change.

Call graph

balance_seam_marks ​

Balance the marks of the realms across their seams, on the replicated trees (issue #110).

Each realm's 2:1 sanitize first, then, seam by seam:

  • one level per seam side: a refine vote on a leaf along the seam refines the whole side; the side derefines only when every leaf along it agrees, otherwise its derefine votes are dropped (the seams join one cell size per side);
  • the levels across the seam: equal for coupling = mirror, the declared 2:1 jump for refined, at most one level apart for auto; a side below what the rule asks is raised (its derefinement dropped, or refined if below [amr] max_level), else the refinement of the side above is dropped. Repeated with sanitize until nothing changes: the trees are replicated, so every rank reaches the same marks.

A coupling = auto pair is balanced face by face instead (issue #110 P3, balance_mixed_pair): each seam leaf against the leaves of the peer realm facing it, their predicted cells equal or in a ratio of 2, so the levels may differ along the seam as they do inside a realm.

fortran
subroutine balance_seam_marks(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmtype(realm_pointer)inoutThe realms.

Call graph

balance_mixed_pair ​

Balance a coupling = auto seam face by face (issue #110 P3): for every seam leaf of realm_a and every seam leaf of realm_b next to it (their seam faces overlap or touch at an edge or a corner, also across a tangential axis periodic on both realms: the seam slab's edge and corner ghosts read those cells too), their predicted cells (a refine mark halves a leaf's cells, a derefine mark doubles them) must be equal or in a ratio of 2. The coarser of a pair too far apart is raised (its derefinement dropped, or refined below its [amr] max_level), else the finer is lowered (its refinement dropped); the caller repeats with each realm's sanitize until nothing changes. The overlaps are those of the current leaves: a derefinement is a family's, and the family's other leaves on the seam are checked too.

fortran
subroutine balance_mixed_pair(realm_a, face_a, realm_b, face_b, f, changed)

Arguments

NameTypeIntentAttributesDescription
realm_aclass(realm_object)inoutRealms of the pair.
face_ainteger(kind=I4P)inSeam faces.
realm_bclass(realm_object)inoutRealms of the pair.
face_binteger(kind=I4P)inSeam faces.
finteger(kind=I4P)inFace pair (for the message).
changedlogicalinoutSome mark changed.

Call graph

side_scan ​

Scan the leaves of a realm along a seam face: their level (one level, error_stop otherwise) and their marks.

fortran
subroutine side_scan(this_realm, face, level, any_refine, all_deref)

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inoutRealm.
faceinteger(kind=I4P)inSeam face (FACE_* code).
levelinteger(kind=I4P)outLevel of the leaves along the face.
any_refinelogicaloutSome leaf refines.
all_dereflogicaloutEvery leaf derefines.

Call graph

side_set ​

Give every leaf of a realm along a seam face the mark mark; changed if any leaf had another.

fortran
subroutine side_set(this_realm, face, mark, changed)

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inoutRealm.
faceinteger(kind=I4P)inSeam face (FACE_* code).
markinteger(kind=I4P)inTO_BE_REFINED, TO_NOT_TOUCH or TO_BE_DEREFINED.
changedlogicalinoutSome mark changed.

Call graph

clear_tree_marks ​

Clear every mark of a replicated tree (a realm that does not mark in a sweep, issue #110).

fortran
subroutine clear_tree_marks(tree)

Arguments

NameTypeIntentAttributesDescription
treetype(tree_object)inoutTree.

Call graph

check_marks_agree ​

Stop if the ranks reached different marks (issue #110): every rank balances its own copy of the replicated trees, so a rank that disagreed would adapt another tree than its peers, silently, and the next exchange would deadlock or mix grids. One checksum per realm, its smallest and largest over the ranks compared.

fortran
subroutine check_marks_agree(realm)

Arguments

NameTypeIntentAttributesDescription
realmtype(realm_pointer)inThe realms.

Call graph

fill_all_seams ​

Fill every inter-realm seam from its peer on the committed state, whatever its cadence (issue #110: around a regrid). The fills read the peer's committed q, since no stage is active between steps.

fortran
subroutine fill_all_seams(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmtype(realm_pointer)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.
realmtype(realm_pointer)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.
realmtype(realm_pointer)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; the forest also times the runtime regrid, its balance and topology rebuilds (issue #110 P6). Off by default, the output unchanged.

fortran
subroutine start_timing(self, realm)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmtype(realm_pointer)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.
realmtype(realm_pointer)inThe 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
realmtype(realm_pointer)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_in)

Arguments

NameTypeIntentAttributesDescription
selfclass(forest_object)inoutThe forest.
realmtype(realm_pointer)inoutInitialized realms whose adam%maps gets populated.
manifest_intype(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 along the axes the trees refine and 1 along a null axis (a quadtree's z, issue #71; either side may be the coarse one), the same tree kind in both realms and a seam normal to a refined axis, equal block cell counts along the seam, the same [amr] seam_ghost_fill, and nested blocks: every fine seam leaf covers one 2:1 piece of the face of one coarse seam leaf (a quarter on an octree, a half on a quadtree), and even block cell counts along the refined axes 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
realmtype(realm_pointer)inoutForest realms.
manifesttype(forest_manifest_t)inParsed manifest.

Call graph

resolve_auto_couplings ​

Resolve every coupling = auto face pair into mirror or refined from the cell sizes of its two seam sides (issue #110): equal sizes join as a mirror seam, a ratio of 2 as a refined one. Runtime AMR may switch a seam from one kind to the other, so what refined requires whatever side is fine is checked every time: the same block cell counts along the seam, the same [amr] seam_ghost_fill and seam_restriction, even block cell counts in both realms. check_couplings then checks the resolved kind as a declared one. A side whose seam leaves hold several cell sizes (mixed levels along the seam, P3) leaves the pair auto: its kind is then decided face by face (leaf_peer_relation), checked by check_mixed_seam and registered by seam_register_faces.

fortran
subroutine resolve_auto_couplings(realm, manifest)

Arguments

NameTypeIntentAttributesDescription
realmtype(realm_pointer)inForest realms.
manifesttype(forest_manifest_t)inoutManifest, auto couplings resolved on return.

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
realmtype(realm_pointer)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 piece of the seam face of one coarse leaf: half its tangential extent, starting at its origin or at its midpoint, along each refined tangential axis, and the whole extent along a null one (a quadtree's z, issue #71).

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

Arguments

NameTypeIntentAttributesDescription
coarseclass(realm_object)inoutCoarse realm.
fineclass(realm_object)inoutFine realm.
face_finteger(kind=I4P)inFine seam face.
rinteger(kind=I4P)inRefinement ratio per axis (2, or 1 along a null axis).
pfxcharacter(len=*)inError message prefix.

Call graph

leaf_peers ​

The leaves of the peer realm that face the seam leaf code of this_realm (issue #110, P3): idx(1:n), indices into peer_codes (the peer's seam leaves), those whose tangential extent overlaps the leaf's by more than half a cell, and k, log2 of the leaf's cell size over theirs (0 equal, +1 the peers twice as fine, -1 twice as coarse). Stops when the peers do not share one cell size, or when it is not the leaf's, half or twice it.

fortran
subroutine leaf_peers(this_realm, face, code, peer, peer_codes, idx, n, k)

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inRealm of the leaf.
faceinteger(kind=I4P)inSeam face of this_realm (FACE_* code).
codeinteger(kind=I8P)inSeam leaf.
peerclass(realm_object)inPeer realm.
peer_codesinteger(kind=I8P)inSeam leaves of the peer (seam_leaf_codes).
idxinteger(kind=I4P)outallocatableOverlapping peers.
ninteger(kind=I4P)outNumber of overlapping peers.
kinteger(kind=I4P)outlog2(leaf cell size / peer cell size).

Call graph

check_mixed_seam ​

Check a seam with mixed levels along it (coupling = auto left unresolved, issue #110 P3), face by face: every seam leaf of each side faces cells of one size, equal to its own, half or twice it (leaf_peers); equal cells cover its face exactly; finer peers are its 2:1 quadrants (half its tangential extent, starting at its origin or at its midpoint) and cover it; a coarser peer holds it as one of its quadrants. The requirements of a 2:1 face that do not depend on the levels (block cell counts, seam fill and restriction) are checked by resolve_auto_couplings; the stencil footprints are checked here, on the realms that are coarse or fine at some 2:1 face (as check_couplings does for coupling = refined).

fortran
subroutine check_mixed_seam(realm, pair, pfx)

Arguments

NameTypeIntentAttributesDescription
realmtype(realm_pointer)inForest realms.
pairtype(forest_face_pair_t)inFace pair (coupling = auto, mixed).
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
realmtype(realm_pointer)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
realmtype(realm_pointer)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.
realmtype(realm_pointer)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).
realmtype(realm_pointer)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.
realmtype(realm_pointer)inoutRealms.

Call graph

Functions ​

seam_leaf_codes ​

The codes of the leaves of a realm along a seam face, in tree order (issue #110). The balance works on this list (tree%node(code)), not on a face test inside the tree traversal: that loop shape was miscompiled by nvfortran 26.9 (-fast, -gpu=lto) in this routine, which then found no leaf on the face, at random, run after run.

Returns: integer(kind=I8P)

fortran
function seam_leaf_codes(this_realm, face) result(codes)

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inRealm.
faceinteger(kind=I4P)inSeam face (FACE_* code).

Call graph

cell_ratio_log2 ​

log2 of the ratio of the seam cell sizes of two sides, a over b, along the seam normal (issues #110, #71): 0 for equal cells, 1 when side a is twice as coarse.

Returns: integer(kind=I4P)

fortran
function cell_ratio_log2(realm_a, face_a, realm_b, face_b) result(k)

Arguments

NameTypeIntentAttributesDescription
realm_aclass(realm_object)inRealms.
face_ainteger(kind=I4P)inSeam faces (FACE_* codes).
realm_bclass(realm_object)inRealms.
face_binteger(kind=I4P)inSeam faces (FACE_* codes).

Call graph

tree_marks_text ​

The refine/derefine mark counts of a replicated tree, r/d (the regrid log of a multi-realm forest, issue #110).

Returns: character(len=32)

fortran
function tree_marks_text(tree) result(text)

Arguments

NameTypeIntentAttributesDescription
treetype(tree_object)inTree.

Call graph

tree_has_marks ​

Whether some leaf of a replicated tree is marked to refine or derefine (issue #110).

Returns: logical

fortran
function tree_has_marks(tree) result(yes)

Arguments

NameTypeIntentAttributesDescription
treetype(tree_object)inTree.

Call graph

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; with uniform present, uniform = .false. instead of the stop when the sizes differ (a mixed-level side, issue #110).

Returns: real(kind=R8P)

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

Arguments

NameTypeIntentAttributesDescription
this_realmclass(realm_object)inRealm.
faceinteger(kind=I4P)inSeam face (FACE_* code).
pfxcharacter(len=*)inError message prefix.
uniformlogicaloutoptionalOne cell size on the side.

Call graph

is_2to1_piece ​

Whether the tangential extent [emin_f, emax_f] of a fine seam leaf is one 2:1 piece of the coarse leaf's [emin_c, emax_c] (issue #71): half the coarse extent, starting at its origin or at its midpoint, along a refined axis (r == 2); the same extent along a null one (r == 1, a quadtree's z).

Attributes: pure

Returns: logical

fortran
function is_2to1_piece(emin_c, emax_c, emin_f, emax_f, r, tol) result(yes)

Arguments

NameTypeIntentAttributesDescription
emin_creal(kind=R8P)inCoarse leaf extent along the tangential axes.
emax_creal(kind=R8P)inCoarse leaf extent along the tangential axes.
emin_freal(kind=R8P)inFine leaf extent along the same axes.
emax_freal(kind=R8P)inFine leaf extent along the same axes.
rinteger(kind=I4P)inRefinement ratio along the same axes.
tolreal(kind=R8P)inGeometric tolerance.

Call graph

pair_refine_ratio ​

The refinement ratio per axis a 2:1 seam between two realms uses (issue #71): maps%refine_ratio of both, which must agree (the same tree kind: octree, quadtree or binary tree), and 2 along the seam normal (a seam across a null axis has no second level to join). Stops otherwise.

Returns: integer(kind=I4P)

fortran
function pair_refine_ratio(r_a, r_b, face_a, pfx) result(r)

Arguments

NameTypeIntentAttributesDescription
r_aclass(realm_object)inThe two realms.
r_bclass(realm_object)inThe two realms.
face_ainteger(kind=I4P)inSeam face of r_a (FACE_* code).
pfxcharacter(len=*)inError message prefix.

Call graph

seam_register_faces ​

The inter-realm register faces of a face pair (issue #110 P3), in a canonical order on the replicated trees: one per seam leaf of the coarse side, in tree order, for a mirror or refined pair (the coarse side: seam_sides); for a pair with mixed levels (auto left unresolved), one per seam leaf of realm_a facing equal cells (a mirror face, realm_a coarse by convention) or finer cells (a 2:1 face, realm_a coarse), then one per seam leaf of realm_b facing finer cells (a 2:1 face, realm_b coarse). Each face carries its own coarse and fine sides.

Returns: type(seam_register_face_t)

fortran
function seam_register_faces(realm, pair) result(faces)

Arguments

NameTypeIntentAttributesDescription
realmtype(realm_pointer)inForest realms.
pairtype(forest_face_pair_t)inFace pair.

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