systems.builtin.deterministic.discrete.DiscreteBatchReactor
systems.builtin.deterministic.discrete.DiscreteBatchReactor(*args, **kwargs)Discrete-time chemical batch reactor with temperature control.
Physical System:
A well-mixed batch reactor where chemical species A converts to B, which then converts to product C. The reactor operates in discrete time steps (sampling intervals) typical of: - Digital control systems with periodic measurements - Batch processing with staged operations - Industrial reactors with discrete valve/heater actuation
The reaction sequence is: A → B → C
Both reactions are first-order and temperature-dependent following Arrhenius kinetics. Temperature affects reaction rates exponentially, creating strong nonlinear coupling between composition and thermal dynamics.
State Space:
State: x[k] = [Cₐ[k], Cᵦ[k], T[k]] Concentration states: - Cₐ: Concentration of reactant A [mol/L] * Initial concentration typically Cₐ[0] = 1.0 mol/L * Decreases monotonically (consumed by first reaction) * Must remain non-negative: Cₐ ≥ 0
- Cᵦ: Concentration of intermediate B [mol/L]
* Produced from A, consumed to form C
* Non-monotonic: rises then falls
* Maximum occurs when r₁ = r₂ (production = consumption)
* Must remain non-negative: Cᵦ ≥ 0
Temperature state:
- T: Reactor temperature [K]
* Typical range: 300-400 K (27-127°C)
* Affects reaction rates exponentially via Arrhenius
* Subject to heat loss to ambient (cooling)
* Controlled by external heating Q
Control: u[k] = [Q[k]] - Q: Heating/cooling rate [K/s] * Q > 0: Heating applied * Q < 0: Active cooling * Q = 0: Natural heat loss only * Typical range: -50 to +50 K/s
Output: y[k] = [Cₐ[k], Cᵦ[k], T[k]] - Full state measurement (all concentrations and temperature) - In practice, concentration may be measured via: * Spectroscopy (UV-Vis, IR) * Chromatography (GC, HPLC) * Online analyzers - Temperature measured via thermocouple or RTD
Dynamics:
The discrete-time dynamics use Euler discretization:
Cₐ[k+1] = Cₐ[k] - dt·r₁[k]
Cᵦ[k+1] = Cᵦ[k] + dt·(r₁[k] - r₂[k])
T[k+1] = T[k] + dt·(Q[k] - α·(T[k] - Tₐₘᵦ))
Reaction Rates (Arrhenius kinetics): r₁[k] = k₁·Cₐ[k]·exp(-E₁/T[k]) [mol/(L·s)] r₂[k] = k₂·Cᵦ[k]·exp(-E₂/T[k]) [mol/(L·s)]
where: - k₁, k₂: Pre-exponential factors (frequency factors) - E₁, E₂: Activation energies [K] (using Eₐ/R as temperature) - exp(-E/T): Arrhenius temperature dependence
Physical Interpretation:
Reaction 1 (A → B): - Rate r₁ proportional to Cₐ (first-order kinetics) - Exponentially increases with temperature - Higher E₁ → more temperature sensitive - Depletes reactant A, produces intermediate B
Reaction 2 (B → C): - Rate r₂ proportional to Cᵦ (first-order kinetics) - Exponentially increases with temperature - Higher E₂ → more temperature sensitive - Consumes intermediate B, produces final product C
Temperature dynamics: - Q[k]: External heating/cooling control - -α·(T - Tₐₘᵦ): Heat loss to ambient (Newton’s cooling) - α: Heat transfer coefficient [1/s] - Tₐₘᵦ: Ambient temperature [K]
Nonlinear Coupling: The system exhibits strong nonlinear coupling: 1. Temperature affects reaction rates exponentially 2. Reactions may be exothermic/endothermic (not modeled here) 3. Competing reactions create non-monotonic Cᵦ profile
Parameters:
k1 : float, default=0.5 Pre-exponential factor for reaction 1 (A→B) [1/s] Higher k₁ → faster depletion of A Typical range: 0.1 - 10.0
k2 : float, default=0.3 Pre-exponential factor for reaction 2 (B→C) [1/s] Higher k₂ → faster conversion of B to C Typical range: 0.1 - 10.0
E1 : float, default=1000.0 Activation energy for reaction 1 [K] (actually Eₐ/R) Higher E₁ → more sensitive to temperature Physical Eₐ typically 8,000 - 30,000 K
E2 : float, default=1500.0 Activation energy for reaction 2 [K] (actually Eₐ/R) E₂ > E₁ means reaction 2 is more temperature-sensitive Creates selectivity control via temperature
alpha : float, default=0.1 Heat transfer coefficient [1/s] Characterizes cooling rate to ambient Higher α → faster heat loss, harder to maintain temperature
T_amb : float, default=300.0 Ambient temperature [K] (27°C) System equilibrium temperature with Q = 0
dt : float, default=1.0 Sampling/discretization time step [s] Critical parameter affecting stability: - Too large → numerical instability - Too small → slow simulation, control system bandwidth Typical: 0.1 - 10.0 seconds
Equilibria:
Steady-state (complete conversion): x_eq = [0, 0, Tₐₘᵦ] (all reactants consumed, cooled to ambient) u_eq = 0 (no heating needed)
This equilibrium is reached after sufficient batch time when: - All A has converted to B: Cₐ → 0 - All B has converted to C: Cᵦ → 0 - Temperature equilibrates with ambient: T → Tₐₘᵦ
This is a stable equilibrium (globally attracting).
Optimal operating point (maximum B yield): If goal is to maximize Cᵦ at a specific time, equilibrium concept doesn’t apply. Instead, use optimal control to find temperature trajectory Q[k] that maximizes Cᵦ at final time.
Temperature setpoint equilibrium (partial reaction): For constant T* > Tₐₘᵦ maintained by control: - Requires Q_eq = α·(T* - Tₐₘᵦ) to balance heat loss - Concentrations evolve according to reaction kinetics at T* - Not a true equilibrium (Cₐ, Cᵦ still changing)
Control Objectives:
Common control goals for batch reactors:
- Temperature tracking: Maintain T[k] ≈ T_ref[k]
- Maximize reaction rate
- Ensure safety (prevent runaway)
- LQR/MPC controllers typical
- Yield optimization: Maximize Cᵦ at final time
- Requires optimal temperature trajectory
- May involve heating → cooling profile
- Dynamic programming or direct optimization
- Batch time minimization: Reach Cₐ < ε in minimum time
- Subject to temperature constraints (T_min ≤ T ≤ T_max)
- Bang-bang control often optimal
- Selectivity control: Maximize ratio Cᵦ/Cᶜ
- Exploit different activation energies (E₁ vs E₂)
- Intermediate temperature maximizes B
State Constraints:
Physical constraints that must be enforced:
- Non-negativity: Cₐ[k] ≥ 0, Cᵦ[k] ≥ 0
- Concentrations cannot be negative
- Euler discretization may violate if dt too large
- Conservation: Cₐ[k] + Cᵦ[k] + Cᶜ[k] = Cₐ[0]
- Total moles conserved (if C tracked)
- Useful for validation
- Temperature limits: T_min ≤ T[k] ≤ T_max
- Safety: prevent runaway or solidification
- Typical: 280 K ≤ T ≤ 450 K
- Actuation limits: Q_min ≤ Q[k] ≤ Q_max
- Physical heating/cooling capacity
- Typical: -50 ≤ Q ≤ 50 K/s
Numerical Considerations:
Stability: The explicit Euler discretization is stable if: dt < 1/λ_max
where λ_max is the maximum eigenvalue of the Jacobian.
For this system, linearizing around typical operating points: λ_max ≈ max(k₁·exp(-E₁/T), k₂·exp(-E₂/T), α)
At high temperature, reaction rates can be very fast, requiring small dt for stability. Rule of thumb: dt < 0.1 / max(k₁·exp(-E₁/T), k₂·exp(-E₂/T))
Accuracy: Higher-order methods (RK4, etc.) can be used: system_continuous = ContinuousBatchReactor(…) system_discrete = system_continuous.discretize(dt=1.0, method=‘rk4’)
Stiffness: If E₁ ≫ E₂ or vice versa, system may be stiff, requiring implicit methods or very small dt.
Example Usage:
Create reactor with default parameters
reactor = DiscreteBatchReactor(dt=0.5)
Initial condition: fresh batch
x0 = np.array([1.0, 0.0, 350.0]) # [Cₐ, Cᵦ, T]
Simulate with constant heating
result = reactor.simulate( … x0=x0, … u_sequence=np.array([10.0]), # Constant Q = 10 K/s … n_steps=100 … )
Plot concentration profiles
import matplotlib.pyplot as plt plt.plot(result[‘time_steps’], result[‘states’][:, 0], label=‘Cₐ’) plt.plot(result[‘time_steps’], result[‘states’][:, 1], label=‘Cᵦ’) plt.xlabel(‘Time step’) plt.ylabel(‘Concentration [mol/L]’) plt.legend()
Design LQR temperature controller
T_ref = 360.0 # Reference temperature x_ref = np.array([0.5, 0.3, T_ref]) u_ref = reactor._calculate_steady_heating(T_ref)
Ad, Bd = reactor.linearize(x_ref, u_ref) Q_lqr = np.diag([0, 0, 100]) # Only care about temperature R_lqr = np.array([[1.0]]) lqr_result = reactor.control.design_lqr(Ad, Bd, Q_lqr, R_lqr, … system_type=‘discrete’) K = lqr_result[‘gain’]
Simulate with LQR control
def lqr_controller(x, k): … return -K @ (x - x_ref) + u_ref
result_lqr = reactor.rollout(x0, lqr_controller, n_steps=100)
Physical Insights:
Reaction Selectivity: Since E₂ > E₁ (default), reaction 2 is more temperature-sensitive. This means: - Low T: Slow r₂, Cᵦ accumulates (favors intermediate) - High T: Fast r₂, Cᵦ depletes quickly (favors product)
Temperature Control Strategy: To maximize Cᵦ yield: 1. Heat initially to accelerate reaction 1 (produce B) 2. Cool before reaction 2 becomes too fast (preserve B) 3. Optimal trajectory: heating → plateau → cooling
Batch Time vs. Yield Tradeoff: - High temperature: Fast reactions, short batch time, but may overshoot optimal Cᵦ (too much conversion to C) - Low temperature: Slow reactions, long batch time, but can maintain high Cᵦ for longer - Economic optimum balances these factors
Safety Considerations: - Exothermic reactions (not modeled) can cause thermal runaway - High temperature reduces selectivity, may form byproducts - Emergency cooling (Q < 0) must be available - Temperature constraints critical for safe operation
See Also:
ContinuousBatchReactor : Continuous-time version of this system DiscreteCSTR : Continuous stirred-tank reactor (continuous flow) LogisticMap : Simpler discrete nonlinear dynamics DiscretePendulum : Another discrete nonlinear control problem
Methods
| Name | Description |
|---|---|
| calculate_steady_heating | Calculate steady-state heating required to maintain temperature setpoint. |
| compute_conversion | Compute fractional conversion of reactant A. |
| compute_selectivity | Compute selectivity to intermediate B. |
| compute_yield | Compute yield of intermediate B. |
| define_system | Define symbolic discrete-time batch reactor dynamics. |
| setup_equilibria | Set up equilibrium points for the batch reactor. |
calculate_steady_heating
systems.builtin.deterministic.discrete.DiscreteBatchReactor.calculate_steady_heating(
T_setpoint,
)Calculate steady-state heating required to maintain temperature setpoint.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| T_setpoint | float | Desired reactor temperature [K] | required |
Returns
| Name | Type | Description |
|---|---|---|
| float | Required heating rate Q [K/s] |
Notes
At steady state (constant T), heat input must balance heat loss: Q = α·(T - T_amb)
compute_conversion
systems.builtin.deterministic.discrete.DiscreteBatchReactor.compute_conversion(
C_A,
C_A0,
)Compute fractional conversion of reactant A.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| C_A | float | Current concentration of A [mol/L] | required |
| C_A0 | float | Initial concentration of A [mol/L] | required |
Returns
| Name | Type | Description |
|---|---|---|
| float | Conversion fraction X_A (0 = no conversion, 1 = complete) |
Examples
>>> reactor = DiscreteBatchReactor()
>>> X = reactor.compute_conversion(C_A=0.3, C_A0=1.0)
>>> print(f"Conversion: {X*100:.1f}%")
Conversion: 70.0%compute_selectivity
systems.builtin.deterministic.discrete.DiscreteBatchReactor.compute_selectivity(
C_B,
C_A,
C_A0,
)Compute selectivity to intermediate B.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| C_B | float | Current concentration of B [mol/L] | required |
| C_A | float | Current concentration of A [mol/L] | required |
| C_A0 | float | Initial concentration of A [mol/L] | required |
Returns
| Name | Type | Description |
|---|---|---|
| float | Selectivity S_B = C_B / (C_A0 - C_A) (moles B per mole A converted) |
Notes
Selectivity measures how much intermediate B is produced per mole of A consumed. Values: - S_B = 1.0: Perfect selectivity (all A → B, no B → C yet) - S_B < 1.0: Some B has already converted to C - S_B → 0: Most B has converted to C (over-reacted)
Examples
>>> reactor = DiscreteBatchReactor()
>>> S = reactor.compute_selectivity(C_B=0.5, C_A=0.3, C_A0=1.0)
>>> print(f"Selectivity: {S:.2f} mol B / mol A converted")compute_yield
systems.builtin.deterministic.discrete.DiscreteBatchReactor.compute_yield(
C_B,
C_A0,
)Compute yield of intermediate B.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| C_B | float | Current concentration of B [mol/L] | required |
| C_A0 | float | Initial concentration of A [mol/L] | required |
Returns
| Name | Type | Description |
|---|---|---|
| float | Yield Y_B = C_B / C_A0 (moles B per initial mole A) |
Notes
Yield is the most important metric for batch optimization. Combines both conversion and selectivity: Y_B = X_A · S_B
Examples
>>> reactor = DiscreteBatchReactor()
>>> Y = reactor.compute_yield(C_B=0.4, C_A0=1.0)
>>> print(f"Yield: {Y*100:.1f}%")
Yield: 40.0%define_system
systems.builtin.deterministic.discrete.DiscreteBatchReactor.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,
dt=1.0,
C_A0=None,
T0=None,
)Define symbolic discrete-time 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 |
| dt | float | Discretization time step [s] | 1.0 |
| C_A0 | Optional[float] | Initial concentration of A for equilibrium setup [mol/L] | None |
| T0 | Optional[float] | Initial temperature for equilibrium setup [K] | None |
setup_equilibria
systems.builtin.deterministic.discrete.DiscreteBatchReactor.setup_equilibria()Set up equilibrium points for the batch reactor.
Adds two equilibria: 1. ‘complete’: Complete conversion (Cₐ=0, Cᵦ=0, T=Tₐₘᵦ) 2. ‘initial’: Optional initial state if C_A0 and T0 specified