Skip to content

Turbulence Analysis

Ben Auer edited this page Oct 13, 2026 · 4 revisions

Analysis of the six turbulence bundles (TR, TRG, DTG, TRI, FSTAR, DFSTAR)

All six are declared in GEOSturbulence_GridComp/GEOS_TurbulenceGridComp.F90. The three imports are at lines 562-592 and the three exports are at lines 1130-1160. They are filled in GEOS_PhysicsGridComp.F90 Initialize (about lines 2200-2420). Each bundle is a list of fields keyed by a tracer name NAME, plus a suffix.

The six bundles

Bundle Direction Per-field key What it holds Source
TR import NAME The diffused quantity (S, U, V, Q and tracers) Physics import S, U, V; turbulence exports TKESHOC and QT2; friendlies from Physics, Moist and Chem
TRG import NAME//'HAT' Effective surface (ground) value of the quantity Surface exports/internals: SHAT, UHAT, VHAT, QHAT, THAT
DTG import NAME//'DEL' Change in the surface value during the step Surface exports DELSS, DELUS, DELVS, DELQS
TRI export NAME//'IT' Tendency of the quantity (pressure-weighted if WeightedTendency) Fields are the Physics export (QVIT for Q, otherwise <NAME>IT)
FSTAR export NAME//'FLX' Surface flux of the quantity Same field objects as Surface imports SH, TAUX, TAUY, EVAP
DFSTAR export NAME//'DFL' d(flux)/d(surface value) Same field objects as Surface imports DSH, DFU, DFV, DEVAP

The name mapping in Physics Initialize for S, U, V, Q is:

  • TRG: SHAT, UHAT, VHAT, QHAT.
  • DTG: DELSS, DELUS, DELVS, DELQS.
  • FSTAR: SH, TAUX, TAUY, EVAP.
  • DFSTAR: DSH, DFU, DFV, DEVAP.
  • Any other tracer name maps to itself. Surface has no such field, so the pointer ends up unassociated.

Wiring: how the bundles are shared

  • Bundle fields are shared field objects, not copies. Physics adds Surface's field into the Turbulence bundle with ESMFL_StateGetField. Both components then see the same memory.
  • TRG and DTG: these come from Surface exports, so Surface writes them and Turbulence reads them.
  • FSTAR and DFSTAR: these are Turbulence exports, but the fields are Surface's own import fields (GIM(SURF)). Turbulence writes them and Surface reads them (SH, TAUX, EVAP, DSH and so on at GEOS_SurfaceGridComp.F90:6225-6229). This is the reverse direction of the usual flow.
  • TRI: these fields are Physics exports, so Turbulence writes them directly into Physics export memory.
  • Unwired tracers: the turbulence default is a friendly tracer with zero surface flux. Tracers with no HAT, FLX or DFL field get unassociated pointers and are treated as having zero flux and no tendency.
  • Terminate imports: Physics calls MAPL_TerminateImport for TR, TRG and DTG (GEOS_PhysicsGridComp.F90:1942). They are therefore not required from the parent and are filled internally.

Run sequence in Physics

  1. Surface Run1 (GEOS_PhysicsGridComp.F90:3211) produces the effective surface values.
    • SHAT = cp*TH + PHIS (SurfaceGridComp:4979), so S's surface value is dry static energy.
    • QHAT, THAT, UHAT and VHAT are divided by the exchange coefficients CQ, CT and CM (lines 4974-4977).
  2. Turbulence Run1 (line 3242) computes a preliminary solve at the unchanged surface value. It reads TR and TRG, and writes TRI, FSTAR and DFSTAR.
  3. Surface Run2 (line 3356) uses FSTAR and DFSTAR to advance the land, ocean and ice surface. It exports DELSS, DELQS, DELUS and DELVS (SurfaceGridComp:8724-8727).
    • DELSS = cp*DTS
    • DELQS = DQS
    • DELUS = DELVS = 0
    • It also uses the flux and derivative: EVAPO = EVAP + DEVAP*DQS, SHO = SH + DSH*DTS*cp.
  4. Turbulence Run2 (line 3370) corrects the solution for the surface change. It reads TR and DTG, and updates TRI, FSTAR and DFSTAR.

This is an implicit coupling in which Turbulence and Surface exchange a linearised flux.

How each bundle is used inside Turbulence

Run1 (GEOS_TurbulenceGridComp.F90:5385-5650):

  • For each TR field it reads the attributes DiffuseLike (U, S or Q, which picks the exchange coefficients and diffusivities), FriendlyToTURBULENCE and WeightedTendency.
  • It gets the pointers S (TR), SOI (TRI), SRG (TRG), SF (FSTAR) and SDF (DFSTAR). Only S must exist.
  • If TRG is missing, it allocates a zero-size SG, so the flux is 0.
  • It does the tridiagonal solve to get the new value SX, using SG as the lower boundary condition.
  • FSTAR: SF = CX*(SG - SX(LM)). Sea spray (SH_SPRAY, LH_SPRAY) is added for S and Q. SCM options override the flux.
  • TRI: SOI = (SX - S)/DT, multiplied by DP if weighted.
  • DFSTAR: SDF = CX*(1 - DX(LM)). DX is the diffusion-matrix elimination factor, so this is the sensitivity of the flux to the surface value.
  • If the field is FRIENDLY, S = SX is written back in place.

