From 51b9cc115945339d830a738d1360bdc55e7d6586 Mon Sep 17 00:00:00 2001 From: Date: Fri, 1 Nov 2024 14:41:06 +0100 Subject: [PATCH] Update documentation --- WS1/intro_to_ws1.html | 34 +- WS1/module_1/0_python_basics.html | 34 +- WS1/module_1/1_hello_world.html | 34 +- WS1/module_1/2_statements.html | 36 +- WS1/module_1/3_control_flow.html | 59 +- WS1/module_1/4_exercise_1.html | 34 +- WS1/module_1/5_exercise_2.html | 34 +- WS1/module_2/0_intro_module_2.html | 34 +- WS1/module_2/1_dictionaries_and_sets.html | 34 +- WS1/module_2/2_dataframes.html | 34 +- WS1/module_2/3_numpy_arrays.html | 34 +- WS1/module_2/4_data_visualizations.html | 34 +- WS1/module_2/5_exercise_1.html | 34 +- WS1/module_3/0_intro_module_3.html | 34 +- WS1/module_3/1_operations.html | 34 +- WS1/module_3/2_linear_regression.html | 34 +- WS1/module_3/3_stoichiometry.html | 34 +- WS1/module_3/4_stoichiometry_solution.html | 34 +- WS1/module_3/5_stoichiometry_pt2.html | 34 +- WS1/module_3/6_exercise_1.html | 34 +- WS1/module_4/0_intro_module_4.html | 34 +- WS1/module_4/1_reactors_in_python.html | 34 +- WS1/module_4/2_combining_balances.html | 34 +- .../3_combining_balances_solution.html | 34 +- WS1/module_4/4_exercise_1.html | 34 +- WS1/module_4/5_exercise_1_solution.html | 34 +- WS1/module_4/6_exercise_2.html | 34 +- WS1/module_4/7_exercise_2_solution.html | 34 +- WS1/module_5/0_FedBatch_Ecoli_simulation.html | 34 +- WS1/module_6/0_intro_module_6.html | 34 +- WS1/module_6/1_Mass_Transfer.html | 34 +- WS1/module_6/2_Diafiltration.html | 34 +- WS1/module_6/3_Cell_lysis.html | 34 +- WS2/intro_to_ws2.html | 34 +- WS2/module_1/0_intro_module_1.html | 42 +- ...ion_Scerevisiae_Lignocellulosic_Batch.html | 943 ++++++++++++++ ...tion_Scerevisiae_Glucose_Aerobic_CSTR.html | 880 +++++++++++++ .../3_Fermentation_MonodHerbert_aerobic.html | 974 +++++++++++++++ ...ch_Ecoli_simulation_proteinproduction.html | 1107 +++++++++++++++++ WS2/module_2/0_intro_module_2.html | 42 +- WS2/module_2/1_dd_classification.html | 340 ++++- WS2/module_2/2_dd_regression copy.html | 871 +++++++++++++ WS2/module_2/2_pytorch_tutorial.html | 973 +++++++++++++++ WS2/module_2/3_dd_regression.html | 866 +++++++++++++ WS2/module_3/0_intro_module_3.html | 52 +- WS2/module_3/1_hyb_regression.html | 658 ++++++++++ ...3a693dee9fee5ed59eabe36a4a1050ef9f6be1.png | Bin 0 -> 102662 bytes ...d3bceeb7c32ba1067229de13fffa4c1f72ec4f.png | Bin 0 -> 77243 bytes ...bd07f7f0757761ac3586ec06946115f54b8e13.png | Bin 0 -> 76719 bytes ...0ac8b642dd82869a1c42778a79bae9f66adad1.png | Bin 0 -> 77282 bytes ...on_Scerevisiae_Lignocellulosic_Batch.ipynb | 415 ++++++ ...ion_Scerevisiae_Glucose_Aerobic_CSTR.ipynb | 343 +++++ .../3_Fermentation_MonodHerbert_aerobic.ipynb | 1 + ...h_Ecoli_simulation_proteinproduction.ipynb | 701 +++++++++++ .../WS2/module_2/1_dd_classification.ipynb | 311 ++++- .../WS2/module_2/2_dd_regression copy.ipynb | 302 +++++ .../WS2/module_2/2_pytorch_tutorial.ipynb | 437 +++++++ _sources/WS2/module_2/3_dd_regression.ipynb | 281 +++++ _sources/WS2/module_3/1_hyb_regression.ipynb | 83 ++ _sources/markdown-notebooks.ipynb | 6 +- genindex.html | 34 +- index.html | 36 +- markdown-notebooks.html | 34 +- mymarkdownfile.html | 42 +- notebooks.html | 34 +- objects.inv | Bin 1265 -> 1542 bytes search.html | 34 +- searchindex.js | 2 +- 68 files changed, 11860 insertions(+), 167 deletions(-) create mode 100755 WS2/module_1/1_Fermentation_Scerevisiae_Lignocellulosic_Batch.html create mode 100755 WS2/module_1/2_Fermentation_Scerevisiae_Glucose_Aerobic_CSTR.html create mode 100755 WS2/module_1/3_Fermentation_MonodHerbert_aerobic.html create mode 100755 WS2/module_1/4_FedBatch_Ecoli_simulation_proteinproduction.html create mode 100755 WS2/module_2/2_dd_regression copy.html create mode 100755 WS2/module_2/2_pytorch_tutorial.html create mode 100755 WS2/module_2/3_dd_regression.html create mode 100755 WS2/module_3/1_hyb_regression.html create mode 100755 _images/1f19ae422dfa4274e376a137363a693dee9fee5ed59eabe36a4a1050ef9f6be1.png create mode 100755 _images/46826d96f91da6c790a0d5bcd9d3bceeb7c32ba1067229de13fffa4c1f72ec4f.png create mode 100755 _images/4fdc3c91e9aaee42296307b21abd07f7f0757761ac3586ec06946115f54b8e13.png create mode 100755 _images/862cab22ba67c511e9b54c9abf0ac8b642dd82869a1c42778a79bae9f66adad1.png create mode 100755 _sources/WS2/module_1/1_Fermentation_Scerevisiae_Lignocellulosic_Batch.ipynb create mode 100755 _sources/WS2/module_1/2_Fermentation_Scerevisiae_Glucose_Aerobic_CSTR.ipynb create mode 100755 _sources/WS2/module_1/3_Fermentation_MonodHerbert_aerobic.ipynb create mode 100755 _sources/WS2/module_1/4_FedBatch_Ecoli_simulation_proteinproduction.ipynb create mode 100755 _sources/WS2/module_2/2_dd_regression copy.ipynb create mode 100755 _sources/WS2/module_2/2_pytorch_tutorial.ipynb create mode 100755 _sources/WS2/module_2/3_dd_regression.ipynb create mode 100755 _sources/WS2/module_3/1_hyb_regression.ipynb diff --git a/WS1/intro_to_ws1.html b/WS1/intro_to_ws1.html index 9930959..5572e87 100755 --- a/WS1/intro_to_ws1.html +++ b/WS1/intro_to_ws1.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_1/0_python_basics.html b/WS1/module_1/0_python_basics.html index 0c21b4f..c5bdb87 100755 --- a/WS1/module_1/0_python_basics.html +++ b/WS1/module_1/0_python_basics.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_1/1_hello_world.html b/WS1/module_1/1_hello_world.html index 56706b2..0c24f50 100755 --- a/WS1/module_1/1_hello_world.html +++ b/WS1/module_1/1_hello_world.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_1/2_statements.html b/WS1/module_1/2_statements.html index 078082a..4090161 100755 --- a/WS1/module_1/2_statements.html +++ b/WS1/module_1/2_statements.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

@@ -784,7 +812,7 @@

3. Lists and generators
- @@ -645,15 +673,18 @@

Repetition -
Row [0.16550853 0.65175605 0.02492288]
-Element 0.16550852761331136
-Element 0.6517560520101003
-Element 0.024922878413327965
-
-Row [0.47226042 0.41175703 0.5538846 ]
-Element 0.472260420990256
-Element 0.4117570284778522
-Element 0.5538846028335506
+
Row
+
+
+
 [0.68736936 0.20126193 0.04658199]
+Element 0.6873693596050051
+Element 0.20126193070467935
+Element 0.04658198840012395
+
+Row [0.57064357 0.57116666 0.75163354]
+Element 0.5706435668382651
+Element 0.5711666629778096
+Element 0.7516335434567888
 
@@ -723,9 +754,7 @@

