Source code for continuo.api
"""Programmatic API: load a model and simulate it.
:func:`parse` runs the whole front end — macro expansion, parsing, and IR
construction — and returns a :class:`Model`, the user-facing handle whose
:meth:`Model.simul` and :meth:`Model.steady_state` call the solver. This
is the layer the CLI wraps; CasADi and the internal IR are not exposed.
import continuo
model = continuo.parse("model.mod")
sol = model.simul() # reads the simulate command
"""
from __future__ import annotations
from pathlib import Path
from continuo import ir
from continuo.io.solution import Solution
from continuo.macro import expand, expand_string
from continuo.parser import parse as _parse_text
from continuo.solve import simulate, steady_state
__all__ = ["Model", "parse", "parse_string"]
[docs]
class Model:
"""A loaded model: its symbol table and the solver entry points."""
def __init__(self, model: ir.Model):
self._model = model
# -- inspection ---------------------------------------------------------
@property
def states(self) -> tuple[str, ...]:
return self._model.states
@property
def jumps(self) -> tuple[str, ...]:
return self._model.jumps
@property
def algebraic(self) -> tuple[str, ...]:
return self._model.algebraic
@property
def endogenous(self) -> tuple[str, ...]:
return self._model.endogenous
@property
def exogenous(self) -> tuple[str, ...]:
return self._model.exogenous
@property
def parameters(self) -> tuple[str, ...]:
return self._model.parameters
def __repr__(self) -> str:
return (
f"<continuo.Model: {len(self.endogenous)} endogenous "
f"({len(self.states)} state, {len(self.jumps)} jump, "
f"{len(self.algebraic)} algebraic), {len(self.parameters)} parameters>"
)
# -- solving ------------------------------------------------------------
[docs]
def steady_state(self, *, exogenous: dict[str, float] | None = None) -> dict[str, float]:
"""Compute the steady state at the given exogenous configuration."""
return steady_state(self._model, exogenous=exogenous)
[docs]
def simul(
self,
*,
horizon: float | None = None,
intervals: int | None = None,
scheme: str | None = None,
) -> Solution:
"""Run the perfect-foresight simulation, returning a :class:`Solution`.
``horizon`` / ``intervals`` / ``scheme`` override the model's
``simulate`` command.
"""
return simulate(self._model, horizon=horizon, intervals=intervals, scheme=scheme)
[docs]
def parse(path: str | Path) -> Model:
"""Load a ``.mod`` file (macro expansion, parsing, IR) into a :class:`Model`."""
text, _linemap = expand(path)
return Model(ir.build(_parse_text(text)))
[docs]
def parse_string(source: str, *, base_dir: str | Path | None = None) -> Model:
"""Load model source given as a string into a :class:`Model`."""
text, _linemap = expand_string(source, base_dir=base_dir)
return Model(ir.build(_parse_text(text)))