Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Somax Ecosystem Refactor Plan

Authors
Affiliations
University of Valencia

A status scrape of the eight sibling libraries in the jejjohnson scientific stack and a phased plan for aligning somax with the framework (pipekit), the domain toolkit (xrtoolz), the numerics foundation (finitevolx / spectraldiffx), and the data-assimilation consumers (vardax / filterax).

Status: Phases 0-2 are merged and Phase 3 is implemented (see What has been done). Phases 4-5 are proposed.

The ecosystem at a glance

        ┌──────────────────── FRAMEWORK (carrier-agnostic) ─────────────────────┐
        │ pipekit            Operator · ConfigMixin · Sequential · Graph · serial │
        │ pipekit-cycle      ForwardModel · ObservationOperator · AnalysisStep    │
        │ pipekit-experiment ModelRegistry · ExperimentTracker · Hydra/DVC adapter│
        │ pipekit-train      TrainingLoop · Loss · datasets   pipekit-jax JaxModelOp│
        └────────────────────────────────────────────────────────────────────────┘
   NUMERICS FOUNDATION              DOMAIN TOOLKITS                 DA CONSUMERS
   gaussx     (structured linalg)   xrtoolz  (xarray preproc+eval)  vardax   (variational DA)
   spectraldiffx (spectral solvers) somax    (PDE/ocean models)     filterax (ensemble DA)
   finitevolx (FV C-grid operators)    ▲ consumes fvx + sdx         both consume gaussx +
        ▲ finitevolx → spectraldiffx    somax ──step()──────────►   pipekit-cycle protocols

Maturity snapshot (all pre-1.0, actively developed)

RepoVersionStateRole for somax
gaussx0.0.15Mature, no stubs, ~122 test filesCovariance / linalg backbone for DA (indirect)
spectraldiffx0.0.12Real; Chebyshev 2D, spherical vorticity inversion, mixed-BC Helmholtzsomax’s spectral Helmholtz backend
finitevolx0.0.41Real; spherical operators, linear EOS, SolveDomain / KnownValueLiftingsomax’s FV operator backend
pipekit0.0.1core / cycle / experiment / train / jax implemented; array / evaluate scaffoldedProtocols + config layer to adopt
xrtoolz0.0.8Broad and real; consumes pipekit.OperatorPreprocessing + evaluation to reuse
vardax0.1.8FourDVarNet real; classical methods architectedConsumes somax as ForwardModel
filterax0.0.3EnKF / ETKF / LETKF / smoothers / EKI real, differentiableConsumes somax as dynamics
somax0.0.8Real model library + CLI; the refactor target—

Where somax stands

What it is. A JAX / equinox / diffrax ocean-modeling library plus a somax-sim CLI runner (~136 Python files). Models: Lorenz 63/96/96t, PDE 1D/2D (diffusion, convection, Burgers, Poisson, Navier–Stokes), shallow-water (1D/2D linear/nonlinear, multilayer), QG (barotropic / baroclinic / reparameterized). The architecture cleanly splits _src/models/ (equations) · _src/cli/scenarios/ (geometry + forcing + IC) · _src/cli/models_registry/ (how to build), funneled through a scenario × model dispatcher.

The core observation. somax predates pipekit and xrtoolz, so it hand-rolls a large amount of “framework” that the rest of the ecosystem now provides:

Hand-written in somax~LOCAlready exists in…
cli/spec.py — dataclasses + from_dict/to_dict/validate/deep-merge/YAML431pipekit.ConfigMixin + serial.dumps/loads; pipekit-experiment Hydra adapter
cli/_run.py — integration / orchestration loop1065pipekit-cycle.Cycle (scan-based stepping + history)
scenarios/ + models_registry/ + _compatibility.py~600pipekit-experiment.ModelRegistry + Operator config
scripts/build_configs.py — Python→YAML materializer66pipekit-experiment Hydra / hydra-zen round-trip
_src/io/xarray.py — state↔Dataset, zarr286xrtoolz (einx.pack/unpack_dataset, io)
cli/_assertions.py — CFL / energy / finite checks235xrtoolz.metrics.physical (geostrophic balance, PV conservation, divergence)