Continue and Break
8
-
-
-
diff --git a/WS1/module_2/0_intro_module_2.html b/WS1/module_2/0_intro_module_2.html index 1760ffd..812eba9 100755 --- a/WS1/module_2/0_intro_module_2.html +++ b/WS1/module_2/0_intro_module_2.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_2/1_dictionaries_and_sets.html b/WS1/module_2/1_dictionaries_and_sets.html index 08b5904..d50b09e 100755 --- a/WS1/module_2/1_dictionaries_and_sets.html +++ b/WS1/module_2/1_dictionaries_and_sets.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_2/2_dataframes.html b/WS1/module_2/2_dataframes.html index a03a6e2..59fa10c 100755 --- a/WS1/module_2/2_dataframes.html +++ b/WS1/module_2/2_dataframes.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_2/3_numpy_arrays.html b/WS1/module_2/3_numpy_arrays.html index e3de34b..f2f5668 100755 --- a/WS1/module_2/3_numpy_arrays.html +++ b/WS1/module_2/3_numpy_arrays.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_2/4_data_visualizations.html b/WS1/module_2/4_data_visualizations.html index b3d0e78..bad9b88 100755 --- a/WS1/module_2/4_data_visualizations.html +++ b/WS1/module_2/4_data_visualizations.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_2/5_exercise_1.html b/WS1/module_2/5_exercise_1.html index 36c7e5c..35dd7d0 100755 --- a/WS1/module_2/5_exercise_1.html +++ b/WS1/module_2/5_exercise_1.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_3/0_intro_module_3.html b/WS1/module_3/0_intro_module_3.html index 7909fff..8e1cc97 100755 --- a/WS1/module_3/0_intro_module_3.html +++ b/WS1/module_3/0_intro_module_3.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_3/1_operations.html b/WS1/module_3/1_operations.html index fcc8512..f1e8043 100755 --- a/WS1/module_3/1_operations.html +++ b/WS1/module_3/1_operations.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_3/2_linear_regression.html b/WS1/module_3/2_linear_regression.html index d0bf9f6..9c30aee 100755 --- a/WS1/module_3/2_linear_regression.html +++ b/WS1/module_3/2_linear_regression.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_3/3_stoichiometry.html b/WS1/module_3/3_stoichiometry.html index 4d34fb5..e3018a8 100755 --- a/WS1/module_3/3_stoichiometry.html +++ b/WS1/module_3/3_stoichiometry.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_3/4_stoichiometry_solution.html b/WS1/module_3/4_stoichiometry_solution.html index 1dd85bd..15a3f1c 100755 --- a/WS1/module_3/4_stoichiometry_solution.html +++ b/WS1/module_3/4_stoichiometry_solution.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_3/5_stoichiometry_pt2.html b/WS1/module_3/5_stoichiometry_pt2.html index e3b4321..e76d687 100755 --- a/WS1/module_3/5_stoichiometry_pt2.html +++ b/WS1/module_3/5_stoichiometry_pt2.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_3/6_exercise_1.html b/WS1/module_3/6_exercise_1.html index d2d055b..5abe523 100755 --- a/WS1/module_3/6_exercise_1.html +++ b/WS1/module_3/6_exercise_1.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_4/0_intro_module_4.html b/WS1/module_4/0_intro_module_4.html index e2a77fb..9b99796 100755 --- a/WS1/module_4/0_intro_module_4.html +++ b/WS1/module_4/0_intro_module_4.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_4/1_reactors_in_python.html b/WS1/module_4/1_reactors_in_python.html index e98afcd..36a5dd9 100755 --- a/WS1/module_4/1_reactors_in_python.html +++ b/WS1/module_4/1_reactors_in_python.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_4/2_combining_balances.html b/WS1/module_4/2_combining_balances.html index 40f4a2f..31788b1 100755 --- a/WS1/module_4/2_combining_balances.html +++ b/WS1/module_4/2_combining_balances.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_4/3_combining_balances_solution.html b/WS1/module_4/3_combining_balances_solution.html index 39d8348..83392de 100755 --- a/WS1/module_4/3_combining_balances_solution.html +++ b/WS1/module_4/3_combining_balances_solution.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_4/4_exercise_1.html b/WS1/module_4/4_exercise_1.html index 809ed0d..aa9f001 100755 --- a/WS1/module_4/4_exercise_1.html +++ b/WS1/module_4/4_exercise_1.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_4/5_exercise_1_solution.html b/WS1/module_4/5_exercise_1_solution.html index 479fa20..c9d7f2c 100755 --- a/WS1/module_4/5_exercise_1_solution.html +++ b/WS1/module_4/5_exercise_1_solution.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_4/6_exercise_2.html b/WS1/module_4/6_exercise_2.html index 7ab9627..aa48a0e 100755 --- a/WS1/module_4/6_exercise_2.html +++ b/WS1/module_4/6_exercise_2.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_4/7_exercise_2_solution.html b/WS1/module_4/7_exercise_2_solution.html index c875942..7c47708 100755 --- a/WS1/module_4/7_exercise_2_solution.html +++ b/WS1/module_4/7_exercise_2_solution.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_5/0_FedBatch_Ecoli_simulation.html b/WS1/module_5/0_FedBatch_Ecoli_simulation.html index a494b49..1600ddd 100755 --- a/WS1/module_5/0_FedBatch_Ecoli_simulation.html +++ b/WS1/module_5/0_FedBatch_Ecoli_simulation.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_6/0_intro_module_6.html b/WS1/module_6/0_intro_module_6.html index 46eaf71..deea236 100755 --- a/WS1/module_6/0_intro_module_6.html +++ b/WS1/module_6/0_intro_module_6.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_6/1_Mass_Transfer.html b/WS1/module_6/1_Mass_Transfer.html index f32ae8e..65109ec 100755 --- a/WS1/module_6/1_Mass_Transfer.html +++ b/WS1/module_6/1_Mass_Transfer.html @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_6/2_Diafiltration.html b/WS1/module_6/2_Diafiltration.html index 9af3119..8242ba2 100755 --- a/WS1/module_6/2_Diafiltration.html +++ b/WS1/module_6/2_Diafiltration.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS1/module_6/3_Cell_lysis.html b/WS1/module_6/3_Cell_lysis.html index 854e8c7..bc6f838 100755 --- a/WS1/module_6/3_Cell_lysis.html +++ b/WS1/module_6/3_Cell_lysis.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS2/intro_to_ws2.html b/WS2/intro_to_ws2.html index a40d12b..3aeb504 100755 --- a/WS2/intro_to_ws2.html +++ b/WS2/intro_to_ws2.html @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

diff --git a/WS2/module_1/0_intro_module_1.html b/WS2/module_1/0_intro_module_1.html index 635d53d..2493018 100755 --- a/WS2/module_1/0_intro_module_1.html +++ b/WS2/module_1/0_intro_module_1.html @@ -62,7 +62,7 @@ - + @@ -230,13 +230,41 @@

Model-based Techniques (WS2)

@@ -607,6 +635,8 @@

5. Mechanistic Modeling in Biochemical Engineering6. Conclusion#

Mechanistic modeling in chemical and biochemical engineering provides valuable insights into system behavior, allowing engineers to predict performance, design optimal processes, and improve operational efficiency. The use of mathematical models based on fundamental principles makes mechanistic modeling a powerful tool in process engineering.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + +
+

Mechanistic model of S. cerevisiae utilizing ligocellulosis in batch fermentation

+ +
+ +
+
+ + + + +
+ +
+

Mechanistic model of S. cerevisiae utilizing ligocellulosis in batch fermentation#

+

This model describes an S. cerevisiae fermentation in a batch reactor. It builds a stochiometric matrix to follow all conversions. The model accounts for the changes of Carbon sources, the development of biomass and ethonal over time.

+
+
+

Package import#

+

This portion of the code handles the import of all the relevant python packages.

+
+
+
from scipy.integrate import odeint
+# Package for plotting
+import math
+# Package for the use of vectors and matrix
+import numpy as np
+import pandas as pd
+import array as arr
+
+from matplotlib.figure import Figure
+import sys
+import os
+import matplotlib.pyplot as plt
+from matplotlib.ticker import FormatStrFormatter
+import glob
+from random import sample
+import random
+import time
+import plotly
+import plotly.graph_objs as go
+import json
+from plotly.subplots import make_subplots
+
+
+
+
+
---------------------------------------------------------------------------
+ModuleNotFoundError                       Traceback (most recent call last)
+Cell In[1], line 1
+----> 1 from scipy.integrate import odeint
+      2 # Package for plotting
+      3 import math
+
+ModuleNotFoundError: No module named 'scipy'
+
+
+
+
+
+
+

Model definition#

+

The model is defined by using a class. This has several adavantages, one being that different classes can be defined independtly, e.g., one class represents the model definition while another will handle parameter optimization with experimental data. The same “optimization” class can be used with different model classes, so the code becomes more easily reusable.

+

The class Scerevisiae_Ligno includes several functions, each with a specific function.

+
    +
  1. init This function initialises the model class by defining all the relavant parameters and initial conditions.

  2. +
  3. rxn This function includes all the model equations. The model uses matrix notation to define the ODEs. In this part the all rates for different carbon sources are defined individually.

  4. +
  5. solve This function generates the timesteps for solving the ODEs. Depending on the initial conditions that were set, the development of the observables is simulated.

  6. +
  7. create_plot This function stores the results of the simulation on a dataframe which is used for plotting all relevant variables.

  8. +
