Getting Started¶
ModelCore owns a time grid and state history, while CoreSolver advances that
state. Solves infer their Array-API namespace from the initial state.
The scalar decay below is one introductory deterministic ODE. The same engine also advances spatial/trait-structured PDEs, nonlocal operators and split dynamics, and stochastic reaction networks. See the model-class map for numerical interfaces and boundaries.
import numpy as np
from op_engine import CoreSolver, ModelCore
times = np.linspace(0.0, 2.0, 21)
core = ModelCore(n_states=1, n_subgroups=1, time_grid=times)
core.set_initial_state(np.asarray([[1.0]]))
def decay(_time, state):
return -0.25 * state
CoreSolver(core).run(decay)
solution = core.state_array
Array-backend solves¶
array-api-compat discovers the numerical namespace from NumPy, JAX, CuPy,
PyTorch, and other supported native arrays. Passing an array to
set_initial_state selects the namespace for state storage, history, solver
stages, and adaptive error control. No backend argument is needed. An RHS
should derive operations from its state when it needs namespace functions:
from op_engine import array_namespace
def portable_decay(_time, state):
xp = array_namespace(state)
return xp.multiply(state, -0.25)
Dense implicit and IMEX operators are converted into the state's namespace and
solved with its Array-API linalg.solve implementation. This provides the
portable correctness path for NumPy, JAX, PyTorch, and other supported
backends. Array portability alone does not promise backend compilation,
automatic differentiation, sparse support, or random-number semantics.
Sparse acceleration is an optional second tier. SciPy sparse operators retain
cached factorization for NumPy state. Installing op-engine[cupy] registers the
matching cupyx.scipy.sparse adapter; CuPy is never imported when it is not
installed. Each adapter detects only its own sparse objects. Backends without a
registered sparse implementation can use dense operators without engine changes.
For long JAX fixed explicit solves, use the compiled loop option to avoid unrolling every internal step during JIT tracing. Adaptive discovery and frozen-mesh replay have separate iteration contracts.