Skip to content

Commit

Permalink
Merge pull request #44 from biosustain/develop
Browse files Browse the repository at this point in the history
Added tutorial for handling evaporation and updated GH actions
  • Loading branch information
viktorht authored Nov 27, 2024
2 parents cc1444a + b7d4c09 commit ae4ffb7
Show file tree
Hide file tree
Showing 12 changed files with 3,856 additions and 6 deletions.
15 changes: 13 additions & 2 deletions .github/workflows/documentation.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
name: Docs
on: [push, pull_request, workflow_dispatch]
on:
push:
branches: [main]
pull_request:
branches: [main]
types: [closed]
# only when docs files have been modified
path: ['docs/**']
permissions:
contents: write

Expand All @@ -10,6 +17,11 @@ env:
jobs:
docs:
runs-on: ubuntu-latest
# Checks that pull request was merged
if: >
github.event_name == 'push' ||
(github.event_name == 'pull_request' &&
github.event.pull_request.merged == true)
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
Expand Down Expand Up @@ -38,7 +50,6 @@ jobs:
cd ..
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
with:
publish_branch: gh-pages
github_token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ on:
push:
branches:
- 'main'
- 'develop'
tags:
- '**'
paths:
- '**.py'
- '**.stan'
pull_request:
workflow_dispatch: {}

Expand Down
1,016 changes: 1,016 additions & 0 deletions article/data/evaporation.csv

Large diffs are not rendered by default.

1,016 changes: 1,016 additions & 0 deletions article/data/fed-batch_evaporation.csv

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions article/simulation_scripts/fed-batch_evaporation.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# This file setups, and runs the ODE systems for the standard exponential fedbatch
# process simulation. Finally, some simple data processing is carried out to format
# the data into a self containing .csv file.

# This simulation simulates a evaporation of water from the bioreactor at a fixed rate.

using OrdinaryDiffEq, DiffEqCallbacks, CSV, DataFrames
include("fermentation_simulator_functions.jl")
include("fermentation_utils_functions.jl")
include("standard_parameters.jl")


# Setting up ODE input
evap_rate = 1 # uL/h Constant evaporation rate
state_variable_names = [:m_Glucose, :m_Biomass, :m_Product, :m_CO2, :v_Volume, :v_Feed_accum, :m_CO2_gas]
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
sample_volume_dict = Dict(zip(sampling_times, repeat([sample_volume], n_samples))) # defines timepoints and sample volumes
ode_input_p = [Kc_s, mu_max, Yxs, Yxp, Yxco2, F0, mu0, s_f, evap_rate]

ode_func = ODEFunction(fedbatch_evaporation!, syms=state_variable_names)
prob = ODEProblem(ode_func, init_cond, tspan, ode_input_p)
cb = PresetTimeCallback(sampling_times, affect_sample!, filter_tstops = true) # creates new sampling callback
sol = solve(prob, Tsit5(), callback=cb, saveat=save_ode_timesteps, abstol = 1e-12) # solve ode system with new sampling callback

# Processing simulated data
df = DataFrame(sol)
for state_variable in state_variable_names
# It is not meaningfull to calculate the concentration of the feed_accum or volume
if state_variable in [:v_Feed_accum, :v_Volume, :m_CO2_gas]
continue
end
metabolite = split(string(state_variable), "_")[2]
new_colname = string("c_", metabolite)
df[!, new_colname] = df[!, state_variable] ./ df[!, :v_Volume]
end

# adding the true growth rate
transform!(df, [:c_Glucose] => ByRow((c_s) -> monod_kinetics(c_s, mu_max, Kc_s)) => :mu_true)

# adding sampling information
insertcols!(df, 1, "sample_volume" => NaN)
df[[x in sampling_times for x in df.timestamp], "sample_volume"] .= sample_volume

# adding the parameter values to the dataframe
output_header = [:Kc_s, :mu_max, :Yxs, :Yxp, :Yxco2, :F0, :mu0, :s_f, :evap_rate]
insertcols!(df, 1, (output_header .=> ode_input_p)...)

