diff --git a/docs/source/tutorials/11_Dynamic_Simulation.ipynb b/docs/source/tutorials/11_Dynamic_Simulation.ipynb index eae3822e..aed3ea92 100644 --- a/docs/source/tutorials/11_Dynamic_Simulation.ipynb +++ b/docs/source/tutorials/11_Dynamic_Simulation.ipynb @@ -47,7 +47,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "This tutorial was made with qsdsan v1.2.2 and exposan v1.2.3\n" + "This tutorial was made with qsdsan v1.2.5 and exposan v1.2.5\n" ] } ], diff --git a/docs/source/tutorials/Tutorial_11.ipynb b/docs/source/tutorials/Tutorial_11.ipynb new file mode 100644 index 00000000..545c1773 --- /dev/null +++ b/docs/source/tutorials/Tutorial_11.ipynb @@ -0,0 +1,401 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "9b7ba848", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This tutorial was made with qsdsan v1.2.5 and exposan v1.2.5\n" + ] + } + ], + "source": [ + "import qsdsan as qs, exposan\n", + "print(f'This tutorial was made with qsdsan v{qs.__version__} and exposan v{exposan.__version__}')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a31c8f69", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "System: bsm1_sys\n", + "ins...\n", + "[0] wastewater\n", + " phase: 'l', T: 293.15 K, P: 101325 Pa\n", + " flow (kmol/hr): S_I 23.1\n", + " S_S 53.4\n", + " X_I 39.4\n", + " X_S 155\n", + " X_BH 21.7\n", + " S_NH 1.34\n", + " S_ND 0.381\n", + " ... 4.26e+04\n", + "outs...\n", + "[0] effluent\n", + " phase: 'l', T: 293.15 K, P: 101325 Pa\n", + " flow: 0\n", + "[1] WAS\n", + " phase: 'l', T: 293.15 K, P: 101325 Pa\n", + " flow: 0\n" + ] + } + ], + "source": [ + "# Let's load the BSM1 system first\n", + "from exposan import bsm1\n", + "bsm1.load()\n", + "sys = bsm1.sys\n", + "sys.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5fe1776f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "A1CSTR:c->A2CSTR:c\n", + "\n", + "\n", + "\n", + " ws1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "A2CSTR:c->O1CSTR:c\n", + "\n", + "\n", + "\n", + " ws3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "O1CSTR:c->O2CSTR:c\n", + "\n", + "\n", + "\n", + " ws5\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "O2CSTR:c->O3CSTR:c\n", + "\n", + "\n", + "\n", + " ws7\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "O3CSTR:c->A1CSTR:c\n", + "\n", + "\n", + "\n", + " RWW\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "O3CSTR:c->C1Flat bottom circular clarifier:c\n", + "\n", + "\n", + "\n", + " treated\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "C1Flat bottom circular clarifier:c->A1CSTR:c\n", + "\n", + "\n", + "\n", + " RAS\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "C1Flat bottom circular clarifier:c-> effluent:w\n", + "\n", + "\n", + " effluent\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "C1Flat bottom circular clarifier:c-> WAS:w\n", + "\n", + "\n", + " WAS\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " wastewater:e->A1CSTR:c\n", + "\n", + "\n", + " wastewater\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "A1CSTR\n", + "\n", + "\n", + "A1CSTR\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "A2CSTR\n", + "\n", + "\n", + "A2CSTR\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "O1CSTR\n", + "\n", + "\n", + "O1CSTR\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "O2CSTR\n", + "\n", + "\n", + "O2CSTR\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "O3CSTR\n", + "\n", + "\n", + "O3CSTR\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "C1Flat bottom circular clarifier\n", + "\n", + "\n", + "C1Flat bottom circular clarifier\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " wastewater\n", + "\n", + "\n", + "\n", + "\n", + " effluent\n", + "\n", + "\n", + "\n", + "\n", + " WAS\n", + "\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# The BSM1 system is composed of 5 CSTRs in series,\n", + "# followed by a flat-bottom circular clarifier.\n", + "sys.diagram()\n", + "# sys.units" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "98d2662c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# We can verify that by\n", + "sys.isdynamic" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e2c64ce0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{: True,\n", + " : True,\n", + " : True,\n", + " : True,\n", + " : True,\n", + " : True}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# This is because the system contains at least one dynamic SanUnit\n", + "{u: u.isdynamic for u in sys.units}\n", + "\n", + "# If we disable dynamic simulation, then `simulate` would work as usual\n", + "# sys.isdynamic = False\n", + "# sys.simulate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8cc6e48", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\joy_c\\anaconda3\\envs\\tut\\lib\\site-packages\\qsdsan\\sanunits\\_suspended_growth_bioreactor.py:44: NumbaPerformanceWarning: \u001b[1m\u001b[1m'@' is faster on contiguous arrays, called on (array(float64, 1d, A), array(float64, 2d, A))\u001b[0m\u001b[0m\n", + " flow_in = Q_ins @ C_ins / V_arr\n", + "C:\\Users\\joy_c\\anaconda3\\envs\\tut\\lib\\site-packages\\numba\\core\\typing\\npydecl.py:913: NumbaPerformanceWarning: \u001b[1m'@' is faster on contiguous arrays, called on (array(float64, 1d, A), array(float64, 2d, A))\u001b[0m\n", + " warnings.warn(NumbaPerformanceWarning(msg))\n" + ] + } + ], + "source": [ + "# Let's try simulating the BSM1 system from day 0 to day 50\n", + "sys.simulate(t_span=(0, 50), method='BDF', state_reset_hook='reset_cache')\n", + "sys.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba51c0b9", + "metadata": {}, + "outputs": [], + "source": [ + "# This shows the units/streams whose state variables are kept track of\n", + "# during dynamic simulations.\n", + "sys.scope.subjects" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4c0bdfd", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1d690bf", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c05808bc", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37df12a9", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:tut]", + "language": "python", + "name": "conda-env-tut-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/qsdsan/equipments/__init__.py b/qsdsan/equipments/__init__.py index ba0f6633..9a10c458 100644 --- a/qsdsan/equipments/__init__.py +++ b/qsdsan/equipments/__init__.py @@ -16,20 +16,16 @@ from ._aeration import * from ._column import * from ._electrode import * -from ._encapsulation import * from ._machine import* from ._membrane import * -from ._vacuum_pump import * from ._vertical_mixer import * from . import ( _aeration, _column, _electrode, - _encapsulation, _machine, _membrane, - _vacuum_pump, _vertical_mixer, ) @@ -38,9 +34,7 @@ *_aeration.__all__, *_column.__all__, *_electrode.__all__, - *_encapsulation.__all__, *_machine.__all__, *_membrane.__all__, - *_vacuum_pump.__all__, *_vertical_mixer.__all__, ) \ No newline at end of file diff --git a/qsdsan/equipments/_encapsulation.py b/qsdsan/equipments/_encapsulation.py deleted file mode 100644 index cba95529..00000000 --- a/qsdsan/equipments/_encapsulation.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- -''' -QSDsan: Quantitative Sustainable Design for sanitation and resource recovery systems - -This module is developed by: - Joy Zhang - -Part of this module is based on the BioSTEAM package: -https://github.com/BioSTEAMDevelopmentGroup/biosteam - -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. -''' - -from .. import Equipment - -__all__ = ('Beads',) - -class Beads(Equipment): - - def __init__(self, linked_unit=None, ID=None, - units={'Diameter': 'mm', - 'Density': 'kg/m3', - 'Unit cost': 'USD/kg', - 'Bead total mass': 'kg'}, - F_BM=1.15, F_D=1., F_P=1., F_M=1., - lifetime=0.5, lifetime_unit='yr', - d_bead=1.0, rho_bead=265, p_bead=1440, - **kwargs): - - Equipment.__init__(self, linked_unit, ID, units, F_BM, F_D, F_P, F_M, - lifetime, lifetime_unit, **kwargs) - self.d_bead = d_bead - self.rho_bead = rho_bead - self.p_bead = p_bead - - def _design(self): - V_bead = self.linked_unit.design_results['Bead total volume'] - return {'Bead total mass': V_bead * self.rho_bead} - - def _cost(self): - m = self._design()['Bead total mass'] - return m * self.p_bead \ No newline at end of file diff --git a/qsdsan/equipments/_membrane.py b/qsdsan/equipments/_membrane.py index 8f88e31a..527bb2f8 100644 --- a/qsdsan/equipments/_membrane.py +++ b/qsdsan/equipments/_membrane.py @@ -19,7 +19,7 @@ from .. import Equipment # from .utils import auom -__all__ = ('Membrane', 'HollowFiberMembrane') +__all__ = ('Membrane', ) class Membrane(Equipment): @@ -80,50 +80,4 @@ def N(self): return self._N @N.setter def N(self, i): - self._N = int(i) - - -class HollowFiberMembrane(Equipment): - - _PermSelect_pricing = { - # model: (area [cm^2], price [USD], min Q_gas [scfm], max Q_gas [scfm]) - 'PDMSXA-10': (10, 117, 4e-5, 4e-3), - 'PDMSXA-1000': (1000, 304, 4e-3, 0.4), - 'PDMSXA-7500': (7500, 541, 2e-2, 1.0), - 'PDMSXA-2.1': (2.1e4, 1045, 4e-2, 2.0) - } - - scfm_to_kmolphr = 0.002641 * 453.59237 * 60 / 1000 - - def __init__(self, ID='', linked_unit=None, - units=dict(), F_BM=1.15, lifetime=5, lifetime_unit='yr', - material='polydimethylsiloxane'): - Equipment.__init__(self=self, ID=ID, linked_unit=linked_unit, units=units, - F_BM=F_BM, lifetime=lifetime, lifetime_unit=lifetime_unit) - self.material = material - - def _design(self): - for ws in self.linked_unit.outs: - if ws.phase == 'g': - Q_mol = ws.F_mol # kmol/hr - break - key = None - N = 0 - while not key: - N += 1 - Q = Q_mol / N - for md, data in self._PermSelect_pricing.items(): - Q_max = data[-1] * self.scfm_to_kmolphr - if Q < Q_max: - key = md - return {'Material': self.material, - 'Number of membrane units': N, - 'Membrane unit model':key} - - def _cost(self): - design = self._design() - N = design['Number of membrane units'] - key = design['Membrane unit model'] - p = self._PermSelect_pricing[key][1] - return N * p - \ No newline at end of file + self._N = int(i) \ No newline at end of file diff --git a/qsdsan/equipments/_vacuum_pump.py b/qsdsan/equipments/_vacuum_pump.py deleted file mode 100644 index 5d3db1bb..00000000 --- a/qsdsan/equipments/_vacuum_pump.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- -''' -QSDsan: Quantitative Sustainable Design for sanitation and resource recovery systems - -This module is developed by: - Joy Zhang - -Part of this module is based on the BioSTEAM package: -https://github.com/BioSTEAMDevelopmentGroup/biosteam - -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. -''' - -from .. import Equipment -from math import log - -__all__ = ('VacuumPump',) - -class VacuumPump(Equipment): - - def __init__(self, linked_unit=None, ID=None, - units={'P_suction': 'torr', 'Vacuum power': 'kW'}, - F_BM=1., F_D=1., F_P=1., F_M=1., - lifetime=20, lifetime_unit='yr', - P_suction=100, **kwargs): - - Equipment.__init__(self, linked_unit, ID, units, F_BM, F_D, F_P, F_M, - lifetime, lifetime_unit, **kwargs) - self.P_suction = P_suction - - @property - def P_vacuum(self): - P_break = 21.4 * self._SF()**0.924 - e_motor = 0.8 + 0.0319*log(P_break, 10) - 0.00182 * log(P_break, 10)**2 - return P_break/e_motor # in kW - - def _SF(self, for_cost=False): - # P_sunction in torr, Qm in kg/hr - for ws in self.linked_unit.outs: - if ws.phase == 'g': - Qm = ws.F_mass # kg/hr - break - Ps = self.P_suction # torr - if for_cost: - return Qm/Ps*2.2 # convert Qm to lb/hr - else: - return min(16, max(0.2, Qm/Ps)) - - def _design(self): - return {'Vacuum power': self.P_vacuum} - - def _cost(self): - SF = self._SF(True) - return 1915 * SF**0.41 - - @property - def P_suction(self): - return self._Ps - - @P_suction.setter - def P_suction(self, P): - if P <= 0: raise ValueError(f'Suction pressure [torr] must be positive, not {P}') - self._Ps = P \ No newline at end of file diff --git a/qsdsan/processes/_adm1.py b/qsdsan/processes/_adm1.py index 9c0457f6..32d99819 100644 --- a/qsdsan/processes/_adm1.py +++ b/qsdsan/processes/_adm1.py @@ -315,7 +315,7 @@ def rhos_adm1(state_arr, params): # ============================================================================= class TempState: def __init__(self): - self.data = [] + self.data = {} # def append(self, value): # self.data += [value] @@ -531,6 +531,7 @@ class ADM1(CompiledProcesses): ('HAc', 'Ac-'), ('HPr', 'Pr-'), ('HBu', 'Bu-'), ('HVa', 'Va-')) _biogas_IDs = ('S_h2', 'S_ch4', 'S_IC') + _biomass_IDs = ('X_su', 'X_aa', 'X_fa', 'X_c4', 'X_pro', 'X_ac', 'X_h2') def __new__(cls, components=None, path=None, N_xc=2.686e-3, N_I=4.286e-3, N_aa=7e-3, f_ch_xc=0.2, f_pr_xc=0.2, f_li_xc=0.3, f_xI_xc=0.2, @@ -591,7 +592,6 @@ def __new__(cls, components=None, path=None, N_xc=2.686e-3, N_I=4.286e-3, N_aa=7 Ka_base = np.array([10**(-pKa) for pKa in pKa_base]) Ka_dH = np.array(Ka_dH) root = TempState() - # root.data = 10**(-7.4655) dct = self.__dict__ dct.update(kwargs) diff --git a/qsdsan/sanunits/__init__.py b/qsdsan/sanunits/__init__.py index 6b0ebccc..625ad879 100644 --- a/qsdsan/sanunits/__init__.py +++ b/qsdsan/sanunits/__init__.py @@ -41,7 +41,6 @@ from ._crop_application import * from ._dynamic_influent import * from ._electrochemical_cell import * -from ._encapsulation_bioreactor import * from ._excretion import * from ._heat_exchanging import * from ._junction import * @@ -89,7 +88,6 @@ _distillation, _dynamic_influent, _electrochemical_cell, - _encapsulation_bioreactor, _excretion, _flash, _heat_exchanging, @@ -133,7 +131,6 @@ *_distillation.__all__, *_dynamic_influent.__all__, *_electrochemical_cell.__all__, - *_encapsulation_bioreactor.__all__, *_excretion.__all__, *_flash.__all__, *_heat_exchanging.__all__, diff --git a/qsdsan/sanunits/_encapsulation_bioreactor.py b/qsdsan/sanunits/_encapsulation_bioreactor.py deleted file mode 100644 index 365b200f..00000000 --- a/qsdsan/sanunits/_encapsulation_bioreactor.py +++ /dev/null @@ -1,338 +0,0 @@ -# -*- coding: utf-8 -*- -''' -QSDsan: Quantitative Sustainable Design for sanitation and resource recovery systems - -This module is developed by: - Joy Zhang - -Part of this module is based on the BioSTEAM package: -https://github.com/BioSTEAMDevelopmentGroup/biosteam - -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. -''' - -from .. import SanUnit -from ..equipments import VerticalMixer, VacuumPump, HollowFiberMembrane, Beads -from ..utils import auom -from warnings import warn -from math import pi - -__all__ = ('H2E', 'CH4E') - -class H2E(SanUnit): - - _N_ins = 1 - _N_outs = 2 - _N_heat_utilities = 1 - A = 265 - b = 0.513 - _Vmin = 1e4 # gal - _Vmax = 1e6 # gal - _rho_bead = 265 # kg/m3 - - - def __init__(self, ID='', ins=None, outs=(), thermo=None, - init_with='WasteStream', equipments=(), lifetime=20, - COD_removal=0.2, H2_yield=4e-4, CH4_yield=0.0, frac_H2=0.28, - tau=1, T=273.15+35, safety_factor=1.3, - V_frac_beads=0.09, e_heat=0.8, p_treatment=0.24, **kwargs): - SanUnit.__init__(self, ID, ins, outs, thermo, init_with, - equpiments=equipments, lifetime=lifetime, F_BM_default=1) - self.COD_removal = COD_removal - self.H2_yield = H2_yield - self.CH4_yield = CH4_yield - self.frac_H2 = frac_H2 - self.tau = tau - self.T = T - self.safety_factor = safety_factor - self.V_frac_beads = V_frac_beads - self.e_heat = e_heat - self.p_treatment = p_treatment - for attr, value in kwargs.items(): - setattr(self, attr, value) - self._init_equip(lifetime) - - def _init_equip(self, lifetime): - if self.equipments: - isa = isinstance - equips = list(self.equipments) - vm = None - vp = None - mb = None - encap = None - for i in equips: - if isa(i, VerticalMixer): - vm = i - equips.remove(i) - elif isa(i, VacuumPump): - vp = i - equips.remove(i) - elif isa(i, HollowFiberMembrane): - mb = i - equips.remove(i) - elif isa(i, Beads): - encap = i - equips.remove(i) - if not vm: - warn('lacking vertical mixer as equipment, will use a default one') - vm = VerticalMixer(linked_unit=self, ID='mixer', lifetime=lifetime) - if not vp: - warn('lacking vacuum pump as equipment, will use a default one') - vp = VacuumPump(linked_unit=self, ID='vacuum', lifetime=lifetime) - if not mb: - warn('lacking hollow fiber membrane as equipment, will use a default one') - mb = HollowFiberMembrane(linked_unit=self, ID='membrane', lifetime=10) - if not encap: - warn('lacking encapsulation beads as equipment, will use a default one') - encap = Beads(linked_unit=self, ID='beads', lifetime=0.5) - self.equipments = (vm, vp, mb, encap, *equips) - else: - vm = VerticalMixer(linked_unit=self, ID='mixer', lifetime=lifetime) - vp = VacuumPump(linked_unit=self, ID='vacuum', lifetime=lifetime) - mb = HollowFiberMembrane(linked_unit=self, ID='membrane', lifetime=10) - encap = Beads(linked_unit=self, ID='beads', lifetime=0.5) - self.equipments = (vm, vp, mb, encap) - - def _run(self): - waste, = self.ins - eff, biogas = self.outs - eff.copy_like(waste) - biogas.phase = 'g' - - # COD removal - COD_rmv = eff.imass['COD'] * self.COD_removal - eff.imass['COD'] -= COD_rmv - biogas.imass['H2'] = h2 = COD_rmv*self.H2_yield - biogas.imass['CH4'] = ch4 = COD_rmv*self.CH4_yield - biogas.imass['N2'] = h2/self.frac_H2 - h2 - ch4 - - _units = { - 'Residence time': 'd', - 'Reactor volume': 'm3', - 'Bead total volume': 'm3' - } - - def _design(self): - Q = self.ins[0].F_vol * 24 #m3/d - design = self.design_results - design['Residence time'] = t = self.tau - design['Reactor volume'] = V = t * Q * self.safety_factor - design['Bead total volume'] = V * self.V_frac_beads - self.add_equipment_design() - mixer, vacuum = self.equipments[:2] - P_mix = mixer.power * mixer.N_mix - P_vcm = vacuum.P_vacuum - self.power_utility(rate=P_mix+P_vcm) - hu = self.heat_utilities[0] - hu.heat_transfer_efficiency = self.e_heat - inf = self.ins[0] - T_in = inf.T - T_target = self.T - unit_duty = inf.F_mass * inf.Cp * (T_target - T_in) #kJ/hr - hu(unit_duty, T_in, T_target) - - def _cost(self): - D, C = self.design_results, self.baseline_purchase_costs - V = D['Reactor volume'] * auom('m3').conversion_factor('gal') - C['Reactor'] = self.A * min(self._Vmax, max(self._Vmin, V)) ** self.b # cone-roof carbon steel storage tank - self.add_equipment_cost() - eff = self.outs[0] - if eff.isproduct(): - self.add_OPEX = {'Effluent treatment': self.p_treatment * eff.imass['COD']} # USD/hr - - @property - def COD_removal(self): - return self._rcod - - @COD_removal.setter - def COD_removal(self, r): - if r > 1 or r < 0: - raise ValueError(f'COD removal must be in [0, 1], not {r}') - self._rcod = r - - @property - def H2_yield(self): - return self._yh2 - - @H2_yield.setter - def H2_yield(self, y): - if y > 8 or y < 0: - raise ValueError(f'H2 yield must be in [0, 8] g-H2/g-COD-removed, not {y}') - self._yh2 = y - - @property - def CH4_yield(self): - return self._ych4 - - @CH4_yield.setter - def CH4_yield(self, y): - if y > 4 or y < 0: - raise ValueError(f'H2 yield must be in [0, 4] g-CH4/g-COD-removed, not {y}') - self._ych4 = y - - @property - def frac_H2(self): - return self._fh2 - @frac_H2.setter - def frac_H2(self, f): - if f > 1 or f < 0: - raise ValueError(f'H2 mass fraction in biogas must be in [0, 1], not {f}') - self._fh2 = f - - @property - def tau(self): - return self._tau - - @tau.setter - def tau(self, t): - if t < 0: raise ValueError(f'residence time tau cannot be negative: {t}') - self._tau = t - - @property - def T(self): - return self._T - @T.setter - def T(self, i): - if i < self.ins[0].T: - warn('Operating temperature should not be lower than influent temperature {self.ins[0].T}') - i = self.ins[0].T - self._T = i - - @property - def safety_factor(self): - return self._sf - @safety_factor.setter - def safety_factor(self, sf): - if sf < 1: raise ValueError(f'safety factor cannot be less than 1: {sf}') - self._sf = sf - - @property - def V_frac_beads(self): - return self._f_Vb - @V_frac_beads.setter - def V_frac_beads(self, f): - if f > 1 or f < 0: - raise ValueError(f'Volume fraction of beads in the reactor must be in [0, 1], not {f}') - self._f_Vb = f - - @property - def e_heat(self): - return self._eh - @e_heat.setter - def e_heat(self, e): - if e > 1 or e < 0: - raise ValueError(f'Heat transfer efficiency must be in [0, 1], not {e}') - self._eh = e - - @property - def p_treatment(self): - return self._ptreat - @p_treatment.setter - def p_treatment(self, p): - self._ptreat = p # USD/kg-COD - -class CH4E(H2E): - - def __init__(self, ID='', ins=None, outs=(), thermo=None, init_with='WasteStream', - equipments=(), lifetime=20, COD_removal=0.55, CH4_yield=0.135, - frac_CH4=0.62, tau=15, T=273.15+35, d_wall=0.1, d_slab=0.1, - safety_factor=1.3, V_frac_beads=0.09, e_heat=0.8, - p_treatment=0.24, p_wall_concrete=497.25, - p_slab_concrete=267.75, p_steel=1203, **kwargs): - H2E.__init__(self, ID, ins, outs, thermo, init_with, equipments, lifetime, - COD_removal, 0, CH4_yield, 0, tau, T, safety_factor, - V_frac_beads, e_heat, p_treatment, **kwargs) - self.frac_CH4 = frac_CH4 - self.d_wall = d_wall - self.d_slab = d_slab - self.p_wall_concrete = p_wall_concrete - self.p_slab_concrete = p_slab_concrete - self.p_steel = p_steel - - def _run(self): - waste, = self.ins - eff, biogas = self.outs - eff.copy_like(waste) - biogas.phase = 'g' - - # COD removal - COD_rmv = eff.imass['COD'] * self.COD_removal - eff.imass['COD'] -= COD_rmv - biogas.imass['H2'] = h2 = COD_rmv*self.H2_yield - biogas.imass['CH4'] = ch4 = COD_rmv*self.CH4_yield - biogas.imass['N2'] = ch4/self.frac_CH4 - h2 - ch4 - - _units = { - 'Wall concrete volume': 'm3', - 'Slab concrete volume': 'm3', - 'Steel cover area': 'm2', - **H2E._units - } - - def _design(self): - H2E._design(self) - design = self.design_results - V = design['Reactor volume'] - design['Wall concrete volume'] = 2 * pi**(1/3) * V**(2/3) * self.d_wall - design['Slab concrete volume'] = pi**(1/3) * V**(2/3) * self.d_slab - design['Steel cover area'] = 5**(1/2) / 2 * pi**(1/3) * V**(2/3) - - def _cost(self): - D, C = self.design_results, self.baseline_purchase_costs - C['Wall concrete'] = D['Wall concrete volume'] * self.p_wall_concrete - C['Slab concrete'] = D['Slab concrete volume'] * self.p_slab_concrete - C['Steel cover'] = D['Steel cover area'] * self.p_steel - self.add_equipment_cost() - eff = self.outs[0] - if eff.isproduct(): - self.add_OPEX = {'Effluent treatment': self.p_treatment * eff.imass['COD']} # USD/hr - - @property - def frac_CH4(self): - return self._fch4 - @frac_CH4.setter - def frac_CH4(self, f): - if f > 1 or f < 0: - raise ValueError(f'CH4 mass fraction in biogas must be in [0, 1], not {f}') - self._fch4 = f - - @property - def d_wall(self): - return self._dw - - @d_wall.setter - def d_wall(self, d): - if d < 0: raise ValueError(f'wall thickness cannot be negative: {d}') - self._dw = d - - @property - def d_slab(self): - return self._ds - - @d_slab.setter - def d_slab(self, d): - if d < 0: raise ValueError(f'slab thickness cannot be negative: {d}') - self._ds = d - - @property - def p_wall_concrete(self): - return self._pwall - @p_wall_concrete.setter - def p_wall_concrete(self, p): - self._pwall = p # USD/m3 - - @property - def p_slab_concrete(self): - return self._pslab - @p_slab_concrete.setter - def p_slab_concrete(self, p): - self._pslab = p # USD/m3 - - @property - def p_steel(self): - return self._pstl - @p_steel.setter - def p_steel(self, p): - self._pstl = p # USD/m2 \ No newline at end of file