openghg_inversions.basis.operators#
Labelled basis geometry operators for retained scaling states.
A BasisOperator separates bucket geometry from flux weighting and
native covariance. For one source, BasisOperator.basis_matrix is the
bucket prolongation U_bucket with native-grid rows and retained-state
columns. A gathered multisource matrix is the spatial membership template from
which BasisOperator.native_prolongation() expands a canonical
source-native U_bucket. Its transpose is not automatically the retained
restriction Pi.
FluxWeightedBasis handles flux weighting for sensitivity projection and
flux reconstruction. Native covariance actions, retained restrictions, and
covariance product transforms instead belong to
openghg_inversions.native_covariance,
openghg_inversions.source_covariance, and
openghg_inversions.basis.covariance_products.
Operators expose one canonical state dimension, named "state" by default.
Multisource operators represent ragged per-source region counts with a
MultiIndex over source and region-within-source, avoiding padded state arrays.
BasisMeta records only the grid dimensions and state-dimension name;
concrete operators perform any source-label alignment.
Serialization uses self-describing xarray.DataTree objects with
schema, kind, and version metadata. Multisource operators store one
source-labelled basis_flat array while readers retain compatibility with
the earlier per-source child layout.
Examples
Construct operators, compute a sensitivity, and round-trip one operator:
op = BucketBasisOperator(basis_flat)
multisource_op = MultiSourceBucketBasisOperator({"a": bf_a, "b": bf_b})
sensitivity = op.sensitivity(fp_x_flux)
restored = BasisOperator.decode_datatree(op.to_datatree())
fp_x_flux contains the configured grid dimensions and typically time.
In multisource workflows it may also have a source dimension, whose
labels the multisource operator aligns to the state MultiIndex.
Note
Basis geometry cannot currently vary through time. A singleton time
dimension is dropped; a longer time dimension raises ValueError.
- class openghg_inversions.basis.operators.BasisMeta(grid_dims: tuple[str, ...] = ('lat', 'lon'), state_dim: str = 'state')#
Bases:
objectMetadata describing how to apply a basis operator.
The intent is to keep this minimal: we only store what is needed for the default implementations of BasisOperator.sensitivity and BasisOperator.interpolate.
- Variables:
- class openghg_inversions.basis.operators.BasisOperator#
Bases:
ABCAbstract basis operator.
Concrete subclasses provide operator metadata, a bucket prolongation
U_bucketwhose ordered dimensions are the grid dimensions followed by the state dimension, and DataTree serialization methods.The default sensitivity implementation requires
fp_x_fluxto contain the grid dimensions and preserves extra dimensions such as time or source.- abstract property basis_matrix: DataArray#
Return the bucket prolongation
U_bucketfrom state to grid.Its ordered dimensions are the configured grid dimensions followed by the state dimension, which may carry a MultiIndex coordinate.
Multiplying this matrix by a state vector reconstructs a native scaling field. Although its transpose has the shape of a restriction, it is not generally the covariance-compatible retained restriction
Pi.
- classmethod decode_datatree(dt: DataTree) BasisOperator#
Dispatches a DataTree to the correct registered operator subclass.
- Parameters:
dt – DataTree representation of a basis operator.
- Returns:
A concrete BasisOperator instance.
- Raises:
ValueError – If the schema or schema version is unsupported.
KeyError – If the kind is not registered.
- abstractmethod classmethod from_datatree(dt: DataTree) Self#
Constructs an operator instance from an xarray.DataTree.
Concrete subclasses implement this to load whatever canonical representation they write in to_datatree().
- Parameters:
dt – DataTree created by to_datatree().
- Returns:
An instance of the operator.
- interpolate(state: DataArray, weights: DataArray | None = None) DataArray#
Interpolates/reconstructs a gridded field from a state vector.
This maps from the reduced basis space back to the grid by multiplying the basis dummy matrix by a state vector.
If weights is provided (e.g. a flux field on the grid), it is multiplied elementwise with the basis matrix before interpolation. This corresponds to using a flux-weighted interpolation operator.
- Parameters:
state – State vector with dimension meta.state_dim.
weights – Optional gridded weights with dimensions matching meta.grid_dims (and broadcastable to basis_matrix).
- Returns:
Reconstructed gridded field with dimensions including meta.grid_dims.
- Raises:
ValueError – If meta.state_dim is not a dimension of state.
- native_prolongation(native_layout: DataArray, *, native_dims: tuple[str, ...]) DataArray#
Return the bucket prolongation on a canonical labelled native grid.
This basis-side operation aligns geometry only. It preserves the basis matrix’s lazy or sparse storage and does not choose an execution or covariance materialization policy.
- Parameters:
native_layout – Array carrying the canonical native coordinates and compatible auxiliary coordinates. It may contain additional non-native dimensions.
native_dims – Ordered native dimensions required by the covariance action.
- Returns:
Potentially lazy
U_bucketwith dimensions(*native_dims, meta.state_dim).- Raises:
ValueError – If this single-source basis does not span exactly the requested native dimensions or their indexes do not match.
- sensitivity(fp_x_flux: DataArray, fillna: bool = True) DataArray#
Computes the sensitivity matrix (“H”) by dotting over the grid.
This implements the common bucket-basis forward operator
H U_bucket:fp_x_flux is a gridded quantity with dimensions that include meta.grid_dims (typically lat and lon) and usually a time dimension.
basis_matrix is a one-hot/dummy matrix that maps each grid cell to exactly one basis region/state.
The returned array keeps all non-grid dimensions from fp_x_flux and includes the reduced state dimension meta.state_dim.
- Parameters:
fp_x_flux – Footprint x flux array to reduce. Must contain all meta.grid_dims.
fillna – if True, fill NaNs in fp_x_flux with 0.0.
- Returns:
Sensitivity matrix with dimension meta.state_dim and any remaining non-grid dimensions (e.g. time).
- class openghg_inversions.basis.operators.BucketBasisOperator(basis_flat: DataArray, *, meta: BasisMeta | None = None, state_dim: str | None = None, region_labels: Literal['range0', 'range1', 'basis_values'] = 'range0', state_metadata: Dataset | BasisStateMetadata | None = None, chunks: dict[str, int] | None = None)#
Bases:
BasisOperatorSingle flat bucket basis: basis_flat(lat, lon) with integer region labels.
Stores basis_flat and constructs basis_matrix via get_xr_dummies.
- property basis_matrix: DataArray#
Return
U_bucket, mapping retained scalings to native scalings.The dimensions are native grid by retained state. Its transpose is not generally the compatible retained restriction
Pi.
- classmethod from_datatree(dt: DataTree) Self#
Deserialises a BucketBasisOperator from a DataTree.
- Parameters:
dt – DataTree produced by BucketBasisOperator.to_datatree().
- Returns:
A reconstructed BucketBasisOperator.
- Raises:
KeyError – If the serialized
basis_flatvariable is missing.ValueError – If serialized labels, metadata, or state coordinates are invalid.
- class openghg_inversions.basis.operators.MultiSourceBucketBasisOperator(basis_flat: dict[str, DataArray], *, meta: BasisMeta | None = None, source_dim: str = 'source', region_in_source_dim: str = 'region_in_source', state_dim: str | None = None, chunks: dict[str, int] | None = None)#
Bases:
BasisOperatorMultiple flat bases keyed by source, with potentially ragged region counts.
The canonical state dimension is a ragged MultiIndex over
(source, region_in_source).- property basis_matrix: DataArray#
Return the gathered spatial template for multisource
U_bucket.The dimensions are spatial grid by retained state; source identity is carried by the ragged state coordinate.
native_prolongation()expands an explicit source-native dimension and zeros cross-source columns. This template’s transpose is not the compatible retained restrictionPi.
- classmethod from_datatree(dt: DataTree) Self#
Deserialises a MultiSourceBucketBasisOperator from a DataTree.
- Parameters:
dt – DataTree produced by MultiSourceBucketBasisOperator.to_datatree().
- Returns:
A reconstructed MultiSourceBucketBasisOperator. The canonical representation obtains order from the
sourcecoordinate. Earlier per-source child artifacts remain readable; for those, legacysource_ordermetadata controls insertion/state order when present.- Raises:
ValueError – If required basis data or source coordinates are missing, or legacy source-order metadata is malformed or inconsistent with stored source children.
- interpolate(state: DataArray, weights: DataArray | None = None) DataArray#
Interpolate/reconstruct a gridded field from a state vector.
For MultiSourceBucketBasisOperator, weights may include a source_dim that is also a level name in the gathered MultiIndex on meta.state_dim. In that case we broadcast weights along the gathered state axis (repeating per-source weights across all regions within that source) by replacing source_dim with meta.state_dim.
The state vector itself is expected to be defined on meta.state_dim and should not include a separate coordinate named like a MultiIndex level (e.g. source_dim).
- Parameters:
state – State vector defined on meta.state_dim.
weights – Optional gridded weights (e.g. prior fluxes) on meta.grid_dims. May optionally include source_dim for per-source weights.
- Returns:
Gridded reconstructed field on meta.grid_dims.
- native_prolongation(native_layout: DataArray, *, native_dims: tuple[str, ...]) DataArray#
Expand the gathered basis to a labelled source-native prolongation.
- Parameters:
native_layout – Canonical native layout carrying an explicit source dimension and native-compatible auxiliary coordinates.
native_dims – Ordered native dimensions. The first must be this operator’s source dimension and the remainder its grid dims.
- Returns:
Potentially lazy
U_bucketwith zero cross-source columns and dimensions(*native_dims, meta.state_dim).- Raises:
ValueError – If dimensions, source labels, or native indexes are incompatible with this multisource basis.
- operator_for_source(source: str, *, state_dim: str | None = None) BucketBasisOperator#
Return a single-source bucket operator for one source.
This keeps source-specific basis selection at the operator boundary, avoiding direct use of the legacy flat-basis compatibility view in modern postprocessing code.
- Parameters:
source – Source label to select from the source-specific basis mapping.
state_dim – Optional state dimension for the returned single-source operator. If omitted, the per-source region dimension is used.
- Returns:
A single-source bucket operator for
source.- Raises:
ValueError – If
sourceis not present in this operator.
- sensitivity(fp_x_flux: DataArray, fillna: bool = True) DataArray#
Compute sensitivity for multisource fp_x_flux.
Fuse source selection with the sparse spatial prolongation before contracting. This avoids broadcasting the full spatial cache across every retained state while keeping the source pairing sparse.
- property source_labels: tuple[str, ...]#
Return canonical source labels in operator/state insertion order.
- Returns:
Source labels in the same order used by
basis_flatand the ragged state MultiIndex.
- to_datatree() DataTree#
Serialises the multisource operator to a DataTree.
The returned DataTree stores one source-labelled
basis_flatarray. Its source coordinate is the sole source-order representation, avoiding source names in storage paths and redundant JSON metadata.- Returns:
DataTree representation of the operator.
- Raises:
ValueError – If source bases do not have compatible labeled grids.
- openghg_inversions.basis.operators.drop_singleton_time(da: DataArray, *, name: str = 'basis_flat') DataArray#
Drop a singleton
timedimension if present; otherwise raise.This is a strict helper intended for basis operators that assume a 2D basis over the grid dims. It avoids silently discarding time-varying basis information.
- Parameters:
da – Input DataArray which may or may not have a
timedimension.name – Label used in error messages to identify what is being checked.
- Returns:
dawithtimeremoved if it exists and has length 1, otherwisedaunchanged.- Raises:
ValueError – If
timeexists and has length not equal to 1.
- openghg_inversions.basis.operators.get_basis_operator_class(kind: str) type[BasisOperator]#
Looks up a registered BasisOperator subclass.
- Parameters:
kind – Registry key for the operator type (e.g. “bucket”).
- Returns:
The registered BasisOperator subclass.
- Raises:
KeyError – If kind is not registered.
- openghg_inversions.basis.operators.register_basis_operator(kind: str) Callable[[type[BasisOperatorT]], type[BasisOperatorT]]#
Registers a BasisOperator subclass for DataTree deserialisation.
This decorator builds a small module-level registry mapping a stable kind string (stored in dt.attrs[“kind”]) to a concrete BasisOperator subclass.
- Parameters:
kind – Stable key identifying the operator type on disk. This is written to dt.attrs[“kind”] by BasisOperator.to_datatree() and used by BasisOperator.decode_datatree().
- Returns:
A class decorator that registers the decorated class under kind.
- Raises:
ValueError – If kind is already registered to a different class.