Source code for biosteam.wastewater.high_rate.system

# -*- coding: utf-8 -*-
# Bioindustrial-Park: BioSTEAM's Premier Biorefinery Models and Results
# Copyright (C) 2021-2024, Yalin Li <mailto.yalin.li@gmail.com>
#
# This module is under the UIUC open-source license. See
# github.com/BioSTEAMDevelopmentGroup/biosteam/blob/master/LICENSE.txt
# for license details.

__all__ = (
    'create_high_rate_wastewater_treatment_system',    
)

import thermosteam as tmo, biosteam as bst
cost = bst.decorators.cost
CEPCI = bst.design_tools.CEPCI_by_year
from . import (
    append_wwt_chemicals, default_insolubles,
    InternalCirculationRx, AnMBR, PolishingFilter, BeltThickener, SludgeCentrifuge,
    get_combustion_energy, prices, GWP_CFs,
)

_mgd_to_cmh = 157.7255 # auom('gallon').conversion_factor('m3')*1e6/24
_gpm_to_cmh = 0.2271 # auom('gallon').conversion_factor('m3')*60
_Gcal_to_kJ = 4184000 # auom('kcal').conversion_factor('kJ')*1e6 # (also MMkcal/hr)
_kW_to_kJhr = 3600 # auom('kW').conversion_factor('kJ/hr')

Rxn = tmo.reaction.Reaction
ParallelRxn = tmo.reaction.ParallelReaction

__all__ = (
    'BiogasUpgrading',
    'create_high_rate_wastewater_treatment_system',
    'CHP',
    'ReverseOsmosis',
    'Skipped',
    )

