Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/QSD-Group/QSDsan into beta
Browse files Browse the repository at this point in the history
  • Loading branch information
yalinli2 committed Dec 10, 2024
2 parents b97d4d1 + 2155dd8 commit a811f78
Show file tree
Hide file tree
Showing 14 changed files with 857 additions and 43 deletions.
4 changes: 2 additions & 2 deletions qsdsan/_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -1041,8 +1041,8 @@ def load_from_file(cls, path='', components=None, data=None,
stoichio = proc[cmp_IDs]
if data.columns[-1] in cmp_IDs: rate_eq = None
else:
if pd.isna(proc[-1]): rate_eq = None
else: rate_eq = proc[-1]
if pd.isna(proc.iloc[-1]): rate_eq = None
else: rate_eq = proc.iloc[-1]
stoichio = stoichio[-pd.isna(stoichio)].to_dict()
ref = None
for k,v in stoichio.items():
Expand Down
10 changes: 5 additions & 5 deletions qsdsan/_waste_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ def _wastestream_info(self, details=True, concentrations=None, N=15):
_ws_info += '\n'
# Only non-zero properties are shown
_ws_info += int(bool(self.pH))*f' pH : {self.pH:.1f}\n'
_ws_info += int(bool(self.SAlk))*f' Alkalinity : {self.SAlk:.1f} mg/L\n'
_ws_info += int(bool(self.SAlk))*f' Alkalinity : {self.SAlk:.1f} mmol/L\n'
if details:
_ws_info += int(bool(self.COD)) *f' COD : {self.COD:.1f} mg/L\n'
_ws_info += int(bool(self.BOD)) *f' BOD : {self.BOD:.1f} mg/L\n'
Expand Down Expand Up @@ -1299,7 +1299,7 @@ def codstates_inf_model(cls, ID='', flow_tot=0., units = ('L/hr', 'mg/L'),
... 9.96e+04
WasteStream-specific properties:
pH : 7.0
Alkalinity : 10.0 mg/L
Alkalinity : 10.0 mmol/L
COD : 430.0 mg/L
BOD : 221.8 mg/L
TC : 265.0 mg/L
Expand Down Expand Up @@ -1522,7 +1522,7 @@ def codbased_inf_model(cls, ID='', flow_tot=0., units = ('L/hr', 'mg/L'),
... 9.96e+04
WasteStream-specific properties:
pH : 7.0
Alkalinity : 10.0 mg/L
Alkalinity : 10.0 mmol/L
COD : 430.0 mg/L
BOD : 249.4 mg/L
TC : 265.0 mg/L
Expand Down Expand Up @@ -1751,7 +1751,7 @@ def bodbased_inf_model(cls, ID='', flow_tot=0., units = ('L/hr', 'mg/L'),
... 9.96e+04
WasteStream-specific properties:
pH : 7.0
Alkalinity : 10.0 mg/L
Alkalinity : 10.0 mmol/L
COD : 431.0 mg/L
BOD : 250.0 mg/L
TC : 264.9 mg/L
Expand Down Expand Up @@ -1983,7 +1983,7 @@ def sludge_inf_model(cls, ID='', flow_tot=0., units = ('L/hr', 'mg/L'),
... 9.88e+04
WasteStream-specific properties:
pH : 7.0
Alkalinity : 10.0 mg/L
Alkalinity : 10.0 mmol/L
COD : 10814.4 mg/L
BOD : 1744.3 mg/L
TC : 4246.5 mg/L
Expand Down
5 changes: 4 additions & 1 deletion qsdsan/processes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def __init__(self):
from ._decay import *
from ._kinetic_reaction import *
from ._pm2 import *
from ._aerobic_digestion_addon import *

from . import (
_aeration,
Expand All @@ -77,7 +78,8 @@ def __init__(self):
# _madm1,
_decay,
_kinetic_reaction,
_pm2
_pm2,
_aerobic_digestion_addon,
)

__all__ = (
Expand All @@ -90,4 +92,5 @@ def __init__(self):
*_decay.__all__,
*_kinetic_reaction.__all__,
*_pm2.__all__,
*_aerobic_digestion_addon.__all__,
)
19 changes: 11 additions & 8 deletions qsdsan/processes/_adm1_p_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -1037,35 +1037,38 @@ def __new__(cls, components=None, path=None,
#!!! new parameter
KS_IP*P_mw, np.array(k_mmp), Ksp_mass,
np.array(K_dis), K_AlOH, K_FeOH]))
def dydt_Sh2_AD(S_h2, state_arr, h, params, f_stoichio, V_liq, S_h2_in):

def adm1p_dydt_Sh2_AD(S_h2, state_arr, h, params, f_stoichio, V_liq, S_h2_in):
state_arr[7] = S_h2
Q = state_arr[45]
rxn = _rhos_adm1p(state_arr, params, h=h)
stoichio = f_stoichio(state_arr) # should return the stoichiometric coefficients of S_h2 for all processes
return Q/V_liq*(S_h2_in - S_h2) + np.dot(rxn, stoichio)


grad_rhosp = np.zeros(5)
X_biop = np.zeros(5)
def grad_dydt_Sh2_AD(S_h2, state_arr, h, params, f_stoichio, V_liq, S_h2_in):
def adm1p_grad_dydt_Sh2_AD(S_h2, state_arr, h, params, f_stoichio, V_liq, S_h2_in):
state_arr[7] = S_h2
ks = params['rate_constants'][[5,6,7,8,10]]
Ks = params['half_sat_coeffs'][2:6]
K_h2 = params['half_sat_coeffs'][7]
pH_ULs = params['pH_ULs']
pH_LLs = params['pH_LLs']
KS_IN = params['KS_IN']
KS_IP = params['KS_IP']
KIs_h2 = params['KIs_h2']
kLa = params['kLa']

X_biop[:] = state_arr[[18,19,19,20,22]]
substrates = state_arr[2:6]
S_va, S_bu, S_IN = state_arr[[3,4,10]]
S_va, S_bu, S_IN, S_IP = state_arr[[3,4,10,11]]
Iph = Hill_inhibit(h, pH_ULs, pH_LLs)[[2,3,4,5,7]]
Iin = substr_inhibit(S_IN, KS_IN)
Iip = substr_inhibit(S_IP, KS_IP)
grad_Ih2 = grad_non_compet_inhibit(S_h2, KIs_h2)

grad_rhosp[:] = ks * X_biop * Iph * Iin
grad_rhosp[:] = ks * X_biop * Iph * Iin * Iip
grad_rhosp[:-1] *= substr_inhibit(substrates, Ks) * grad_Ih2
if S_va > 0: grad_rhosp[1] *= 1/(1+S_bu/S_va)
if S_bu > 0: grad_rhosp[2] *= 1/(1+S_va/S_bu)
Expand All @@ -1074,12 +1077,12 @@ def grad_dydt_Sh2_AD(S_h2, state_arr, h, params, f_stoichio, V_liq, S_h2_in):
stoichio = f_stoichio(state_arr)

Q = state_arr[45]
return -Q/V_liq + np.dot(grad_rhos, stoichio[[5,6,7,8,10]]) + kLa*stoichio[-3]
return -Q/V_liq + np.dot(grad_rhosp, stoichio[[5,6,7,8,10]]) + kLa*stoichio[-3]

dct['flex_rhos'] = _rhos_adm1p
dct['solve_pH'] = adm1p_solve_pH
dct['dydt_Sh2_AD'] = dydt_Sh2_AD
dct['grad_dydt_Sh2_AD'] = grad_dydt_Sh2_AD
dct['dydt_Sh2_AD'] = adm1p_dydt_Sh2_AD
dct['grad_dydt_Sh2_AD'] = adm1p_grad_dydt_Sh2_AD
return self

def set_half_sat_K(self, K, process):
Expand Down
107 changes: 107 additions & 0 deletions qsdsan/processes/_aerobic_digestion_addon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
'''
QSDsan: Quantitative Sustainable Design for sanitation and resource recovery systems
This module is developed by:
Joy Zhang <[email protected]>
This module is under the University of Illinois/NCSA Open Source License.
Please refer to https://github.com/QSD-Group/QSDsan/blob/main/LICENSE.txt
for license details.
'''
import numpy as np
from qsdsan import Process
from thermosteam import settings
_load_components = settings.get_default_chemicals

__all__ = ('ASM_AeDigAddOn',)

class ASM_AeDigAddOn(Process):
'''
Creates a `Process` object representing the degradation of particulate
inert organic materials that typically occur in an aerobic digester.
Stoichiometry is determined by rules of element conservation in corresponding
activated sludge models.
Parameters
----------
k_dig : float, optional
The 1st-order degradation rate constant, in d^(-1). The default is 0.04.
See Also
--------
:class:`qsdsan.processes.ASM1`
:class:`qsdsan.processes.ASM2d`
:class:`qsdsan.processes.mASM2d`
Examples
--------
>>> import qsdsan.processes as pc
>>> cmps_asm1 = pc.create_asm1_cmps()
>>> dig_asm1 = pc.ASM_AeDigAddOn('dig_asm1')
>>> dig_asm1.show()
Process: dig_asm1
[stoichiometry] X_I: -1
X_S: 1
S_NH: 0.06
S_ALK: 0.0514
[reference] X_I
[rate equation] X_I*k_dig
[parameters] k_dig: 0.04
[dynamic parameters]
>>> cmps_masm2d = pc.create_masm2d_cmps(set_thermo=False)
>>> dig_masm2d = pc.ASM_AeDigAddOn('dig_masm2d', components=cmps_masm2d)
>>> dig_masm2d.show()
Process: dig_masm2d
[stoichiometry] S_NH4: 0.0265
S_PO4: 0.0009
S_IC: 0.0433
X_I: -1
X_S: 1
[reference] X_I
[rate equation] X_I*k_dig
[parameters] k_dig: 0.04
[dynamic parameters]
'''

def __init__(self, ID, k_dig=0.04, components=None):
cmps = _load_components(components)
rxn = 'X_I -> X_S'
consrv = []
if 'S_ALK' in cmps.IDs:
consrv.append('charge')
rxn += ' + [?]S_ALK'
elif 'S_IC' in cmps.IDs:
consrv.append('C')
rxn += ' + [?]S_IC'

if 'S_NH' in cmps.IDs:
consrv.append('N')
rxn += ' + [?]S_NH'
elif 'S_NH4' in cmps.IDs:
consrv.append('N')
rxn += ' + [?]S_NH4'

if 'S_PO4' in cmps.IDs:
consrv.append('P')
rxn += ' +[?]S_PO4'

super().__init__(ID=ID, reaction=rxn,
rate_equation='k_dig*X_I',
ref_component='X_I',
components=cmps,
conserved_for=consrv,
parameters=('k_dig',))
self.k_dig=k_dig
self._stoichiometry = np.asarray(self._stoichiometry, dtype=float)

@property
def k_dig(self):
'''[float] Degradation rate constant, in d^(-1).'''
return self._k
@k_dig.setter
def k_dig(self, k):
self._k = k
self.set_parameters(k_dig=k)
115 changes: 113 additions & 2 deletions qsdsan/processes/_asm2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@
import numpy as np
from thermosteam.utils import chemicals_user
from thermosteam import settings
from qsdsan import Component, Components, Processes, CompiledProcesses
from qsdsan import Component, Components, WasteStream, Processes, CompiledProcesses
from ..utils import ospath, data_path, load_data
from . import Monod, ion_speciation
from scipy.optimize import brenth
# from math import log10


__all__ = ('create_asm2d_cmps', 'ASM2d',
'create_masm2d_cmps', 'mASM2d')
'create_masm2d_cmps', 'mASM2d',
'create_masm2d_inf')

_path = ospath.join(data_path, 'process_data/_asm2d.tsv')
_load_components = settings.get_default_chemicals
Expand Down Expand Up @@ -990,3 +991,113 @@ def set_pKsps(self, ps):
K *= m2m**abs(v)
Ksp_mass.append(K)
self.rate_function._params['Ksp'] = np.array(Ksp_mass)

#%%

def create_masm2d_inf(
ID, Q, Q_unit='m3/d', T=298.15,
COD=430, NH4_N=25.0, PO4_P=8.0, alkalinity=7.0,
fr_SI=0.05, fr_SF=0.2, fr_SA=0.0,
fr_XI=0.13, fr_XH=0.0, fr_XAUT=0.0,
fr_XPAO=0.0, fr_XPHA=0.0, X_PP=0.0,
S_NO3=0.0, S_O2=0.0, S_N2=18,
S_Ca=140, S_Mg=50, S_K=28, S_Na=87, S_Cl=425,
X_AlOH=0, X_AlPO4=0, X_FeOH=0, X_FePO4=0,
X_CaCO3=0, X_ACP=0, X_MgCO3=0, X_newb=0, X_struv=0
):
'''
Convenient function to create an influent `WasteStream` object with `mASM2d`
state variables based on specified bulk properties.
Parameters
----------
ID : str
Unique identification for the `WasteStream` object.
Q : float
Total volumetric flow rate.
Q_unit : str, optional
Unit of measurement for flow rate. The default is 'm3/d'.
T : float, optional
Temperature, in K. The default is 298.15.
COD : float, optional
Total chemical oxygen demand, not accounting for electron acceptors like
dissvoled O2 or nitrate/nitrite, in mg-COD/L. The default is 430.
NH4_N : float, optional
Ammonium nitrogen, in mg-N/L. The default is 25.0.
PO4_P : float, optional
Ortho-phosphate, in mg-P/L. The default is 8.0.
alkalinity : float, optional
In mmol/L. The default is 7.0.
fr_SI : float, optional
Soluble inert fraction of total COD. The default is 0.05.
fr_SF : float, optional
Fermentable biodegradable fraction of total COD. The default is 0.2.
fr_SA : float, optional
VFA fraction of total COD. The default is 0.0.
fr_XI : float, optional
Particulate inert fraction of total COD. The default is 0.13.
fr_XH : float, optional
Heterotrophic biomass fraction of total COD. The default is 0.0.
fr_XAUT : float, optional
Autotrophic biomass fraction of total COD. The default is 0.0.
fr_XPAO : float, optional
Phosphorus accumulating biomass fraction of total COD. The default is 0.0.
fr_XPHA : float, optional
PHA fraction of total COD. The default is 0.0.
X_PP : float, optional
Poly-phosphate in mg-P/L. The default is 0.0.
S_NO3 : float, optional
Nitrate and nitrite in mg-N/L. The default is 0.0.
S_O2 : float, optional
Dissolved oxygen in mg-O2/L. The default is 0.0.
S_N2 : float, optional
Dissolved nitrogen gas in mg-N/L. The default is 18.
S_Ca : float, optional
Total soluble calcium in mg-Ca/L. The default is 140.
S_Mg : float, optional
Total soluble magnesium in mg-Mg/L. The default is 50.
S_K : float, optional
Total soluble potassium in mg-K/L. The default is 28.
S_Na : float, optional
Other cation, in mg-Na/L. The default is 87.
S_Cl : float, optional
Other anion, in mg-Cl/L. The default is 425.
X_AlOH : float, optional
Aluminum hydroxide [mg/L]. The default is 0.
X_AlPO4 : float, optional
Aluminum phosphate [mg/L]. The default is 0.
X_FeOH : float, optional
Iron hydroxide [mg/L]. The default is 0.
X_FePO4 : float, optional
Iron phosphate [mg/L]. The default is 0.
X_CaCO3 : float, optional
Calcium carbonate [mg/L]. The default is 0.
X_ACP : float, optional
Calcium phosphate [mg/L]. The default is 0.
X_MgCO3 : float, optional
Magnesium carbonate [mg/L]. The default is 0.
X_newb : float, optional
Newbryite [mg/L]. The default is 0.
X_struv : float, optional
Struvite [mg/L]. The default is 0.
'''

fr_xs = 1.0-fr_SI-fr_SF-fr_SA-fr_XI-fr_XH-fr_XAUT-fr_XPAO-fr_XPHA
if fr_xs < 0:
raise ValueError('The sum of all COD fractions of organic materials must '
'not exceed 1.')

inf = WasteStream(ID, T=T, SAlk=alkalinity)
concs = dict(
S_NH4=NH4_N, S_PO4=PO4_P, S_IC=alkalinity*12,
S_I=COD*fr_SI, S_F=COD*fr_SF, S_A=COD*fr_SA,
X_S=COD*fr_xs, X_I=COD*fr_XI, X_H=COD*fr_XH,
X_AUT=COD*fr_XAUT, X_PAO=COD*fr_XPAO, X_PHA=COD*fr_XPHA,
X_PP=X_PP, S_NO3=S_NO3, S_O2=S_O2, S_N2=S_N2,
S_Ca=S_Ca, S_Mg=S_Mg, S_K=S_K, S_Na=S_Na, S_Cl=S_Cl,
X_AlOH=X_AlOH, X_AlPO4=X_AlPO4, X_FeOH=X_FeOH, X_FePO4=X_FePO4,
X_CaCO3=X_CaCO3, X_ACP=X_ACP, X_MgCO3=X_MgCO3, X_newb=X_newb, X_struv=X_struv
)
inf.set_flow_by_concentration(Q, concs, (Q_unit, 'mg/L'))
return inf
Loading

0 comments on commit a811f78

Please sign in to comment.