{
"cells": [
{
"cell_type": "markdown",
"id": "8d891055",
"metadata": {},
"source": [
"# Anaerobic Digestion Model No. 1 (ADM1) \n",
"\n",
"*Click the badge below to try this tutorial interactively in your browser:*\n",
"\n",
"[](https://mybinder.org/v2/gh/QSD-Group/QSDsan-env/main?urlpath=git-pull%3Frepo%3Dhttps%253A%252F%252Fgithub.com%252FQSD-group%252FQSDsan%26urlpath%3Dlab%252Ftree%252FQSDsan%252Fdocs%252Fsource%252Ftutorials%26branch%3Dmain)\n",
"\n",
"*You can also run this tutorial in [Google Colab](https://colab.research.google.com). It takes a one-time setup per session: follow the [Colab instructions](https://qsdsan.readthedocs.io/en/latest/tutorials/index.html#run-in-colab).*\n",
"\n",
"- **Prepared by:**\n",
"\n",
" - [Ga-Yeong Kim](https://github.com/GaYeongKim)\n",
"\n",
"- **Learning objectives.** After this tutorial, you will be able to:\n",
"\n",
" - Apply the ADM1 biokinetic model to anaerobic digestion\n",
" - Set up a multi-component dynamic system\n",
" - Analyze how operating conditions drive the digester response\n",
"\n",
"- **Prerequisites:** [11. Dynamic Simulation](https://qsdsan.readthedocs.io/en/latest/tutorials/11_Dynamic_Simulation.html)\n",
"\n",
"- **Covered topics:**\n",
"\n",
" - 1. Introduction\n",
" - 2. System setup\n",
" - 3. Result discussion\n"
]
},
{
"cell_type": "markdown",
"id": "2f0ecf42bf56",
"metadata": {},
"source": [
"\n",
"\n",
"## Setup\n",
"\n",
"Import `QSDsan` and confirm the installed version. An ADM1 example system is also packaged ready-to-run in the `adm` module of [EXPOsan](https://github.com/QSD-Group/EXPOsan); this notebook builds an equivalent system from scratch so each piece is visible."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "9a2a96b7",
"metadata": {
"execution": {
"iopub.execute_input": "2026-05-28T19:08:52.706293Z",
"iopub.status.busy": "2026-05-28T19:08:52.706293Z",
"iopub.status.idle": "2026-05-28T19:09:09.148354Z",
"shell.execute_reply": "2026-05-28T19:09:09.147906Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"This tutorial was made with qsdsan v1.5.3\n"
]
}
],
"source": [
"import qsdsan as qs\n",
"print(f'This tutorial was made with qsdsan v{qs.__version__}')"
]
},
{
"cell_type": "markdown",
"id": "1bbdffaa",
"metadata": {},
"source": [
"## 1. Introduction "
]
},
{
"cell_type": "markdown",
"id": "t12-s11",
"metadata": {},
"source": [
"In Tutorials 10 and 11, we have learned about the foundamentals of process modeling. As a practical example, this tutorial uses **ADM1**, the International Water Association (IWA)-validated benchmark for anaerobic digestion, as a substantial worked example that uses both at once.\n",
"\n",
"- [Tutorial 10](https://qsdsan.readthedocs.io/en/latest/tutorials/10_Process.html) covered how a kinetic model is represented in QSDsan: the `Process` and `Processes` classes that hold stoichiometry and rate equations, and the Petersen matrix that tabulates a whole process model. In this tutorial, we will use `ADM1`, which is a pre-constructed and ready-to-use `CompiledProcesses` object for ADM1 in QSDsan.\n",
"\n",
"- [Tutorial 11](https://qsdsan.readthedocs.io/en/latest/tutorials/11_Dynamic_Simulation.html) covered how a dynamic simulation is set up and run: the `AnaerobicCSTR` / `CompleteMixTank` family of dynamic units, the `_state` / `_dstate` methods, the integrator loop, and `set_dynamic_tracker` for following state evolution. In this tutorial, we will use `AnaerobicCSTR`, a dynamic `SanUnit`. We will also construct a dynamic `System` that contains `AnaerobicCSTR` for simulation."
]
},
{
"cell_type": "markdown",
"id": "t12-s12-head",
"metadata": {},
"source": [
"### 1.1. What ADM1 models"
]
},
{
"cell_type": "markdown",
"id": "t12-s12-body",
"metadata": {},
"source": [
"ADM1 describes the biochemical and physico-chemical processes for anaerobic digestion (AD).\n",
"\n",
"The **biochemical steps** are organized as a cascade:\n",
"\n",
"- **Disintegration** of homogeneous particulates (`X_c`) into carbohydrates, proteins, and lipids.\n",
"- **Hydrolysis** of those particulates to sugars (`S_su`), amino acids (`S_aa`), and long-chain fatty acids (LCFA, `S_fa`).\n",
"- **Acidogenesis** of sugars and amino acids to volatile fatty acids (VFAs), acetate, and hydrogen.\n",
"- **Acetogenesis** of LCFA and the longer VFAs (valerate, butyrate, propionate) to acetate and hydrogen.\n",
"- **Methanogenesis** in two parallel paths: acetoclastic (`S_ac → S_ch4`) and hydrogenotrophic (`S_h2 → S_ch4`).\n",
"\n",
"Seven biomass groups catalyse these steps and decay back into the composite pool.\n",
"\n",
"The **physico-chemical equations** cover acid/base equilibria (which set the pH) and gas-liquid transfer for H₂, CH₄, and CO₂."
]
},
{
"cell_type": "markdown",
"id": "t12-s12-fig",
"metadata": {},
"source": [
"\n",
"
"
]
},
{
"cell_type": "markdown",
"id": "t12-s12-cap",
"metadata": {},
"source": [
"*The four canonical AD steps as represented in ADM1. Each band corresponds to one step; the biomass group labelled on each arrow catalyses that conversion.*\n",
"\n",
"Reference: Batstone, D. J., et al. (2002). The IWA Anaerobic Digestion Model No. 1 (ADM1). *Water Science and Technology*, 45(10), 65–73. https://doi.org/10.2166/wst.2002.0292"
]
},
{
"cell_type": "markdown",
"id": "t12-s13-head",
"metadata": {},
"source": [
"### 1.2. Differential-algebraic structure"
]
},
{
"cell_type": "markdown",
"id": "t12-s13-body",
"metadata": {},
"source": [
"Implemented as a **differential-algebraic (DAE) set**, ADM1 has 26 dynamic state variables in the liquid phase and 8 implicit algebraic variables in the headspace (the partial pressures of H₂, CH₄, CO₂, and H₂O, plus the ion balance that sets pH). Implemented as pure ODEs, there are 32 dynamic variables. The figure below shows the DAE shape that `AnaerobicCSTR` evaluates at each integrator step."
]
},
{
"cell_type": "markdown",
"id": "t12-s13-fig",
"metadata": {},
"source": [
"
\n",
"
"
]
},
{
"cell_type": "markdown",
"id": "t12-s13-cap",
"metadata": {},
"source": [
"*Each integrator step evaluates the liquid-phase ODE block, the headspace algebraic block, and the gas-liquid transfer terms (`k_L·a · (S_aq − K_H · p)`) that link them. Liquid effluent and biogas leave through outflow arrows; the biomass-decay loop (not drawn here) returns active biomass to `X_c` in the ODE block.*\n",
"\n",
"
| \n", " | S_su | \n", "S_aa | \n", "S_fa | \n", "S_va | \n", "S_bu | \n", "... | \n", "X_h2 | \n", "X_I | \n", "S_cat | \n", "S_an | \n", "H2O | \n", "
|---|---|---|---|---|---|---|---|---|---|---|---|
| disintegration | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0.2 | \n", "0 | \n", "0 | \n", "0 | \n", "
| hydrolysis_carbs | \n", "1 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| hydrolysis_proteins | \n", "0 | \n", "1 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| hydrolysis_lipids | \n", "0.05 | \n", "0 | \n", "0.95 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| uptake_sugars | \n", "-1 | \n", "0 | \n", "0 | \n", "0 | \n", "0.117 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| uptake_amino_acids | \n", "0 | \n", "-1 | \n", "0 | \n", "0.212 | \n", "0.239 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| uptake_LCFA | \n", "0 | \n", "0 | \n", "-1 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| uptake_valerate | \n", "0 | \n", "0 | \n", "0 | \n", "-1 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| uptake_butyrate | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "-1 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| uptake_propionate | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| uptake_acetate | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| uptake_h2 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0.06 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| decay_Xsu | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| decay_Xaa | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| decay_Xfa | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| decay_Xc4 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| decay_Xpro | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| decay_Xac | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| decay_Xh2 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "-1 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| h2_transfer | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| ch4_transfer | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
| IC_transfer | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "
22 rows × 27 columns
\n", "