[docs] @cost(basis='Volumetric flow', ID='Reactor', units='m3/hr', # 2.7 in million gallons per day (MGD) cost=2450000, S=2.7*_mgd_to_cmh, CE=CEPCI[2012], n=1, BM=1.8) @cost(basis='Volumetric flow', ID='Evaporator', units='m3/hr', # 2.7 in million gallons per day (MGD) kW=1103.636, cost=5000000, S=2.7*_mgd_to_cmh, CE=CEPCI[2012], n=0.6, BM=1.6) class ReverseOsmosis(bst.Unit): _N_ins = 1 _N_outs = 2 _units = {'Volumetric flow': 'm3/hr'} @property def RO_treated_water(self): return self.outs[0] @property def brine(self): return self.outs[1] def _run(self): influent = self.ins[0] water, brine = self.outs self.design_results['Volumetric flow'] = self.F_vol_in # Based on stream 626 and 627 in ref [1] water.imass['Water'] = 376324/(376324+4967) * influent.imass['Water'] brine.mol = influent.mol - water.mol water.T = brine.T = influent.T
[docs] class Skipped(bst.Unit): """ Copy ins[`main_in`] as ins[`main_out`]. Can be also used to calculate the cost of wastewater treatment by clearing all WWT-related units and streams. Parameters ---------- main_in : int Which influent will be copied to `main_out`. main_out : int Which effluent will be copied from `main_in`. wwt_units : Iterable Collection of units whose costs will be cleared when `clear_wwt` is True. ins and outs of the units will be emptied if its price isn't 0. wwt_streams : Iterable Collection of streams which will be emptied when `clear_wwt` is True. Usually should at least includes the biogas and sludge stream. clear_wwt : bool Whether to clear the costs of and select streams associated with `wwt_units`. """ _ins_size_is_fixed = False _outs_size_is_fixed = False cache_dct = {} # to save some computation effort def _init(self, main_in=0, main_out=0, wwt_units=[], wwt_streams=[], clear_wwt=False): self.main_in = main_in self.main_out = main_out self.wwt_units = wwt_units self.wwt_streams = wwt_streams self.clear_wwt = clear_wwt def _run(self): self.outs[self.main_out].copy_like(self.ins[self.main_in]) def _cost(self): if not self.clear_wwt: return for u in self.wwt_units: u.baseline_purchase_costs.clear() u.installed_costs.clear() for hu in u.heat_utilities: hu.empty() u.power_utility(0) for s in u.ins+u.outs: if s.price: s.empty() u._utility_cost = 0. for s in self.wwt_streams: s.empty() @property def utility_cost(self): return 0.
[docs] class CHP(bst.Unit): """ Used to estimate the cost of producing electricity as in [6]_. Parameters ---------- eff : float Combined efficiency for combustion and power generation. unit_CAPEX : float Capital cost of the CHP per kW of power generated, $/kW. """ _ins_size_is_fixed = False _F_BM_default = {'CHP': 1.} def _init(self, eff=0.3375, # average of 40.5%, 27%, 36%, and 31.5% for the four types in ref [1] unit_CAPEX=1225): self.eff = eff self.unit_CAPEX = unit_CAPEX def _run(self): mixed, = self.outs mixed.mix_from(self.ins) H_net = get_combustion_energy(mixed, 1) self.H_for_power = H_net * self.eff def _design(self): kW = self.H_for_power / 3600 # kJ/hr to kW self.baseline_purchase_costs['CHP'] = kW*self.unit_CAPEX self.power_utility(-kW)
[docs] class BiogasUpgrading(bst.Unit): """ Upgrade the biogas to renewable natural gas (RNG). Note that the second influent is a dummy stream to calculate the upgrading cost. Parameters ---------- ratio : float How much of the incoming biogas will be upgraded to RNG. loss : float CH4 loss during upgrading. unit_upgrading_cost : float Unit cost for upgrading, in $/MMBtu. unit_upgrading_GWP : float Unit 100-yr global warming potential (GWP) for biogas upgrading, in kg CO2/MMBtu. References ---------- Yang et al., Cost and Life-Cycle Greenhouse Gas Implications of Integrating Biogas Upgrading and Carbon Capture Technologies in Cellulosic Biorefineries. Environ. Sci. Technol. 2020. https://doi.org/10.1021/acs.est.0c02816. IEA. Outlook for Biogas and Biomethane: Prospects for Organic Growth; IEA: Paris, 2020. https://www.iea.org/reports/outlook-for-biogas-and-biomethane-prospects-for-organic-growth Rai et al., Comparative Life Cycle Evaluation of the Global Warming Potential (GWP) Impacts of Renewable Natural Gas Production Pathways. Environ. Sci. Technol. 2022. https://doi.org/10.1021/acs.est.2c00093. """ # Default upgrading cost is calculated by # 0.18*0.914/35.3147*1055.056 # where $0.18/NM is from ref 1, # 0.914 ft3/MJ is from U.S. EIA https://www.eia.gov/energyexplained/units-and-calculators/energy-conversion-calculators.php#natgascalc # 35.3147 ft3/m3 is auom('m3').conversion_factor('ft3') # 1055.056 MJ/MMBtu is auom('MMBtu').conversion_factor('MJ') # This cost is close to the general range of $2-4 for a # 3.5 million m3/yr biogas plant in ref 2. # Default upgrading GWP is from calculated by # 8.67/1e3*1055.056 # where 8.67 g CO2/MJ is the average of upgrading GWP (5, 8, 13) # from Section S5 of ref 3 _N_ins = 2 _N_outs = 2 def _init(self, ratio=0, loss=0.05, unit_upgrading_cost=4.92, unit_upgrading_GWP=9.15): self.ratio = ratio self.loss = loss self.unit_upgrading_cost = unit_upgrading_cost self.unit_upgrading_GWP = unit_upgrading_GWP self.RIN_incentive = prices['RIN'] # Credits from the displaced fossil natural gas self.FNG_price = bst.stream_prices.get('Natural gas', 0) # $/kg self.FNG_CF = GWP_CFs['CH4'] def _run(self): biogas, foo = self.ins RNG, remained = self.outs RNG.empty() RNG.phase = remained.phase = 'g' RNG.imass['CH4'] = biogas.imass['CH4'] * self.ratio remained.mass = biogas.mass - RNG.mass # assume impurities left in the unused biogas RNG.mass *= (1 - self.loss) # lost methane not included in the unused biogas RNG.price = self.FNG_price + self.RIN_incentive RNG.characterization_factors['GWP'] = self.FNG_CF foo.copy_like(RNG) # Upgrading cost/GWP if not foo.isempty(): foo.price = self.unit_upgrading_cost/1055.056*(foo.HHV/1e3)/foo.F_mass # HHV in kJ/hr foo.characterization_factors['GWP'] = self.unit_upgrading_GWP/1055.056*(foo.HHV/1e3)/foo.F_mass
# %% # ============================================================================= # System function # ============================================================================= @bst.SystemFactory( ID='wastewater_sys', outs=[dict(ID='RNG'), # renewable natural gas dict(ID='biogas'), dict(ID='sludge'), dict(ID='RO_treated_water'), dict(ID='brine')], fixed_ins_size=False, fthermo=append_wwt_chemicals, ) def create_high_rate_wastewater_treatment_system( ins=None, outs=None, process_ID='6', flowsheet=None, autopopulate=False, skip_IC=False, IC_kwargs={}, skip_AnMBR=False, AnMBR_kwargs={}, skip_AeF=False, AeF_kwargs={} ): """ Return a system for wastewater treatment (WWT) as described in Li et al. [1]_ The system includes internal circulation (IC), anaerobic membrane bioreactors (AnMBR), aerobic polishing filter (AeF), belt thickener and sludge centrifuge, and a reverse osmosis unit. Users can choose whether to skip IC (makes sense when influent COD is only on the order of grams per liter), AnMBR (not recommended unless the COD is very low), and/or AeF (not recommended if want to achieve low COD in the effluent). An optional biogas upgrading unit can be included to upgrade the biogas (from IC and AnMBR) as renewable natural gas (RNG) for sale with incentives, this is achieved through adjusting `BiogasUpgrading.ratio`. Parameters ---------- ins : Wastewater streams (without solids). Defaults to all product streams at run time that are not sold and cannot generate energy through combustion (i.e. streams that have no sink, no price, and a LHV less that 1 kJ / g). outs : * [0] RNG * [1] biogas * [2] sludge * [3] RO_treated_water * [4] brine process_ID : float Number of the process. E.g., the default `process_ID` is 6, then the first mixer of this WWT system will be M601. flowsheet : Flowsheet, optional If provided, the WWT system will be added to the given flowsheet. autopopulate : bool, optional Whether to automatically add wastewater streams. skip_IC : bool Whether to skip the IC unit. IC_kwargs : dict kwargs to be passed to the IC unit (refer to the doc of the IC unit for details). skip_AnMBR : bool Whether to skip the AnMBR unit. AnMBR_kwargs : dict kwargs to be passed to the AnMBR unit (refer to the doc of the AnMBR unit for details). skip_AeF : bool Whether to skip the AeF unit. AeF_kwargs : dict kwargs to be passed to the AeF unit (refer to the doc of the AeF unit for details). Examples -------- Check for PolishingFilter vent accumulation >>> from biosteam import Stream, create_high_rate_wastewater_treatment_system, settings >>> from biorefineries import cornstover as cs >>> settings.set_thermo(cs.create_chemicals()) >>> feed = Stream( ... ID='wastewater', ... Water=2.634e+04, ... Ethanol=0.07225, ... AceticAcid=24.67, ... Furfural=6.206, ... Glycerol=1.784, ... LacticAcid=17.7, ... SuccinicAcid=3.472, ... DAP=1.001, ... AmmoniumSulfate=17.63, ... HMF=2.366, ... Glucose=2.816, ... Xylose=6.953, ... Arabinose=12.78, ... Extract=65.98, ... Ash=83.52, ... Lignin=1.659, ... SolubleLignin=4.202, ... GlucoseOligomer=6.796, ... GalactoseOligomer=0.01718, ... MannoseOligomer=0.009008, ... XyloseOligomer=2.878, ... ArabinoseOligomer=0.3508, ... Z_mobilis=0.6668, ... Protein=2.569, ... Glucan=0.1555, ... Xylan=0.06121, ... Xylitol=4.88, ... Cellobiose=0.9419, ... Arabinan=0.02242, ... Mannan=0.06448, ... Galactan=0.01504, ... Cellulase=25.4, ... units='kmol/hr' ... ) >>> wwt_sys = create_high_rate_wastewater_treatment_system(ins=feed) >>> wwt_sys.simulate() >>> wwt_sys.show('cwt100') System: wastewater_sys Highest convergence error among components in recycle stream M603-0 after 6 loops: - flow rate 1.57e+01 kmol/hr (0.066%) - temperature 1.19e-03 K (0.00039%) ins... [0] wastewater phase: 'l', T: 298.15 K, P: 101325 Pa composition (%): Water 94.7 Ethanol 0.000664 AceticAcid 0.296 Furfural 0.119 Glycerol 0.0328 LacticAcid 0.318 SuccinicAcid 0.0818 DAP 0.0264 AmmoniumSulfate 0.465 HMF 0.0595 Glucose 0.101 Xylose 0.208 Arabinose 0.383 Extract 2.37 Ash 0.0167 Lignin 0.0504 SolubleLignin 0.128 GlucoseOligomer 0.22 GalactoseOligomer 0.000556 MannoseOligomer 0.000291 XyloseOligomer 0.0759 ArabinoseOligomer 0.00925 Z_mobilis 0.00328 Protein 0.0117 Glucan 0.00503 Xylan 0.00161 Xylitol 0.148 Cellobiose 0.0643 Arabinan 0.000591 Mannan 0.00209 Galactan 0.000487 Cellulase 0.122 ----------------- 5.01e+05 kg/hr outs... [0] RNG phase: 'g', T: 298.15 K, P: 101325 Pa flow: 0 [1] biogas phase: 'g', T: 298.15 K, P: 101325 Pa composition (%): CH4 26.9 H2S 0.0348 CO2 73.1 --- 4.33e+04 kg/hr [2] sludge phase: 'l', T: 307.85 K, P: 101325 Pa composition (%): Water 80 Ethanol 2.49e-05 AceticAcid 0.0111 Furfural 0.00447 Glycerol 0.00123 NH3 0.0448 LacticAcid 0.0117 SuccinicAcid 0.00307 DAP 0.0636 AmmoniumSulfate 1.12 HMF 0.00224 Glucose 0.0038 Xylose 0.00679 Arabinose 0.0144 Extract 0.0872 Ash 0.832 Lignin 2.51 SolubleLignin 0.00469 GlucoseOligomer 0.0081 GalactoseOligomer 2.05e-05 MannoseOligomer 1.07e-05 XyloseOligomer 0.0028 ArabinoseOligomer 0.000341 Z_mobilis 0.163 Protein 0.584 Glucan 0.251 Xylan 0.0805 Xylitol 0.00556 Cellobiose 0.00242 Arabinan 0.0295 Mannan 0.104 Galactan 0.0243 WWTsludge 14 Cellulase 0.00447 ----------------- 1e+04 kg/hr [3] RO_treated_water phase: 'l', T: 303.15 K, P: 101325 Pa composition (%): Water 100 ----- 4.59e+05 kg/hr [4] brine phase: 'l', T: 303.15 K, P: 101325 Pa composition (%): Water 71.2 Ethanol 2.18e-05 AceticAcid 0.00971 Furfural 0.00391 Glycerol 0.00108 NH3 1.07 LacticAcid 0.0121 SuccinicAcid 0.00269 DAP 1.48 AmmoniumSulfate 26 HMF 0.00195 Glucose 0.00332 Xylose 0.0133 Arabinose 0.0126 Extract 0.09 SolubleLignin 0.00484 GlucoseOligomer 0.00822 GalactoseOligomer 2.08e-05 MannoseOligomer 1.09e-05 XyloseOligomer 0.00284 ArabinoseOligomer 0.000346 Xylitol 0.00486 Cellobiose 0.00211 Cellulase 0.00462 ----------------- 8.51e+03 kg/hr >>> u = wwt_sys.flowsheet.unit >>> print(round(u.R603.outs[3].F_mol, 2)) 50.46 >>> wwt_sys.simulate() >>> print(round(u.R603.outs[3].F_mol, 2)) 50.46 >>> wwt_sys.simulate() >>> print(round(u.R603.outs[3].F_mol, 2)) 50.46 Check if system can finish simulating with a dilute influent stream. >>> from biosteam import Stream, create_high_rate_wastewater_treatment_system, settings >>> from biorefineries import cornstover as cs >>> settings.set_thermo(cs.create_chemicals()) >>> feed = Stream( ... ID='wastewater', ... Water=2.634e+05, ... Ethanol=0.07225, ... AceticAcid=24.67, ... Furfural=6.206, ... Glycerol=1.784, ... LacticAcid=17.7, ... SuccinicAcid=3.472, ... DAP=1.001, ... AmmoniumSulfate=17.63, ... HMF=2.366, ... Glucose=2.816, ... Xylose=6.953, ... Arabinose=12.78, ... Extract=65.98, ... Ash=83.52, ... Lignin=1.659, ... SolubleLignin=4.202, ... GlucoseOligomer=6.796, ... GalactoseOligomer=0.01718, ... MannoseOligomer=0.009008, ... XyloseOligomer=2.878, ... ArabinoseOligomer=0.3508, ... Z_mobilis=0.6668, ... Protein=2.569, ... Glucan=0.1555, ... Xylan=0.06121, ... Xylitol=4.88, ... Cellobiose=0.9419, ... Arabinan=0.02242, ... Mannan=0.06448, ... Galactan=0.01504, ... Cellulase=25.4, ... units='kmol/hr' ... ) >>> wwt_sys = create_high_rate_wastewater_treatment_system(ins=feed) >>> wwt_sys.simulate() >>> wwt_sys.show('cwt100') System: wastewater_sys Highest convergence error among components in recycle stream M603-0 after 6 loops: - flow rate 5.52e+00 kmol/hr (0.027%) - temperature 4.36e-04 K (0.00014%) ins... [0] wastewater phase: 'l', T: 298.15 K, P: 101325 Pa composition (%): Water 99.4 Ethanol 6.98e-05 AceticAcid 0.031 Furfural 0.0125 Glycerol 0.00344 LacticAcid 0.0334 SuccinicAcid 0.00859 DAP 0.00277 AmmoniumSulfate 0.0488 HMF 0.00625 Glucose 0.0106 Xylose 0.0219 Arabinose 0.0402 Extract 0.249 Ash 0.00175 Lignin 0.00529 SolubleLignin 0.0134 GlucoseOligomer 0.0231 GalactoseOligomer 5.84e-05 MannoseOligomer 3.06e-05 XyloseOligomer 0.00797 ArabinoseOligomer 0.000971 Z_mobilis 0.000344 Protein 0.00123 Glucan 0.000528 Xylan 0.000169 Xylitol 0.0156 Cellobiose 0.00676 Arabinan 6.21e-05 Mannan 0.000219 Galactan 5.11e-05 Cellulase 0.0128 ----------------- 4.77e+06 kg/hr outs... [0] RNG phase: 'g', T: 298.15 K, P: 101325 Pa flow: 0 [1] biogas phase: 'g', T: 298.15 K, P: 101325 Pa composition (%): CH4 26.9 H2S 0.0348 CO2 73.1 --- 4.33e+04 kg/hr [2] sludge phase: 'l', T: 307.9 K, P: 101325 Pa composition (%): Water 79.9 Ethanol 2e-05 AceticAcid 0.00889 Furfural 0.00358 Glycerol 0.000986 NH3 0.0359 LacticAcid 0.00936 SuccinicAcid 0.00246 DAP 0.0511 AmmoniumSulfate 0.9 HMF 0.00179 Glucose 0.00304 Xylose 0.00544 Arabinose 0.0115 Extract 0.0698 Ash 0.838 Lignin 2.53 SolubleLignin 0.00375 GlucoseOligomer 0.00649 GalactoseOligomer 1.64e-05 MannoseOligomer 8.6e-06 XyloseOligomer 0.00224 ArabinoseOligomer 0.000273 Z_mobilis 0.165 Protein 0.589 Glucan 0.253 Xylan 0.0811 Xylitol 0.00445 Cellobiose 0.00193 Arabinan 0.0297 Mannan 0.105 Galactan 0.0245 WWTsludge 14.3 Cellulase 0.00358 ----------------- 9.97e+03 kg/hr [3] RO_treated_water phase: 'l', T: 303.15 K, P: 101325 Pa composition (%): Water 100 ----- 4.67e+06 kg/hr [4] brine phase: 'l', T: 303.15 K, P: 101325 Pa composition (%): Water 96.1 Ethanol 2.92e-06 AceticAcid 0.0013 Furfural 0.000522 Glycerol 0.000144 NH3 0.144 LacticAcid 0.00161 SuccinicAcid 0.000359 DAP 0.198 AmmoniumSulfate 3.49 HMF 0.000261 Glucose 0.000445 Xylose 0.00178 Arabinose 0.00168 Extract 0.012 SolubleLignin 0.000647 GlucoseOligomer 0.0011 GalactoseOligomer 2.78e-06 MannoseOligomer 1.46e-06 XyloseOligomer 0.000379 ArabinoseOligomer 4.62e-05 Xylitol 0.000651 Cellobiose 0.000282 Cellulase 0.000617 ----------------- 6.42e+04 kg/hr """ # Setup if flowsheet: bst.main_flowsheet.set_flowsheet(flowsheet) RNG, biogas, sludge, RO_treated_water, brine = outs RO_treated_water.register_alias('recycled_water') ##### Units ##### # Mix waste liquids for treatment X = str(process_ID) MX01 = bst.Mixer(f'M{X}01', ins=ins or []) MX01.autopopulate = False if autopopulate is None else autopopulate @MX01.add_specification(run=True) def autopopulate_waste_streams(): if MX01.autopopulate and not MX01.ins: sys = MX01.system streams = bst.FreeProductStreams(sys.streams) MX01.ins.extend(streams.noncombustible_slurries) for i in sys.facilities: if isinstance(i, bst.BlowdownMixer): MX01.ins.append(i.outs[0]) RX01_outs = (f'biogas_R{X}01', 'IC_eff', 'IC_sludge') if skip_IC: RX01 = Skipped(f'R{X}01', ins=MX01-0, outs=RX01_outs, main_in=0, main_out=1) # outs[0] is the gas phase else: RX01 = InternalCirculationRx(f'R{X}01', ins=MX01-0, outs=RX01_outs, T=35+273.15, **IC_kwargs) RX02_outs = (f'biogas_R{X}02', f'permeate_R{X}02', f'sludge_R{X}02', f'vent_R{X}02') if skip_AnMBR: RX02 = Skipped(f'R{X}02', ins=RX01-1, outs=RX02_outs, main_in=0, main_out=1) else: # Just setting the prices, flows will be updated upon simulation naocl_RX02 = tmo.Stream(f'naocl_R{X}02', NaOCl=0.125, Water=1-0.125, units='kg/hr') naocl_RX02.price = (naocl_RX02.F_mass/naocl_RX02.F_vol/1000)*prices['naocl'] # $/L to $/kg citric_RX02 = tmo.Stream(f'citric_R{X}02', CitricAcid=1, units='kg/hr') citric_RX02.price = (citric_RX02.F_mass/citric_RX02.F_vol/1000)*prices['citric_acid'] # $/L to $/kg bisulfite_RX02 = tmo.Stream(f'bisulfite_R{X}02', Bisulfite=0.38, Water=1-0.38, units='kg/hr') bisulfite_RX02.price = (bisulfite_RX02.F_mass/bisulfite_RX02.F_vol/1000)*prices['bisulfite'] # $/L to $/kg RX02 = AnMBR(f'R{X}02', ins=(RX01-1, '', naocl_RX02, citric_RX02, bisulfite_RX02, f'air_R{X}02'), outs=RX02_outs, reactor_type='CSTR', membrane_configuration='cross-flow', membrane_type='multi-tube', membrane_material='ceramic', include_aerobic_filter=False, add_GAC=False, include_degassing_membrane=True, # Below include in the TEA include_pump_building_cost=False, include_excavation_cost=False, **AnMBR_kwargs) RX03_outs = (f'biogas_R{X}03', f'treated_R{X}03', f'sludge_R{X}03', f'vent_R{X}03') if skip_AeF: RX03 = Skipped(f'R{X}03', ins=(RX02-1, ''), outs=RX03_outs, main_in=0, main_out=1) else: RX03 = PolishingFilter(f'R{X}03', ins=(RX02-1, '', f'air_R{X}03'), outs=RX03_outs, filter_type='aerobic', include_degassing_membrane=False, # Below include in the TEA include_pump_building_cost=False, include_excavation_cost=False, **AeF_kwargs) MX02 = bst.Mixer(f'M{X}02', ins=(RX01-0, RX02-0, RX03-0)) BiogasUpgrading('Upgrading', ins=(MX02-0, 'upgrading_placeholder'), outs=(RNG, biogas)) # Recycled the majority of sludge (96%) to the aerobic filter, # 96% from the membrane bioreactor in ref [2] SX01 = bst.Splitter(f'S{X}01', ins=RX03-2, outs=(f'recycled_S{X}01', f'wasted_S{X}01'), split=0.96) solubles = [i.ID for i in SX01.chemicals if not i.ID in default_insolubles] SX02 = BeltThickener(f'S{X}02', ins=(RX01-2, RX02-2, SX01-1), outs=(f'eff_S{X}02', f'sludge_S{X}02'), sludge_moisture=0.96, solubles=solubles) SX03 = SludgeCentrifuge(f'S{X}03', ins=SX02-1, outs=(f'centrate_S{X}03', sludge), sludge_moisture=0.8, solubles=solubles, centrifuge_type='reciprocating_pusher') # Mix recycles to aerobic digestion bst.Mixer(f'M{X}03', ins=(SX01-0, SX02-0, SX03-0), outs=1-RX03) # Reverse osmosis to treat aerobically polished water ReverseOsmosis(f'S{X}04', ins=RX03-1, outs=(RO_treated_water, brine))