+
+
+
class SCerevisiae_Ligno:
+  #initialize model
+    def __init__(self, Control=False):
+         # define value of model parameters
+        self.nuMaxGlu = 2.348# h-1
+        self.nuMaxXyl =  1.622 # h-1
+        self.Ks_Glu = 0.565 # kg Glu m-3
+        self.Ks_Xyl = 18.1 # kg Xyl m-3
+        self.Ki_Glu = 283.7 # kg Glu m-3
+        self.Ki_Xyl = 18.1 # kg Xyl m-3
+        self.Ki_GluXyl = 10 # kg Glu m-3
+        self.Y_XGlu = 0.115 # kg X/kg Glu
+        self.Y_XXyl = 0.162  # kg X/kg Xyl
+        self.Ki_EtOHmaxGlu = 103 # kg Glu m-3
+        self.Ki_EtOHmaxXyl =  60.2 # kg Xyl m-3
+        self.Y_EtOHGlu = 0.47# kg EtOH/kg Glu
+        self.Y_EtOHXyl = 0.4 # kg EtOH/kg Xyl
+        self.gammaG = 1.42 # no unit
+        self.gammaX = 0.608 # no unit
+
+        # Acetate parameters
+        self.nuHAcMax = 0.04428 # h-1
+        self.Ks_HAc = 2.5 # kg HAc m-3
+        self.Ki_HAcGlu = 2.74 # kg HAc m-3
+        self.Ki_HAcXyl = 0.073 # kg HAc m-3
+        self.Y_HAcHMF = 0.234 # kg Ac/kg HMF
+
+        # Furfural parameters
+        self.nuFurMax = 0.16812 # h-1
+        self.Ks_Fur =  0.05 # kg Furfural m-3
+        self.Ki_FurGlu = 0.75 # kg Furfural m-3
+        self.Ki_FurXyl = 0.35 # kg Furfural m-3
+        self.Ki_FurHMF = 0.25 # kg Furfural m-3
+        self.Y_FurFA = 1.02 # kg FA/kg Fur
+
+        # Furfuryl alcohol parameters
+        self.Ki_FAGlu = 5# kg FA m-3
+        self.Ki_FAXyl = 6 # kg FA m-3
+
+        # HMF parameters
+        self.nuHMFMax = 0.31536 # h-1
+        self.Ks_HMF = 0.5 # kg HMF m-3
+        self.Ki_HMFGlu = 2 # kg HMF m-3
+        self.Ki_HMFXyl = 10 # kg HMF m-3
+
+        # Initial state variable
+        self.X0 = 0.5  # g/L
+        self.Glu0 = 40  # g/L
+        self.Xyl0 = 20  # g/L
+        self.EtOH0 = 0  # g/L
+        self.Fur0 = 1  # g/L
+        self.HAc0 = 1  # g/L
+        self.HMF0 = 0.5  # g/L
+        self.FA0 = 0  # g/L
+
+        #t and V conditions
+        self.t_end = 30
+        self.t_start = 0
+        self.V0 = 2
+        self.T0 = 30
+
+
+
+
+#define the stoichiometric matrix
+    def rxn(self, C, t, u, fc):
+
+
+        # number of components
+        n = 8
+        m = 5
+        # initialize the stoichiometric matrix, s
+        s = np.zeros((m, n))
+
+        s[0, 0] = (self.Y_XGlu)
+        s[1, 0] = (self.Y_XXyl)
+        s[2, 0] = (0)
+        s[3, 0] = (0)
+        s[4, 0] = (0)
+
+        s[0, 1] = (-1)
+        s[1, 1] = (0)
+        s[2, 1] = (0)
+        s[3, 1] = (0)
+        s[4, 1] = (0)
+
+        s[0, 2] = (0)
+        s[1, 2] = (-1)
+        s[2, 2] = (0)
+        s[3, 2] = (0)
+        s[4, 2] = (0)
+
+        s[0, 3] = (self.Y_EtOHGlu)
+        s[1, 3] = (self.Y_EtOHXyl)
+        s[2, 3] = (0)
+        s[3, 3] = (0)
+        s[4, 3] = (0)
+
+        s[0, 4] = (0)
+        s[1, 4] = (0)
+        s[2, 4] = (-1)
+        s[3, 4] = (0)
+        s[4, 4] = (0)
+
+        s[0, 5] = (0)
+        s[1, 5] = (0)
+        s[2, 5] = (0)
+        s[3, 5] = (-1)
+        s[4, 5] = (self.Y_HAcHMF)
+
+        s[0, 6] = (0)
+        s[1, 6] = (0)
+        s[2, 6] = (0)
+        s[3, 6] = (0)
+        s[4, 6] = (-1)
+
+        s[0, 7] = (0)
+        s[1, 7] = (0)
+        s[2, 7] = (self.Y_FurFA)
+        s[3, 7] = (0)
+        s[4, 7] = (0)
+
+        # initialize the rate vector
+        rho = np.zeros((m))
+
+              # Glucose uptake process
+        rho[0] = self.nuMaxGlu * C[0] * (C[1] / (self.Ks_Glu + C[1] + ((C[1] ** 2) / self.Ki_Glu)) *
+                                           (1 - (C[3] / self.Ki_EtOHmaxGlu) ** self.gammaG) *
+                                           (1 / (1 + (C[4] / self.Ki_FurGlu))) *
+                                           (1 / (1 + (C[5] / self.Ki_HAcGlu))) *
+                                           (1 / (1 + (C[6] / self.Ki_HMFGlu))) *
+                                           (1 / (1 + (C[7] / self.Ki_FAGlu))))
+        # Xylose uptake process
+        rho[1] = self.nuMaxXyl * C[0] * (C[2] / (self.Ks_Xyl + C[2] + ((C[2] ** 2) / self.Ki_Xyl)) *
+                                           (1 - (C[3] / self.Ki_EtOHmaxXyl) ** self.gammaX) *
+                                           (1 / (1 + (C[4] / self.Ki_FurXyl))) *
+                                           (1 / (1 + (C[5] / self.Ki_HAcXyl))) *
+                                           (1 / (1 + (C[6] / self.Ki_HMFXyl))) *
+                                           (1 / (1 + (C[7] / self.Ki_FAXyl))) *
+                                           (1 / (1 + (C[1] / self.Ki_GluXyl))))
+        # Fur uptake process
+        rho[2] = self.nuFurMax * C[0] * (C[4] / (self.Ks_Fur + C[4]))
+        # HAc uptake process
+        rho[3] = self.nuHAcMax * C[0] * (C[5] / (self.Ks_HAc + C[5]))
+        # HMF uptake process
+        rho[4] = self.nuHMFMax * C[0] * (C[6] / (self.Ks_HMF + C[6])) * (1 / (1 + (C[4] / self.Ki_FurGlu)))
+
+
+ #Solving the mass balances
+        dXdt = s[0, 0] * rho[0] + s[1, 0] * rho[1] + s[2, 0] * rho[2] + s[3, 0] * rho[3] + s[4, 0] * rho[4]
+        dGludt = s[0, 1] * rho[0] + s[1, 1] * rho[1] + s[2, 1] * rho[2] + s[3, 1] * rho[3] + s[4, 1] * rho[4]
+        dXyldt = s[0, 2] * rho[0] + s[1, 2] * rho[1] + s[2, 2] * rho[2] + s[3, 2] * rho[3] + s[4, 2] * rho[4]
+        dEtOHdt = s[0, 3] * rho[0] + s[1, 3] * rho[1] + s[2, 3] * rho[2] + s[3, 3] * rho[3] + s[4, 3] * rho[4]
+        dFurdt = s[0, 4] * rho[0] + s[1, 4] * rho[1] + s[2, 4] * rho[2] + s[3, 4] * rho[3] + s[4, 4] * rho[4]
+        dHAcdt = s[0, 5] * rho[0] + s[1, 5] * rho[1] + s[2, 5] * rho[2] + s[3, 5] * rho[3] + s[4, 5] * rho[4]
+        dHMFdt = s[0, 6] * rho[0] + s[1, 6] * rho[1] + s[2, 6] * rho[2] + s[3, 6] * rho[3] + s[4, 6] * rho[4]
+        dFAdt = s[0, 7] * rho[0] + s[1, 7] * rho[1] + s[2, 7] * rho[2] + s[3, 7] * rho[3] + s[4, 7] * rho[4]
+
+        dVdt=0
+        dTdt = 0
+
+        return [dXdt, dGludt, dXyldt, dEtOHdt, dFurdt, dHAcdt, dHMFdt, dFAdt, dVdt, dTdt]
+#solve the ODES
+    def solve(self):
+
+        t = np.linspace(0, 30) #generation of the time-points
+
+        u = 0
+        fc = 1
+        C0 = [self.X0, self.Glu0, self.Xyl0, self.EtOH0, self.Fur0, self.HAc0, self.HMF0, self.FA0, self.V0, self.T0] #initial conditions vector
+        C = odeint(self.rxn, C0, t, rtol=1e-7, mxstep=500000, args=(u, fc,)) #solve ODEs
+
+
+        return t, C
+
+   #generate the plot of model variables
+    def create_plot(self, t, C):
+        figure = make_subplots(rows=1, cols=2) #make figure with 2 subplots
+        #assign simulation results to variable for plotting
+        X = C[:, 0]
+        Glu = C[:, 1]
+        Xly = C[:, 2]
+        EtOH = C[:, 3]
+        Fur = C[:, 4]
+        HAc = C[:, 5]
+        HMF = C[:, 6]
+        FA = C[:, 7]
+        V = C[:, 8]
+
+         #collect all variables to plot in 1st subplot in a dataframe
+        df = pd.DataFrame({'t': t, 'Glu': Glu, 'X': X, 'Xly':Xly, 'EtOH': EtOH, 'Fur':Fur, 'HAc': HAc, 'HMF':HMF, 'FA':FA})
+         #add the different traces to 1st subplot
+        figure.add_trace(go.Scatter(x=df['t'], y=df['Glu'], name='Glucose'), row=1, col=1)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['X'], name='Biomass'), row=1, col=1)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['Xly'], name='Xylose'), row=1, col=1)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['EtOH'], name='Ethanol'), row=1, col=1)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['Fur'], name='Furfural'), row=1, col=1)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['HAc'], name='Acetic acid'), row=1, col=1)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['HMF'], name='HMF'), row=1, col=1)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['FA'], name='Furfuryl alcohol'), row=1, col=1)
+         #add the title and axes labels
+        figure.update_layout(title=('Simulation of the model for the Scerevisiae in fedbatch using lignocellulosic'),
+                             xaxis_title='time (h)',
+                             yaxis_title='Concentration (g/L)')
+      #dataframe with varible to plot in 2nd subplot
+        df2 = pd.DataFrame({'t': t, 'V':V})
+
+         #add trace to the 2nd subplot
+        figure.append_trace(go.Scatter(x=df2['t'], y=df2['V'], name='Volume'), row=1, col=2)
+
+        return figure
+
+
+
+
+
+
+

Extract results and make plots#

+

This portion of the code extracts the results from the model class and creates the figures to display the simulation results.

+
+
+
model = SCerevisiae_Ligno() # Instantiate the SCerevisiae_Ligno class
+t, C = model.solve() # Call the solve method to get the simulation results
+fig = model.create_plot(t, C) # Call create_plot with the simulation results
+
+fig.show()
+
+
+
+
+
---------------------------------------------------------------------------
+NameError                                 Traceback (most recent call last)
+Cell In[3], line 2
+      1 model = SCerevisiae_Ligno() # Instantiate the SCerevisiae_Ligno class
+----> 2 t, C = model.solve() # Call the solve method to get the simulation results
+      3 fig = model.create_plot(t, C) # Call create_plot with the simulation results
+      5 fig.show()
+
+Cell In[2], line 166, in SCerevisiae_Ligno.solve(self)
+    164 def solve(self):
+--> 166     t = np.linspace(0, 30) #generation of the time-points
+    168     u = 0
+    169     fc = 1
+
+NameError: name 'np' is not defined
+
+
+
+
+

Created on Thu Sep 6 13:34:32 2018

+

@author: simoca

