Skip to content

Commit

Permalink
Merge branch 'main' into pfas
Browse files Browse the repository at this point in the history
  • Loading branch information
jiananf2 committed May 12, 2024
2 parents 47432c4 + ea32b82 commit 9949eca
Show file tree
Hide file tree
Showing 19 changed files with 115 additions and 60 deletions.
8 changes: 5 additions & 3 deletions .github/workflows/build-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ jobs:
python-version: ["3.9", "3.10"]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand All @@ -36,7 +36,9 @@ jobs:
run: |
pytest --cov=./ --cov-report=xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
directory: ./coverage/reports/
env_vars: OS,PYTHON
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-only.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
python-version: ["3.9", "3.10"]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
Expand Down
11 changes: 8 additions & 3 deletions docs/source/Systems.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ When publishing the paper that introduces QSDsan (`Li and Zhang`_ et al., 2022),

Water Resource Recovery Facilities
----------------------------------
Work is underway to model the 25 distinct Water Resource Recovery Facility (WRRF) configurations identified as “typical” across North America (`Tarallo <https://doi.org/10.2166/9781780407685>`_ et al., 2015; Zhang et al., *In Prep*, 2023).
Work is underway to model the 25 distinct Water Resource Recovery Facility (WRRF) configurations identified as “typical” across North America (`Tarallo <https://doi.org/10.2166/9781780407685>`_ et al., 2015; Zhang et al., *In Prep*, 2024).

.. figure:: images/wrrf_configs.png

Expand Down Expand Up @@ -58,14 +58,19 @@ A variety of other sanitation and resource recovery systems have been developed

#. Hydrothermal systems for fuel and fertilizer production from wet organic wastes

* Manuscript: Feng et al., Characterizing the Opportunity Space for Sustainable Hydrothermal Valorization of Wet Organic Wastes, Submitted.
* Publication: `Feng <https://pubs.acs.org/doi/10.1021/acs.est.3c07394>`_ et al., 2024.
* `htl EXPOsan module <https://github.com/QSD-Group/EXPOsan/tree/main/exposan/htl>`_

#. Modular encapsulated two-stage anaerobic biological system
#. Modular encapsulated two-stage anaerobic biological (METAB) system

* Manuscript: Zhang et al., Sustainable design of a modular anaerobic system for distributed energy recovery from industrial wastewaters, In Prep.
* `metab EXPOsan module <https://github.com/QSD-Group/EXPOsan/tree/main/exposan/metab>`_

#. EcoRecover system: microalgae-based tertiary P recovery process

* Manuscript: Kim et al., Development and validation of a Phototrophic-Mixotrophic Process Model (PM2) and a process simulator for microalgae-based wastewater treatment, In Prep.
* `pm2_ecorecover EXPOsan module <https://github.com/QSD-Group/EXPOsan/tree/main/exposan/pm2_ecorecover>`_


**Notes:**
- "Under NDA" indicates that the system is under non-disclosure agreement with the technology design team and unfortunately we are not able to share the codes in full at this stage. The source link code will lead to a private repository that only individuals who have signed the NDA can access.
Expand Down
21 changes: 21 additions & 0 deletions docs/source/api/Process.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,25 @@ CompiledProcesses
-----------------

.. autoclass:: qsdsan.CompiledProcesses
:members:


DynamicParameter
----------------

.. autoclass:: qsdsan.DynamicParameter
:members:


Kinetics
--------

.. autoclass:: qsdsan.Kinetics
:members:


MultiKinetics
-------------

.. autoclass:: qsdsan.MultiKinetics
:members:
2 changes: 1 addition & 1 deletion docs/source/api/_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
API
===

The UML below shows the structure and core Python classes implemented in ``QSDsan``. Each class is represented by a box containing the class name (bold, top part of the box) with select data (middle part of the box) and method (end with parentheses, bottom part of the box) attributes. The ``Component`` and ``WasteStream`` classes in blue are inherited from the ``Chemical`` and ``Stream`` classes in `Thermosteam <https://github.com/BioSTEAMDevelopmentGroup/thermosteam>`_ with the addition of wastewater-related attributes. The `Process` class in red enables dynamic simulation of Component objects’ transformation during kinetic processes (e.g., degradation of substrates). The ``SanUnit``, ``System``, ``TEA``, and ``Model`` classes in yellow are inherited from `BioSTEAM <https://github.com/BioSTEAMDevelopmentGroup/biosteam>`_ with added capacities for dynamic simulation and handling of construction inventories. Green boxes including ``mpactItem``, ``ImpactIndicator``, and ``LCA`` are implemented in ``QSDsan`` to enable LCA functionalities.
The UML below shows the structure and core Python classes implemented in ``QSDsan``. Each class is represented by a box containing the class name (bold, top part of the box) with select data (middle part of the box) and method (end with parentheses, bottom part of the box) attributes. The ``Component`` and ``WasteStream`` classes in blue are inherited from the ``Chemical`` and ``Stream`` classes in `Thermosteam <https://github.com/BioSTEAMDevelopmentGroup/thermosteam>`_ with the addition of wastewater-related attributes. The `Process` class in red enables dynamic simulation of Component objects’ transformation during kinetic processes (e.g., degradation of substrates). The ``SanUnit``, ``System``, ``TEA``, and ``Model`` classes in yellow are inherited from `BioSTEAM <https://github.com/BioSTEAMDevelopmentGroup/biosteam>`_ with added capacities for dynamic simulation and handling of construction inventories. Green boxes including ``ImpactItem``, ``ImpactIndicator``, and ``LCA`` are implemented in ``QSDsan`` to enable LCA functionalities.

