systems.builtin.stochastic.discrete.DiscreteStochasticBatchReactor

systems.builtin.stochastic.discrete.DiscreteStochasticBatchReactor(
    *args,
    **kwargs,
)

Discrete-time stochastic batch reactor for digital control and estimation.

Implements a sampled-data model of the chemical batch reactor with process noise, suitable for discrete-time control algorithms, Kalman filtering, and reinforcement learning applications.

Discrete-Time Stochastic Dynamics

Difference equation (Euler discretization):

C_A[k+1] = C_A[k] - r₁(X[k])·Δt + w_A[k]
C_B[k+1] = C_B[k] + (r₁(X[k]) - r₂(X[k]))·Δt + w_B[k]
T[k+1] = T[k] + (Q[k] - α·(T[k] - T_amb))·Δt + w_T[k]

where: - X[k] = [C_A[k], C_B[k], T[k]]: State at time step k - r₁, r₂: Reaction rates (Arrhenius kinetics) - w[k] = [w_A[k], w_B[k], w_T[k]]: Process noise - w[k] ~ N(0, diag(σ_A², σ_B², σ_T²)) - Δt: Sampling period

Physical Interpretation

Discrete-Time Operation:

Industrial batch reactors operate with digital control: - Sensors sampled every Δt seconds (PLC scan rate) - Control computed and applied at discrete intervals - Zero-order hold: Q[k] constant during [k·Δt, (k+1)·Δt] - Measurements: y[k] available at t = k·Δt

Process Noise Sources:

w[k] represents accumulated disturbances over sampling interval: 1. Concentration noise: Feed variations, mixing imperfections 2. Temperature noise: Heat transfer fluctuations, ambient changes 3. Model uncertainty: Discretization errors, parameter drift

Noise Scaling from Continuous:

If continuous noise intensity is σ_c [state/√time]: σ_discrete = σ_c·√Δt [state]

Example: σ_T = 1 K/√s continuous, Δt = 1 s → σ_T_discrete = 1·√1 = 1 K per time step

State Space

State: X[k] = [C_A[k], C_B[k], T[k]] Discrete-time samples of concentrations and temperature: - C_A[k]: Concentration of A at time k·Δt [mol/L] - C_B[k]: Concentration of B at time k·Δt [mol/L] - T[k]: Temperature at time k·Δt [K]

Control: u[k] = Q[k] - Q[k]: Heating rate during interval [k·Δt, (k+1)·Δt] [K/s] - Zero-order hold: Constant between samples

Noise: w[k] = [w_A[k], w_B[k], w_T[k]] - Gaussian white noise: w[k] ~ N(0, Σ_w) - Independent over time: w[k] ⊥ w[j] for k ≠ j - Diagonal covariance: Σ_w = diag(σ_A², σ_B², σ_T²)

Key Properties

Markov Property: X[k+1] depends only on X[k], enabling: - Dynamic programming - Kalman filtering - Reinforcement learning

Time-Invariant: Dynamics f same at all time steps (constant parameters).

Gaussian Noise: Enables analytical Kalman filter (optimal for Gaussian).

Additive Noise: Simplifies estimation (linearity in noise).

Discrete-Time Native: Exact for digital control (no continuous-time approximation).

Parameters

Name Type Description Default
k1 float Pre-exponential factor for A→B reaction [1/s] required
k2 float Pre-exponential factor for B→C reaction [1/s] required
E1 float Activation energy for reaction 1 [K] required
E2 float Activation energy for reaction 2 [K] required
alpha float Heat transfer coefficient [1/s] required
T_amb float Ambient temperature [K] required
C_A0 Optional[float] Initial concentration of A for equilibrium setup [mol/L] required
T0 Optional[float] Initial temperature for equilibrium setup [K] required
sigma_A float Process noise std dev for C_A [mol/L per step] - Typical: 0.001-0.1 mol/L - Conversion from continuous: σ_c·√Δt 0.01
sigma_B float Process noise std dev for C_B [mol/L per step] 0.01
sigma_T float Process noise std dev for T [K per step] - Typical: 0.1-5.0 K - Conversion from continuous: σ_c·√Δt 1.0
dt float Sampling period [s] - Typical: 0.1-10 s for batch reactors - Smaller dt → more accurate, more computation - Must satisfy Nyquist: dt < 1/(2·f_max) 1.0
discretization_method str Discretization method for continuous dynamics - ‘euler’: Forward Euler (first-order) - ‘rk4’: Runge-Kutta 4 (fourth-order) - ‘exact’: Exact for linear parts 'euler'

Applications

1. Discrete-Time MPC: Model Predictive Control with constraints: - Optimization horizon: N steps - Constraints: X_min ≤ X[k] ≤ X_max - Receding horizon: Solve at each k