+
+ + + + +
+ + + + + + + + +
+ + + + + + +
+
+ + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/WS2/module_1/2_Fermentation_Scerevisiae_Glucose_Aerobic_CSTR.html b/WS2/module_1/2_Fermentation_Scerevisiae_Glucose_Aerobic_CSTR.html new file mode 100755 index 0000000..3ebd6cc --- /dev/null +++ b/WS2/module_1/2_Fermentation_Scerevisiae_Glucose_Aerobic_CSTR.html @@ -0,0 +1,880 @@ + + + + + + + + + + + Mechanistic model of S. cerevisiae in continous stirred tank reactor under aerobic conditions — Dig4Bio-workshops + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + +
+

Mechanistic model of S. cerevisiae in continous stirred tank reactor under aerobic conditions

+ +
+ +
+
+ + + + +
+ +
+

Mechanistic model of S. cerevisiae in continous stirred tank reactor under aerobic conditions#

+

This model describes an aerobic S. cerevisiae fermentation with glucose as carbon source in a continous stirred tank reactor. It builds a stochiometric matrix to follow all conversions. The model accounts for the changes of glucose, ethanol, dissolved oxygen, biomass and volume.

+
+
+

Package import#

+

This portion of the code handles the import of all the relevant python packages.

+
+
+
from scipy.integrate import odeint
+#Package for plotting
+import math
+#Package for the use of vectors and matrix
+import numpy as np
+import pandas as pd
+import array as arr
+from matplotlib.figure import Figure
+import sys
+import os
+import matplotlib.pyplot as plt
+from matplotlib.ticker import FormatStrFormatter
+import glob
+from random import sample
+import random
+import time
+import plotly
+import plotly.graph_objs as go
+import json
+from plotly.subplots import make_subplots
+
+
+
+
+
---------------------------------------------------------------------------
+ModuleNotFoundError                       Traceback (most recent call last)
+Cell In[1], line 1
+----> 1 from scipy.integrate import odeint
+      2 #Package for plotting
+      3 import math
+
+ModuleNotFoundError: No module named 'scipy'
+
+
+
+
+
+
+

Model definition#

+

The model is defined by using a class. This has several adavantages, one being that different classes can be defined independtly, e.g., one class represents the model definition while another will handle parameter optimization with experimental data. The same “optimization” class can be used with different model classes, so the code becomes more easily reusable.

+

The class SCerevisiae includes several functions, each with a specific function.

+
    +
  1. init This function initialises the model class by defining all the relavant parameters and initial conditions.

  2. +
  3. rxn This function includes all the model equations. The model uses matrix notation to define the ODEs. In this part the settings for feed are specified as well, it includes terms for dilution, addtion and washing out effects.

  4. +
  5. solve This function generates the timesteps for solving the ODEs. Depending on the initial conditions that were set, the development of the observables is simulated.

  6. +
  7. create_plot This function stores the results of the simulation on a dataframe which is used for plotting all relevant variables.

  8. +
+
+
+
class SCerevisiae_Cstr:
+   #initialise the model
+    def __init__(self):
+
+      # define value of model parameters
+
+        self.Yox_XG = 0.8
+        self.Yred_XG = 0.05
+        self.Yox_XE = 0.72
+        self.Y_OG = 1.067
+        self.Y_EG = 0.5
+        self.Y_OE = 1.5
+        self.q_g = 4.68
+        self.q_o = 0.37
+        self.q_e = 0.86
+        self.t_lag = 4.66
+        self.Kg = 0.17
+        self.Ko = 0.0001
+        self.Ke = 0.56
+        self.Ki = 0.31
+        self.O_sat = 0.00755
+        self.kla = 1004
+
+        #define initial conditions
+        self.G0 = 18
+        self.E0 = 0.34
+        self.O0 = 0.00755
+        self.X0 = 0.1
+        self.T0 = 30
+        self.V0 = 2
+
+        #define feed parameters
+        self.t_end = 100
+        self.Cin = 100
+        self.Fconst = 0.05
+        self.t_feed_start = 15
+        self.t_start=2
+        self.steps = (self.t_end - self.t_start)*24
+
+
+    #define the stoichiometric matrix
+    def rxn(self, C,t):
+
+        #matrix
+        #number of components
+        n = 4
+        m = 4
+        #initialize the stoichiometric matrix, s
+        s = np.zeros((m,n))
+        s[0,0] = -1
+        s[0,1] = 0
+        s[0,2] = -self.Y_OG
+        s[0,3] = self.Yox_XG
+
+        s[1,0] = -1
+        s[1,1] = self.Y_EG
+        s[1,2] = 0
+        s[1,3] = self.Yred_XG
+
+        s[2,0] = 0
+        s[2,1] = -1
+        s[2,2] = -self.Y_OE
+        s[2,3] = self.Yox_XE
+
+        s[3,0] = 0
+        s[3,1] = 0
+        s[3,2] = 1
+        s[3,3] = 0
+        #initialize the rate vector
+        rho = np.zeros((4,1))
+        ##initialize the overall conversion vector
+        r=np.zeros((4,1))
+        #Volume balance
+        if (t>=self.t_feed_start):
+            Fin = self.Fconst
+            Fout = self.Fconst
+        else:
+            Fin = 0
+            Fout = 0
+
+        F = Fin - Fout
+
+        rho[0,0] = ((1/self.Y_OG)*min(self.q_o*(C[2]/(C[2]+self.Ko)),self.Y_OG*(self.q_g*(C[0]/(C[0]+self.Kg)))))*C[3]
+        rho[1,0] = ((1-math.exp(-t/self.t_lag))*((self.q_g*(C[0]/(C[0]+self.Kg)))-(1/self.Y_OG)*min(self.q_o*(C[2]/(C[2]+self.Ko)),self.Y_OG*(self.q_g*(C[0]/(C[0]+self.Kg))))))*C[3]
+        rho[2,0] = ((1/self.Y_OE)*min(self.q_o*(C[2]/(C[2]+self.Ko))-(1/self.Y_OG)*min(self.q_o*(C[2]/(C[2]+self.Ko)),self.Y_OG*(self.q_g*(C[0]/(C[0]+self.Kg)))),self.Y_OE*(self.q_e*(C[1]/(C[1]+self.Ke))*(self.Ki/(C[0]+self.Ki)))))*C[3]
+        rho[3,0] = self.kla*(self.O_sat - C[2])
+
+        #Developing the matrix, the overall conversion rate is stoichiometric *rates
+        r[0,0] = (s[0,0]*rho[0,0])+(s[1,0]*rho[1,0])+(s[2,0]*rho[2,0])+(s[3,0]*rho[3,0])
+        r[1,0] = (s[0,1]*rho[0,0])+(s[1,1]*rho[1,0])+(s[2,1]*rho[2,0])+(s[3,1]*rho[3,0])
+        r[2,0] = (s[0,2]*rho[0,0])+(s[1,2]*rho[1,0])+(s[2,2]*rho[2,0])+(s[3,2]*rho[3,0])
+        r[3,0] = (s[0,3]*rho[0,0])+(s[1,3]*rho[1,0])+(s[2,3]*rho[2,0])+(s[3,3]*rho[3,0])
+
+
+        #Solving the mass balances terms for dilution, addtion and washing out added
+        dGdt = r[0,0] -F/C[4]*C[0] + Fin/C[4]*self.Cin - Fout/C[4]*C[0]
+        dEdt = r[1,0] -F/C[4]*C[1] - Fout/C[4]*C[1]
+        dOdt = r[2,0]
+        dXdt = r[3,0] -F/C[4]*C[3] - Fout/C[4]*C[3]
+        dVdt = F
+        dTdt = 0
+
+        return [dGdt,dEdt,dOdt,dXdt, dVdt, dTdt]
+
+    #solve the ODES
+    def solve(self):
+        #time
+      t = np.linspace(self.t_start, self.t_end, self.steps)  #generation of the time-points
+      u=0
+      C0 = [self.G0, self.E0, self.O0, self.X0, self.V0, self.T0]  #initial conditions vector
+      C = odeint(self.rxn, C0, t, rtol = 1e-7, mxstep= 500000)  #solve ODEs
+
+      return t, C
+
+
+   #generate the plot of model variables
+
+
+    def create_plot(self, t, C):
+        fig = make_subplots(rows=1, cols=2) #make figure with 2 subplots
+        #assign simulation results to variable for plotting
+        G = C[:, 0]
+        E = C[:, 1]
+        O = C[:, 2]
+        B = C[:, 3]
+        V = C[:, 4]
+
+        #collect all variables to plot in 1st subplot in a dataframe
+        df = pd.DataFrame({'t': t, 'G': G, 'B': B, 'E': E, 'O':O, 'V':V})
+
+        #add the different traces to 1st subplot
+        fig.add_trace(go.Scatter(x=df['t'], y=df['G'], name='Glucose'), row=1, col=1)
+        fig.add_trace(go.Scatter(x=df['t'], y=df['B'], name='Biomass'), row=1, col=1)
+        fig.add_trace(go.Scatter(x=df['t'], y=df['E'], name='Ethanol'), row=1, col=1)
+        fig.add_trace(go.Scatter(x=df['t'], y=df['O'], name='Oxygen'), row=1, col=1)
+
+        #add the title and axes labels
+        fig.update_layout(title=('Simulation of the model for the Scerevisiae in continuous'),
+                          xaxis_title='time (h)',
+                          yaxis_title='Concentration (g/L)')
+
+        #dataframe with varible to plot in 2nd subplot
+        df2 = pd.DataFrame({'t': t, 'V':V})
+
+        #add trace to the 2nd subplot
+        fig.append_trace(go.Scatter(x=df2['t'], y=df2['V'], name='Volume'), row=1, col=2)
+
+        return fig
+
+
+
+
+
+
+

Extract results and make plots#

+

This portion of the code extracts the results from the model class and creates the figures to display the simulation results.