.. figure:: https://lucid.app/publicSegments/view/c8de361f-7292-47e3-8870-d6f668691728/image.png
:width: 100%
Expand Down
4 changes: 4 additions & 0 deletions docs/source/api/processes/PM2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Phototrophic Mixotrophic Process Model (PM2)
============================================
.. autoclass:: qsdsan.processes.PM2
:members:
9 changes: 8 additions & 1 deletion docs/source/api/processes/_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ List of Biological Kinetic Models
+----------+------------------+-----------------------------+
| ASM2d | `asm`_ & `bsm1`_ | `Henze`_ et al., 2006 |
+----------+------------------+-----------------------------+
| PM2 | `pm2_ecorecover`_| N/A |
| | & `pm2_batch`_ | |
+----------+------------------+-----------------------------+



List of Other Kinetic Modules
Expand All @@ -38,6 +42,8 @@ List of Other Kinetic Modules
.. _asm: https://github.com/QSD-Group/EXPOsan/tree/main/exposan/asm
.. _bsm1: https://github.com/QSD-Group/EXPOsan/tree/main/exposan/bsm1
.. _bwaise: https://github.com/QSD-Group/EXPOsan/tree/main/exposan/bwaise
.. _pm2_batch: https://github.com/QSD-Group/EXPOsan/tree/main/exposan/pm2_batch
.. _pm2_ecorecover: https://github.com/QSD-Group/EXPOsan/tree/main/exposan/pm2_ecorecover

.. _Batstone: https://iwaponline.com/ebooks/book/152/Anaerobic-Digestion-Model-No-1-ADM1
.. _EPA design manual: https://nepis.epa.gov/Exe/ZyPURL.cgi?Dockey=3000464S.TXT
Expand All @@ -57,4 +63,5 @@ Links to docs
ASM1
ASM2d
Decay
KineticReaction
KineticReaction
PM2
4 changes: 4 additions & 0 deletions docs/source/api/sanunits/Reactor.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Reactor
=======
.. automodule:: qsdsan.sanunits._reactor
:members:
2 changes: 1 addition & 1 deletion docs/source/api/sanunits/_index.csv
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ADMtoASM,Yes,Yes,Completed
AnMBR (anaerobic membrane bioreactor),No,No,Completed
ActivatedSludgeProcess,No,No,Completed
AnaerobicBaffledReactor,No,No,Completed
AnaerobicCSTR,Yes,No,Under development
AnaerobicCSTR,Yes,No,Completed
AnaerobicDigestion,No,No,Completed
ASMtoADM,Yes,Yes,Completed
BatchExperiment,Yes,Yes,Completed
Expand Down
2 changes: 2 additions & 0 deletions docs/source/api/sanunits/_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Individual Unit Operations
combustion
compressor
CropApplication
distillation
DynamicInfluent
ElectrochemicalCell
Excretion
Expand All @@ -50,6 +51,7 @@ Individual Unit Operations
non_reactive
PolishingFilter
pumping
Reactor
Screening
Sedimentation
SepticTank
Expand Down
4 changes: 4 additions & 0 deletions docs/source/api/sanunits/distillation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Distillation
============
.. automodule:: qsdsan.sanunits._distillation
:members:
5 changes: 5 additions & 0 deletions docs/source/api/utils/model_eval.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ DictAttrSetter
.. autofunction:: qsdsan.utils.DictAttrSetter


MethodSetter
^^^^^^^^^^^^
.. autofunction:: qsdsan.utils.MethodSetter


Sample Manipulation
-------------------

Expand Down
Binary file modified docs/source/images/wrrf_configs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 14 additions & 5 deletions qsdsan/_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -1191,8 +1191,12 @@ def stoichiometry(self):
if isa(stoichio, list) and len(v_params) > 0:
stoichio_vals = []
for row in stoichio:
stoichio_vals.append([v.subs(v_params) if not isa(v, (float, int)) else v for v in row])
try: stoichio_vals = np.asarray(stoichio_vals, dtype=float)
# stoichio_vals.append([v.subs(v_params) if not isa(v, (float, int)) else v for v in row])
stoichio_vals.append([v.evalf(subs=v_params) if not isa(v, (float, int)) else v for v in row])
try:
stoichio_vals = np.asarray(stoichio_vals, dtype=float)
#!!! round-off error
# stoichio_vals[np.abs(stoichio_vals) < 2.22044604925e-16] = 0.0
except TypeError: pass
return pd.DataFrame(stoichio_vals, index=self.IDs, columns=self._components.IDs)
else: return pd.DataFrame(stoichio, index=self.IDs, columns=self._components.IDs)
Expand All @@ -1201,11 +1205,16 @@ def _lambdify_stoichio(self):
dct = self._dyn_params
dct_vals = self._parameters
if dct:
sbs = [i.symbol for i in dct.values()]
lamb = lambdify(sbs, self._stoichiometry, 'numpy')
static_params = {k:v for k,v in dct_vals.items() if k not in dct}
stoichio = []
isa = isinstance
for row in self._stoichiometry:
stoichio.append([v.evalf(subs=static_params) if not isa(v, (float, int)) else v for v in row])
sbs = [symbols(i) for i in dct.keys()]
lamb = lambdify(sbs, stoichio, 'numpy')
arr = np.empty((self.size, len(self._components)))
def f():
v = [v for k,v in dct_vals.items() if k in dct.keys()]
v = [dct_vals[k] for k in dct.keys()]
arr[:,:] = lamb(*v)
return arr
self.__dict__['_stoichio_lambdified'] = f
Expand Down
19 changes: 11 additions & 8 deletions qsdsan/processes/_adm1.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,9 +569,12 @@ def __new__(cls, components=None, path=None, N_xc=2.686e-3, N_I=4.286e-3, N_aa=7
**kwargs):

cmps = _load_components(components)
cmps.X_c.i_N = N_xc * N_mw
cmps.X_I.i_N = cmps.S_I.i_N = N_I * N_mw
cmps.S_aa.i_N = cmps.X_pr.i_N = N_aa * N_mw
cmps.X_c.i_N = round(N_xc * N_mw, 4)
cmps.X_I.i_N = cmps.S_I.i_N = round(N_I * N_mw, 4)
cmps.S_aa.i_N = cmps.X_pr.i_N = round(N_aa * N_mw, 4)
# cmps.X_c.i_N = N_xc * N_mw
# cmps.X_I.i_N = cmps.S_I.i_N = N_I * N_mw
# cmps.S_aa.i_N = cmps.X_pr.i_N = N_aa * N_mw

if not path: path = _path
self = Processes.load_from_file(path,
Expand All @@ -591,11 +594,11 @@ def __new__(cls, components=None, path=None, N_xc=2.686e-3, N_I=4.286e-3, N_aa=7
self.extend(gas_transfer)
self.compile(to_class=cls)

stoichio_vals = (f_ch_xc, f_pr_xc, f_li_xc, f_xI_xc, 1-f_ch_xc-f_pr_xc-f_li_xc-f_xI_xc,
f_fa_li, f_bu_su, f_pro_su, f_ac_su, 1-f_bu_su-f_pro_su-f_ac_su,
f_va_aa, f_bu_aa, f_pro_aa, f_ac_aa, 1-f_va_aa-f_bu_aa-f_pro_aa-f_ac_aa,
f_ac_fa, 1-f_ac_fa, f_pro_va, f_ac_va, 1-f_pro_va-f_ac_va,
f_ac_bu, 1-f_ac_bu, f_ac_pro, 1-f_ac_pro,
stoichio_vals = (f_ch_xc, f_pr_xc, f_li_xc, f_xI_xc, 1.0-f_ch_xc-f_pr_xc-f_li_xc-f_xI_xc,
f_fa_li, f_bu_su, f_pro_su, f_ac_su, 1.0-f_bu_su-f_pro_su-f_ac_su,
f_va_aa, f_bu_aa, f_pro_aa, f_ac_aa, 1.0-f_va_aa-f_bu_aa-f_pro_aa-f_ac_aa,
f_ac_fa, 1.0-f_ac_fa, f_pro_va, f_ac_va, 1.0-f_pro_va-f_ac_va,
f_ac_bu, 1.0-f_ac_bu, f_ac_pro, 1.0-f_ac_pro,
Y_su, Y_aa, Y_fa, Y_c4, Y_pro, Y_ac, Y_h2)
pH_LLs = np.array([pH_limits_aa[0]]*6 + [pH_limits_ac[0], pH_limits_h2[0]])
pH_ULs = np.array([pH_limits_aa[1]]*6 + [pH_limits_ac[1], pH_limits_h2[1]])
Expand Down
22 changes: 13 additions & 9 deletions qsdsan/sanunits/_anaerobic_reactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ def _init_state(self):

def _update_state(self):
y = self._state
y[-1] = sum(ws.state[-1] for ws in self.ins)
f_rtn = self._f_retain
i_mass = self.components.i_mass
chem_MW = self.components.chem_MW
Expand Down Expand Up @@ -473,7 +474,7 @@ def _update_state(self):
gas.state[:n_cmps] = gas.state[:n_cmps] * chem_MW / i_mass * 1e3 # i.e., M biogas to mg (measured_unit) / L

def _update_dstate(self):
self._tempstate = self.model.rate_function._params['root'].data.copy()
# self._tempstate = self.model.rate_function._params['root'].data.copy()
dy = self._dstate
f_rtn = self._f_retain
n_cmps = len(self.components)
Expand Down Expand Up @@ -536,19 +537,23 @@ def _compile_ODE(self):
gas_mass2mol_conversion = (cmps.i_mass / cmps.chem_MW)[self._gas_cmp_idx]
hasexo = bool(len(self._exovars))
f_exovars = self.eval_exo_dynamic_vars
# _rQ = self._rQ
if self._fixed_P_gas:
f_qgas = self.f_q_gas_fixed_P_headspace
else:
f_qgas = self.f_q_gas_var_P_headspace

if self.model._dyn_params:
def M_stoichio(state_arr):
_f_param(state_arr)
return self.model.stoichio_eval().T
else:
_M_stoichio = self.model.stoichio_eval().T
M_stoichio = lambda state_arr: _M_stoichio
def dy_dt(t, QC_ins, QC, dQC_ins):
S_liq = QC[:n_cmps]
S_gas = QC[n_cmps: (n_cmps+n_gas)]
#!!! Volume change due to temperature difference accounted for
# in _run and _init_state
# Q = QC[-1]
# S_in = QC_ins[0,:-1] * 1e-3 # mg/L to kg/m3
# Q_in = QC_ins[0,-1]
Q_ins = QC_ins[:, -1]
S_ins = QC_ins[:, :-1] * 1e-3 # mg/L to kg/m3
Q = sum(Q_ins)
Expand All @@ -557,15 +562,14 @@ def dy_dt(t, QC_ins, QC, dQC_ins):
QC = np.append(QC, exo_vars)
T = exo_vars[0]
else: T = self.T
_f_param(QC)
M_stoichio = _M_stoichio()
rhos =_f_rhos(QC)
_dstate[:n_cmps] = (Q_ins @ S_ins - Q*S_liq*(1-f_rtn))/V_liq \
+ np.dot(M_stoichio.T, rhos)
+ np.dot(M_stoichio(QC), rhos)
q_gas = f_qgas(rhos[-3:], S_gas, T)
_dstate[n_cmps: (n_cmps+n_gas)] = - q_gas*S_gas/V_gas \
+ rhos[-3:] * V_liq/V_gas * gas_mass2mol_conversion
_dstate[-1] = dQC_ins[0,-1] #* _rQ
# _dstate[-1] = dQC_ins[0,-1]
_dstate[-1] = 0.
_update_dstate()
self._ODE = dy_dt

Expand Down
9 changes: 4 additions & 5 deletions qsdsan/sanunits/_membrane_bioreactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,19 +409,18 @@ def _compute_mod_case_tank_N(self):

mod_per_cas, cas_per_tank = N_mod_min, N_cas_min

J, J_max, N_train = self.J, self.J_max, self._N_train_min
while J > J_max:
J = self.J
while J > self.J_max:
mod_per_cas += 1
if mod_per_cas == N_mod_max + 1:
if cas_per_tank == N_cas_max + 1:
N_train += 1
self.N_train += 1
mod_per_cas, cas_per_tank = N_mod_min, N_cas_min
else:
cas_per_tank += 1
mod_per_cas = N_mod_min

self._N_train, self._mod_per_cas, self._cas_per_tank = \
N_train, mod_per_cas, cas_per_tank
self._mod_per_cas, self._cas_per_tank = mod_per_cas, cas_per_tank


# Called by _run
Expand Down
1 change: 1 addition & 0 deletions qsdsan/sanunits/_polishing_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ def _run(self):
air_out.empty()
else:
biogas.empty()
air_out.empty()
degassing(eff, air_out)
degassing(waste, air_out)
air_out.imol['N2'] += air_in.imol['N2']
Expand Down
Loading

0 comments on commit 9949eca

Please sign in to comment.