systems.builtin.deterministic.continuous.ContinuousBatchReactor
systems.builtin.deterministic.continuous.ContinuousBatchReactor(*args, **kwargs)Continuous-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 continuous
time with differential equations governing the evolution of concentrations
and temperature.
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 = [Cₐ, Cᵦ, T]
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 = [Q]
- 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 = [Cₐ, Cᵦ, T]
- 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 continuous-time dynamics are:
dCₐ/dt = -r₁
dCᵦ/dt = r₁ - r₂
dT/dt = Q - α·(T - Tₐₘᵦ)
**Reaction Rates (Arrhenius kinetics)**:
r₁ = k₁·Cₐ·exp(-E₁/T) [mol/(L·s)]
r₂ = k₂·Cᵦ·exp(-E₂/T) [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: 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
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 asymptotically as t → ∞ 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(t) 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:
1. **Temperature tracking**: Maintain T(t) ≈ T_ref(t)
- Maximize reaction rate
- Ensure safety (prevent runaway)
- PID/LQR/MPC controllers typical
2. **Yield optimization**: Maximize Cᵦ at final time
- Requires optimal temperature trajectory
- May involve heating → cooling profile
- Calculus of variations or optimal control
3. **Batch time minimization**: Reach Cₐ < ε in minimum time
- Subject to temperature constraints (T_min ≤ T ≤ T_max)
- Bang-bang control often optimal
4. **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:
1. **Non-negativity**: Cₐ(t) ≥ 0, Cᵦ(t) ≥ 0
- Concentrations cannot be negative
- Physical meaning: species present or absent
2. **Conservation**: Cₐ(t) + Cᵦ(t) + Cᶜ(t) = Cₐ(0)
- Total moles conserved (if C tracked)
- Useful for validation
3. **Temperature limits**: T_min ≤ T(t) ≤ T_max
- Safety: prevent runaway or solidification
- Typical: 280 K ≤ T ≤ 450 K
4. **Actuation limits**: Q_min ≤ Q(t) ≤ Q_max
- Physical heating/cooling capacity
- Typical: -50 ≤ Q ≤ 50 K/s
Numerical Integration:
**Stiffness**: This system can be **moderately stiff** due to:
- Exponential temperature dependence (Arrhenius)
- Different time scales (fast reactions at high T, slow cooling)
- Stiffness ratio ≈ exp((E₂ - E₁)/T)
**Recommended Solvers**:
- **Moderate stiffness**: RK45 (adaptive Runge-Kutta)
- **High stiffness**: Radau, BDF (implicit methods)
- **High accuracy**: Vern7, Vern9 (Julia DiffEq)
- **GPU acceleration**: JAX with diffrax
**Tolerance Selection**:
- Standard: rtol=1e-6, atol=1e-8
- High accuracy: rtol=1e-9, atol=1e-11
- Tighter tolerances needed for optimization
Example Usage:
>>> # Create reactor with default parameters
>>> reactor = ContinuousBatchReactor()
>>>
>>> # Initial condition: fresh batch
>>> x0 = np.array([1.0, 0.0, 350.0]) # [Cₐ, Cᵦ, T]
>>>
>>> # Simulate with constant heating
>>> def controller(x, t):
... return np.array([10.0]) # Constant Q = 10 K/s
>>>
>>> result = reactor.simulate(
... x0=x0,
... controller=controller,
... t_span=(0, 100),
... dt=0.1
... )
>>>
>>> # Plot concentration profiles
>>> import matplotlib.pyplot as plt
>>> plt.plot(result['time'], result['states'][:, 0], label='Cₐ')
>>> plt.plot(result['time'], result['states'][:, 1], label='Cᵦ')
>>> plt.xlabel('Time (s)')
>>> 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)
>>>
>>> A, B = 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(A, B, Q_lqr, R_lqr,
... system_type='continuous')
>>> K = lqr_result['gain']
>>>
>>> # Simulate with LQR control
>>> def lqr_controller(x, t):
... return -K @ (x - x_ref) + u_ref
>>>
>>> result_lqr = reactor.simulate(x0, lqr_controller, t_span=(0, 100), dt=0.1)
>>>
>>> # Or use integrate() for adaptive time stepping
>>> result_adaptive = reactor.integrate(
... x0=x0,
... u=lqr_controller,
... t_span=(0, 100),
... method='Radau', # Stiff solver
... rtol=1e-8,
... atol=1e-10
... )
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
**Comparison with Discrete Version**:
This continuous-time model is the "ground truth" that discrete
systems approximate:
- Discrete system: Uses Euler/RK4 discretization with fixed dt
- Continuous system: Adaptive time stepping, arbitrary accuracy
- Use discretize() method to create discrete version:
reactor_discrete = reactor.discretize(dt=1.0, method='rk4')See Also:
DiscreteBatchReactor : Discrete-time version of this system
ContinuousCSTR : Continuous stirred-tank reactor (continuous flow)
Lorenz : Another nonlinear continuous system with multiple equilibria
VanDerPolOscillator : Continuous nonlinear oscillator with limit cycle
Notes
**Stiffness Detection**: If integration is slow or fails, try:
1. Check condition number of Jacobian
2. Use stiff solver (Radau, BDF)
3. Reduce temperature range
4. Use Julia backend for better stiff solvers
**Optimal Control**: For batch optimization:
1. Define cost functional: J = -Cᵦ(t_f) + ∫(Q²/R)dt
2. Solve using Pontryagin's maximum principle
3. Or use direct methods (collocation, multiple shooting)
4. Result: Bang-bang or singular arc control
**Parameter Estimation**: If fitting to data:
1. Minimize ||data - model(θ)||²
2. Use scipy.optimize.minimize with integrate()
3. May need to estimate k₁, k₂, E₁, E₂, α
4. Ensure identifiability (different θ → different output)
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 continuous-time batch reactor dynamics. |
| setup_equilibria | Set up equilibrium points for the batch reactor. |
calculate_steady_heating
systems.builtin.deterministic.continuous.ContinuousBatchReactor.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.continuous.ContinuousBatchReactor.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 = ContinuousBatchReactor()
>>> 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.continuous.ContinuousBatchReactor.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 = ContinuousBatchReactor()
>>> 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.continuous.ContinuousBatchReactor.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 = ContinuousBatchReactor()
>>> 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.continuous.ContinuousBatchReactor.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,
C_A0=None,
T0=None,
)Define symbolic continuous-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 |
| 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.continuous.ContinuousBatchReactor.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