2. Discrete Kalman Filter: Optimal state estimation: - Process model: This discrete stochastic system - Measurement model: y[k] = h(X[k]) + v[k] - Extended or Unscented KF for nonlinearity

3. LQG Control: Linear-Quadratic-Gaussian: - Kalman filter for state estimation - LQR for optimal control - Certainty equivalence principle

4. Reinforcement Learning: Discrete-time RL algorithms: - Q-learning: Discrete state/action - Policy gradient: Episodic updates - Model-based RL: Use this as forward model

5. Batch-to-Batch Optimization: Iterative learning control: - Update policy based on previous batches - Stochastic gradient descent - Safe learning with constraints

Numerical Simulation

Direct Simulation: No integration needed - direct evaluation: for k in range(N): X[k+1] = f(X[k], u[k]) + w[k]

where w[k] ~ N(0, Σ_w).

Monte Carlo Ensemble: Run multiple simulations to characterize stochasticity: - Mean trajectory - Variance growth - Confidence intervals

Deterministic Part: Set σ_A = σ_B = σ_T = 0 to recover deterministic discrete system.

Comparison with Continuous Stochastic

Continuous Stochastic: - SDE: dX = f·dt + g·dW - Requires SDE integration - Theoretical foundation - Noise intensity in [state]/√[time]

Discrete Stochastic: - Difference equation: X[k+1] = f + w[k] - Direct evaluation (no integration) - Implementation ready - Noise variance in [state]²

Conversion: σ_discrete = σ_continuous·√Δt

Discrete Kalman Filter Usage

This system provides process model for EKF:

# Process model
X_pred = reactor.step(X_est, u[k])
F = reactor.linearize(X_est, u[k])[0]  # Jacobian
P_pred = F @ P @ F.T + Q

# Measurement update (if measurement available)
y_pred = h(X_pred)
H = jacobian(h, X_pred)
K = P_pred @ H.T @ inv(H @ P_pred @ H.T + R)
X_est = X_pred + K @ (y[k] - y_pred)
P = (I - K @ H) @ P_pred

Limitations

  • Euler discretization: O(Δt) error
  • Additive noise only (not multiplicative)
  • Constant noise variance (not state-dependent)
  • Independent noise (no correlation)
  • White noise (no temporal correlation)

Extensions

  • Higher-order discretization (RK4)
  • Multiplicative noise: w[k] depends on X[k]
  • Colored noise: w[k] correlated over time
  • Measurement delays: y[k] = h(X[k-d]) + v[k]
  • Time-varying parameters: θ[k+1] = θ[k] + ε[k]

Methods

Name Description
convert_from_continuous_noise Convert continuous noise intensities to discrete.
define_system Define discrete-time stochastic batch reactor dynamics.
estimate_discretization_error Estimate discretization error (Euler vs exact).
get_process_noise_covariance Get process noise covariance matrix Σ_w.
setup_equilibria Set up equilibrium points (for deterministic part).

convert_from_continuous_noise

systems.builtin.stochastic.discrete.DiscreteStochasticBatchReactor.convert_from_continuous_noise(
    sigma_A_continuous,
    sigma_B_continuous,
    sigma_T_continuous,
)

Convert continuous noise intensities to discrete.

For continuous noise σ_c [state/√s], discrete noise is: σ_d = σ_c·√dt

Parameters

Name Type Description Default
sigma_A_continuous float Continuous noise intensity for C_A [mol/(L·√s)] required
sigma_B_continuous float Continuous noise intensity for C_B [mol/(L·√s)] required
sigma_T_continuous float Continuous noise intensity for T [K/√s] required

Returns

Name Type Description
dict Discrete noise parameters

Examples

>>> reactor = DiscreteStochasticBatchReactor(dt=1.0)
>>> discrete_noise = reactor.convert_from_continuous_noise(
...     sigma_A_continuous=0.01,
...     sigma_B_continuous=0.01,
...     sigma_T_continuous=1.0
... )
>>> print(f"Discrete σ_A: {discrete_noise['sigma_A']:.4f}")
>>> print(f"Discrete σ_T: {discrete_noise['sigma_T']:.4f}")

define_system

systems.builtin.stochastic.discrete.DiscreteStochasticBatchReactor.define_system(
    k1_val=0.5,
    k2_val=0.3,
    E1_val=1000.0,
    E2_val=1500.0,
    alpha_val=0.1,
    T_amb_val=300.0,
    sigma_A=0.01,
    sigma_B=0.01,
    sigma_T=1.0,
    dt=1.0,
    discretization_method='euler',
    C_A0=None,
    T0=None,
)

Define discrete-time stochastic batch reactor dynamics.

Parameters

