A mechanistic (ODE-based) model of Bacillus cereus growth, sporulation, germination, and thermal mortality in food, used to calibrate kinetic parameters against experimental growth/inactivation data.
Built as part of a thesis project studying predictive microbiology under dynamic temperature, pH, and water activity conditions.
The core of the project is a 4-state ODE system that tracks a bacterial population through the full life cycle relevant to food safety:
| State | Meaning |
|---|---|
S |
Substrate concentration |
X |
Vegetative cell concentration |
Xsp |
Spore concentration |
Dv |
Cumulative thermal damage |
dS/dt = -mu_max · γ_growth · Y_sx · X · ρ(S)
dX/dt = mu_max · γ_growth · X · ρ(S) - m·X - sp_max·γ_spor·X + g_max·γ_germ·Xsp
dXsp/dt = m·X·γ_spor + sp_max·γ_spor·X - msp·Xsp - g_max·γ_germ·Xsp
dDv/dt = step(T>Tmax) · exp(-av·(T - Tz))
Growth, sporulation, and germination rates are each scaled by a γ (cardinal) term — the product of temperature, pH, and water-activity effects (Rosso 1995/2001 cardinal parameter models), each normalized to [0, 1]. Thermal mortality uses the Béchet (2017) model driven by cumulative damage Dv.
Parameters are calibrated against experimental datasets using a two-stage optimizer: a global differential evolution search followed by local least-squares refinement, with residuals computed in log10 space.
RG-Model/
├── main.py # Runs the ODE model with a fixed cooking temperature profile and plots results
├── functions.py # Cardinal models (temperature/pH/aw), mortality models, substrate kinetics
├── Bcereus_meta.py # Default B. cereus parameter set (growth, spore, mortality constants)
├── plots.py # Plotting helpers (biomass, spores, thermal zones)
├── randomizer.py # Splits datasets into calibration/validation sets
├── data_v1.csv # Primary experimental dataset
├── CONTEXT.md # Detailed technical notes on the ODE system and calibration pipeline
│
├── calib9/, calib10/ # Calibration run scripts, inputs, and results (most recent iterations)
├── calib_mortality/ # Mortality-specific calibration experiments
├── mortality/ # Standalone mortality model fitting (Mafart, Arrhenius-Bigelow)
├── thesis_results/ # Final analysis scripts and figures used in the thesis
├── lunarc_calib910_analysis/ # HPC (LUNARC cluster) batch analysis scripts
├── parsing_combase/ # Scripts + raw exports for parsing the ComBase database
└── old/ # Archive of earlier model/calibration versions (see note below)
Note on
old/: this folder holds superseded versions of the calibration pipeline (V1–V3, calib1–calib7) kept for traceability. It isn't needed to run the current model and could be moved out of the main repo (see recommendations below).
Requirements: Python 3.10+, with:
numpy
pandas
scipy
matplotlib
(no requirements.txt exists yet — see recommendations below)
Run the base model (fixed cooking-temperature scenario, no calibration):
python main.pyThis solves the ODE system over a 4.5-hour cooking profile (freezer → 60 °C hold → cooling) and produces growth/mortality plots via plots.py.
Run a calibration (example, calib10):
cd calib10
python calib10.pyCalibration scripts load a CSV of experimental time-series, group them by condition, and fit model parameters via differential evolution + least-squares. See CONTEXT.md for the full pipeline description, parameter bounds, and CSV format.
HPC (LUNARC) runs: several folders include run_parallel_calib.sh SLURM scripts for running calibrations on a cluster (conda env nextflow-bioprocess).
Experimental data is expected as CSV with columns including:
Record ID,Temperature (C),Aw,pHLogcs— semicolon-separatedtime;log_countpairsSorter— grouping tag for calibration batchesinit_conditions— e.g.[S0=10e9, X0=3.15, Xsp0=Xsp0, Dv=0]params— which parameters to calibrate for that group
Full column reference is in CONTEXT.md.
- Some historical calibration runs are missing traceability back to their exact input files.
- R² is not consistently computed across all calibration outputs.
- The
old/folder mixes deprecated code with some datasets still referenced by newer scripts — check before deleting anything.
A few changes would make this repo much easier for others (or future-you) to navigate:
- Add a
.gitignore—__pycache__/,*.pyc,.vscode/, and lock files (.~lock.*) are currently tracked. This alone removes a lot of clutter. - Split code from outputs. Result files (
.json, result.png,.csvoutputs) currently live next to source scripts incalib9/,calib10/,calib_mortality/, etc. Consider a top-levelresults/folder, or at least ensure result folders are gitignored and regenerated by running the scripts, not committed. - Consider Git LFS or excluding large binary data.
parsing_combase/combase_exports/alone has ~290 Excel files. If these are raw source data rather than something that changes often, they might live better in a separate data store (or Git LFS) than in the main repo history. - Archive, don't co-mingle, old versions. The
old/folder (V1–V3, calib1–calib8) is useful for traceability but makes the active codebase harder to find. Consider moving it to a separate branch, alegacy/repo, or at minimum flagging in this README (done above) what's current. - One canonical
functions.py/Bcereus_meta.py. These are duplicated across nearly everycalibX/folder. If they've diverged, that's worth knowing; if they haven't, a shared module (or symlinks) would prevent silent drift between calibration runs. - Turn
CONTEXT.md's pipeline description into inline docstrings infunctions.pyand the main calibration script — it's excellent documentation but currently lives one file away from the code it describes. - Replace the informal troubleshooting log (previously in this README) with GitHub Issues — it's easier to search, close, and reference from commits than a running list in the README.