The missing seam. SomaxModel exposed vector_field + integrate (a diffrax Solution) but not step(state, dt) -> state — exactly the pipekit_cycle.ForwardModel protocol method, and the building block the DA libraries call. That one gap blocked vardax/filterax from consuming somax.

Good news on API alignment. somax already imports the new finitevolx names (CartesianGrid2D.from_interior, Mask2D threaded as an operator attribute, no .w / .psi field access, no manual * mask post-multiply). The only stale coupling was the spectraldiffx Helmholtz constructor keyword (alpha → lambda_). So alignment is a version bump, not a rewrite.

The plan

Sequenced by unblocking value. Phases 0–1 are the high-leverage core; 2–3 are the bulk cleanup; 4–5 are the future-facing payoff. Suggested order: 0 → 1 → 3 → 2 → 4 → 5.

Phase 0 — Foundation alignment & dependency hygiene (done)

Phase 1 — Adopt pipekit protocols on the model layer (done)

Phase 2 — Replace hand-rolled config / registry / runner with pipekit (done)

What actually shipped (PR #124): the term algebra, the somax.operators Operator bridge, and the pipekit_cycle.Cycle-driven runner all landed. The config layer below was investigated and kept: pipekit.serial is primitives-only and cannot round-trip somax’s nested-dict YAML schema, so cli/spec.py / RunSpec remain. The pipekit dependency is spent where it fits — Operators + Cycle — not on (de)serialization.

Phase 3 — somax-native evaluation surface (done)

Revised after surveying xrtoolz’s actual API. The original plan was to reuse xrtoolz for IO + metrics. On inspection that premise did not hold: xrtoolz.einx.pack_dataset/unpack_dataset stack a Dataset’s variables into a channel axis (an ML-export op) — they are not a pytree-state↔Dataset converter, and xrtoolz exposes no NetCDF/Zarr IO of its own (it defers to xarray). So there is nothing for _src/io/xarray.py to swap onto; its security-allowlisted class round-trip and zarr-v3 handling stay as-is. xrtoolz’s physical metrics (geostrophic_balance_error, divergence_error) also assume lat/lon ocean observations with Coriolis derived from latitude, whereas somax runs on idealised Cartesian f-/β-plane grids (metres, no lat/lon coords); pv_conservation_error needs Lagrangian trajectories somax does not produce. Finally xrtoolz is a heavy dependency (cartopy, pyproj, rioxarray, xskillscore, …). Decision: give somax its own lightweight evaluation surface on its native grid, with no new dependency.

Phase 4 — Data-assimilation integration (vardax / filterax)

Phase 5 — Training & experiment tracking (opportunistic)

What has been done

Phases 0-2 are merged (PR #124, squash-merged to main): the dependency alignment (finitevolx v0.0.41, spectraldiffx pinned to the git tag v0.0.10), SomaxModel.step for ForwardModel conformance, the composable term algebra (Sum/Scaled/Compose, IMEX lowering), the pipekit Operator bridge (somax.operators), and the pipekit_cycle.Cycle-driven runner. pipekit / pipekit-cycle are base dependencies; somax’s core never imports them (conformance is structural).

Phase 3 (this branch) adds the somax-native evaluation surface:

The full test suite passes, and ruff check, ruff format, and ty check are clean on the changed files.

Note on local development. The finitevolx / spectraldiffx dependencies are private git pins. To run the suite against the local sibling checkouts, temporarily add:

[tool.uv.sources]
finitevolx = { path = "../finitevolX", editable = true }
spectraldiffx = { path = "../spectraldiffx", editable = true }

to pyproject.toml, then uv sync. Remove it before committing — the committed dependency spec uses the published git tags.