+
+
+
model = SCerevisiae_Cstr() # Instantiate the SCerevisiae_Cstr class
+t, C = model.solve() # Call the solve method to get the simulation results
+fig = model.create_plot(t, C) # Call create_plot with the simulation results
+
+fig.show()
+
+
+
+
+
---------------------------------------------------------------------------
+NameError                                 Traceback (most recent call last)
+Cell In[3], line 2
+      1 model = SCerevisiae_Cstr() # Instantiate the SCerevisiae_Cstr class
+----> 2 t, C = model.solve() # Call the solve method to get the simulation results
+      3 fig = model.create_plot(t, C) # Call create_plot with the simulation results
+      5 fig.show()
+
+Cell In[2], line 108, in SCerevisiae_Cstr.solve(self)
+    106 def solve(self):
+    107     #time
+--> 108   t = np.linspace(self.t_start, self.t_end, self.steps)  #generation of the time-points
+    109   u=0
+    110   C0 = [self.G0, self.E0, self.O0, self.X0, self.V0, self.T0]  #initial conditions vector
+
+NameError: name 'np' is not defined
+
+
+
+
+

Created on Thu Sep 6 18:33:18 2018

+

By Simoneta Cano de las Heras and bjogut

+
+ + + + +
+ + + + + + + + +
+ + + + + + +
+
+ + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/WS2/module_1/3_Fermentation_MonodHerbert_aerobic.html b/WS2/module_1/3_Fermentation_MonodHerbert_aerobic.html new file mode 100755 index 0000000..7706ae4 --- /dev/null +++ b/WS2/module_1/3_Fermentation_MonodHerbert_aerobic.html @@ -0,0 +1,974 @@ + + + + + + + + + + + Aerobic fermentation model (S. cerevisiae) — Dig4Bio-workshops + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + +
+

Aerobic fermentation model (S. cerevisiae)

+ +
+ +
+
+ + + + +
+ +

Created on Thu Sep 6 13:34:32 2018 +By Simoneta Cano de las Heras

+

Adapted by Mariana Albino, October 2024

+
+

Aerobic fermentation model (S. cerevisiae)#

+

This model describes an aerobic batch S. cerevisiae fermentation. It uses Monod-Herbert expressions to describe the kinetics of the process. It can take into account the metabolic heat produced by the cells, in which case a PID controller is used to maintain a stable temperature

+
+
+

Package import#

+

This portion of the code handles the import of all the relevant python packages.

+
+
+
#import the necesary packages
+
+from scipy.integrate import odeint
+#Package for plotting
+import math
+#Package for the use of vectors and matrix
+import numpy as np
+import array as arr
+from matplotlib.figure import Figure
+import sys
+import os
+import matplotlib.pyplot as plt
+from matplotlib.ticker import FormatStrFormatter
+import glob
+from random import sample
+import random
+import time
+import plotly.graph_objects as go
+import plotly
+import json
+import pandas as pd
+
+
+
+
+
---------------------------------------------------------------------------
+ModuleNotFoundError                       Traceback (most recent call last)
+Cell In[1], line 3
+      1 #import the necesary packages
+----> 3 from scipy.integrate import odeint
+      4 #Package for plotting
+      5 import math
+
+ModuleNotFoundError: No module named 'scipy'
+
+
+
+
+
+
+

Model definition#

+

The model is defined by using a class. This has several adavantages, one being that different classes can be defined independtly, e.g., one class represents the model definition while another will handle parameter optimization with experimental data. The same “optimization” class can be used with different model classes, so the code becomes more easily reusable.

+

The class Monod_Herbert includes several functions, each with a specific function.

+
    +
  1. init This function initialises the model class by defining all the relavant parameters and initial conditions.

  2. +
  3. rxn This function includes all the model equations. The model uses matrix notation to define the ODEs

  4. +
  5. solve This function generates the timesteps for solving the ODEs. It includes an approach for when the “CONTROL” option is turned OFF and an approach that solves a PDI controller for temperature control, for when the “CONTROL” feature is ON.

  6. +
  7. create_plot This function stores the results of the simulation on a dataframe which is used for plotting all relevant variables.

  8. +
+
+
+
class Monod_Herbert:
+  #initialise the model
+    def __init__(self, Control=False):  #change to Control=True if you want to see the effect of Temperature control
+         # define value of model parameters
+        self.Y_XS = 0.8
+        self.Y_OX = 1.05
+        self.y_x = 0.5
+        self.mu_max = 1.1
+        self.Ks = 0.17
+        self.kd = 0.08
+        self.kla = 1004
+        self.O_sat = 0.0755
+
+        #define initial conditions
+        self.G0 = 18
+        self.O0 = 0.0755
+        self.X0 = 0.01
+        self.V0 = 40
+
+        #define parameters for control, default every 1/24 hours:
+        self.t_end = 30
+        self.t_start = 0
+        self.Control = False
+        self.coolingOn = True
+        self.steps = (self.t_end - self.t_start)*24
+        self.T0 = 30
+        self.K_p = 2.31e+01
+        self.K_i = 3.03e-01
+        self.K_d = -3.58e-03
+        self.Tset = 30
+        self.u_max = 150
+        self.u_min = 1
+
+
+
+
+    #define the stoichiometric matrix
+    def rxn(self, C,t, u):
+        #when there is no control, k has no effect
+        k=1
+        #when cooling is off than u = 0
+        if self.coolingOn == False:
+            u = 0  #flow of cooling water
+      #define temperature controller
+        if self.Control == True :
+            #Cardinal temperature model with inflection: Salvado et al 2011 "Temperature Adaptation Markedly Determines Evolution within the Genus Saccharomyces"
+            #Strain S. cerevisiae PE35 M
+
+            #How is the growth rate of the organism affected by the changes in temperature
+            Topt = 30
+            Tmax = 45.48
+            Tmin = 5.04
+            T = C[4]
+            if T < Tmin or T > Tmax:
+                 k = 0  #growth rate
+            else:
+                 D = (T-Tmax)*(T-Tmin)**2
+                 E = (Topt-Tmin)*((Topt-Tmin)*(T-Topt)-(Topt-Tmax)*(Topt+Tmin-2*T))
+                 k = D/E
+
+        #number of components
+        n = 3
+        m = 3
+        #initialize the stoichiometric matrix, s
+        s = np.zeros((m,n))
+        s[0,0] = -1/self.Y_XS
+        s[0,1] = -1/self.Y_OX
+        s[0,2] = 1
+
+
+        s[1,0] = 0
+        s[1,1] = 1/self.y_x
+        s[1,2] = -1
+
+        s[2,0] = 0
+        s[2,1] = self.kla
+        s[2,2] = 0
+        #initialize the rate vector
+        rho = np.zeros((m,1))
+        ##initialize the overall conversion vector
+        r=np.zeros((n,1))
+        rho[0,0] = self.mu_max*(C[0]/(C[0]+self.Ks))*C[2]
+        rho[1,0] = self.kd*C[2]
+        rho[2,0] = self.kla*(self.O_sat - C[1])
+
+        #Developing the matrix, the overall conversion rate is stoichiometric *rates
+        r[0,0] = (s[0,0]*rho[0,0])+(s[1,0]*rho[1,0])+(s[2,0]*rho[2,0])
+        r[1,0] = (s[0,1]*rho[0,0])+(s[1,1]*rho[1,0])+(s[2,1]*rho[2,0])
+        r[2,0] = (s[0,2]*rho[0,0])+(s[1,2]*rho[1,0])+(s[2,2]*rho[2,0])
+
+
+        #Solving the mass balances
+        dSdt = r[0,0]
+        dOdt = r[1,0]
+        dXdt = r[2,0]
+        dVdt = 0
+        if self.Control == True :
+            '''
+
+             dHrxn heat produced by cells estimated by yeast heat combustion coeficcient dhc0 = -21.2 kJ/g
+
+             dHrxn = dGdt*V*dhc0(G)-dEdt*V*dhc0(E)-dXdt*V*dhc0(X)
+
+             (when cooling is working)  Q = - dHrxn -W ,
+
+             dT = V[L] * 1000 g/L / 4.1868 [J/gK]*dE [kJ]*1000 J/KJ
+
+             dhc0(EtOH) = -1366.8 kJ/gmol/46 g/gmol [KJ/g]
+
+             dhc0(Glc) = -2805 kJ/gmol/180g/gmol [KJ/g]
+
+
+
+            '''
+            #Metabolic heat: [W]=[J/s], dhc0 from book "Bioprocess Engineering Principles" (Pauline M. Doran) : Appendix Table C.8
+            dHrxndt =   dXdt*C[4]*(-21200) #[J/s]
+            #Shaft work 1 W/L1
+            W = -1*C[4] #[J/S] negative because exothermic
+
+
+            #Mass flow cooling water
+            M=u/3600*1000 #[kg/s]
+            #Define Tin = 5 C, Tout=TReactor
+            #heat capacity water = 4190 J/kgK
+            Tin = 5
+            #Estimate water at outlet same as Temp in reactor
+            Tout = C[4]
+            cpc = 4190
+            #Calculate Q from Eq 9.47
+            Q=-M*cpc*(Tout-Tin) # J/s
+            #Calculate Temperature change
+            dTdt = -1*(dHrxndt - Q + W)/(C[4]*1000*4.1868) #[K/s]
+        else:
+            dTdt = 0
+        return [dSdt, dOdt, dXdt, dVdt, dTdt]
+
+
+    def solve(self):
+
+        t = np.linspace(self.t_start, self.t_end, self.steps) #generation of the time-points
+
+        #solve if Control is OFF:
+        if self.Control == False :
+            u = 0
+            C0 = [self.G0,  self.O0, self.X0,self.V0, self.T0]   #initial conditions vector
+            C = odeint(self.rxn, C0, t, rtol = 1e-7, mxstep= 500000, args=(u,)) #solve ODEs
+
+        #solve for Control ON
+        else:
+            """
+
+            PID Temperature Control:
+
+            """
+            # storage for recording values
+            C = np.ones([len(t), 5])
+            C0 = [self.G0, self.O0, self.X0,self.V0,self.T0]
+            self.ctrl_output = np.zeros(len(t)) # controller output
+            e = np.zeros(len(t)) # error
+            ie = np.zeros(len(t)) # integral of the error
+            dpv = np.zeros(len(t)) # derivative of the pv
+            P = np.zeros(len(t)) # proportional
+            I = np.zeros(len(t)) # integral
+            D = np.zeros(len(t)) # derivative
+
+            for i in range(len(t)-1):
+
+                #PID control of cooling water
+                dt = t[i+1]-t[i]
+                #Error
+                e[i] = C[i,4] - self.Tset
+                #print(e[i])
+                if i >= 1:
+                    dpv[i] = (C[i,4]-C[i-1,4])/dt
+                    ie[i] = ie[i-1] + e[i] * dt
+
+                #print(ie)
+                P[i]=self.K_p*e[i]
+                I[i]=self.K_i*ie[i]
+                D[i]=self.K_d*dpv[i]
+
+                self.ctrl_output[i]=P[i]+I[i]+D[i]
+                u=self.ctrl_output[i]
+
+                #print(u)
+                if u>self.u_max:
+                    u=self.u_max
+                    ie[i] = ie[i] - e[i]*dt # anti-reset windup
+
+                if u < self.u_min:
+                    u =self.u_min
+                    ie[i] = ie[i] - e[i]*dt # anti-reset windup
+
+                #print(u)
+
+                if i > 0 and e[i] * e[i-1] < 0:
+                  ie[i] = 0
+                #time for solving ODE
+                ts = [t[i],t[i+1]]
+
+                #solve ODE from last timepoint to new timepoint with old values
+
+                y =  odeint(self.rxn, C0, ts, rtol = 1e-7, mxstep= 500000, args=(u,))
+                #update C0
+                C0 = y[-1]
+                #merge y to C
+                C[i+1]=y[-1]
+
+        return t, C
+
+    #generate the plot of model variables
+
+    def create_plot(self, t, C):
+        G = C[:, 0]
+        O = C[:, 1]
+        B = C[:, 2]
+        T = C[:, 4]
+        df = pd.DataFrame({'t': t, 'Substrate': G, 'Biomass': B, 'Oxygen': O, 'Temperature' :T})
+        fig = go.Figure()
+        fig.add_trace(go.Scatter(x=df['t'], y=df['Substrate'], name='Substrate'))
+        fig.add_trace(go.Scatter(x=df['t'], y=df['Biomass'], name='Biomass'))
+        fig.add_trace(go.Scatter(x=df['t'], y=df['Oxygen'], name='Oxygen'))
+        fig.update_layout(                          xaxis_title='time (h)',
+                          yaxis_title='Concentration (g/L)')
+        figT =go.Figure()
+        figT.add_trace(go.Scatter(x=df['t'], y=df['Temperature'], name='Temperature'))
+        figT.update_layout(xaxis_title='time (h)',
+                          yaxis_title='Temperature (C)')
+
+
+
+        return fig, figT
+
+
+
+
+
+
+