CSV.write(string("data/evaporation.csv"), df)

36 changes: 36 additions & 0 deletions article/simulation_scripts/fermentation_simulator_functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,42 @@ function fedbatch_prod_inhib_volatile!(dudt, u, p, t)
return dudt
end

"""
Defines the ODE system for a fed-batch fermentation operated with an exponential
feed profile. The growth of the microorganism is modelled through monods equation.
In this simulation water evaporates at a constant rate from the reactor.
The system holds 7 state variables with are defined as follows:
dudt[1] : mass of substrate in the liquid
dudt[2] : mass of biomass in the liquid
dudt[3] : mass of product in the liquid
dudt[4] : mass of co2 in liquid
dudt[5] : volume of liquid
dudt[6] : feeding rate
dudt[7] : mass of co2 in off-gas
The systems is build under the assumption that the CO2 evaporates instantly
after production.
"""
function fedbatch_evaporation!(dudt, u, p, t)
Kc_s, mu_max, Yxs, Yxp, Yxco2, F0, mu0, s_f, evap_rate = p

# Growth kinetics consider moving this as a seperate function
c_s = u[1]/u[5]
mu = monod_kinetics(c_s, mu_max, Kc_s)

dudt[1] = -Yxs * mu * u[2] + v_feed(t, F0, mu0) * s_f
dudt[2] = mu * u[2]
dudt[3] = Yxp * mu * u[2]
dudt[4] = Yxco2 * mu * u[2] - Yxco2 * mu * u[2] # co2 production - co2 evaporation
dudt[5] = v_feed(t, F0, mu0) - evap_rate # volume
dudt[6] = v_feed(t, F0, mu0) # feed used to integrate feed_accum
dudt[7] = Yxco2 * mu * u[2] # all co2 evaporates immidately into off-gas analyzer


return dudt
end

############################# EVENT HANDLING / CALLBACKS #####################
"""
Calculates the amount of mass removed when a give volume is sampled from the
Expand Down
632 changes: 632 additions & 0 deletions docs/source/Tutorials/10 - Special case evaporation.ipynb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/source/Tutorials/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ Here you will find tutorials for how to use the pseudobatch transformation.
7 - Pseudobatch transformation with uncertainties
8 - Special case gaseous species
9 - Special case non-feed additions
10 - Special case evaporation
7 changes: 4 additions & 3 deletions pseudobatch/datasets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from ._dataloaders import (
load_standard_fedbatch,
load_product_inhibited_fedbatch,
load_standard_fedbatch,
load_product_inhibited_fedbatch,
load_cho_cell_like_fedbatch,
load_real_world_yeast_fedbatch,
load_volatile_compounds_fedbatch,
)
load_evaporation_fedbatch,
)
16 changes: 16 additions & 0 deletions pseudobatch/datasets/_dataloaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,20 @@ def load_volatile_compounds_fedbatch(sampling_points_only: bool = False):
If True, only the rows where a sample was taken is kept, by default False
"""
data_path = pathlib.Path(__file__).parent / "data" / "volatile_product.csv"
return _prepare_simulated_dataset(data_path, sampling_points_only=sampling_points_only)


def load_evaporation_fedbatch(sampling_points_only: bool = False):
"""Load the evaporation fed-batch process dataset. This dataset
mimicks a substrate limited exponential fed-batch process utilizing
a glucose feed. During the fed-batch process, samples are withdrawn.
The parameters values use for the simulation is stored in the
dataframe.
Parameters
----------
sampling_points_only : bool, optional
If True, only the rows where a sample was taken is kept, by default False
"""
data_path = pathlib.Path(__file__).parent / "data" / "evaporation.csv"
return _prepare_simulated_dataset(data_path, sampling_points_only=sampling_points_only)
Loading

0 comments on commit ae4ffb7

Please sign in to comment.