Run2 (lines 6162-6420):

  • It does not use TRG. It uses DTG (DSG) instead.
  • SX = S + DKX*DSG at every level. DKX is the per-level sensitivity (DKS, DKQ or DKV).
  • The tendency is incremented: SOI += DKX*DSG/DT, times DP if weighted.
  • The flux is corrected: SF = SF + DSG*SDF. This is the linearised update of FSTAR and why DFSTAR exists.
  • DPDTTRB(LM) = g*SF for Q.
  • DTG is also used for the dissipation terms and the SHVC parameterisation.

Relationships

  • TR is the index. Every other bundle is looked up as NAME + suffix. The TR field list is built first, and its names are used to build the other five.
  • TRG and DTG are the surface state (value and increment). FSTAR and DFSTAR are the flux and its slope. TRI is the result.
  • The surface state goes into Turbulence, and the flux and its slope go back to Surface. TRG and DTG carry Surface's state to Turbulence, while FSTAR and DFSTAR carry Turbulence's flux and its derivative back to Surface.
  • Two-pass coupling. Pass 1 uses TRG, FSTAR and DFSTAR. Pass 2 uses DTG and DFSTAR to correct FSTAR and TRI.
  • Surface-side exports. In Surface, SHAT is an export, THAT, QHAT, UHAT and VHAT are internal state, and DELxS are exports. Physics treats them uniformly.

Observations

  • The header comment in the turbulence file (lines 104-112) says TRG and DTG are "Used only by Run2". The code does not match.
    • TRG is used only by Run1, at line 5430.
    • DTG is used only by Run2, at line 6198.
  • The DiffuseLike default is dflt_q, which is Q, not S. The header comment says the default is 'S'.
  • The header says "four attributes" but lists three. The attributes are FriendlyTo[Comp], WeightedTendency and DiffuseLike.
  • For the U and V momentum fields, DTG is always 0 (DELUS = DELVS = 0). Run2 therefore does not change the U and V solution for a surface-value change, but the flux is still updated through DFSTAR.

How ESMFL_StateGetField builds the bundles

Defined in Shared/@MAPL/base/ESMFL_Mod.F90:216-310. It copies named fields from an ESMF State into an ESMF FieldBundle. Fields not in the State become empty placeholders, and fields that need a different name become renamed references to the same data.

Signature: ESMFL_StateGetField(State, FieldName(:), Bundle, [FieldAlias(:)], [RC])

  • State is the source.
  • FieldName(:) lists the names to look up in State.
  • Bundle is the destination (INOUT).
  • FieldAlias(:) optionally gives the name each field takes inside the bundle.

Per-field steps (lines 245-307)

  1. NameInState = FieldName(I). NameInBundle = FieldAlias(I) if given, otherwise the same name.
  2. Assert that NameInBundle is not already in the bundle. Otherwise it fails with "found in bundle".
  3. ESMF_StateGet(itemType=...). If the item is not an ESMF_STATEITEM_FIELD (this includes a missing name), set NotInState. Otherwise fetch the Field.
  4. If the field is in the State, call MAPL_AllocateCoupling(stateField) (Base/Base_Base_implementation.F90:39).
    • If the field is not COMPLETE (it has a grid and attributes but no memory), it reads the DIMS, VLOCATION, HALOWIDTH, PRECISION, UNGRIDDED_DIMS and DEFAULT_VALUE attributes. Then MAPL_FieldAllocCommit allocates the array and fills it with the default value or 0.
    • This is how a bundle can point at memory for a field the owning component has not yet allocated, for example Surface's import fields behind FSTAR and DFSTAR.
  5. NeedNewField = NotInState .or. (NameInState /= NameInBundle).
    • Not in State: ESMF_FieldEmptyCreate(name=NameInBundle). This field has a name only, with no grid, array or attributes. Its status is EMPTY.
    • In State, renamed: MAPL_FieldCreate(stateField, name=...) calls MAPL_FieldCreateRename (Base_Base_implementation.F90:1206). It uses ESMF_FieldCreate(..., datacopyflag=ESMF_DATACOPY_REFERENCE) and then MAPL_FieldCopyAttributes. The new field has the same grid and the same ESMF_Array (shared memory, not a copy) and keeps the attributes.
    • In State, same name: the State's own Field object is added directly.
  6. MAPL_FieldBundleAdd(BUNDLE, bundleField).

Mapping for each bundle (GEOS_PhysicsGridComp.F90:2293-2413)

Bundle Source State Name in State Alias in bundle Result
TRI Physics EXPORT QVIT for Q, otherwise <N>IT <N>IT Renamed reference for Q (QVIT becomes QIT). Otherwise the State's own field.
TRG GEX(SURF) <N>HAT none The Surface export field itself.
DTG GEX(SURF) DELSS, DELUS, DELVS, DELQS <N>DEL Renamed reference.
FSTAR GIM(SURF) SH, TAUX, TAUY, EVAP <N>FLX Renamed reference to Surface's import field.
DFSTAR GIM(SURF) DSH, DFU, DFV, DEVAP <N>DFL Renamed reference to Surface's import field.

