def test_StorageTank(): bst_s, qs_ws = create_streams(1) bst.settings.set_thermo(chems) bst_unit = bst.units.StorageTank(ins=bst_s) qs.set_thermo(cmps) qs_unit = qs.sanunits.StorageTank(ins=qs_ws) check_results(bst_unit, qs_unit)
def test_Splitter(): bst_s, qs_ws = create_streams(1) bst.settings.set_thermo(chems) bst_unit = bst.units.Splitter(ins=bst_s, split=0.1) qs.set_thermo(cmps) qs_unit = qs.sanunits.Splitter(ins=qs_ws, split=0.1) check_results(bst_unit, qs_unit)
def test_HXutility(): bst_s, qs_ws = create_streams(1) bst.settings.set_thermo(chems) bst_unit = bst.units.HXutility(ins=bst_s, T=400, rigorous=False) #!!! Try True qs.set_thermo(cmps) qs_unit = qs.sanunits.HXutility(ins=qs_ws, T=400, rigorous=False) #!!! Try True check_results(bst_unit, qs_unit)
def test_HXprocess(): bst_s, qs_ws = create_streams(2) bst_s[0].T = qs_ws[0].T = 400 bst.settings.set_thermo(chems) bst_unit = bst.units.HXprocess(ins=bst_s, phase0='l', phase1='l') qs.set_thermo(cmps) qs_unit = qs.sanunits.HXprocess(ins=qs_ws, phase0='l', phase1='l') check_results(bst_unit, qs_unit)
def test_bsm1(): from numpy.testing import assert_allclose as ac from numpy import arange from qsdsan import set_thermo from exposan import bsm1 as b1 sys = b1.bsm1 set_thermo(b1.cmps) sys.reset_cache() sys.simulate(t_span=(0, 50), method='BDF', t_eval=arange(0, 51, 1)) assert sys.outs[0].isempty() == False ac(float(sys.outs[0].iconc['S_S']), 0.895, rtol=1e-2) ac(float(sys.outs[1].iconc['X_BH']), 4994.3, rtol=1e-2) ac(sys.outs[0].COD, 47.5, rtol=1e-2) ac(sys.outs[1].get_TSS(), 6377.9, rtol=1e-2)
def create_streams(num): bst.settings.set_thermo(chems) bst_s = [] for n in range(num): s = bst.Stream(Methanol=100 * (n + 1), Ethanol=100 * (n + 1), units='kg/hr') bst_s.append(s) qs.set_thermo(cmps) qs_ws = [] for n in range(num): ws = qs.WasteStream(Methanol=100 * (n + 1), Ethanol=100 * (n + 1), units='kg/hr') qs_ws.append(ws) return bst_s, qs_ws
def test_waste_stream(): import pytest, numpy as np from numpy.testing import assert_allclose from math import isclose from qsdsan import set_thermo, Components, WasteStream components = Components.load_default() set_thermo(components) ws1 = WasteStream.codstates_inf_model('ws1', 1e5) ws2 = WasteStream.codstates_inf_model('ws2', 1e5*24/1e3, units=('m3/d', 'g/m3')) assert isclose(ws1.COD, 430, rel_tol=1e-2) assert isclose(ws1.TKN, 40, rel_tol=1e-2) assert isclose(ws1.TP, 10, rel_tol=1e-2) assert isclose(ws1.F_vol, ws2.F_vol) ws3 = WasteStream(S_Ac=5, H2O=1000, units='kg/hr') ws4 = WasteStream(X_NOO=10, H2O=1000, units='kg/hr') ws5 = WasteStream() ws5.mix_from((ws3, ws4)) assert_allclose(ws5.F_mass, 2015.0) # TODO: After updating the default component properties, # add in tests here to make sure COD, etc. are calculated correctly assert_allclose(ws5.COD, 7414.267796, rtol=1e-2) # Make sure below attributes are calculated based on flow info, cannot be set with pytest.raises(AttributeError): ws5.COD = 5 # Concentration calclation ws6 = WasteStream(X_CaCO3=1, H2O=1000, units='kg/hr') assert_allclose(np.abs(ws6.conc.value-ws6.mass/ws6.F_vol*1e3).sum(), 0, atol=1e-6) ws6.imass['X_B_Subst', 'X_GAO_PHA'] = (100, 1) ws7 = WasteStream(X_CaCO3=1, X_B_Subst=100, X_GAO_PHA=1, H2O=1000, units='kg/hr') assert_allclose(np.abs(ws6.conc.value-ws7.mass/ws7.F_vol*1e3).sum(), 0, atol=1e-6) ws6.mass[:] = 1e-3 ws6.imass['H2O'] = 1e3 diff = ws6.conc.value - np.ones_like(ws6.conc.value) diff[components.index('H2O')] = 0 assert_allclose(np.max(np.abs(diff)), 0, atol=1e-2)
def _load_components(): global components, _components_loaded components = create_components() qs.set_thermo(components) _components_loaded = True
def test_component(): import pytest from qsdsan import Chemical, Component, Components, set_thermo, \ _waste_stream as ws_module from chemicals.elements import molecular_weight from math import isclose S_NH4 = Component('S_NH4', formula='NH4+', measured_as='N', f_BOD5_COD=0, f_uBOD_COD=0, f_Vmass_Totmass=0, description="Ammonium", particle_size="Soluble", degradability="Undegradable", organic=False) assert S_NH4.i_N == 1 assert S_NH4.i_NOD == molecular_weight({'O': 4}) / molecular_weight( {'N': 1}) S_NH4.measured_as = None assert S_NH4.i_mass == 1 S_Ac = Component('S_Ac', formula='CH3COO-', measured_as='COD', f_BOD5_COD=0.717, f_uBOD_COD=0.863, f_Vmass_Totmass=1, description="Acetate", particle_size="Soluble", degradability="Readily", organic=True) assert S_Ac.i_COD == 1 S_Ac.measured_as = None assert S_Ac.i_mass == 1 assert S_Ac.i_COD == molecular_weight({'O': 4}) / molecular_weight({ 'C': 2, 'H': 3, 'O': 2 }) S_HS = Component.from_chemical('S_HS', Chemical('Hydrosulfide'), particle_size="Soluble", degradability="Undegradable", organic=False) assert S_HS.i_charge < 0 S_HS.measured_as = 'S' assert S_HS.i_mass > 1 # Check default components cmps1 = Components.load_default(default_compile=False) H2O_chemical = Chemical('H2O') H2O = Component.from_chemical('H2O', H2O_chemical) with pytest.raises(ValueError): # H2O already in default components cmps1.append(H2O) with pytest.raises( RuntimeError): # key chemical-related properties missing cmps1.compile() # Can compile with default-filling those missing properties cmps1.default_compile(lock_state_at='', particulate_ref='NaCl') cmps2 = Components((cmp for cmp in cmps1 if cmp.ID != 'H2O')) H2O = Component.from_chemical('H2O', Chemical('H2O'), particle_size='Soluble', degradability='Undegradable', organic=False) cmps2.append(H2O) cmps2.default_compile(lock_state_at='', particulate_ref='NaCl') cmps3 = Components.load_default() assert cmps3.S_H2.measured_as == 'COD' assert cmps3.S_H2.i_COD == 1 assert isclose(cmps3.S_N2.i_COD, -molecular_weight({'O': 1.5}) / molecular_weight({'N': 1}), rel_tol=1e-3) assert isclose(cmps3.S_NO3.i_COD, -molecular_weight({'O': 4}) / molecular_weight({'N': 1}), rel_tol=1e-3) set_thermo(cmps3) # Check if the default groups are up-to-date cached_cmp_IDs = ws_module._default_cmp_IDs cached_cmp_groups = ws_module._specific_groups assert set(cmps3.IDs) == cached_cmp_IDs get_IDs = lambda attr: {cmp.ID for cmp in getattr(cmps3, attr)} for attr, IDs in cached_cmp_groups.items(): assert IDs == get_IDs(attr)
c1s = {k: v for k, v in dct['C1_s'].items() if v > 0} c1x = {k: v for k, v in dct['C1_x'].items() if v > 0} tss = [v for v in dct['C1_tss'].values() if v > 0] C1.set_init_solubles(**c1s) C1.set_init_sludge_solids(**c1x) C1.set_init_TSS(tss) #%% # ============================================================================= # Benchmark Simulation Model No. 1 # ============================================================================= ############# load components and set thermo ############# cmps = components = pc.load_asm1_cmps() set_thermo(cmps) ############# create WasteStream objects ################# Q = 18446 # influent flowrate [m3/d] Temp = 273.15 + 20 # temperature [K] PE = WasteStream('Wastewater', T=Temp) inf_kwargs = { 'concentrations': { 'S_S': 69.5, 'X_BH': 28.17, 'X_S': 202.32, 'X_I': 51.2, 'S_NH': 31.56, 'S_I': 30, 'S_ND': 6.95,
def test_process(): import pytest import os from sympy import symbols, Eq from sympy.parsing.sympy_parser import parse_expr from math import isclose from qsdsan import set_thermo, Components, Process, Processes, CompiledProcesses, _pk import qsdsan.processes as pc cmps = Components.load_default() S_A = cmps.S_Ac.copy('S_A') S_ALK = cmps.S_CO3.copy('S_ALK') # measured as g C S_F = cmps.S_F.copy('S_F') S_I = cmps.S_U_E.copy('S_I') S_N2 = cmps.S_N2.copy('S_N2') S_NH4 = cmps.S_NH4.copy('S_NH4') S_NO3 = cmps.S_NO3.copy('S_NO3') S_O2 = cmps.S_O2.copy('S_O2') S_PO4 = cmps.S_PO4.copy('S_PO4') X_AUT = cmps.X_AOO.copy('X_AUT') X_H = cmps.X_OHO.copy('X_H') X_I = cmps.X_U_OHO_E.copy('X_I') X_MeOH = cmps.X_FeOH.copy('X_MeOH') X_MeP = cmps.X_FePO4.copy('X_MeP') X_PAO = cmps.X_PAO.copy('X_PAO') X_PHA = cmps.X_PAO_PHA.copy('X_PHA') X_PP = cmps.X_PAO_PP_Lo.copy('X_PP') X_S = cmps.X_B_Subst.copy('X_S') S_I.i_N = 0.01 S_F.i_N = 0.03 X_I.i_N = 0.02 X_S.i_N = 0.04 X_H.i_N = X_PAO.i_N = X_AUT.i_N = 0.07 S_I.i_P = 0.00 S_F.i_P = 0.01 X_I.i_P = 0.01 X_S.i_P = 0.01 X_H.i_P = X_PAO.i_P = X_AUT.i_P = 0.02 cmps_asm2d = Components([ S_O2, S_F, S_A, S_NH4, S_NO3, S_PO4, S_I, S_ALK, S_N2, X_I, X_S, X_H, X_PAO, X_PP, X_PHA, X_AUT, X_MeOH, X_MeP ]) cmps_asm2d.compile() set_thermo(cmps_asm2d) p1 = Process( 'aero_hydrolysis', 'X_S -> [1-f_SI]S_F + [f_SI]S_I + [?]S_NH4 + [?]S_PO4 + [?]S_ALK', ref_component='X_S', rate_equation='K_h * S_O2/(K_O2+S_O2) * X_S/(K_X*X_H+X_S) * X_H', parameters=('f_SI', 'K_h', 'K_O2', 'K_X')) f_SI = symbols('f_SI') assert abs(sum(p1._stoichiometry * p1._components.i_N).subs({'f_SI': 1 })) < 1e-8 assert abs(sum(p1._stoichiometry * p1._components.i_N).subs({'f_SI': 0 })) < 1e-8 assert abs(sum(p1._stoichiometry * p1._components.i_P).subs({'f_SI': 1 })) < 1e-8 assert abs( sum(p1._stoichiometry * p1._components.i_charge).subs({'f_SI': 1 })) < 1e-8 p1.set_parameters(f_SI=0.0) assert p1.parameters['f_SI'] == 0.0 assert Eq(p1._stoichiometry[p1._components._index['S_I']], parse_expr('1*f_SI')) p12 = Process( 'anox_storage_PP', 'S_PO4 + [Y_PHA]X_PHA + [?]S_NO3 -> X_PP + [?]S_N2 + [?]S_NH4 + [?]S_ALK', ref_component='X_PP', rate_equation= 'q_PP * S_O2/(K_O2+S_O2) * S_PO4/(K_PS+S_PO4) * S_ALK/(K_ALK+S_ALK) * (X_PHA/X_PAO)/(K_PHA+X_PHA/X_PAO) * (K_MAX-X_PP/X_PAO)/(K_PP+K_MAX-X_PP/X_PAO) * X_PAO * eta_NO3 * K_O2/S_O2 * S_NO3/(K_NO3+S_NO3)', parameters=('Y_PHA', 'q_PP', 'K_O2', 'K_PS', 'K_ALK', 'K_PHA', 'eta_NO3', 'K_PP', 'K_NO3'), conserved_for=('COD', 'N', 'P', 'NOD', 'charge')) p14 = Process( 'PAO_anox_growth', '[1/Y_H]X_PHA + [?]S_NO3 + [?]S_PO4 -> X_PAO + [?]S_N2 + [?]S_NH4 + [?]S_ALK', ref_component='X_PAO', rate_equation= 'mu_PAO * S_O2/(K_O2 + S_O2) * S_NH4/(K_NH4 + S_NH4) * S_PO4/(K_P + S_PO4) * S_CO3/(K_ALK + S_ALK) * (X_PHA/X_PAO)/(K_PHA + X_PHA/X_PAO) * X_PAO * eta_NO3 * K_O2/S_O2 * S_NO3/(K_NO3 + S_NO3)', parameters=('Y_H', 'mu_PAO', 'K_O2', 'K_NH4', 'K_P', 'K_ALK', 'K_PHA', 'eta_NO3', 'K_NO3'), conserved_for=('COD', 'N', 'P', 'NOD', 'charge')) PAO_anox_processes = Processes([p12, p14]) assert PAO_anox_processes.PAO_anox_growth.ref_component == X_PAO with pytest.raises(AttributeError): print(PAO_anox_processes.production_rates) params = ('f_SI', 'Y_H', 'f_XI', 'Y_PO4', 'Y_PHA', 'Y_A', 'K_h', 'eta_NO3', 'eta_fe', 'K_O2', 'K_NO3', 'K_X', 'mu_H', 'q_fe', 'eta_NO3_deni', 'b_H', 'K_F', 'K_fe', 'K_A', 'K_NH4', 'K_P', 'K_ALK', 'q_PHA', 'q_PP', 'mu_PAO', 'b_PAO', 'b_PP', 'b_PHA', 'K_PS', 'K_PP', 'K_MAX', 'K_IPP', 'K_PHA', 'mu_AUT', 'b_AUT', 'K_O2_AUT', 'K_NH4_AUT', 'K_ALK_2', 'k_PRE', 'k_RED') path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ASM2d_original.tsv') asm2d = Processes.load_from_file(path, conserved_for=('COD', 'N', 'P', 'charge'), parameters=params, compile=False) asm2d.extend(PAO_anox_processes) asm2d.compile() assert isinstance(asm2d, CompiledProcesses) assert p12 in asm2d assert set(asm2d.parameters.keys()) == set(params) # Try re-pickling if the tests are run locally and # the environment supports Pickle Protocol 5 pickle = True if _pk else False try: pc.load_asm1_cmps() except: pc._asm1._create_asm1_cmps(pickle=pickle) try: pc.load_asm2d_cmps() except: pc._asm2d._create_asm2d_cmps(pickle=pickle) pc.load_asm2d_cmps()
def create_asm_validation_system(process_model='ASM1', aerated=False): suffix = 'aer' if aerated else 'an' flowsheet = qs.Flowsheet(f'{process_model}_{suffix}') qs.main_flowsheet.set_flowsheet(flowsheet) # Load process model (including associated components) pc_lower = process_model.lower() if pc_lower == 'asm1': # Thermodynamic conditions cmps = pc.load_asm1_cmps() set_thermo(cmps) # Influent inf_kwargs = { 'concentrations': { 'S_S': 69.5, 'X_BH': 28.17, 'X_S': 202.32, 'X_I': 51.2, 'S_NH': 31.56, 'S_I': 30, 'S_ND': 6.95, 'X_ND': 10.59, 'S_ALK': 7*12, }, 'units': ('m3/d', 'mg/L'), } # Process model asm = pc.ASM1( Y_A=0.24, Y_H=0.67, f_P=0.08, i_XB=0.08, i_XP=0.06, mu_H=4.0, K_S=10.0, K_O_H=0.2, K_NO=0.5, b_H=0.3, eta_g=0.8, eta_h=0.8, k_h=3.0, K_X=0.1, mu_A=0.5, K_NH=1.0, b_A=0.05, K_O_A=0.4, k_a=0.05, fr_SS_COD=1/1.48, path=os.path.join(asm_path, 'data/_asm1.tsv'), ) # Initial conditions in the CSTR init_conds = { 'S_I': 30, 'S_S': 5, 'X_I': 1000, 'X_S': 100, 'X_BH': 500, 'X_BA': 100, 'X_P': 100, 'S_O': 2, 'S_NO': 20, 'S_NH': 2, 'S_ND': 1, 'X_ND': 1, 'S_ALK': 7*12, } DO_ID = 'S_O' elif pc_lower == 'asm2d': cmps = pc.load_asm2d_cmps() set_thermo(cmps) inf_kwargs = { 'concentrations': { # Henze et al., Activated Sludge Models ASM1, ASM2, ASM2d and ASM3, P91 'S_I': 30, 'S_F': 30, 'S_A': 0, 'S_NH4': 16, 'S_PO4': 3.6, 'S_ALK': 5*12, # mmol/L to mg C/L 'X_I': 25, 'X_S': 125, 'X_H': 30, }, 'units': ('m3/d', 'mg/L'), } asm = pc.ASM2d( iN_SI=0.01, iN_SF=0.03, iN_XI=0.02, iN_XS=0.04, iN_BM=0.07, iP_SI=0.0, iP_SF=0.01, iP_XI=0.01, iP_XS=0.01, iP_BM=0.02, iTSS_XI=0.75, iTSS_XS=0.75, iTSS_BM=0.9, f_SI=0.0, Y_H=0.67, f_XI_H=0.1, Y_PAO=0.625, Y_PO4=0.4, Y_PHA=0.2, f_XI_PAO=0.1, Y_A=0.24, f_XI_AUT=0.1, K_h=3.0, eta_NO3=0.6, eta_fe=0.4, K_O2=0.2, K_NO3=0.5, K_X=0.1, mu_H=4.0, q_fe=3.0, eta_NO3_H=0.8, b_H=0.3, K_O2_H=0.2, K_F=4.0, K_fe=4.0, K_A_H=4.0, K_NO3_H=0.5, K_NH4_H=0.05, K_P_H=0.01, K_ALK_H=0.1, q_PHA=3.0, q_PP=1.5, mu_PAO=1.0, eta_NO3_PAO=0.6, b_PAO=0.2, b_PP=0.2, b_PHA=0.2, K_O2_PAO=0.2, K_NO3_PAO=0.5, K_A_PAO=4.0, K_NH4_PAO=0.05, K_PS=0.2, K_P_PAO=0.01, K_ALK_PAO=0.1, K_PP=0.01, K_MAX=0.34, K_IPP=0.02, K_PHA=0.01, mu_AUT=1.0, b_AUT=0.15, K_O2_AUT=0.5, K_NH4_AUT=1.0, K_ALK_AUT=0.5, K_P_AUT=0.01, k_PRE=1.0, k_RED=0.6, K_ALK_PRE=0.5, ) init_conds = { 'S_I': 30, 'S_F': 5, 'S_A': 5, 'X_I': 1000, 'X_S': 100, 'X_H': 500, 'X_AUT': 100, 'S_O2': 2, 'S_NH4': 2, 'S_N2': 0, 'S_NO3': 20, 'S_PO4': 5, 'X_PAO': 200, 'X_PP': 100, 'X_PHA': 100, 'S_ALK': 7*12, } DO_ID = 'S_O2' else: raise ValueError(f'`process_model` can only be "ASM1" or "ASM2d", not {process_model}.') # Aeration if aerated: V = V_aer # aer = pc.DiffusedAeration('aer', DO_ID, KLa=240, DOsat=8.0, V=V) aer = 2 # fixed DO at 2 mg/L else: V = V_an aer = None # Set up system inf = WasteStream('influent', T=Temp) inf.set_flow_by_concentration(Q, **inf_kwargs) eff = WasteStream('effluent', T=Temp) CSTR = su.CSTR('CSTR', ins=inf, outs=eff, V_max=V, DO_ID=DO_ID, aeration=aer, suspended_growth_model=asm) CSTR.set_init_conc(**init_conds) sys = System('sys', path=(CSTR,)) sys.set_dynamic_tracker(CSTR, inf, eff) return sys
def test_cas(): from qsdsan import set_thermo from exposan import cas set_thermo(cas.cmps) cas.sys.simulate()