Version: dev

model

Stable type · Document types

Tier: stable · Kind: type · Topic: Document types

JuliaTypeScriptPythonRustGo
Modelnot exportedModelModelModel

Julia

struct Model <: EarthSciAST.SubsystemNode
Model(variables::AbstractDict{String, ModelVariable}, equations::Vector{Equation}, discrete_events::Vector{DiscreteEvent}, continuous_events::Vector{ContinuousEvent}, subsystems::AbstractDict{String}; tolerance, tests, analyses, initialization_equations, guesses, system_kind, reference)
Model(variables::AbstractDict{String, ModelVariable}, equations::Vector{Equation}; discrete_events, continuous_events, subsystems, tolerance, tests, analyses, initialization_equations, guesses, system_kind, reference)
Model

ODE-based model component containing variables, equations, and optional subsystems. Supports hierarchical composition through subsystems. A subsystem value is a child Model or — only until references are resolved — a SubsystemRef; the field is typed by their shared supertype, OrderedDict{String,SubsystemNode}.

variables and subsystems are OrderedDicts, and the parser populates them in DOCUMENT ORDER. That is not a convenience: esm-libraries-spec §4.7.5 step 4 makes the flattened system’s ordering normative (variables in the order their component declares them), and a flattener can only inherit an order the IR preserved. A plain Dict here leaked its hash order all the way out to flatten’s parameters, which a consumer reads POSITIONALLY as the parameter vector.

Source

Example (inspect)

model = doc.models["Decay"]
@assert sort(collect(keys(model.variables))) == ["N", "half_life", "lambda"]

# The two declared kinds...
@assert unknowns(model) == ["N", "half_life"]
@assert parameters(model) == ["lambda"]

# ...and what each unknown is, derived from the equations.
@assert ode_states(model) == ["N"]                 # has a D(N)/dt equation
@assert observed_unknowns(model) == ["half_life"]  # defined explicitly
@assert algebraic_unknowns(model) == String[]      # none solved implicitly

defs = observed_definitions(model)
@assert to_ascii(defs["half_life"]) == "log(2) / lambda"

From docs/api/examples/julia/documents.jl

TypeScript

TypeScript does not export this type.

Python

class Model
Model(name: str, variables: dict[str, ModelVariable] = dict(), equations: list[Equation] = list(), metadata: dict[str, Any] = dict(), subsystems: dict[str, Model] = dict(), tolerance: Tolerance | None = None, tests: list[Test] = list(), analyses: list[Analysis] = list(), initialization_equations: list[Equation] = list(), guesses: dict[str, float | Expr] = dict(), system_kind: str | None = None, continuous_events: list[ContinuousEvent] = list(), discrete_events: list[DiscreteEvent] = list(), reference: Reference | None = None)

A mathematical model containing variables and equations.

Source

Example (inspect)

model = doc.models["Decay"]
for name, var in model.variables.items():
    print(f"{name}: {var.type} [{var.units}]")
assert {n: v.type for n, v in model.variables.items()} == {
    "N": "unknown",
    "lambda": "parameter",
    "half_life": "unknown",
}

# The file declares only "unknown" and "parameter"; the finer classes are derived.
assert earthsci_ast.ode_states(model) == ["N"]
assert earthsci_ast.observed_unknowns(model) == ["half_life"]
assert earthsci_ast.algebraic_unknowns(model) == []
assert earthsci_ast.constant_parameters(model) == ["lambda"]
assert earthsci_ast.unknowns(model) == ["N", "half_life"]

From docs/api/examples/python/documents.py

Rust

pub struct Model {
    pub name: Option<String>,
    pub reference: Option<Reference>,
    pub variables: IndexMap<String, ModelVariable>,
    pub equations: Vec<Equation>,
    pub discrete_events: Option<Vec<DiscreteEvent>>,
    pub continuous_events: Option<Vec<ContinuousEvent>>,
    pub subsystems: Option<IndexMap<String, Value>>,
    pub description: Option<String>,
    pub tolerance: Option<Tolerance>,
    pub tests: Option<Vec<ModelTest>>,
    pub analyses: Option<Vec<ModelAnalysis>>,
    pub initialization_equations: Option<Vec<Equation>>,
    pub guesses: Option<HashMap<String, Value>>,
    pub system_kind: Option<String>,
}

ODE-based model component

name / description are NOT schema keys

The schema’s Model is additionalProperties: false and declares neither name (a model’s name is its key in the models map) nor description. Both fields below are therefore schema-INVALID the moment they are set, and save() will happily emit a document that fails its own schema. They are harmless only while they stay None, which is what every load-parsed model leaves them as — no deserializer ever fills them, because the wire form has no such keys.

They are not dead, though: crate::update_model_metadata — a tier- extension, Rust-only entry in api-surface.json — sets both, so its ONLY observable effect is to make a valid document invalid. graph.rs also reads name for a component-node label, where it is always None in practice. They are part of the published API surface, so they stay; leave them None.

Source · Rust docs

Go

type Model struct {
	Reference        *Reference               `json:"reference,omitempty"`
	Variables        map[string]ModelVariable `json:"variables"`
	Equations        []Equation               `json:"equations"`
	DiscreteEvents   []DiscreteEvent          `json:"discrete_events,omitempty"`
	ContinuousEvents []ContinuousEvent        `json:"continuous_events,omitempty"`
	Subsystems       map[string]any           `json:"subsystems,omitempty"`
	// Tolerance is the model-level default numerical tolerance applied to
	// inline tests that do not override it (esm-spec §6.6).
	Tolerance *Tolerance `json:"tolerance,omitempty"`
	// Tests are inline validation tests for this model (esm-spec §6.6).
	Tests []Test `json:"tests,omitempty"`
	// Analyses are inline illustrative runs + plot specs (esm-spec §6.7).
	Analyses []Analysis `json:"analyses,omitempty"`
	// InitializationEquations hold only at t=0 (not dynamically time-stepped).
	// Used by models that must solve an auxiliary system before the main
	// time-stepping begins (e.g. aerosol equilibrium, plume rise). See gt-ebuq.
	InitializationEquations []Equation `json:"initialization_equations,omitempty"`
	// Guesses provides initial-guess seeds for nonlinear solvers during
	// initialization, keyed by variable name. Values may be numeric literals
	// or Expression graphs (serialized as interface{}).
	Guesses map[string]any `json:"guesses,omitempty"`
	// SystemKind discriminates the MTK system type this model maps to.
	// One of "ode" (default), "nonlinear", "sde", "pde".
	SystemKind *string `json:"system_kind,omitempty"`
}

func (m *Model) UnmarshalJSON(data []byte) error

Model represents an ODE system

Methods

func (m *Model) UnmarshalJSON(data []byte) error

Custom JSON unmarshaling for Model. Decodes guesses values through UnmarshalExpression so integer-valued initial guesses keep their int wire shape (RFC §5.4.1); every other field decodes exactly as its struct tags specify (via the alias type, which does not carry this method and so uses the default struct decoder). The embedded Guesses shadows the alias’s field so the raw guess objects are captured here rather than decoded lossily.

Source · Go docs