Empty (incomplete) fields

  • A tracer with no matching Surface field, such as NAME//HAT for an arbitrary friendly tracer, gets an EMPTY field in the bundle. It counts toward the bundle's field count and name list.
  • Nothing ever completes these fields. MAPL_AllocateCoupling would fail on one, because it lacks the DIMS and other attributes.
  • A bundle can therefore hold a mix of complete and empty fields. A tracer can be in TR and have empty companions in TRG, DTG, FSTAR or DFSTAR.
  • The header comment says the routine fails when adding a dummy field to an empty bundle (no grid to borrow). I did not see that check enforced in the code. It would only fail later, if something used the field.
  • All of this runs once, in Physics Initialize, so the wiring is fixed for the run.

How Turbulence handles empty fields

ESMFL_BundleGetPointerByName3 (ESMFL_Mod.F90:741-763) is the helper behind ESMFL_BundleGetPointerToData. It finds the field by name and calls ESMF_FieldGet(status=...). If the status is COMPLETE it returns the data pointer. Otherwise it does NULLIFY(ptr) and returns success. So an empty placeholder becomes a null pointer, with no error. (I read only the rank-3 variant. The rank-2 one very likely does the same.)

Turbulence then checks associated() before use.

Run1 (GEOS_TurbulenceGridComp.F90)

  • S (TR): _ASSERT(associated(S)) at 5440. The quantity must exist.
  • SRG (TRG): if associated, SG => SRG. Otherwise allocate(SG(0,0)) (5445-5450), meaning zero flux. It is deallocated at 5650.
  • SF (FSTAR): computed only if(associated(SF)) (5565).
  • SOI (TRI): written only if(associated(SOI)) (5600).
  • SDF (DFSTAR): written only if(associated(SDF)) (5646).

Run2

  • DSG (DTG): the correction is guarded by associated(DSG) .and. SCM_SL == 0 at 6232, 6296, 6318 and 6413.
  • SOI increment: needs associated(SOI) .and. associated(DSG) (6296).
  • SF update: needs associated(SF) .and. associated(DSG) (6413).

Notes:

  • The empty-field check is split across layers. The helper checks the field status, and Turbulence sees only the resulting null pointer.
  • "Zero flux" applies only to the Run1 surface boundary value SG. For SF, SOI and SDF, the outputs are simply not produced when the pointer is null. Nothing is set to zero.
  • Run2 skips the correction entirely when DSG is missing.
  • A null SF for a real tracer is treated as optional. A mis-spelled tracer or Surface field name would therefore give no flux and no error. I could not tell from the code whether this is intended.
  • Correction to the Wiring section above: TRI's source is the Physics EXPORT state, not Surface. TRG, DTG, FSTAR and DFSTAR come from Surface.

Part 2: Additional survey findings

Everything below was added after the original analysis. Nothing in Part 1 was changed.

Open items left by Part 1

  1. Only the rank-3 ESMFL_BundleGetPointerByName3 was read. The rank-2 variant has not been confirmed to nullify empty fields.
  2. A misspelled tracer or Surface field name gives a null SF and no error. Whether this is intended is unknown.
  3. Header comment mismatches in GEOS_TurbulenceGridComp.F90 lines 104-145: TRG is Run1-only (not "Used only by Run2"); the DiffuseLike default is dflt_q; "four attributes" lists three.
  4. The header says adding a dummy field to an empty bundle fails. This is not enforced in the code.

Bundle usage in Turbulence (GEOS_TurbulenceGridComp.F90)

All six are declared with DATATYPE = MAPL_BundleItem.

Bundle Spec Dims
TR Import, 562-571 HorzVert, center
TRG Import, 574-581 HorzOnly
DTG Import, 585-592 HorzOnly
TRI Export, 1130-1137 HorzVert
FSTAR Export, 1140-1147 HorzOnly
DFSTAR Export, 1150-1157 HorzOnly

Local declarations: line 2928 (TR); 5244-5248 (Run1); 5798-5802 (Run2).

