The batch (bioreactor) simulator#
A “golden batch” is the run where everything went right, and batch automation
commonly responds by replaying its charging recipe and setpoint schedules for
every future batch. Replaying is open-loop control: it assumes the
disturbances present during the golden run will recur, and they do not. The
process_improve.simulation.batch module exists to make the
consequences of that assumption measurable, with numbers that reproduce
exactly from a seed:
Replaying a golden schedule does not reproduce the golden outcome.
The spread persists even when the measured initial conditions are held identical, because disturbances also arise while the batch runs.
The quality variance under replay therefore splits into a share that adapting the schedule before the batch could address, a share observable only during the batch (the case for mid-course correction), and a noise floor.
Because the simulator’s own kinetics are known, the true optimal schedule for any batch’s initial conditions is computable. That gives later, data-driven schemes (latent-variable models, trajectory optimisation, mid-course correction) a ceiling to be scored against, which no historical dataset can offer.
The process model#
A 10-day fed-batch bioreactor, sampled twice daily: biomass, substrate,
product (titer) and volume, with pH and temperature as the manipulated
schedule and dissolved oxygen, offgas CO2 and volume as recorded responses.
Growth follows the gamma-concept cardinal temperature and pH model of Rosso
et al. (1995); production follows Luedeking and Piret (1959). Three couplings
give the schedule a real, condition-dependent optimum: an oxygen-transfer
ceiling with hypoxic death, strong hypothermic growth arrest (the reason the
industrial biphasic temperature shift works), and substrate consumption by
production with starvation death. The full equations, parameter meanings and
citations are in the module docstring
(process_improve.simulation.batch).
Three disturbance channels can each be scaled or switched off independently:
measured initial conditions (an 11-variable upstream Z block with three
feed classes A, B and C), an unmeasured within-batch disturbance that is
visible in the gas trajectories, and control-loop plus measurement noise at
instrument scale. All random draws are made on every call and multiplied by
their channel scale, so the same seed with one channel switched off is a true
counterfactual for the same batch.
Realism: the sensitivity budget#
A simulator whose quality output moves when an input moves by less than an instrument can resolve is not believable. The nominal schedule therefore sits at stationary points of the response (the pH optimum and the interior optimum of the production-phase temperature hold), so instrument-scale deviations are second order, while sustained multi-degree deviations cross real mechanisms and cost real titer. Measured on the default configuration (nominal titer 8.01 g/L, 200 noise replicates, seed 0):
Perturbation |
Effect on titer |
|---|---|
Control-loop noise, sd 0.15 degC and 0.02 pH |
sd 0.25% |
Sustained bias of +0.1 / -0.1 degC |
-0.22% / -0.20% |
Sustained bias of +0.02 / -0.02 pH |
-0.01% |
One 12-hour sample at +0.5 degC |
-0.30% |
Sustained bias of +1.0 / -1.0 degC |
-11.5% / -7.1% |
Sustained bias of +2.0 / -2.0 degC |
-20.6% / -22.5% |
These numbers are enforced as acceptance tests in
tests/test_simulation_batch.py, and
sensitivity_budget()
recomputes the table from the live configuration, so the claim can be checked
against any parameter changes rather than taken on trust:
from process_improve.simulation import BioreactorSimulator
sim = BioreactorSimulator()
print(sim.sensitivity_budget(random_state=0))
The three claims, in code#
Replaying the schedule does not reproduce the outcome:
campaign = sim.simulate_campaign(50, policy="replay", random_state=0)
titer = campaign.quality["titer"]
print(titer.mean(), titer.std(ddof=1)) # a spread of roughly 13% CV
Holding the measured initial conditions identical does not remove the spread.
dataclasses.replace derives a configuration with the initial-condition
channel off; what remains (close to 7% CV, about thirty times the noise
floor) arose during the batches:
import dataclasses
from process_improve.simulation import BioreactorConfig
same_z = BioreactorSimulator(
dataclasses.replace(BioreactorConfig(), ic_scale=0.0, noise_scale=0.0)
)
same_z_campaign = same_z.simulate_campaign(50, policy="replay", random_state=0)
And the decomposition, which runs the four campaigns and reports the interaction residual of the nonlinear model explicitly instead of forcing the buckets to sum:
from process_improve.simulation import variance_decomposition
print(variance_decomposition(sim, n_batches=200, random_state=0))
The golden batch and the adaptation ceiling#
golden_trajectory()
finds the true optimal schedule for the nominal initial conditions by
optimising over the simulator’s own kinetics; it recovers the industrial
biphasic shape (a warm growth phase near 37.5 degC, a smooth downshift into a
production hold near 29 degC) without being told it. On the defaults it
reaches 8.54 g/L against 8.01 g/L for the built-in nominal recipe.
optimal_trajectory()
does the same for any batch’s own initial conditions. The gap between
replaying the golden schedule and each batch’s own optimum is the value a
perfect feedforward adaptation could recover; on the default configuration it
ranges from about 3% at the centre of the good feed class to over 40% at the
centre of the poorest.
The "adapted" campaign policy runs every batch at its own optimum, as
that ceiling, and the "historical" policy adds deliberate setpoint
variation, since a perfectly consistent history carries no information about
how the controls affect quality.
golden = sim.golden_trajectory()
adapted = sim.optimal_trajectory(campaign.initial_conditions.iloc[0])
Campaign outputs use the package’s standard batch dictionary format
(dict[batch_id, DataFrame]), so they feed directly into
process_improve.batch.data_input.dict_to_wide() and the alignment
tooling for latent-variable modelling.
Agent tools#
Two registered tools expose the baseline to agent callers:
simulate_batch_campaign (campaign outcomes with the disturbance-free
reference titer for comparison) and decompose_batch_quality_variance (the
variance split). The golden_batch_baseline analysis recipe chains them
into the three-step argument above.
Note
The accompanying book, Process Improvement using Data, develops the golden-batch discussion and the latent-variable methods this baseline is built to support.