Extract results and make plots#

+

This portion of the code extracts the results from the Monod_Herbert class and creates the figures to display the simulation results.

+
+
+
model = Monod_Herbert() # Instantiate the Monod_Herbert class
+t, C = model.solve() # Call the solve method to get the simulation results
+fig = model.create_plot(t, C)[0] # Call create_plot with the simulation results
+figT = model.create_plot(t, C)[1] # Call create_plot with the simulation results
+
+fig.show()
+figT.show()
+
+
+
+
+
---------------------------------------------------------------------------
+NameError                                 Traceback (most recent call last)
+Cell In[3], line 2
+      1 model = Monod_Herbert() # Instantiate the Monod_Herbert class
+----> 2 t, C = model.solve() # Call the solve method to get the simulation results
+      3 fig = model.create_plot(t, C)[0] # Call create_plot with the simulation results
+      4 figT = model.create_plot(t, C)[1] # Call create_plot with the simulation results
+
+Cell In[2], line 140, in Monod_Herbert.solve(self)
+    138 def solve(self):
+--> 140     t = np.linspace(self.t_start, self.t_end, self.steps) #generation of the time-points
+    142     #solve if Control is OFF:
+    143     if self.Control == False :
+
+NameError: name 'np' is not defined
+
+
+
+
+
+
+

Using the model#

+

With this code you are able to test the behaviour of the process in a more simple case when we assume temperature is constant since the generation of metabolic heat is ignored. You can also turn ON the control option and evaluate the dymamics in Temperature. Furthermore, you can try to test different initila conditions, e.g., initial substrate concentrations (remember, these should be realistic values), and study the impact on the dynamics of Biomass.

+
+ + + + +
+ + + + + + + + +
+ + + + + + +
+
+ + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/WS2/module_1/4_FedBatch_Ecoli_simulation_proteinproduction.html b/WS2/module_1/4_FedBatch_Ecoli_simulation_proteinproduction.html new file mode 100755 index 0000000..8ae07e4 --- /dev/null +++ b/WS2/module_1/4_FedBatch_Ecoli_simulation_proteinproduction.html @@ -0,0 +1,1107 @@ + + + + + + + + + + + E. coli Fed-batch fermentation model, including recombinant protein production — Dig4Bio-workshops + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + + + + + + +
+ +
+

E. coli Fed-batch fermentation model, including recombinant protein production#

+

In this Python Script the ODE (Ordinary Differential Equation) +of an Ecoli model is solved with the Python ODE Solver odeint.

+

The equations for E. coli growth have been based on the work of Anane et al, which can be found in https://doi.org/10.1016/j.bej.2017.05.013. The protein production dynamics have been based in the work of Chae et al. which can be found in https://doi.org/10.1002/1097-0290(20000805)69:3<275::aid-bit5>3.0.co;2-y. The combine implementation can be found in the MSc Thesis https://scholar.tecnico.ulisboa.pt/records/uuoC1N4NzN890o9vFCM5lQm9zjqNvBsOY3pp in Section 3.1.

+
+
+

Package import#

+

This portion of the code handles the import of all the relevant python packages.

+
+
+
# import packages
+from __future__ import division
+import numpy as np
+import matplotlib.pyplot as plt
+from scipy.integrate import odeint
+import plotly.express as px
+import plotly.graph_objects as go
+from scipy.integrate import solve_ivp
+from plotly.subplots import make_subplots
+import pandas as pd
+%matplotlib inline
+#%matplotlib qt
+
+
+
+
+
---------------------------------------------------------------------------
+ModuleNotFoundError                       Traceback (most recent call last)
+Cell In[1], line 3
+      1 # import packages
+      2 from __future__ import division
+----> 3 import numpy as np
+      4 import matplotlib.pyplot as plt
+      5 from scipy.integrate import odeint
+
+ModuleNotFoundError: No module named 'numpy'
+
+
+
+
+
+
+

Define timesteps#

+

This cell defines the initial and end-time, and the timestops to perform the simulation

+
+
+
# discretizing the time
+dt = 0.01
+
+# defining the initial time
+t0 = 0
+
+# defining the end-time
+T = 25 #h
+
+# generation of the time-points
+t = np.linspace(t0, T, int(T/dt)+1)
+
+
+
+
+
---------------------------------------------------------------------------
+NameError                                 Traceback (most recent call last)
+Cell In[2], line 11
+      8 T = 25 #h
+     10 # generation of the time-points
+---> 11 t = np.linspace(t0, T, int(T/dt)+1)
+
+NameError: name 'np' is not defined
+
+
+
+
+
+
+

Define initial conditions and process parameters#

+

In this cell the initial conditions for the monitored variables as well as process parameters regarding the feed strategy. y0 is the vector which contains the initial conditions values.

+
+
+
# initial values at time 0
+X0   = 0.025     # Biomass [g/L]
+S0   = 20         # Substrate [g/L]
+DOT0 = 90        # Dissolved oxygen [%]
+A0   = 0          # Acetate [g/L]
+V0   = 1400     # Volume [L]
+I0 = 0            #Inducer concentration [g/L]
+P0 =0             #Protein concentratuin [g/L]
+
+# process parameters
+
+Si        = 300
+If        = 50
+Fout      = 0
+KLa       = 600
+muset     = 0.3
+
+# define initial condition and save it in the y
+y0 = [X0, S0, DOT0, A0, V0, I0, P0]
+
+
+
+
+
+

First pass of the model#

+

The feeding strategy employed is trigered by a decrease in Glucose concentration below a certain value. In the approach implemented here the timepoint at which this happens is determined by running the model a first time, without considering feed, i.e., running the model as if we were studying a batch process. We also extract the Biomass concetration at the end of the batch phase, which is necessary for the feed rate calculations

+

Note: If using e.g., the solve_ivp solver, a different approach to extract these values can be followed by the use of flags. This would be a more elegant implementation

+
+
+
+

Define the model equations#

+

This cell defines the model equation in Batch mode.

+
    +
  1. The first step is to assign the variable to simulate to the result vector y.

  2. +
  3. The feed parameters are defined, in this case set to 0, since we consider a batch process

  4. +
  5. The parameters characteristic for the strain are defined as well as physicochemical constants

  6. +
  7. The algebraic expressions that describe the kinetics of the system are defined

  8. +
  9. The ODE system is defined

  10. +