Iteration and access:

  • 3120-3122: ESMF_StateGet(IMPORT,'TR') then FieldBundleGet(TR, fieldCOUNT=NTR). Only the count is used, for the aerosol PBL calculation.
  • Run1: TR and TRG fetched at 5323-5324; TRI, FSTAR, DFSTAR at 5343-5345. 5362 counts TR into KM. 5368-5377 assert TRI, TRG, FSTAR, DFSTAR have the same count KM. 5395 is the by-index FieldBundleGet(TR, K, FIELD) loop. 5426-5434 use ESMFL_BundleGetPointerToData by name (NAME, NAME//'IT', 'HAT', 'FLX', 'DFL').
  • Run2: 5969-5974 get the bundles; 5979 and 5985 count TR and DTG; 6083 is the SHVC "S first" index get; 6162 is the main index get; 6194-6202 use NAME//'DEL' for DTG.

Attributes read per TR field with ESMF_AttributeGet:

  • DiffuseLike: Run1 5404 (default dflt_q), Run2 6171 (default dflt_Q).
  • FriendlyToTURBULENCE: Run1 5411, Run2 6178. Default false.
  • WeightedTendency: Run1 5418, Run2 6185. Default false.

Extra header mismatch: the comment around 5226-5232 says WeightedTendency defaults to true, but the code defaults it to false.

Only Turbulence reads these attributes. The only other reference is GCHP-only, @geos-chem/Interfaces/GCHP/Chem_GridCompMod.F90:2055-2064, which checks that a field is not also friendly to TURBULENCE.

Population in GEOS_PhysicsGridComp.F90 (Initialize, ~2190-2420)

  • TR (2203): ESMF_StateGet(GIM(TURBL),'TR').
    • S with DiffuseLike="S", WeightedTendency=.TRUE. (2210-2214).
    • U, V with DiffuseLike="U" (2219-2228).
    • Turbulence exports TKESHOC and QT2 with DiffuseLike="Q" (2231-2242). QT3 is commented out.
    • MAPL_GridCompGetFriendlies(GC,"TURBULENCE",BUNDLE) for Physics' own QW (2253), then for Moist (2256) and Chem (2259).
    • Names are printed (2262-2270), then count and NAMES list are taken (2276-2285). NAMES drives all other bundles.
  • TRI (2293-2307): from Physics EXPORT, Q mapped to QVIT, alias <N>IT.
  • TRG (2316-2324): GEX(SURF), <N>HAT.
  • DTG (2333-2354): GEX(SURF), DELSS/DELUS/DELVS/DELQS for S/U/V/Q, else the tracer name; alias <N>DEL.
  • FSTAR (2363-2384): GIM(SURF), SH/TAUX/TAUY/EVAP, else the name; alias <N>FLX.
  • DFSTAR (2393-2414): GIM(SURF), DSH/DFU/DFV/DEVAP, else the name; alias <N>DFL.
  • Disconnected imports: MAPL_TerminateImport on SURF imports SH, TAUX, TAUY, EVAP, DEWL, FRSL, DSH, DFU, DFV, DEVAP, DDEWL, DFRSL (1898-1906), and on Turbulence TR, TRG, DTG (1942-1946).
  • Run-time access: line 3174, with SYNCTQ on, Physics Run does ESMF_StateGet(GIM(TURBL),'TR') and BundleGetPointerToData('S') to overwrite S.

Who is friendly to TURBULENCE

FRIENDLYTO in an internal spec sets the FriendlyToTURBULENCE field attribute (MAPL_Generic.F90:6888-6915).

  • Physics: QW, FRIENDLYTO='TURBULENCE:MOIST' (line 208).
  • Turbulence: TKH, FRIENDLYTO='TURBULENCE' (GEOS_TurbulenceGridComp.F90:2404). It is not in TR.
  • Moist: all microphysics interfaces (GFDL_1M, BACM_1M, MGB2_2M, NSSL_2M, THOM_1M) set QV to DYNAMICS:TURBULENCE:CHEMISTRY:ANALYSIS, and QLLS, QLCN, QILS, QICN to DYNAMICS:TURBULENCE. Depending on scheme also QRAIN, QSNOW, QGRAUPEL and NCPL, NCPI, NRAIN, NSNOW, NGRAUPEL. See GEOS_GFDL_1M_InterfaceMod.F90:88-97 and GEOS_MGB2_2M_InterfaceMod.F90:117-143. CLLS, CLCN are DYNAMICS-only. GEOS_GF_InterfaceMod.F90:68-70 CNV_TR is not TURBULENCE.
  • Chem: GOCART2G (DU, SS, SU, NI, CA, all DYNAMICS:TURBULENCE:MOIST), MAM, ACHEM (ACHEM_GridCompMod.F90:627-716), CARMA (CARMAchem_GridCompMod.F90:231), PChem (GEOS_PChemGridComp.F90:412-474), StratChem.
  • GWD: none. Surface: none (self-friendly only); Surface is a data source for TRG, DTG, FSTAR and DFSTAR.

No component other than Turbulence consumes these six bundles. Unrelated friendly bundles: TRADV (Dynamics), TRANA (Analysis), MTR, MTRI, H2ORTRI, MCHEMTRI (Moist, 2425-2520), FriendlyToMOIST (Process_Library.F90:376-398).

MAPL connectivity facts (/home/bmauer/models/geos_v11.11.0/GEOSgcm/src/Shared/@MAPL/generic)

  • MAPL_AddConnectivity (MAPL_Generic.F90:305-310): Rename (5104-5129, scalar SRC_NAME/SRC_ID/DST_NAME/DST_ID), RenameMany (5135-5158), Many (5165-5189, array SHORT_NAME; no scalar form). Storage in VarConn.F90:37-80.
  • MAPL_AddConnectivityE2E (5075-5095) is commented out of the generic interface (306); used by MAPL_StateAddExportSpecFrmChldName (3670-3692).
  • Wiring is MAPL_WireComponent (~7060-7330). Sibling export to import: varIsConnected at 7212 and 7234. Renames are supported. If specs match it does a direct MAPL_Reconnect, otherwise it needs the same precision and builds a coupler.
  • Bundles are one VarSpec, so connections are bundle-to-bundle. Code that would skip friendly bundles is #if 0'd out (7188-7192).
  • Export to export: child exports are promoted to the parent when a connection targets import=MAPL_Self (7119-7154), matching by child short name, so renames are not applied in that pass. MAPL_AddExportSpec(..., CHILD_ID=...) is the usual route (e.g. GEOS_PhysicsGridComp.F90:1405-1425).
  • No connection from a component's export to its own import (I==J skipped).
  • Import to import: unsatisfied child imports are promoted to the parent under the same name, with no rename. MAPL_TerminateImport and DoNotConnect (5194-5249; MAPL_AnyChildImport at 7165) block this.
  • MAPL_GenericConnCheck (8352-8420, called at 746) fails if any connection or do-not-connect entry was never used.
  • Ungridded dims: UNGRIDDED_DIMS, UNGRIDDED_NAME/UNIT/COORDS on Import/Export/Internal specs (3308-3326, 3500-3523, 3833-3861); attributes set at 6858-6866; fields are rank 4. GOCART2G uses them (DU2G_GridCompMod.F90:245, SS2G_GridCompMod.F90:169, OH_GridCompMod.F90:615). MAPL_GridCompGetFriendlies splits rank-4 fields into per-bin 3D fields (8130-8165; Base_Base_implementation.F90:3191).

Tracer list: dynamic at runtime

TR is built at Initialize from GetFriendlies over Moist, Chem and Physics. NTR and KM come from FieldCount at run time (3122, 5362, 5979).

  • Always present: S, U, V, TKESHOC, QT2 (hard-coded, 2205-2242) and QW.
  • Moist: about 7 base fields, plus 3 precipitation species (GFDL_1M) or up to 8 more (MGB2_2M). Depends on the runtime scheme.
  • Chem: depends on enabled providers and runtime nbins.
  • No fixed total was found.

Part 3: Redesign discussion, removing ESMF_FieldBundle

Goal: eliminate all ESMF_FieldBundles for these six items and use plain ESMF_Fields with connections like other items. Added constraint: Physics must not access its children's states. Assumed: bit-for-bit results, Turbulence-only scope, other friendly bundles (TRADV, MTR and so on) left alone.

Options considered

  • A. Static per-tracer specs. Rejected. Turbulence SetServices cannot know the tracer names, which Moist and Chem fix only in their own SetServices.
  • B. Per-tracer specs plus MAPL_AddConnectivity. Chosen, because it is the only option that satisfies the constraint. Physics collects names after the Moist and Chem SetServices, passes them to a late Turbulence registration, and wires by name.
  • C. Fortran arrays of fields in an internal-state type. Least invasive and keeps bit-for-bit, but Physics Initialize still calls ESMFL_StateGetField on GEX(SURF), GIM(SURF), GIM(TURBL) and EXPORT. Rejected because it breaks the constraint.
  • D. One rank-4 ungridded field per role. Static specs, but per-tracer attributes need side arrays, friendlies are already split on collection, and copying loses zero-copy sharing. Not pursued.

Plan for Option B

  1. Turbulence (GEOS_TurbulenceGridComp.F90):
    • Remove the six bundle specs (562-592, 1130-1160).
    • SetServices takes a tracer table (name, DiffuseLike, WeightedTendency, Friendly, HasSurface). Per tracer, imports <N>, plus <N>HAT and <N>DEL if HasSurface; exports <N>IT, plus <N>FLX and <N>DFL if HasSurface.
    • Run1 and Run2 (5395, 6083, 6162) and line 3122 loop over the table, not over bundles or ESMF_AttributeGet.
    • Optional fields are omitted from the spec set rather than EMPTY. Use an explicit has-flag rather than relying on null pointers, because an unconnected import is allocated, not null. This is the bit-for-bit risk.
  2. Physics (GEOS_PhysicsGridComp.F90):
    • Call the Moist and Chem SetServices first, then collect names with MAPL_GridCompGetFriendlies (gridcomp handles, not states). Pass the list to Turbulence by gridcomp attribute, config resource or a new MAPL_MethodAdd hook.
    • Wire with MAPL_AddConnectivity: TRG <N>HAT from Surface; DTG DELSS, DELUS, DELVS, DELQS to SDEL, UDEL, VDEL, QDEL; TRI via AddExportSpec with CHILD_ID including QVIT to QIT; FSTAR and DFSTAR as normal Turbulence-export to Surface-import connections (SH, TAUX, TAUY, EVAP, DSH, DFU, DFV, DEVAP).
    • Delete the ESMFL_StateGetField blocks (2293-2414) and the TerminateImport calls (1898-1906, 1942-1946).
  3. Surface: SHAT and DELxS are already exports and its flux imports stay. No spec changes beyond the connectivity.

Problems identified

  1. FSTAR and DFSTAR direction: a Turbulence export to a Surface import is a normal sibling connection, so this is fine and lets the Surface-side TerminateImport go.
  2. Friendly tracers are internal fields, not exports. MAPL_AddConnectivity only goes export to import. Decision: export the tracers (below).
  3. Same-component connection: TKESHOC and QT2 are Turbulence's own exports feeding its own TR. MAPL skips I==J, so Turbulence must read its own export memory directly. QW, S, U, V come in as Physics imports through normal connectivity.
  4. In-place update: S = SX (friendly write-back) and the SYNCTQ overwrite at 3174 need writable shared memory. A reconnected import shares the owner's memory, so it works, and Physics reaches S through its own import.
  5. MAPL_GenericConnCheck: every connection must be consumed, so connected names must match Turbulence's registered specs per scheme and bin count.
  6. Spec ordering: registering Turbulence late changes MAPL setup order. Check the export promotion (7119-7154) and the other friendly bundles for order dependence.

Decision: export the tracers

  • Each Moist and Chem component adds a normal export spec for every tracer whose FriendlyTo contains TURBULENCE, sharing memory with the internal field. A copy would break the in-place write-back. Whether MAPL supports an export aliased to an internal field is unverified. If not, the export would be pointer-assigned in the component's Initialize or Run.
  • Scope: Moist (GFDL_1M, BACM_1M, MGB2_2M, NSSL_2M, THOM_1M) and Chem (GOCART2G bins, MAM, ACHEM, CARMA, StratChem, PChem). The Moist and Chem wrappers must promote their children's exports with CHILD_ID. GOCART bins are 4D ungridded, so they need a 4D export that Turbulence splits, or per-bin exports.
  • Physics needs a name registry exposed through the gridcomp handle (attribute or config), not states.

Risks

  • Scale: touches every Moist microphysics scheme and every Chem provider. This is the largest part.
  • Memory aliasing is the main technical risk for bit-for-bit.
  • Double ownership: Dynamics and Analysis still read the same tracers as friendlies. The export must not change their FRIENDLYTO behaviour.
  • GCHP: Chem_GridCompMod.F90:2055-2064 has its own friendly handling and needs the same treatment or an exclusion.

Suggested order

  1. Prototype one tracer (QV from GFDL_1M) exported, connected and read by Turbulence, and confirm the export aliases internal memory. Compare bit-for-bit.
  2. Add the tracer table and spec loop to Turbulence and convert Run1 and Run2.
  3. Move the Physics wiring to SetServices with AddConnectivity.
  4. Roll out the exports to the other Moist and Chem components.
  5. Remove the bundle code and the TerminateImport calls, then run and compare against baseline.

Status: analysis only. No source files were modified.


Part 4: Alternative, keep the bundles but remove Physics access to child states

Constraint: Physics must not access its children's states. This is a sketch from the survey findings. None of the MAPL behaviour below has been tested.

Idea

Make each bundle belong to one component, and let MAPL share it across components with ordinary connectivity. Today Physics reaches into GIM(TURBL), GEX(SURF), GIM(SURF) and EXPORT to fill the bundles. Instead, each component fills a bundle in its own state.

Why it can work

A bundle is a single VarSpec, so MAPL can already connect bundles (MAPL_WireComponent, ~7060-7330 in MAPL_Generic.F90):

  • Sibling export to import: a direct reconnect when the specs match. The importer then holds the same bundle object.
  • Unsatisfied child import: promoted to the parent import under the same name.

Per-bundle proposal

Bundle Owner fills it in its own state Link
TR Physics. It puts S, U, V (its own imports), QW (its own internal) and the Moist and Chem friendlies into a Physics-level TR bundle. Physics declares an import bundle TR. Turbulence's TR import is promoted to it by same-name import-to-import.
TRG, DTG Surface. It exports one bundle of its own SHAT, UHAT, VHAT, QHAT fields, and one for DELSS, DELUS, DELVS, DELQS. Fields are keyed by tracer name (S, U, V, Q). AddConnectivity from Surface's export bundle to Turbulence's import bundle.
FSTAR, DFSTAR Surface. It declares import bundles and adds its own SH, TAUX, TAUY, EVAP, DSH and so on, keyed by tracer name. AddConnectivity from Turbulence's export bundle to Surface's import bundle.
TRI Physics. It declares a bundle of its own export fields <N>IT. Same as TR, via import-to-import. This means flipping TRI to an import in Turbulence, or a child-export promotion plus aliasing in Physics.
  • Physics: touches only its own state, plus MAPL_GridCompGetFriendlies(GCS(MOIST|CHEM)) for names. That is a MAPL API call, not opening a child state.
  • Surface: adds fields to its own bundles. It needs no tracer list, because only S, U, V, Q have surface fields.
  • Turbulence: lookup changes from NAME//'HAT' to NAME inside a dedicated bundle. The <N>HAT/DEL/FLX/DFL/IT suffix scheme goes away. Run1 and Run2 change a few lines each.

Gains over the no-bundle design (Part 3)

  • No per-tracer specs, no late Turbulence registration, no changes to Moist or Chem. The tracer list stays dynamic and the friendly-tracer problem disappears.
  • No export-to-same-component problem.
  • Bundles keep zero-copy field sharing, so bit-for-bit should hold.

To verify before committing

  1. Bundle sharing: confirm the reconnect shares the bundle object rather than building a coupler that copies. The #if 0'd code at 7188-7192 suggests friendly-bundle handling here is fragile.
  2. Initialize ordering: fields must be added before anything reads them. GEOS_TurbulenceGridComp.F90:3122 reads the TR count, so check whether it runs in Initialize or Run.
  3. Missing fields: a tracer absent from TRG means "not in the bundle", not an EMPTY placeholder. ESMFL_BundleGetPointerToData must return null for a missing name, or callers need an existence check.
  4. Required imports: Physics' new import bundles get promoted to the Agcm level unless handled. They need MAPL_TerminateImport or a not-required flag, and MAPL_GenericConnCheck must still pass.
  5. TRI direction: Turbulence exports TRI today. Making it an import that Physics fills with its own export fields must not break the WeightedTendency write path.
  6. Spec equality: the bundle specs on both sides must match, or MAPL inserts a coupler.

Verdict

Much smaller than exporting the tracers. It touches Physics, Turbulence and Surface only, and nothing in Moist or Chem. Items 1 and 3 decide whether it works and can be checked read-only.


Part 5: Verification of the Part 4 open questions

These are code readings of the MAPL, ESMF and GEOS source. Nothing was run. Paths are shortened to Generic (MAPL/generic/MAPL_Generic.F90), VarSpec (generic/VarSpec.F90), ESMFL (base/ESMFL_Mod.F90), Phys (GEOS_PhysicsGridComp.F90) and Turb (GEOS_TurbulenceGridComp.F90).

# Question Result
1 Does bundle connection share the object? Yes
2 When is the TR count at Turb:3122 read? Fine
3 What happens for a name that is not in the bundle? Error, not null. Needs a fix.
4 What happens to an unsatisfied import bundle in Physics? Manageable
5 Can TRI become an import? Yes
6 Do the specs have to match? Yes, DIMS and VLOCATION

1. Wiring of bundle items: shared object, not a copy

  • The skip for friendly bundles at Generic:7188-7192 is inside #if 0, so bundle imports go through normal wiring.
  • If EX_SPECS(N) == IM_SPECS(K) (Generic:7251), MAPL calls MAPL_Reconnect, with no coupler. The SHORT_NAME is temporarily swapped so names match (Generic:7244-7250).
  • The coupler branch (Generic:7262-7270) runs only when the specs differ. GenericCplComp.F90 has no bundle handling, and MAPL_SetVarSpecForCC skips bundle items (Generic:8315). Avoid this branch.
  • MAPL_Reconnect (Generic:7684-7724) only calls MAPL_LinkCreate. The sharing happens at the root's SetServices (Generic:740-815), which copies FIELDPTR, BUNDLEPTR and STATEPTR from the lowest-label spec (Generic:808-813). MAPL_VarSpecSetBundlePtr (VarSpec:753-766) sets SPEC%SPECPtr%BUNDLE => BUNDLEPTR. Both specs then point at one ESMF_FieldBundle.
  • MAPL_StateCreateFromSpecNew (Generic:6595-6618) creates the bundle if it does not exist yet, otherwise reuses the handle, then adds it to its own state. Whichever component initializes first creates it. A side effect: the RESTART attribute is overwritten by whichever component creates its state last (Generic:6613).
  • Fields added after wiring are visible to both sides, because ESMF_FieldBundle is a pointer to a shared container (ESMF ESMF_FieldBundle.cppF90:108-114). MAPL_FieldBundleAdd (Base_Base_implementation.F90:2469-2525) also updates the shared MAPL_BundleItemOrderList attribute. Caveat: this was read from ESMF source found at /home/mathomp4/ESMF_Crash, which may not be the build GEOS links.
  • Import-to-import promotion shares the same spec: MAPL_VarSpecAddRefToList (StateSpecification.F90:501-) stores SPECPtr => ITEM%SPECPtr, called at Generic:7285, with MAPL_Reconnect on duplicates (Generic:7292-7301). The parent creates its import state after the children initialize (Generic:1123, 1126), so it reuses the child's bundle. A duplicate name with a non-equal spec is a hard failure (VarSpec:1540-1555).
  • Precedent: AERO_DP is a Chem export bundle (GEOS_ChemGridComp.F90:473-478) connected at Phys:1683-1688 to a Surface import (GEOS_SurfaceGridComp.F90:645-651), HorzOnly on both.

2. Turb:3122 timing

  • It sits in the contained routine REFRESH (Turb:2854-5207) inside RUN1 (Turb:2522-5660), called at Turb:2823 only when the refresh alarm rings. It is not in Initialize. Turbulence has no custom Initialize (Turb:60, 266-268).
  • Physics Initialize calls MAPL_GenericInitialize at Phys:2168, which initializes the children first. The bundle fill code runs afterwards (Phys:2203-2420), so Run sees filled bundles.

3. Name not in the bundle

  • ESMFL_BundleGetPointerToData (ByName2 and ByName3, ESMFL:717-763) calls ESMF_FieldBundleGet(BUNDLE, NAME, FIELD=...) then _VERIFY(STATUS).
  • Null is returned only when the field exists but is not COMPLETE, i.e. an EMPTY placeholder (created by ESMFL_StateGetField, ESMFL:272-286).
  • A name that is not in the bundle should give a non-success rc from ESMF's container lookup (ESMF_FieldBundle.cppF90:2073-2078). I did not read the C++ container.
  • Callers VERIFY_ after each call (Run1 Turb:5426-5435, Run2 Turb:6194-6203), so a missing name makes Run1 and Run2 fail.
  • Count asserts: Run1 requires TRI, TRG, FSTAR and DFSTAR counts to equal TR's (Turb:5368-5380, _ASSERT(KM==K)), and Run2 checks the DTG count (Turb:5985-5989). Only TR must be non-null (Turb:5439).
  • Consequence for Part 4: a bundle holding only S, U, V and Q needs an existence check before each lookup, and the count asserts must be relaxed.

4. Unsatisfied import bundle declared in Physics

  • An import that no sibling provides and that is not do-not-connect is promoted to the parent (Generic:7279-7289), repeating at each level up to the root.
  • Each state creates an empty bundle from the spec (Generic:6595-6604).
  • At the root, ExtData fills only what is in its rc file. For anything else it logs "ExtData could not satisfy import" and continues (ExtDataGridCompMod.F90:~771-773; MAPL_CapGridComp.F90:~690-712).
  • There is no REQUIRED attribute in Generic. MAPL_AddImportSpec defaults RESTART to MAPL_RestartOptional (Generic:3366). Imports are only read from a restart if IMPORT_RESTART_FILE: is set (Generic:1565-1573).
  • MAPL_GenericConnCheck (Generic:8352-8420) only checks for unused CONNECT or DO_NOT_CONNECT entries. It does not check that imports are satisfied, but a TerminateImport naming something that does not exist would fail it.
  • Existing pattern: Physics calls MAPL_TerminateImport(SHORT_NAME=['TR','TRG','DTG'], CHILD=TURBL) (Phys:1941-1945) and does the same for Moist MTR (Phys:1970-1973). That adds a do-not-connect entry (Generic:5197-5217), sets PARENTIMPV=.false. (Generic:7197-7203), and stops promotion.
  • Rule: if Physics fills the bundle itself, add a TerminateImport. Otherwise the spec propagates to the root and sits empty.
  • Not determinable from code: whether the real driver or ExtData configuration treats a stray empty root import differently.

5. TRI direction

  • TRI is a Turbulence export today (Turb:1130-1137). Turbulence writes through pointers into field data (SOI = ... at Turb:5600-5605, 6296-6303, 6386-6390), not with MAPL_FieldBundleAdd.
  • Physics Initialize adds the fields (Phys:2293-2306) using ESMFL_StateGetField(EXPORT, ...), so each bundle field shares the array of the Physics export. A name missing from the export state gets an EMPTY field and a null pointer. It asserts the name is not already in the bundle (ESMFL:~243), so it must run once.
  • MAPL has no write protection on imports. Physics already writes into the Turbulence TR import via ESMFL_BundleGetPointerToData(BUNDLE,'S',SFORTURB) and SFORTURB = SAFMOIST (Phys:3174-3178).
  • WeightedTendency is a per-field ESMF attribute (Turb:5418, 6185) that only decides whether DP is applied when writing SOI. It is independent of direction.

6. Spec equality

  • MAPL_VarSpecEQ (VarSpec:1373-1401) compares SHORT_NAME, DIMS, LOCATION, HALOWIDTH, PRECISION, and ACCMLT_INTERVAL and COUPLE_INTERVAL if both are non-zero.
  • It ignores LONG_NAME, UNITS, FIELD_TYPE, STAT (where MAPL_BundleItem lives, under #if 0), RESTART and direction.
  • Import vs export direction does not matter, and the names match because wiring swaps them first.
  • DIMS and VLOCATION must match. A mismatch (HorzVert vs HorzOnly, or Center vs None) makes == false, which leads to the coupler branch that has no bundle support (item 1). MAPL_VarSpecSamePrec is also asserted there (Generic:7263).
  • For TR and TRI style bundles, both sides should use DIMS=MAPL_DimsHorzVert, VLOCATION=MAPL_VLocationCenter (Turb:563-570, 1130-1137).
  • The same == applies when a child import is promoted to a parent that already has that name. A mismatch is a hard failure (VarSpec:1540-1555).

Effect on the Part 4 plan

  • The Part 4 verdict still holds: no changes to Moist or Chem, and the bundles keep sharing memory.
  • New requirement: a "field exists" check in Turbulence before each lookup, plus relaxed count asserts, if a bundle holds only S, U, V and Q.
  • Physics import bundles that Physics fills need a TerminateImport.
  • Both ends of each link must use identical DIMS and VLOCATION.
  • Still unverified: the ESMF shallow-copy behaviour on the real build, the C++ container error return for a missing name, and the driver or ExtData handling of stray root imports.

Clone this wiki locally