icon_sc.core.state — state layer¶
State layer: names/units registries, boundary DataArrays, and the S05 vault.
Canonical-name registry (architecture §2.5).
One table maps each canonical quantity name to its canonical units, CF standard name (if any), ICON Fortran short name (if any) and GRIB2 triplet (ingestion only).
Namespacing (§2.5): CF standard names are the canonical names and carry no prefix
(the cf: namespace is implicit — spelling it out is rejected). Solver-internal
quantities — those without a CF standard name — live in the explicit icon:
namespace (icon:exner_function, icon:normal_wind, …). The registry enforces
that split both ways:
an unprefixed registration claims CF identity (
cf_namedefaults to the name);an unprefixed registration that disclaims CF identity (
cf_name=NO_CF) is the “unnamespaced icon” error: it must be registered asicon:<name>instead;an
icon:registration passing acf_nameis contradictory: quantities with a CF standard name are registered under it, unprefixed;any namespace prefix other than
icon:is rejected.
The _on_interface_levels suffix (sympl convention) marks the height_interface
dim variant of a quantity; unit lookups for a suffixed name fall back to its base
quantity, since the physical quantity — hence its canonical units — is the same.
Seed rows: icon4py v0.2.0 model/common/states/data.py (see REFERENCES.lock).
- icon_sc.core.state.names.NO_CF: Final[str] = '<no-cf-standard-name>'¶
Sentinel for “this quantity has no CF standard name” (distinct from the default
None= “the canonical name is the CF standard name”).
- exception icon_sc.core.state.names.NamesRegistryError¶
Invalid or conflicting canonical-name registration/lookup.
- class icon_sc.core.state.names.QuantityDef(name, units, cf_name, icon_name, grib2)¶
One row of the canonical registry: name ↔ units ↔ CF ↔ ICON ↔ GRIB2.
- icon_sc.core.state.names.base_name(name)¶
Strip the
_on_interface_levelssuffix (identity when absent).
- icon_sc.core.state.names.is_on_interface_levels(name)¶
True if
namecarries the_on_interface_levelssuffix.
- icon_sc.core.state.names.known_quantities()¶
Sorted canonical names currently registered.
- icon_sc.core.state.names.lookup_quantity(name)¶
Return the registered quantity, resolving the interface-levels fallback.
- Parameters:
name (str)
- Return type:
- icon_sc.core.state.names.on_interface_levels(name)¶
The interface-level variant of a canonical name (idempotent).
- icon_sc.core.state.names.register_quantity(name, units, cf_name=None, icon_name=None, grib2=None)¶
Register a canonical quantity (frozen interface, SPEC S02).
unitsis the canonical unit string for the quantity; component contracts are verified against it byicon_sc.core.state.units.verify_noop().
Canonical-units table access and the no-op-conversion verifier (architecture §2.4).
Canonical units are stored as plain strings in the names registry; Pint is consulted
only at negotiation time (registration/verification), through a cached identity check
(pattern from stubbiali/sympl oop: lru_cache on the registry call). Nothing on
the execution path — in particular IngressPlan.apply() — may import Pint; the
import happens lazily inside units_identical(), and only when two unit strings
are not literally equal.
Unit-string cleanup (% → percent, ° → degree) and the extra
degrees_north/degrees_east/percent definitions are ported from upstream
sympl _core/units.py (see REFERENCES.lock).
- exception icon_sc.core.state.units.UnitsError¶
A component’s declared units are not the canonical units (no-op violation).
- icon_sc.core.state.units.canonical_units(name)¶
Canonical unit string of a registered quantity (frozen interface, SPEC S02).
*_on_interface_levelsvariants fall back to their base quantity.
- icon_sc.core.state.units.convert_array(values, source, target)¶
Convert an array from
sourcetotargetunits via Pint (allocating).Negotiation-time only (non-strict ingress executing a
ConversionPlan, S03); strict mode forbids the call sites, and nothing on the apply path may reach this.- Raises:
UnitsError – When Pint cannot convert (undefined/incompatible units).
- Parameters:
- Return type:
- icon_sc.core.state.units.units_identical(units_a, units_b)¶
True iff Pint deems the two unit strings the same unit (identity, not mere dimensional compatibility): conversion between them is a no-op.
- icon_sc.core.state.units.verify_noop(component_units, canonical)¶
Verify a component’s declared units equal the canonical units (§2.4).
sympl’s Pint conversion path must compile to a no-op in production; any pair Pint deems non-identity (
KvsdegC,m s-1vskm h-1,1vsg/kg, …) is rejected. Called at negotiation time only.
Boundary DataArray construction (architecture §2.2).
State values are xarray.DataArray s over FieldBuffer
buffers; construction here is the only sanctioned way to stamp the attrs schema
(units / location / halo / grid_uuid). The buffer is wrapped, never
copied and never coerced (no .values anywhere on core paths — the duck-array
lesson of §4.2).
- icon_sc.core.state.dataarray.make_dataarray(buffer, *, name, dims, units, location, grid_uuid=None)¶
Wrap
bufferin a boundary DataArray (frozen interface, SPEC S02).Stamps
attrs['units'],attrs['location'],attrs['halo'](HaloState.VALID— a freshly constructed field has no stale ghost points by definition; the halo validator pass flips it) and, when given,attrs['grid_uuid'](provenance: refuse to mix grids, §2.2).- Raises:
TypeError – If
bufferdoes not satisfy theFieldBufferprotocol.ValueError – On a rank mismatch, multiple horizontal dims, or a horizontal dim contradicting
location.
- Parameters:
- Return type:
DataArray
StateVault — the state’s execution-phase form (architecture §8.2, SPEC S05).
A dense, slotted container: a flat list of raw buffers plus an interned
name → index map that is consulted only at bind time. The public
dict-of-DataArrays view survives as a lazily materialized façade
(icon_sc.core.state.facade), so monitors, interactive inspection and the
interpreted tier keep unmodified sympl semantics while nothing xarray-shaped
executes on the step path.
Two counters govern coherence:
epoch— bumped by any out-of-band state mutation through the façade (rebinding or deleting a field). A plan materialized against the vault records the epoch and raisesStalePlanErroron the nextrun_stepafter a bump (§8.2 staleness guard).generation— bumped by plan-internal buffer swaps (Swap). It invalidates the façade’s cached DataArray wrappers only; the plan stays valid (swaps are its own doing).
- class icon_sc.core.state.vault.SlotMeta(name, dims, units, location, dtype, attrs)¶
Boundary metadata of one vault slot (what the façade needs to rewrap it).
- class icon_sc.core.state.vault.StateVault¶
Slotted execution-phase state (frozen interface, SPEC S05).
StateVault.from_state(state)adopts the state’s raw buffers (zero-copy:.dataof every boundary DataArray, never.values);vault.facade()returns the lazy dict-of-DataArrays view.buffers/names/schema_hash/epochare the public §8.2 surface; the compiler may append slots for published outputs at materialization (add_slot), which refreshesschema_hash.- classmethod from_state(state)¶
Build a vault over a dict-of-DataArrays state (frozen interface).
Buffers are adopted, not copied; the
timeentry is carried alongside the slots (it is not a field). Non-DataArray entries other thantimeare rejected — the vault is the execution form of a boundary state.- Parameters:
- Return type:
- add_slot(name, buffer, meta)¶
Append a published-output slot (compiler use, bind/materialize time).
Refreshes
schema_hash(the slot set is part of the schema) and bumpsgenerationso a live façade picks the new field up.
- dim_sizes()¶
Dimension-name → length map over all slots (consistency-checked).
- note_out_of_band_mutation()¶
Record an out-of-band mutation (façade rebind/delete): plans go stale.
- Return type:
None
- note_swap()¶
Record a plan-internal buffer swap: façade caches invalidate, plans stay valid.
- Return type:
None
- facade()¶
The lazy dict-of-DataArrays view (frozen interface, SPEC S05).
DataArray wrappers are cached per slot and reconstructed only when the vault
generationmoved (slot swaps, added slots) — sympl semantics for monitors and inspection, zero cost for the step path. The same façade object is returned on every call. Rebinding or deleting a field through the façade bumpsepochand stales any bound plan.
Lazy DataArray façade over a StateVault (§8.2).
The public dict-of-DataArrays state view of the execution tier: DataArray
wrappers are materialized on access, cached per slot, and invalidated by the
vault’s generation counter (bumped by plan-internal swaps and slot
additions). Rebinding or deleting a field through the façade is an
out-of-band mutation: it bumps the vault epoch, and any plan bound to the
vault raises StalePlanError on its next run_step (SPEC S05 guard).
In-place writes to a field’s buffer (facade[name].data[...] = ...) preserve
buffer identity and do not stale plans — values are the user’s business,
identities are the plan’s.
- class icon_sc.core.state.facade.VaultFacade(vault)¶
Mapping view of a vault: slot fields as boundary DataArrays plus
time.- Parameters:
vault (StateVault)