solve
Stable function · Simulation
Tier: stable · Kind: function · Topic: Simulation
| Julia | TypeScript | Python | Rust | Go |
|---|---|---|---|---|
| not exported | outside profile | solve | solve | outside profile |
Julia
Julia does not export this function.
TypeScript
Not available in TypeScript. TypeScript is outside the simulation capability profile (Julia, Python, Rust): build an RHS and integrate it; TypeScript and Go are deliberately non-simulating.
Python
solve(prob: EsmProblem | EnsembleProblem, *, alg: str = DEFAULT_ALG, abstol: float | None = None, reltol: float | None = None, saveat: Any = None, callback: Any = None, maxiters: int | None = None, trajectories: int | None = None) -> Solution | list[Solution]
Run prob to completion and return its Solution
.
The vocabulary is SciML’s, not SciPy’s (API_SPEC §4): alg (not
method), abstol (not atol), reltol (not rtol), saveat
(not t_eval). The defaults — reltol=1e-4, abstol=1e-6 — are the
canonical cross-binding ones, so Julia, Python and Rust solving the same
document with default options produce comparable trajectories. Pass an
explicit tolerance when accuracy matters.
Parameters
prob(EsmProblem | EnsembleProblem) — TheEsmProblemto run, or anEnsembleProblem(in which casetrajectoriesis required and alistof solutions comes back).alg(str) — The solver algorithm. This binding’s ecosystem has no first-class algorithm object, so a SciPy method name is accepted (§2.5.3).abstol,reltol— Absolute and relative INTEGRATION tolerances.None(the default) means “not given” and resolves per esm-spec §2.2.2, most-specific first: an explicit argument here, then the document’ssolver.abstol/solver.reltol(§2.2), then the binding default (abstol1e-6,reltol1e-4). The two resolve independently, so a document declaring onlyreltolleavesabstolon the default.These are a different quantity from the
toleranceobject an assertion is COMPARED at (§6.6.4), which resolves on its own chain.saveat(Any) — Output times: an explicit sequence, or a scalar output STEP measured fromtspan[0].Nonekeeps the dense uniform default grid.callback(Any) — REPLACES the EsmProblem’s callback set entirely — it does not append or merge (§2.5.4). To extend it, compose explicitly withcallbacks(prob) + extra.maxiters(int | None) — Budget of right-hand-side evaluations. When spent, the run stops and reportsReturnCode.MaxIters.None(the default) is unbudgeted.
Returns
Solution— Indexed by variable NAME, carrying aReturnCode. A failure that the solver or the model reports comes back as a non-Successcode, not an exception, so interactive workflows can branch on it; a dimensionality violation still raises.
Example (simulate)
doc = earthsci_ast.load_path(FIXTURES / "decay.esm")
prob = earthsci_ast.esm_problem(doc, (0.0, 10.0))
half_life = math.log(2) / 0.1
sol = earthsci_ast.solve(prob, saveat=[0.0, half_life, 10.0])
assert sol.retcode == earthsci_ast.ReturnCode.Success
# A solution is indexed by variable name.
n = sol["N"]
print("N at t = 0, ln 2 / λ, 10:", n)
assert math.isclose(n[1], 50.0, rel_tol=1e-3)
assert math.isclose(n[-1], 100.0 * math.exp(-1.0), rel_tol=1e-3)
# Observed variables are read the same way.
assert math.isclose(sol["half_life"][-1], half_life, rel_tol=1e-9)
From docs/api/examples/python/simulate.py
Example (ensemble_problem)
rates = [0.1, 0.2, 0.3]
ensemble = earthsci_ast.EnsembleProblem(
prob, lambda p, i: earthsci_ast.remake(p, p={"lambda": rates[i]})
)
runs = earthsci_ast.solve(ensemble, trajectories=len(rates), saveat=[10.0])
finals = [run["N"][-1] for run in runs]
for rate, final in zip(rates, finals):
assert math.isclose(final, 100.0 * math.exp(-rate * 10.0), rel_tol=1e-3)
Rust
pub fn solve(prob: &EsmProblem, opts: &SolveOptions) -> Result<Solution, SimulateError>
Run prob to completion (§2.5.3).
opts carries only per-run knobs: alg, abstol, reltol, saveat,
callback, maxiters. The result carries a
retcode
— a caller distinguishes “ran to tspan.1”
from “stopped early, here is why” without parsing prose.
opts.callback REPLACES the EsmProblem’s callback set entirely. It does
not append, merge or wrap. To extend rather than replace, read the set back
with callbacks
and compose
explicitly. See §2.5.4 for why
replacement is the safe default.
opts.output_observed adds rows. A Solution
carries state rows by
default; a name listed there is appended as extra rows in the flat cell-key
spelling the state uses, which is what crate::derive_output_plan
needs
to write that field alongside the state (RFC decision 8).
Example (simulate)
let path = fixture("decay.esm");
let prob = esm_problem(path.as_path(), (0.0, 10.0), ProblemOptions::default())?;
// Sample the solution at t = ln 2 / λ, one half-life in.
let lambda = 0.1_f64;
let t_half = 2.0_f64.ln() / lambda;
let opts = SolveOptions {
saveat: Some(vec![0.0, t_half]),
..Default::default()
};
let sol = solve(&prob, &opts)?;
assert_eq!(sol.retcode, ReturnCode::Success);
// States are read by name; a bare name resolves when it is unique.
let n = sol.get("N").expect("N is a state");
assert!((n[0] - 100.0).abs() < 1e-9);
assert!((n[1] - 50.0).abs() < 1e-3, "N(ln 2 / λ) = {}", n[1]);
// half_life is an observed: computed from the solution, not stored in it.
let half_life = observed_trajectory(&prob, &sol, "half_life")?;
assert!(half_life.iter().all(|h| (h - t_half).abs() < 1e-9));
Go
Not available in Go. Go is outside the simulation capability profile (Julia, Python, Rust): build an RHS and integrate it; TypeScript and Go are deliberately non-simulating.