+
+
+
# define the function; Run the model a first time, with no feeding to determibe tf
+def eColi_first_pass(y, t):
+    X   = y[0]
+    S   = y[1]
+    DOT = y[2]
+    A   = y[3]
+    V   = y[4]
+    I   = y[5]
+    P   = y[6]
+
+    # process parameters
+
+    F1=0
+    Fi=0
+
+    # parameters describing the characteristics of the strain
+    qAmax   = 1           # max Acetate consumption rate [g/g*h]
+    Kaq     = 0.01        # affinity constant Acetate consumption [g/L]
+    Ksq     = 0.1         # affinity constnat Substrate consumption [g/L]
+    Yam     = 0.2         # yield acetate maintenance [g/g]
+    Yaresp  = 0.2         # yield acetate respiratory [g/g]
+    Yem     = 0.56        # yield excluding maintance [g/g]
+    qSmax   = 1.4         # max glucose uptake rate [g/g*h]
+    Ks      = 0.05        # affinity constant glucose consumption[g/L]
+    qm      = 0.04        # specific maintenance coefficient[g/g*h]
+    Ko      = 1           # Affinity constant, oxygen consumption [g/L]
+    Yosresp = 1.217       # yield from S to X, respiratory [g/g]
+    pAmax   = 1           # max Acetate production rate [g/g*h]
+    Kap     = 10          # affinity constant intracellular acetate production[g/L]
+    Yaof    = 1           # aceate yield in overflow[g/g]
+    Yofm    = Yem         # [g/g]
+    k1=0.32
+    k2=0.00044
+    k3=0.6
+    Ki=0.55
+    muset = 0.3         #Growth rate set in feed phase
+
+    # physicochemical constants
+    Cs      = 0.391       # ratio of substrate per C in [gC/gS]
+    Cx      = 0.488       # ratio of biomass per C in [gC/gX]
+    H       = 14000       # conversion factor
+    DOTstar = 90
+
+    # algebraic variables
+    qS   = qSmax * S / (S + Ks) * DOT / (DOT + Ko)     # substrate uptake
+    qSof = pAmax * qS / ( qS + Kap ) / Yaof            # overflow substrate conversion
+    pA   = pAmax * qS / (qS + Kap)                     # production of acetate
+    qSox = qS - qSof                                   # substrate uptake excluding overflow
+    qSan =(qSox - qm) * Yem * Cx / Cs                  # anabolic substrate consumption
+    qsA  = qAmax * A / (A + Kaq) * (Ksq / (Ksq + qS))  # acetate consumption
+    qA   = pA - qsA                                    # total acetate equilibrium
+    qO   = Yosresp * (qSox - qSan) + qsA * Yaresp      # oxygen uptake
+
+    # growth rate equation
+    my =(qSox - qm) * Yem + qsA * Yam + qSof * Yofm
+
+    # differential equation system
+    dXdt   = -F1 / V * X + my * X                   # biomass growth
+    dSdt   = -F1 / V * S - qS * X + F1 / V * Si     # substrate evolution
+    dDOTdt = KLa * (DOTstar - DOT) - qO * X * H     # oxygen dynamics
+    dAdt   = qA * X - F1 / V * A                    # aetate evolution
+    dVdt   = (F1 - Fout)                            # volume change
+    dIdt = -F1/V*I + (Fi/V)*(If-I)                  #inducer dynamics
+    dPdt = -F1/V*P + ((k1*my*I)/(Ki+I)+k2)* X - k3 * P #protein dynamics
+
+    #the solution of odeint is formulated as a system of an ODE
+    dydt = [dXdt, dSdt, dDOTdt, dAdt, dVdt, dIdt, dPdt]
+
+    return dydt
+
+
+
+
+
+
+

Extract the relevant variables#

+

From the first pass on the model solving we extract the values that correspond to S < 0.5

+
+
+
solution_first_pass = odeint(eColi_first_pass, y0, t)
+
+# Extract S and find tf when S drops below 0.5
+S = solution_first_pass[:, 1]   #Substrate concentraion S
+X = solution_first_pass[:, 0]  # biomass concentration X
+V = solution_first_pass[:, 4]  # volume V
+
+tf, Xbf, Vbf = None, None, None
+for i in range(len(t)):
+    if S[i] < 0.5:
+        tf = t[i]     # Time when S drops below 0.5
+        Xbf = X[i]    # Biomass concentration X at that time
+        Vbf = V[i]    # Volume V at that time
+        break  # Exit the loop once we find tf
+
+print(f"tf: {tf}, Xbf: {Xbf}, Vbf: {Vbf}")
+
+
+
+
+
---------------------------------------------------------------------------
+NameError                                 Traceback (most recent call last)
+Cell In[5], line 1
+----> 1 solution_first_pass = odeint(eColi_first_pass, y0, t)
+      3 # Extract S and find tf when S drops below 0.5
+      4 S = solution_first_pass[:, 1]   #Substrate concentraion S
+
+NameError: name 'odeint' is not defined
+
+
+
+
+
+

Second pass of the model#

+

Here the model will be defined including the Fed-batch dynamics

+
+
+
+

Define the model equations#

+

This cell defines the model equation in Fed-Batch mode.

+
    +
  1. The first step is to assign the variable to simulate to the result vector y.

  2. +
  3. The parameters characteristic for the strain are defined as well as physicochemical constants

  4. +
  5. The algebraic expressions that describe the kinetics of the system are defined

  6. +
  7. The feeding strategy is defined, both for the substrate and inductor. The expression used to calculate the substrate feed (F1) is dependednt on the substrate concentration. The expression used to calculate the inducer feed (Fi) is depedend on the biomass and inducer concentration.

  8. +
  9. The ODE system is defined

  10. +
+
+
+
# define the function; Solve the model a second time, now including the feed eqs.
+def eColi_second_pass(y, t, tf, Xbf, Vbf):
+    X   = y[0]
+    S   = y[1]
+    DOT = y[2]
+    A   = y[3]
+    V   = y[4]
+    I   = y[5]
+    P   = y[6]
+
+
+    # parameters describing the characteristics of the strain
+
+    qAmax   = 1           # max Acetate consumption rate [g/g*h]
+    Kaq     = 0.01        # affinity constant Acetate consumption [g/L]
+    Ksq     = 0.1         # affinity constnat Substrate consumption [g/L]
+    Yam     = 0.2         # yield acetate maintenance [g/g]
+    Yaresp  = 0.2         # yield acetate respiratory [g/g]
+    Yem     = 0.56        # yield excluding maintance [g/g]
+    qSmax   = 1.4         # max glucose uptake rate [g/g*h]
+    Ks      = 0.05        # affinity constant glucose consumption[g/L]
+    qm      = 0.04        # specific maintenance coefficient[g/g*h]
+    Ko      = 1           # Affinity constant, oxygen consumption [g/L]
+    Yosresp = 1.217       # yield from S to X, respiratory [g/g]
+    pAmax   = 1           # max Acetate production rate [g/g*h]
+    Kap     = 10          # affinity constant intracellular acetate production[g/L]
+    Yaof    = 1           # aceate yield in overflow[g/g]
+    Yofm    = Yem         # [g/g]
+    k1=0.32
+    k2=0.00044
+    k3=0
+    Ki=0.55
+
+
+    # physicochemical constants
+    Cs      = 0.391       # ratio of substrate per C in [gC/gS]
+    Cx      = 0.488       # ratio of biomass per C in [gC/gX]
+    H       = 14000       # conversion factor
+    DOTstar = 90
+
+    # algebraic variables
+    qS   = qSmax * S / (S + Ks) * DOT / (DOT + Ko)     # substrate uptake
+    qSof = pAmax * qS / ( qS + Kap ) / Yaof            # overflow substrate conversion
+    pA   = pAmax * qS / (qS + Kap)                     # production of acetate
+    qSox = qS - qSof                                   # substrate uptake excluding overflow
+    qSan =(qSox - qm) * Yem * Cx / Cs                  # anabolic substrate consumption
+    qsA  = qAmax * A / (A + Kaq) * (Ksq / (Ksq + qS))  # acetate consumption
+    qA   = pA - qsA                                    # total acetate equilibrium
+    qO   = Yosresp * (qSox - qSan) + qsA * Yaresp      # oxygen uptake
+
+    # growht rate equation
+    my =(qSox - qm) * Yem + qsA * Yam + qSof * Yofm
+
+
+    #Define the feeding eqautions
+
+    #no feed if t<tf
+    if  t < tf:
+        F1 = 0
+
+    #We start with exponential feed
+    else:
+        if t <= (tf + 3):
+            F1 = (Xbf * Vbf * muset / (Yosresp * Si)) * np.exp(muset * (t - tf))
+        else:
+          #once S exceeds 0.03 we switch to a constant feeding strategy
+            if S >= 0.03:
+                F1 = 43.5
+                if F1 < 0:
+                    F1 = 0
+            else:
+                F1 = (Xbf * Vbf * muset / (Yosresp * Si)) * np.exp(muset * (t - tf))
+
+    # Handle Fi dynamically based on X > 20; Start induction for rhGH production
+    if X < 20:
+        Fi = 0
+    else:
+      #We start with a flow rate of 20 to quicly increase IPTG concentration and start protein production
+       if I < 0.238:
+        Fi = 20
+       # Once the desired concentration of 0.238 (1mM) is reached, the flow rate is calculated such that this concentration remains constant
+       else:
+          Fi = 0.238 * F1 / (If - 0.238)
+
+
+    # differential equation system
+    dXdt   = -F1 / V * X + my * X                   # biomass growth
+    dSdt   = -F1 / V * S - qS * X + F1 / V * Si     # substrate evolution
+    dDOTdt = KLa * (DOTstar - DOT) - qO * X * H     # oxygen dynamics
+    dAdt   = qA * X - F1 / V * A                    # aetate evolution
+    dVdt   = (F1 + Fi - Fout)                       # volume change
+    dIdt = -F1/V*I + (Fi/V)*(If-I)                  #inducer dynamics
+    dPdt = -F1/V*P + (((k1*my*I)/(Ki+I))+k2)* X -k3 *P  #protein dynamics
+
+    #the solution of odeint is formulated as a system of an ODE
+    dydt = [dXdt, dSdt, dDOTdt, dAdt, dVdt, dIdt, dPdt]
+
+    return dydt
+
+
+
+
+
+
+

Extract the solution of the ODE system#

+
+
+
# calling the numerical solver to approximate the integral of the differential equation system
+y = odeint(eColi_second_pass, y0, t, args=(tf, Xbf, Vbf))
+
+
+
+
+
---------------------------------------------------------------------------
+NameError                                 Traceback (most recent call last)
+Cell In[7], line 2
+      1 # calling the numerical solver to approximate the integral of the differential equation system
+----> 2 y = odeint(eColi_second_pass, y0, t, args=(tf, Xbf, Vbf))
+
+NameError: name 'odeint' is not defined
+
+
+
+
+
+
+

Generate the plots#

+

This cell is used to collect all the simulated variables in a dataframe, and add them to 4 different subplots.

