icon_sc.core.contracts — property contracts¶
Contracts: property-dict schema, static/dynamic checkers, ingress/egress plans.
Typed property-dict schema (architecture §2.4, §8.6).
Components keep sympl’s input_properties / tendency_properties /
diagnostic_properties / output_properties dicts; ICON-sc’s schema extends the
sympl entries (dims, units, alias) with location, halo,
differentiable (§8.6) and params (§8.6), and drops sympl’s wildcard/dims-like
machinery — canonical names + canonical units make target dims explicit.
parse_properties() validates the dicts themselves (definition-only, no data)
and normalizes them into frozen, slotted PropertySpec records; it is the
substrate the static checker runs on.
- class icon_sc.core.contracts.properties.Differentiable(value)¶
Differentiability axis of the contract (§8.6).
- class icon_sc.core.contracts.properties.HaloPolicy(value)¶
Contract-side halo requirement of one field in one property dict (§2.4).
- exception icon_sc.core.contracts.properties.PropertyDictError¶
A property dict is malformed (definition-time error, names the component).
- class icon_sc.core.contracts.properties.PropertySpec(name, dims, units, location, halo=None, alias=None, dtype=None, differentiable=Differentiable.NONE, params=())¶
Normalized, immutable form of one property-dict entry (frozen interface).
- Parameters:
name (str)
units (str)
location (Location)
halo (HaloPolicy | None)
alias (str | None)
dtype (dtype[Any] | None)
differentiable (Differentiable)
- icon_sc.core.contracts.properties.parse_properties(properties)¶
Validate a property dict and normalize it (frozen interface, SPEC S02).
Checks the dict itself — entry types, required/unknown keys, enum values, alias bijectivity — with errors naming the offending field. Insertion order is kept (it is the ingress argument order).
Static and dynamic contract checkers (architecture §4.2 ← Ubbiali sympl fork).
The static/dynamic split is the internal structure of the negotiation phase (§8.2):
StaticCheckerconsumes component definitions only — it validates the property dicts of a component class at composition time (invoked from the__init_subclass__hook S03 wires).DynamicCheckercrosses definitions with actual data at bind time: it checks one property dict against a state (or a lightweightStateSchema, so S05 can reuse it without real data).strict=True(production, §2.4) turns any ingress that would allocate — unit conversion, dim transpose, dtype cast, host↔device transfer — into an exception naming field and component;strict=Falsecollects the needed conversions into aConversionPlaninstead.
Neither checker runs on the step path.
- icon_sc.core.contracts.checkers.PROPERTY_DICT_NAMES: tuple[str, ...] = ('input_properties', 'tendency_properties', 'diagnostic_properties', 'output_properties')¶
The sympl property dicts a component may define (§2.4).
- class icon_sc.core.contracts.checkers.ContractViolation(field, component, kind, actual, target)¶
One convertible contract violation: field, component, kind, actual vs wanted.
- exception icon_sc.core.contracts.checkers.ContractViolationError(violations)¶
Strict-mode contract failure; carries every violation found.
- Parameters:
violations (list[ContractViolation])
- Return type:
None
- class icon_sc.core.contracts.checkers.DynamicChecker(spec, state, *, component='<component>', strict=True, device=None)¶
Definition x data checks (frozen interface, SPEC S02).
DynamicChecker(spec, state)—specis a parsed property dict (Mapping[str, PropertySpec]),statea dict-of-DataArrays or aStateSchema. Missing fields, incompatible dim sets and location mismatches raise unconditionally (no conversion exists). Convertible mismatches — units, dim order, dtype, device — raise understrict=True(each named by field + component) and are collected intoself.plan(aConversionPlan) understrict=False.Device expectation:
device(a DLPack device tuple) is normally supplied by the caller (S03’s ComputeContext / S05’s plan compiler). Withdevice=Nonethe checker only enforces that all spec’d fields share one device, adopting the device of the first field in property-dict order as the expectation — so which field gets flagged on a mixed-device state depends on property-dict order. Passdeviceexplicitly whenever a backend-mandated device exists.- Parameters:
spec (Mapping[str, PropertySpec])
state (Mapping[str, Any] | StateSchema)
component (str)
strict (bool)
- class icon_sc.core.contracts.checkers.FieldSchema(dims, units, dtype, device, location=None)¶
Shape-free schema of one state field: what the dynamic checkers consume.
- class icon_sc.core.contracts.checkers.StateSchema(fields)¶
name →
FieldSchemamap; the data-free face of a state (§8.2, S05).- Parameters:
fields (Mapping[str, FieldSchema])
- classmethod from_state(state)¶
Derive the schema of a dict-of-DataArrays state (skips the
timekey).- Parameters:
- Return type:
- class icon_sc.core.contracts.checkers.StaticChecker(component_cls)¶
Definition-only checks on a component class (frozen interface, SPEC S02).
StaticChecker(component_cls)validates every property dict the class defines (schema of the dicts themselves), cross-dict consistency (dims and canonical units of same-named state-valued entries, alias bijectivity) and — for names known to the canonical registry — that declared units are the canonical units. A constructed instance means the definition passed and exposes the parsed specs.- Raises:
PropertyDictError – Naming the offending component and field.
- Parameters:
component_cls (type)
ConversionPlan executor: allocating non-strict ingress (SPEC S03, §2.4).
Strict mode turns any ingress that would allocate into an error; with
strict=False the DynamicChecker
collects the needed conversions into a
ConversionPlan instead, and this module
executes that plan — the debugging/education path of T0 (never the production
path, never the step path).
apply_conversion_plan returns a new shallow-copied state whose offending
fields are replaced by converted copies; the input state is never mutated.
- exception icon_sc.core.contracts.conversion.ConversionError¶
A conversion step cannot be executed (unsupported kind or bad data).
- icon_sc.core.contracts.conversion.apply_conversion_plan(plan, state, *, component='<component>')¶
Execute
planagainststate(allocating; negotiation-time only).Steps are applied in plan order, several per field composing left to right. The returned dict shares every untouched entry with
state; converted fields are copies with their attrs schema (units/location/halo) preserved and updated. Errors name field and component.
Dynamic operators: ingress/egress plans and conversion plans (§2.3, §4.2, §8.2).
An IngressPlan is built once at bind time from a property dict and a
StateSchema (S05 pre-resolves it into bound
argument packs) and applied per step by S03’s interpreted tier. The plan holds field
names only — schema in, raw buffers out; no xarray objects, no Pint, no per-step
negotiation. apply is a plain tuple comprehension over state[name].data:
zero-copy by construction (buffer identities are stable across applications).
ConversionPlan is what the non-strict dynamic checker returns instead of
raising: the ordered allocating conversions (units/transpose/cast/transfer) that
would reconcile a state with a contract. S03 executes it; strict mode forbids it.
- class icon_sc.core.contracts.operators.ConversionPlan(steps=())¶
Ordered conversions the non-strict checker proposes (empty = no-op ingress).
- Parameters:
steps (tuple[ConversionStep, ...])
- class icon_sc.core.contracts.operators.ConversionStep(field, kind, source, target)¶
One allocating reconciliation of a field with its contract.
- class icon_sc.core.contracts.operators.EgressPlan(component, fields, names)¶
Egress twin of
IngressPlan: resolves a component’s output buffers.Mechanically identical — outputs are caller-provided/preallocated DataArrays living in the same state mapping (§8.2 buffer-identity contract), so egress is the same pre-resolved raw-buffer extraction, kept as its own type so plans are self-describing in S03/S05 op lists.
- class icon_sc.core.contracts.operators.IngressPlan(component, fields, names)¶
Pre-resolved zero-copy ingress (frozen interface, SPEC S02).
namesare the state keys to pull, in property-dict (= argument) order, with aliases already resolved;fieldsare the contract names they satisfy.- classmethod build(spec, state_schema, *, component='<component>', device=None)¶
Build the plan at bind time; strict by construction.
Runs the strict dynamic checker: a state that would need any allocating conversion cannot be pre-resolved (run the
DynamicCheckerwithstrict=Falseand execute its plan first).deviceis the DLPack device expectation forwarded to the checker;Nonefalls back to the checker’s first-field baseline (see its docstring).- Parameters:
- Return type: