Skip to content

Commit 90c3eda

Browse files
committed
Validate that the method work when volume evaporates
Setup a simulation where volume evaporated from the bioreactor at a constant rate. This data set was added to the datasets module and validated in a evaporation notebook.
1 parent cc1444a commit 90c3eda

File tree

8 files changed

+3565
-3
lines changed

8 files changed

+3565
-3
lines changed

article/data/evaporation.csv

Lines changed: 1016 additions & 0 deletions
Large diffs are not rendered by default.

article/data/fed-batch_evaporation.csv

Lines changed: 1016 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# This file setups, and runs the ODE systems for the standard exponential fedbatch
2+
# process simulation. Finally, some simple data processing is carried out to format
3+
# the data into a self containing .csv file.
4+
5+
# This simulation simulates a evaporation of water from the bioreactor at a fixed rate.
6+
7+
using OrdinaryDiffEq, DiffEqCallbacks, CSV, DataFrames
8+
include("fermentation_simulator_functions.jl")
9+
include("fermentation_utils_functions.jl")
10+
include("standard_parameters.jl")
11+
12+
13+
# Setting up ODE input
14+
evap_rate = 1 # uL/h Constant evaporation rate
15+
state_variable_names = [:m_Glucose, :m_Biomass, :m_Product, :m_CO2, :v_Volume, :v_Feed_accum, :m_CO2_gas]
16+
init_cond = [s0*V0, x0*V0, p0*V0, co2_0*V0, V0, 0., 0.] # input for the model is masses not concentrations, accum feed at time zero is 0
17+
sample_volume_dict = Dict(zip(sampling_times, repeat([sample_volume], n_samples))) # defines timepoints and sample volumes
18+
ode_input_p = [Kc_s, mu_max, Yxs, Yxp, Yxco2, F0, mu0, s_f, evap_rate]
19+
20+
ode_func = ODEFunction(fedbatch_evaporation!, syms=state_variable_names)
21+
prob = ODEProblem(ode_func, init_cond, tspan, ode_input_p)
22+
cb = PresetTimeCallback(sampling_times, affect_sample!, filter_tstops = true) # creates new sampling callback
23+
sol = solve(prob, Tsit5(), callback=cb, saveat=save_ode_timesteps, abstol = 1e-12) # solve ode system with new sampling callback
24+
25+
# Processing simulated data
26+
df = DataFrame(sol)
27+
for state_variable in state_variable_names
28+
# It is not meaningfull to calculate the concentration of the feed_accum or volume
29+
if state_variable in [:v_Feed_accum, :v_Volume, :m_CO2_gas]
30+
continue
31+
end
32+
metabolite = split(string(state_variable), "_")[2]
33+
new_colname = string("c_", metabolite)
34+
df[!, new_colname] = df[!, state_variable] ./ df[!, :v_Volume]
35+
end
36+
37+
# adding the true growth rate
38+
transform!(df, [:c_Glucose] => ByRow((c_s) -> monod_kinetics(c_s, mu_max, Kc_s)) => :mu_true)
39+
40+
# adding sampling information
41+
insertcols!(df, 1, "sample_volume" => NaN)
42+
df[[x in sampling_times for x in df.timestamp], "sample_volume"] .= sample_volume
43+
44+
# adding the parameter values to the dataframe
45+
output_header = [:Kc_s, :mu_max, :Yxs, :Yxp, :Yxco2, :F0, :mu0, :s_f, :evap_rate]
46+
insertcols!(df, 1, (output_header .=> ode_input_p)...)
47+
48+
CSV.write(string("data/evaporation.csv"), df)
49+

article/simulation_scripts/fermentation_simulator_functions.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,42 @@ function fedbatch_prod_inhib_volatile!(dudt, u, p, t)
248248
return dudt
249249
end
250250

251+
"""
252+
Defines the ODE system for a fed-batch fermentation operated with an exponential
253+
feed profile. The growth of the microorganism is modelled through monods equation.
254+
In this simulation water evaporates at a constant rate from the reactor.
255+
256+
The system holds 7 state variables with are defined as follows:
257+
dudt[1] : mass of substrate in the liquid
258+
dudt[2] : mass of biomass in the liquid
259+
dudt[3] : mass of product in the liquid
260+
dudt[4] : mass of co2 in liquid
261+
dudt[5] : volume of liquid
262+
dudt[6] : feeding rate
263+
dudt[7] : mass of co2 in off-gas
264+
265+
The systems is build under the assumption that the CO2 evaporates instantly
266+
after production.
267+
"""
268+
function fedbatch_evaporation!(dudt, u, p, t)
269+
Kc_s, mu_max, Yxs, Yxp, Yxco2, F0, mu0, s_f, evap_rate = p
270+
271+
# Growth kinetics consider moving this as a seperate function
272+
c_s = u[1]/u[5]
273+
mu = monod_kinetics(c_s, mu_max, Kc_s)
274+
275+
dudt[1] = -Yxs * mu * u[2] + v_feed(t, F0, mu0) * s_f
276+
dudt[2] = mu * u[2]
277+
dudt[3] = Yxp * mu * u[2]
278+
dudt[4] = Yxco2 * mu * u[2] - Yxco2 * mu * u[2] # co2 production - co2 evaporation
279+
dudt[5] = v_feed(t, F0, mu0) - evap_rate # volume
280+
dudt[6] = v_feed(t, F0, mu0) # feed used to integrate feed_accum
281+
dudt[7] = Yxco2 * mu * u[2] # all co2 evaporates immidately into off-gas analyzer
282+
283+
284+
return dudt
285+
end
286+
251287
############################# EVENT HANDLING / CALLBACKS #####################
252288
"""
253289
Calculates the amount of mass removed when a give volume is sampled from the

docs/source/Tutorials/10 - Special case evaporation.ipynb

Lines changed: 412 additions & 0 deletions
Large diffs are not rendered by default.

pseudobatch/datasets/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from ._dataloaders import (
2-
load_standard_fedbatch,
3-
load_product_inhibited_fedbatch,
2+
load_standard_fedbatch,
3+
load_product_inhibited_fedbatch,
44
load_cho_cell_like_fedbatch,
55
load_real_world_yeast_fedbatch,
66
load_volatile_compounds_fedbatch,
7-
)
7+
load_evaporation_fedbatch,
8+
)

pseudobatch/datasets/_dataloaders.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,20 @@ def load_volatile_compounds_fedbatch(sampling_points_only: bool = False):
107107
If True, only the rows where a sample was taken is kept, by default False
108108
"""
109109
data_path = pathlib.Path(__file__).parent / "data" / "volatile_product.csv"
110+
return _prepare_simulated_dataset(data_path, sampling_points_only=sampling_points_only)
111+
112+
113+
def load_evaporation_fedbatch(sampling_points_only: bool = False):
114+
"""Load the evaporation fed-batch process dataset. This dataset
115+
mimicks a substrate limited exponential fed-batch process utilizing
116+
a glucose feed. During the fed-batch process, samples are withdrawn.
117+
The parameters values use for the simulation is stored in the
118+
dataframe.
119+
120+
Parameters
121+
----------
122+
sampling_points_only : bool, optional
123+
If True, only the rows where a sample was taken is kept, by default False
124+
"""
125+
data_path = pathlib.Path(__file__).parent / "data" / "evaporation.csv"
110126
return _prepare_simulated_dataset(data_path, sampling_points_only=sampling_points_only)

0 commit comments

Comments
 (0)