├── .gitignore ├── LICENSE ├── README └── src ├── Matlabout_dyn.csv ├── PyADM1.py ├── digester_influent.csv ├── digester_initial.csv ├── dynamic_out.csv └── ringtest.csv /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Peyman Sadrimajd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | PyADM1: a Python implementation of the Anaerobic Digestion Model number 1 2 | 3 | PyADM1 is a modeling and simulation tool for anaerobic digestion reactors (a.k.a biogas plants). 4 | 5 | In order to use PyADM1, you need Python v3 and a few Python modules installed (copy, SciPy, NumPy). scipy.integrate.solve_ivp is used for solving the mdoel equations. 6 | 7 | To run PyADM1 please download the src folder which contains the Python source code and the example influent/effluent files and ring test files. 8 | 9 | After characterising the influent (feed) to the reactor and analyzing the inoculum (or initial conditions) of the reactor you can modify the influent states, initial states, simulation time, time steps, and parameters based on your data or simulation scenario. 10 | Then, you run the PyADM1 code and it will simulate dynamic operation of you reactor and print the results in dynamic_out.csv at the end of simulation time. 11 | 12 | PyADM1 is based on ADM1 implementation in Benchmark Simulation Model number 2 (BSM2) and has beed ring tested with the BSM2 implementation in Matlab. For more information about BSM2 and ADM1 model, please follow: 13 | 14 | C. Ros ́en, U. Jeppsson, Aspects on adm1 implementation within the bsm2 framework, Department of Industrial Electrical Engineering and Automation, Lund University, Lund, Sweden (2006) 1–35. 15 | 16 | please cite this software in your work: 17 | 18 | @article {Sadrimajd2021.03.03.433746, 19 | author = {Sadrimajd, Peyman and Mannion, Patrick and Howley, Enda and Lens, Piet N. L.}, 20 | title = {PyADM1: a Python implementation of Anaerobic Digestion Model No. 1}, 21 | elocation-id = {2021.03.03.433746}, 22 | year = {2021}, 23 | doi = {10.1101/2021.03.03.433746}, 24 | URL = {https://www.biorxiv.org/content/early/2021/03/04/2021.03.03.433746}, 25 | eprint = {https://www.biorxiv.org/content/early/2021/03/04/2021.03.03.433746.full.pdf}, 26 | journal = {bioRxiv} 27 | } 28 | 29 | --------------------------------------------------------------------------------------- 30 | 31 | LICENSE 32 | 33 | MIT License 34 | 35 | Copyright (c) 2021 Peyman Sadrimajd 36 | 37 | Permission is hereby granted, free of charge, to any person obtaining a copy 38 | of this software and associated documentation files (the "Software"), to deal 39 | in the Software without restriction, including without limitation the rights 40 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 41 | copies of the Software, and to permit persons to whom the Software is 42 | furnished to do so, subject to the following conditions: 43 | 44 | The above copyright notice and this permission notice shall be included in all 45 | copies or substantial portions of the Software. 46 | 47 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 48 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 49 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 50 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 51 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 52 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 53 | SOFTWARE. 54 | 55 | -------------------------------------------------------------------------------- /src/PyADM1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import scipy.integrate 3 | import copy 4 | import pandas as pd 5 | import matplotlib.pyplot as plt 6 | from scipy import integrate 7 | 8 | 9 | ## unit for each parameter is commented after it is declared (inline) 10 | ## if the suggested value for the parameter is different - 11 | ## The original default value from the original ADM1 report by Batstone et al (2002), is commented after each unit (inline) 12 | 13 | ##constant definition from the Rosen et al (2006) BSM2 report 14 | R = 0.083145 #bar.M^-1.K^-1 15 | T_base = 298.15 #K 16 | p_atm = 1.013 #bar 17 | T_op = 308.15 #k ##T_ad #=35 C 18 | 19 | ##parameter definition from the Rosen et al (2006) BSM2 report bmadm1_report 20 | # Stoichiometric parameter 21 | f_sI_xc = 0.1 22 | f_xI_xc = 0.2 23 | f_ch_xc = 0.2 24 | f_pr_xc = 0.2 25 | f_li_xc = 0.3 26 | N_xc = 0.0376 / 14 27 | N_I = 0.06 / 14 #kmole N.kg^-1COD 28 | N_aa = 0.007 #kmole N.kg^-1COD 29 | C_xc = 0.02786 #kmole C.kg^-1COD 30 | C_sI = 0.03 #kmole C.kg^-1COD 31 | C_ch = 0.0313 #kmole C.kg^-1COD 32 | C_pr = 0.03 #kmole C.kg^-1COD 33 | C_li = 0.022 #kmole C.kg^-1COD 34 | C_xI = 0.03 #kmole C.kg^-1COD 35 | C_su = 0.0313 #kmole C.kg^-1COD 36 | C_aa = 0.03 #kmole C.kg^-1COD 37 | f_fa_li = 0.95 38 | C_fa = 0.0217 #kmole C.kg^-1COD 39 | f_h2_su = 0.19 40 | f_bu_su = 0.13 41 | f_pro_su = 0.27 42 | f_ac_su = 0.41 43 | N_bac = 0.08 / 14 #kmole N.kg^-1COD 44 | C_bu = 0.025 #kmole C.kg^-1COD 45 | C_pro = 0.0268 #kmole C.kg^-1COD 46 | C_ac = 0.0313 #kmole C.kg^-1COD 47 | C_bac = 0.0313 #kmole C.kg^-1COD 48 | Y_su = 0.1 49 | f_h2_aa = 0.06 50 | f_va_aa = 0.23 51 | f_bu_aa = 0.26 52 | f_pro_aa = 0.05 53 | f_ac_aa = 0.40 54 | C_va = 0.024 #kmole C.kg^-1COD 55 | Y_aa = 0.08 56 | Y_fa = 0.06 57 | Y_c4 = 0.06 58 | Y_pro = 0.04 59 | C_ch4 = 0.0156 #kmole C.kg^-1COD 60 | Y_ac = 0.05 61 | Y_h2 = 0.06 62 | 63 | 64 | # Biochemical parameter values from the Rosen et al (2006) BSM2 report 65 | k_dis = 0.5 #d^-1 66 | k_hyd_ch = 10 #d^-1 67 | k_hyd_pr = 10 #d^-1 68 | k_hyd_li = 10 #d^-1 69 | K_S_IN = 10 ** -4 #M 70 | k_m_su = 30 #d^-1 71 | K_S_su = 0.5 #kgCOD.m^-3 72 | pH_UL_aa = 5.5 73 | pH_LL_aa = 4 74 | k_m_aa = 50 #d^-1 75 | K_S_aa = 0.3 ##kgCOD.m^-3 76 | k_m_fa = 6 #d^-1 77 | K_S_fa = 0.4 #kgCOD.m^-3 78 | K_I_h2_fa = 5 * 10 ** -6 #kgCOD.m^-3 79 | k_m_c4 = 20 #d^-1 80 | K_S_c4 = 0.2 #kgCOD.m^-3 81 | K_I_h2_c4 = 10 ** -5 #kgCOD.m^-3 82 | k_m_pro = 13 #d^-1 83 | K_S_pro = 0.1 #kgCOD.m^-3 84 | K_I_h2_pro = 3.5 * 10 ** -6 #kgCOD.m^-3 85 | k_m_ac = 8 #kgCOD.m^-3 86 | K_S_ac = 0.15 #kgCOD.m^-3 87 | K_I_nh3 = 0.0018 #M 88 | pH_UL_ac = 7 89 | pH_LL_ac = 6 90 | k_m_h2 = 35 #d^-1 91 | K_S_h2 = 7 * 10 ** -6 #kgCOD.m^-3 92 | pH_UL_h2 = 6 93 | pH_LL_h2 = 5 94 | k_dec_X_su = 0.02 #d^-1 95 | k_dec_X_aa = 0.02 #d^-1 96 | k_dec_X_fa = 0.02 #d^-1 97 | k_dec_X_c4 = 0.02 #d^-1 98 | k_dec_X_pro = 0.02 #d^-1 99 | k_dec_X_ac = 0.02 #d^-1 100 | k_dec_X_h2 = 0.02 #d^-1 101 | ## M is kmole m^-3 102 | 103 | # Physico-chemical parameter values from the Rosen et al (2006) BSM2 report 104 | T_ad = 308.15 #K 105 | 106 | 107 | K_w = 10 ** -14.0 * np.exp((55900 / (100 * R)) * (1 / T_base - 1 / T_ad)) #M #2.08 * 10 ^ -14 108 | 109 | K_a_va = 10 ** -4.86 #M ADM1 value = 1.38 * 10 ^ -5 110 | K_a_bu = 10 ** -4.82 #M #1.5 * 10 ^ -5 111 | K_a_pro = 10 ** -4.88 #M #1.32 * 10 ^ -5 112 | K_a_ac = 10 ** -4.76 #M #1.74 * 10 ^ -5 113 | 114 | 115 | K_a_co2 = 10 ** -6.35 * np.exp((7646 / (100 * R)) * (1 / T_base - 1 / T_ad)) #M #4.94 * 10 ^ -7 116 | K_a_IN = 10 ** -9.25 * np.exp((51965 / (100 * R)) * (1 / T_base - 1 / T_ad)) #M #1.11 * 10 ^ -9 117 | 118 | 119 | k_A_B_va = 10 ** 10 #M^-1 * d^-1 120 | k_A_B_bu = 10 ** 10 #M^-1 * d^-1 121 | k_A_B_pro = 10 ** 10 #M^-1 * d^-1 122 | k_A_B_ac = 10 ** 10 #M^-1 * d^-1 123 | k_A_B_co2 = 10 ** 10 #M^-1 * d^-1 124 | k_A_B_IN = 10 ** 10 #M^-1 * d^-1 125 | 126 | 127 | p_gas_h2o = 0.0313 * np.exp(5290 * (1 / T_base - 1 / T_ad)) #bar #0.0557 128 | k_p = 5 * 10 ** 4 #m^3.d^-1.bar^-1 #only for BSM2 AD conditions, recalibrate for other AD cases #gas outlet friction 129 | k_L_a = 200.0 #d^-1 130 | K_H_co2 = 0.035 * np.exp((-19410 / (100 * R))* (1 / T_base - 1 / T_ad)) #Mliq.bar^-1 #0.0271 131 | K_H_ch4 = 0.0014 * np.exp((-14240 / (100 * R)) * (1 / T_base - 1 / T_ad)) #Mliq.bar^-1 #0.00116 132 | K_H_h2 = 7.8 * 10 ** -4 * np.exp(-4180 / (100 * R) * (1 / T_base - 1 / T_ad)) #Mliq.bar^-1 #7.38*10^-4 133 | 134 | # Physical parameter values used in BSM2 from the Rosen et al (2006) BSM2 report 135 | V_liq = 3400 #m^3 136 | V_gas = 300 #m^3 137 | V_ad = V_liq + V_gas #m^-3 138 | 139 | # reading influent and initial condition data from csv files 140 | influent_state = pd.read_csv("digester_influent.csv") 141 | initial_state = pd.read_csv("digester_initial.csv") 142 | 143 | # Function to set influent values for influent state variables at each simulation step 144 | def setInfluent(i): 145 | global S_su_in, S_aa_in, S_fa_in, S_va_in, S_bu_in, S_pro_in, S_ac_in, S_h2_in,S_ch4_in, S_IC_in, S_IN_in, S_I_in,X_xc_in, X_ch_in,X_pr_in,X_li_in,X_su_in,X_aa_in,X_fa_in,X_c4_in,X_pro_in,X_ac_in,X_h2_in,X_I_in,S_cation_in,S_anion_in 146 | ##variable definition 147 | # Input values (influent/feed) 148 | S_su_in = influent_state['S_su'][i] #kg COD.m^-3 149 | S_aa_in = influent_state['S_aa'][i] #kg COD.m^-3 150 | S_fa_in = influent_state['S_fa'][i] #kg COD.m^-3 151 | S_va_in = influent_state['S_va'][i] #kg COD.m^-3 152 | S_bu_in = influent_state['S_bu'][i] #kg COD.m^-3 153 | S_pro_in = influent_state['S_pro'][i] #kg COD.m^-3 154 | S_ac_in = influent_state['S_ac'][i] #kg COD.m^-3 155 | S_h2_in = influent_state['S_h2'][i] #kg COD.m^-3 156 | S_ch4_in = influent_state['S_ch4'][i] #kg COD.m^-3 157 | S_IC_in = influent_state['S_IC'][i] #kmole C.m^-3 158 | S_IN_in = influent_state['S_IN'][i] #kmole N.m^-3 159 | S_I_in = influent_state['S_I'][i] #kg COD.m^-3 160 | 161 | X_xc_in = influent_state['X_xc'][i] #kg COD.m^-3 162 | X_ch_in = influent_state['X_ch'][i] #kg COD.m^-3 163 | X_pr_in = influent_state['X_pr'][i] #kg COD.m^-3 164 | X_li_in = influent_state['X_li'][i] #kg COD.m^-3 165 | X_su_in = influent_state['X_su'][i] #kg COD.m^-3 166 | X_aa_in = influent_state['X_aa'][i] #kg COD.m^-3 167 | X_fa_in = influent_state['X_fa'][i] #kg COD.m^-3 168 | X_c4_in = influent_state['X_c4'][i] #kg COD.m^-3 169 | X_pro_in = influent_state['X_pro'][i] #kg COD.m^-3 170 | X_ac_in = influent_state['X_ac'][i] #kg COD.m^-3 171 | X_h2_in = influent_state['X_h2'][i] #kg COD.m^-3 172 | X_I_in = influent_state['X_I'][i] #kg COD.m^-3 173 | 174 | S_cation_in = influent_state['S_cation'][i] #kmole.m^-3 175 | S_anion_in = influent_state['S_anion'][i] #kmole.m^-3 176 | 177 | 178 | # initiate variables (initial values for the reactor state at the initial time (t0) 179 | S_su = initial_state['S_su'][0] #kg COD.m^-3 monosaccharides 180 | S_aa = initial_state['S_aa'][0] #kg COD.m^-3 amino acids 181 | S_fa = initial_state['S_fa'][0] #kg COD.m^-3 total long chain fatty acids 182 | S_va = initial_state['S_va'][0] #kg COD.m^-3 total valerate 183 | S_bu = initial_state['S_bu'][0] #kg COD.m^-3 total butyrate 184 | S_pro = initial_state['S_pro'][0] #kg COD.m^-3 total propionate 185 | S_ac = initial_state['S_ac'][0] #kg COD.m^-3 total acetate 186 | S_h2 = initial_state['S_h2'][0] #kg COD.m^-3 hydrogen gas 187 | S_ch4 = initial_state['S_ch4'][0] #kg COD.m^-3 methane gas 188 | S_IC = initial_state['S_IC'][0] #kmole C.m^-3 inorganic carbon 189 | S_IN = initial_state['S_IN'][0] #kmole N.m^-3 inorganic nitrogen 190 | S_I = initial_state['S_I'][0] #kg COD.m^-3 soluble inerts 191 | 192 | X_xc = initial_state['X_xc'][0] #kg COD.m^-3 composites 193 | X_ch = initial_state['X_ch'][0] #kg COD.m^-3 carbohydrates 194 | X_pr = initial_state['X_pr'][0] #kg COD.m^-3 proteins 195 | X_li = initial_state['X_li'][0] #kg COD.m^-3 lipids 196 | X_su = initial_state['X_su'][0] #kg COD.m^-3 sugar degraders 197 | X_aa = initial_state['X_aa'][0] #kg COD.m^-3 amino acid degraders 198 | X_fa = initial_state['X_fa'][0] #kg COD.m^-3 LCFA degraders 199 | X_c4 = initial_state['X_c4'][0] #kg COD.m^-3 valerate and butyrate degraders 200 | X_pro = initial_state['X_pro'][0] #kg COD.m^-3 propionate degraders 201 | X_ac = initial_state['X_ac'][0] #kg COD.m^-3 acetate degraders 202 | X_h2 = initial_state['X_h2'][0] #kg COD.m^-3 hydrogen degraders 203 | X_I = initial_state['X_I'][0] #kg COD.m^-3 particulate inerts 204 | 205 | S_cation = initial_state['S_cation'][0] #kmole.m^-3 cations (metallic ions, strong base) 206 | S_anion = initial_state['S_anion'][0] #kmole.m^-3 anions (metallic ions, strong acid) 207 | 208 | pH = 7.4655377 # initial pH 209 | S_H_ion = initial_state['S_H_ion'][0] #kmole H.m^-3 210 | S_va_ion = initial_state['S_va_ion'][0] #kg COD.m^-3 valerate 211 | S_bu_ion = initial_state['S_bu_ion'][0] #kg COD.m^-3 butyrate 212 | S_pro_ion = initial_state['S_pro_ion'][0] #kg COD.m^-3 propionate 213 | S_ac_ion = initial_state['S_ac_ion'][0] #kg COD.m^-3 acetate 214 | S_hco3_ion = initial_state['S_hco3_ion'][0] #kmole C.m^-3 bicarbonate 215 | S_nh3 = initial_state['S_nh3'][0] #kmole N.m^-3 ammonia 216 | S_nh4_ion = 0.0041 #kmole N.m^-3 the initial value is from Rosen et al (2006) BSM2 report and it is calculated further down and does not need to be initiated 217 | S_co2 = 0.14 #kmole C.m^-3 the initial value is from Rosen et al (2006) BSM2 report and it is calculated further down and does not need to be initiated 218 | S_gas_h2 = initial_state['S_gas_h2'][0] #kg COD.m^-3 hydrogen concentration in gas phase 219 | S_gas_ch4 = initial_state['S_gas_ch4'][0] #kg COD.m^-3 methane concentration in gas phase 220 | S_gas_co2 = initial_state['S_gas_co2'][0]#kmole C.m^-3 carbon dioxide concentration in gas phas 221 | 222 | # related to pH inhibition taken from BSM2 report, they are global variables to avoid repeating them in DAE part 223 | K_pH_aa = (10 ** (-1 * (pH_LL_aa + pH_UL_aa) / 2.0)) 224 | nn_aa = (3.0 / (pH_UL_aa - pH_LL_aa)) #we need a differece between N_aa and n_aa to avoid typos and nn_aa refers to the n_aa in BSM2 report 225 | K_pH_ac = (10 ** (-1 * (pH_LL_ac + pH_UL_ac) / 2.0)) 226 | n_ac = (3.0 / (pH_UL_ac - pH_LL_ac)) 227 | K_pH_h2 = (10 ** (-1 * (pH_LL_h2 + pH_UL_h2) / 2.0)) 228 | n_h2 = (3.0 / (pH_UL_h2 - pH_LL_h2)) 229 | 230 | #pH equation 231 | pH = - np.log10(S_H_ion) 232 | 233 | setInfluent(0) #setting the influent for the initial time (t0) to be ready for the start of the simulation 234 | q_ad = 178.4674 #m^3.d^-1 initial flow rate (can be modified during the simulation by the control algorithm) 235 | 236 | 237 | state_zero = [S_su, 238 | S_aa, 239 | S_fa, 240 | S_va, 241 | S_bu, 242 | S_pro, 243 | S_ac, 244 | S_h2, 245 | S_ch4, 246 | S_IC, 247 | S_IN, 248 | S_I, 249 | X_xc, 250 | X_ch, 251 | X_pr, 252 | X_li, 253 | X_su, 254 | X_aa, 255 | X_fa, 256 | X_c4, 257 | X_pro, 258 | X_ac, 259 | X_h2, 260 | X_I, 261 | S_cation, 262 | S_anion, 263 | S_H_ion, 264 | S_va_ion, 265 | S_bu_ion, 266 | S_pro_ion, 267 | S_ac_ion, 268 | S_hco3_ion, 269 | S_co2, 270 | S_nh3, 271 | S_nh4_ion, 272 | S_gas_h2, 273 | S_gas_ch4, 274 | S_gas_co2] 275 | 276 | state_input = [S_su_in, 277 | S_aa_in, 278 | S_fa_in, 279 | S_va_in, 280 | S_bu_in, 281 | S_pro_in, 282 | S_ac_in, 283 | S_h2_in, 284 | S_ch4_in, 285 | S_IC_in, 286 | S_IN_in, 287 | S_I_in, 288 | X_xc_in, 289 | X_ch_in, 290 | X_pr_in, 291 | X_li_in, 292 | X_su_in, 293 | X_aa_in, 294 | X_fa_in, 295 | X_c4_in, 296 | X_pro_in, 297 | X_ac_in, 298 | X_h2_in, 299 | X_I_in, 300 | S_cation_in, 301 | S_anion_in] 302 | 303 | 304 | # Function for calulating the derivatives related to ADM1 system of equations from the Rosen et al (2006) BSM2 report 305 | def ADM1_ODE(t, state_zero): 306 | global S_nh4_ion, S_co2, p_gas, q_gas, q_ch4 307 | S_su = state_zero[0] 308 | S_aa = state_zero[1] 309 | S_fa = state_zero[2] 310 | S_va = state_zero[3] 311 | S_bu = state_zero[4] 312 | S_pro = state_zero[5] 313 | S_ac = state_zero[6] 314 | S_h2 = state_zero[7] 315 | S_ch4 = state_zero[8] 316 | S_IC = state_zero[9] 317 | S_IN = state_zero[10] 318 | S_I = state_zero[11] 319 | X_xc = state_zero[12] 320 | X_ch = state_zero[13] 321 | X_pr = state_zero[14] 322 | X_li = state_zero[15] 323 | X_su = state_zero[16] 324 | X_aa = state_zero[17] 325 | X_fa = state_zero[18] 326 | X_c4 = state_zero[19] 327 | X_pro = state_zero[20] 328 | X_ac = state_zero[21] 329 | X_h2 = state_zero[22] 330 | X_I = state_zero[23] 331 | S_cation = state_zero[24] 332 | S_anion = state_zero[25] 333 | S_H_ion = state_zero[26] 334 | S_va_ion = state_zero[27] 335 | S_bu_ion = state_zero[28] 336 | S_pro_ion = state_zero[29] 337 | S_ac_ion = state_zero[30] 338 | S_hco3_ion = state_zero[31] 339 | S_co2 = state_zero[32] 340 | S_nh3 = state_zero[33] 341 | S_nh4_ion = state_zero[34] 342 | S_gas_h2 = state_zero[35] 343 | S_gas_ch4 = state_zero[36] 344 | S_gas_co2 = state_zero[37] 345 | 346 | 347 | S_su_in = state_input[0] 348 | S_aa_in = state_input[1] 349 | S_fa_in = state_input[2] 350 | S_va_in = state_input[3] 351 | S_bu_in = state_input[4] 352 | S_pro_in = state_input[5] 353 | S_ac_in = state_input[6] 354 | S_h2_in = state_input[7] 355 | S_ch4_in = state_input[8] 356 | S_IC_in = state_input[9] 357 | S_IN_in = state_input[10] 358 | S_I_in = state_input[11] 359 | X_xc_in = state_input[12] 360 | X_ch_in = state_input[13] 361 | X_pr_in = state_input[14] 362 | X_li_in = state_input[15] 363 | X_su_in = state_input[16] 364 | X_aa_in = state_input[17] 365 | X_fa_in = state_input[18] 366 | X_c4_in = state_input[19] 367 | X_pro_in = state_input[20] 368 | X_ac_in = state_input[21] 369 | X_h2_in = state_input[22] 370 | X_I_in = state_input[23] 371 | S_cation_in = state_input[24] 372 | S_anion_in = state_input[25] 373 | 374 | S_nh4_ion = (S_IN - S_nh3) 375 | 376 | S_co2 = (S_IC - S_hco3_ion) 377 | 378 | I_pH_aa = ((K_pH_aa ** nn_aa) / (S_H_ion ** nn_aa + K_pH_aa ** nn_aa)) 379 | I_pH_ac = ((K_pH_ac ** n_ac) / (S_H_ion ** n_ac + K_pH_ac ** n_ac)) 380 | I_pH_h2 = ((K_pH_h2 ** n_h2) / (S_H_ion ** n_h2 + K_pH_h2 ** n_h2)) 381 | I_IN_lim = (1 / (1 + (K_S_IN / S_IN))) 382 | I_h2_fa = (1 / (1 + (S_h2 / K_I_h2_fa))) 383 | I_h2_c4 = (1 / (1 + (S_h2 / K_I_h2_c4))) 384 | I_h2_pro = (1 / (1 + (S_h2 / K_I_h2_pro))) 385 | I_nh3 = (1 / (1 + (S_nh3 / K_I_nh3))) 386 | 387 | I_5 = (I_pH_aa * I_IN_lim) 388 | I_6 = I_5 389 | I_7 = (I_pH_aa * I_IN_lim * I_h2_fa) 390 | I_8 = (I_pH_aa * I_IN_lim * I_h2_c4) 391 | I_9 = I_8 392 | I_10 = (I_pH_aa * I_IN_lim * I_h2_pro) 393 | I_11 = (I_pH_ac * I_IN_lim * I_nh3) 394 | I_12 = (I_pH_h2 * I_IN_lim) 395 | 396 | 397 | 398 | # biochemical process rates from Rosen et al (2006) BSM2 report 399 | Rho_1 = (k_dis * X_xc) # Disintegration 400 | Rho_2 = (k_hyd_ch * X_ch) # Hydrolysis of carbohydrates 401 | Rho_3 = (k_hyd_pr * X_pr) # Hydrolysis of proteins 402 | Rho_4 = (k_hyd_li * X_li) # Hydrolysis of lipids 403 | Rho_5 = k_m_su * S_su / (K_S_su + S_su) * X_su * I_5 # Uptake of sugars 404 | Rho_6 = (k_m_aa * (S_aa / (K_S_aa + S_aa)) * X_aa * I_6) # Uptake of amino-acids 405 | Rho_7 = (k_m_fa * (S_fa / (K_S_fa + S_fa)) * X_fa * I_7) # Uptake of LCFA (long-chain fatty acids) 406 | Rho_8 = (k_m_c4 * (S_va / (K_S_c4 + S_va )) * X_c4 * (S_va / (S_bu + S_va + 1e-6)) * I_8) # Uptake of valerate 407 | Rho_9 = (k_m_c4 * (S_bu / (K_S_c4 + S_bu )) * X_c4 * (S_bu / (S_bu + S_va + 1e-6)) * I_9) # Uptake of butyrate 408 | Rho_10 = (k_m_pro * (S_pro / (K_S_pro + S_pro)) * X_pro * I_10) # Uptake of propionate 409 | Rho_11 = (k_m_ac * (S_ac / (K_S_ac + S_ac)) * X_ac * I_11) # Uptake of acetate 410 | Rho_12 = (k_m_h2 * (S_h2 / (K_S_h2 + S_h2)) * X_h2 * I_12) # Uptake of hydrogen 411 | Rho_13 = (k_dec_X_su * X_su) # Decay of X_su 412 | Rho_14 = (k_dec_X_aa * X_aa) # Decay of X_aa 413 | Rho_15 = (k_dec_X_fa * X_fa) # Decay of X_fa 414 | Rho_16 = (k_dec_X_c4 * X_c4) # Decay of X_c4 415 | Rho_17 = (k_dec_X_pro * X_pro) # Decay of X_pro 416 | Rho_18 = (k_dec_X_ac * X_ac) # Decay of X_ac 417 | Rho_19 = (k_dec_X_h2 * X_h2) # Decay of X_h2 418 | 419 | # acid-base rates for the BSM2 ODE implementation from Rosen et al (2006) BSM2 report 420 | Rho_A_4 = (k_A_B_va * (S_va_ion * (K_a_va + S_H_ion) - K_a_va * S_va)) 421 | Rho_A_5 = (k_A_B_bu * (S_bu_ion * (K_a_bu + S_H_ion) - K_a_bu * S_bu)) 422 | Rho_A_6 = (k_A_B_pro * (S_pro_ion * (K_a_pro + S_H_ion) - K_a_pro * S_pro)) 423 | Rho_A_7 = (k_A_B_ac * (S_ac_ion * (K_a_ac + S_H_ion) - K_a_ac * S_ac)) 424 | Rho_A_10 = (k_A_B_co2 * (S_hco3_ion * (K_a_co2 + S_H_ion) - K_a_co2 * S_IC)) 425 | Rho_A_11 = (k_A_B_IN * (S_nh3 * (K_a_IN + S_H_ion) - K_a_IN * S_IN)) 426 | 427 | # gas phase algebraic equations from Rosen et al (2006) BSM2 report 428 | p_gas_h2 = (S_gas_h2 * R * T_op / 16) 429 | p_gas_ch4 = (S_gas_ch4 * R * T_op / 64) 430 | p_gas_co2 = (S_gas_co2 * R * T_op) 431 | 432 | 433 | p_gas= (p_gas_h2 + p_gas_ch4 + p_gas_co2 + p_gas_h2o) 434 | q_gas = (k_p * (p_gas- p_atm)) 435 | if q_gas < 0: q_gas = 0 436 | 437 | q_ch4 = q_gas * (p_gas_ch4/p_gas) # methane flow 438 | 439 | # gas transfer rates from Rosen et al (2006) BSM2 report 440 | Rho_T_8 = (k_L_a * (S_h2 - 16 * K_H_h2 * p_gas_h2)) 441 | Rho_T_9 = (k_L_a * (S_ch4 - 64 * K_H_ch4 * p_gas_ch4)) 442 | Rho_T_10 = (k_L_a * (S_co2 - K_H_co2 * p_gas_co2)) 443 | 444 | ##differential equaitons from Rosen et al (2006) BSM2 report 445 | # differential equations 1 to 12 (soluble matter) 446 | diff_S_su = q_ad / V_liq * (S_su_in - S_su) + Rho_2 + (1 - f_fa_li) * Rho_4 - Rho_5 # eq1 447 | 448 | diff_S_aa = q_ad / V_liq * (S_aa_in - S_aa) + Rho_3 - Rho_6 # eq2 449 | 450 | diff_S_fa = q_ad / V_liq * (S_fa_in - S_fa) + (f_fa_li * Rho_4) - Rho_7 # eq3 451 | 452 | diff_S_va = q_ad / V_liq * (S_va_in - S_va) + (1 - Y_aa) * f_va_aa * Rho_6 - Rho_8 # eq4 453 | 454 | diff_S_bu = q_ad / V_liq * (S_bu_in - S_bu) + (1 - Y_su) * f_bu_su * Rho_5 + (1 - Y_aa) * f_bu_aa * Rho_6 - Rho_9 # eq5 455 | 456 | diff_S_pro = q_ad / V_liq * (S_pro_in - S_pro) + (1 - Y_su) * f_pro_su * Rho_5 + (1 - Y_aa) * f_pro_aa * Rho_6 + (1 - Y_c4) * 0.54 * Rho_8 - Rho_10 # eq6 457 | 458 | diff_S_ac = q_ad / V_liq * (S_ac_in - S_ac) + (1 - Y_su) * f_ac_su * Rho_5 + (1 - Y_aa) * f_ac_aa * Rho_6 + (1 - Y_fa) * 0.7 * Rho_7 + (1 - Y_c4) * 0.31 * Rho_8 + (1 - Y_c4) * 0.8 * Rho_9 + (1 - Y_pro) * 0.57 * Rho_10 - Rho_11 # eq7 459 | 460 | #diff_S_h2 is defined with DAE paralel equaitons 461 | 462 | diff_S_ch4 = q_ad / V_liq * (S_ch4_in - S_ch4) + (1 - Y_ac) * Rho_11 + (1 - Y_h2) * Rho_12 - Rho_T_9 # eq9 463 | 464 | 465 | ## eq10 start## 466 | s_1 = (-1 * C_xc + f_sI_xc * C_sI + f_ch_xc * C_ch + f_pr_xc * C_pr + f_li_xc * C_li + f_xI_xc * C_xI) 467 | s_2 = (-1 * C_ch + C_su) 468 | s_3 = (-1 * C_pr + C_aa) 469 | s_4 = (-1 * C_li + (1 - f_fa_li) * C_su + f_fa_li * C_fa) 470 | s_5 = (-1 * C_su + (1 - Y_su) * (f_bu_su * C_bu + f_pro_su * C_pro + f_ac_su * C_ac) + Y_su * C_bac) 471 | s_6 = (-1 * C_aa + (1 - Y_aa) * (f_va_aa * C_va + f_bu_aa * C_bu + f_pro_aa * C_pro + f_ac_aa * C_ac) + Y_aa * C_bac) 472 | s_7 = (-1 * C_fa + (1 - Y_fa) * 0.7 * C_ac + Y_fa * C_bac) 473 | s_8 = (-1 * C_va + (1 - Y_c4) * 0.54 * C_pro + (1 - Y_c4) * 0.31 * C_ac + Y_c4 * C_bac) 474 | s_9 = (-1 * C_bu + (1 - Y_c4) * 0.8 * C_ac + Y_c4 * C_bac) 475 | s_10 = (-1 * C_pro + (1 - Y_pro) * 0.57 * C_ac + Y_pro * C_bac) 476 | s_11 = (-1 * C_ac + (1 - Y_ac) * C_ch4 + Y_ac * C_bac) 477 | s_12 = ((1 - Y_h2) * C_ch4 + Y_h2 * C_bac) 478 | s_13 = (-1 * C_bac + C_xc) 479 | 480 | Sigma = (s_1 * Rho_1 + s_2 * Rho_2 + s_3 * Rho_3 + s_4 * Rho_4 + s_5 * Rho_5 + s_6 * Rho_6 + s_7 * Rho_7 + s_8 * Rho_8 + s_9 * Rho_9 + s_10 * Rho_10 + s_11 * Rho_11 + s_12 * Rho_12 + s_13 * (Rho_13 + Rho_14 + Rho_15 + Rho_16 + Rho_17 + Rho_18 + Rho_19)) 481 | 482 | diff_S_IC = q_ad / V_liq * (S_IC_in - S_IC) - Sigma - Rho_T_10 483 | ## eq10 end## 484 | 485 | 486 | 487 | diff_S_IN = q_ad / V_liq * (S_IN_in - S_IN) + (N_xc - f_xI_xc * N_I - f_sI_xc * N_I-f_pr_xc * N_aa) * Rho_1 - Y_su * N_bac * Rho_5 + (N_aa - Y_aa * N_bac) * Rho_6 - Y_fa * N_bac * Rho_7 - Y_c4 * N_bac * Rho_8 - Y_c4 * N_bac * Rho_9 - Y_pro * N_bac * Rho_10 - Y_ac * N_bac * Rho_11 - Y_h2 * N_bac * Rho_12 + (N_bac - N_xc) * (Rho_13 + Rho_14 + Rho_15 + Rho_16 + Rho_17 + Rho_18 + Rho_19) # eq11 488 | 489 | 490 | diff_S_I = q_ad / V_liq * (S_I_in - S_I) + f_sI_xc * Rho_1 # eq12 491 | 492 | 493 | # Differential equations 13 to 24 (particulate matter) 494 | diff_X_xc = q_ad / V_liq * (X_xc_in - X_xc) - Rho_1 + Rho_13 + Rho_14 + Rho_15 + Rho_16 + Rho_17 + Rho_18 + Rho_19 # eq13 495 | 496 | diff_X_ch = q_ad / V_liq * (X_ch_in - X_ch) + f_ch_xc * Rho_1 - Rho_2 # eq14 497 | 498 | diff_X_pr = q_ad / V_liq * (X_pr_in - X_pr) + f_pr_xc * Rho_1 - Rho_3 # eq15 499 | 500 | diff_X_li = q_ad / V_liq * (X_li_in - X_li) + f_li_xc * Rho_1 - Rho_4 # eq16 501 | 502 | diff_X_su = q_ad / V_liq * (X_su_in - X_su) + Y_su * Rho_5 - Rho_13 # eq17 503 | 504 | diff_X_aa = q_ad / V_liq * (X_aa_in - X_aa) + Y_aa * Rho_6 - Rho_14 # eq18 505 | 506 | diff_X_fa = q_ad / V_liq * (X_fa_in - X_fa) + Y_fa * Rho_7 - Rho_15 # eq19 507 | 508 | diff_X_c4 = q_ad / V_liq * (X_c4_in - X_c4) + Y_c4 * Rho_8 + Y_c4 * Rho_9 - Rho_16 # eq20 509 | 510 | diff_X_pro = q_ad / V_liq * (X_pro_in - X_pro) + Y_pro * Rho_10 - Rho_17 # eq21 511 | 512 | diff_X_ac = q_ad / V_liq * (X_ac_in - X_ac) + Y_ac * Rho_11 - Rho_18 # eq22 513 | 514 | diff_X_h2 = q_ad / V_liq * (X_h2_in - X_h2) + Y_h2 * Rho_12 - Rho_19 # eq23 515 | 516 | diff_X_I = q_ad / V_liq * (X_I_in - X_I) + f_xI_xc * Rho_1 # eq24 517 | 518 | # Differential equations 25 and 26 (cations and anions) 519 | diff_S_cation = q_ad / V_liq * (S_cation_in - S_cation) # eq25 520 | 521 | diff_S_anion = q_ad / V_liq * (S_anion_in - S_anion) # eq26 522 | 523 | diff_S_h2 = 0 524 | 525 | # Differential equations 27 to 32 (ion states, only for ODE implementation) 526 | diff_S_va_ion = 0 # eq27 527 | 528 | diff_S_bu_ion = 0 # eq28 529 | 530 | diff_S_pro_ion = 0 # eq29 531 | 532 | diff_S_ac_ion = 0 # eq30 533 | 534 | diff_S_hco3_ion = 0 # eq31 535 | 536 | diff_S_nh3 = 0 # eq32 537 | 538 | # Gas phase equations: Differential equations 33 to 35 539 | diff_S_gas_h2 = (q_gas / V_gas * -1 * S_gas_h2) + (Rho_T_8 * V_liq / V_gas) # eq33 540 | 541 | diff_S_gas_ch4 = (q_gas / V_gas * -1 * S_gas_ch4) + (Rho_T_9 * V_liq / V_gas) # eq34 542 | 543 | diff_S_gas_co2 = (q_gas / V_gas * -1 * S_gas_co2) + (Rho_T_10 * V_liq / V_gas) # eq35 544 | 545 | diff_S_H_ion = 0 546 | diff_S_co2 = 0 547 | diff_S_nh4_ion = 0 #to keep the output same length as input for ADM1_ODE funcion 548 | 549 | 550 | return diff_S_su, diff_S_aa, diff_S_fa, diff_S_va, diff_S_bu, diff_S_pro, diff_S_ac, diff_S_h2, diff_S_ch4, diff_S_IC, diff_S_IN, diff_S_I, diff_X_xc, diff_X_ch, diff_X_pr, diff_X_li, diff_X_su, diff_X_aa, diff_X_fa, diff_X_c4, diff_X_pro, diff_X_ac, diff_X_h2, diff_X_I, diff_S_cation, diff_S_anion, diff_S_H_ion, diff_S_va_ion, diff_S_bu_ion, diff_S_pro_ion, diff_S_ac_ion, diff_S_hco3_ion, diff_S_co2, diff_S_nh3, diff_S_nh4_ion, diff_S_gas_h2, diff_S_gas_ch4, diff_S_gas_co2 551 | 552 | # Function for integration of ADM1 differential equations 553 | def simulate(t_step, solvermethod): 554 | r = scipy.integrate.solve_ivp(ADM1_ODE, t_step, state_zero, method= solvermethod) 555 | return r.y 556 | 557 | # Function for DAE equations adopted from the Rosen et al (2006) BSM2 report bmadm1_report 558 | def DAESolve(): 559 | global S_va_ion, S_bu_ion, S_pro_ion, S_ac_ion, S_hco3_ion, S_nh3, S_H_ion, pH, p_gas_h2, S_h2, S_nh4_ion, S_co2, P_gas, q_gas 560 | 561 | ## DAE calculations 562 | eps = 0.0000001 563 | prevS_H_ion = S_H_ion 564 | 565 | #initial values for Newton-Raphson solver parameter 566 | shdelta = 1.0 567 | shgradeq = 1.0 568 | S_h2delta = 1.0 569 | S_h2gradeq = 1.0 570 | tol = 10 ** (-12) #solver accuracy tolerance 571 | maxIter = 1000 #maximum number of iterations for solver 572 | i = 1 573 | j = 1 574 | 575 | ## DAE solver for S_H_ion from Rosen et al. (2006) 576 | while ((shdelta > tol or shdelta < -tol) and (i <= maxIter)): 577 | S_va_ion = K_a_va * S_va / (K_a_va + S_H_ion) 578 | S_bu_ion = K_a_bu * S_bu / (K_a_bu + S_H_ion) 579 | S_pro_ion = K_a_pro * S_pro / (K_a_pro + S_H_ion) 580 | S_ac_ion = K_a_ac * S_ac / (K_a_ac + S_H_ion) 581 | S_hco3_ion = K_a_co2 * S_IC / (K_a_co2 + S_H_ion) 582 | S_nh3 = K_a_IN * S_IN / (K_a_IN + S_H_ion) 583 | shdelta = S_cation + (S_IN - S_nh3) + S_H_ion - S_hco3_ion - S_ac_ion / 64.0 - S_pro_ion / 112.0 - S_bu_ion / 160.0 - S_va_ion / 208.0 - K_w / S_H_ion - S_anion 584 | shgradeq = 1 + K_a_IN * S_IN / ((K_a_IN + S_H_ion) * (K_a_IN + S_H_ion)) + K_a_co2 * S_IC / ((K_a_co2 + S_H_ion) * (K_a_co2 + S_H_ion)) + 1 / 64.0 * K_a_ac * S_ac / ((K_a_ac + S_H_ion) * (K_a_ac + S_H_ion)) + 1 / 112.0 * K_a_pro * S_pro / ((K_a_pro + S_H_ion) * (K_a_pro + S_H_ion)) + 1 / 160.0 * K_a_bu * S_bu / ((K_a_bu + S_H_ion) * (K_a_bu + S_H_ion)) + 1 / 208.0 * K_a_va * S_va / ((K_a_va + S_H_ion) * (K_a_va + S_H_ion)) + K_w / (S_H_ion * S_H_ion) 585 | S_H_ion = S_H_ion - shdelta / shgradeq 586 | if S_H_ion <= 0: 587 | S_H_ion = tol 588 | i+=1 589 | 590 | # pH calculation 591 | pH = - np.log10(S_H_ion) 592 | 593 | #DAE solver for S_h2 from Rosen et al. (2006) 594 | while ((S_h2delta > tol or S_h2delta < -tol) and (j <= maxIter)): 595 | I_pH_aa = (K_pH_aa ** nn_aa) / (prevS_H_ion ** nn_aa + K_pH_aa ** nn_aa) 596 | 597 | I_pH_h2 = (K_pH_h2 ** n_h2) / (prevS_H_ion ** n_h2 + K_pH_h2 ** n_h2) 598 | I_IN_lim = 1 / (1 + (K_S_IN / S_IN)) 599 | I_h2_fa = 1 / (1 + (S_h2 / K_I_h2_fa)) 600 | I_h2_c4 = 1 / (1 + (S_h2 / K_I_h2_c4)) 601 | I_h2_pro = 1 / (1 + (S_h2 / K_I_h2_pro)) 602 | 603 | I_5 = I_pH_aa * I_IN_lim 604 | I_6 = I_5 605 | I_7 = I_pH_aa * I_IN_lim * I_h2_fa 606 | I_8 = I_pH_aa * I_IN_lim * I_h2_c4 607 | I_9 = I_8 608 | I_10 = I_pH_aa * I_IN_lim * I_h2_pro 609 | 610 | I_12 = I_pH_h2 * I_IN_lim 611 | Rho_5 = k_m_su * (S_su / (K_S_su + S_su)) * X_su * I_5 # Uptake of sugars 612 | Rho_6 = k_m_aa * (S_aa / (K_S_aa + S_aa)) * X_aa * I_6 # Uptake of amino-acids 613 | Rho_7 = k_m_fa * (S_fa / (K_S_fa + S_fa)) * X_fa * I_7 # Uptake of LCFA (long-chain fatty acids) 614 | Rho_8 = k_m_c4 * (S_va / (K_S_c4 + S_va)) * X_c4 * (S_va / (S_bu + S_va+ 1e-6)) * I_8 # Uptake of valerate 615 | Rho_9 = k_m_c4 * (S_bu / (K_S_c4 + S_bu)) * X_c4 * (S_bu / (S_bu + S_va+ 1e-6)) * I_9 # Uptake of butyrate 616 | Rho_10 = k_m_pro * (S_pro / (K_S_pro + S_pro)) * X_pro * I_10 # Uptake of propionate 617 | Rho_12 = k_m_h2 * (S_h2 / (K_S_h2 + S_h2)) * X_h2 * I_12 # Uptake of hydrogen 618 | p_gas_h2 = S_gas_h2 * R * T_ad / 16 619 | Rho_T_8 = k_L_a * (S_h2 - 16 * K_H_h2 * p_gas_h2) 620 | S_h2delta = q_ad / V_liq * (S_h2_in - S_h2) + (1 - Y_su) * f_h2_su * Rho_5 + (1 - Y_aa) * f_h2_aa * Rho_6 + (1 - Y_fa) * 0.3 * Rho_7 + (1 - Y_c4) * 0.15 * Rho_8 + (1 - Y_c4) * 0.2 * Rho_9 + (1 - Y_pro) * 0.43 * Rho_10 - Rho_12 - Rho_T_8 621 | S_h2gradeq = - 1.0 / V_liq * q_ad - 3.0 / 10.0 * (1 - Y_fa) * k_m_fa * S_fa / (K_S_fa + S_fa) * X_fa * I_pH_aa / (1 + K_S_IN / S_IN) / ((1 + S_h2 / K_I_h2_fa) * (1 + S_h2 / K_I_h2_fa)) / K_I_h2_fa - 3.0 / 20.0 * (1 - Y_c4) * k_m_c4 * S_va * S_va / (K_S_c4 + S_va) * X_c4 / (S_bu + S_va + eps) * I_pH_aa / (1 + K_S_IN / S_IN) / ((1 + S_h2 / K_I_h2_c4 ) * (1 + S_h2 / K_I_h2_c4 )) / K_I_h2_c4 - 1.0 / 5.0 * (1 - Y_c4) * k_m_c4 * S_bu * S_bu / (K_S_c4 + S_bu) * X_c4 / (S_bu + S_va + eps) * I_pH_aa / (1 + K_S_IN / S_IN) / ((1 + S_h2 / K_I_h2_c4 ) * (1 + S_h2 / K_I_h2_c4 )) / K_I_h2_c4 - 43.0 / 100.0 * (1 - Y_pro) * k_m_pro * S_pro / (K_S_pro + S_pro) * X_pro * I_pH_aa / (1 + K_S_IN / S_IN) / ((1 + S_h2 / K_I_h2_pro ) * (1 + S_h2 / K_I_h2_pro )) / K_I_h2_pro - k_m_h2 / (K_S_h2 + S_h2) * X_h2 * I_pH_h2 / (1 + K_S_IN / S_IN) + k_m_h2 * S_h2 / ((K_S_h2 + S_h2) * (K_S_h2 + S_h2)) * X_h2 * I_pH_h2 / (1 + K_S_IN / S_IN) - k_L_a 622 | S_h2 = S_h2 - S_h2delta / S_h2gradeq 623 | if S_h2 <= 0: 624 | S_h2 = tol 625 | j+=1 626 | 627 | ## time array definition 628 | t = influent_state['time'] 629 | 630 | 631 | # Initiate the cache data frame for storing simulation results 632 | simulate_results = pd.DataFrame([state_zero]) 633 | columns = ["S_su", "S_aa", "S_fa", "S_va", "S_bu", "S_pro", "S_ac", "S_h2", "S_ch4", "S_IC", "S_IN", "S_I", "X_xc", "X_ch", "X_pr", "X_li", "X_su", "X_aa", "X_fa", "X_c4", "X_pro", "X_ac", "X_h2", "X_I", "S_cation", "S_anion", "pH", "S_va_ion", "S_bu_ion", "S_pro_ion", "S_ac_ion", "S_hco3_ion", "S_co2", "S_nh3", "S_nh4_ion", "S_gas_h2", "S_gas_ch4", "S_gas_co2"] 634 | simulate_results.columns = columns 635 | 636 | # Setting the solver method for the simulate function 637 | solvermethod = 'DOP853' 638 | t0=0 639 | n=0 640 | 641 | # Initiate cache data frame for storing gasflow values 642 | initflow = {'q_gas': [0], 'q_ch4': [0]} 643 | gasflow = pd.DataFrame(initflow) 644 | total_ch4 = 0 645 | 646 | # Initiate cache data frame for storing feedflow values 647 | initq = {'q_ad' : [170]} 648 | feedflow = pd.DataFrame(initq) 649 | 650 | 651 | ## Dynamic simulation 652 | # Loop for simlating at each time step and feeding the results to the next time step 653 | for u in t[1:]: 654 | n+=1 655 | setInfluent(n) 656 | 657 | state_input = [S_su_in,S_aa_in,S_fa_in,S_va_in,S_bu_in,S_pro_in,S_ac_in,S_h2_in,S_ch4_in,S_IC_in,S_IN_in,S_I_in,X_xc_in,X_ch_in,X_pr_in,X_li_in,X_su_in,X_aa_in,X_fa_in,X_c4_in,X_pro_in,X_ac_in,X_h2_in,X_I_in,S_cation_in,S_anion_in] 658 | 659 | # Span for next time step 660 | tstep = [t0,u] 661 | 662 | # Solve and store ODE results for next step 663 | sim_S_su, sim_S_aa, sim_S_fa, sim_S_va, sim_S_bu, sim_S_pro, sim_S_ac, sim_S_h2, sim_S_ch4, sim_S_IC, sim_S_IN, sim_S_I, sim_X_xc, sim_X_ch, sim_X_pr, sim_X_li, sim_X_su, sim_X_aa, sim_X_fa, sim_X_c4, sim_X_pro, sim_X_ac, sim_X_h2, sim_X_I, sim_S_cation, sim_S_anion, sim_S_H_ion, sim_S_va_ion, sim_S_bu_ion, sim_S_pro_ion, sim_S_ac_ion, sim_S_hco3_ion, sim_S_co2, sim_S_nh3, sim_S_nh4_ion, sim_S_gas_h2, sim_S_gas_ch4, sim_S_gas_co2 = simulate(tstep, solvermethod) 664 | 665 | # Store ODE simulation result states 666 | S_su, S_aa, S_fa, S_va, S_bu, S_pro, S_ac, S_h2, S_ch4, S_IC, S_IN, S_I, X_xc, X_ch, X_pr, X_li, X_su, X_aa, X_fa, X_c4, X_pro, X_ac, X_h2, X_I, S_cation, S_anion, S_H_ion, S_va_ion, S_bu_ion, S_pro_ion, S_ac_ion, S_hco3_ion, S_co2, S_nh3, S_nh4_ion, S_gas_h2, S_gas_ch4, S_gas_co2 = sim_S_su[-1], sim_S_aa[-1], sim_S_fa[-1], sim_S_va[-1], sim_S_bu[-1], sim_S_pro[-1], sim_S_ac[-1], sim_S_h2[-1], sim_S_ch4[-1], sim_S_IC[-1], sim_S_IN[-1], sim_S_I[-1], sim_X_xc[-1], sim_X_ch[-1], sim_X_pr[-1], sim_X_li[-1], sim_X_su[-1], sim_X_aa[-1], sim_X_fa[-1], sim_X_c4[-1], sim_X_pro[-1], sim_X_ac[-1], sim_X_h2[-1], sim_X_I[-1], sim_S_cation[-1], sim_S_anion[-1], sim_S_H_ion[-1], sim_S_va_ion[-1], sim_S_bu_ion[-1], sim_S_pro_ion[-1], sim_S_ac_ion[-1], sim_S_hco3_ion[-1], sim_S_co2[-1], sim_S_nh3[-1], sim_S_nh4_ion[-1], sim_S_gas_h2[-1], sim_S_gas_ch4[-1], sim_S_gas_co2[-1] 667 | 668 | # Solve DAE states 669 | DAESolve() 670 | 671 | # Algebraic equations 672 | p_gas_h2 = (S_gas_h2 * R * T_op / 16) 673 | p_gas_ch4 = (S_gas_ch4 * R * T_op / 64) 674 | p_gas_co2 = (S_gas_co2 * R * T_op) 675 | p_gas= (p_gas_h2 + p_gas_ch4 + p_gas_co2 + p_gas_h2o) 676 | q_gas = (k_p * (p_gas- p_atm)) 677 | if q_gas < 0: 678 | q_gas = 0 679 | 680 | q_ch4 = q_gas * (p_gas_ch4/p_gas) # methane flow 681 | if q_ch4 < 0: 682 | q_ch4 = 0 683 | 684 | flowtemp = {'q_gas' : q_gas, 'q_ch4' : q_ch4} 685 | gasflow = gasflow.append(flowtemp, ignore_index=True) 686 | 687 | S_nh4_ion = (S_IN - S_nh3) 688 | S_co2 = (S_IC - S_hco3_ion) 689 | total_ch4 = total_ch4 + q_ch4 690 | 691 | 692 | #state transfer 693 | state_zero = [S_su, S_aa, S_fa, S_va, S_bu, S_pro, S_ac, S_h2, S_ch4, S_IC, S_IN, S_I, X_xc, X_ch, X_pr, X_li, X_su, X_aa, X_fa, X_c4, X_pro, X_ac, X_h2, X_I, S_cation, S_anion, S_H_ion, S_va_ion, S_bu_ion, S_pro_ion, S_ac_ion, S_hco3_ion, S_co2, S_nh3, S_nh4_ion, S_gas_h2, S_gas_ch4, S_gas_co2] 694 | 695 | dfstate_zero = pd.DataFrame([state_zero], columns = columns) 696 | simulate_results = simulate_results.append(dfstate_zero) 697 | t0 = u 698 | 699 | 700 | # Write the dynamic simulation resutls to csv 701 | phlogarray = -1 * np.log10(simulate_results['pH']) 702 | simulate_results['pH'] = phlogarray 703 | simulate_results.to_csv("dynamic_out.csv", index = False) 704 | 705 | ## ring test begin 706 | # to compare the resutls with the dynamic simulation data from the BSM2 Matlab implementation 707 | # pyOut = pd.read_csv("dynamic_out.csv") 708 | # pyIn = pd.read_csv("digester_influent.csv") 709 | # MatlabOut = pd.read_csv("Matlabout_dyn.csv") 710 | 711 | # pyOut.time = pyIn.time 712 | # pyOut.Q = pyIn.Q 713 | # MatlabOut.Q = pyOut.Q 714 | # mvalue = pvalue = 0 715 | # ringtest = pd.DataFrame(columns=["state", "Matlab", "Python", "error"]) 716 | 717 | # n = 0 718 | # for i in pyOut.columns: 719 | # Matlabinteg = integrate.trapz(MatlabOut[i] , MatlabOut.time) 720 | # pyinteg = integrate.trapz(pyOut[i] , pyOut.time) 721 | # results =pd.DataFrame([[MatlabOut[i].name, Matlabinteg/280, pyinteg/280, abs(pyinteg-Matlabinteg)/280]], columns=["state", "Matlab", "Python", "error"]) 722 | # ringtest = ringtest.append(results) 723 | # print("Matlab " + MatlabOut[i].name + " average = " + str(Matlabinteg/280) + " Python " + pyOut[i].name + " average = " + str(pyinteg/280) + " Error =" + str(abs(pyinteg-Matlabinteg)/280)) 724 | 725 | # ringtest.to_csv("ringtest.csv", index = False) 726 | 727 | 728 | # for i in pyOut.columns: 729 | # plt.figure(figsize=(32, 8)) 730 | # plt.plot(MatlabOut.time, MatlabOut[i], label = MatlabOut[i].name, linestyle="-", color = "red") 731 | # plt.plot(pyOut.time, pyOut[i], label = pyOut[i].name, linestyle="--", color = "blue") 732 | # plt.legend() 733 | # plt.show() 734 | 735 | ## ring test end 736 | 737 | -------------------------------------------------------------------------------- /src/digester_initial.csv: -------------------------------------------------------------------------------- 1 | S_su,S_aa,S_fa,S_va,S_bu,S_pro,S_ac,S_h2,S_ch4,S_IC,S_IN,S_I,X_xc,X_ch,X_pr,X_li,X_su,X_aa,X_fa,X_c4,X_pro,X_ac,X_h2,X_I,S_cation,S_anion,S_H_ion,S_va_ion,S_bu_ion,S_pro_ion,S_ac_ion,S_hco3_ion,S_co2,S_nh3,S_nh4_ion,S_gas_h2,S_gas_ch4,S_gas_co2 2 | 0.012394,0.0055432,0.10741,0.012333,0.014003,0.017584,0.089315,2.51E-07,0.05549,0.095149,0.094468,0.13087,0.10792,0.020517,0.08422,0.043629,0.31222,0.93167,0.33839,0.33577,0.10112,0.67724,0.28484,17.2162,1.08E-47,0.0052101,5.46E-08,0.012284,0.013953,0.017511,0.089035,0.08568,0.0094689,0.001884,0.092584,1.10E-05,1.6535,0.01354 3 | -------------------------------------------------------------------------------- /src/ringtest.csv: -------------------------------------------------------------------------------- 1 | state,Matlab,Python,error 2 | S_su,0.012644598254072933,0.012423887145855166,0.00022071110821776692 3 | S_aa,0.0056384398805175194,0.00553317674306596,0.00010526313745155875 4 | S_fa,0.11197417562395554,0.10935538960316608,0.0026187860207894558 5 | S_va,0.01260995963728815,0.012325923825364593,0.0002840358119235569 6 | S_bu,0.0143086071037721,0.014015984128340837,0.00029262297543126227 7 | S_pro,0.018152390575256323,0.017698114072435392,0.0004542765028209336 8 | S_ac,0.09296660756330793,0.09103199468235969,0.0019346128809482467 9 | S_h2,2.5517425595238093e-07,2.5049205025075255e-07,4.682205701628347e-09 10 | S_ch4,0.05559646874476147,0.055417191486049205,0.0001792772587122674 11 | S_IC,0.09486565803909837,0.09535298763589038,0.00048732959679200454 12 | S_IN,0.09428208478542356,0.09473237418664689,0.0004502894012233202 13 | S_I,0.12715157027971033,0.13145846765804023,0.004306897378329896 14 | X_xc,0.10735129542553963,0.10821745341180834,0.0008661579862687166 15 | X_ch,0.02054442235617738,0.021019803544710614,0.0004753811885332336 16 | X_pr,0.08571824063976843,0.08422608386314849,0.001492156776619932 17 | X_li,0.04356977459814423,0.04312345333223114,0.0004463212659130887 18 | X_su,0.308686347253763,0.31896276037416077,0.010276413120397824 19 | X_aa,0.93466611401797,0.9335087113747988,0.0011574026431710699 20 | X_fa,0.3331566753530551,0.3345677850121938,0.0014111096591386842 21 | X_c4,0.3365039953053872,0.33686644323699405,0.0003624479316068273 22 | X_pro,0.10098187163596639,0.1019136725551738,0.000931800919207415 23 | X_ac,0.6744499132540595,0.6778723843739465,0.0034224711198869986 24 | X_h2,0.2831558486270022,0.28521622863815344,0.002060380011151202 25 | X_I,16.733926458508513,17.598821066098825,0.8648946075903106 26 | S_cation,1.2869412251436012e-29,7.348277955097665e-49,1.2869412251436012e-29 27 | S_anion,0.005238900010074362,0.005261278030905615,2.2378020831253272e-05 28 | pH,7.261406518994832,7.262572677912232,0.0011661589173992785 29 | S_va_ion,0.012559844814910794,0.012277177430115022,0.0002826673847957714 30 | S_bu_ion,0.014256730232831739,0.013965406994859195,0.00029132323797254376 31 | S_pro_ion,0.018076640173438022,0.017624777685556377,0.00045186248788164587 32 | S_ac_ion,0.09267340042276774,0.09074613109624835,0.0019272693265193906 33 | S_hco3_ion,0.08540294541073096,0.08585830023638619,0.000455354825655225 34 | S_co2,0.009462712628367417,0.009497115431498204,3.440280313078732e-05 35 | S_nh3,0.0018810057666948851,0.001890888685890292,9.882919195406642e-06 36 | S_nh4_ion,0.09240107901872871,0.09283983959301845,0.00043876057428973475 37 | S_gas_h2,1.1176154947916664e-05,1.0985624607367807e-05,1.9053034054885675e-07 38 | S_gas_ch4,1.6551068908931927,1.6511473823261427,0.003959508567050055 39 | S_gas_co2,0.013530575890469118,0.013576824685926869,4.6248795457751577e-05 40 | --------------------------------------------------------------------------------