Name Type Description Default
k1_val float Pre-exponential factor for A→B reaction [1/s] 0.5
k2_val float Pre-exponential factor for B→C reaction [1/s] 0.3
E1_val float Activation energy for reaction 1 [K] 1000.0
E2_val float Activation energy for reaction 2 [K] 1500.0
alpha_val float Heat transfer coefficient [1/s] 0.1
T_amb_val float Ambient temperature [K] 300.0
sigma_A float Process noise std dev for C_A [mol/L per step] - For conversion from continuous: σ_d = σ_c·√dt - Typical: 0.001-0.1 mol/L 0.01
sigma_B float Process noise std dev for C_B [mol/L per step] 0.01
sigma_T float Process noise std dev for T [K per step] - For conversion from continuous: σ_d = σ_c·√dt - Typical: 0.1-5.0 K 1.0
dt float Sampling period [s] - Typical: 0.1-10 s for batch reactors - Rule: dt < τ_system/10 - Affects both dynamics and noise 1.0
discretization_method str Discretization method: - ‘euler’: Forward Euler (simple, first-order) - ‘rk4’: Runge-Kutta 4 (accurate, fourth-order) 'euler'
C_A0 Optional[float] Initial conditions for equilibrium setup None
T0 Optional[float] Initial conditions for equilibrium setup None

Notes

Discretization (Euler):

Continuous: dx/dt = f(x, u) Discrete: x[k+1] = x[k] + f(x[k], u[k])·dt

For reactions: C_A[k+1] = C_A[k] - r₁·dt C_B[k+1] = C_B[k] + (r₁ - r₂)·dt T[k+1] = T[k] + (Q - α·(T - T_amb))·dt

Process Noise:

Additive Gaussian white noise: w[k] ~ N(0, Σ_w) Σ_w = diag(σ_A², σ_B², σ_T²)

Noise Scaling:

From continuous σ_c [state/√s]: σ_discrete = σ_c·√dt

Example: σ_T_c = 1 K/√s, dt = 1 s → σ_T_d = 1·√1 = 1 K per step

Example: σ_T_c = 1 K/√s, dt = 0.1 s → σ_T_d = 1·√0.1 ≈ 0.316 K per step

Sampling Period Selection:

Guidelines: 1. Nyquist: dt < 1/(2·f_max) 2. Control: dt < τ_cl/10 (closed-loop time constant) 3. Reactor: dt < τ_reaction/5

For typical batch reactor: - Fast control: dt = 0.1-0.5 s - Moderate: dt = 1.0-5.0 s - Slow: dt = 5.0-10.0 s

Discretization Method:

Euler (default): - Simple, explicit - O(dt) accuracy - May need small dt for accuracy - Most common in industrial control

RK4 (optional): - More accurate: O(dt⁴) - Can use larger dt - More computation per step - Better for simulation studies

Validation:

Check discretization accuracy: 1. Compare with continuous simulation 2. Verify mass conservation (approximately) 3. Check final concentrations converge as dt → 0

estimate_discretization_error

systems.builtin.stochastic.discrete.DiscreteStochasticBatchReactor.estimate_discretization_error(
    x,
    u,
)

Estimate discretization error (Euler vs exact).

Returns approximate relative error in state update.

Parameters

Name Type Description Default
x np.ndarray Current state required
u np.ndarray Control input required

Returns

Name Type Description
float Estimated relative error (dimensionless)

Notes

Euler discretization has O(dt) error. Error estimate: ||f(x)||·dt²/||x||

Large error → Consider smaller dt or higher-order method.

Examples

>>> reactor = DiscreteStochasticBatchReactor(dt=1.0)
>>> x = np.array([0.5, 0.3, 360.0])
>>> u = np.array([10.0])
>>> error = reactor.estimate_discretization_error(x, u)
>>> print(f"Estimated error: {error:.2e}")

get_process_noise_covariance

systems.builtin.stochastic.discrete.DiscreteStochasticBatchReactor.get_process_noise_covariance(
)

Get process noise covariance matrix Σ_w.

Returns

Name Type Description
np.ndarray 3x3 diagonal covariance matrix

Notes

Σ_w = diag(σ_A², σ_B², σ_T²)

Diagonal → independent noise sources.

Examples

>>> reactor = DiscreteStochasticBatchReactor(
...     sigma_A=0.01, sigma_B=0.01, sigma_T=1.0
... )
>>> Q = reactor.get_process_noise_covariance()
>>> print(f"Process noise covariance:\n{Q}")

setup_equilibria

systems.builtin.stochastic.discrete.DiscreteStochasticBatchReactor.setup_equilibria(
)

Set up equilibrium points (for deterministic part).

Note: In discrete time, equilibria are fixed points of f: x_eq = f(x_eq, u_eq)