+
+
+
def create_plot(t, y):
+        figure = make_subplots(rows=2, cols=2,specs=[[{},{"secondary_y": True}], [{},{}]]) #make figure with 2 subplots
+        #assign simulation results to variable for plotting
+        X = y[:, 0]  # Extract data as 1D arrays
+        S = y[:, 1]
+        DOT = y[:, 2]
+        A = y[:, 3]
+        V = y[:, 4]
+        I = y[:, 5]
+        P = y[:, 6]
+
+
+         #collect all variables to plot in 1st subplot in a dataframe
+        df = pd.DataFrame({'t': t, 'X': X, 'S': S, 'DOT':DOT, 'Acet': A, 'V':V, 'Ind': I, 'P':P})
+         #add the different traces to 1st subplot
+
+        figure.add_trace(go.Scatter(x=df['t'], y=df['X'], name='Biomass'), row=1, col=1)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['S'], name='Substrate'), row=1, col=1)
+
+        figure.add_trace(go.Scatter(x=df['t'], y=df['Acet'], name='Acetate'), row=1, col=2)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['P'], name='protein'), row=1, col=2)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['Ind'], name='inducer'), row=1, col=2, secondary_y=True)
+        figure.add_trace(go.Scatter(x=df['t'], y=df['DOT'], name='DOT'), row=2, col=1)
+
+        figure.update_yaxes(title_text="Concentration (g/L)", row=1, col=1)  # Subplot (1, 1)
+
+        figure.update_yaxes(title_text="Protein and acetate (g/L)", row=1, col=2)  # Subplot (1, 2)
+        figure.update_yaxes(title_text="Inducer (g/L)", row=1, col=2, secondary_y=True) # Subplot (1, 2), secondary y-axis
+        figure.update_yaxes(title_text="Volume (L)", row=2, col=2)  # Subplot (2, 2)
+        figure.update_yaxes(title_text="DOT (%)", row=2, col=1)  # Subplot (2, 1)
+
+         #add the title and axes labels
+        figure.update_layout(title=('Simulaton results'),
+                             xaxis_title='time (h)')
+
+         #add trace to the 2nd subplot
+        figure.append_trace(go.Scatter(x=df['t'], y=df['V'], name='Volume'), row=2, col=2)
+
+
+        return figure
+
+
+
+
+
+
+

Create the figure#

+
+
+
fig = create_plot(t, y) # Call create_plot with the simulation results
+
+fig.show()
+
+
+
+
+
---------------------------------------------------------------------------
+NameError                                 Traceback (most recent call last)
+Cell In[9], line 1
+----> 1 fig = create_plot(t, y) # Call create_plot with the simulation results
+      3 fig.show()
+
+NameError: name 't' is not defined
+
+
+
+
+

E. coli fed-batch simulation +Created on Thu Dec 10 12:14:22 2015

+

@author: Terrance Wilms, Nicolas Cruz, Kevin Stegemann, Rosa Haßfurther Updated Sept 2024

+

Protein production dynamics added by Mariana Albino (October 2024)

+
+ + + + +
+ + + + + + + + +
+ + + + + + +
+
+ + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/WS2/module_2/0_intro_module_2.html b/WS2/module_2/0_intro_module_2.html index 330df65..6c102a5 100755 --- a/WS2/module_2/0_intro_module_2.html +++ b/WS2/module_2/0_intro_module_2.html @@ -57,11 +57,13 @@ + + - + @@ -228,13 +230,41 @@

Model-based Techniques (WS2)

@@ -652,12 +682,12 @@

Model Comparison and Selection

previous

-

Mechanistic modelling

+

E. coli Fed-batch fermentation model, including recombinant protein production

DOCUMENTATION_OPTIONS.pagename = 'WS2/module_2/1_dd_classification'; - + @@ -228,13 +228,41 @@

Model-based Techniques (WS2)

@@ -438,7 +466,9 @@ `); - + @@ -454,6 +484,23 @@

Classification using Data Driven Approaches

@@ -465,7 +512,256 @@

Classification using Data Driven Approaches

Classification using Data Driven Approaches#

-

dsfdsfdsfdsffds

+
+

Objectives#

+

In this exercice, we will work with a dataset provided here. The aim is to perform fault diagnosis on a CSTR based on the measured signals.

+

This datasets contains a set of simulations of a known benchmark in the fault diagnosis for chemical processes community, i.e., the Continuous Stirred Tank Reactor. This system carries an exothermic reaction A -> B, and a feedback loop for controlling the reactor’s temperature. There are 7 measured variables. +These variables are:

+
    +
  • Concentration of A in the inlet flow,

  • +
  • Temperature of the inlet flow,

  • +
  • Temperature of the inlet coolant flow,

  • +
  • Coolant flow-rate,

  • +
  • Concentration of B in the outlet flow,

  • +
  • Temperature of the outlet flow,

  • +
  • Temperature of the outlet coolant flow.

  • +
+

The goal is to predict a set of 12 faults from these 7 variables, measured throughout 200 minutes, at a 1 minute rate. +Additional information on the faults can be found in [Montesuma, 2021, Montesuma et al., 2022].

+
+
+

Exploartory data Analysis#

+

The first step is to perform eploratory data analysis (EDA). This includes:

+
    +
  • loading the data

  • +
  • convert to a suitable format

  • +
  • inspect the dataset (size, content)

  • +
  • clean the dataset

  • +
+
+
+
# import necessairy packages (hint: numpy, sk-learn, matplotlib, etc.)
+import numpy as np
+
+print(np.zeroes(5))
+
+
+
+
+
---------------------------------------------------------------------------
+AttributeError                            Traceback (most recent call last)
+Cell In[1], line 4
+      1 # import necessairy packages (hint: numpy, sk-learn, matplotlib, etc.)
+      2 import numpy as np
+----> 4 print(np.zeroes(5))
+
+File ~\AppData\Local\miniconda3\envs\aimbio\lib\site-packages\numpy\__init__.py:320, in __getattr__(attr)
+    317     from .testing import Tester
+    318     return Tester
+--> 320 raise AttributeError("module {!r} has no attribute "
+    321                      "{!r}".format(__name__, attr))
+
+AttributeError: module 'numpy' has no attribute 'zeroes'
+
+
+
+
+
+
+
# load the dataset (hint....numpy)
+
+# convert to pandas dataframe (much nicer visuals)
+
+
+
+
+
+
+
# inspect the dataset (hint: show the top 10 rows)
+
+
+
+
+

the last 4 columns contain the following data:

+
    +
  • Column 1400 refers to the Fault.

  • +
  • Column 1401 refers to the domain level

  • +
  • Column 1402 refers to the noise level

  • +
  • Column 1403 refers to the reaction order

  • +
+
+
+
# extract the data e.g. fault_label=df.iloc[:,-4]
+
+
+
+
+
+
+
# investigate the uniqueness of the domains
+
+
+
+
+
+
+
# investigate the uniqueness of the faults
+
+
+
+
+
+
+
# investigate the uniqueness of the parameter noise
+
+
+
+
+
+
+
# investigate the uniqueness of the reaction order
+
+
+
+
+
+
+
# investigate how these uniques values are associated in each domain, is there some exlusivity?
+
+#for domain in np.unique(domain_label):
+#    domain_parameters = np.where(domain_label == domain)[0]
+#    print(f'The domain {domain} has a noise level of {np.unique(parameter_noise[domain_parameters]).item()} and a reaction order of {np.unique(reaction_order[domain_parameters]).item()}')
+
+
+
+
+
+
+

Data preprocessing#

+
+
+
# split the data into features and target
+
+
+
+
+
+
+
# split the data into training, validation and test
+
+
+
+
+
+
+

Model evaluation#

+
+
+
# crate a function for evaluating various classification model
+
+#def predictions(classifier, X_train, y_train, X_test, y_test, model_name):
+#    classifier.fit(X_train, y_train)
+#    print(classifier.score(X_test, y_test))
+
+#    cm = confusion_matrix(classifier.predict(X_test), y_test)
+#    sns.heatmap(cm, annot=True, cmap='viridis')
+#    plt.title(f'Confusion matrix for {model_name}')
+#    plt.show()
+
+
+
+
+

With the current data, train and validate the following models:

+
    +
  • SVC

  • +
  • DecisionTree

  • +
  • RF

  • +
  • XGB

  • +
+
+
+
# SVC
+#print('SVC')
+#predictions(SVC(random_state=2207, probability=True), X_train, y_train, X_val, y_val, model_name='SVC')
+
+
+
+
+
+
+

Improving Performance#

+

Inspect the data again, have you noticed anything? +are all variable of the same scale? +If not, then scale them.

+
+
+
# scale the data (hint: StandardScaler())
+
+
+
+
+

Retrain the models. how is the accuracy compared to previousely?

+
+
+

Dimentionality Reduction#

+

A total of 1400 features are used. A question would be, is that really necessairy? +Try to use PCA to reduce the number of features. +and hpow much reduction is acceptable (see scree tree)

+
+
+
# Perform PCA (e.g. with variance threshhold of 0.956)
+
+
+
+
+
+
+
# find the threshold and select the data
+
+
+
+
+
+
+
# Scale the data (again)
+
+
+
+
+
+
+
# retrain the models
+
+
+
+
+
+
+

Puhsing performance even further (extra)#

+

the models used so far relied on the default hyperparameters. +Investigate the documentation of one model and create a grid search. +Find the best combination of hyperparameters

+
+

Notice:#

+

Thanks to XXX and XXXX for contributing to the development of the solution

+
+
+
+[HdHPK14] +

Christopher Ramsay Holdgraf, Wendy de Heer, Brian N. Pasley, and Robert T. Knight. Evidence for Predictive Coding in Human Auditory Cortex. In International Conference on Cognitive Neuroscience. Brisbane, Australia, Australia, 2014. Frontiers in Neuroscience.

+
+
+[Mon21] +

Eduardo Fernandes Montesuma. Cross-Domain Fault Diagnosis through Optimal Transport. Bachelor's Thesis, Universidade Federal do Ceará, 2021.

+
+
+[MMCM22] +

Eduardo Fernandes Montesuma, Michela Mulas, Francesco Corona, and Fred Maurice Ngole Mboula. Cross-domain fault diagnosis through optimal transport for a cstr process. IFAC-PapersOnLine, 55(7):946–951, 2022.

+
+
+
+
+
- + @@ -508,7 +804,7 @@

Classification using Data Driven Approaches +