archimedes.discretize¶
- archimedes.discretize(func=None, dt=None, method='rk4', n_steps=1, name=None, **options)¶
Convert continuous-time dynamics to discrete-time using numerical integration.
Transforms a continuous-time ordinary differential equation (ODE) function into a discrete-time difference equation.
The function implements numerical integration schemes while maintaining automatic differentiation compatibility for gradient-based optimization, extended Kalman filtering, etc. The resulting discrete-time function preserves the same calling convention as the original continuous-time dynamics.
Given a continuous-time system:
dx/dt = ode(t, x, u, params)
this function returns a discrete-time approximation:
x[k+1] = f(t[k], x[k], u[k], params)
where the discrete-time function
f
integrates the continuous dynamics over the time intervaldt
using the specified numerical method.Can also be used as a decorator to convert a continuous-time function into a discrete-time function:
@discretize(dt=0.1, method="rk4") def dyn(t, x, u, params): # ... continuous-time dynamics implementation
- Parameters:
func (callable or FunctionCache) –
Continuous-time dynamics function with signature
func(t, x, u, params)
:t
: Current time (scalar)x
: State vector of shape(nx,)
u
: Input vector of shape(nu,)
params
: Parameters (any PyTree structure)
Must return the time derivative
dx/dt
as an array of shape(nx,)
. The function can be a regular Python function or a pre-compiledFunctionCache
object.dt (float) – Sampling time step for discretization. Represents the time interval between discrete samples
t[k+1] - t[k]
. Smaller values provide better accuracy but increase computational cost.method (str, optional) –
Numerical integration method. Default is
"rk4"
. Available methods:"rk4"
: Fourth-order Runge-Kutta method. Explicit, O(h⁴) accuracy. Excellent balance of accuracy and computational efficiency for most systems. Recommended for well-behaved, non-stiff dynamics."radau5"
: Fifth-order Radau IIA implicit method. Implicit, A-stable with excellent stability properties. Suitable for stiff systems and when high accuracy is required. Involves solving nonlinear equations at each step using Newton’s method.
n_steps (int, optional) – Number of integration sub-steps within each sampling interval
dt
. Default is 1. Whenn_steps > 1
, the integration step size becomesh = dt / n_steps
, improving accuracy for rapid dynamics or large sampling intervals. Useful for maintaining accuracy whendt
is constrained by measurement availability rather than dynamics.name (str, optional) – Name for the resulting discrete-time function. If None, automatically generated as
"{method}_{func.name}"
. Used for debugging and performance profiling of compiled functions.**options –
Additional method-specific options:
- For
method="radau5"
: newton_solver
: str, default"fast_newton"
Newton solver for implicit equations. Other options inclue"kinsol"
and"newton"
.
- For
- Returns:
discrete_func – Discrete-time dynamics function with signature
discrete_func(t, x, u, params)
that returns the next statex[k+1]
given current statex[k]
. The function is automatically compiled for efficient evaluation and supports automatic differentiation for gradient computation.- Return type:
FunctionCache
Notes
Method Selection Guide:
- RK4 Method (
method="rk4"
): Best for: Non-stiff systems, general-purpose discretization
Advantages: Fast, explicit, well-tested, excellent accuracy/cost ratio
Limitations: Can become unstable for stiff systems or large time steps
Typical use: Mechanical systems, mild nonlinearities, most applications
- Radau5 Method (
method="radau5"
): Best for: Stiff systems, high accuracy requirements, DAE systems
Advantages: A-stable, handles stiff dynamics, high-order accuracy
Limitations: Computational overhead from implicit solve
Typical use: Chemical kinetics, electrical circuits, multiscale dynamics
Accuracy Considerations:
The discretization error depends on both the method order and the time step:
RK4: Error ∼ O(dt⁴), typically accurate for
dt < T/10
whereT
is the fastest system time constantRadau5: Error ∼ O(dt⁵), maintains accuracy even for moderate
dt
Sub-stepping: Use
n_steps > 1
when measurement rate limitsdt
but dynamics require smaller integration steps
Automatic Differentiation:
The discretized function preserves automatic differentiation through the integration process, enabling efficient gradient computation for:
Parameter estimation via
pem()
Sensitivity analysis and uncertainty quantification
Gradient-based optimal control
Performance Optimization:
The returned function is automatically compiled using CasADi’s just-in-time compilation for efficient repeated evaluation. For system identification applications involving hundreds of function evaluations, this provides significant performance benefits over pure Python implementations.
Examples
Basic Discretization:
>>> import numpy as np >>> import archimedes as arc >>> >>> # Define continuous-time harmonic oscillator >>> def oscillator(t, x, u, params): ... omega = params["omega"] # Natural frequency ... return np.hstack([ ... x[1], # dx1/dt = x2 (velocity) ... -omega**2 * x[0] + u[0] # dx2/dt = -ω²x1 + u (acceleration) ... ]) >>> >>> # Discretize with 50ms sampling >>> dt = 0.05 >>> oscillator_discrete = arc.discretize(oscillator, dt, method="rk4") >>> >>> # Simulate one step >>> t0 = 0.0 >>> x0 = np.array([1.0, 0.0]) # Initial position and velocity >>> u0 = np.array([0.0]) # No input >>> params = {"omega": 2.0} # 2 rad/s natural frequency >>> >>> x1 = oscillator_discrete(t0, x0, u0, params) >>> print(f"Next state: {x1}") Next state: [0.995004 -0.199334]
Stiff System with Radau5:
>>> # Stiff Van der Pol oscillator >>> def van_der_pol(t, x, u, params): ... mu = params["mu"] # Large damping parameter ... return np.hstack([ ... x[1], ... mu * (1 - x[0]**2) * x[1] - x[0] ... ]) >>> >>> # Use implicit method for stiff dynamics >>> dt = 0.1 # Larger time step acceptable with Radau5 >>> vdp_discrete = arc.discretize( ... van_der_pol, dt, ... method="radau5", ... newton_solver="fast_newton" ... ) >>> >>> params_stiff = {"mu": 10.0} # Stiff parameter >>> x0 = np.array([2.0, 0.0]) >>> x1 = vdp_discrete(0.0, x0, np.array([0.0]), params_stiff)
See also
archimedes.sysid.pem
Parameter estimation using discrete-time models
odeint
Continuous-time integration for comparison and validation
References