#################################################################################
# WaterTAP Copyright (c) 2020-2024, The Regents of the University of California,
# through Lawrence Berkeley National Laboratory, Oak Ridge National Laboratory,
# National Renewable Energy Laboratory, and National Energy Technology
# Laboratory (subject to receipt of any required approvals from the U.S. Dept.
# of Energy). All rights reserved.
#
# Please see the files COPYRIGHT.md and LICENSE.md for full copyright and license
# information, respectively. These files are also available online at the URL
# "https://github.com/watertap-org/watertap/"
#################################################################################
import itertools
from pyomo.environ import (
ConcreteModel,
value,
Param,
Var,
Constraint,
Expression,
Objective,
TransformationFactory,
NonNegativeReals,
RangeSet,
check_optimal_termination,
assert_optimal_termination,
units as pyunits,
)
from pyomo.network import Arc
from idaes.core import FlowsheetBlock
from idaes.core.solvers import get_solver
from idaes.core.util.model_statistics import degrees_of_freedom
from idaes.core.util.initialization import (
propagate_state as _pro_state,
)
from idaes.models.unit_models import Mixer, Separator, Product, Feed
from idaes.core import UnitModelCostingBlock
import idaes.core.util.scaling as iscale
from idaes.core.util.misc import StrEnum
import watertap.property_models.NaCl_prop_pack as props
from watertap.core.util.initialization import (
assert_degrees_of_freedom,
)
from watertap.unit_models.reverse_osmosis_0D import (
ReverseOsmosis0D,
ConcentrationPolarizationType,
)
from watertap.unit_models.osmotically_assisted_reverse_osmosis_0D import (
OsmoticallyAssistedReverseOsmosis0D,
MassTransferCoefficient,
PressureChangeType,
)
from watertap.unit_models.pressure_changer import Pump, EnergyRecoveryDevice
from watertap.costing import WaterTAPCosting
from watertap.costing.unit_models.pump import cost_low_pressure_pump
[docs]class ERDtype(StrEnum):
pump_as_turbine = "pump_as_turbine"
def erd_type_not_found(erd_type):
raise NotImplementedError(
"erd_type was {}, but can only " "be pump_as_turbine" "".format(erd_type.value)
)
def propagate_state(arc):
_pro_state(arc)
print(arc.destination.name)
arc.destination.display()
def main(number_of_stages, system_recovery, erd_type=ERDtype.pump_as_turbine):
# set up solver
solver = get_solver()
# build, set, and initialize
m = build(number_of_stages=number_of_stages, erd_type=erd_type)
set_operating_conditions(m)
initialize_system(
m,
number_of_stages,
solvent_multiplier=0.5,
solute_multiplier=0.7,
solver=solver,
)
optimize_set_up(
m, number_of_stages=number_of_stages, water_recovery=system_recovery
)
results = solve(m, solver=solver)
assert_optimal_termination(results)
print("\n***---Optimization results---***")
display_system(m)
display_design(m)
if erd_type == ERDtype.pump_as_turbine:
display_state(m)
else:
pass
return m
def build(number_of_stages, erd_type=ERDtype.pump_as_turbine):
# flowsheet set up
m = ConcreteModel()
m.fs = FlowsheetBlock(dynamic=False)
m.fs.erd_type = erd_type
m.fs.properties = props.NaClParameterBlock()
m.fs.costing = WaterTAPCosting()
# stage set up
m.fs.NumberOfStages = Param(initialize=number_of_stages)
m.fs.Stages = RangeSet(m.fs.NumberOfStages)
m.fs.NonFirstStages = RangeSet(2, m.fs.NumberOfStages)
m.fs.NonFinalStages = RangeSet(m.fs.NumberOfStages - 1)
if number_of_stages > 2:
m.fs.IntermediateStages = RangeSet(2, m.fs.NumberOfStages - 1)
else:
m.fs.IntermediateStages = RangeSet(0)
m.fs.FirstStage = m.fs.Stages.first()
m.fs.LastStage = m.fs.Stages.last()
# Control volume flow blocks
m.fs.feed = Feed(property_package=m.fs.properties)
m.fs.product = Product(property_package=m.fs.properties)
m.fs.disposal = Product(property_package=m.fs.properties)
# --- Main pump ---
m.fs.PrimaryPumps = Pump(m.fs.Stages, property_package=m.fs.properties)
for pump in m.fs.PrimaryPumps.values():
pump.costing = UnitModelCostingBlock(
flowsheet_costing_block=m.fs.costing,
)
# --- Recycle pump ---
m.fs.RecyclePumps = Pump(m.fs.NonFirstStages, property_package=m.fs.properties)
for pump in m.fs.RecyclePumps.values():
pump.costing = UnitModelCostingBlock(
flowsheet_costing_block=m.fs.costing, costing_method=cost_low_pressure_pump
)
m.fs.total_pump_work = Expression(
expr=sum(
pyunits.convert(pump.work_mechanical[0], to_units=pyunits.kW)
for pump in itertools.chain(
m.fs.PrimaryPumps.values(), m.fs.RecyclePumps.values()
)
)
)
# --- Reverse Osmosis Block ---
m.fs.RO = ReverseOsmosis0D(
property_package=m.fs.properties,
has_pressure_change=True,
pressure_change_type=PressureChangeType.calculated,
mass_transfer_coefficient=MassTransferCoefficient.calculated,
concentration_polarization_type=ConcentrationPolarizationType.calculated,
has_full_reporting=True,
)
m.fs.RO.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
# --- Osmotically Assisted Reverse Osmosis Block ---
m.fs.OAROUnits = OsmoticallyAssistedReverseOsmosis0D(
m.fs.NonFinalStages,
property_package=m.fs.properties,
has_pressure_change=True,
pressure_change_type=PressureChangeType.calculated,
mass_transfer_coefficient=MassTransferCoefficient.calculated,
concentration_polarization_type=ConcentrationPolarizationType.calculated,
has_full_reporting=True,
)
for stage in m.fs.OAROUnits.values():
stage.costing = UnitModelCostingBlock(
flowsheet_costing_block=m.fs.costing,
costing_method_arguments={"oaro_type": "high_pressure"},
)
# --- ERD blocks ---
if erd_type == ERDtype.pump_as_turbine:
# add energy recovery turbine block
m.fs.EnergyRecoveryDevices = EnergyRecoveryDevice(
m.fs.Stages, property_package=m.fs.properties
)
# add costing for ERD config
for erd in m.fs.EnergyRecoveryDevices.values():
erd.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
else:
erd_type_not_found(erd_type)
# --- Separators ---
m.fs.Separators = Separator(
m.fs.NonFirstStages,
property_package=m.fs.properties,
outlet_list=["treat", "purge"],
)
if number_of_stages > 1:
separator_list = ["product"]
for i in range(2, number_of_stages + 1):
separator_list.append("makeup" + str(i))
m.fs.ProductSeparator = Separator(
property_package=m.fs.properties, outlet_list=separator_list
)
# --- Mixers ---
m.fs.Mixers = Mixer(
m.fs.NonFirstStages,
property_package=m.fs.properties,
inlet_list=["treat", "makeup"],
)
if number_of_stages > 1:
mixer_list = []
for i in range(1, number_of_stages + 1):
mixer_list.append("purge" + str(i))
m.fs.WasteMixer = Mixer(property_package=m.fs.properties, inlet_list=mixer_list)
m.fs.recovered_pump_work = Expression(
expr=sum(
pyunits.convert(erd.work_mechanical[0], to_units=pyunits.kW)
for erd in m.fs.EnergyRecoveryDevices.values()
)
)
m.fs.net_pump_work = Expression(
expr=m.fs.total_pump_work + m.fs.recovered_pump_work
)
# additional parameters, variables or expressions
m.fs.oaro_min_pressure = Param(initialize=10e5, units=pyunits.Pa, mutable=True)
m.fs.oaro_max_pressure = Param(initialize=65e5, units=pyunits.Pa, mutable=True)
m.fs.ro_min_pressure = Param(initialize=10e5, units=pyunits.Pa, mutable=True)
m.fs.ro_max_pressure = Param(initialize=85e5, units=pyunits.Pa, mutable=True)
m.fs.recycle_pump_min_pressure = Param(
initialize=1e5, units=pyunits.Pa, mutable=True
)
m.fs.recycle_pump_max_pressure = Param(
initialize=85e5, units=pyunits.Pa, mutable=True
)
# process costing and add system level metrics
m.fs.costing.utilization_factor.fix(0.9)
m.fs.costing.TIC.fix(2)
m.fs.costing.maintenance_labor_chemical_factor.fix(0.03)
# unfix wacc since we fix capital_recovery_factor
m.fs.costing.wacc.unfix()
m.fs.costing.capital_recovery_factor.fix(0.1)
m.fs.costing.electricity_cost.set_value(0.07)
m.fs.costing.reverse_osmosis.factor_membrane_replacement.fix(0.15)
m.fs.costing.reverse_osmosis.membrane_cost.fix(30)
m.fs.costing.reverse_osmosis.high_pressure_membrane_cost.fix(50)
m.fs.costing.high_pressure_pump.cost.fix(53 / 1e5 * 3600)
m.fs.costing.energy_recovery_device.pressure_exchanger_cost.fix(535)
m.fs.costing.cost_process()
product_flow_vol_total = m.fs.product.properties[0].flow_vol
m.fs.costing.add_annual_water_production(product_flow_vol_total)
m.fs.costing.add_LCOW(product_flow_vol_total)
m.fs.costing.add_specific_energy_consumption(product_flow_vol_total)
m.fs.costing.add_specific_electrical_carbon_intensity(product_flow_vol_total)
# Expressions for parameter sweep -----------------------------------------
# Final permeate concentration as mass fraction
m.fs.product.properties[0].mass_frac_phase_comp
# Touch feed concentration as mass concentration
m.fs.feed.properties[0].conc_mass_phase_comp
# Touch final brine concentration as mass concentration
m.fs.disposal.properties[0].conc_mass_phase_comp
# Touch final brine concentration as mass fraction
m.fs.disposal.properties[0].mass_frac_phase_comp
m.fs.system_salt_rejection = Expression(
expr=1
- m.fs.product.properties[0].conc_mass_phase_comp["Liq", "NaCl"]
/ m.fs.feed.properties[0].conc_mass_phase_comp["Liq", "NaCl"]
)
m.fs.annual_feed = Expression(
expr=pyunits.convert(
m.fs.feed.properties[0].flow_vol, to_units=pyunits.m**3 / pyunits.year
)
* m.fs.costing.utilization_factor
)
m.fs.final_permeate_concentration = Expression(
expr=m.fs.product.flow_mass_phase_comp[0, "Liq", "NaCl"]
/ sum(m.fs.product.flow_mass_phase_comp[0, "Liq", j] for j in ["H2O", "NaCl"])
)
m.fs.costing.add_LCOW(m.fs.feed.properties[0].flow_vol, name="LCOW_feed")
m.fs.costing.add_specific_energy_consumption(
m.fs.feed.properties[0].flow_vol, name="specific_energy_consumption_feed"
)
m.fs.costing.primary_pump_capex_lcow = Expression(
expr=m.fs.costing.capital_recovery_factor
* sum(m.fs.PrimaryPumps[n].costing.capital_cost for n in m.fs.Stages)
/ m.fs.costing.annual_water_production
)
m.fs.total_membrane_area = Expression(
expr=sum(oaro.area for oaro in m.fs.OAROUnits.values()) + m.fs.RO.area
)
m.fs.costing.recycle_pump_capex_lcow = Expression(
expr=m.fs.costing.capital_recovery_factor
* (
sum(m.fs.RecyclePumps[n].costing.capital_cost for n in m.fs.NonFirstStages)
if number_of_stages > 1
else 0 * m.fs.costing.base_currency
)
/ m.fs.costing.annual_water_production,
)
m.fs.costing.erd_capex_lcow = Expression(
expr=m.fs.costing.capital_recovery_factor
* sum(erd.costing.capital_cost for erd in m.fs.EnergyRecoveryDevices.values())
/ m.fs.costing.annual_water_production
)
m.fs.costing.electricity_lcow = Expression(
expr=m.fs.costing.aggregate_flow_costs["electricity"]
* m.fs.costing.utilization_factor
/ m.fs.costing.annual_water_production
)
m.fs.costing.pumping_energy_aggregate_lcow = Expression(
expr=m.fs.costing.total_investment_factor
* (
m.fs.costing.primary_pump_capex_lcow
+ (
m.fs.costing.recycle_pump_capex_lcow
if number_of_stages > 1
else 0 * m.fs.costing.base_currency
)
+ m.fs.costing.erd_capex_lcow
)
* (
1
+ m.fs.costing.maintenance_labor_chemical_factor
/ m.fs.costing.total_investment_factor
/ m.fs.costing.capital_recovery_factor
)
+ m.fs.costing.electricity_lcow
)
m.fs.costing.membrane_capex_lcow = Expression(
expr=m.fs.costing.capital_recovery_factor
* (
sum(m.fs.OAROUnits[n].costing.capital_cost for n in m.fs.NonFinalStages)
+ m.fs.RO.costing.capital_cost
)
/ m.fs.costing.annual_water_production
)
m.fs.costing.indirect_capex_lcow = Expression(
expr=m.fs.costing.capital_recovery_factor
* (m.fs.costing.total_capital_cost - m.fs.costing.aggregate_capital_cost)
/ m.fs.costing.annual_water_production
)
m.fs.costing.membrane_replacement_lcow = Expression(
expr=(
sum(
m.fs.OAROUnits[n].costing.fixed_operating_cost
for n in m.fs.NonFinalStages
)
+ m.fs.RO.costing.fixed_operating_cost
)
/ m.fs.costing.annual_water_production
)
m.fs.costing.chemical_labor_maintenance_lcow = Expression(
expr=m.fs.costing.maintenance_labor_chemical_operating_cost
/ m.fs.costing.annual_water_production
)
m.fs.costing.membrane_aggregate_lcow = Expression(
expr=m.fs.costing.total_investment_factor
* m.fs.costing.membrane_capex_lcow
* (
1
+ m.fs.costing.maintenance_labor_chemical_factor
/ m.fs.costing.total_investment_factor
/ m.fs.costing.capital_recovery_factor
)
+ m.fs.costing.membrane_replacement_lcow
)
# system water recovery
m.fs.water_recovery = Var(
initialize=0.5,
bounds=(0, 1),
domain=NonNegativeReals,
units=pyunits.dimensionless,
doc="System Volumetric Recovery of Water",
)
m.fs.eq_water_recovery = Constraint(
expr=(
m.fs.feed.properties[0].flow_vol_phase["Liq"]
+ sum(
m.fs.Mixers[stage].makeup_state[0].flow_vol_phase["Liq"]
for stage in m.fs.NonFirstStages
)
)
* m.fs.water_recovery
== m.fs.product.properties[0].flow_vol_phase["Liq"]
)
m.fs.mass_water_recovery = Var(
initialize=0.5,
bounds=(0, 1),
domain=NonNegativeReals,
units=pyunits.dimensionless,
doc="System Mass Recovery of Water",
)
m.fs.eq_mass_water_recovery = Constraint(
expr=(
m.fs.feed.properties[0].flow_mass_phase_comp["Liq", "H2O"]
+ sum(
m.fs.Mixers[stage].makeup_state[0].flow_mass_phase_comp["Liq", "H2O"]
for stage in m.fs.NonFirstStages
)
)
* m.fs.mass_water_recovery
== m.fs.product.properties[0].flow_mass_phase_comp["Liq", "H2O"]
)
# connections
if erd_type == ERDtype.pump_as_turbine:
last_stage = m.fs.LastStage
first_stage = m.fs.FirstStage
# Connect the feed to the first pump
m.fs.feed_to_pump = Arc(
source=m.fs.feed.outlet, destination=m.fs.PrimaryPumps[first_stage].inlet
)
if number_of_stages > 1:
# Connect the Pump n to OARO n
m.fs.pump_to_OARO = Arc(
m.fs.NonFinalStages,
rule=lambda fs, n: {
"source": fs.PrimaryPumps[n].outlet,
"destination": fs.OAROUnits[n].feed_inlet,
},
)
# Connect OARO n permeate outlet to Pump n+1
m.fs.OARO_to_pump = Arc(
m.fs.NonFinalStages,
rule=lambda fs, n: {
"source": fs.OAROUnits[n].permeate_outlet,
"destination": fs.PrimaryPumps[n + 1].inlet,
},
)
# Connect OARO n feed outlet to EnergyRecoveryDevice n
m.fs.OARO_to_ERD = Arc(
m.fs.NonFinalStages,
rule=lambda fs, n: {
"source": fs.OAROUnits[n].feed_outlet,
"destination": fs.EnergyRecoveryDevices[n].inlet,
},
)
# Connect EnergyRecoveryDevice n to Separator n
m.fs.ERD_to_separator = Arc(
m.fs.NonFirstStages,
rule=lambda fs, n: {
"source": fs.EnergyRecoveryDevices[n].outlet,
"destination": fs.Separators[n].inlet,
},
)
m.fs.separator_to_mixer = Arc(
m.fs.NonFirstStages,
rule=lambda fs, n: {
"source": fs.Separators[n].treat,
"destination": fs.Mixers[n].treat,
},
)
m.fs.makeup_to_mixer = Arc(
m.fs.NonFirstStages,
rule=lambda fs, n: {
"source": getattr(fs.ProductSeparator, "makeup" + str(n)),
"destination": fs.Mixers[n].makeup,
},
)
m.fs.mixer_to_recyclepump = Arc(
m.fs.NonFirstStages,
rule=lambda fs, n: {
"source": fs.Mixers[n].outlet,
"destination": fs.RecyclePumps[n].inlet,
},
)
# Connect RecyclePumps n to OARO n-1 permeate_inlet
m.fs.recyclepump_to_OARO = Arc(
m.fs.NonFirstStages,
rule=lambda fs, n: {
"source": fs.RecyclePumps[n].outlet,
"destination": fs.OAROUnits[n - 1].permeate_inlet,
},
)
# Connect purge streams to waste mixer
m.fs.purge_to_wastemixer = Arc(
m.fs.NonFirstStages,
rule=lambda fs, n: {
"source": fs.Separators[n].purge,
"destination": getattr(fs.WasteMixer, "purge" + str(n)),
},
)
if number_of_stages > 1:
# Connect first EnergyRecoveryDevice to Mixer
m.fs.ERD_to_wastemixer = Arc(
source=m.fs.EnergyRecoveryDevices[first_stage].outlet,
destination=m.fs.WasteMixer.purge1,
)
# Connect waste mixer to disposal
m.fs.wastemixer_to_disposal = Arc(
source=m.fs.WasteMixer.outlet,
destination=m.fs.disposal.inlet,
)
else:
m.fs.ERD_to_disposal = Arc(
source=m.fs.EnergyRecoveryDevices[first_stage].outlet,
destination=m.fs.disposal.inlet,
)
# Connect the last Pump to RO
m.fs.pump_to_ro = Arc(
source=m.fs.PrimaryPumps[last_stage].outlet, destination=m.fs.RO.inlet
)
# Connect the primary RO permeate to the product
# m.fs.ro_to_product = Arc(
# source=m.fs.RO.permeate, destination=m.fs.product.inlet
# )
# Connect the primary RO permeate to the product
if number_of_stages > 1:
m.fs.ro_to_productseparator = Arc(
source=m.fs.RO.permeate, destination=m.fs.ProductSeparator.inlet
)
m.fs.productseparator_to_product = Arc(
source=m.fs.ProductSeparator.product, destination=m.fs.product.inlet
)
else:
m.fs.ro_to_product = Arc(
source=m.fs.RO.permeate, destination=m.fs.product.inlet
)
# Connect RO retentate to the last EnergyRecoveryDevice
m.fs.ro_to_ERD = Arc(
source=m.fs.RO.retentate,
destination=m.fs.EnergyRecoveryDevices[last_stage].inlet,
)
else:
# this case should be caught in the previous conditional
erd_type_not_found(erd_type)
TransformationFactory("network.expand_arcs").apply_to(m)
# scaling
# set default property values
m.fs.properties.set_default_scaling("flow_mass_phase_comp", 1, index=("Liq", "H2O"))
m.fs.properties.set_default_scaling(
"flow_mass_phase_comp", 1e2, index=("Liq", "NaCl")
)
# set unit model values
for pump in m.fs.PrimaryPumps.values():
iscale.set_scaling_factor(pump.control_volume.work, 1e-3)
for pump in m.fs.RecyclePumps.values():
iscale.set_scaling_factor(pump.control_volume.work, 1e-3)
iscale.set_scaling_factor(m.fs.RO.area, 1e-2)
for stage in m.fs.NonFinalStages:
iscale.set_scaling_factor(m.fs.OAROUnits[stage].area, 1e-2)
m.fs.feed.properties[0].flow_vol_phase["Liq"]
m.fs.feed.properties[0].mass_frac_phase_comp["Liq", "NaCl"]
if erd_type == ERDtype.pump_as_turbine:
for erd in m.fs.EnergyRecoveryDevices.values():
iscale.set_scaling_factor(erd.control_volume.work, 1e-3)
else:
erd_type_not_found(erd_type)
# unused scaling factors needed by IDAES base costing module
# calculate and propagate scaling factors
iscale.calculate_scaling_factors(m)
return m
def set_operating_conditions(
m,
solver=None,
):
if solver is None:
solver = get_solver()
# ---specifications---
# feed
# state variables
pressure_atmospheric = 101325
feed_temperature = 273.15 + 25
m.fs.feed.properties[0].pressure.fix(pressure_atmospheric) # feed pressure [Pa]
m.fs.feed.properties[0].temperature.fix(feed_temperature) # feed temperature [K]
Cin = 75000e-6
Qin = 5.416667e-3
m.fs.feed.properties.calculate_state(
var_args={
("mass_frac_phase_comp", ("Liq", "NaCl")): value(
Cin
), # feed mass concentration
("flow_vol_phase", "Liq"): Qin,
}, # volumetric feed flowrate [-]
hold_state=True, # fixes the calculated component mass flow rates
)
# primary pumps
for idx, pump in m.fs.PrimaryPumps.items():
# pump.control_volume.properties_out[0].pressure = 10e5 + 65e5 / float(idx)
if idx == m.fs.Stages.last():
pump.control_volume.properties_out[0].pressure = 85e5
else:
pump.control_volume.properties_out[0].pressure = 65e5
pump.control_volume.properties_out[0].pressure.fix()
pump.efficiency_pump.fix(0.75)
for idx, pump in m.fs.RecyclePumps.items():
pump.control_volume.properties_out[0].pressure = 2e5
pump.control_volume.properties_out[0].pressure.fix()
pump.efficiency_pump.fix(0.75)
# Initialize OARO
A_OARO = 1.0e-12
B_OARO = 8.0e-8
spacer_porosity = 0.75
for idx, stage in m.fs.OAROUnits.items():
stage.A_comp.fix(A_OARO)
stage.B_comp.fix(B_OARO)
stage.structural_parameter.fix(1200e-6)
stage.permeate_side.channel_height.fix(2e-3)
stage.permeate_side.spacer_porosity.fix(spacer_porosity)
stage.feed_side.channel_height.fix(2e-3)
stage.feed_side.spacer_porosity.fix(spacer_porosity)
stage.feed_side.velocity[0, 0].fix(0.1)
stage.recovery_mass_phase_comp[0, "Liq", "H2O"].fix(0.3)
# RO unit
A_RO = 4.2e-12
B_RO = 3.5e-8
width = 5 * Qin / 1e-3 # effective membrane width [m]
m.fs.RO.A_comp.fix(A_RO) # membrane water permeability coefficient [m/s-Pa]
m.fs.RO.B_comp.fix(B_RO) # membrane salt permeability coefficient [m/s]
m.fs.RO.feed_side.channel_height.fix(2e-3) # channel height in membrane stage [m]
m.fs.RO.feed_side.spacer_porosity.fix(
spacer_porosity
) # spacer porosity in membrane stage [-]
m.fs.RO.permeate.pressure[0].fix(101325) # atmospheric pressure [Pa]
m.fs.RO.width.fix(width / float(m.fs.NumberOfStages)) # stage width [m]
m.fs.RO.recovery_mass_phase_comp[0, "Liq", "H2O"].fix(0.3)
# ERD unit
if m.fs.erd_type == ERDtype.pump_as_turbine:
# energy recovery turbine - efficiency and outlet pressure
for erd in m.fs.EnergyRecoveryDevices.values():
erd.efficiency_pump.fix(0.9)
erd.control_volume.properties_out[0].pressure.fix(pressure_atmospheric)
else:
erd_type_not_found(m.fs.erd_type)
# Makeup
for i in range(2, m.fs.NumberOfStages + 1):
m.fs.ProductSeparator.split_fraction[:, "makeup" + str(i)].fix(0)
# Separator
for sep in m.fs.Separators.values():
sep.split_fraction[:, "treat"] = 1
sep.split_fraction[:, "treat"].fix()
# check degrees of freedom
if degrees_of_freedom(m) != 0:
raise RuntimeError(
"The set_operating_conditions function resulted in {} "
"degrees of freedom rather than 0. This error suggests "
"that too many or not enough variables are fixed for a "
"simulation.".format(degrees_of_freedom(m))
)
def purge_initializer(sep, oaro, solute_multiplier):
split_fraction_treat = value(
oaro.feed_inlet.flow_mass_phase_comp[0, "Liq", "NaCl"]
* solute_multiplier
/ sep.inlet.flow_mass_phase_comp[0, "Liq", "NaCl"]
)
if split_fraction_treat <= 1:
sep.split_fraction[:, "treat"].value = (
oaro.feed_inlet.flow_mass_phase_comp[0, "Liq", "NaCl"]
* solute_multiplier
/ sep.inlet.flow_mass_phase_comp[0, "Liq", "NaCl"]
)
else:
sep.split_fraction[:, "treat"].value = 1
def makeup_initializer(mixer, oaro, solvent_multiplier):
makeup_flow_mass = value(
oaro.feed_inlet.flow_mass_phase_comp[0, "Liq", "H2O"] * solvent_multiplier
- mixer.treat.flow_mass_phase_comp[0, "Liq", "H2O"]
)
if makeup_flow_mass >= 0:
mixer.makeup_state[0].flow_mass_phase_comp["Liq", "H2O"].value = (
oaro.feed_inlet.flow_mass_phase_comp[0, "Liq", "H2O"] * solvent_multiplier
- mixer.treat.flow_mass_phase_comp[0, "Liq", "H2O"]
)
else:
mixer.makeup_state[0].flow_mass_phase_comp["Liq", "H2O"].value = 1e-9
mixer.makeup_state[0].flow_mass_phase_comp["Liq", "NaCl"].value = 0
def recycle_pump_initializer(pump, oaro, solvent_multiplier, solute_multiplier):
feed_temperature = 273.15 + 25
pump.control_volume.properties_out[0].temperature.value = feed_temperature
pump.control_volume.properties_out[0].flow_mass_phase_comp["Liq", "H2O"].value = (
oaro.feed_inlet.flow_mass_phase_comp[0, "Liq", "H2O"] * solvent_multiplier
)
pump.control_volume.properties_out[0].flow_mass_phase_comp["Liq", "NaCl"].value = (
oaro.feed_inlet.flow_mass_phase_comp[0, "Liq", "NaCl"] * solute_multiplier
)
def solve(model, solver=None, tee=False, raise_on_failure=False):
# ---solving---
if solver is None:
solver = get_solver(options={"bound_push": 1e-2})
results = solver.solve(model, tee=tee)
if check_optimal_termination(results):
return results
msg = (
"The current configuration is infeasible. Please adjust the decision variables."
)
if raise_on_failure:
raise RuntimeError(msg)
else:
print(msg)
return results
def initialize_loop(m, solvent_multiplier, solute_multiplier):
for stage in m.fs.IntermediateStages:
propagate_state(m.fs.OARO_to_pump[stage - 1])
m.fs.PrimaryPumps[stage].initialize()
# ---initialize loop---
propagate_state(m.fs.pump_to_OARO[stage])
recycle_pump_initializer(
m.fs.RecyclePumps[stage + 1],
m.fs.OAROUnits[stage],
solvent_multiplier=solvent_multiplier,
solute_multiplier=solute_multiplier,
)
propagate_state(m.fs.recyclepump_to_OARO[stage + 1])
m.fs.OAROUnits[stage].initialize()
propagate_state(m.fs.OARO_to_ERD[stage])
m.fs.EnergyRecoveryDevices[stage].initialize()
propagate_state(m.fs.ERD_to_separator[stage])
purge_initializer(
m.fs.Separators[stage],
m.fs.OAROUnits[stage - 1],
solute_multiplier=solute_multiplier,
)
m.fs.Separators[stage].initialize()
propagate_state(m.fs.separator_to_mixer[stage])
makeup_initializer(
m.fs.Mixers[stage],
m.fs.OAROUnits[stage - 1],
solvent_multiplier=solvent_multiplier,
)
m.fs.Mixers[stage].initialize()
propagate_state(m.fs.mixer_to_recyclepump[stage])
m.fs.RecyclePumps[stage].initialize()
propagate_state(m.fs.recyclepump_to_OARO[stage])
propagate_state(m.fs.pump_to_OARO[stage - 1])
m.fs.OAROUnits[stage - 1].initialize()
def initialize_system(
m,
number_of_stages=None,
solvent_multiplier=None,
solute_multiplier=None,
solver=None,
):
if solver is None:
solver = get_solver()
last_stage = m.fs.LastStage
first_stage = m.fs.FirstStage
# ---initialize feed block---
m.fs.feed.initialize()
propagate_state(m.fs.feed_to_pump)
m.fs.PrimaryPumps[first_stage].initialize()
# ---initialize first OARO unit---
if number_of_stages > 1:
propagate_state(m.fs.pump_to_OARO[first_stage])
recycle_pump_initializer(
m.fs.RecyclePumps[first_stage + 1],
m.fs.OAROUnits[first_stage],
solvent_multiplier=solvent_multiplier,
solute_multiplier=solute_multiplier,
)
propagate_state(m.fs.recyclepump_to_OARO[first_stage + 1])
m.fs.OAROUnits[first_stage].initialize()
if number_of_stages > 2:
initialize_loop(m, solvent_multiplier, solute_multiplier)
# ---initialize RO loop---
propagate_state(m.fs.OARO_to_pump[last_stage - 1])
m.fs.PrimaryPumps[last_stage].initialize()
propagate_state(m.fs.pump_to_ro)
m.fs.RO.initialize()
propagate_state(m.fs.ro_to_ERD)
m.fs.EnergyRecoveryDevices[last_stage].initialize()
if number_of_stages > 1:
purge_initializer(
m.fs.Separators[last_stage],
m.fs.OAROUnits[last_stage - 1],
solute_multiplier=solute_multiplier,
)
propagate_state(m.fs.ERD_to_separator[last_stage])
m.fs.Separators[last_stage].initialize()
propagate_state(m.fs.separator_to_mixer[last_stage])
makeup_initializer(
m.fs.Mixers[last_stage],
m.fs.OAROUnits[last_stage - 1],
solvent_multiplier=solvent_multiplier,
)
m.fs.Mixers[last_stage].initialize()
propagate_state(m.fs.mixer_to_recyclepump[last_stage])
m.fs.RecyclePumps[last_stage].initialize()
propagate_state(m.fs.recyclepump_to_OARO[last_stage])
propagate_state(m.fs.pump_to_OARO[last_stage - 1])
m.fs.OAROUnits[last_stage - 1].initialize()
# ---initialize first ERD---
propagate_state(m.fs.OARO_to_ERD[first_stage])
m.fs.EnergyRecoveryDevices[first_stage].initialize()
m.fs.costing.initialize()
def optimize_set_up(
m,
number_of_stages=None,
water_recovery=None,
):
# add objective
m.fs.objective = Objective(expr=m.fs.costing.LCOW)
# Primary pumps
for idx, pump in m.fs.PrimaryPumps.items():
pump.control_volume.properties_out[0].pressure.unfix()
pump.deltaP.setlb(0)
if idx == m.fs.Stages.last():
pump.max_ro_pressure_con = Constraint(
expr=(
m.fs.ro_min_pressure,
pump.control_volume.properties_out[0].pressure,
m.fs.ro_max_pressure,
)
)
else:
pump.max_oaro_pressure_con = Constraint(
expr=(
m.fs.oaro_min_pressure,
pump.control_volume.properties_out[0].pressure,
m.fs.oaro_max_pressure,
)
)
# Recycle pumps
for idx, pump in m.fs.RecyclePumps.items():
pump.control_volume.properties_out[0].pressure.unfix()
pump.max_recycle_pump_pressure_con = Constraint(
expr=(
m.fs.recycle_pump_min_pressure,
pump.control_volume.properties_out[0].pressure,
m.fs.recycle_pump_max_pressure,
)
)
iscale.constraint_scaling_transform(
pump.max_recycle_pump_pressure_con,
iscale.get_scaling_factor(pump.control_volume.properties_out[0].pressure),
)
pump.deltaP.setlb(0)
# OARO Units
for stage in m.fs.OAROUnits.values():
stage.recovery_mass_phase_comp[0, "Liq", "H2O"].unfix()
stage.area.unfix()
stage.area.setlb(1)
stage.area.setub(20000)
stage.width.unfix()
stage.width.setlb(0.1)
stage.width.setub(1000)
stage.feed_side.velocity[0, 0].unfix()
stage.feed_side.velocity[0, 0].setlb(0)
stage.feed_side.velocity[0, 0].setub(1)
stage.feed_side.N_Re[0, 0].setlb(100)
stage.feed_side.N_Re[0, 0].setub(2000)
stage.feed_side.N_Re[0, 1].setlb(100)
stage.feed_side.N_Re[0, 1].setub(2000)
stage.permeate_side.N_Re[0, 0].setlb(100)
stage.permeate_side.N_Re[0, 0].setub(2000)
stage.permeate_side.N_Re[0, 1].setlb(100)
stage.permeate_side.N_Re[0, 1].setub(2000)
stage.permeate_outlet.pressure[0].unfix()
stage.permeate_outlet.pressure[0].setlb(101325)
stage.oaro_avg_water_flux_con = Constraint(
expr=(
0.1,
pyunits.convert(
stage.flux_mass_phase_comp_avg[0, "Liq", "H2O"]
/ stage.dens_solvent,
to_units=pyunits.L / pyunits.m**2 / pyunits.hr,
),
10,
)
)
stage.oaro_feed_water_flux_con = Constraint(
expr=(
0.1 / 5,
pyunits.convert(
stage.flux_mass_phase_comp[0, 0, "Liq", "H2O"] / stage.dens_solvent,
to_units=pyunits.L / pyunits.m**2 / pyunits.hr,
),
10 * 1.5,
)
)
stage.oaro_permeate_water_flux_con = Constraint(
expr=(
0.1 / 5,
pyunits.convert(
stage.flux_mass_phase_comp[0, 1, "Liq", "H2O"] / stage.dens_solvent,
to_units=pyunits.L / pyunits.m**2 / pyunits.hr,
),
10 * 1.5,
)
)
stage.oaro_avg_salt_flux_con = Constraint(
expr=(
0,
pyunits.convert(
stage.flux_mass_phase_comp_avg[0, "Liq", "NaCl"],
to_units=pyunits.g / pyunits.m**2 / pyunits.hr,
),
50,
)
)
stage.oaro_inlet_salt_flux_con = Constraint(
expr=(
0,
pyunits.convert(
stage.flux_mass_phase_comp[0, 0, "Liq", "NaCl"],
to_units=pyunits.g / pyunits.m**2 / pyunits.hr,
),
50,
)
)
stage.oaro_permeate_salt_flux_con = Constraint(
expr=(
0,
pyunits.convert(
stage.flux_mass_phase_comp[0, 1, "Liq", "NaCl"],
to_units=pyunits.g / pyunits.m**2 / pyunits.hr,
),
50,
)
)
stage.min_permeate_flow_rate_con = Constraint(
expr=sum(
stage.permeate_inlet.flow_mass_phase_comp[0, "Liq", j]
for j in ["H2O", "NaCl"]
)
>= 0.15
* sum(
stage.feed_inlet.flow_mass_phase_comp[0, "Liq", j]
for j in ["H2O", "NaCl"]
)
)
stage.max_permeate_flow_rate_con = Constraint(
expr=sum(
stage.permeate_inlet.flow_mass_phase_comp[0, "Liq", j]
for j in ["H2O", "NaCl"]
)
<= 0.8
* sum(
stage.feed_inlet.flow_mass_phase_comp[0, "Liq", j]
for j in ["H2O", "NaCl"]
)
)
# RO
m.fs.RO.recovery_mass_phase_comp[0, "Liq", "H2O"].unfix()
m.fs.RO.area.setlb(1)
m.fs.RO.area.setub(20000)
m.fs.RO.width.unfix()
m.fs.RO.width.setlb(0.1)
m.fs.RO.width.setub(1000)
m.fs.RO.feed_side.N_Re[0, 0].setlb(100)
m.fs.RO.feed_side.N_Re[0, 0].setub(2000)
m.fs.RO.feed_side.N_Re[0, 1].setlb(100)
m.fs.RO.feed_side.N_Re[0, 1].setub(2000)
m.fs.RO.feed_side.properties[0, 0].conc_mass_phase_comp["Liq", "NaCl"].setlb(
10 * pyunits.g / pyunits.L
)
m.fs.RO.feed_side.properties[0, 1].conc_mass_phase_comp["Liq", "NaCl"].setlb(
10 * pyunits.g / pyunits.L
)
m.fs.RO.ro_avg_water_flux_con = Constraint(
expr=(
0.4,
pyunits.convert(
m.fs.RO.flux_mass_phase_comp_avg[0, "Liq", "H2O"]
/ m.fs.RO.dens_solvent,
to_units=pyunits.L / pyunits.m**2 / pyunits.hr,
),
40,
)
)
m.fs.RO.ro_feed_water_flux_con = Constraint(
expr=(
0.4 / 5,
pyunits.convert(
m.fs.RO.flux_mass_phase_comp[0.0, 0.0, "Liq", "H2O"]
/ m.fs.RO.dens_solvent,
to_units=pyunits.L / pyunits.m**2 / pyunits.hr,
),
40 * 1.5,
)
)
m.fs.RO.ro_permeate_water_flux_con = Constraint(
expr=(
0.4 / 5,
pyunits.convert(
m.fs.RO.flux_mass_phase_comp[0.0, 1.0, "Liq", "H2O"]
/ m.fs.RO.dens_solvent,
to_units=pyunits.L / pyunits.m**2 / pyunits.hr,
),
40 * 1.5,
)
)
m.fs.RO.ro_avg_salt_flux_con = Constraint(
expr=(
0,
pyunits.convert(
m.fs.RO.flux_mass_phase_comp_avg[0, "Liq", "NaCl"],
to_units=pyunits.g / pyunits.m**2 / pyunits.hr,
),
50,
)
)
m.fs.RO.ro_feed_salt_flux_con = Constraint(
expr=(
0,
pyunits.convert(
m.fs.RO.flux_mass_phase_comp[0, 0, "Liq", "NaCl"],
to_units=pyunits.g / pyunits.m**2 / pyunits.hr,
),
50,
)
)
m.fs.RO.ro_retentate_salt_flux_con = Constraint(
expr=(
0,
pyunits.convert(
m.fs.RO.flux_mass_phase_comp[0, 1, "Liq", "NaCl"],
to_units=pyunits.g / pyunits.m**2 / pyunits.hr,
),
50,
)
)
# Separator
for sep in m.fs.Separators.values():
sep.split_fraction[:, "treat"].unfix()
sep.split_fraction[:, "treat"].setlb(0)
sep.split_fraction[:, "treat"].setub(1)
# Makeup
if number_of_stages > 1:
for i in range(2, number_of_stages + 1):
m.fs.ProductSeparator.split_fraction[:, "makeup" + str(i)].unfix()
m.fs.ProductSeparator.split_fraction[:, "makeup" + str(i)].setlb(0)
m.fs.ProductSeparator.split_fraction[:, "makeup" + str(i)].setub(1)
# additional specifications
m.fs.product_salinity = Param(
initialize=500e-6, mutable=True
) # product NaCl mass fraction [-]
# additional constraints
if water_recovery is not None:
# product mass flow rate fraction of feed [-]
m.fs.mass_water_recovery.fix(water_recovery)
m.fs.eq_product_quality = Constraint(
expr=m.fs.product.properties[0].mass_frac_phase_comp["Liq", "NaCl"]
<= m.fs.product_salinity
)
iscale.constraint_scaling_transform(
m.fs.eq_product_quality, 1e3
) # scaling constraint
if number_of_stages > 1:
m.fs.purge_rate_con = Constraint(
expr=(
0,
sum(
m.fs.Separators[stage].purge.flow_mass_phase_comp[0, "Liq", j]
for stage in m.fs.NonFirstStages
for j in ["H2O", "NaCl"]
)
/ (
sum(
m.fs.OAROUnits[1].permeate_outlet.flow_mass_phase_comp[
0, "Liq", j
]
for j in ["H2O", "NaCl"]
)
- sum(
m.fs.OAROUnits[1].permeate_inlet.flow_mass_phase_comp[
0, "Liq", j
]
for j in ["H2O", "NaCl"]
)
),
0.2,
)
)
assert_degrees_of_freedom(
m,
6 * (m.fs.NumberOfStages - 1)
+ 3
- (1 if (water_recovery is not None) else 0),
)
def display_system(m):
print("----system metrics----")
feed_flow_vol = m.fs.feed.properties[0].flow_vol_phase["Liq"].value * 3600
feed_flow_mass = sum(
m.fs.feed.flow_mass_phase_comp[0, "Liq", j].value for j in ["H2O", "NaCl"]
)
feed_mass_frac_NaCl = (
m.fs.feed.flow_mass_phase_comp[0, "Liq", "NaCl"].value / feed_flow_mass
)
print(
"Feed: %.2f m3/h, %.2f kg/s, %.0f ppm"
% (feed_flow_vol, feed_flow_mass, feed_mass_frac_NaCl * 1e6)
)
prod_flow_mass = sum(
m.fs.product.flow_mass_phase_comp[0, "Liq", j].value for j in ["H2O", "NaCl"]
)
prod_mass_frac_NaCl = (
m.fs.product.flow_mass_phase_comp[0, "Liq", "NaCl"].value / prod_flow_mass
)
print("Product: %.3f kg/s, %.0f ppm" % (prod_flow_mass, prod_mass_frac_NaCl * 1e6))
brine_flow_mass = sum(
m.fs.disposal.flow_mass_phase_comp[0, "Liq", j].value for j in ["H2O", "NaCl"]
)
brine_mass_frac_NaCl = (
m.fs.disposal.flow_mass_phase_comp[0, "Liq", "NaCl"].value / brine_flow_mass
)
print("Brine: %.3f kg/s, %.0f ppm" % (brine_flow_mass, brine_mass_frac_NaCl * 1e6))
print("Mass water recovery: %.1f%%" % (value(m.fs.mass_water_recovery) * 100))
print("Volumetric water recovery: %.1f%%" % (value(m.fs.water_recovery) * 100))
print(f"Number of Stages: {value(m.fs.NumberOfStages)}")
total_area = value(
sum(m.fs.OAROUnits[a].area for a in m.fs.NonFinalStages) + m.fs.RO.area
)
print(f"Total Membrane Area: {total_area:.2f}")
print(
"Energy Consumption: %.1f kWh/m3"
% value(m.fs.costing.specific_energy_consumption)
)
print("Levelized cost of water: %.2f $/m3" % value(m.fs.costing.LCOW))
print(
f"Primary Pump Capital Cost ($/m3):"
f"{value(m.fs.costing.capital_recovery_factor*sum(m.fs.PrimaryPumps[stage].costing.capital_cost for stage in m.fs.Stages)/ m.fs.costing.annual_water_production)}"
)
print(
f"Recycle Pump Capital Cost ($/m3): "
f"{value(m.fs.costing.capital_recovery_factor*sum(m.fs.RecyclePumps[stage].costing.capital_cost for stage in m.fs.NonFirstStages) / m.fs.costing.annual_water_production)}"
)
print(
f"ERD Capital Cost ($/m3):"
f"{value(m.fs.costing.capital_recovery_factor*sum(erd.costing.capital_cost for erd in m.fs.EnergyRecoveryDevices.values()) / m.fs.costing.annual_water_production)}"
)
print(
f"Membrane Capital Cost ($/m3): "
f"{value(m.fs.costing.capital_recovery_factor*(sum(m.fs.OAROUnits[stage].costing.capital_cost for stage in m.fs.NonFinalStages) + m.fs.RO.costing.capital_cost) / m.fs.costing.annual_water_production)}"
)
print(
f"Indirect Capital Cost ($/m3): "
f"{value(m.fs.costing.capital_recovery_factor*(m.fs.costing.total_capital_cost - m.fs.costing.aggregate_capital_cost) / m.fs.costing.annual_water_production)}"
)
electricity_cost = value(
m.fs.costing.aggregate_flow_costs["electricity"]
* m.fs.costing.utilization_factor
/ m.fs.costing.annual_water_production
)
print(f"Electricity cost ($/m3): {electricity_cost}")
print("\n")
def display_design(m):
print("--decision variables--")
for stage in m.fs.NonFinalStages:
print(
"OARO Stage %d water recovery: %.3f"
% (
stage,
value(m.fs.OAROUnits[stage].recovery_mass_phase_comp[0, "Liq", "H2O"]),
)
)
print(
"OARO Stage %d average water flux: %.1f L/m2/h"
% (
stage,
value(m.fs.OAROUnits[stage].flux_mass_phase_comp_avg[0, "Liq", "H2O"])
/ 1e3
* 1000
* 3600,
)
)
print(
"OARO Stage %d average salt flux: %.1f g/m2/h"
% (
stage,
value(m.fs.OAROUnits[stage].flux_mass_phase_comp_avg[0, "Liq", "NaCl"])
* 1000
* 3600,
)
)
print(
"OARO Stage %d feed operating pressure %.1f bar"
% (stage, m.fs.OAROUnits[stage].feed_inlet.pressure[0].value / 1e5)
)
print(
"OARO Stage %d permeate operating pressure %.1f bar"
% (stage, m.fs.OAROUnits[stage].permeate_inlet.pressure[0].value / 1e5)
)
print(
"OARO Stage %d membrane area %.1f m2"
% (stage, m.fs.OAROUnits[stage].area.value)
)
print(
"OARO Stage %d water perm. coeff. %.3f LMH/bar"
% (stage, m.fs.OAROUnits[stage].A_comp[0, "H2O"].value * (3.6e11))
)
print(
"OARO Stage %d salt perm. coeff. %.3f LMH/bar"
% (stage, m.fs.OAROUnits[stage].B_comp[0, "NaCl"].value * (1000.0 * 3600.0))
)
print(
"OARO Stage %d feed-side velocity. %.3f"
% (stage, m.fs.OAROUnits[stage].feed_side.velocity[0, 0].value)
)
print(
"OARO Stage %d feed-side Reynolds number. %.3f"
% (stage, m.fs.OAROUnits[stage].feed_side.N_Re[0, 0].value)
)
print(
"RO water recovery: %.3f"
% (value(m.fs.RO.recovery_mass_phase_comp[0, "Liq", "H2O"]))
)
print(
"RO feed operating pressure %.1f bar" % (m.fs.RO.inlet.pressure[0].value / 1e5)
)
print(
"RO permeate operating pressure %.1f bar"
% (m.fs.RO.permeate.pressure[0].value / 1e5)
)
print("RO membrane area %.1f m2" % (m.fs.RO.area.value))
print("RO membrane width %.1f m" % (m.fs.RO.width.value))
print(
"RO water perm. coeff. %.3f LMH/bar"
% (m.fs.RO.A_comp[0, "H2O"].value * (3.6e11))
)
print(
"RO salt perm. coeff. %.3f LMH"
% (m.fs.RO.B_comp[0, "NaCl"].value * (1000.0 * 3600.0))
)
def display_state(m):
print("--------state---------")
def print_state(s, b):
feed_flow_mass = sum(
m.fs.feed.flow_mass_phase_comp[0, "Liq", j].value for j in ["H2O", "NaCl"]
)
flow_mass = sum(
b.flow_mass_phase_comp[0, "Liq", j].value for j in ["H2O", "NaCl"]
)
normalized_flow_mass = flow_mass / feed_flow_mass * 100
mass_frac_ppm = b.flow_mass_phase_comp[0, "Liq", "NaCl"].value / flow_mass * 1e3
pressure_bar = b.pressure[0].value / 1e5
print(
s.ljust(20)
+ ": %.0f, %.3f g/L, %.1f bar, %.4f kg/s, %.4f kg/s"
% (
normalized_flow_mass,
mass_frac_ppm,
pressure_bar,
b.flow_mass_phase_comp[0, "Liq", "H2O"].value,
b.flow_mass_phase_comp[0, "Liq", "NaCl"].value,
)
)
print_state("Feed", m.fs.feed.outlet)
for stage in m.fs.Stages:
print_state(f"Primary Pump {stage} out", m.fs.PrimaryPumps[stage].outlet)
print_state(
f"ERD {stage} out",
m.fs.EnergyRecoveryDevices[stage].outlet,
)
if stage == m.fs.LastStage:
pass
else:
print_state(f"OARO {stage} feed inlet", m.fs.OAROUnits[stage].feed_inlet)
print_state(f"OARO {stage} feed outlet", m.fs.OAROUnits[stage].feed_outlet)
print_state(
f"OARO {stage} permeate inlet", m.fs.OAROUnits[stage].permeate_inlet
)
print_state(
f"OARO {stage} permeate outlet", m.fs.OAROUnits[stage].permeate_outlet
)
if stage == m.fs.FirstStage:
pass
else:
print_state(f"Recycle Pump {stage} out", m.fs.RecyclePumps[stage].outlet)
print_state(f"Purge {stage}", m.fs.Separators[stage].purge)
print_state(f"Make-up {stage}", m.fs.Mixers[stage].makeup)
print_state(f"RO inlet", m.fs.RO.inlet)
print_state(f"RO permeate", m.fs.RO.permeate)
print_state(f"RO retentate", m.fs.RO.retentate)
print_state(f"Disposal", m.fs.disposal.inlet)
print_state(f"Product", m.fs.product.inlet)
if __name__ == "__main__":
m = main(number_of_stages=3, system_recovery=0.5, erd_type=ERDtype.pump_as_turbine)