From 6b2b7da8d6b04c1354e4e2013212f65b7fcdeb42 Mon Sep 17 00:00:00 2001 From: Zhuoran Zhang Date: Tue, 30 Jan 2024 14:28:30 -0500 Subject: [PATCH 01/34] initial commit --- .../reflo/property_models/fo_draw_solution_properties.py | 0 .../reflo/unit_models/zero_order/forward_osmosis.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py create mode 100644 src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis.py diff --git a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py new file mode 100644 index 00000000..e69de29b diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis.py new file mode 100644 index 00000000..e69de29b From 6750017ab7e5c0150d142d27582701cdf5ffc142 Mon Sep 17 00:00:00 2001 From: Zhuoran Zhang Date: Tue, 30 Apr 2024 10:55:22 -0400 Subject: [PATCH 02/34] save progress --- .../reflo/property_models/__init__.py | 1 + .../fo_draw_solution_properties.py | 616 ++++++++++++++++++ .../tests/test_FO_draw_solution_properties.py | 171 +++++ .../reflo/unit_models/__init__.py | 1 + .../reflo/unit_models/forward_osmosis_0D.py | 204 ++++++ .../unit_models/zero_order/forward_osmosis.py | 0 6 files changed, 993 insertions(+) create mode 100644 src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py create mode 100644 src/watertap_contrib/reflo/unit_models/forward_osmosis_0D.py delete mode 100644 src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis.py diff --git a/src/watertap_contrib/reflo/property_models/__init__.py b/src/watertap_contrib/reflo/property_models/__init__.py index 261cd003..9018f945 100644 --- a/src/watertap_contrib/reflo/property_models/__init__.py +++ b/src/watertap_contrib/reflo/property_models/__init__.py @@ -6,3 +6,4 @@ MolarVolumeCalculation, ) from .basic_water_properties import BasicWaterParameterBlock +from .fo_draw_solution_properties import FODrawSolutionParameterBlock diff --git a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py index e69de29b..3a6b4988 100644 --- a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py @@ -0,0 +1,616 @@ +################################################################################# +# WaterTAP Copyright (c) 2020-2023, 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/" +################################################################################# + +""" +This module contains a basic property package for simple water treatment models. +Volumetric flow and component concentration are used to determine mass flow. +""" +from idaes.core import ( + EnergyBalanceType, + MaterialBalanceType, + MaterialFlowBasis, + PhysicalParameterBlock, + StateBlock, + StateBlockData, + declare_process_block_class, +) +from idaes.core.base.components import Solvent, Solute +from idaes.core.base.phases import LiquidPhase +from idaes.core.solvers.get_solver import get_solver +from idaes.core.util.misc import add_object_reference +from idaes.core.util.initialization import ( + fix_state_vars, + revert_state_vars, + solve_indexed_blocks, +) +from idaes.core.util.model_statistics import ( + degrees_of_freedom, + number_unfixed_variables, +) +import idaes.logger as idaeslog +import idaes.core.util.scaling as iscale +from idaes.core.util.exceptions import InitializationError + +from pyomo.environ import ( + Param, + PositiveReals, + units as pyunits, + Reals, + NonNegativeReals, + Var, + Constraint, + Suffix, + value, + check_optimal_termination, +) +from pyomo.common.config import ConfigValue + + +__author__ = "Zhuoran Zhang" + +# Set up logger +_log = idaeslog.getLogger(__name__) + + +@declare_process_block_class("FODrawSolutionParameterBlock") +class FODrawSolutionParameterBlockData(PhysicalParameterBlock): + """ + Property Parameter Block Class + + Defines component lists, along with base units and constant + parameters. + """ + + CONFIG = PhysicalParameterBlock.CONFIG() + + def build(self): + """ + Callable method for Block construction. + """ + super().build() + + self._state_block_class = FODrawSolutionStateBlock + + # components + self.H2O = Solvent() + self.DrawSolution = Solute() + + # phases + self.Liq = LiquidPhase() + + # --------------------------------------------------------------------- + # mass density parameters, equation derived from experimental data + dens_units = pyunits.kg / pyunits.m**3 + + self.dens_mass_param_A0 = Param( + initialize=1.000446e3, + units=dens_units, + doc="Mass density parameter A0", + ) + + self.dens_mass_param_A1 = Param( + initialize=1.1849, + units=dens_units, + doc="Mass density parameter A1", + ) + + self.dens_mass_param_A2 = Param( + initialize=1.1455e-2, + units=dens_units, + doc="Mass density parameter A2", + ) + + self.dens_mass_param_A3 = Param( + initialize=-1.63e-4, + units=dens_units, + doc="Mass density parameter A3", + ) + + # osmotic coefficient parameters, equation derived from experimental data + self.osm_coeff_param_0 = Param( + initialize=1.2370854e5, + units=pyunits.Pa, + doc="Osmotic coefficient parameter 0", + ) + self.osm_coeff_param_1 = Param( + initialize=1.2961975e5, + units=pyunits.Pa, + doc="Osmotic coefficient parameter 1", + ) + self.osm_coeff_param_2 = Param( + initialize=1.386231e4, + units=pyunits.Pa, + doc="Osmotic coefficient parameter 2", + ) + self.osm_coeff_param_3 = Param( + initialize=6.356857e2, + units=pyunits.Pa, + doc="Osmotic coefficient parameter 3", + ) + self.osm_coeff_param_4 = Param( + initialize=-1.10696e1, + units=pyunits.Pa, + doc="Osmotic coefficient parameter 4", + ) + self.osm_coeff_param_5 = Param( + initialize=6.92e-2, + units=pyunits.Pa, + doc="Osmotic coefficient parameter 5", + ) + + # specific heat parameters, derived from experimental data + cp_units = pyunits.J / (pyunits.kg * pyunits.K) + self.cp_phase_param_A0 = Param( + initialize=4.20003e3, + units=cp_units, + doc="Specific heat of seawater parameter A0", + ) + self.cp_phase_param_A1 = Param( + initialize=-3.49888e1, + units=cp_units, + doc="Specific heat of seawater parameter A1", + ) + self.cp_phase_param_A2 = Param( + initialize=1.33883e-1, + units=cp_units, + doc="Specific heat of seawater parameter A2", + ) + + # --------------------------------------------------------------------- + # Set default scaling factors + self.set_default_scaling("temperature", 1e-2) + self.set_default_scaling("pressure", 1e-6) + self.set_default_scaling("dens_mass_phase", 1e-3, index="Liq") + self.set_default_scaling("cp_mass_phase", 1e-3, index="Liq") + + @classmethod + def define_metadata(cls, obj): + obj.add_default_units( + { + "time": pyunits.s, + "length": pyunits.m, + "mass": pyunits.kg, + "amount": pyunits.mol, + "temperature": pyunits.K, + } + ) # Not used + + obj.add_properties( + { + "flow_mass_phase_comp": {"method": None}, + "temperature": {"method": None}, + "pressure": {"method": None}, + "flow_vol_phase": {"method": "_flow_vol_phase"}, + "conc_mass_phase_comp": {"method": "_conc_mass_phase_comp"}, + "mass_frac_phase_comp": {"method": "_mass_frac_phase_comp"}, + "dens_mass_phase": {"method": "_dens_mass_phase"}, + "pressure_osm_phase": {"method": "_pressure_osm_phase"}, + "cp_mass_phase": {"method": "_cp_mass_phase"}, + # "visc_d": {"method": "_visc_d"}, + } + ) + + +class _FODrawSolutionStateBlock(StateBlock): + """ + This Class contains methods which should be applied to Property Blocks as a + whole, rather than individual elements of indexed Property Blocks. + """ + + def initialize( + self, + state_args=None, + state_vars_fixed=False, + hold_state=False, + outlvl=idaeslog.NOTSET, + solver=None, + optarg=None, + ): + """ + Initialization routine for property package. + + Keyword Arguments: + state_args : Dictionary with initial guesses for the state vars + chosen. Note that if this method is triggered + through the control volume, and if initial guesses + were not provied at the unit model level, the + control volume passes the inlet values as initial + guess. + outlvl : sets output level of initialization routine + state_vars_fixed: Flag to denote if state vars have already been + fixed. + - True - states have already been fixed and + initialization does not need to worry + about fixing and unfixing variables. + - False - states have not been fixed. The state + block will deal with fixing/unfixing. + optarg : solver options dictionary object (default=None, use + default solver options) + solver : str indicating which solver to use during + initialization (default = None, use default solver) + hold_state : flag indicating whether the initialization routine + should unfix any state variables fixed during + initialization (default=False). + - True - states varaibles are not unfixed, and + a dict of returned containing flags for + which states were fixed during + initialization. + - False - state variables are unfixed after + initialization by calling the + relase_state method + + Returns: + If hold_states is True, returns a dict containing flags for + which states were fixed during initialization. + """ + + init_log = idaeslog.getInitLogger(self.name, outlvl, tag="properties") + solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="properties") + + # Set solver and options + opt = get_solver(solver, optarg) + + # Fix state variables + flags = fix_state_vars(self, state_args) + + # initialize vars calculated from state vars + # for k in self.keys(): + # for j in self[k].params.component_list: + # if self[k].is_property_constructed("flow_mass_comp"): + # if j == "H2O": + # self[k].flow_mass_comp[j].set_value( + # self[k].flow_vol * self[k].dens_mass + # ) + # else: + # self[k].flow_mass_comp[j].set_value( + # self[k].flow_vol * self[k].conc_mass_comp[j] + # ) + + # Check when the state vars are fixed already result in dof 0 + for k in self.keys(): + dof = degrees_of_freedom(self[k]) + if dof != 0: + raise InitializationError( + "\nWhile initializing {sb_name}, the degrees of freedom " + "are {dof}, when zero is required. \nInitialization assumes " + "that the state variables should be fixed and that no other " + "variables are fixed. \nIf other properties have a " + "predetermined value, use the calculate_state method " + "before using initialize to determine the values for " + "the state variables and avoid fixing the property variables." + "".format(sb_name=self.name, dof=dof) + ) + + # # --------------------------------------------------------------------- + skip_solve = True # skip solve if only state variables are present + for k in self.keys(): + if number_unfixed_variables(self[k]) != 0: + skip_solve = False + + if not skip_solve: + # Initialize properties + with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: + results = solve_indexed_blocks(opt, [self], tee=slc.tee) + if not check_optimal_termination(results): + raise InitializationError( + "The property package failed to solve during initialization." + ) + init_log.info_high( + "Property initialization: {}.".format(idaeslog.condition(results)) + ) + + # --------------------------------------------------------------------- + # If input block, return flags, else release state + if state_vars_fixed is False: + if hold_state is True: + return flags + else: + self.release_state(flags) + + def release_state(self, flags, outlvl=idaeslog.NOTSET): + """ + Method to release state variables fixed during initialization. + + Keyword Arguments: + flags : dict containing information of which state variables + were fixed during initialization, and should now be + unfixed. This dict is returned by initialize if + hold_state=True. + outlvl : sets output level of of logging + """ + init_log = idaeslog.getInitLogger(self.name, outlvl, tag="properties") + + if flags is None: + return + + # Unfix state variables + revert_state_vars(self, flags) + init_log.info("State Released.") + + +@declare_process_block_class( + "FODrawSolutionStateBlock", block_class=_FODrawSolutionStateBlock +) +class FODrawSolutionStateBlockData(StateBlockData): + """A FO draw solution property package.""" + + def build(self): + """Callable method for Block construction.""" + super().build() + + self.scaling_factor = Suffix(direction=Suffix.EXPORT) + + # Add state variables + self.flow_mass_phase_comp = Var( + self.params.phase_list, + self.params.component_list, + initialize={("Liq", "H2O"): 0.2, ("Liq", "DrawSolution"): 0.8}, + bounds=(0.0, None), + domain=NonNegativeReals, + units=pyunits.kg / pyunits.s, + doc="Mass flow rate", + ) + + self.temperature = Var( + initialize=298.15, + bounds=(273.15, 373.15), + domain=NonNegativeReals, + units=pyunits.K, + doc="Temperature", + ) + + self.pressure = Var( + initialize=101325, + bounds=(1e3, 5e7), + domain=NonNegativeReals, + units=pyunits.Pa, + doc="Pressure", + ) + + # ----------------------------------------------------------------------------- + # Property Methods + def _mass_frac_phase_comp(self): + self.mass_frac_phase_comp = Var( + self.params.phase_list, + self.params.component_list, + initialize=0.1, + bounds=(0.0, None), + units=pyunits.dimensionless, + doc="Mass fraction", + ) + + def rule_mass_frac_phase_comp(b, p, j): + return b.mass_frac_phase_comp[p, j] == b.flow_mass_phase_comp[p, j] / sum( + b.flow_mass_phase_comp[p, j] for j in b.params.component_list + ) + + self.eq_mass_frac_phase_comp = Constraint( + self.params.phase_list, + self.params.component_list, + rule=rule_mass_frac_phase_comp, + ) + + def _dens_mass_phase(self): + self.dens_mass_phase = Var( + self.params.phase_list, + initialize=1e3, + bounds=(1, 1e6), + units=pyunits.kg * pyunits.m**-3, + doc="Mass density of Trevi's FO draw solution", + ) + + def rule_dens_mass_phase(b, p): # density, eqn derived from experimental data + s = b.mass_frac_phase_comp[p, "DrawSolution"] * 100 + dens_mass = ( + b.params.dens_mass_param_A0 + + b.params.dens_mass_param_A1 * s + + b.params.dens_mass_param_A2 * s**2 + + b.params.dens_mass_param_A3 * s**3 + ) + return b.dens_mass_phase[p] == dens_mass + + self.eq_dens_mass_phase = Constraint( + self.params.phase_list, rule=rule_dens_mass_phase + ) + + def _flow_vol_phase(self): + self.flow_vol_phase = Var( + self.params.phase_list, + initialize=1, + bounds=(0.0, None), + units=pyunits.m**3 / pyunits.s, + doc="Volumetric flow rate", + ) + + def rule_flow_vol_phase(b, p): + return ( + b.flow_vol_phase[p] + == sum(b.flow_mass_phase_comp[p, j] for j in b.params.component_list) + / b.dens_mass_phase[p] + ) + + self.eq_flow_vol_phase = Constraint( + self.params.phase_list, rule=rule_flow_vol_phase + ) + + def _conc_mass_phase_comp(self): + self.conc_mass_phase_comp = Var( + self.params.phase_list, + self.params.component_list, + initialize=10, + bounds=(0.0, 1e6), + units=pyunits.kg * pyunits.m**-3, + doc="Mass concentration", + ) + + def rule_conc_mass_phase_comp(b, p, j): + return ( + b.conc_mass_phase_comp[p, j] + == b.dens_mass_phase[p] * b.mass_frac_phase_comp[p, j] + ) + + self.eq_conc_mass_phase_comp = Constraint( + self.params.phase_list, + self.params.component_list, + rule=rule_conc_mass_phase_comp, + ) + + def _pressure_osm_phase(self): + self.pressure_osm_phase = Var( + self.params.phase_list, + initialize=1e6, + bounds=(1, 1e8), + units=pyunits.Pa, + doc="Osmotic pressure", + ) + + def rule_pressure_osm_phase( + b, p + ): # osmotic pressure, derived from experimental data + s = b.mass_frac_phase_comp[p, "DrawSolution"] * 100 + pressure_osm_phase = ( + b.params.osm_coeff_param_0 + + b.params.osm_coeff_param_1 * s + + b.params.osm_coeff_param_2 * s**2 + + b.params.osm_coeff_param_3 * s**3 + + b.params.osm_coeff_param_4 * s**4 + + b.params.osm_coeff_param_5 * s**5 + ) + return b.pressure_osm_phase[p] == pressure_osm_phase + + self.eq_pressure_osm_phase = Constraint( + self.params.phase_list, rule=rule_pressure_osm_phase + ) + + def _cp_mass_phase(self): + self.cp_mass_phase = Var( + self.params.phase_list, + initialize=4e3, + bounds=(0.0, 1e8), + units=pyunits.J / pyunits.kg / pyunits.K, + doc="Specific heat capacity", + ) + + def rule_cp_mass_phase( + b, p + ): # specific heat, equation derived from experimental data + s = b.mass_frac_phase_comp[p, "DrawSolution"] * 100 + cp_mass_phase = ( + b.params.cp_phase_param_A0 + + b.params.cp_phase_param_A1 * s + + b.params.cp_phase_param_A2 * s**2 + ) + + return b.cp_mass_phase[p] == cp_mass_phase + + self.eq_cp_mass_phase = Constraint( + self.params.phase_list, rule=rule_cp_mass_phase + ) + + # ----------------------------------------------------------------------------- + # General Methods + def get_material_flow_terms(self, p, j): + """Create material flow terms for control volume.""" + return self.flow_mass_phase_comp[p, j] + + def default_material_balance_type(self): + return MaterialBalanceType.componentTotal + + def get_material_flow_basis(self): + return MaterialFlowBasis.mass + + def define_state_vars(self): + """Define state vars.""" + return { + "flow_mass_phase_comp": self.flow_mass_phase_comp, + "temperature": self.temperature, + "pressure": self.pressure, + } + + # ----------------------------------------------------------------------------- + # Scaling methods + def calculate_scaling_factors(self): + super().calculate_scaling_factors() + + # these variables should have user input + if iscale.get_scaling_factor(self.flow_mass_phase_comp["Liq", "H2O"]) is None: + sf = iscale.get_scaling_factor( + self.flow_mass_phase_comp["Liq", "H2O"], default=1e0, warning=True + ) + iscale.set_scaling_factor(self.flow_mass_phase_comp["Liq", "H2O"], sf) + + if ( + iscale.get_scaling_factor(self.flow_mass_phase_comp["Liq", "DrawSolution"]) + is None + ): + sf = iscale.get_scaling_factor( + self.flow_mass_phase_comp["Liq", "DrawSolution"], + default=1e0, + warning=True, + ) + iscale.set_scaling_factor( + self.flow_mass_phase_comp["Liq", "DrawSolution"], sf + ) + + if self.is_property_constructed("pressure_osm_phase"): + if iscale.get_scaling_factor(self.pressure_osm_phase["Liq"]) is None: + iscale.set_scaling_factor( + self.pressure_osm_phase["Liq"], + iscale.get_scaling_factor(self.pressure), + ) + + if self.is_property_constructed("mass_frac_phase_comp"): + for j in self.params.component_list: + if ( + iscale.get_scaling_factor(self.mass_frac_phase_comp["Liq", j]) + is None + ): + if j == "DrawSolution": + sf = iscale.get_scaling_factor( + self.flow_mass_phase_comp["Liq", j] + ) / iscale.get_scaling_factor( + self.flow_mass_phase_comp["Liq", "H2O"] + ) + iscale.set_scaling_factor( + self.mass_frac_phase_comp["Liq", j], sf + ) + elif j == "H2O": + iscale.set_scaling_factor( + self.mass_frac_phase_comp["Liq", j], 1 + ) + + if self.is_property_constructed("flow_vol_phase"): + sf = iscale.get_scaling_factor( + self.flow_mass_phase_comp["Liq", "H2O"] + ) / iscale.get_scaling_factor(self.dens_mass_phase["Liq"]) + iscale.set_scaling_factor(self.flow_vol_phase, sf) + + if self.is_property_constructed("conc_mass_phase_comp"): + for j in self.params.component_list: + sf_dens = iscale.get_scaling_factor(self.dens_mass_phase["Liq"]) + if ( + iscale.get_scaling_factor(self.conc_mass_phase_comp["Liq", j]) + is None + ): + if j == "H2O": + # solvents typically have a mass fraction between 0.5-1 + iscale.set_scaling_factor( + self.conc_mass_phase_comp["Liq", j], sf_dens + ) + elif j == "DrawSolution": + iscale.set_scaling_factor( + self.conc_mass_phase_comp["Liq", j], + sf_dens + * iscale.get_scaling_factor( + self.mass_frac_phase_comp["Liq", j] + ), + ) diff --git a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py new file mode 100644 index 00000000..ef22bc4c --- /dev/null +++ b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py @@ -0,0 +1,171 @@ +############################################################################### +# WaterTAP Copyright (c) 2021, 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 pytest +from pyomo.environ import ( + ConcreteModel, + Suffix, + value, + Var, + Set, + assert_optimal_termination, +) +from pyomo.util.check_units import assert_units_consistent +from idaes.core import FlowsheetBlock + +from idaes.core.solvers.get_solver import get_solver +from idaes.core.util.model_statistics import ( + number_variables, + number_total_constraints, + number_unused_variables, +) + +from idaes.core.util.scaling import calculate_scaling_factors + +from watertap.core.util.initialization import check_dof +from watertap.property_models.tests.property_test_harness import PropertyAttributeError + +import watertap_contrib.reflo.property_models.fo_draw_solution_properties as ds_props + + +solver = get_solver() + +# class TestFODrawSolutionProperty_idaes(PropertyTestHarness_idaes): +# def configure(self): +# self.prop_pack = ds_props.FODrawSolutionParameterBlock +# self.param_args = {} +# self.prop_args = {} + +# self.skip_initialization_raises_exception_test = True +# self.skip_state_block_mole_frac_phase_comp_test = True + + +@pytest.fixture(scope="module") +def m(): + m = ConcreteModel() + + m.fs = FlowsheetBlock(dynamic=False) + m.fs.properties = ds_props.FODrawSolutionParameterBlock() + + return m + + +@pytest.mark.unit +def test_parameter_block(m): + assert isinstance(m.fs.properties.component_list, Set) + for j in m.fs.properties.component_list: + assert j in ["H2O", "DrawSolution"] + assert isinstance(m.fs.properties.solute_set, Set) + assert "H2O" not in m.fs.properties.solute_set + assert isinstance(m.fs.properties.solvent_set, Set) + for j in m.fs.properties.solvent_set: + assert j in ["H2O"] + + assert isinstance(m.fs.properties.phase_list, Set) + for j in m.fs.properties.phase_list: + assert j in ["Liq"] + + assert m.fs.properties._state_block_class is ds_props.FODrawSolutionStateBlock + + +@pytest.mark.component +def test_parameters(m): + m.fs.stream = m.fs.properties.build_state_block([0], defined_state=True) + + assert hasattr(m.fs.stream[0], "scaling_factor") + assert isinstance(m.fs.stream[0].scaling_factor, Suffix) + + state_vars_lst = ["flow_mass_phase_comp", "temperature", "pressure"] + state_vars_dict = m.fs.stream[0].define_state_vars() + assert len(state_vars_dict) == len(state_vars_lst) + for sv in state_vars_lst: + assert sv in state_vars_dict + assert hasattr(m.fs.stream[0], sv) + var = getattr(m.fs.stream[0], sv) + assert isinstance(var, Var) + + metadata = m.fs.properties.get_metadata().properties + + # check that properties are not built if not demanded + for v in metadata.list_supported_properties(): + if metadata[v.name].method is not None: + if m.fs.stream[0].is_property_constructed(v.name): + raise PropertyAttributeError( + "Property {v_name} is an on-demand property, but was found " + "on the stateblock without being demanded".format(v_name=v.name) + ) + + # check that properties are built if demanded + for v in metadata.list_supported_properties(): + if metadata[v.name].method is not None: + if not hasattr(m.fs.stream[0], v.name): + raise PropertyAttributeError( + "Property {v_name} is an on-demand property, but was not built " + "when demanded".format(v_name=v.name) + ) + + assert number_variables(m) == 12 + assert number_total_constraints(m) == 8 + assert number_unused_variables(m) == 2 + + m.fs.stream[0].flow_mass_phase_comp["Liq", "H2O"].fix(0.5) + m.fs.stream[0].flow_mass_phase_comp["Liq", "DrawSolution"].fix(0.5) + m.fs.stream[0].temperature.fix(25 + 273.15) + m.fs.stream[0].pressure.fix(101325) + m.fs.stream[0].dens_mass_phase[...] + m.fs.stream[0].cp_mass_phase[...] + m.fs.stream[0].pressure_osm_phase[...] + m.fs.stream[0].flow_vol_phase[...] + m.fs.stream[0].conc_mass_phase_comp[...] + m.fs.stream[0].mass_frac_phase_comp[...] + + m.fs.properties.set_default_scaling("flow_mass_phase_comp", 1, index=("Liq", "H2O")) + m.fs.properties.set_default_scaling( + "flow_mass_phase_comp", 1, index=("Liq", "DrawSolution") + ) + + calculate_scaling_factors(m) + + assert_units_consistent(m) + + check_dof(m, fail_flag=True) + + m.fs.stream.initialize() + + results = solver.solve(m) + assert_optimal_termination(results) + + assert value(m.fs.stream[0].dens_mass_phase["Liq"]) == pytest.approx( + 1067.9535, rel=1e-3 + ) + assert value(m.fs.stream[0].cp_mass_phase["Liq"]) == pytest.approx( + 2785.2975, rel=1e-3 + ) + assert value(m.fs.stream[0].pressure_osm_phase["Liq"]) == pytest.approx( + 7.3161e7, rel=1e-3 + ) + assert value(m.fs.stream[0].flow_vol_phase["Liq"]) == pytest.approx( + 9.3637e-4, rel=1e-3 + ) + assert value(m.fs.stream[0].conc_mass_phase_comp["Liq", "H2O"]) == pytest.approx( + 533.977, rel=1e-3 + ) + assert value( + m.fs.stream[0].conc_mass_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(533.977, rel=1e-3) + assert value(m.fs.stream[0].mass_frac_phase_comp["Liq", "H2O"]) == pytest.approx( + 0.5, rel=1e-3 + ) + assert value( + m.fs.stream[0].mass_frac_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(0.5, rel=1e-3) diff --git a/src/watertap_contrib/reflo/unit_models/__init__.py b/src/watertap_contrib/reflo/unit_models/__init__.py index e0a2b41b..6ef276a2 100644 --- a/src/watertap_contrib/reflo/unit_models/__init__.py +++ b/src/watertap_contrib/reflo/unit_models/__init__.py @@ -12,3 +12,4 @@ from .zero_order.chemical_softening_zo import ChemicalSofteningZO from .air_stripping_0D import AirStripping0D +from .forward_osmosis_0D import ForwardOsmosis0D diff --git a/src/watertap_contrib/reflo/unit_models/forward_osmosis_0D.py b/src/watertap_contrib/reflo/unit_models/forward_osmosis_0D.py new file mode 100644 index 00000000..ed86eb10 --- /dev/null +++ b/src/watertap_contrib/reflo/unit_models/forward_osmosis_0D.py @@ -0,0 +1,204 @@ +################################################################################# +# WaterTAP Copyright (c) 2020-2023, 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/" +################################################################################# + +from copy import deepcopy + +# Import Pyomo libraries +from pyomo.environ import ( + Var, + Param, + Suffix, + check_optimal_termination, + units as pyunits, +) +from pyomo.common.config import ConfigBlock, ConfigValue, In, PositiveInt + +# Import IDAES cores +from idaes.core import ( + declare_process_block_class, + UnitModelBlockData, + useDefault, +) +from idaes.core.util.model_statistics import degrees_of_freedom +from idaes.core.util.config import is_physical_parameter_block +import idaes.core.util.scaling as iscale +from idaes.core.solvers import get_solver +from idaes.core.util.exceptions import ConfigurationError, InitializationError +import idaes.logger as idaeslog + +# from watertap_contrib.reflo.costing.units.med_tvc_surrogate import ( +# cost_med_tvc_surrogate, +# ) + +_log = idaeslog.getLogger(__name__) +__author__ = "Zhuoran Zhang" + + +@declare_process_block_class("ForwardOsmosis0D") +class ForwardOsmosis0DData(UnitModelBlockData): + """ + Forward Osmosis - 0D model + """ + + CONFIG = ConfigBlock() + + CONFIG.declare( + "dynamic", + ConfigValue( + domain=In([False]), + default=False, + description="Dynamic model flag - must be False", + doc="""Indicates whether this model will be dynamic or not, + **default** = False. """, + ), + ) + CONFIG.declare( + "has_holdup", + ConfigValue( + default=False, + domain=In([False]), + description="Holdup construction flag - must be False", + doc="""Indicates whether holdup terms should be constructed or not. + **default** - False.""", + ), + ) + CONFIG.declare( + "property_package_water", + ConfigValue( + default=useDefault, + domain=is_physical_parameter_block, + description="Property package to use for feed, distillate, brine and cooling water properties", + doc="""Property parameter object used to define water property calculations, + **default** - useDefault. + **Valid values:** { + **useDefault** - use default package from parent model or flowsheet, + **PhysicalParameterObject** - a PhysicalParameterBlock object.}""", + ), + ) + CONFIG.declare( + "property_package_drawsolution", + ConfigValue( + default=useDefault, + domain=is_physical_parameter_block, + description="Property package to use for heating and motive steam properties", + doc="""Property parameter object used to define steasm property calculations, + **default** - useDefault. + **Valid values:** { + **useDefault** - use default package from parent model or flowsheet, + **PhysicalParameterObject** - a PhysicalParameterBlock object.}""", + ), + ) + CONFIG.declare( + "property_package_args", + ConfigBlock( + implicit=True, + description="Arguments to use for constructing property packages", + doc="""A ConfigBlock with arguments to be passed to a property block(s) + and used when constructing these, + **default** - None. + **Valid values:** { + see property package for documentation.}""", + ), + ) + + def build(self): + super().build() + + self.scaling_factor = Suffix(direction=Suffix.EXPORT) + + units_meta = ( + self.config.property_package_liquid.get_metadata().get_derived_units + ) + + """ + Specify system configurations + """ + self.recovery_ratio = Var( + initialize=0.3, + bounds=(0, 1), + units=pyunits.dimensionless, + doc="Recovery ratio", + ) + + self.regeneration_temp = Var( + initialize = 90 + 273.15, + bounds=(0, None), + units=pyunits.K, + doc='Regeneration temperature of draw solution in the separator' + ) + + self.separator_temp_loss = Var( + initialize = 1, + bounds=(0, None), + units=pyunits.K, + doc='Temperature loss of draw solution in the separator' + ) + + """ + Add block for feed water + """ + tmp_dict = dict(**self.config.property_package_args) + tmp_dict["has_phase_equilibrium"] = False + tmp_dict["parameters"] = self.config.property_package_water + tmp_dict["defined_state"] = True + + self.feed_props = self.config.property_package_water.state_block_class( + self.flowsheet().config.time, + doc="Material properties of feed water", + **tmp_dict, + ) + + + """ + Add block for brine + """ + tmp_dict["defined_state"] = False + + self.brine_props = self.config.property_package_liquid.state_block_class( + self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict + ) + + """ + Add block for strong draw solution entering the membrane module + """ + tmp_dict["defined_state"] = False + + self.strong_draw_props = self.config.property_package_drawsolution.state_block_class( + self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict + ) + + """ + Add block for weak draw solution leaving the membrane module + """ + tmp_dict["defined_state"] = False + + self.weak_draw_props = self.config.property_package_drawsolution.state_block_class( + self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict + ) + + """ + Add block for regenerated draw solution from the separator + """ + tmp_dict["defined_state"] = False + + self.reg_draw_props = self.config.property_package_drawsolution.state_block_class( + self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict + ) + + """ + Add block for the product water from the separator + """ + tmp_dict["defined_state"] = False + + self.product_props = self.config.property_package_drawsolution.state_block_class( + self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict + ) \ No newline at end of file diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis.py deleted file mode 100644 index e69de29b..00000000 From dd90b6063a155e3e6e6b2b1921dd700fefc3b426 Mon Sep 17 00:00:00 2001 From: Zhuoran Zhang Date: Thu, 2 May 2024 21:40:30 -0400 Subject: [PATCH 03/34] save progress --- .../fo_draw_solution_properties.py | 166 ++++- .../tests/test_FO_draw_solution_properties.py | 112 ++- .../reflo/unit_models/__init__.py | 2 +- .../reflo/unit_models/forward_osmosis_0D.py | 204 ------ .../zero_order/forward_osmosis_zo.py | 683 ++++++++++++++++++ .../zero_order/tests/test_forward_osmosis.py | 253 +++++++ 6 files changed, 1158 insertions(+), 262 deletions(-) delete mode 100644 src/watertap_contrib/reflo/unit_models/forward_osmosis_0D.py create mode 100644 src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py create mode 100644 src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py diff --git a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py index 3a6b4988..e013fa81 100644 --- a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py @@ -38,7 +38,12 @@ ) import idaes.logger as idaeslog import idaes.core.util.scaling as iscale -from idaes.core.util.exceptions import InitializationError + +from idaes.core.util.exceptions import ( + ConfigurationError, + InitializationError, + PropertyPackageError, +) from pyomo.environ import ( Param, @@ -54,6 +59,7 @@ ) from pyomo.common.config import ConfigValue +from watertap.core.util.scaling import transform_property_constraints __author__ = "Zhuoran Zhang" @@ -98,51 +104,51 @@ def build(self): ) self.dens_mass_param_A1 = Param( - initialize=1.1849, + initialize=1.1849e2, units=dens_units, doc="Mass density parameter A1", ) self.dens_mass_param_A2 = Param( - initialize=1.1455e-2, + initialize=1.1455e2, units=dens_units, doc="Mass density parameter A2", ) self.dens_mass_param_A3 = Param( - initialize=-1.63e-4, + initialize=-1.6343e2, units=dens_units, doc="Mass density parameter A3", ) # osmotic coefficient parameters, equation derived from experimental data self.osm_coeff_param_0 = Param( - initialize=1.2370854e5, + initialize=-1.2370854e5, units=pyunits.Pa, doc="Osmotic coefficient parameter 0", ) self.osm_coeff_param_1 = Param( - initialize=1.2961975e5, + initialize=1.2961975e7, units=pyunits.Pa, doc="Osmotic coefficient parameter 1", ) self.osm_coeff_param_2 = Param( - initialize=1.386231e4, + initialize=-1.386231e8, units=pyunits.Pa, doc="Osmotic coefficient parameter 2", ) self.osm_coeff_param_3 = Param( - initialize=6.356857e2, + initialize=6.356857e8, units=pyunits.Pa, doc="Osmotic coefficient parameter 3", ) self.osm_coeff_param_4 = Param( - initialize=-1.10696e1, + initialize=-1.10696e9, units=pyunits.Pa, doc="Osmotic coefficient parameter 4", ) self.osm_coeff_param_5 = Param( - initialize=6.92e-2, + initialize=6.9232e8, units=pyunits.Pa, doc="Osmotic coefficient parameter 5", ) @@ -155,12 +161,12 @@ def build(self): doc="Specific heat of seawater parameter A0", ) self.cp_phase_param_A1 = Param( - initialize=-3.49888e1, + initialize=-3.49888e3, units=cp_units, doc="Specific heat of seawater parameter A1", ) self.cp_phase_param_A2 = Param( - initialize=1.33883e-1, + initialize=1.33883e3, units=cp_units, doc="Specific heat of seawater parameter A2", ) @@ -262,19 +268,6 @@ def initialize( # Fix state variables flags = fix_state_vars(self, state_args) - # initialize vars calculated from state vars - # for k in self.keys(): - # for j in self[k].params.component_list: - # if self[k].is_property_constructed("flow_mass_comp"): - # if j == "H2O": - # self[k].flow_mass_comp[j].set_value( - # self[k].flow_vol * self[k].dens_mass - # ) - # else: - # self[k].flow_mass_comp[j].set_value( - # self[k].flow_vol * self[k].conc_mass_comp[j] - # ) - # Check when the state vars are fixed already result in dof 0 for k in self.keys(): dof = degrees_of_freedom(self[k]) @@ -329,13 +322,117 @@ def release_state(self, flags, outlvl=idaeslog.NOTSET): """ init_log = idaeslog.getInitLogger(self.name, outlvl, tag="properties") - if flags is None: - return - # Unfix state variables revert_state_vars(self, flags) init_log.info("State Released.") + def calculate_state( + self, + var_args=None, + hold_state=False, + outlvl=idaeslog.NOTSET, + solver=None, + optarg=None, + ): + """ + Solves state blocks given a set of variables and their values. These variables can + be state variables or properties. This method is typically used before + initialization to solve for state variables because non-state variables (i.e. properties) + cannot be fixed in initialization routines. + Keyword Arguments: + var_args : dictionary with variables and their values, they can be state variables or properties + {(VAR_NAME, INDEX): VALUE} + hold_state : flag indicating whether all of the state variables should be fixed after calculate state. + True - State variables will be fixed. + False - State variables will remain unfixed, unless already fixed. + outlvl : idaes logger object that sets output level of solve call (default=idaeslog.NOTSET) + solver : solver name string if None is provided the default solver + for IDAES will be used (default = None) + optarg : solver options dictionary object (default={}) + Returns: + results object from state block solve + """ + # Get logger + solve_log = idaeslog.getSolveLogger(self.name, level=outlvl, tag="properties") + + # Initialize at current state values (not user provided) + self.initialize(solver=solver, optarg=optarg, outlvl=outlvl) + + # Set solver and options + opt = get_solver(solver, optarg) + + # Fix variables and check degrees of freedom + flags = ( + {} + ) # dictionary noting which variables were fixed and their previous state + for k in self.keys(): + sb = self[k] + for (v_name, ind), val in var_args.items(): + var = getattr(sb, v_name) + if iscale.get_scaling_factor(var[ind]) is None: + _log.warning( + "While using the calculate_state method on {sb_name}, variable {v_name} " + "was provided as an argument in var_args, but it does not have a scaling " + "factor. This suggests that the calculate_scaling_factor method has not been " + "used or the variable was created on demand after the scaling factors were " + "calculated. It is recommended to touch all relevant variables (i.e. call " + "them or set an initial value) before using the calculate_scaling_factor " + "method.".format(v_name=v_name, sb_name=sb.name) + ) + if var[ind].is_fixed(): + flags[(k, v_name, ind)] = True + if value(var[ind]) != val: + raise ConfigurationError( + "While using the calculate_state method on {sb_name}, {v_name} was " + "fixed to a value {val}, but it was already fixed to value {val_2}. " + "Unfix the variable before calling the calculate_state " + "method or update var_args." + "".format( + sb_name=sb.name, + v_name=var.name, + val=val, + val_2=value(var[ind]), + ) + ) + else: + flags[(k, v_name, ind)] = False + var[ind].fix(val) + + if degrees_of_freedom(sb) != 0: + raise RuntimeError( + "While using the calculate_state method on {sb_name}, the degrees " + "of freedom were {dof}, but 0 is required. Check var_args and ensure " + "the correct fixed variables are provided." + "".format(sb_name=sb.name, dof=degrees_of_freedom(sb)) + ) + + # Solve + with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: + results = solve_indexed_blocks(opt, [self], tee=slc.tee) + solve_log.info_high( + "Calculate state: {}.".format(idaeslog.condition(results)) + ) + + if not check_optimal_termination(results): + _log.warning( + "While using the calculate_state method on {sb_name}, the solver failed " + "to converge to an optimal solution. This suggests that the user provided " + "infeasible inputs, or that the model is poorly scaled, poorly initialized, " + "or degenerate." + ) + + # unfix all variables fixed with var_args + for (k, v_name, ind), previously_fixed in flags.items(): + if not previously_fixed: + var = getattr(self[k], v_name) + var[ind].unfix() + + # fix state variables if hold_state + if hold_state: + fix_state_vars(self) + + return results + @declare_process_block_class( "FODrawSolutionStateBlock", block_class=_FODrawSolutionStateBlock @@ -383,7 +480,7 @@ def _mass_frac_phase_comp(self): self.params.phase_list, self.params.component_list, initialize=0.1, - bounds=(0.0, None), + bounds=(0.0, 1), units=pyunits.dimensionless, doc="Mass fraction", ) @@ -409,7 +506,7 @@ def _dens_mass_phase(self): ) def rule_dens_mass_phase(b, p): # density, eqn derived from experimental data - s = b.mass_frac_phase_comp[p, "DrawSolution"] * 100 + s = b.mass_frac_phase_comp[p, "DrawSolution"] dens_mass = ( b.params.dens_mass_param_A0 + b.params.dens_mass_param_A1 * s @@ -476,7 +573,7 @@ def _pressure_osm_phase(self): def rule_pressure_osm_phase( b, p ): # osmotic pressure, derived from experimental data - s = b.mass_frac_phase_comp[p, "DrawSolution"] * 100 + s = b.mass_frac_phase_comp[p, "DrawSolution"] pressure_osm_phase = ( b.params.osm_coeff_param_0 + b.params.osm_coeff_param_1 * s @@ -503,7 +600,7 @@ def _cp_mass_phase(self): def rule_cp_mass_phase( b, p ): # specific heat, equation derived from experimental data - s = b.mass_frac_phase_comp[p, "DrawSolution"] * 100 + s = b.mass_frac_phase_comp[p, "DrawSolution"] cp_mass_phase = ( b.params.cp_phase_param_A0 + b.params.cp_phase_param_A1 * s @@ -528,6 +625,9 @@ def default_material_balance_type(self): def get_material_flow_basis(self): return MaterialFlowBasis.mass + def default_energy_balance_type(self): + return EnergyBalanceType.none + def define_state_vars(self): """Define state vars.""" return { @@ -614,3 +714,5 @@ def calculate_scaling_factors(self): self.mass_frac_phase_comp["Liq", j] ), ) + + transform_property_constraints(self) diff --git a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py index ef22bc4c..ac8a1441 100644 --- a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py @@ -37,18 +37,12 @@ import watertap_contrib.reflo.property_models.fo_draw_solution_properties as ds_props +from watertap.property_models.tests.property_test_harness import ( + PropertyTestHarness, +) solver = get_solver() -# class TestFODrawSolutionProperty_idaes(PropertyTestHarness_idaes): -# def configure(self): -# self.prop_pack = ds_props.FODrawSolutionParameterBlock -# self.param_args = {} -# self.prop_args = {} - -# self.skip_initialization_raises_exception_test = True -# self.skip_state_block_mole_frac_phase_comp_test = True - @pytest.fixture(scope="module") def m(): @@ -60,6 +54,57 @@ def m(): return m +class TestSeawaterProperty(PropertyTestHarness): + def configure(self): + self.prop_pack = ds_props.FODrawSolutionParameterBlock + self.param_args = {} + self.scaling_args = { + ("flow_mass_phase_comp", ("Liq", "H2O")): 1, + ("flow_mass_phase_comp", ("Liq", "DrawSolution")): 1, + } + self.stateblock_statistics = { + "number_variables": 12, + "number_total_constraints": 8, + "number_unused_variables": 2, + "default_degrees_of_freedom": 2, + } # 4 state vars, but pressure is not active + self.default_solution = { + ("mass_frac_phase_comp", ("Liq", "H2O")): 0.2, + ("mass_frac_phase_comp", ("Liq", "DrawSolution")): 0.8, + ("dens_mass_phase", "Liq"): 1084.87, + ("flow_vol_phase", "Liq"): 9.2170e-4, + ("conc_mass_phase_comp", ("Liq", "H2O")): 216.975, + ("conc_mass_phase_comp", ("Liq", "DrawSolution")): 867.899, + ("pressure_osm_phase", "Liq"): 2.0447e7, + ("cp_mass_phase", "Liq"): 2257.78, + } + + +# @pytest.mark.component +# class TestCalculateState(PropertyCalculateStateTest): +# def configure(self): +# self.prop_pack = ds_props.FODrawSolutionStateBlock +# self.param_args = {} + +# self.solver = "ipopt" +# self.optarg = {"nlp_scaling_method": "user-scaling"} + +# self.scaling_args = { +# ("flow_mass_phase_comp", ("Liq", "H2O")): 1e0, +# ("flow_mass_phase_comp", ("Liq", "DrawSolution")): 1e0, +# } +# self.var_args = { +# ("flow_vol_phase", "Liq"): 1, +# ("mass_frac_phase_comp", ("Liq", "DrawSolution")): 0.5, +# ("temperature", None): 273.15 + 25, +# ("pressure", None): 101325, +# } +# self.state_solution = { +# ("flow_mass_phase_comp", ("Liq", "H2O")): 19.66, +# ("flow_mass_phase_comp", ("Liq", "DrawSolution")): 1.035, +# } + + @pytest.mark.unit def test_parameter_block(m): assert isinstance(m.fs.properties.component_list, Set) @@ -118,16 +163,10 @@ def test_parameters(m): assert number_total_constraints(m) == 8 assert number_unused_variables(m) == 2 - m.fs.stream[0].flow_mass_phase_comp["Liq", "H2O"].fix(0.5) - m.fs.stream[0].flow_mass_phase_comp["Liq", "DrawSolution"].fix(0.5) + m.fs.stream[0].flow_mass_phase_comp["Liq", "H2O"].fix(0.6) + m.fs.stream[0].flow_mass_phase_comp["Liq", "DrawSolution"].fix(1.4) m.fs.stream[0].temperature.fix(25 + 273.15) m.fs.stream[0].pressure.fix(101325) - m.fs.stream[0].dens_mass_phase[...] - m.fs.stream[0].cp_mass_phase[...] - m.fs.stream[0].pressure_osm_phase[...] - m.fs.stream[0].flow_vol_phase[...] - m.fs.stream[0].conc_mass_phase_comp[...] - m.fs.stream[0].mass_frac_phase_comp[...] m.fs.properties.set_default_scaling("flow_mass_phase_comp", 1, index=("Liq", "H2O")) m.fs.properties.set_default_scaling( @@ -146,26 +185,49 @@ def test_parameters(m): assert_optimal_termination(results) assert value(m.fs.stream[0].dens_mass_phase["Liq"]) == pytest.approx( - 1067.9535, rel=1e-3 + 1083.462, rel=1e-3 ) assert value(m.fs.stream[0].cp_mass_phase["Liq"]) == pytest.approx( - 2785.2975, rel=1e-3 + 2406.8407, rel=1e-3 ) assert value(m.fs.stream[0].pressure_osm_phase["Liq"]) == pytest.approx( - 7.3161e7, rel=1e-3 + 9.64168e6, rel=1e-3 ) assert value(m.fs.stream[0].flow_vol_phase["Liq"]) == pytest.approx( - 9.3637e-4, rel=1e-3 + 1.8459e-3, rel=1e-3 ) assert value(m.fs.stream[0].conc_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 533.977, rel=1e-3 + 325.0386, rel=1e-3 ) assert value( m.fs.stream[0].conc_mass_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(533.977, rel=1e-3) + ) == pytest.approx(758.423, rel=1e-3) assert value(m.fs.stream[0].mass_frac_phase_comp["Liq", "H2O"]) == pytest.approx( - 0.5, rel=1e-3 + 0.3, rel=1e-3 ) assert value( m.fs.stream[0].mass_frac_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(0.5, rel=1e-3) + ) == pytest.approx(0.7, rel=1e-3) + + +@pytest.mark.component +def test_calculate_state(m): + m.fs.stream2 = m.fs.properties.build_state_block([0], defined_state=True) + + m.fs.stream2.calculate_state( + var_args={ + ("flow_vol_phase", "Liq"): 1, + ("mass_frac_phase_comp", ("Liq", "DrawSolution")): 0.5, + ("temperature", None): 25 + 273.15, + # feed flow is at atmospheric pressure + ("pressure", None): 101325, + }, + hold_state=True, + ) + + assert value(m.fs.stream2[0].flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( + 533.95, rel=1e-3 + ) + assert value( + m.fs.stream2[0].flow_mass_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(533.95, rel=1e-3) diff --git a/src/watertap_contrib/reflo/unit_models/__init__.py b/src/watertap_contrib/reflo/unit_models/__init__.py index 6ef276a2..107d7161 100644 --- a/src/watertap_contrib/reflo/unit_models/__init__.py +++ b/src/watertap_contrib/reflo/unit_models/__init__.py @@ -12,4 +12,4 @@ from .zero_order.chemical_softening_zo import ChemicalSofteningZO from .air_stripping_0D import AirStripping0D -from .forward_osmosis_0D import ForwardOsmosis0D +from .zero_order.forward_osmosis_zo import ForwardOsmosisZO diff --git a/src/watertap_contrib/reflo/unit_models/forward_osmosis_0D.py b/src/watertap_contrib/reflo/unit_models/forward_osmosis_0D.py deleted file mode 100644 index ed86eb10..00000000 --- a/src/watertap_contrib/reflo/unit_models/forward_osmosis_0D.py +++ /dev/null @@ -1,204 +0,0 @@ -################################################################################# -# WaterTAP Copyright (c) 2020-2023, 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/" -################################################################################# - -from copy import deepcopy - -# Import Pyomo libraries -from pyomo.environ import ( - Var, - Param, - Suffix, - check_optimal_termination, - units as pyunits, -) -from pyomo.common.config import ConfigBlock, ConfigValue, In, PositiveInt - -# Import IDAES cores -from idaes.core import ( - declare_process_block_class, - UnitModelBlockData, - useDefault, -) -from idaes.core.util.model_statistics import degrees_of_freedom -from idaes.core.util.config import is_physical_parameter_block -import idaes.core.util.scaling as iscale -from idaes.core.solvers import get_solver -from idaes.core.util.exceptions import ConfigurationError, InitializationError -import idaes.logger as idaeslog - -# from watertap_contrib.reflo.costing.units.med_tvc_surrogate import ( -# cost_med_tvc_surrogate, -# ) - -_log = idaeslog.getLogger(__name__) -__author__ = "Zhuoran Zhang" - - -@declare_process_block_class("ForwardOsmosis0D") -class ForwardOsmosis0DData(UnitModelBlockData): - """ - Forward Osmosis - 0D model - """ - - CONFIG = ConfigBlock() - - CONFIG.declare( - "dynamic", - ConfigValue( - domain=In([False]), - default=False, - description="Dynamic model flag - must be False", - doc="""Indicates whether this model will be dynamic or not, - **default** = False. """, - ), - ) - CONFIG.declare( - "has_holdup", - ConfigValue( - default=False, - domain=In([False]), - description="Holdup construction flag - must be False", - doc="""Indicates whether holdup terms should be constructed or not. - **default** - False.""", - ), - ) - CONFIG.declare( - "property_package_water", - ConfigValue( - default=useDefault, - domain=is_physical_parameter_block, - description="Property package to use for feed, distillate, brine and cooling water properties", - doc="""Property parameter object used to define water property calculations, - **default** - useDefault. - **Valid values:** { - **useDefault** - use default package from parent model or flowsheet, - **PhysicalParameterObject** - a PhysicalParameterBlock object.}""", - ), - ) - CONFIG.declare( - "property_package_drawsolution", - ConfigValue( - default=useDefault, - domain=is_physical_parameter_block, - description="Property package to use for heating and motive steam properties", - doc="""Property parameter object used to define steasm property calculations, - **default** - useDefault. - **Valid values:** { - **useDefault** - use default package from parent model or flowsheet, - **PhysicalParameterObject** - a PhysicalParameterBlock object.}""", - ), - ) - CONFIG.declare( - "property_package_args", - ConfigBlock( - implicit=True, - description="Arguments to use for constructing property packages", - doc="""A ConfigBlock with arguments to be passed to a property block(s) - and used when constructing these, - **default** - None. - **Valid values:** { - see property package for documentation.}""", - ), - ) - - def build(self): - super().build() - - self.scaling_factor = Suffix(direction=Suffix.EXPORT) - - units_meta = ( - self.config.property_package_liquid.get_metadata().get_derived_units - ) - - """ - Specify system configurations - """ - self.recovery_ratio = Var( - initialize=0.3, - bounds=(0, 1), - units=pyunits.dimensionless, - doc="Recovery ratio", - ) - - self.regeneration_temp = Var( - initialize = 90 + 273.15, - bounds=(0, None), - units=pyunits.K, - doc='Regeneration temperature of draw solution in the separator' - ) - - self.separator_temp_loss = Var( - initialize = 1, - bounds=(0, None), - units=pyunits.K, - doc='Temperature loss of draw solution in the separator' - ) - - """ - Add block for feed water - """ - tmp_dict = dict(**self.config.property_package_args) - tmp_dict["has_phase_equilibrium"] = False - tmp_dict["parameters"] = self.config.property_package_water - tmp_dict["defined_state"] = True - - self.feed_props = self.config.property_package_water.state_block_class( - self.flowsheet().config.time, - doc="Material properties of feed water", - **tmp_dict, - ) - - - """ - Add block for brine - """ - tmp_dict["defined_state"] = False - - self.brine_props = self.config.property_package_liquid.state_block_class( - self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict - ) - - """ - Add block for strong draw solution entering the membrane module - """ - tmp_dict["defined_state"] = False - - self.strong_draw_props = self.config.property_package_drawsolution.state_block_class( - self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict - ) - - """ - Add block for weak draw solution leaving the membrane module - """ - tmp_dict["defined_state"] = False - - self.weak_draw_props = self.config.property_package_drawsolution.state_block_class( - self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict - ) - - """ - Add block for regenerated draw solution from the separator - """ - tmp_dict["defined_state"] = False - - self.reg_draw_props = self.config.property_package_drawsolution.state_block_class( - self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict - ) - - """ - Add block for the product water from the separator - """ - tmp_dict["defined_state"] = False - - self.product_props = self.config.property_package_drawsolution.state_block_class( - self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict - ) \ No newline at end of file diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py new file mode 100644 index 00000000..b9ec0eb0 --- /dev/null +++ b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py @@ -0,0 +1,683 @@ +################################################################################# +# WaterTAP Copyright (c) 2020-2023, 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/" +################################################################################# + +from copy import deepcopy + +# Import Pyomo libraries +from pyomo.environ import ( + Var, + Param, + Suffix, + check_optimal_termination, + units as pyunits, +) +from pyomo.common.config import ConfigBlock, ConfigValue, In, PositiveInt + +# Import IDAES cores +from idaes.core import ( + declare_process_block_class, + UnitModelBlockData, + useDefault, +) +from idaes.core.util.model_statistics import degrees_of_freedom +from idaes.core.util.config import is_physical_parameter_block +import idaes.core.util.scaling as iscale +from idaes.core.solvers import get_solver +from idaes.core.util.exceptions import ConfigurationError, InitializationError +from idaes.core.util.tables import ( + create_stream_table_dataframe, + stream_table_dataframe_to_string, +) +import idaes.logger as idaeslog + +# from watertap_contrib.reflo.costing.units.forward_osmosis_zo import ( +# cost_forward_osmosis, +# ) + +_log = idaeslog.getLogger(__name__) +__author__ = "Zhuoran Zhang" + + +@declare_process_block_class("ForwardOsmosisZO") +class ForwardOsmosisZOData(UnitModelBlockData): + """ + Forward Osmosis - ZO model + """ + + CONFIG = ConfigBlock() + + CONFIG.declare( + "dynamic", + ConfigValue( + domain=In([False]), + default=False, + description="Dynamic model flag - must be False", + doc="""Indicates whether this model will be dynamic or not, + **default** = False. """, + ), + ) + CONFIG.declare( + "has_holdup", + ConfigValue( + default=False, + domain=In([False]), + description="Holdup construction flag - must be False", + doc="""Indicates whether holdup terms should be constructed or not. + **default** - False.""", + ), + ) + CONFIG.declare( + "property_package_water", + ConfigValue( + default=useDefault, + domain=is_physical_parameter_block, + description="Property package to use for feed, distillate, brine and cooling water properties", + doc="""Property parameter object used to define water property calculations, + **default** - useDefault. + **Valid values:** { + **useDefault** - use default package from parent model or flowsheet, + **PhysicalParameterObject** - a PhysicalParameterBlock object.}""", + ), + ) + CONFIG.declare( + "property_package_draw_solution", + ConfigValue( + default=useDefault, + domain=is_physical_parameter_block, + description="Property package to use for heating and motive steam properties", + doc="""Property parameter object used to define steasm property calculations, + **default** - useDefault. + **Valid values:** { + **useDefault** - use default package from parent model or flowsheet, + **PhysicalParameterObject** - a PhysicalParameterBlock object.}""", + ), + ) + CONFIG.declare( + "property_package_args", + ConfigBlock( + implicit=True, + description="Arguments to use for constructing property packages", + doc="""A ConfigBlock with arguments to be passed to a property block(s) + and used when constructing these, + **default** - None. + **Valid values:** { + see property package for documentation.}""", + ), + ) + + def build(self): + super().build() + + self.scaling_factor = Suffix(direction=Suffix.EXPORT) + + units_meta = self.config.property_package_water.get_metadata().get_derived_units + + """ + Specify system configurations + """ + self.recovery_ratio = Var( + initialize=0.3, + bounds=(0, 1), + units=pyunits.dimensionless, + doc="Recovery ratio", + ) + + self.regeneration_temp = Var( + initialize=90 + 273.15, + bounds=(0, None), + units=pyunits.K, + doc="Regeneration temperature of draw solution in the separator", + ) + + self.separator_temp_loss = Var( + initialize=1, + bounds=(0, None), + units=pyunits.K, + doc="Temperature loss of draw solution in the separator", + ) + + self.dp_brine = Var( + initialize=0, + bounds=(0, None), + units=pyunits.Pa, + doc="Desired pressure difference of draw solution over brine osmotic pressure", + ) + + self.heat_mixing = Var( + initialize=105, + bounds=(0, None), + units=pyunits.MJ / pyunits.m**3, + doc="Heat of mixing in membrane (per m3 of product water)", + ) + + """ + Add intermediate variables + """ + self.heat_transfer_to_brine = Var( + initialize=50, + bounds=(0, None), + units=pyunits.MJ / pyunits.m**3, + doc="Heat of mixing transferred to brine (per m3 of product water)", + ) + + self.heat_transfer_to_weak_draw = Var( + initialize=55, + bounds=(0, None), + units=pyunits.MJ / pyunits.m**3, + doc="Heat of mixing transferred to weak draw solution (per m3 of product water)", + ) + + self.delta_temp_membrane = Var( + initialize=5, + bounds=(0, None), + units=pyunits.K, + doc="Temperature difference between membrane and outlet flow due to heat of mixing", + ) + + self.membrane_temp = Var( + initialize=20 + 273.15, + bounds=(0, None), + units=pyunits.K, + doc="Membrane temperature", + ) + + """ + Add block for feed water + """ + tmp_dict = dict(**self.config.property_package_args) + tmp_dict["has_phase_equilibrium"] = False + tmp_dict["parameters"] = self.config.property_package_water + tmp_dict["defined_state"] = True + + self.feed_props = self.config.property_package_water.state_block_class( + self.flowsheet().config.time, + doc="Material properties of feed water", + **tmp_dict, + ) + + """ + Add block for brine + """ + tmp_dict["defined_state"] = False + + self.brine_props = self.config.property_package_water.state_block_class( + self.flowsheet().config.time, doc="Material properties of brine", **tmp_dict + ) + + """ + Add block for strong draw solution entering the membrane module + """ + tmp_dict["parameters"] = self.config.property_package_draw_solution + self.strong_draw_props = ( + self.config.property_package_draw_solution.state_block_class( + self.flowsheet().config.time, + doc="Material properties of strong draw solution", + **tmp_dict, + ) + ) + + """ + Add block for weak draw solution leaving the membrane module + """ + self.weak_draw_props = ( + self.config.property_package_draw_solution.state_block_class( + self.flowsheet().config.time, + doc="Material properties of weak draw solution", + **tmp_dict, + ) + ) + + """ + Add block for regenerated draw solution from the separator + """ + self.reg_draw_props = ( + self.config.property_package_draw_solution.state_block_class( + self.flowsheet().config.time, + doc="Material properties of regenerated draw solution", + **tmp_dict, + ) + ) + + """ + Add block for the product water from the separator + """ + self.product_props = ( + self.config.property_package_draw_solution.state_block_class( + self.flowsheet().config.time, + doc="Material properties of product water", + **tmp_dict, + ) + ) + + # Add ports + self.add_port(name="feed", block=self.feed_props) + self.add_port(name="brine", block=self.brine_props) + self.add_port(name="strong_draw", block=self.strong_draw_props) + self.add_port(name="weak_draw", block=self.weak_draw_props) + self.add_port(name="reg_draw", block=self.reg_draw_props) + self.add_port(name="product", block=self.product_props) + + """ + Mass balances + """ + + @self.Constraint(doc="Brine volumetric flow rate") + def eq_brine_vol_flow(b): + return b.brine_props[0].flow_vol_phase["Liq"] == b.feed_props[ + 0 + ].flow_vol_phase["Liq"] * (1 - b.recovery_ratio) + + @self.Constraint(doc="Brine salinity") + def eq_brine_salinity(b): + return b.brine_props[0].conc_mass_phase_comp["Liq", "TDS"] == b.feed_props[ + 0 + ].conc_mass_phase_comp["Liq", "TDS"] / (1 - b.recovery_ratio) + + @self.Constraint(doc="Product water flow rate") + def eq_product_water_mass_flow(b): + return ( + b.product_props[0].flow_mass_phase_comp["Liq", "H2O"] + == b.feed_props[0].flow_mass_phase_comp["Liq", "H2O"] + - b.brine_props[0].flow_mass_phase_comp["Liq", "H2O"] + ) + + @self.Constraint(doc="Draw solution mass remains same in the system") + def eq_draw_sol_mass_balance(b): + return ( + b.strong_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"] + == b.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"] + ) + + @self.Constraint(doc="Draw solution dilution") + def eq_water_mass_in_weak_draw(b): + return ( + b.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"] + == b.strong_draw_props[0].flow_mass_phase_comp["Liq", "H2O"] + + b.feed_props[0].flow_mass_phase_comp["Liq", "H2O"] * b.recovery_ratio + ) + + """ + Energy balances + """ + + @self.Constraint(doc="Brine temperature") + def eq_brine_temp(b): + return ( + b.brine_props[0].temperature == b.membrane_temp + b.delta_temp_membrane + ) + + @self.Constraint(doc="Weak draw solution temperature") + def eq_weak_draw_temp(b): + return ( + b.weak_draw_props[0].temperature + == b.membrane_temp + b.delta_temp_membrane + ) + + @self.Constraint(doc="Heat of mixing in membrane") + def eq_heat_mixing(b): + return ( + b.heat_mixing == b.heat_transfer_to_weak_draw + b.heat_transfer_to_brine + ) + + @self.Constraint( + doc="Brine and weak solution approaching same temperature at the outlet of FO membrane" + ) + def eq_temp_dif_membrane1(b): + return b.delta_temp_membrane == pyunits.convert( + b.heat_transfer_to_weak_draw + * b.product_props[0].flow_vol_phase["Liq"] + / b.weak_draw_props[0].dens_mass_phase["Liq"] + / b.weak_draw_props[0].flow_vol_phase["Liq"] + / b.weak_draw_props[0].cp_mass_phase["Liq"], + to_units=pyunits.K, + ) + + @self.Constraint( + doc="Brine and weak solution approaching same temperature at the outlet of FO membrane" + ) + def eq_temp_dif_membrane2(b): + return b.delta_temp_membrane == pyunits.convert( + b.heat_transfer_to_brine + * b.product_props[0].flow_vol_phase["Liq"] + / b.brine_props[0].dens_mass_phase["Liq"] + / b.brine_props[0].flow_vol_phase["Liq"] + / b.brine_props[0].cp_mass_phase["Liq"], + to_units=pyunits.K, + ) + + @self.Constraint(doc="Calculate membrane temperature") + def eq_memb_temp(b): + return b.membrane_temp == ( + b.strong_draw_props[0].dens_mass_phase["Liq"] + * b.strong_draw_props[0].flow_vol_phase["Liq"] + * b.strong_draw_props[0].cp_mass_phase["Liq"] + * b.strong_draw_props[0].temperature + + b.feed_props[0].dens_mass_phase["Liq"] + * b.feed_props[0].flow_vol_phase["Liq"] + * b.feed_props[0].cp_mass_phase["Liq"] + * b.feed_props[0].temperature + ) / ( + b.strong_draw_props[0].dens_mass_phase["Liq"] + * b.strong_draw_props[0].flow_vol_phase["Liq"] + * b.strong_draw_props[0].cp_mass_phase["Liq"] + + b.feed_props[0].dens_mass_phase["Liq"] + * b.feed_props[0].flow_vol_phase["Liq"] + * b.feed_props[0].cp_mass_phase["Liq"] + ) + + """ + System configuration + """ + + @self.Constraint(doc="Required draw solution osmotic pressure") + def eq_weak_draw_osm_pres(b): + return ( + b.weak_draw_props[0].pressure_osm_phase["Liq"] + == b.brine_props[0].pressure_osm_phase["Liq"] + b.dp_brine + ) + + @self.Constraint( + self.config.property_package_draw_solution.component_list, + doc="Draw solution regenerated at the same concentration as strong draw", + ) + def eq_reg_draw_mass_flow(b, j): + return ( + b.reg_draw_props[0].flow_mass_phase_comp["Liq", j] + == b.strong_draw_props[0].flow_mass_phase_comp["Liq", j] + ) + + @self.Constraint(doc="Draw solution regenerated at designed temperature") + def eq_reg_draw_temp(b): + return ( + b.reg_draw_props[0].temperature + == b.regeneration_temp - b.separator_temp_loss + ) + + @self.Constraint(doc="Product water separated at designed temperature") + def eq_product_temp(b): + return ( + b.product_props[0].temperature + == b.regeneration_temp - b.separator_temp_loss + ) + + def initialize_build( + blk, + state_args=None, + outlvl=idaeslog.NOTSET, + solver=None, + optarg=None, + ): + """ + General wrapper for initialization routines + + Keyword Arguments: + state_args : a dict of arguments to be passed to the property + package(s) to provide an initial state for + initialization (see documentation of the specific + property package) (default = {}). + outlvl : sets output level of initialization routine + optarg : solver options dictionary object (default=None) + solver : str indicating which solver to use during + initialization (default = None) + + Returns: None + """ + init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") + solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") + + opt = get_solver(solver, optarg) + # --------------------------------------------------------------------- + flags = blk.feed_props.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + state_args=state_args, + hold_state=True, + ) + init_log.info("Initialization Step 1 Complete.") + # --------------------------------------------------------------------- + # Initialize other state blocks + if state_args is None: + blk.state_args = state_args = {} + state_dict = blk.feed_props[ + blk.flowsheet().config.time.first() + ].define_port_members() + + for k in state_dict.keys(): + if state_dict[k].is_indexed(): + state_args[k] = {} + for m in state_dict[k].keys(): + state_args[k][m] = state_dict[k][m].value + else: + state_args[k] = state_dict[k].value + + state_args_brine = deepcopy(state_args) + for p, j in blk.brine_props.phase_component_set: + if p == "Liq" and j == "H2O": + state_args_brine["flow_mass_phase_comp"][(p, j)] = ( + state_args["flow_mass_phase_comp"][(p, j)] + * (1 - blk.recovery_ratio) + * pyunits.kg + / pyunits.s + ) + + blk.brine_props.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + state_args=state_args_brine, + ) + + # Initialize draw solution blocks + state_args_draw_solution = {} + state_dict_draw_solution = blk.strong_draw_props[ + blk.flowsheet().config.time.first() + ].define_port_members() + + for k in state_dict_draw_solution.keys(): + if state_dict_draw_solution[k].is_indexed(): + state_args_draw_solution[k] = {} + for m in state_dict_draw_solution[k].keys(): + state_args_draw_solution[k][m] = state_dict_draw_solution[k][ + m + ].value + else: + state_args_draw_solution[k] = state_dict_draw_solution[k].value + + blk.strong_draw_props.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + state_args=state_args_draw_solution, + ) + + blk.weak_draw_props.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + state_args=state_args_draw_solution, + ) + + blk.reg_draw_props.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + state_args=state_args_draw_solution, + ) + + state_args_prod = deepcopy(state_args) + for p, j in blk.product_props.phase_component_set: + if j == "DrawSolution": + state_args_prod["flow_mass_phase_comp"][(p, j)] = 0 + + blk.product_props.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + state_args=state_args_draw_solution, + ) + + # Check degree of freedom + assert degrees_of_freedom(blk) == 0 + + # Solve unit + with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: + res = opt.solve(blk, tee=slc.tee) + init_log.info("Initialization Step 2 {}.".format(idaeslog.condition(res))) + # --------------------------------------------------------------------- + # Release Inlet state + blk.feed_props.release_state(flags, outlvl=outlvl) + init_log.info("Initialization Complete: {}".format(idaeslog.condition(res))) + + if not check_optimal_termination(res): + raise InitializationError(f"Unit model {blk.name} failed to initialize") + + def unfix_and_fix_freedom(self, mass_frac_strong_draw, mass_frac_product): + + # Unfix the mass flow rate of draw solution to apply the specified mass fraction + self.strong_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].unfix() + self.strong_draw_props[0].mass_frac_phase_comp["Liq", "DrawSolution"].fix( + mass_frac_strong_draw + ) + + self.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].unfix() + self.product_props[0].mass_frac_phase_comp["Liq", "DrawSolution"].fix( + mass_frac_product + ) + + # Touch properties that need to be calculated + self.reg_draw_props[0].flow_vol_phase["Liq"] + self.reg_draw_props[0].mass_frac_phase_comp["Liq", "DrawSolution"] + + def calculate_scaling_factors(self): + super().calculate_scaling_factors() + + if iscale.get_scaling_factor(self.recovery_ratio) is None: + iscale.set_scaling_factor(self.recovery_ratio, 1e0) + + if iscale.get_scaling_factor(self.regeneration_temp) is None: + iscale.set_scaling_factor(self.regeneration_temp, 1e-2) + + if iscale.get_scaling_factor(self.separator_temp_loss) is None: + iscale.set_scaling_factor(self.separator_temp_loss, 1e0) + + if iscale.get_scaling_factor(self.heat_mixing) is None: + iscale.set_scaling_factor(self.heat_mixing, 1e-2) + + if iscale.get_scaling_factor(self.heat_transfer_to_brine) is None: + iscale.set_scaling_factor(self.heat_transfer_to_brine, 1e-2) + + if iscale.get_scaling_factor(self.heat_transfer_to_weak_draw) is None: + iscale.set_scaling_factor(self.heat_transfer_to_weak_draw, 1e-2) + + if iscale.get_scaling_factor(self.dp_brine) is None: + iscale.set_scaling_factor(self.dp_brine, 1e-6) + + if iscale.get_scaling_factor(self.delta_temp_membrane) is None: + iscale.set_scaling_factor(self.delta_temp_membrane, 1e0) + + if iscale.get_scaling_factor(self.membrane_temp) is None: + iscale.set_scaling_factor(self.membrane_temp, 1e-2) + + # Transforming constraints + sf = iscale.get_scaling_factor(self.feed_props[0].flow_vol_phase["Liq"]) + iscale.constraint_scaling_transform(self.eq_brine_vol_flow, sf) + + sf = iscale.get_scaling_factor( + self.feed_props[0].conc_mass_phase_comp["Liq", "TDS"] + ) + iscale.constraint_scaling_transform(self.eq_brine_salinity, sf) + + sf = iscale.get_scaling_factor(self.feed_props[0].temperature) + iscale.constraint_scaling_transform(self.eq_brine_temp, sf) + + sf = iscale.get_scaling_factor(self.weak_draw_props[0].temperature) + iscale.constraint_scaling_transform(self.eq_weak_draw_temp, sf) + + sf = iscale.get_scaling_factor( + self.strong_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"] + ) + iscale.constraint_scaling_transform(self.eq_draw_sol_mass_balance, sf) + + sf = iscale.get_scaling_factor( + self.strong_draw_props[0].flow_mass_phase_comp["Liq", "H2O"] + ) + iscale.constraint_scaling_transform(self.eq_water_mass_in_weak_draw, sf) + + sf = iscale.get_scaling_factor( + self.weak_draw_props[0].pressure_osm_phase["Liq"] + ) + iscale.constraint_scaling_transform(self.eq_weak_draw_osm_pres, sf) + + sf = iscale.get_scaling_factor(self.heat_mixing) + iscale.constraint_scaling_transform(self.eq_heat_mixing, sf) + + sf = iscale.get_scaling_factor(self.delta_temp_membrane) + iscale.constraint_scaling_transform(self.eq_temp_dif_membrane1, sf) + + sf = iscale.get_scaling_factor(self.delta_temp_membrane) + iscale.constraint_scaling_transform(self.eq_temp_dif_membrane2, sf) + + sf = iscale.get_scaling_factor( + self.product_props[0].flow_mass_phase_comp["Liq", "H2O"] + ) + iscale.constraint_scaling_transform(self.eq_product_water_mass_flow, sf) + + sf = iscale.get_scaling_factor(self.reg_draw_props[0].temperature) + iscale.constraint_scaling_transform(self.eq_reg_draw_temp, sf) + + for ind, c in self.eq_reg_draw_mass_flow.items(): + sf = iscale.get_scaling_factor( + self.strong_draw_props[0].flow_mass_phase_comp["Liq", ind] + ) + iscale.constraint_scaling_transform(c, sf) + + sf = iscale.get_scaling_factor(self.product_props[0].temperature) + iscale.constraint_scaling_transform(self.eq_product_temp, sf) + + sf = iscale.get_scaling_factor(self.membrane_temp) + iscale.constraint_scaling_transform(self.eq_memb_temp, sf) + + def _get_stream_table_contents(self, time_point=0): + return create_stream_table_dataframe( + { + "Feed Water": self.feed_props, + "Brine": self.brine_props, + "Strong Draw": self.strong_draw_props, + "Weak Draw": self.weak_draw_props, + "Regenerated Draw": self.reg_draw_props, + "Product Water": self.product_props, + }, + time_point=time_point, + ) + + def _get_performance_contents(self, time_point=0): + var_dict = {} + var_dict[ + "Strong draw solution volumetric flow rate (m3/s)" + ] = self.strong_draw_props[0].flow_vol_phase["Liq"] + var_dict[ + "Weak draw solution volumetric flow rate (m3/s)" + ] = self.weak_draw_props[0].flow_vol_phase["Liq"] + var_dict["Mass fraction of weak draw solution"] = self.weak_draw_props[ + 0 + ].mass_frac_phase_comp["Liq", "DrawSolution"] + var_dict["Brine salinity (g/L)"] = self.brine_props[0].conc_mass_phase_comp[ + "Liq", "TDS" + ] + var_dict["Membrane temperature (K)"] = self.membrane_temp + + return {"vars": var_dict} + + # @property + # def default_costing_method(self): + # return cost_forward_osmosis diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py new file mode 100644 index 00000000..0bbb023c --- /dev/null +++ b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py @@ -0,0 +1,253 @@ +################################################################################# +# WaterTAP Copyright (c) 2020-2023, 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 pytest +from pyomo.environ import ( + ConcreteModel, + value, + assert_optimal_termination, + units as pyunits, +) +import re +from pyomo.network import Port +from idaes.core import FlowsheetBlock, UnitModelCostingBlock +from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ( + ForwardOsmosisZO, +) +from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( + FODrawSolutionParameterBlock, +) + +from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock +from watertap_contrib.reflo.costing import REFLOCosting +from idaes.core.util.testing import initialization_tester +from idaes.core.util.exceptions import ConfigurationError +from watertap.core.util.initialization import assert_no_degrees_of_freedom +from pyomo.util.check_units import assert_units_consistent + +from idaes.core.solvers import get_solver +from idaes.core.util.model_statistics import ( + degrees_of_freedom, + number_variables, + number_total_constraints, + number_unused_variables, + unused_variables_set, +) +from idaes.core.util.testing import initialization_tester +from idaes.core.util.scaling import ( + calculate_scaling_factors, + constraint_scaling_transform, + unscaled_variables_generator, + unscaled_constraints_generator, + badly_scaled_var_generator, +) + +import idaes.logger as idaeslog + +# ----------------------------------------------------------------------------- +# Get default solver for testing +solver = get_solver() + + +class TestMEDTVC: + @pytest.fixture(scope="class") + def FO_frame(self): + # create model, flowsheet + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + m.fs.water_prop = SeawaterParameterBlock() + m.fs.draw_solution_prop = FODrawSolutionParameterBlock() + m.fs.fo = ForwardOsmosisZO( + property_package_water=m.fs.water_prop, + property_package_draw_solution=m.fs.draw_solution_prop, + ) + + fo = m.fs.fo + feed = fo.feed_props[0] + brine = fo.brine_props[0] + strong_draw = fo.strong_draw_props[0] + weak_draw = fo.weak_draw_props[0] + product = fo.product_props[0] + reg_draw = fo.reg_draw_props[0] + + # System specifications + recovery_ratio = 0.3 # Assumed FO recovery ratio + dp_brine = 0 # Required pressure over brine osmotic pressure (Pa) + heat_mixing = 105 # Heat of mixing in the membrane (MJ/m3 product) + reneration_temp = 90 # Separation temperature of the draw solution (C) + separator_temp_loss = 1 # Temperature loss in the separator (K) + feed_temperature = 13 # Feed water temperature (C) + feed_vol_flow = 3.704 # Feed water volumetric flow rate (m3/s) + feed_TDS_mass = 0.035 # TDS mass fraction of feed + strong_draw_temp = 20 # Strong draw solution inlet temperature (C) + strong_draw_mass = 0.8 # Strong draw solution mass fraction + product_draw_mass = 0.01 # Mass fraction of draw in the product water + + fo.recovery_ratio.fix(recovery_ratio) + fo.dp_brine.fix(dp_brine) + fo.heat_mixing.fix(heat_mixing) + fo.regeneration_temp.fix(reneration_temp + 273.15) + fo.separator_temp_loss.fix(separator_temp_loss) + + # Specifyf strong draw solution properties + fo.strong_draw_props.calculate_state( + var_args={ + ("flow_vol_phase", "Liq"): 1, + ("mass_frac_phase_comp", ("Liq", "DrawSolution")): strong_draw_mass, + ("temperature", None): strong_draw_temp + 273.15, + ("pressure", None): 101325, + }, + hold_state=True, + ) + + strong_draw.flow_mass_phase_comp["Liq", "DrawSolution"].unfix() + + # Specifyf product water properties + fo.product_props.calculate_state( + var_args={ + ("flow_vol_phase", "Liq"): 1, + ("mass_frac_phase_comp", ("Liq", "DrawSolution")): product_draw_mass, + ("temperature", None): reneration_temp - separator_temp_loss + 273.15, + ("pressure", None): 101325, + }, + hold_state=True, + ) + + product.flow_mass_phase_comp["Liq", "H2O"].unfix() + product.temperature.unfix() + + # Specify feed properties + fo.feed_props.calculate_state( + var_args={ + ("flow_vol_phase", "Liq"): feed_vol_flow, + ("mass_frac_phase_comp", ("Liq", "TDS")): feed_TDS_mass, + ("temperature", None): feed_temperature + 273.15, + ("pressure", None): 101325, + }, + hold_state=True, + ) + + # Set scaling factors for mass flow rates + m.fs.water_prop.set_default_scaling( + "flow_mass_phase_comp", 1, index=("Liq", "H2O") + ) + m.fs.water_prop.set_default_scaling( + "flow_mass_phase_comp", 1e2, index=("Liq", "TDS") + ) + m.fs.draw_solution_prop.set_default_scaling( + "flow_mass_phase_comp", 1, index=("Liq", "H2O") + ) + m.fs.draw_solution_prop.set_default_scaling( + "flow_mass_phase_comp", 1, index=("Liq", "DrawSolution") + ) + + return m + + @pytest.mark.unit + def test_dof(self, FO_frame): + m = FO_frame + assert degrees_of_freedom(m) == 0 + + @pytest.mark.unit + def test_calculate_scaling(self, FO_frame): + m = FO_frame + calculate_scaling_factors(m) + + # check that all variables have scaling factors + unscaled_var_list = list(unscaled_variables_generator(m)) + assert len(unscaled_var_list) == 0 + + # check that all constraints have been scaled + unscaled_constraint_list = list(unscaled_constraints_generator(m)) + assert len(unscaled_constraint_list) == 0 + + @pytest.mark.component + def test_var_scaling(self, FO_frame): + m = FO_frame + badly_scaled_var_lst = list(badly_scaled_var_generator(m)) + assert badly_scaled_var_lst == [] + + @pytest.mark.component + def test_initialize(self, FO_frame): + m = FO_frame + initialization_tester(m, unit=m.fs.fo, outlvl=idaeslog.DEBUG) + + @pytest.mark.component + def test_solve(self, FO_frame): + m = FO_frame + + # Unfix the state variables and fix mass fractrion of two state blocks + strong_draw_mass = 0.8 # Strong draw solution mass fraction + product_draw_mass = 0.01 # Mass fraction of draw in the product water + m.fs.fo.unfix_and_fix_freedom(strong_draw_mass, product_draw_mass) + + results = solver.solve(m) + + # Check for optimal solution + assert_optimal_termination(results) + + @pytest.mark.component + def test_solution(self, FO_frame): + m = FO_frame + strong_draw = m.fs.fo.strong_draw_props[0] + weak_draw = m.fs.fo.weak_draw_props[0] + brine = m.fs.fo.brine_props[0] + product = m.fs.fo.product_props[0] + reg_draw = m.fs.fo.reg_draw_props[0] + + assert value( + strong_draw.flow_mass_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(1516.85, rel=1e-3) + assert value(strong_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( + 379.21, rel=1e-3 + ) + assert value(strong_draw.flow_vol_phase["Liq"]) == pytest.approx( + 1.748, rel=1e-3 + ) + assert value( + weak_draw.flow_mass_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(1516.85, rel=1e-3) + assert value(weak_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( + 1479.86, rel=1e-3 + ) + assert value(weak_draw.flow_vol_phase["Liq"]) == pytest.approx(2.804, rel=1e-3) + assert value( + weak_draw.mass_frac_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(0.5062, rel=1e-3) + assert value(brine.mass_frac_phase_comp["Liq", "TDS"]) == pytest.approx( + 0.04954, rel=1e-3 + ) + assert value(brine.conc_mass_phase_comp["Liq", "TDS"]) == pytest.approx( + 51.321, rel=1e-3 + ) + assert value(brine.pressure_osm_phase["Liq"]) == pytest.approx( + 3699254, rel=1e-3 + ) + assert value(product.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( + 1116.09, rel=1e-3 + ) + assert value( + product.flow_mass_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(11.273, rel=1e-3) + assert value(product.flow_vol_phase["Liq"]) == pytest.approx(1.1255, rel=1e-3) + assert value(reg_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( + 379.21, rel=1e-3 + ) + assert value( + reg_draw.flow_mass_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(1516.85, rel=1e-3) + assert value(m.fs.fo.delta_temp_membrane) == pytest.approx(6.2687, rel=1e-3) + assert value(m.fs.fo.membrane_temp) == pytest.approx(287.689, rel=1e-3) + assert value(m.fs.fo.heat_transfer_to_weak_draw) == pytest.approx( + 46.267, rel=1e-3 + ) + assert value(m.fs.fo.heat_transfer_to_brine) == pytest.approx(58.733, rel=1e-3) From 19d881e1763504cabb60e6996ae13dd1672da300 Mon Sep 17 00:00:00 2001 From: Zhuoran Zhang Date: Fri, 3 May 2024 01:28:13 -0400 Subject: [PATCH 04/34] save progress --- .../example_flowsheets/fo_trevi_flowsheet.py | 186 ++++++++++++++++++ .../test/test_fo_trevi_flowsheet.py | 0 .../zero_order/forward_osmosis_zo.py | 6 +- .../zero_order/tests/test_forward_osmosis.py | 31 +-- 4 files changed, 206 insertions(+), 17 deletions(-) create mode 100644 src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py create mode 100644 src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py new file mode 100644 index 00000000..118d0605 --- /dev/null +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py @@ -0,0 +1,186 @@ +################################################################################# +# WaterTAP Copyright (c) 2020-2023, 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/" +################################################################################# + +# Pyomo imports +from pyomo.environ import ( + ConcreteModel, + TransformationFactory, + units as pyunits, + Var, + value, + assert_optimal_termination, +) +from pyomo.network import Arc +from pyomo.util.calc_var_value import calculate_variable_from_constraint + +# IDAES imports +import idaes.core.util.scaling as iscale +import idaes.logger as idaeslog +from idaes.core.util.scaling import ( + calculate_scaling_factors, +) +from idaes.core import FlowsheetBlock, MaterialBalanceType +from idaes.core.solvers.get_solver import get_solver +from idaes.models.unit_models import ( + Mixer, + Separator, + HeatExchanger +) +# from idaes.models.unit_models.heat_exchanger import HX0DInitializer +from idaes.models.unit_models.heat_exchanger import delta_temperature_lmtd_callback + +# WaterTAP imports +from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock +from watertap_contrib.reflo.property_models.fo_draw_solution_properties import FODrawSolutionParameterBlock +from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ForwardOsmosisZO + +def build_fo_treviflowsheet( + m=None, + +): + """ + This function builds a flowsheet as a representative of Trevi's FO system configuration + + Returns: + object: A Pyomo concrete optimization model and flowsheet + """ + if m is None: + m = ConcreteModel() + + m.fs = FlowsheetBlock(dynamic=False) + m.fs.seawater_properties = SeawaterParameterBlock() + m.fs.draw_solution_properties = FODrawSolutionParameterBlock() + + add_fo(m.fs) + + add_HX(m.fs) + + return m + +def add_HX(fs): + fs.HX1A = HeatExchanger( + delta_temperature_callback=delta_temperature_lmtd_callback, + hot_side_name="product_water", + cold_side_name="weak_draw", + product_water={"property_package": fs.draw_solution_properties}, + weak_draw={"property_package": fs.draw_solution_properties} + ) + +def add_fo(fs): + """ + This function adds a FO module to the current flowsheet + + Returns: + object: A FO zero-order model + """ + fs.fo = ForwardOsmosisZO( + property_package_water = fs.seawater_properties, + property_package_draw_solution = fs.draw_solution_properties, + ) + + fo = fs.fo + + # System specifications + recovery_ratio = 0.3 # Assumed FO recovery ratio + dp_brine = 0 # Required pressure over brine osmotic pressure (Pa) + heat_mixing = 105 # Heat of mixing in the membrane (MJ/m3 product) + reneration_temp = 90 # Separation temperature of the draw solution (C) + separator_temp_loss = 1 # Temperature loss in the separator (K) + feed_temperature = 13 # Feed water temperature (C) + feed_vol_flow = 3.704 # Feed water volumetric flow rate (m3/s) + feed_TDS_mass = 0.035 # TDS mass fraction of feed + strong_draw_temp = 20 # Strong draw solution inlet temperature (C) + strong_draw_mass = 0.8 # Strong draw solution mass fraction + product_draw_mass = 0.01 # Mass fraction of draw in the product water + + + fo.recovery_ratio.fix(recovery_ratio) + fo.dp_brine.fix(dp_brine) + fo.heat_mixing.fix(heat_mixing) + fo.regeneration_temp.fix(reneration_temp + 273.15) + fo.separator_temp_loss.fix(separator_temp_loss) + + # Specifyf strong draw solution properties + fo.strong_draw_props.calculate_state( + var_args={ + ("flow_vol_phase", "Liq"): 1, + ("mass_frac_phase_comp", ("Liq", "DrawSolution")): strong_draw_mass, + ("temperature", None): strong_draw_temp + 273.15, + ("pressure", None): 101325, + }, + hold_state=True, + ) + + fo.strong_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].unfix() + + # Specifyf product water properties + fo.product_props.calculate_state( + var_args={ + ("flow_vol_phase", "Liq"): 1, + ("mass_frac_phase_comp", ("Liq", "DrawSolution")): product_draw_mass, + ("temperature", None): reneration_temp - separator_temp_loss + 273.15, + ("pressure", None): 101325, + }, + hold_state=True, + ) + + fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].unfix() + fo.product_props[0].temperature.unfix() + + # Specify feed properties + fo.feed_props.calculate_state( + var_args={ + ("flow_vol_phase", "Liq"): feed_vol_flow, + ("mass_frac_phase_comp", ("Liq", "TDS")): feed_TDS_mass, + ("temperature", None): feed_temperature + 273.15, + ("pressure", None): 101325, + }, + hold_state=True, + ) + + # Set scaling factors for mass flow rates + fs.seawater_properties.set_default_scaling( + "flow_mass_phase_comp", 1, index=("Liq", "H2O") + ) + fs.seawater_properties.set_default_scaling( + "flow_mass_phase_comp", 1e2, index=("Liq", "TDS") + ) + fs.draw_solution_properties.set_default_scaling( + "flow_mass_phase_comp", 1, index=("Liq", "H2O") + ) + fs.draw_solution_properties.set_default_scaling( + "flow_mass_phase_comp", 1, index=("Liq", "DrawSolution") + ) + + +def fix_dof_and_initialize(m, outlvl=idaeslog.WARNING): + calculate_scaling_factors(m) + + m.fs.fo.initialize() + # Unfix the state variables and fix mass fractrion of two state blocks + strong_draw_mass = 0.8 # Strong draw solution mass fraction + product_draw_mass = 0.01 # Mass fraction of draw in the product water + m.fs.fo.unfix_and_fix_freedom(strong_draw_mass, product_draw_mass) + + +if __name__=="__main__": + m = build_fo_treviflowsheet() + fix_dof_and_initialize(m) + + from watertap.core.util.initialization import check_dof + check_dof(m, fail_flag=True) + + solver = get_solver() + + results = solver.solve(m) + assert_optimal_termination(results) + diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py new file mode 100644 index 00000000..e69de29b diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py index b9ec0eb0..5784d487 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py @@ -15,7 +15,6 @@ # Import Pyomo libraries from pyomo.environ import ( Var, - Param, Suffix, check_optimal_termination, units as pyunits, @@ -32,10 +31,9 @@ from idaes.core.util.config import is_physical_parameter_block import idaes.core.util.scaling as iscale from idaes.core.solvers import get_solver -from idaes.core.util.exceptions import ConfigurationError, InitializationError +from idaes.core.util.exceptions import InitializationError from idaes.core.util.tables import ( create_stream_table_dataframe, - stream_table_dataframe_to_string, ) import idaes.logger as idaeslog @@ -119,8 +117,6 @@ def build(self): self.scaling_factor = Suffix(direction=Suffix.EXPORT) - units_meta = self.config.property_package_water.get_metadata().get_derived_units - """ Specify system configurations """ diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py index 0bbb023c..2639d37e 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py @@ -20,19 +20,14 @@ import re from pyomo.network import Port from idaes.core import FlowsheetBlock, UnitModelCostingBlock -from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ( - ForwardOsmosisZO, -) -from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( - FODrawSolutionParameterBlock, -) +from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ForwardOsmosisZO +from watertap_contrib.reflo.property_models.fo_draw_solution_properties import FODrawSolutionParameterBlock from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock from watertap_contrib.reflo.costing import REFLOCosting from idaes.core.util.testing import initialization_tester from idaes.core.util.exceptions import ConfigurationError -from watertap.core.util.initialization import assert_no_degrees_of_freedom -from pyomo.util.check_units import assert_units_consistent + from idaes.core.solvers import get_solver from idaes.core.util.model_statistics import ( @@ -72,12 +67,8 @@ def FO_frame(self): ) fo = m.fs.fo - feed = fo.feed_props[0] - brine = fo.brine_props[0] strong_draw = fo.strong_draw_props[0] - weak_draw = fo.weak_draw_props[0] product = fo.product_props[0] - reg_draw = fo.reg_draw_props[0] # System specifications recovery_ratio = 0.3 # Assumed FO recovery ratio @@ -151,6 +142,22 @@ def FO_frame(self): ) return m + + @pytest.mark.unit + def test_build(self, FO_frame): + m = FO_frame + + # test ports + port_lst = ["feed", "brine", "strong_draw", "weak_draw", "reg_draw", "product"] + for port_str in port_lst: + port = getattr(m.fs.fo, port_str) + assert isinstance(port, Port) + assert len(port.vars) == 3 + + # test statistics + assert number_variables(m) == 163 + assert number_total_constraints(m) == 50 + assert number_unused_variables(m) == 70 # vars from property package parameters @pytest.mark.unit def test_dof(self, FO_frame): From 9d8100edd886c34653197d5666f570f5ba52f1ac Mon Sep 17 00:00:00 2001 From: Zhuoran Zhang Date: Fri, 3 May 2024 01:57:06 -0400 Subject: [PATCH 05/34] merge main --- apidoc/modules.rst | 7 +++ apidoc/reflo.analysis.net_metering.rst | 26 ++++++++++ apidoc/reflo.analysis.rst | 17 +++++++ apidoc/reflo.core.rst | 33 +++++++++++++ apidoc/reflo.costing.rst | 35 ++++++++++++++ apidoc/reflo.costing.solar.rst | 40 ++++++++++++++++ apidoc/reflo.costing.units.rst | 47 +++++++++++++++++++ apidoc/reflo.data.rst | 9 ++++ apidoc/reflo.property_models.rst | 41 ++++++++++++++++ apidoc/reflo.property_models.tests.rst | 33 +++++++++++++ apidoc/reflo.rst | 34 ++++++++++++++ apidoc/reflo.solar_models.rst | 17 +++++++ apidoc/reflo.solar_models.zero_order.rst | 26 ++++++++++ apidoc/reflo.tests.rst | 19 ++++++++ apidoc/reflo.unit_models.rst | 36 ++++++++++++++ apidoc/reflo.unit_models.surrogate.rst | 40 ++++++++++++++++ apidoc/reflo.unit_models.tests.rst | 26 ++++++++++ apidoc/reflo.unit_models.zero_order.rst | 33 +++++++++++++ .../example_flowsheets/fo_trevi_flowsheet.py | 2 + 19 files changed, 521 insertions(+) create mode 100644 apidoc/modules.rst create mode 100644 apidoc/reflo.analysis.net_metering.rst create mode 100644 apidoc/reflo.analysis.rst create mode 100644 apidoc/reflo.core.rst create mode 100644 apidoc/reflo.costing.rst create mode 100644 apidoc/reflo.costing.solar.rst create mode 100644 apidoc/reflo.costing.units.rst create mode 100644 apidoc/reflo.data.rst create mode 100644 apidoc/reflo.property_models.rst create mode 100644 apidoc/reflo.property_models.tests.rst create mode 100644 apidoc/reflo.rst create mode 100644 apidoc/reflo.solar_models.rst create mode 100644 apidoc/reflo.solar_models.zero_order.rst create mode 100644 apidoc/reflo.tests.rst create mode 100644 apidoc/reflo.unit_models.rst create mode 100644 apidoc/reflo.unit_models.surrogate.rst create mode 100644 apidoc/reflo.unit_models.tests.rst create mode 100644 apidoc/reflo.unit_models.zero_order.rst diff --git a/apidoc/modules.rst b/apidoc/modules.rst new file mode 100644 index 00000000..3e819209 --- /dev/null +++ b/apidoc/modules.rst @@ -0,0 +1,7 @@ +reflo +===== + +.. toctree:: + :maxdepth: 4 + + reflo diff --git a/apidoc/reflo.analysis.net_metering.rst b/apidoc/reflo.analysis.net_metering.rst new file mode 100644 index 00000000..5f2426b9 --- /dev/null +++ b/apidoc/reflo.analysis.net_metering.rst @@ -0,0 +1,26 @@ +reflo.analysis.net\_metering package +==================================== + +Submodules +---------- + +reflo.analysis.net\_metering.PV\_RO module +------------------------------------------ + +.. automodule:: reflo.analysis.net_metering.PV_RO + :members: + :show-inheritance: + +reflo.analysis.net\_metering.util module +---------------------------------------- + +.. automodule:: reflo.analysis.net_metering.util + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.analysis.net_metering + :members: + :show-inheritance: diff --git a/apidoc/reflo.analysis.rst b/apidoc/reflo.analysis.rst new file mode 100644 index 00000000..6a0b0e0e --- /dev/null +++ b/apidoc/reflo.analysis.rst @@ -0,0 +1,17 @@ +reflo.analysis package +====================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + reflo.analysis.net_metering + +Module contents +--------------- + +.. automodule:: reflo.analysis + :members: + :show-inheritance: diff --git a/apidoc/reflo.core.rst b/apidoc/reflo.core.rst new file mode 100644 index 00000000..04201441 --- /dev/null +++ b/apidoc/reflo.core.rst @@ -0,0 +1,33 @@ +reflo.core package +================== + +Submodules +---------- + +reflo.core.pysam\_watertap module +--------------------------------- + +.. automodule:: reflo.core.pysam_watertap + :members: + :show-inheritance: + +reflo.core.solar\_energy\_base module +------------------------------------- + +.. automodule:: reflo.core.solar_energy_base + :members: + :show-inheritance: + +reflo.core.wt\_reflo\_database module +------------------------------------- + +.. automodule:: reflo.core.wt_reflo_database + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.core + :members: + :show-inheritance: diff --git a/apidoc/reflo.costing.rst b/apidoc/reflo.costing.rst new file mode 100644 index 00000000..d158767f --- /dev/null +++ b/apidoc/reflo.costing.rst @@ -0,0 +1,35 @@ +reflo.costing package +===================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + reflo.costing.solar + reflo.costing.units + +Submodules +---------- + +reflo.costing.util module +------------------------- + +.. automodule:: reflo.costing.util + :members: + :show-inheritance: + +reflo.costing.watertap\_reflo\_costing\_package module +------------------------------------------------------ + +.. automodule:: reflo.costing.watertap_reflo_costing_package + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.costing + :members: + :show-inheritance: diff --git a/apidoc/reflo.costing.solar.rst b/apidoc/reflo.costing.solar.rst new file mode 100644 index 00000000..297e4411 --- /dev/null +++ b/apidoc/reflo.costing.solar.rst @@ -0,0 +1,40 @@ +reflo.costing.solar package +=========================== + +Submodules +---------- + +reflo.costing.solar.flat\_plate module +-------------------------------------- + +.. automodule:: reflo.costing.solar.flat_plate + :members: + :show-inheritance: + +reflo.costing.solar.photovoltaic module +--------------------------------------- + +.. automodule:: reflo.costing.solar.photovoltaic + :members: + :show-inheritance: + +reflo.costing.solar.thermal\_energy\_storage module +--------------------------------------------------- + +.. automodule:: reflo.costing.solar.thermal_energy_storage + :members: + :show-inheritance: + +reflo.costing.solar.trough\_surrogate module +-------------------------------------------- + +.. automodule:: reflo.costing.solar.trough_surrogate + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.costing.solar + :members: + :show-inheritance: diff --git a/apidoc/reflo.costing.units.rst b/apidoc/reflo.costing.units.rst new file mode 100644 index 00000000..34c548a9 --- /dev/null +++ b/apidoc/reflo.costing.units.rst @@ -0,0 +1,47 @@ +reflo.costing.units package +=========================== + +Submodules +---------- + +reflo.costing.units.air\_stripping module +----------------------------------------- + +.. automodule:: reflo.costing.units.air_stripping + :members: + :show-inheritance: + +reflo.costing.units.chemical\_softening\_zo module +-------------------------------------------------- + +.. automodule:: reflo.costing.units.chemical_softening_zo + :members: + :show-inheritance: + +reflo.costing.units.lt\_med\_surrogate module +--------------------------------------------- + +.. automodule:: reflo.costing.units.lt_med_surrogate + :members: + :show-inheritance: + +reflo.costing.units.med\_tvc\_surrogate module +---------------------------------------------- + +.. automodule:: reflo.costing.units.med_tvc_surrogate + :members: + :show-inheritance: + +reflo.costing.units.vagmd\_surrogate module +------------------------------------------- + +.. automodule:: reflo.costing.units.vagmd_surrogate + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.costing.units + :members: + :show-inheritance: diff --git a/apidoc/reflo.data.rst b/apidoc/reflo.data.rst new file mode 100644 index 00000000..041fe97c --- /dev/null +++ b/apidoc/reflo.data.rst @@ -0,0 +1,9 @@ +reflo.data package +================== + +Module contents +--------------- + +.. automodule:: reflo.data + :members: + :show-inheritance: diff --git a/apidoc/reflo.property_models.rst b/apidoc/reflo.property_models.rst new file mode 100644 index 00000000..9a451441 --- /dev/null +++ b/apidoc/reflo.property_models.rst @@ -0,0 +1,41 @@ +reflo.property\_models package +============================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + reflo.property_models.tests + +Submodules +---------- + +reflo.property\_models.air\_water\_equilibrium\_properties module +----------------------------------------------------------------- + +.. automodule:: reflo.property_models.air_water_equilibrium_properties + :members: + :show-inheritance: + +reflo.property\_models.basic\_water\_properties module +------------------------------------------------------ + +.. automodule:: reflo.property_models.basic_water_properties + :members: + :show-inheritance: + +reflo.property\_models.fo\_draw\_solution\_properties module +------------------------------------------------------------ + +.. automodule:: reflo.property_models.fo_draw_solution_properties + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.property_models + :members: + :show-inheritance: diff --git a/apidoc/reflo.property_models.tests.rst b/apidoc/reflo.property_models.tests.rst new file mode 100644 index 00000000..6fd6ea61 --- /dev/null +++ b/apidoc/reflo.property_models.tests.rst @@ -0,0 +1,33 @@ +reflo.property\_models.tests package +==================================== + +Submodules +---------- + +reflo.property\_models.tests.test\_FO\_draw\_solution\_properties module +------------------------------------------------------------------------ + +.. automodule:: reflo.property_models.tests.test_FO_draw_solution_properties + :members: + :show-inheritance: + +reflo.property\_models.tests.test\_air\_water\_equilibrium\_properties module +----------------------------------------------------------------------------- + +.. automodule:: reflo.property_models.tests.test_air_water_equilibrium_properties + :members: + :show-inheritance: + +reflo.property\_models.tests.test\_basic\_water\_properties module +------------------------------------------------------------------ + +.. automodule:: reflo.property_models.tests.test_basic_water_properties + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.property_models.tests + :members: + :show-inheritance: diff --git a/apidoc/reflo.rst b/apidoc/reflo.rst new file mode 100644 index 00000000..dc1d1e89 --- /dev/null +++ b/apidoc/reflo.rst @@ -0,0 +1,34 @@ +reflo package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + reflo.analysis + reflo.core + reflo.costing + reflo.data + reflo.property_models + reflo.solar_models + reflo.tests + reflo.unit_models + +Submodules +---------- + +reflo.conftest module +--------------------- + +.. automodule:: reflo.conftest + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo + :members: + :show-inheritance: diff --git a/apidoc/reflo.solar_models.rst b/apidoc/reflo.solar_models.rst new file mode 100644 index 00000000..46f34343 --- /dev/null +++ b/apidoc/reflo.solar_models.rst @@ -0,0 +1,17 @@ +reflo.solar\_models package +=========================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + reflo.solar_models.zero_order + +Module contents +--------------- + +.. automodule:: reflo.solar_models + :members: + :show-inheritance: diff --git a/apidoc/reflo.solar_models.zero_order.rst b/apidoc/reflo.solar_models.zero_order.rst new file mode 100644 index 00000000..f4483a91 --- /dev/null +++ b/apidoc/reflo.solar_models.zero_order.rst @@ -0,0 +1,26 @@ +reflo.solar\_models.zero\_order package +======================================= + +Submodules +---------- + +reflo.solar\_models.zero\_order.flat\_plate\_physical module +------------------------------------------------------------ + +.. automodule:: reflo.solar_models.zero_order.flat_plate_physical + :members: + :show-inheritance: + +reflo.solar\_models.zero\_order.photovoltaic\_zo module +------------------------------------------------------- + +.. automodule:: reflo.solar_models.zero_order.photovoltaic_zo + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.solar_models.zero_order + :members: + :show-inheritance: diff --git a/apidoc/reflo.tests.rst b/apidoc/reflo.tests.rst new file mode 100644 index 00000000..d45045ba --- /dev/null +++ b/apidoc/reflo.tests.rst @@ -0,0 +1,19 @@ +reflo.tests package +=================== + +Submodules +---------- + +reflo.tests.test\_version module +-------------------------------- + +.. automodule:: reflo.tests.test_version + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.tests + :members: + :show-inheritance: diff --git a/apidoc/reflo.unit_models.rst b/apidoc/reflo.unit_models.rst new file mode 100644 index 00000000..0df423fe --- /dev/null +++ b/apidoc/reflo.unit_models.rst @@ -0,0 +1,36 @@ +reflo.unit\_models package +========================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + reflo.unit_models.surrogate + reflo.unit_models.tests + reflo.unit_models.zero_order + +Submodules +---------- + +reflo.unit\_models.air\_stripping\_0D module +-------------------------------------------- + +.. automodule:: reflo.unit_models.air_stripping_0D + :members: + :show-inheritance: + +reflo.unit\_models.thermal\_energy\_storage module +-------------------------------------------------- + +.. automodule:: reflo.unit_models.thermal_energy_storage + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.unit_models + :members: + :show-inheritance: diff --git a/apidoc/reflo.unit_models.surrogate.rst b/apidoc/reflo.unit_models.surrogate.rst new file mode 100644 index 00000000..da615b44 --- /dev/null +++ b/apidoc/reflo.unit_models.surrogate.rst @@ -0,0 +1,40 @@ +reflo.unit\_models.surrogate package +==================================== + +Submodules +---------- + +reflo.unit\_models.surrogate.lt\_med\_surrogate module +------------------------------------------------------ + +.. automodule:: reflo.unit_models.surrogate.lt_med_surrogate + :members: + :show-inheritance: + +reflo.unit\_models.surrogate.med\_tvc\_surrogate module +------------------------------------------------------- + +.. automodule:: reflo.unit_models.surrogate.med_tvc_surrogate + :members: + :show-inheritance: + +reflo.unit\_models.surrogate.vagmd\_surrogate module +---------------------------------------------------- + +.. automodule:: reflo.unit_models.surrogate.vagmd_surrogate + :members: + :show-inheritance: + +reflo.unit\_models.surrogate.vagmd\_surrogate\_base module +---------------------------------------------------------- + +.. automodule:: reflo.unit_models.surrogate.vagmd_surrogate_base + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.unit_models.surrogate + :members: + :show-inheritance: diff --git a/apidoc/reflo.unit_models.tests.rst b/apidoc/reflo.unit_models.tests.rst new file mode 100644 index 00000000..1ad6c6f9 --- /dev/null +++ b/apidoc/reflo.unit_models.tests.rst @@ -0,0 +1,26 @@ +reflo.unit\_models.tests package +================================ + +Submodules +---------- + +reflo.unit\_models.tests.test\_air\_stripping\_0D module +-------------------------------------------------------- + +.. automodule:: reflo.unit_models.tests.test_air_stripping_0D + :members: + :show-inheritance: + +reflo.unit\_models.tests.test\_thermal\_energy\_storage module +-------------------------------------------------------------- + +.. automodule:: reflo.unit_models.tests.test_thermal_energy_storage + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.unit_models.tests + :members: + :show-inheritance: diff --git a/apidoc/reflo.unit_models.zero_order.rst b/apidoc/reflo.unit_models.zero_order.rst new file mode 100644 index 00000000..f664ace4 --- /dev/null +++ b/apidoc/reflo.unit_models.zero_order.rst @@ -0,0 +1,33 @@ +reflo.unit\_models.zero\_order package +====================================== + +Submodules +---------- + +reflo.unit\_models.zero\_order.battery module +--------------------------------------------- + +.. automodule:: reflo.unit_models.zero_order.battery + :members: + :show-inheritance: + +reflo.unit\_models.zero\_order.chemical\_softening\_zo module +------------------------------------------------------------- + +.. automodule:: reflo.unit_models.zero_order.chemical_softening_zo + :members: + :show-inheritance: + +reflo.unit\_models.zero\_order.forward\_osmosis\_zo module +---------------------------------------------------------- + +.. automodule:: reflo.unit_models.zero_order.forward_osmosis_zo + :members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: reflo.unit_models.zero_order + :members: + :show-inheritance: diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py index 118d0605..7b0f584b 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py @@ -184,3 +184,5 @@ def fix_dof_and_initialize(m, outlvl=idaeslog.WARNING): results = solver.solve(m) assert_optimal_termination(results) + print(m.fs.fo.strong_draw_props[0].flow_vol_phase["Liq"].value) + From 7a622de20bf7f8e13ba36526f08007091dc5aaf5 Mon Sep 17 00:00:00 2001 From: Zhuoran Zhang Date: Fri, 3 May 2024 01:58:41 -0400 Subject: [PATCH 06/34] revert --- apidoc/modules.rst | 7 ---- apidoc/reflo.analysis.net_metering.rst | 26 ------------- apidoc/reflo.analysis.rst | 17 --------- apidoc/reflo.core.rst | 33 ----------------- apidoc/reflo.costing.rst | 35 ------------------ apidoc/reflo.costing.solar.rst | 40 -------------------- apidoc/reflo.costing.units.rst | 47 ------------------------ apidoc/reflo.data.rst | 9 ----- apidoc/reflo.property_models.rst | 41 --------------------- apidoc/reflo.property_models.tests.rst | 33 ----------------- apidoc/reflo.rst | 34 ----------------- apidoc/reflo.solar_models.rst | 17 --------- apidoc/reflo.solar_models.zero_order.rst | 26 ------------- apidoc/reflo.tests.rst | 19 ---------- apidoc/reflo.unit_models.rst | 36 ------------------ apidoc/reflo.unit_models.surrogate.rst | 40 -------------------- apidoc/reflo.unit_models.tests.rst | 26 ------------- apidoc/reflo.unit_models.zero_order.rst | 33 ----------------- 18 files changed, 519 deletions(-) delete mode 100644 apidoc/modules.rst delete mode 100644 apidoc/reflo.analysis.net_metering.rst delete mode 100644 apidoc/reflo.analysis.rst delete mode 100644 apidoc/reflo.core.rst delete mode 100644 apidoc/reflo.costing.rst delete mode 100644 apidoc/reflo.costing.solar.rst delete mode 100644 apidoc/reflo.costing.units.rst delete mode 100644 apidoc/reflo.data.rst delete mode 100644 apidoc/reflo.property_models.rst delete mode 100644 apidoc/reflo.property_models.tests.rst delete mode 100644 apidoc/reflo.rst delete mode 100644 apidoc/reflo.solar_models.rst delete mode 100644 apidoc/reflo.solar_models.zero_order.rst delete mode 100644 apidoc/reflo.tests.rst delete mode 100644 apidoc/reflo.unit_models.rst delete mode 100644 apidoc/reflo.unit_models.surrogate.rst delete mode 100644 apidoc/reflo.unit_models.tests.rst delete mode 100644 apidoc/reflo.unit_models.zero_order.rst diff --git a/apidoc/modules.rst b/apidoc/modules.rst deleted file mode 100644 index 3e819209..00000000 --- a/apidoc/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -reflo -===== - -.. toctree:: - :maxdepth: 4 - - reflo diff --git a/apidoc/reflo.analysis.net_metering.rst b/apidoc/reflo.analysis.net_metering.rst deleted file mode 100644 index 5f2426b9..00000000 --- a/apidoc/reflo.analysis.net_metering.rst +++ /dev/null @@ -1,26 +0,0 @@ -reflo.analysis.net\_metering package -==================================== - -Submodules ----------- - -reflo.analysis.net\_metering.PV\_RO module ------------------------------------------- - -.. automodule:: reflo.analysis.net_metering.PV_RO - :members: - :show-inheritance: - -reflo.analysis.net\_metering.util module ----------------------------------------- - -.. automodule:: reflo.analysis.net_metering.util - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.analysis.net_metering - :members: - :show-inheritance: diff --git a/apidoc/reflo.analysis.rst b/apidoc/reflo.analysis.rst deleted file mode 100644 index 6a0b0e0e..00000000 --- a/apidoc/reflo.analysis.rst +++ /dev/null @@ -1,17 +0,0 @@ -reflo.analysis package -====================== - -Subpackages ------------ - -.. toctree:: - :maxdepth: 4 - - reflo.analysis.net_metering - -Module contents ---------------- - -.. automodule:: reflo.analysis - :members: - :show-inheritance: diff --git a/apidoc/reflo.core.rst b/apidoc/reflo.core.rst deleted file mode 100644 index 04201441..00000000 --- a/apidoc/reflo.core.rst +++ /dev/null @@ -1,33 +0,0 @@ -reflo.core package -================== - -Submodules ----------- - -reflo.core.pysam\_watertap module ---------------------------------- - -.. automodule:: reflo.core.pysam_watertap - :members: - :show-inheritance: - -reflo.core.solar\_energy\_base module -------------------------------------- - -.. automodule:: reflo.core.solar_energy_base - :members: - :show-inheritance: - -reflo.core.wt\_reflo\_database module -------------------------------------- - -.. automodule:: reflo.core.wt_reflo_database - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.core - :members: - :show-inheritance: diff --git a/apidoc/reflo.costing.rst b/apidoc/reflo.costing.rst deleted file mode 100644 index d158767f..00000000 --- a/apidoc/reflo.costing.rst +++ /dev/null @@ -1,35 +0,0 @@ -reflo.costing package -===================== - -Subpackages ------------ - -.. toctree:: - :maxdepth: 4 - - reflo.costing.solar - reflo.costing.units - -Submodules ----------- - -reflo.costing.util module -------------------------- - -.. automodule:: reflo.costing.util - :members: - :show-inheritance: - -reflo.costing.watertap\_reflo\_costing\_package module ------------------------------------------------------- - -.. automodule:: reflo.costing.watertap_reflo_costing_package - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.costing - :members: - :show-inheritance: diff --git a/apidoc/reflo.costing.solar.rst b/apidoc/reflo.costing.solar.rst deleted file mode 100644 index 297e4411..00000000 --- a/apidoc/reflo.costing.solar.rst +++ /dev/null @@ -1,40 +0,0 @@ -reflo.costing.solar package -=========================== - -Submodules ----------- - -reflo.costing.solar.flat\_plate module --------------------------------------- - -.. automodule:: reflo.costing.solar.flat_plate - :members: - :show-inheritance: - -reflo.costing.solar.photovoltaic module ---------------------------------------- - -.. automodule:: reflo.costing.solar.photovoltaic - :members: - :show-inheritance: - -reflo.costing.solar.thermal\_energy\_storage module ---------------------------------------------------- - -.. automodule:: reflo.costing.solar.thermal_energy_storage - :members: - :show-inheritance: - -reflo.costing.solar.trough\_surrogate module --------------------------------------------- - -.. automodule:: reflo.costing.solar.trough_surrogate - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.costing.solar - :members: - :show-inheritance: diff --git a/apidoc/reflo.costing.units.rst b/apidoc/reflo.costing.units.rst deleted file mode 100644 index 34c548a9..00000000 --- a/apidoc/reflo.costing.units.rst +++ /dev/null @@ -1,47 +0,0 @@ -reflo.costing.units package -=========================== - -Submodules ----------- - -reflo.costing.units.air\_stripping module ------------------------------------------ - -.. automodule:: reflo.costing.units.air_stripping - :members: - :show-inheritance: - -reflo.costing.units.chemical\_softening\_zo module --------------------------------------------------- - -.. automodule:: reflo.costing.units.chemical_softening_zo - :members: - :show-inheritance: - -reflo.costing.units.lt\_med\_surrogate module ---------------------------------------------- - -.. automodule:: reflo.costing.units.lt_med_surrogate - :members: - :show-inheritance: - -reflo.costing.units.med\_tvc\_surrogate module ----------------------------------------------- - -.. automodule:: reflo.costing.units.med_tvc_surrogate - :members: - :show-inheritance: - -reflo.costing.units.vagmd\_surrogate module -------------------------------------------- - -.. automodule:: reflo.costing.units.vagmd_surrogate - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.costing.units - :members: - :show-inheritance: diff --git a/apidoc/reflo.data.rst b/apidoc/reflo.data.rst deleted file mode 100644 index 041fe97c..00000000 --- a/apidoc/reflo.data.rst +++ /dev/null @@ -1,9 +0,0 @@ -reflo.data package -================== - -Module contents ---------------- - -.. automodule:: reflo.data - :members: - :show-inheritance: diff --git a/apidoc/reflo.property_models.rst b/apidoc/reflo.property_models.rst deleted file mode 100644 index 9a451441..00000000 --- a/apidoc/reflo.property_models.rst +++ /dev/null @@ -1,41 +0,0 @@ -reflo.property\_models package -============================== - -Subpackages ------------ - -.. toctree:: - :maxdepth: 4 - - reflo.property_models.tests - -Submodules ----------- - -reflo.property\_models.air\_water\_equilibrium\_properties module ------------------------------------------------------------------ - -.. automodule:: reflo.property_models.air_water_equilibrium_properties - :members: - :show-inheritance: - -reflo.property\_models.basic\_water\_properties module ------------------------------------------------------- - -.. automodule:: reflo.property_models.basic_water_properties - :members: - :show-inheritance: - -reflo.property\_models.fo\_draw\_solution\_properties module ------------------------------------------------------------- - -.. automodule:: reflo.property_models.fo_draw_solution_properties - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.property_models - :members: - :show-inheritance: diff --git a/apidoc/reflo.property_models.tests.rst b/apidoc/reflo.property_models.tests.rst deleted file mode 100644 index 6fd6ea61..00000000 --- a/apidoc/reflo.property_models.tests.rst +++ /dev/null @@ -1,33 +0,0 @@ -reflo.property\_models.tests package -==================================== - -Submodules ----------- - -reflo.property\_models.tests.test\_FO\_draw\_solution\_properties module ------------------------------------------------------------------------- - -.. automodule:: reflo.property_models.tests.test_FO_draw_solution_properties - :members: - :show-inheritance: - -reflo.property\_models.tests.test\_air\_water\_equilibrium\_properties module ------------------------------------------------------------------------------ - -.. automodule:: reflo.property_models.tests.test_air_water_equilibrium_properties - :members: - :show-inheritance: - -reflo.property\_models.tests.test\_basic\_water\_properties module ------------------------------------------------------------------- - -.. automodule:: reflo.property_models.tests.test_basic_water_properties - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.property_models.tests - :members: - :show-inheritance: diff --git a/apidoc/reflo.rst b/apidoc/reflo.rst deleted file mode 100644 index dc1d1e89..00000000 --- a/apidoc/reflo.rst +++ /dev/null @@ -1,34 +0,0 @@ -reflo package -============= - -Subpackages ------------ - -.. toctree:: - :maxdepth: 4 - - reflo.analysis - reflo.core - reflo.costing - reflo.data - reflo.property_models - reflo.solar_models - reflo.tests - reflo.unit_models - -Submodules ----------- - -reflo.conftest module ---------------------- - -.. automodule:: reflo.conftest - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo - :members: - :show-inheritance: diff --git a/apidoc/reflo.solar_models.rst b/apidoc/reflo.solar_models.rst deleted file mode 100644 index 46f34343..00000000 --- a/apidoc/reflo.solar_models.rst +++ /dev/null @@ -1,17 +0,0 @@ -reflo.solar\_models package -=========================== - -Subpackages ------------ - -.. toctree:: - :maxdepth: 4 - - reflo.solar_models.zero_order - -Module contents ---------------- - -.. automodule:: reflo.solar_models - :members: - :show-inheritance: diff --git a/apidoc/reflo.solar_models.zero_order.rst b/apidoc/reflo.solar_models.zero_order.rst deleted file mode 100644 index f4483a91..00000000 --- a/apidoc/reflo.solar_models.zero_order.rst +++ /dev/null @@ -1,26 +0,0 @@ -reflo.solar\_models.zero\_order package -======================================= - -Submodules ----------- - -reflo.solar\_models.zero\_order.flat\_plate\_physical module ------------------------------------------------------------- - -.. automodule:: reflo.solar_models.zero_order.flat_plate_physical - :members: - :show-inheritance: - -reflo.solar\_models.zero\_order.photovoltaic\_zo module -------------------------------------------------------- - -.. automodule:: reflo.solar_models.zero_order.photovoltaic_zo - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.solar_models.zero_order - :members: - :show-inheritance: diff --git a/apidoc/reflo.tests.rst b/apidoc/reflo.tests.rst deleted file mode 100644 index d45045ba..00000000 --- a/apidoc/reflo.tests.rst +++ /dev/null @@ -1,19 +0,0 @@ -reflo.tests package -=================== - -Submodules ----------- - -reflo.tests.test\_version module --------------------------------- - -.. automodule:: reflo.tests.test_version - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.tests - :members: - :show-inheritance: diff --git a/apidoc/reflo.unit_models.rst b/apidoc/reflo.unit_models.rst deleted file mode 100644 index 0df423fe..00000000 --- a/apidoc/reflo.unit_models.rst +++ /dev/null @@ -1,36 +0,0 @@ -reflo.unit\_models package -========================== - -Subpackages ------------ - -.. toctree:: - :maxdepth: 4 - - reflo.unit_models.surrogate - reflo.unit_models.tests - reflo.unit_models.zero_order - -Submodules ----------- - -reflo.unit\_models.air\_stripping\_0D module --------------------------------------------- - -.. automodule:: reflo.unit_models.air_stripping_0D - :members: - :show-inheritance: - -reflo.unit\_models.thermal\_energy\_storage module --------------------------------------------------- - -.. automodule:: reflo.unit_models.thermal_energy_storage - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.unit_models - :members: - :show-inheritance: diff --git a/apidoc/reflo.unit_models.surrogate.rst b/apidoc/reflo.unit_models.surrogate.rst deleted file mode 100644 index da615b44..00000000 --- a/apidoc/reflo.unit_models.surrogate.rst +++ /dev/null @@ -1,40 +0,0 @@ -reflo.unit\_models.surrogate package -==================================== - -Submodules ----------- - -reflo.unit\_models.surrogate.lt\_med\_surrogate module ------------------------------------------------------- - -.. automodule:: reflo.unit_models.surrogate.lt_med_surrogate - :members: - :show-inheritance: - -reflo.unit\_models.surrogate.med\_tvc\_surrogate module -------------------------------------------------------- - -.. automodule:: reflo.unit_models.surrogate.med_tvc_surrogate - :members: - :show-inheritance: - -reflo.unit\_models.surrogate.vagmd\_surrogate module ----------------------------------------------------- - -.. automodule:: reflo.unit_models.surrogate.vagmd_surrogate - :members: - :show-inheritance: - -reflo.unit\_models.surrogate.vagmd\_surrogate\_base module ----------------------------------------------------------- - -.. automodule:: reflo.unit_models.surrogate.vagmd_surrogate_base - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.unit_models.surrogate - :members: - :show-inheritance: diff --git a/apidoc/reflo.unit_models.tests.rst b/apidoc/reflo.unit_models.tests.rst deleted file mode 100644 index 1ad6c6f9..00000000 --- a/apidoc/reflo.unit_models.tests.rst +++ /dev/null @@ -1,26 +0,0 @@ -reflo.unit\_models.tests package -================================ - -Submodules ----------- - -reflo.unit\_models.tests.test\_air\_stripping\_0D module --------------------------------------------------------- - -.. automodule:: reflo.unit_models.tests.test_air_stripping_0D - :members: - :show-inheritance: - -reflo.unit\_models.tests.test\_thermal\_energy\_storage module --------------------------------------------------------------- - -.. automodule:: reflo.unit_models.tests.test_thermal_energy_storage - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.unit_models.tests - :members: - :show-inheritance: diff --git a/apidoc/reflo.unit_models.zero_order.rst b/apidoc/reflo.unit_models.zero_order.rst deleted file mode 100644 index f664ace4..00000000 --- a/apidoc/reflo.unit_models.zero_order.rst +++ /dev/null @@ -1,33 +0,0 @@ -reflo.unit\_models.zero\_order package -====================================== - -Submodules ----------- - -reflo.unit\_models.zero\_order.battery module ---------------------------------------------- - -.. automodule:: reflo.unit_models.zero_order.battery - :members: - :show-inheritance: - -reflo.unit\_models.zero\_order.chemical\_softening\_zo module -------------------------------------------------------------- - -.. automodule:: reflo.unit_models.zero_order.chemical_softening_zo - :members: - :show-inheritance: - -reflo.unit\_models.zero\_order.forward\_osmosis\_zo module ----------------------------------------------------------- - -.. automodule:: reflo.unit_models.zero_order.forward_osmosis_zo - :members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: reflo.unit_models.zero_order - :members: - :show-inheritance: From a0f214a56b1a55a7938368f549dd7dba1d5fd6ae Mon Sep 17 00:00:00 2001 From: Zhuoran Zhang Date: Mon, 6 May 2024 13:00:26 -0400 Subject: [PATCH 07/34] revert and update --- .../fo_draw_solution_properties.py | 43 +++++++++++++++++++ .../tests/test_FO_draw_solution_properties.py | 41 +++--------------- 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py index e013fa81..d8ca369c 100644 --- a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py @@ -48,6 +48,7 @@ from pyomo.environ import ( Param, PositiveReals, + Expression, units as pyunits, Reals, NonNegativeReals, @@ -177,6 +178,7 @@ def build(self): self.set_default_scaling("pressure", 1e-6) self.set_default_scaling("dens_mass_phase", 1e-3, index="Liq") self.set_default_scaling("cp_mass_phase", 1e-3, index="Liq") + self.set_default_scaling("enth_mass_phase", 1e-5, index="Liq") @classmethod def define_metadata(cls, obj): @@ -201,6 +203,8 @@ def define_metadata(cls, obj): "dens_mass_phase": {"method": "_dens_mass_phase"}, "pressure_osm_phase": {"method": "_pressure_osm_phase"}, "cp_mass_phase": {"method": "_cp_mass_phase"}, + "enth_mass_phase": {"method": "_enth_mass_phase"}, + "enth_flow": {"method": "_enth_flow"}, # "visc_d": {"method": "_visc_d"}, } ) @@ -613,12 +617,44 @@ def rule_cp_mass_phase( self.params.phase_list, rule=rule_cp_mass_phase ) + def _enth_mass_phase(self): + self.enth_mass_phase = Var( + self.params.phase_list, + initialize=1e6, + bounds=(1, 1e9), + units=pyunits.J * pyunits.kg**-1, + doc="Specific enthalpy", + ) + + def rule_enth_mass_phase( + b, p + ): + return (b.enth_mass_phase[p] == b.cp_mass_phase[p] * (b.temperature - 273.15 * pyunits.K)) + + self.eq_enth_mass_phase = Constraint( + self.params.phase_list, rule=rule_enth_mass_phase + ) + + def _enth_flow(self): + # enthalpy flow expression for get_enthalpy_flow_terms method + + def rule_enth_flow(b): # enthalpy flow [J/s] + return ( + sum(b.flow_mass_phase_comp["Liq", j] for j in b.params.component_list) + * b.enth_mass_phase["Liq"] + ) + + self.enth_flow = Expression(rule=rule_enth_flow) # ----------------------------------------------------------------------------- # General Methods def get_material_flow_terms(self, p, j): """Create material flow terms for control volume.""" return self.flow_mass_phase_comp[p, j] + def get_enthalpy_flow_terms(self, p): + """Create enthalpy flow terms.""" + return self.enth_flow + def default_material_balance_type(self): return MaterialBalanceType.componentTotal @@ -715,4 +751,11 @@ def calculate_scaling_factors(self): ), ) + if self.is_property_constructed("enth_flow"): + iscale.set_scaling_factor( + self.enth_flow, + iscale.get_scaling_factor(self.flow_mass_phase_comp["Liq", "H2O"]) + * iscale.get_scaling_factor(self.enth_mass_phase["Liq"]), + ) + transform_property_constraints(self) diff --git a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py index ac8a1441..c7ec4f38 100644 --- a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py @@ -63,10 +63,10 @@ def configure(self): ("flow_mass_phase_comp", ("Liq", "DrawSolution")): 1, } self.stateblock_statistics = { - "number_variables": 12, - "number_total_constraints": 8, - "number_unused_variables": 2, - "default_degrees_of_freedom": 2, + "number_variables": 13, + "number_total_constraints": 9, + "number_unused_variables": 1, + "default_degrees_of_freedom": 3, } # 4 state vars, but pressure is not active self.default_solution = { ("mass_frac_phase_comp", ("Liq", "H2O")): 0.2, @@ -79,32 +79,6 @@ def configure(self): ("cp_mass_phase", "Liq"): 2257.78, } - -# @pytest.mark.component -# class TestCalculateState(PropertyCalculateStateTest): -# def configure(self): -# self.prop_pack = ds_props.FODrawSolutionStateBlock -# self.param_args = {} - -# self.solver = "ipopt" -# self.optarg = {"nlp_scaling_method": "user-scaling"} - -# self.scaling_args = { -# ("flow_mass_phase_comp", ("Liq", "H2O")): 1e0, -# ("flow_mass_phase_comp", ("Liq", "DrawSolution")): 1e0, -# } -# self.var_args = { -# ("flow_vol_phase", "Liq"): 1, -# ("mass_frac_phase_comp", ("Liq", "DrawSolution")): 0.5, -# ("temperature", None): 273.15 + 25, -# ("pressure", None): 101325, -# } -# self.state_solution = { -# ("flow_mass_phase_comp", ("Liq", "H2O")): 19.66, -# ("flow_mass_phase_comp", ("Liq", "DrawSolution")): 1.035, -# } - - @pytest.mark.unit def test_parameter_block(m): assert isinstance(m.fs.properties.component_list, Set) @@ -159,10 +133,6 @@ def test_parameters(m): "when demanded".format(v_name=v.name) ) - assert number_variables(m) == 12 - assert number_total_constraints(m) == 8 - assert number_unused_variables(m) == 2 - m.fs.stream[0].flow_mass_phase_comp["Liq", "H2O"].fix(0.6) m.fs.stream[0].flow_mass_phase_comp["Liq", "DrawSolution"].fix(1.4) m.fs.stream[0].temperature.fix(25 + 273.15) @@ -190,6 +160,9 @@ def test_parameters(m): assert value(m.fs.stream[0].cp_mass_phase["Liq"]) == pytest.approx( 2406.8407, rel=1e-3 ) + assert value(m.fs.stream[0].enth_mass_phase["Liq"]) == pytest.approx( + 6.0171e4, rel=1e-3 + ) assert value(m.fs.stream[0].pressure_osm_phase["Liq"]) == pytest.approx( 9.64168e6, rel=1e-3 ) From 1a2531d8668ae525e6eeeeea2d9c51d678553e02 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Sun, 16 Jun 2024 13:23:13 -0400 Subject: [PATCH 08/34] save progress --- .../example_flowsheets/fo_trevi_flowsheet.py | 522 ++++++++++++++++++ .../fo_draw_solution_properties.py | 115 ++++ .../tests/test_FO_draw_solution_properties.py | 30 + .../zero_order/forward_osmosis_zo.py | 116 ++++ .../zero_order/tests/test_forward_osmosis.py | 62 +++ 5 files changed, 845 insertions(+) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py index 7b0f584b..3adf7479 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py @@ -15,10 +15,25 @@ ConcreteModel, TransformationFactory, units as pyunits, +<<<<<<< Updated upstream +======= + Expression, + Reference, +>>>>>>> Stashed changes Var, value, assert_optimal_termination, ) +<<<<<<< Updated upstream +======= +from idaes.core.util.model_statistics import ( + degrees_of_freedom, + number_variables, + number_total_constraints, + number_unused_variables, + unused_variables_set, + ) +>>>>>>> Stashed changes from pyomo.network import Arc from pyomo.util.calc_var_value import calculate_variable_from_constraint @@ -27,23 +42,43 @@ import idaes.logger as idaeslog from idaes.core.util.scaling import ( calculate_scaling_factors, +<<<<<<< Updated upstream ) from idaes.core import FlowsheetBlock, MaterialBalanceType +======= + list_badly_scaled_variables, +) +from idaes.core import FlowsheetBlock, MaterialBalanceType, EnergyBalanceType +>>>>>>> Stashed changes from idaes.core.solvers.get_solver import get_solver from idaes.models.unit_models import ( Mixer, Separator, +<<<<<<< Updated upstream HeatExchanger ) # from idaes.models.unit_models.heat_exchanger import HX0DInitializer from idaes.models.unit_models.heat_exchanger import delta_temperature_lmtd_callback +======= + HeatExchanger, + Heater, +) +# from idaes.models.unit_models.heat_exchanger import HX0DInitializer +from idaes.models.unit_models.heat_exchanger import delta_temperature_lmtd_callback, delta_temperature_underwood_callback +>>>>>>> Stashed changes # WaterTAP imports from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock from watertap_contrib.reflo.property_models.fo_draw_solution_properties import FODrawSolutionParameterBlock from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ForwardOsmosisZO +<<<<<<< Updated upstream def build_fo_treviflowsheet( +======= +from idaes.core.util import DiagnosticsToolbox + +def build_fo_trevi_flowsheet( +>>>>>>> Stashed changes m=None, ): @@ -59,6 +94,7 @@ def build_fo_treviflowsheet( m.fs = FlowsheetBlock(dynamic=False) m.fs.seawater_properties = SeawaterParameterBlock() m.fs.draw_solution_properties = FODrawSolutionParameterBlock() +<<<<<<< Updated upstream add_fo(m.fs) @@ -74,6 +110,150 @@ def add_HX(fs): product_water={"property_package": fs.draw_solution_properties}, weak_draw={"property_package": fs.draw_solution_properties} ) +======= + mfs = m.fs + + RO_recovery = 0.9 + NF_recovery = 0.8 + + + # Add FO module + add_fo(mfs) + + def separation_heat(t): + return 105000 + + # Add heat exchangers + mfs.HX1A = HeatExchanger( + delta_temperature_callback=delta_temperature_underwood_callback, + hot_side_name="product_water", + cold_side_name="weak_draw", + product_water={"property_package": m.fs.draw_solution_properties}, + weak_draw={"property_package": m.fs.draw_solution_properties}, + ) + + # mfs.HX1A.cold_side.properties_in[0].liquid_separation = 0 + # mfs.HX1A.cold_side.properties_out[0].liquid_separation = 0 + + mfs.HX2A = HeatExchanger( + delta_temperature_callback=delta_temperature_underwood_callback, + hot_side_name="reg_draw", + cold_side_name="weak_draw", + reg_draw={"property_package": m.fs.draw_solution_properties}, + weak_draw={"property_package": m.fs.draw_solution_properties} + ) + + + @mfs.Constraint(doc = 'Same outlet temperature from HX 1A and 2A') + def outlet_temp_HX1(b): + return (b.HX1A.weak_draw_outlet.temperature[0] == b.HX2A.weak_draw_outlet.temperature[0]) + + # Add a separator to represent RO and NF fed with the product water to remove remained + # draw solution (NF) and boron (RO), if existing + m.fs.S2 = Separator( + property_package=m.fs.draw_solution_properties, + mixed_state_block=None, + outlet_list=["RO_reject", "NF_reject", "fresh_water"], + split_basis = 3, # Component flow + ) + + @mfs.Constraint(mfs.draw_solution_properties.component_list, + doc = 'NF permeate is sent to RO for further treatment') + def S2_RO_reject(b,j): + permeate_coeff = {"H2O": NF_recovery * (1 - RO_recovery), + "DrawSolution": 0} + return (b.S2.RO_reject.flow_mass_phase_comp[0, "Liq", j] + == b.S2.inlet.flow_mass_phase_comp[0, "Liq", j] + * permeate_coeff[j] + ) + + @mfs.Constraint(mfs.draw_solution_properties.component_list, + doc = 'NF removes all draw solution and the reject is recirculated') + def S2_NF_reject(b,j): + permeate_coeff = {"H2O": 1 - NF_recovery, + "DrawSolution": 1} + return (b.S2.NF_reject.flow_mass_phase_comp[0, "Liq", j] + == b.S2.inlet.flow_mass_phase_comp[0, "Liq", j] + * permeate_coeff[j] + ) + + # Add mixer to mix NF reject that contains draw solution and weak draw + mfs.M1 = Mixer( + property_package=m.fs.draw_solution_properties, + material_balance_type=MaterialBalanceType.componentPhase, + # momentum_mixing_type = 2, + energy_mixing_type=1, + inlet_list=["NF_reject", "weak_draw"], + ) + + # Add a separator to diverge the weak draw solution from FO module for heat recovery + mfs.S1 = Separator( + property_package=m.fs.draw_solution_properties, + mixed_state_block=m.fs.M1.mixed_state, + outlet_list=["to_HX1A", "to_HX2A"], + split_basis = 1, # Total flow + ) + + # Add mixer to combine pre-heated weak draw solution + mfs.M2 = Mixer( + property_package=m.fs.draw_solution_properties, + material_balance_type=MaterialBalanceType.componentPhase, + energy_mixing_type=1, + inlet_list=["HX1A", "HX2A"], + ) + + # Add connections + mfs.HX1A_cold_inlet = Arc( + source=m.fs.S1.to_HX1A, destination=m.fs.HX1A.cold_side_inlet + ) + mfs.HX1A_hot_inlet = Arc( + source=m.fs.fo.product, destination=m.fs.HX1A.hot_side_inlet + ) + mfs.HX2A_cold_inlet = Arc( + source=m.fs.S1.to_HX2A, destination=m.fs.HX2A.cold_side_inlet + ) + mfs.HX2A_hot_inlet = Arc( + source=m.fs.fo.reg_draw, destination=m.fs.HX2A.hot_side_inlet + ) + mfs.S2_inlet = Arc( + source=m.fs.HX1A.hot_side_outlet, destination=m.fs.S2.inlet + ) + mfs.NF_reject_to_M1 = Arc( + source=m.fs.S2.NF_reject, destination=m.fs.M1.NF_reject + ) + mfs.weak_draw_to_M1 = Arc( + source=m.fs.fo.weak_draw, destination=m.fs.M1.weak_draw + ) + mfs.HX1A_to_M2 = Arc( + source=m.fs.HX1A.cold_side_outlet, destination=m.fs.M2.HX1A + ) + mfs.HX2A_to_M2 = Arc( + source=m.fs.HX2A.cold_side_outlet, destination=m.fs.M2.HX2A + ) + + TransformationFactory("network.expand_arcs").apply_to(m) + + + mfs.HX1A.area.fix(50000) + mfs.HX1A.overall_heat_transfer_coefficient[0].fix(100) + mfs.HX2A.area.fix(50000) + mfs.HX2A.overall_heat_transfer_coefficient[0].fix(100) + # mfs.HX1A.heat_transfer_equation.deactivate() + # mfs.HX2A.heat_transfer_equation.deactivate() + + iscale.set_scaling_factor(mfs.HX1A.area, 1e-5) + iscale.set_scaling_factor(mfs.HX1A.overall_heat_transfer_coefficient, 1e-3) + iscale.set_scaling_factor(mfs.HX1A.hot_side.heat, 1e-7) + iscale.set_scaling_factor(mfs.HX1A.cold_side.heat, 1e-7) + iscale.set_scaling_factor(mfs.HX2A.area, 1e-5) + iscale.set_scaling_factor(mfs.HX2A.overall_heat_transfer_coefficient, 1e-3) + iscale.set_scaling_factor(mfs.HX2A.hot_side.heat, 1e-7) + iscale.set_scaling_factor(mfs.HX2A.cold_side.heat, 1e-7) + + return m + + +>>>>>>> Stashed changes def add_fo(fs): """ @@ -91,6 +271,10 @@ def add_fo(fs): # System specifications recovery_ratio = 0.3 # Assumed FO recovery ratio +<<<<<<< Updated upstream +======= + nanofiltration_recovery_ratio = 0.8 # Nanofiltration recovery ratio +>>>>>>> Stashed changes dp_brine = 0 # Required pressure over brine osmotic pressure (Pa) heat_mixing = 105 # Heat of mixing in the membrane (MJ/m3 product) reneration_temp = 90 # Separation temperature of the draw solution (C) @@ -104,6 +288,10 @@ def add_fo(fs): fo.recovery_ratio.fix(recovery_ratio) +<<<<<<< Updated upstream +======= + fo.nanofiltration_recovery_ratio.fix(nanofiltration_recovery_ratio) +>>>>>>> Stashed changes fo.dp_brine.fix(dp_brine) fo.heat_mixing.fix(heat_mixing) fo.regeneration_temp.fix(reneration_temp + 273.15) @@ -147,6 +335,7 @@ def add_fo(fs): hold_state=True, ) +<<<<<<< Updated upstream # Set scaling factors for mass flow rates fs.seawater_properties.set_default_scaling( "flow_mass_phase_comp", 1, index=("Liq", "H2O") @@ -159,6 +348,23 @@ def add_fo(fs): ) fs.draw_solution_properties.set_default_scaling( "flow_mass_phase_comp", 1, index=("Liq", "DrawSolution") +======= + fo.weak_draw_props[0].pressure.fix(101325) + fo.reg_draw_props[0].pressure.fix(101325) + + # Set scaling factors for mass flow rates + fs.seawater_properties.set_default_scaling( + "flow_mass_phase_comp", 1e-1, index=("Liq", "H2O") + ) + fs.seawater_properties.set_default_scaling( + "flow_mass_phase_comp", 1e1, index=("Liq", "TDS") + ) + fs.draw_solution_properties.set_default_scaling( + "flow_mass_phase_comp", 1e-2, index=("Liq", "H2O") + ) + fs.draw_solution_properties.set_default_scaling( + "flow_mass_phase_comp", 1e-2, index=("Liq", "DrawSolution") +>>>>>>> Stashed changes ) @@ -172,17 +378,333 @@ def fix_dof_and_initialize(m, outlvl=idaeslog.WARNING): m.fs.fo.unfix_and_fix_freedom(strong_draw_mass, product_draw_mass) +<<<<<<< Updated upstream if __name__=="__main__": m = build_fo_treviflowsheet() fix_dof_and_initialize(m) +======= + m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + m.fs.S1.to_HX1A.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value / 2 + m.fs.S1.to_HX1A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value / 2 + m.fs.S1.to_HX2A.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value / 2 + m.fs.S1.to_HX2A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value / 2 + m.fs.S1.to_HX1A_state[0].flow_vol_phase["Liq"] + m.fs.S1.to_HX2A_state[0].flow_vol_phase["Liq"] + m.fs.S1.to_HX1A_state[0].cp_mass_phase["Liq"] + m.fs.S1.initialize() + + state_args_1A_hot = {"flow_mass_phase_comp":{("Liq", "H2O"): m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value, + ("Liq", "DrawSolution"): m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value}, + "temperature": m.fs.fo.product_props[0].temperature.value, + "pressure": m.fs.fo.product_props[0].pressure.value} + state_args_1A_cold = {"flow_mass_phase_comp":{("Liq", "H2O"): m.fs.S1.to_HX1A.flow_mass_phase_comp[0, "Liq", "H2O"].value, + ("Liq", "DrawSolution"): m.fs.S1.to_HX1A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value}, + "temperature": m.fs.S1.to_HX1A.temperature[0].value, + "pressure": m.fs.S1.to_HX1A.pressure[0].value} + m.fs.HX1A.initialize(state_args_1 = state_args_1A_hot, state_args_2 = state_args_1A_cold) + m.fs.HX1A.cold_side.properties_out[0].liquid_separation = 1 + + state_args_2A_hot = {"flow_mass_phase_comp":{("Liq", "H2O"): m.fs.fo.reg_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value, + ("Liq", "DrawSolution"): m.fs.fo.reg_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value}, + "temperature": m.fs.fo.reg_draw_props[0].temperature.value, + "pressure": m.fs.fo.reg_draw_props[0].pressure.value} + state_args_2A_cold = {"flow_mass_phase_comp":{("Liq", "H2O"): m.fs.S1.to_HX2A.flow_mass_phase_comp[0, "Liq", "H2O"].value, + ("Liq", "DrawSolution"): m.fs.S1.to_HX2A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value}, + "temperature": m.fs.S1.to_HX2A.temperature[0].value, + "pressure": m.fs.S1.to_HX2A.pressure[0].value} + m.fs.HX2A.initialize(state_args_1 = state_args_2A_hot, state_args_2 = state_args_2A_cold) + m.fs.HX2A.cold_side.properties_out[0].liquid_separation = 1 + + m.fs.S2.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + m.fs.S2.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value * 1e-1 + m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value * 1e-1 + m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + m.fs.S2.initialize() + + m.fs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value + m.fs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + m.fs.M1.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value + m.fs.M1.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value + m.fs.M1.outlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value + m.fs.M1.outlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + m.fs.M1.mixed_state[0].flow_vol_phase["Liq"] + + m.fs.M1.initialize(outlvl = idaeslog.DEBUG) + + + + m.fs.M2.HX1A.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value + m.fs.M2.HX1A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + m.fs.M2.HX2A.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value + m.fs.M2.HX2A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value + m.fs.M2.outlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value + m.fs.M2.outlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + m.fs.M2.initialize() + + print('flowsheet initalization completed') + +if __name__=="__main__": + # from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP + # from pyomo.environ import Objective + # m.fs.o = Objective(expr = 0) + # nlp = PyomoNLP(m) + # jac = nlp.evaluate_jacobian() + # var_list = nlp.primals_names() + # con_list = nlp.constraint_names() + # jac_array = jac.toarray() + # import pandas as pd + # df = pd.DataFrame(jac_array, index = con_list, columns=var_list) + # # if has_touched_vars: + # # msg= "_with_touched_vars" + # # else: + # # msg = "" + # df.to_csv(f'jacobian.csv') + + m = build_fo_trevi_flowsheet() + fix_dof_and_initialize(m) + + # dt = DiagnosticsToolbox(m) + # dt.report_structural_issues() + + # try: + # fix_dof_and_initialize(m) + # except: + # pass + + # dt.report_numerical_issues() + + # display_constraints_with_large_residuals() + # display_variables_at_or_outside_bounds() + # display_variables_with_extreme_jacobians() + # display_constraints_with_extreme_jacobians() + + + + + # print(m.fs.HX1A._get_stream_table_contents()) + # print(m.fs.HX1A.heat_duty[0].value) + + print('no. of var', number_variables(m)) + print('no. of const', + number_total_constraints(m),) + print('no of unused var', number_unused_variables(m)) + +>>>>>>> Stashed changes from watertap.core.util.initialization import check_dof check_dof(m, fail_flag=True) solver = get_solver() +<<<<<<< Updated upstream results = solver.solve(m) assert_optimal_termination(results) print(m.fs.fo.strong_draw_props[0].flow_vol_phase["Liq"].value) +======= + # results = solver.solve(m) + # assert_optimal_termination(results) + + m.fs.HX1A.overall_heat_transfer_coefficient[0].unfix() + m.fs.HX2A.overall_heat_transfer_coefficient[0].unfix() + m.fs.HX1A.weak_draw_outlet.temperature.fix(70 + 273.15) + m.fs.HX1A.product_water_outlet.temperature.fix(23 + 273.15) + # m.fs.HX2A.reg_draw_outlet.temperature.fix(23 + 273.15) + + + check_dof(m, fail_flag=True) + results = solver.solve(m) + assert_optimal_termination(results) + + # def separation_heat(t): + # return 0 + + # mfs = m.fs + # cold_side = m.fs.HX1A.cold_side + # # cold_side.del_component(cold_side.heat) + # # mfs.HX1A.del_component(mfs.HX1A.heat_duty) + # cold_side.del_component(cold_side.enthalpy_balances) + + # cold_side.add_total_enthalpy_balances( + # # balance_type=EnergyBalanceType.enthalpyTotal, + # has_heat_transfer=True, + # custom_term = separation_heat, + # ) + # mfs.HX1A.heat_duty = Reference(cold_side.heat) + + # mfs = m.fs + # mfs.HX1A.cold_side.del_component(mfs.HX1A.cold_side.enthalpy_balances) + + # mfs.HX1A.cold_side.add_energy_balances( + # balance_type=EnergyBalanceType.enthalpyTotal, + # has_heat_transfer=True, + # custom_term = separation_heat, + # ) + # mfs.HX1A.heat_duty = Reference(cold_side.heat) + + # cold_side = mfs.HX1A.cold_side + + # results = solver.solve(m) + # assert_optimal_termination(results) + + + + print('1A cold in temp', m.fs.HX1A.weak_draw_inlet.temperature[0].value - 273.15) + print('1A cold out temp', m.fs.HX1A.weak_draw_outlet.temperature[0].value - 273.15) + print('1A hot in temp', m.fs.HX1A.product_water_inlet.temperature[0].value - 273.15) + print('1A hot out temp', m.fs.HX1A.product_water_outlet.temperature[0].value - 273.15) + print('') + print('2A cold in temp', m.fs.HX2A.weak_draw_inlet.temperature[0].value - 273.15) + print('2A cold out temp', m.fs.HX2A.weak_draw_outlet.temperature[0].value - 273.15) + print('2A hot in temp', m.fs.HX2A.reg_draw_inlet.temperature[0].value - 273.15) + print('2A hot out temp', m.fs.HX2A.reg_draw_outlet.temperature[0].value - 273.15) + print('') + print('FO product vol', m.fs.fo.product_props[0].flow_vol_phase["Liq"].value) + print('FO product cp', m.fs.fo.product_props[0].cp_mass_phase["Liq"].value) + print('FO reg draw vol', m.fs.fo.reg_draw_props[0].flow_vol_phase["Liq"].value) + print('FO reg draw cp', m.fs.fo.reg_draw_props[0].cp_mass_phase["Liq"].value) + print('FO weak draw vol', m.fs.fo.weak_draw_props[0].flow_vol_phase["Liq"].value) + print('FO weak draw cp', m.fs.fo.weak_draw_props[0].cp_mass_phase["Liq"].value) + print('') + print('FO weak H2O', m.fs.fo.weak_draw.flow_mass_phase_comp[0,"Liq","H2O"].value) + print('FO weak draw', m.fs.fo.weak_draw.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) + print('FO weak temp', m.fs.fo.weak_draw.temperature[0].value - 273.15) + print('') + print('FO mixed H2O', m.fs.S1.inlet.flow_mass_phase_comp[0,"Liq","H2O"].value) + print('FO mixed draw', m.fs.S1.inlet.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) + print('FO mixed temp', m.fs.S1.inlet.temperature[0].value - 273.15) + + print('') + print('1A cold in H2O', m.fs.HX1A.cold_side_inlet.flow_mass_phase_comp[0,"Liq","H2O"].value) + print('1A cold in Draw', m.fs.HX1A.cold_side_inlet.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) + print('1A cold vol', m.fs.S1.to_HX1A_state[0].flow_vol_phase["Liq"].value) + print('1A hot in H2O', m.fs.HX1A.hot_side_inlet.flow_mass_phase_comp[0,"Liq","H2O"].value) + print('1A hot in Draw', m.fs.HX1A.hot_side_inlet.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) + print('') + print('2A cold in H2O', m.fs.HX2A.cold_side_inlet.flow_mass_phase_comp[0,"Liq","H2O"].value) + print('2A cold in Draw', m.fs.HX2A.cold_side_inlet.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) + print('2A cold vol', m.fs.S1.to_HX2A_state[0].flow_vol_phase["Liq"].value) + print('2A hot in H2O', m.fs.HX2A.hot_side_inlet.flow_mass_phase_comp[0,"Liq","H2O"].value) + print('2A hot in Draw', m.fs.HX2A.hot_side_inlet.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) + + print('') + print('HX1A hot side heat load (MJ)', value(m.fs.HX1A.hot_side.heat[0]) / 1e6) + print('HX1A cold side heat load (MJ)', value(m.fs.HX1A.cold_side.heat[0]) / 1e6) + print('HX2A hot side heat load (MJ)', value(m.fs.HX2A.hot_side.heat[0]) / 1e6) + print('HX2A cold side heat load (MJ)', value(m.fs.HX2A.cold_side.heat[0]) / 1e6) + print('HX1A approach T', value(m.fs.HX1A.delta_temperature[0])) + + print('') + print('HX1A area', m.fs.HX1A.area.value) + print('HX2A area', m.fs.HX2A.area.value) + print('HX1A coef', m.fs.HX2A.overall_heat_transfer_coefficient[0].value) + print('HX2A coef', m.fs.HX2A.overall_heat_transfer_coefficient[0].value) + + print('') + print('Fresh water mass', m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "H2O"].value) + print('RO reject mass', m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value) + print('NF reject H2O mass', m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value) + print('NF reject draw mass', m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value) + + print('') + print('NF reject temp', m.fs.S2.NF_reject.temperature[0].value - 273.15) + print('weak draw temp', m.fs.fo.weak_draw_props[0].temperature.value - 273.15) + print('M1 temperature', m.fs.M1.mixed_state[0].temperature.value -273.15) + print('M1 H2O mass', m.fs.M1.mixed_state[0].flow_mass_phase_comp['Liq', 'H2O'].value) + print('M1 H2O mass', m.fs.M1.mixed_state[0].flow_mass_phase_comp['Liq', 'DrawSolution'].value) + + print('') + print('M2 mixed H2O mass', m.fs.M2.mixed_state[0].flow_mass_phase_comp["Liq", "H2O"].value) + print('M2 mixed draw mass', m.fs.M2.mixed_state[0].flow_mass_phase_comp["Liq", "DrawSolution"].value) + print('M2 mixed temp', m.fs.M2.mixed_state[0].temperature.value - 273.15) + + + + # m = ConcreteModel() + # m.fs = FlowsheetBlock(dynamic=False) + # m.fs.seawater_properties = SeawaterParameterBlock() + # m.fs.draw_solution_properties = FODrawSolutionParameterBlock() + # mfs = m.fs + # # Add mixer to mix NF reject that contains draw solution and weak draw + # mfs.M1 = Mixer( + # property_package=m.fs.draw_solution_properties, + # material_balance_type=MaterialBalanceType.componentTotal, + # energy_mixing_type=1, + # inlet_list=["NF_reject", "weak_draw"], + # ) + + # mfs.M1.NF_reject.temperature[0].fix(47.8+273.15) + # mfs.M1.NF_reject.pressure[0].fix(101325) + # mfs.M1.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].fix(223.2) + # mfs.M1.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].fix(0.01) + + # mfs.M1.weak_draw.temperature[0].fix(20.81+273.15) + # mfs.M1.weak_draw.pressure[0].fix(101325) + # mfs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "H2O"].fix(1479.96) + # mfs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "DrawSolution"].fix(1516.85) + + # # Set scaling factors for mass flow rates + # mfs.seawater_properties.set_default_scaling( + # "flow_mass_phase_comp", 1e-2, index=("Liq", "H2O") + # ) + # mfs.seawater_properties.set_default_scaling( + # "flow_mass_phase_comp", 1e-3, index=("Liq", "TDS") + # ) + + # mfs.draw_solution_properties.set_default_scaling( + # "flow_mass_phase_comp", 1e-2, index=("Liq", "H2O") + # ) + # mfs.draw_solution_properties.set_default_scaling( + # "flow_mass_phase_comp", 1e-1, index=("Liq", "DrawSolution") + # ) + + # from idaes.core.util.scaling import ( + # calculate_scaling_factors, + # constraint_scaling_transform, + # unscaled_variables_generator, + # unscaled_constraints_generator, + # badly_scaled_var_generator, + # list_badly_scaled_variables, + # ) + # badly_scaled_var_lst = list(badly_scaled_var_generator(m)) + # list_badly_scaled_variables(m) + + # # @mfs.Constraint( + # # doc="Temperature difference") + # # def temp_bound2(b): + # # return b.M1.weak_draw_state[0].mass_frac_phase_comp["Liq", "DrawSolution"] <= 0.6 + + # # @mfs.Constraint( + # # doc="Temperature difference") + # # def temp_bound3(b): + # # return b.M1.mixed_state[0].mass_frac_phase_comp["Liq", "DrawSolution"] <= 0.6 + + + + # mfs.M1.weak_draw.temperature[0].value = 273.15 + 23 + # mfs.M1.weak_draw.pressure[0].value = 101325 + # mfs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "H2O"].value = 1700 + # mfs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = 1528 + # mfs.M1.initialize() + + # from watertap.core.util.initialization import check_dof + # check_dof(m, fail_flag=True) + + # solver = get_solver() + + # results = solver.solve(m) + # assert_optimal_termination(results) + + # print(mfs.M1.mixed_state[0].temperature.value - 273.15) + # print(mfs.M1.mixed_state[0].flow_mass_phase_comp["Liq", "H2O"].value) + # print(mfs.M1.mixed_state[0].flow_mass_phase_comp["Liq", "DrawSolution"].value) + # print(mfs.M1.NF_reject_state[0].mass_frac_phase_comp["Liq", "DrawSolution"].value) + # print(mfs.M1.weak_draw_state[0].mass_frac_phase_comp["Liq", "DrawSolution"].value) + # print(mfs.M1.mixed_state[0].mass_frac_phase_comp["Liq", "DrawSolution"].value) +>>>>>>> Stashed changes diff --git a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py index d8ca369c..5ae1127d 100644 --- a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py @@ -47,7 +47,11 @@ from pyomo.environ import ( Param, +<<<<<<< Updated upstream PositiveReals, +======= + Binary, +>>>>>>> Stashed changes Expression, units as pyunits, Reals, @@ -124,21 +128,34 @@ def build(self): # osmotic coefficient parameters, equation derived from experimental data self.osm_coeff_param_0 = Param( +<<<<<<< Updated upstream initialize=-1.2370854e5, +======= + initialize=-2.31586e5, +>>>>>>> Stashed changes units=pyunits.Pa, doc="Osmotic coefficient parameter 0", ) self.osm_coeff_param_1 = Param( +<<<<<<< Updated upstream initialize=1.2961975e7, +======= + initialize=9.16006e6, +>>>>>>> Stashed changes units=pyunits.Pa, doc="Osmotic coefficient parameter 1", ) self.osm_coeff_param_2 = Param( +<<<<<<< Updated upstream initialize=-1.386231e8, +======= + initialize=-3.25759e7, +>>>>>>> Stashed changes units=pyunits.Pa, doc="Osmotic coefficient parameter 2", ) self.osm_coeff_param_3 = Param( +<<<<<<< Updated upstream initialize=6.356857e8, units=pyunits.Pa, doc="Osmotic coefficient parameter 3", @@ -154,6 +171,12 @@ def build(self): doc="Osmotic coefficient parameter 5", ) +======= + initialize=5.75176e7, + units=pyunits.Pa, + doc="Osmotic coefficient parameter 3", + ) +>>>>>>> Stashed changes # specific heat parameters, derived from experimental data cp_units = pyunits.J / (pyunits.kg * pyunits.K) self.cp_phase_param_A0 = Param( @@ -171,7 +194,15 @@ def build(self): units=cp_units, doc="Specific heat of seawater parameter A2", ) +<<<<<<< Updated upstream +======= + self.separation_heat = Param( + initialize = 105, + units = pyunits.MJ / pyunits.m**3, + doc = "Separation heat per m3 of the separated water", + ) +>>>>>>> Stashed changes # --------------------------------------------------------------------- # Set default scaling factors self.set_default_scaling("temperature", 1e-2) @@ -204,8 +235,13 @@ def define_metadata(cls, obj): "pressure_osm_phase": {"method": "_pressure_osm_phase"}, "cp_mass_phase": {"method": "_cp_mass_phase"}, "enth_mass_phase": {"method": "_enth_mass_phase"}, +<<<<<<< Updated upstream "enth_flow": {"method": "_enth_flow"}, # "visc_d": {"method": "_visc_d"}, +======= + "heat_separation_phase": {"method":"_heat_separation_phase"}, + "enth_flow": {"method": "_enth_flow"}, +>>>>>>> Stashed changes } ) @@ -569,7 +605,11 @@ def _pressure_osm_phase(self): self.pressure_osm_phase = Var( self.params.phase_list, initialize=1e6, +<<<<<<< Updated upstream bounds=(1, 1e8), +======= + bounds=(-1e6, 1e8), +>>>>>>> Stashed changes units=pyunits.Pa, doc="Osmotic pressure", ) @@ -583,8 +623,13 @@ def rule_pressure_osm_phase( + b.params.osm_coeff_param_1 * s + b.params.osm_coeff_param_2 * s**2 + b.params.osm_coeff_param_3 * s**3 +<<<<<<< Updated upstream + b.params.osm_coeff_param_4 * s**4 + b.params.osm_coeff_param_5 * s**5 +======= + # + b.params.osm_coeff_param_4 * s**4 + # + b.params.osm_coeff_param_5 * s**5 +>>>>>>> Stashed changes ) return b.pressure_osm_phase[p] == pressure_osm_phase @@ -617,11 +662,55 @@ def rule_cp_mass_phase( self.params.phase_list, rule=rule_cp_mass_phase ) +<<<<<<< Updated upstream +======= + def _heat_separation_phase(self): + self.heat_separation_phase = Var( + self.params.phase_list, + initialize = 1e6, + bounds = (0,1e10), + units = pyunits.J / pyunits.s, + doc="Heat of liquid separation" + ) + self.liquid_separation = Param( + initialize = 1e-6, + mutable=True, + units = pyunits.dimensionless, + doc = 'Indication of whether liquid separation will happen', + ) + def rule_heat_separation_phase( + b,p + ): + test = (b.liquid_separation + * b.params.separation_heat + * b.flow_mass_phase_comp[p, "H2O"] + + * 1e-3 * pyunits.m**3/ pyunits.kg + + # * b.mass_frac_phase_comp[p,"H2O"] + # / (1-b.mass_frac_phase_comp[p,"H2O"]) + # * (1-0.8) / 0.8 + ) + + # Expr_if & Smooth_min + + + return b.heat_separation_phase[p] == pyunits.convert(test, to_units = pyunits.J / pyunits.s) + + self.eq_heat_separation_phase = Constraint( + self.params.phase_list, rule=rule_heat_separation_phase + ) + +>>>>>>> Stashed changes def _enth_mass_phase(self): self.enth_mass_phase = Var( self.params.phase_list, initialize=1e6, +<<<<<<< Updated upstream bounds=(1, 1e9), +======= + bounds=(1, 1e10), +>>>>>>> Stashed changes units=pyunits.J * pyunits.kg**-1, doc="Specific enthalpy", ) @@ -641,7 +730,12 @@ def _enth_flow(self): def rule_enth_flow(b): # enthalpy flow [J/s] return ( sum(b.flow_mass_phase_comp["Liq", j] for j in b.params.component_list) +<<<<<<< Updated upstream * b.enth_mass_phase["Liq"] +======= + * b.enth_mass_phase["Liq"] + + b.heat_separation_phase["Liq"] +>>>>>>> Stashed changes ) self.enth_flow = Expression(rule=rule_enth_flow) @@ -662,7 +756,11 @@ def get_material_flow_basis(self): return MaterialFlowBasis.mass def default_energy_balance_type(self): +<<<<<<< Updated upstream return EnergyBalanceType.none +======= + return EnergyBalanceType.enthalpyTotal +>>>>>>> Stashed changes def define_state_vars(self): """Define state vars.""" @@ -751,6 +849,23 @@ def calculate_scaling_factors(self): ), ) +<<<<<<< Updated upstream +======= + if self.is_property_constructed("heat_separation_phase"): + iscale.set_scaling_factor( + self.heat_separation_phase, + 1 / self.liquid_separation.value + * iscale.get_scaling_factor(self.flow_mass_phase_comp["Liq", "H2O"]), + ) + + if self.is_property_constructed("enth_mass_phase"): + iscale.set_scaling_factor( + self.enth_mass_phase, + iscale.get_scaling_factor(self.cp_mass_phase["Liq"]) + * iscale.get_scaling_factor(self.temperature), + ) + +>>>>>>> Stashed changes if self.is_property_constructed("enth_flow"): iscale.set_scaling_factor( self.enth_flow, diff --git a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py index c7ec4f38..17b242b2 100644 --- a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py @@ -63,8 +63,13 @@ def configure(self): ("flow_mass_phase_comp", ("Liq", "DrawSolution")): 1, } self.stateblock_statistics = { +<<<<<<< Updated upstream "number_variables": 13, "number_total_constraints": 9, +======= + "number_variables": 14, + "number_total_constraints": 10, +>>>>>>> Stashed changes "number_unused_variables": 1, "default_degrees_of_freedom": 3, } # 4 state vars, but pressure is not active @@ -75,8 +80,14 @@ def configure(self): ("flow_vol_phase", "Liq"): 9.2170e-4, ("conc_mass_phase_comp", ("Liq", "H2O")): 216.975, ("conc_mass_phase_comp", ("Liq", "DrawSolution")): 867.899, +<<<<<<< Updated upstream ("pressure_osm_phase", "Liq"): 2.0447e7, ("cp_mass_phase", "Liq"): 2257.78, +======= + ("pressure_osm_phase", "Liq"): 1.5697e7, + ("cp_mass_phase", "Liq"): 2257.78, + # ("heat_separation_phase", "Liq"): 1e-5, +>>>>>>> Stashed changes } @pytest.mark.unit @@ -138,6 +149,12 @@ def test_parameters(m): m.fs.stream[0].temperature.fix(25 + 273.15) m.fs.stream[0].pressure.fix(101325) +<<<<<<< Updated upstream +======= + # Active liquid separation + m.fs.stream[0].liquid_separation = 1 + +>>>>>>> Stashed changes m.fs.properties.set_default_scaling("flow_mass_phase_comp", 1, index=("Liq", "H2O")) m.fs.properties.set_default_scaling( "flow_mass_phase_comp", 1, index=("Liq", "DrawSolution") @@ -164,7 +181,11 @@ def test_parameters(m): 6.0171e4, rel=1e-3 ) assert value(m.fs.stream[0].pressure_osm_phase["Liq"]) == pytest.approx( +<<<<<<< Updated upstream 9.64168e6, rel=1e-3 +======= + 9.9468e6, rel=1e-3 +>>>>>>> Stashed changes ) assert value(m.fs.stream[0].flow_vol_phase["Liq"]) == pytest.approx( 1.8459e-3, rel=1e-3 @@ -181,6 +202,15 @@ def test_parameters(m): assert value( m.fs.stream[0].mass_frac_phase_comp["Liq", "DrawSolution"] ) == pytest.approx(0.7, rel=1e-3) +<<<<<<< Updated upstream +======= + assert value( + m.fs.stream[0].enth_mass_phase["Liq"] + ) == pytest.approx(60171.0, rel=1e-3) + assert value( + m.fs.stream[0].heat_separation_phase["Liq"] + ) == pytest.approx(6750, rel=1e-3) +>>>>>>> Stashed changes @pytest.mark.component diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py index 5784d487..2b4d1f13 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py @@ -124,7 +124,18 @@ def build(self): initialize=0.3, bounds=(0, 1), units=pyunits.dimensionless, +<<<<<<< Updated upstream doc="Recovery ratio", +======= + doc="Recovery ratio of FO", + ) + + self.nanofiltration_recovery_ratio = Var( + initialize=0.8, + bounds=(0, 1), + units=pyunits.dimensionless, + doc="Recovery ratio of nanofiltration", +>>>>>>> Stashed changes ) self.regeneration_temp = Var( @@ -281,9 +292,16 @@ def eq_brine_salinity(b): @self.Constraint(doc="Product water flow rate") def eq_product_water_mass_flow(b): return ( +<<<<<<< Updated upstream b.product_props[0].flow_mass_phase_comp["Liq", "H2O"] == b.feed_props[0].flow_mass_phase_comp["Liq", "H2O"] - b.brine_props[0].flow_mass_phase_comp["Liq", "H2O"] +======= + b.product_props[0].flow_vol_phase["Liq"] + == b.feed_props[0].flow_vol_phase["Liq"] + * b.recovery_ratio + / b.nanofiltration_recovery_ratio +>>>>>>> Stashed changes ) @self.Constraint(doc="Draw solution mass remains same in the system") @@ -330,7 +348,11 @@ def eq_heat_mixing(b): def eq_temp_dif_membrane1(b): return b.delta_temp_membrane == pyunits.convert( b.heat_transfer_to_weak_draw +<<<<<<< Updated upstream * b.product_props[0].flow_vol_phase["Liq"] +======= + * pyunits.m**3 / pyunits.s +>>>>>>> Stashed changes / b.weak_draw_props[0].dens_mass_phase["Liq"] / b.weak_draw_props[0].flow_vol_phase["Liq"] / b.weak_draw_props[0].cp_mass_phase["Liq"], @@ -343,7 +365,11 @@ def eq_temp_dif_membrane1(b): def eq_temp_dif_membrane2(b): return b.delta_temp_membrane == pyunits.convert( b.heat_transfer_to_brine +<<<<<<< Updated upstream * b.product_props[0].flow_vol_phase["Liq"] +======= + * pyunits.m**3 / pyunits.s +>>>>>>> Stashed changes / b.brine_props[0].dens_mass_phase["Liq"] / b.brine_props[0].flow_vol_phase["Liq"] / b.brine_props[0].cp_mass_phase["Liq"], @@ -456,6 +482,10 @@ def initialize_build( else: state_args[k] = state_dict[k].value +<<<<<<< Updated upstream +======= + # Initial guess of brine properties +>>>>>>> Stashed changes state_args_brine = deepcopy(state_args) for p, j in blk.brine_props.phase_component_set: if p == "Liq" and j == "H2O": @@ -488,6 +518,7 @@ def initialize_build( ].value else: state_args_draw_solution[k] = state_dict_draw_solution[k].value +<<<<<<< Updated upstream blk.strong_draw_props.initialize( outlvl=outlvl, @@ -495,18 +526,70 @@ def initialize_build( solver=solver, state_args=state_args_draw_solution, ) +======= + + # Initial guess of weak draw properties + state_args_weak_draw = deepcopy(state_args_draw_solution) + for p, j in blk.weak_draw_props.phase_component_set: + if p == "Liq" and j == "H2O": + state_args_weak_draw["flow_mass_phase_comp"][(p, j)] = ( + state_args["flow_mass_phase_comp"][(p, j)] + * blk.recovery_ratio * 1.5 + * pyunits.kg + / pyunits.s + ) + elif p == "Liq" and j == "DrawSolution": + state_args_weak_draw["flow_mass_phase_comp"][(p, j)] = ( + state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] + ) +>>>>>>> Stashed changes blk.weak_draw_props.initialize( outlvl=outlvl, optarg=optarg, solver=solver, +<<<<<<< Updated upstream state_args=state_args_draw_solution, ) +======= + state_args=state_args_weak_draw, + ) + + # Initial guess of strong draw properties + state_args_strong_draw = deepcopy(state_args_draw_solution) + for p, j in blk.strong_draw_props.phase_component_set: + if p == "Liq" and j == "DrawSolution": + state_args_strong_draw["flow_mass_phase_comp"][(p, j)] = ( + state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] + ) + elif p == "Liq" and j == "H2O": + state_args_strong_draw["flow_mass_phase_comp"][(p, j)] = ( + state_args_strong_draw["flow_mass_phase_comp"][(p, "DrawSolution")] + * 0.25 # typical draw : water in strong draw solution + ) + + blk.strong_draw_props.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + state_args=state_args_strong_draw, + ) + + # Initial guess of regenerated draw solution properties + state_args_reg_draw = deepcopy(state_args_draw_solution) + for p, j in blk.reg_draw_props.phase_component_set: + if p == "Liq": + state_args_reg_draw["flow_mass_phase_comp"][(p, j)] = ( + state_args_strong_draw["flow_mass_phase_comp"][(p, j)] + ) + +>>>>>>> Stashed changes blk.reg_draw_props.initialize( outlvl=outlvl, optarg=optarg, solver=solver, +<<<<<<< Updated upstream state_args=state_args_draw_solution, ) @@ -514,12 +597,38 @@ def initialize_build( for p, j in blk.product_props.phase_component_set: if j == "DrawSolution": state_args_prod["flow_mass_phase_comp"][(p, j)] = 0 +======= + state_args=state_args_reg_draw, + ) + + # Initial guess of product properties + state_args_product = deepcopy(state_args_draw_solution) + for p, j in blk.product_props.phase_component_set: + if p == "Liq" and j == "H2O": + state_args_product["flow_mass_phase_comp"][(p, j)] = ( + state_args["flow_mass_phase_comp"][(p, j)] + * (blk.recovery_ratio) + * pyunits.kg + / pyunits.s + ) + elif p == "Liq" and j == "DrawSolution": + state_args_product["flow_mass_phase_comp"][(p, j)] = ( + state_args_product["flow_mass_phase_comp"][(p, "H2O")] + * 0.01 # Typical mass fraction of draw in product + * pyunits.kg + / pyunits.s + ) +>>>>>>> Stashed changes blk.product_props.initialize( outlvl=outlvl, optarg=optarg, solver=solver, +<<<<<<< Updated upstream state_args=state_args_draw_solution, +======= + state_args=state_args_product, +>>>>>>> Stashed changes ) # Check degree of freedom @@ -553,6 +662,13 @@ def unfix_and_fix_freedom(self, mass_frac_strong_draw, mass_frac_product): # Touch properties that need to be calculated self.reg_draw_props[0].flow_vol_phase["Liq"] self.reg_draw_props[0].mass_frac_phase_comp["Liq", "DrawSolution"] +<<<<<<< Updated upstream +======= + self.reg_draw_props[0].cp_mass_phase["Liq"] + self.product_props[0].flow_vol_phase["Liq"] + self.product_props[0].mass_frac_phase_comp["Liq", "DrawSolution"] + self.product_props[0].cp_mass_phase["Liq"] +>>>>>>> Stashed changes def calculate_scaling_factors(self): super().calculate_scaling_factors() diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py index 2639d37e..ff5d8640 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py @@ -72,6 +72,10 @@ def FO_frame(self): # System specifications recovery_ratio = 0.3 # Assumed FO recovery ratio +<<<<<<< Updated upstream +======= + nanofiltration_recovery_ratio = 0.8 # Nanofiltration recovery ratio +>>>>>>> Stashed changes dp_brine = 0 # Required pressure over brine osmotic pressure (Pa) heat_mixing = 105 # Heat of mixing in the membrane (MJ/m3 product) reneration_temp = 90 # Separation temperature of the draw solution (C) @@ -84,6 +88,10 @@ def FO_frame(self): product_draw_mass = 0.01 # Mass fraction of draw in the product water fo.recovery_ratio.fix(recovery_ratio) +<<<<<<< Updated upstream +======= + fo.nanofiltration_recovery_ratio.fix(nanofiltration_recovery_ratio) +>>>>>>> Stashed changes fo.dp_brine.fix(dp_brine) fo.heat_mixing.fix(heat_mixing) fo.regeneration_temp.fix(reneration_temp + 273.15) @@ -155,7 +163,11 @@ def test_build(self, FO_frame): assert len(port.vars) == 3 # test statistics +<<<<<<< Updated upstream assert number_variables(m) == 163 +======= + assert number_variables(m) == 164 +>>>>>>> Stashed changes assert number_total_constraints(m) == 50 assert number_unused_variables(m) == 70 # vars from property package parameters @@ -192,7 +204,11 @@ def test_initialize(self, FO_frame): def test_solve(self, FO_frame): m = FO_frame +<<<<<<< Updated upstream # Unfix the state variables and fix mass fractrion of two state blocks +======= + # Unfix the state variables and fix mass fractrion of draw solution state blocks +>>>>>>> Stashed changes strong_draw_mass = 0.8 # Strong draw solution mass fraction product_draw_mass = 0.01 # Mass fraction of draw in the product water m.fs.fo.unfix_and_fix_freedom(strong_draw_mass, product_draw_mass) @@ -213,6 +229,7 @@ def test_solution(self, FO_frame): assert value( strong_draw.flow_mass_phase_comp["Liq", "DrawSolution"] +<<<<<<< Updated upstream ) == pytest.approx(1516.85, rel=1e-3) assert value(strong_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( 379.21, rel=1e-3 @@ -230,6 +247,26 @@ def test_solution(self, FO_frame): assert value( weak_draw.mass_frac_phase_comp["Liq", "DrawSolution"] ) == pytest.approx(0.5062, rel=1e-3) +======= + ) == pytest.approx(1585.94, rel=1e-3) + assert value(strong_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( + 396.48, rel=1e-3 + ) + assert value(strong_draw.flow_vol_phase["Liq"]) == pytest.approx( + 1.827, rel=1e-3 + ) + assert value( + weak_draw.flow_mass_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(1585.94, rel=1e-3) + assert value(weak_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( + 1497.13, rel=1e-3 + ) + assert value(weak_draw.flow_vol_phase["Liq"]) == pytest.approx(2.883, rel=1e-3) + assert value(weak_draw.temperature) == pytest.approx(293.26, rel=1e-3) + assert value( + weak_draw.mass_frac_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(0.5144, rel=1e-3) +>>>>>>> Stashed changes assert value(brine.mass_frac_phase_comp["Liq", "TDS"]) == pytest.approx( 0.04954, rel=1e-3 ) @@ -237,6 +274,7 @@ def test_solution(self, FO_frame): 51.321, rel=1e-3 ) assert value(brine.pressure_osm_phase["Liq"]) == pytest.approx( +<<<<<<< Updated upstream 3699254, rel=1e-3 ) assert value(product.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( @@ -258,3 +296,27 @@ def test_solution(self, FO_frame): 46.267, rel=1e-3 ) assert value(m.fs.fo.heat_transfer_to_brine) == pytest.approx(58.733, rel=1e-3) +======= + 3689527, rel=1e-3 + ) + assert value(brine.temperature) == pytest.approx(293.26, rel=1e-3) + assert value(product.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( + 1377.39, rel=1e-3 + ) + assert value( + product.flow_mass_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(13.913, rel=1e-3) + assert value(product.flow_vol_phase["Liq"]) == pytest.approx(1.389, rel=1e-3) + assert value(reg_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( + 396.48, rel=1e-3 + ) + assert value( + reg_draw.flow_mass_phase_comp["Liq", "DrawSolution"] + ) == pytest.approx(1585.94, rel=1e-3) + assert value(m.fs.fo.delta_temp_membrane) == pytest.approx(5.52, rel=1e-3) + assert value(m.fs.fo.membrane_temp) == pytest.approx(287.689, rel=1e-3) + assert value(m.fs.fo.heat_transfer_to_weak_draw) == pytest.approx( + 46.84, rel=1e-3 + ) + assert value(m.fs.fo.heat_transfer_to_brine) == pytest.approx(58.16, rel=1e-3) +>>>>>>> Stashed changes From 9f59d0057e9519b0e8ed61ec1e771ab80178f6ef Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Mon, 24 Jun 2024 17:57:48 -0400 Subject: [PATCH 09/34] FO model v1 upload --- .../unit_models/Trevi_fo_flowsheet.png | Bin 0 -> 95399 bytes docs/_static/unit_models/fo_unit_model.png | Bin 0 -> 90743 bytes .../treatment_models/forward_osmosis.rst | 107 ++ .../forward_osmosis_trevi_flowsheet.rst | 52 + .../unit_models/treatment_models/index.rst | 4 +- .../example_flowsheets/fo_trevi_flowsheet.py | 952 +++++++++--------- .../test/test_fo_trevi_flowsheet.py | 0 .../reflo/costing/units/forward_osmosis_zo.py | 151 +++ .../fo_draw_solution_properties.py | 158 +-- .../tests/test_FO_draw_solution_properties.py | 33 +- .../zero_order/forward_osmosis_zo.py | 111 +- .../zero_order/tests/test_forward_osmosis.py | 126 +-- 12 files changed, 901 insertions(+), 793 deletions(-) create mode 100644 docs/_static/unit_models/Trevi_fo_flowsheet.png create mode 100644 docs/_static/unit_models/fo_unit_model.png create mode 100644 docs/technical_reference/unit_models/treatment_models/forward_osmosis.rst create mode 100644 docs/technical_reference/unit_models/treatment_models/forward_osmosis_trevi_flowsheet.rst delete mode 100644 src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py create mode 100644 src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py diff --git a/docs/_static/unit_models/Trevi_fo_flowsheet.png b/docs/_static/unit_models/Trevi_fo_flowsheet.png new file mode 100644 index 0000000000000000000000000000000000000000..8aae891e3258df81fa3b5f889ca5c90c87ad0157 GIT binary patch literal 95399 zcmeFZWmFu&);0D&-6ncH-2n7(!a?i<$@I?`$|9jro_i>9(EpWh`_^WnoAdGb%ZR zh4yN-d=UtXp=Gmq&`O5(=}(OpEy2>)7kJEZWuY zhQdn1!*>^6t#`b46L-GO=WV&y0vqG_FciYj;Sd;PP+|e_=#c;S!;rZuF{A1!B}Dpgl?%AqQIcZ`;I`9LQ#Kg8ZIHqXJY*A`o+)= zrsc+xYJ%sW=9~J+YmyG|V=Gb21HH?U`+jatr@jA`iuS)9#SSd8KwEc;KiTj^_Rs~E z-w8?k1jk7;c>ip2EA}=dMS*UB_fXWaTKDdB-_W5lbe}*!9x>ecc7C%l5_#eqe8&Cp z9S;F~v!evcKK?f?2&4>yKroe@YAZ+~Ie`wgY$iH^7gzSYDeX5WU5>m-FlM}So?1^& zN_@zZ``p-cMITaWAR}9 zsBoEB&qiNWAV@i4S&ShBMJ16b6GDWe02Dgkij?J^u}Wb+}9a$ zslb#I@+Y)5@~2NM&v*npt_?DoJP#UWbB}r<%2u2iS9oz6s8mKY99M&gJFLd)n8&lY zu#;gl3Q(vGZ<59yHu_8>sup<_bpFi%@Z7@Fjp(PAkBDtbQP**&1_semmEP_kcJ!ai z>JQ=ws=$Z#IP^u0Rp5F1MhJzW&lP>@7I$y+t7==-F;7xQ{Z17b6*b@Aa4b(!`^J!_ zBb{G~S)%zZF=1A70#Hu0^)o^6XWmbNq=c+X%)!cTrSSwc^$w+|idr74EIT@i?yE;E zIFZ4}Y%FfyU$L*cv!RE~?m{8uEqs@uI&bR6&f26?gAxRAPF2GvvaPV=(#5}4+^U#P zPux1zv(z;{GwiG5!26WbX_APdM=U8ewtall2WsC~s(9@ipw}XrI|K{6YpYl<%oNO2 zRBF$NX3NU)qCjnl8Hn_&{4oLq4|WB*HvCHYH!3F97>epU@epllyBjvfcO)3z&oB!r z<{i@s$15rN&gP=MD%hO7AFcgFz)5G5)058!6A8W0lY1X=5kmAcIjDRgs)i*bC?hf~vYq8<;#X5Ggq5BS7E$LcOQ`BB)P$uyy(lYRDt5Wy$)=~Epu8yli-zVgNBD_3ZgM-PIugeZwQAbW9M6c94H~GZTwV@S$7{58`5atK zsI#NCrz=03juFKS0F-xc4mCgo(+tZGl!-hRb<3V$X5xeQ7hF;L)u;QfdZX(2y~o)K z8r4(XxPr^t7aEiVgFzj%2j<;aag22Ey?l1fBM0Yqt64GhntRzw=S9gnvkh-hUw*4w z8ZU02#QeOUN-WW)>%kUBksFnn&d~>nhKQsXf)~Qr&}Qsopfep}Bqj-l_SCcWE6T=u z8x(bJW03Z+(zeT8QHd^SsJ1@m9mt6o;w0O*GQ=omUErm-13bXSS<<~53LF=tIo@~eV) zbPmo}vJnGsU1@7xYhe@Xa50lJ8wt0HjJ6_YsuY24B!Xi$K;JWgJ0k!$2D^HEG_9fX z({K9I@i0Mk%DAUBtBpE|o5M#*3_9yF?XK&m z&&N0}*g?n?YcLVg2P~FKv&!pEZcOpnR_ndKdCETQ16K3j;dTbQQEckt|H}JKG zpX5!T&O?V7nL7uE{daeXUEIuC4r(PUcu)ipOwKeJ%w%tKM0G4e4zb#`iSW{&39WfN#TRW z++B_`CVpAPZ{J~SL>ko5`_D6gTvnk^*`AZ~hjB4KEv zcg4m_Who0CD*`20d#AV>U8Pn^K|`GX`4sxy8c42({;EZ^?fVj}&!gP|W_f4D6o7dbs( zqVENHzo7Qd#p{8fGYNZcX=w&Fgba46oSZJ}|1gzo!$2t~3hWFHOfKp5gDS98RB9$B zrXJA+r%Uo<->8a9q~na+6BRU*34l3my6&FSQqxmDAI}^42^^+ujYEHd8>*S4>5mqv zEQ?QOV_li?^?k*q$2gzdR@ao=5`vhxPU+K7zTV!c<`+t$B~B zNB zxi8p`8O?i*I5^$81L&XX(a!=w;WblQsV2Mm7k^D zq_gN{*Z)$-UzCN4^%ten&<15V{l=jCG0q;>rLP+IuPu7NnaB@XitO|A6hd?Of5QFV z7Rg8zVaZj=`yT6uSw zHprKc$7LQC)#wg8ly^~MA9Rq$p+Y*Sh4Kt6Qs!bkb{f;@0M&kH0k}TMI2yo@_`7aq z_VR~wJBl04VDY{AOq$!Sr7W#>O7}aqB9Hw+oD|Kj$E`&X{uYmsas?v0e{M$aFNuJx zKcsdRNss@~#!GNek<`B(>mm@qT;HN!h|jO*($Uulb`GpFxDY6o>z=bFa-NjtUTL@b z6I|J+D4ud3z%CPX2wC|@$139u25DJ|PJ1l}JdX5;h`}=_ogbHL_n)((n?7hdKP9yG z+s6e5$!VUJf=QR}9CYg*;+1CGC`#;8+7Kf(J77`xsaMntI#OKLEi|S1iR?l6Eg@Zu zvA6$B+4TMr4*>j#t+|%&BL6#Gf8#;+Egd9=nLo4oOAVi2g_$d0bDi5S?{mRZjmWcr zo%*}6Z#TDx)=zS(e)ce(Ql8hijLg#YTW5l*n^nN~4qz7M*DiEXp9xwi0{rjT6RWTS{^!IixB8qjQjJdZWIKbIbV>@2slGSEh&=t1uu0-THjsU;P@r`Ug+~wU zLciF-2Nb2*SbXfmsX3{+H*-YZKE8;D68<1Kf8T_md2vdIB2wpuMV;s}ONcaHk=k4E z+gRR}n7gZBVaE2vrSAbMTmj6f-qGK1dhp%{x&n<&UTlE$97P3rP47KcH*>8#)RCHO zKEM+v++rdQj#t6jj&xd-yn>AUtWi2Bd$~YKxXT%8kVjcfqwCk+<)0;xYsUfJ!CmB- z$q-KSnjH8Mieg=4?#?7BP;b~g1^P9g?l+=?wZttWW2P1dCcV0?>-L~gd1f8ag|GNc zmWgak*fn8fD)*}4pz-%-9HKf{Ft&C+8|3J?-@u0v$E&^ix@xwLqS6 zTp2XSd+p(AKxMn;?4Qqe<3#LMmc!3ld+GxLw(UanH)87Q1S!Q0N-nd7b+{eHbzi;p zMP&9z`i8yK2A+q;KZ=RSU}ivOT=H2pHlby@1% zP5h~npG3I{`LTIRO>KZ%Zinyn5FSl^)ry7LsMn*W)KVwz<7sB3rOpc7c-d$8Qbf<2 zZ9e*S7Vq@%TvkpO2Pur<1@`17F+Tm%vZ&B_g@FW^8AMSKcD&CnAo#Wv>ZoeLtvWsM z+L&+t{M<6y-?nS9%cQTsk1$J*^X`0Qtteq_ask;}=<*x!pp7^Dg;&D= zvz!D{rVDq#6;ifQ&bj^lT^J79FS0fDGQMC@!zX2jatQW8>$XF>FwxKHl3>Zq<)XaT zf?hCprxAseKH&u=H+RRy7G_EzOvV`4kNBx7C^&_3GTU$0*^?|*3!G;4Y$2g#m~l2^ z(W@e3S62O?DC&D7H|3E~QZ^)RmOMaY7_orB;wavRC_cBas38>Kp+4WdVa!{aNY#xK zE^LX{a*cEx`U!Ncq^z|`t*+@1W_Vy?L%d?y_@tXo1N~iBHk>5sC)$50`N%(U`eT`ibkVt*ea?KrsO0V znDkCqbhGRPE0J=<;H$Rs{gVCwd5Svz$<)7r@GIj$L>Lbj<`V`m<`iOy$K^Awa)60^ zYuhDnc;Ezz5db$zaAtMsLWkCXgD5CrEVthZKW4AKmWz8H&b8%9GE~BEmq_BDVKq+! z!grYEc;DBun|H0yMEeQVKymQE{l2J0ASoqfSV8c?bfL}Vz@usL#7s*|YiWe?t0z%998+%{$PWNZQ%o1ywwm?X@$XVwVDZA2id zYm|6@rijlqEX&CZlw6&}EE`Kd+eFBalIa9>Gs~9%RwFx(PeQc5&B3gVO~>*;@-8Bz zu-v}IZZ_1Fg=xbh>{!_O#M&|05G$OHt~!3Qp{D2i%a?lxb1QjGptw9d7bn+IrqEQ{ zC!-+U3eUxN61cW8cAUq4xWC$$X3w1eb$jNR7c|5i_K4d zX}1sn9^iFaf5A|siSO=MdAUbqPMyxX69Yw)a*~NH2HC!3V&*H}w>)nA{uFHE$3GL$+LS$1la@vL>x^&qbEZq*`U ztr_U5rCYkh(*Z7MN+>u-j%;3G2SPvR|RfDSmy3l&K8Olb--pfq&hP z{8FG_U8_79J1xsC&)V48wd?S05IGF$3zGlTY}dK!-bYcxyY%c%>(_rc71)1u?DcS< zp$nKp!1_su_)lxO*d{!UFw`=J&sEvc`-QrmpgwG$b{X7kK38Kj{tJkc^{&r^oHEv0 z8!U3!e95DV$m8o8)!IX69p{1|p9g1<)hvzHJ)4il=JhAR8K}b_$XRwfG*9KD!J2#h z*eMU{ui?y@?&r}Qw$36vA6?jMXtti(=TKN$@ZfoM;0}OY#m%W!%cUdYpZY*No_h;y z^|6KD3VmNIZ<-5)XnDQ!N_HWGu}E2Yz1GIe(Fd&!RoNDAEN>6o{*A>W=9VbVxjEaAZFLxb-A9F} z0nnuNn$W8x_rv8-OmWUk!C96@$`WhzA)l>JbwvG#%Blq_yPZA3MFHkR+q@AS7&bB4 zVyE_z6GHrkD#rQ+l`tOjN?ol0x5*1y8hOurz)wOe8gA|d4bf-apqCfaPXRV~c`;vT z*A(L$^$`Aq{4=FaB={G6`%J+!2fsroUrMs-Am+gX&U9VojlHw(nY=?2kP2XJkZo@cZydI?J3E^=gJ)R2HR%i9|iAv7fm>~^zsGl8}M5&EFN#$Ip z$CPCt%zjy5+A#a#hC;MUiPFT#7@Ufd^)mzZ^U!z(+d!z=)Gd?LEjkG~E2!mahTDRF z_;H9tEY&|SmCPqLXXK-s7*{L%9IeT(3vBuDo-KZli)+9$C}Z=&E_YWp>!7uMza?36 zRk7lJ&!oxI-?KX*OT0689WOZX@vHzfp+Vi z>65ua8@yT3+0&QlTVh1>U=;8Ga34M}I`?;+8mjXnUt`fRE{d@+)oQ2iusnVSjz1%n zT1CF%yG`ygv*jD^KKxGS!rPMnR_i;CD1K;K*b4>Ky~T)L7Drtcfm(Fk`#fUmGjoy! z>)c@Vei_J(PSDAzs1_$N4}YJ9#8VY8MCS{{>pLrgvtt_@S_U@rHw~ zf@13@E})SJN%GEWVxKfG*Xb7)__~O+zRYZ6CyBeU^H@3DEn?~}2nzN5O|Ery9htVq zj8V|3LqM?8N#&NFWPbwVXGar5jCi->cDLOV!P19-K$(@~IO^DXC6^uk&h%zD>`|9| zrm1W<9^Hvjr%uA*vW9~-wdkn)a8Al z9WspEbyp+#HT^C1Ja^ty=Wa zt~}MPJL`isv2ix~b(TJ$j}?&>6`b{q|LEj5&o5*wI?6yg;IWN7s!k8~nBqX$UubIC z^gmHf>KHq{TBl0&6;VnY)y8*Xh114Pv>VXjE*A9x4MgjrV$@*vFHeTZRtN>;){TIk ztdHfe1bgt`D&=-8106psjR21wBvl<~6k7Ykz-#>&-&jL5}xQ!1oken}sT) z(wvp{D+iK~?#o8Ay|EuDJe0Ie$e};;{T_5OCqQ@PU`|TIqHReGwdjHLH!HNAwOsgm z*VaDniu&y3Yo}$rgwwX1%VR~0BLBuK)=2Oj7(44aUHsD_qrY#n8CXF#luSdLo%8)t z6U-*$=INs^KPypKHB@hiPMbbL=C);*&ZMF1k-1yTG}}rHYv`I$qZoMT0yUP46%p~O zpoR5L<)%r{ahr>AxAW(?zK@XDEEhwI)k2;w_&m?lrgEABvZ{u#=xx>_tM_ zHr{jyB_-WY$v`T7&h604A+@oej$yOoz-{y0{uv#%>!-&9p8_;uxP47gGrE2f&=&$9 zSGOzdy95mlVTPLmFcF7Kc1@WqBMyK)^!hSVGG-}e%%P%hUbF&yD`g#B6P9Hg&0=a1 zd+8F$DPAOPJ|0hv?OTJ_m_&)$|Svu&NGv&pDP{$Bkq+Q$uU3NA9!F={m{5XTXjeuncD8 z+<~{+Zkc=7`?zNo_22pZ{!(jjsls^JEaEi33MJ73TQ0Y>UVgxz_q_Rau6+jgK!Wvb zmM%h|Fz&nG>Aw$mh6qJzTDad?s`7;Y$G-eW<9hF?>dx6WHySC%6#iaXLW7zKOT|uE zGE3DPUZ~a?QM>UbSp6+IzaezQdyqB2Wxsle{T0fs=);>WZ4gLo^9eJYe%tBC$PD9> z_(lt0?B6+4{w^1O&|Zbopy$=Ev+|Omqf7eQygv7iBN@N#{zTAFqxNhN{x^a4uf!ov zpj;CT&5L^sA-6qC!)(0kVKn=cqZr#mXs_?Zyelvp%NqH;OWv#bpGo-NC%xoU7i`Cv z-{8=2Lyd^E7~ZSyFdN5>&&mRjJxhYGxMk`1siMLtb(} z-wq7o)i8DTKEn8_UsnF#HT~C%2Nve^x_h0zC@Y5WNkOlmU!eW?9LCsLI%!L9JJ@A{ zP@;xt&k|Ra)@5)OGA7Q2I+;ojwz62ZS24csaSc<K9=={dBO?85Aj`6_(+6cak$y6-jj zQ;a)_fOLtdtYJCti-{lOVtci+LsNp#$e9e-!d+ z%e9};^e{{{l&Fw_q$QEtR}~5vEq02;Kcy9HNs>0R!1O#V9=+)Q!TME(GFCW~Gb99?fOj0))-^JpJGRMqHdVza{WQyul32jlR`Eh^B)kqy zTfffY5Kv%6Ef-#9rG0=@lRenxSx~;@8*J89Lgy3YAiU0~@c5w(MSIK!WE49qKP`l6 z+SUMwOXlgH5m5JIxB4B(Ot_~PG)>RF>iD5!FGUAc$EvprY5m24U!*i+r<1*9@ATrf0&To7{JddcT(%S48&l?dbk#n3 z9CV~eKD?_eoVeX&ex zc4lA`mk)lWm}zd78m^64l7AzmP*MJ}z8qQQDU{g|a>E5S`~DI3QnV+4$hh;l zPMg+uiYpR7q;`l+e{s1o?M2ye`we_p;b}Kn@PWWrLYLsSHPl7^sobIWa8{J@fF;#) zB{X<;dVE)ds`@CA*9J>bka6-#n>ILQx;ng+KuBL6sN>sEyd>Qk&&35NaI*Y(XR4|# z@;%*};z{=gn1V&!LPxvi9C4O5JpZY;BDPAqHUHGfVq@d2$`(L)I_5gGNdfBed0dvI9ADRV`XtD-4igG**;tuOlo2t zx^A&hz+dCNP!T6n#P^X@b5cK|aF$U68W?a^U6&l}!^h<)aZ0hyCw}T?v(5BxBj@%V zJxh_Pt+pMD431gHZHZmPzVysvsH?F#Xj%}IaVo5<;tB(^e)jh`a6SmgqB`k9Iir6>A(EyrSgot&R9ying!P^^wS&}kc}l}us>=q%ZtlTNc#@c$|mEME&c@^SNdE9eoB{2x$|JL zaRCTMl7x#F0$S9_9r2$dX;wodJwCt^7l$PkpoVXck(fWymui+7KP5H&S+oxszavCv z4}uhZvH;_0>bUsPux-qw3F<)}fyLkS>UA4i%tK@gCVYvoG)Qa8xE2TT9z+|!G4iCI z?%AK2IITXZCY@PIeyb_VWFbue$(Hp$hjt_nLg zNaql3(Og~aAT9JlfgpufbDya5qpiND`Z~g;3yZ(MI1Z<1 z8gbP`Y%GPy}(DBda<0l?Rj+s4a#GSiKux7jS2%|Ej5khpS(%Urj<*N zv2_A(ur0Y;_dRCW*njmF2Q=yrxyO7fhItWaR2HZpldb?{w{c9eRP0!_Q!$>jM~Y78 zv%-!R&|{Rid`draoX~^Q&Ffm+NEBwi!7?J_w}9L5Qw%V+xXyObb#-M9TD(nhf78&C z-?1Y6xfG9`oc}?qP)N3IrH}kX6N}sS>dQ!uWyfiZu9+-OQf0-X_C^5fIXhSPyimAY zRBKC`xx@a)THzhSC3*lEKYVGLGu-FZW{uBD?)#& zvoI8{_u#OnuZZUS?mW$;Xy2y4q-r`=+qFc$I9gLSzcW06zEr(h#Z44Zt#g8{H4N*) zO>s=(o#jqDNYU)L9%?Vf1m{=kEb77SO4qG9?rp0f8DlbD$(lXOpp^dih_ZqBA6_L( zNc{GS@#td>*aizwK(1yQZ@)8|mVzY*`ifU=8!ETUHHlv3;Ck&2QkCO~bcWQnrtOKLm#MuKMvI*xG6aK1WK8}!hgXkgD-q7SRtd_BN= z!beMzB54wDL_(8|E5kbY?kKn|;U>Q<#77!N!HFEJSU1tt+js8Rxbb*qDT}lLDN*F& zF_--=FQxBtA_fe2x^tzdY`6W)I2|E*eDXwB;<54b zQ_9z;=W|Qu7d@Ie*ATPh1D?HcI|;92=c*sT!%Gh&Cx!JdPa1zMS@%yb{>g&=&SR2k z7=BTK$-fH~>o7vg4IrehflFmdX^-hOUgj?Hz9@{osngmjzKNQWNo~z&i0G7khkn{EJlNY4LM7<+9o|m&@p|;@k{qiZ+6UE9vg7?x)89%SrO@2L< ze1LqAsh{JC9l1<{-AECuLVc8J(G`Dn?T8e=mAW-ZfJl+saDw=7L>!oML1g|&5_Z4v z;q<;^|JR1HBEdY2gkH7`-T`TN>+*s;K~ZuD@KE2_DXD^GAX;az%|ay=Ge>b&bgsFTy2ir`%9VNsc_ zny(T+m4y&esu~&&E@6E-R_dS}}C3Xr>&WHAGJu!2B z*y5$+v$D~AYQBDX&{O1?bxnxNw%Ej!kg3zTmE2ZayB;HHcenG^bVXSggum>!3(N1& zI#YI$I^LXb(Q}hiPNRhW2H!XtuY${4;f$pCflDdV8u#1uqHG6zM{XMw8!Vr&UYeQt zT~Ov*(YKYcr@Rdkj<(Tov(Wm+MK8W#5*lM%$5a~SZzWw$^iN|6)HKPaR|k-*IC}dk zt*LO>U2L}A1lADxLeIkaCj)7HVeOo*MhRS=X+j6s$YG#7Lx>x#(?@s_qu_ujh8lus zc-3$3@j#H!+CT%yKhwSR6 z)--jEuJr?1BD|i4v1`S^cf~o73!!$(wP4b!p_Yv#n~!A@|0y#gv)OTqD4%n?B;U%5 zWxPQHSmN7am(G&=Re#fn2XKN!ZdYR@evVO6T^V^r8z;0SqWgjL@7@9zC#QPr_7}o6 z5(i@{Y#*wR5tG~ff~qcUud(%G_O?7V?ycKR2=oA$)UUd5J8$eX;D#7Y7GbtE-0eU| z9(Z^BF{Kbj|AFA$bEBI-NthA*w!nh9)GRe3d;3Q!a|!#w09kz&oezNZbyc607YP6# zPCQd$UQjNRp}lQ7Y*fgD1G@k8Uq_%^#7)4`?NIHzpR&<-q}Y{3#IC`#UDH}gz}BaRdmvKoJYQv4T)$;ZiP_o5 zmRweF-6L#CXrP9BKT?!6@fmwOvWKm2N0EpKm9xu2Ow?gpW`!Y$msofiZ+I;ND0A|H zc=w04j$)f3b;KodhMOFEs~Xv*pLD6<@wEI~hg#GA+9(?<>#vANhplWXR4+{`pfJ^q zhk#J5Sij=OdSLyq7)!LnTVcn``L~Xut~L9)GRU?y5nhwzVcEk~pQ(kJIKO+qoSCY0atOgwY!((9ejwNBVFv5rmM^6LoN&%E-cJ-Oe4 zaj=`PO!u7r`pby^#t0J=)udj~{w zeY^%vd%ilXjj#=Aud@5vPT%Hxg95N!aHy`cAyAg ziG{)r(UGdNTyCZ~aB?6m7RpL4S!d_3zcKC$?g+Wc!SaP#_?cOm&S%BBmppY70vlM} z4zk@Fnws1aV>en>DJgoU4@?RrPM*96D$4`|JWh>9$ zw}0GIzG6-VhIjZcC^)ydp3>+AFxVov zg}v-c5a-!|19RP|0m!hj9a={-RG+%Z ze#Vkfvlar$lN03l(dhc@=i&+AwS- z^o!IqrLi`aRmrcR$7aSrsm_w}Gbb7ixh;-uUbV71x6P2fz|r?TEJ$VmitEv zC-Da~>azrIY!5_oDT5AT?}1fj@rKunt#YwU%=)L1H*)76Y^rxHd2|CuV+_tFbRwnG zC+o&CsH;0~X`xq<-35#BYNTxz(I)SGzl>vku?pctPegcVHFV4I*mshR+_yyS1N&t^J~3aXbL}!K1PI_=9o?J1tF&L_1X-c3@y06$HJq@X@uMaZxRmTRtu8uFKhW1y z0x(hJuaT$qDM^nDWGnp<;?Ia6NcMmv(hHkxltQ$s`jwxNTWh~29%1XZ%uAEe3cknr zS=^lEJYYk&OSk$X4^gSb)>!$AtX*f!M>l4h(i<(^odwR0jj z`sG_K_66I%9%V+~;RibX-~z~Z3fxEvWSa3+7MyXVmUJOSfm|+KyQ2pjL|#j{4h~GC zzi*h)F(4Sm62uA{{{mmRG``XATo7iMtZdU{#VNER?J(o#+KwhZ6i&tn^*GDL!T!-R zoH-DLZ=SZ!;PVohx}>hNd^R}0wsDNx?|bHA#_in(%zoOPnJrfhbJhB`ff}Yb10_E@ zOmV7Jb_JtVi;RBm2F&@BuL|eLFZ#pV$kji(_)Io~qa)za*KM!7+TolLERpS}63%D@?(kA$KCW z>#hE}hoCJPs4ghlTGskX7dwia)%PQGgp^@Vvvzz@w!(?-N#(7#lg&qF7gvL@Wn@R- z(J}pu@C!!i*cXUUsF2UsNt+>1)adNb_&m*#NX}Yxp@^p>N|cNTlL$3w1ENeptK5-- z5E%4yl%J`oL)KiT1>o{pDI3GV4kPro82o6w&^%RP!n@4j;uNm9jS|l0S?1~cdTD!i zCUuLbbU9AD082b0ZFjiLpaFh8o8wDvR)l_S0Y3wwVOhHNtuJ(F(-1&^ikeK+9!8Jr zd7G;^*q*-n4#aPC!I0(SI|BOM#Ek2n>avnr*%#>b&+qYvD|@PFaD_4RUHPtNA_eJD zUk(uZl@td>hdz5i97P5GhE69%L+Kg#FfTRBW-%~7LhwEANk`j6?7Z{+$0L z;7Lxr{!tiati^~IVmx#vz4#=-l#dramHqI?BA9K$Ds3eUCcRajZ3N{(E4`$UjPVNz z^4Ws~FhOs^?mCqn7xvMR%pY%T*;@PFTT?eX-%Ri5^5KsdxLGPC@fb%SKg?_=qZz{P zq%Cm^j4*0dIw)S3og9c)VQIwr^6f?|V?4%8qCIKV4{^S!_EWUYbHr%283S!zk0Etq zvjtt)jos{|HvS=0U20lMJ;AG!^L7s&GtCw`>MLsc_4i$DusxD&@7pkp%oK~IdqDTQ zXT)&{bIB7aLfAgS(B9;mnGVZ##V0cr`~BK%i5+daTCB@a7y@n^t-L4}R;>u}0Rx`x zwCD|Gb*#P+7s44g`{RT?jo@{{JHn$i5yXq?{F&HJ`WLfPhw%EbE#PqIc#1$rUd$T+ zC~ENGa*Ey4ayY@4Ni`OY{7)S=OApDous2Pq*;ff}QZexOPq5l5VdJLesqVe=U|xe+ zf~AW6qf4D%!fOwWVZWjNdF1h)2m4%@a{=PrqQen7DS7oQ*@TZNbNMobP{ojDfGB3o z#K_I5x4RFJ0US4~WKqyGaCT_#4$6W^&jMGj6{++E`_)VHYH4z*OQ@3FMvJuFjd|k( zGN-8$pE zM9tSw$CH(9ABVLu5An4vpDTTiD%%yv(x0yBe1mR^e98`-blwy1fTJcyIxHxYRo{&1c;mz(Q)&QHPC7I>~JR} zV^XfC0`ZfF>HNrbh;hv}5n2*cqoiFT=*;^mhM2*k>LU2r%EnakGm!a6Y=?J^<&NCe zuD5rWomL-}>GDDVj#+#0vy)v5ejW{3B2j70pfI3&4*3b+B9iS0pCShr^%;Xa*9pvo zW2rOsW#v4^nSCUNkXEytq=*6a&3KysS}vS~;SY=rwl-t6r}p%DpS{3vLFowGrpWW( z?abTRH`T*c7jfi+(e^q$L=##Xze&@odnAWjcwUB_KO3?9d?C2*GAap~d=o(8`mg^2 z7*<4C1BMwLNdq@eT9TwfA}^W!oMspzM!BiH5))MGsJ8U@iL&Bu(0WAwpxDo69ERSg zeVUt7C`$IcfCZ+3hWsmNGg7k4sVWFP*?X$X^SKa3sYZ;gqML{Ygs&D=CBH_#=@~8D zO$i<>K0_tP#qar~LU8$L+b^PzX~z(l(nG^shk@KFRzbSDNX$-cUGF~T7sxE!Gky@H z19aENClfvGcI=QHG!l|OEp6pnDm*#vK0AQ0pYEivQ{~RXq@)}#nSD*s_*K;kPO*mN zqtiyLeHq8r4ma@bW-fH_m4L;QI*EIjDbcrs3okE5bFq3c>3;scg+FrhBiv<1x8UJN z>jAZ;0|hO+78&cYIP#nZrMgRcAW>)i_0mcK^*n(`>jPYl{2gN&$pS)82+M`xwNGNZ zXR+q_u}Z-sg&a1=&I%=I@Gbr(Bb|z-&MY*S-d);1_E$OrF?w%GQBlEnk1b3il}djgSAncpEt^RX(poY%fwx5()#LbtAYo9l$mW8aF%~wDPvn`yLrlSJ9Z-L zn*6eQzS&DG(euf{9&2*9IW60>MddrK1uEIzIkHpk=~U_(2nf7TqblMLI5~yhzrE|l z6wx3>5y<*|wGKh5fmdea#BX1!y7vQFc6TFjnS4Sw;d{cZmy4)8wrakO@-KcO+_a>2 z7%Y*Fd9t1b(6G_{eu{~V4*uIvbpx*-1PuHty0YZt{pAF60+$S1FM6w4>CRhUVBd_q zwoeNzFfwkWnHwtq;?xSyHU22*HZbu=-x7lk)~LT098<+CMaR z1P&imDxB8&5qI7$arjQLKT?MFhs(+D7W=*Q2lFajC}$O2WZY+^xL-P%5}(C3fmC&i zQG509!H;s#w<39K8%X>9Bz^L#hV(lRY>Jl4{l)zpHsmzO7pVBxjSfk(4xLdpZuQY4 zD6eQ6Jl$dN6Ng{m!!z9h!)!kccybM?VEqw0M^%#D8hsU5@c~0aP`fOSS3j6S1EUOU z^avF19wa&Y!#Rz5-BJ=2?YmwiNzX}n%uxj&>~@^sQcsXj#XQ7to%>)$K*~}gB2eCs zN2SC$SlQX4^+QXS9CP1Tvn{zRqwiH<6xdc^f$F1Xp7RP>f9ONExD|4KeEQaGlX05C zB0sql;I27TP221ku1Jv8M%?p<^L4Q3j0Ny|%robLk^*mR7UU$(fjRgQ-c4QBb9?c^ zW~2mpGV;xi$Mwgl)0V>hX;h1$x?|^fG0UFvwQT;SRzJttG--UZvBm2bkFWJ8`SxyC zxY)f4(sjuQbrNa%4b|#O&atG?Ov7tH5VB@<{mNQbca0PTi+tdwZ^j>R_uQT)H^H1mL?Q{F8eWMmF&S~zAGaRMg%^6 z(IcXR;t?8g&DvriODuxorK%U+V4J|nned<}y2s7sdU|KmGql|JOic!8TjNAvm8*~TPDCKk)kbCC_$%OE^DS6B?1 zCy8$`3u>{`Pe(NgnE*M_nKog+?|Y4+W==M<8;T*vM&VjJ{)5D~Y!L78=?w1l3dCe# zaF?6ia^ukgc2VcRtoX&H>DE6ui|^Qj)f0K7LT5|5d!PA~)ryPc=_bieb4G@nNfaAf#RqE0UBPy8DCPNz&tgo1Fs z@SWY@@o>`&=v$0^yBl71e)vj{r7+T^lUs^)NuMF>=JqZunReAGnb}72eOjy5prS{Z z;UW^(8bEwbeG{?gJ-~Svhp}z6CWjzoNK8h?I8h5|+6?Hu@RHA$v~BdlbK02kmG0_T z(u=~*jPKU5nT>^@J~C-lt9|1uRvbmNN|~12>m8lu|%Z==NGczLT2gm2tj_ACZCbjLi)ondZmRctze_ zli`&0@?9-Um0E1pB6Y133#@>^nOz#=gsXlYB|KMlIs33CBT1JI-aCH`yL3MsNb~Fg zU19Z=1Nybj9E*FRjG@8-XHP~_Bj9jDXHE01Lgd=Q1qJDQXNBRLW2w@6KgHCFh}g|; zvY`(wMu}dH5f`$!pqdg?)mM<84c=F3K?PBiStdELz*sWv- zvl|N}8d{_G6AxxUVM~r?#vRcmrM9o_iHWEqr`>tNN?5JTv7kVZC)ckYCKnlgwvz*2 zCd*vA@iM}o6R4~@KaH7AO3)Mtz#S+lC~?8@v6lCK+B>fo70+Q|rz@9WD{QUYp$Vyl4lq*7xr6gJ34C$`7Z5y(`U9MNs zQ>7@gR_Uj&THB?~v5}n?NQ(4;e$9}sJg1`%Sv(g!=u=ghaCYa3Sv>v824RF8*K0%z za}{kVjx?RP(fTM)dW_D{rb*@Y;yZXpZaV(56{OwM&3IP<$2Rk}HO${{2<_&udH})? zHgW1UO{j{>*fwfTXuy^rti zrFT2&uOm3W<9h1vP?2h{w3$7U?1yLAyHD=jbcJv0n1R?bz!}ka5#Uz$g&;!W~q_g%jOQ6OeFmBSH_{uini-dX3cRQA4{&Cko9kllg*MCV~2w*e^cQ zf_~@XduCogFNW(#GKXGO@-@tOFt~F8a4x&6d1-imRsf$EX^$!qgT z9?z(0gZ#lAfERM7fc^&6a#jxA3k}&db6Aj3q~h$!ux~7RPavo3U-Tq_kYG9KX`x)E z=_V`zQKyUDWi+W|dH3Yap9FU&7P^cA!!elGz;PAH;BB(Y1rxGo&$oF{tG-)*PCwYYSN^NP>J+MpgB+UN_A9Lc2 z-q#5+1Zh3eym!n7`pxi{y^5^gZK*eQlBl)w<|VPJ>BusE~z9X5uL?n z7m0UaDOb_xHN)!@#^@6*fr+6dz47avcWe;I$KUJbHEj!C-}f#u)u5+jw?%B~YBX)Z z6J;oHCkIW>c57MtW>XzFaN27}Hcft%eem+M+MXw zf&2{3S#Kf2mN$$g^ELNQ86Ouj=OFPXYi%LsDF%_AS2Z@?eC7B`Wz>4WGTT<1>uSN3g%X z8(sn9&o~r3R4ybjvEk!gX-#)BaEbPnH!!c>M)1VP>lxIKJ%dR7VlPki1Z{5f(9{oa zc%PsEYo6d4`%`%{KP}zTrRELHmA2r1$kll z0pFH0v}qv5(HV{KXA44)jRd*Q@^Hc}PZKjeZ8lkh+*P`7B8Nj<`%qjC^mvhr50 zUpFS4=*FrV)Z2A*f;KqaBMmr$5jif23Japdj^S`mZtL03hlzRkEJ6N*1CHU)u@x2@ zKLkBbE8)A?hH`GhLCZpJ;CF^)UUaM~#yi!qn>o*uOe!csufXFrT!$ZUA~)6kIg|~! zKM=5){pr+x^!Zs`ckC7mjq}1uCLU^WpjPf_!(u911u7yBIUSv2tvxGnKQEiF4H&uN zBJ9YdBCP5$=1NW|TuZ}E1g#BAH|6gb5_jwnu1BsXt>VE1WhTnzqpKY`QRsLS>;-o? z=Nw;_#-2ApVNsSkH!UxRsca##NYuTq)Vi=4S@B+UfNzf6t>O&4B3aw=VA3mR__mb; z*1ti%**h>lBZRPK&%Qo|=Wk#buhpV&v$bEZYQ+8-rWgDb^LW#%edv)piHb}QE4AF@ z0qH(!4e~Cl5_YtSE3D4>=X)X>t{GA5K7tI{6I|z77bKUrdaK7SUz!M%;KT1y7ej8s zI+8dLeK&8&g%-Ctd-|_<>k{x>kw=1FeOAZ#jpJt&L_U>3SlBdcq)laYh;*M!)(Q{#s>^+{V!b$fULAgG34vomGVjBOQya^#JKY}6 zj!TlJ^C6hsOMY%6iI(z`IB7GW)&=?m%MsEYhmLM)33!`7U*u%Id*)j<7TW>hM>OQP zP^@ZtHZW3rQu`^8lruWm9`K=Aiqn1|O&duvlz#WQ^R|VGJ)~5Y#Ep6RKwI$w;g~Z` z4TZ`pVQYB+e^^m%bIga0S za#xMr1&ToAXVkJuH*{6Tq6_-8vq8ha&8TDjWao*sT(hM&fJ}#%;n)g`Y6_zaW?O3* z9m%KdM#PLHveQWB<#5m$q+!}+COEz`27yI)&ezQL(1kzLVthIuCp;X^{TVP>QH@BYkf8jEnhtqC;gY+S;C^v!mFcdhXV< z)V_4a>9xX(1>e`-I-_HzR78K5e&&W)6+s7kn5#oA+rkvFCeNG3`cD+s`^gWKVD|Vo(iclNp`p_w z+uz%5$rk7*J{(0@VsD?Ysk{e$l_K_hh$`LC4PJMdFeeb3z253D+A(L4O`ra0$YUcAaBj zb3f~f^3ceDVA}pQnG0hRHwGfSy%FtujkTMGRiDwg#Wjcf%qqiCkZo;?v!kZ+!N@F$ zEKv_S;WU78C+s8K$YHjZEF>XwS7{&5C`lAyx@(Q%A=OW`zJi zHGi3xr8fw3Z#jZ4hw(AvMx)-$G+!nLcr0@OYDfIedt^>}QrE`b5_R}7W z5Wa+QQhe<6eS&8=yGO-^iOkDVUD#tt9FL_Qi6}k3(agEUo>c^f357??<6(wYGtHcJ zbFg0k|CmQ#8|G}=>taW*J_<@EWnVw{>q9I2xuBqcpa2-80AV{MpH|z-!GN{h*K|>% zmq#qTwUg_UyVGv@N?VvnLqj}#;9#<_7e}#vn$4d5$;6x4q>_ABTq;)%(ZDtg1pA-3 z0)z_Cgub*28E8k4J{Qn7d{tP=PqR;13a!yOzMrUrQ>e{dfntBqvU)IPA8O?@VAa<_(h0)meB z2l^e6pEUR28gSg0yC4EYS9F3zWgD{|qw&qPz311C4;4QkWw?aQ9 zxVE+(w9g|1HK8(`Zcd{NV5G{cJI&4uk1!Q=BQ#m>(l0s(F3XP%=x0gE^{6-3MScy& zKC&~#b8#IMDabD6XwVFHou5xv!(d+^cl>0*=yyMWcSli|7wq@?akJ)LC8RTrOZ!^A`apr z+m&7k1PA-x!sjGi%=9e1h2tMwUM(Iv=DZNTw_n|6H`q(%^h*cBTR9jkuAB-6Bg%|| zNJVK1nTCv|UIb?!?s^80Li;uJ9@o2aj!_Y3aI#Z=ARLJK@R5S2`Mc-kh`wsIq7ue znwNHh`^ja*gNrNpn|(z{$++%dOK}d_Ozy};1^EZGHF!|hi0%+6ULAL$_+j27xUA~V zWSJ=zoF~=<*hB=(`*l0~Vtp`3GjDIwqf;gqC&@^`$wDFietq;I;wFAmUf|VQ+v7Be zP$1m;p38)bX$HH?nU3W~SMY)#B41%T@Sx}ez~{;>lIJIlz|&_ft&E$k*yaPFm7Yp= z%Y`YK(gP~yo-GncNC9t{vx&lLevS0J^PrktaL_lP7k_(zX|8)Z!@wiC&2w^tlzH4AJN)cZa53&`;dK*#-=d+2NYNBBkJdgoj`1M-QF#H=beth>~hA{@DpG}I-^{8KU@krKTK%r#yIx|=l72M_w$=HvI*K(-^i*r$_dR-YQkS2-(1%~`NwDo#M-Lq*2@+Ivu?iT$5rD?fr z*?HAt;bMF6+AVEPTOUd_TqLUH<>4^!7}dS9?Q!sAtCdNIPm78j|#IE$BCRIR!}E^85twKF?ZI zX6)E;UzOdbX-F^D7!|8MFPdc>864QS1xd-47?C-DI(?XT#CnsD@DD-~iGm?iMY$O; za(;o_7{#3@n#g|o6tWNo*PEog8}bu8fh{d)!2GU)2OVn5hd`TQyZ(DbaO1L-S__b^ z*-;Kf2LYg zGrur{>WGR@)Hx6@)WWXXUXO~wq0AJLEZM*@ zy_zi*f_mLzLka*OSA-y21ZwnW7-c>(=p1|J*r{3Vr_YK62L89pCzcEhYQ7)k(_<95 z&0Zo$*E(aNZJTU3Tk#DMgE@`{npFNgPs)Cy5dSE5LNj%)@dwtP*MWqq#BKj{fO8pB zP9Pli$LyHU634?C()-qY$z&7-1m+b&fnD3J_6%#Qy~(rv+16zZ26jHaVZCrHszF+* zH0@#2y2!RluN*SAcL;A3gWKKO@EU^?f3|K`%zvLki2sGVf4j^w!D6mvUs$}~*2o9| zDxSihp}aixzwS7tca9Tio1~z0-}j!qlAMi-AO>BCe!20De9`5JUhl&6qL5vokx|RTw#55E#YVU zw?qFV)OQr`H{ZT@ec>;G!F}2;)kb)F>WMFdsA-)~%S4}vEluy=Zz6Qz=}skroO`F5 zPubACGn`r=8hn4~xjhgMv~_S`6?@i+1{oydjCp8jIFB4J>bQn(w@0C@#bt0gC=q{Q z? zcpP@gQI4JEbCO7&kAZ%YPDOErB|6B((`HFkE1c(ie2gVI&kwD1PQb)<+<(yc7rhA? zA=e0D;Y6<>APB#3K{Waqk7n@37i)H_;Q^ecP|pVKx~U zTBnuDTg#G1 z3%F0F*62`_5w|)Ne0+I%X}UX--Ck_9+_*n(F7ol|A}osR+lIm5;2=%8c>AF9@q*!1 zuqHJ;3a9U1koJ2Z0FNy2bN2&8xGrct;?PF`;FXEI67SFATvh>!80Djq>XGd3P|QQ# z0Zyl;^b~eWwS8oR`@CpY?TG0Xw9D#IJ{F6|{{MIGJHO^`U&4d}lJ;|vuunMl_V%ep zhK7ovenXg5w4g?_MOM2>bH7^Jbavf_q5fe!fINwA(k#K-9qVp+-3IRd@wBZ7)c1l707Qx)ALMGg z+kmZt{E|i#TW)7;*{<3Rsf+T?LHBBe)AyhM^8;G>7A; zr!aKCCH`ZGx5zxm_MRfjwFs>IEe34TYc1$d_m{EE6+KX8es4B#in;H`m<*AiAs}u; z;Wv9D5h3KwX8r$=E1gDLUy|R;$0Ph^^9Ag;IRpk7Aqp1V(@TO@@n{qE7LJS?0vxFj z!Y5v1_8z3m&w)T?^ppVQY0yFYiKf$5n%D;)^!r|a$UlGm8;rgO34Hcs(ydj|Xtqsq z+D?YIlYt05UGgZG$v!+YeGjF2$0IQW3HdSDyM#(3IXPLZSh*a5tm%!}p#(#V!Bt() z|M1l}HR!&4mEOsM0c@^n-`c@JQxfYNnh)jSnNEohwW^nkFh0M z4JGY$>9g0O-Mj9T$dwXK?`cN)#GX-9-GAw&X|ds(m9mOl9%p3)?)V&?;G;J^cEq^+`v9xl9=i$ihIM*tC(iHfjpIm*inmCxs^MSmd$zVDcAuLTm!*-6&*C_ zDHw+ zof<@Ag4%iO^k7=oS0`2H>DR`hUJ_BW$X>JIc6w(@5>d9(dfZ%g-44lrY<2a0b1)lk z^>P<3RZ$RUe-mmZ3LUikoAC}CBxqQOSQm_pU{s30ZUbsSFbEyTF1E>{=Moia@eY1* z@87O8R-X&v@6wH%2Yc8Z0z9Nk-bJ3N>-7AYMEH-1K_dF_S`4hLF+n9jqJnew`R<#x zV;~i#LOPxOIE^h@^?W?ml_KQeNSv6QG>nLOs+LfZ!H9kHFEUF&o^15a0U>UGAa!$6P=J^Jn(i zox3v@a)q#o3b>f`J0?WpgER=;#4mYmzTZhD{UH5QjBWqnu)F|d5%mAqsaWYeuwFEv z5Bxi^e~ktDkk9H44L?gkfGjzn1|tUK&lK-aK#x3E8rszBaR7D(eL;p$~0 z1`c;IBLB^B0Y`$SgLv)T(fXi9-no!M3m4{c8ze4 z9P?i<=0t|bw9PTrpr6eFc|6%-vE;ILb{D}{^2L@s_=r+Tj6PZ}>u)mw^RnL%z9^m63 zR2`6md@(Rc>9}@Ct9pOO@w@X))8~h4R=1lY)Z^lwg~$h(B<&q|`}L*Jum5%K$kWFnq`bhkgJ zkM-P(gvx6gi!p-Z=}LO*<4M!E^q4u7HcyXEuG$!Q%Z~TEDr0<1SnvK}{%@l~e^^zy zC!gs{7q_eT4&T=%d8ar@XcMJ~s^S0(5*^ar;gQPNfY6(NNH7>gmcW_}yPYc~mRd%*O;o(- zyKT@vbbJy>SkCO>mJ4fqx)>}1z2oX?QfAPwbl`{SWT8YN{SS+I=1P6c!~K%Fs*iuS zWPgu8`hyUK=;E6WIY_t~k~`E(N>vgq7Hh-FrPB&2PF@d+%PAQc%6pK|(a^>)wcQ1! zQdqN~2;9f8+{kfk-XA`EV1az|FZfajXg}7t&;QOUu-qsF@{JlVOPJ)5pfMp(^2iPs z7c24O#6a*DruM@`kfG%Y#d5AfFyy;1yY-4BXnS)&F@>>sWvwDSP8)%qS zM!|ng-8Z(~g5BK8CgokRS*mwKEqwdkFLK#HK$4Emvd{FCbgK4lZ*5(HibBF(Qk>4a zF(+Mj(?vnugQid)IV|R)iWM|T#VtYR_z$yb>J%pZcX-?meS2G!neM~f$=A+GE3{D`fE&F|3%6A1x^XPS`teq%&g1$WdY8m*Iyn+6J`igbqKbNl>32 zAxsS$aC8iegZ`KB$52cLaz({{1LpCmB6+fLKC$Hd`1x{SlAR(r;3R`nPt%jz$5aA&-U3*tjLw{$(%<_(^vIXk%gbww@Qu)U zm3+5H?e+g{^+?Bf=X(XWJ``B*XNH?o9`+HLt=_X9thj-(SubZSS*cDzz)<``+sVl; z4bYbOw@2$~cJ?CXdXuHpyg3A$=_p~Jx$GTnDmDFB?Q$LQPG)4=9JSbe@f_;#mC-ZVcl&=>gS8hC3UXa#zV9zNw` zuEGUfly_=tkA6!(^n~7~YIpi$Q+(P{qcm%)R1K+g8c>iiMF$Hcr_%K~B0b|qnj9ci ziHZ03*56#60$w8Vn;t)iyE!A1T^}i15p=k8e$dUEpUn6csU=eoV2*I<{27G=j0t4F z8%4GM%_|9kadDZ4#5KrzKKy2ybyMnTav6C2i~Ep>AxVRqEv_6j=4-bks#HoGCR-xU z?8NJ|+5~fqt};#S_mkrmO%_*i#BmKmvd>V4{C-%^I<;6qh#J@M<-s}v_)I+0rZ`_>a5N0c@^vhg}Z_qNM zR8T4tGE;?uZcg(PECF)hv+*yD87s<>;e&(2`ET5uqNakwLh_Z40rH;|&{ii+c4yl5 z$69%r;TGFu>xzX_UgZE>;?%hT}7RwK!5ik)0!&seaV~0(*F*EzR_dZ+LMZFbYbo^k?B_kI+hL@#c0oF1u}e>Mf@_ zu5{bKI6Te0CukLvcItYr63S8J_Y#c^Ym7;h#Xz&erGmS63}X(U{@5p zd>3#;W9*QoIW>EqNonMUA*pEzXmSD6AahK$;hdW8ftbO z7{o8rI9CKV7ZUNmk^EZ(;_pBR@u_`e)W#tU9QZBi}?MSa&=T^;Lg!0O($Et4NVzyzJFb=@;x*r#%V6Lmk z-gE(KRlYYj<7(j&)r8C%k`M(OUC1U&W8zbU3QgZ&USfvJmo|oL z))9{#r-r?1XYD+RaspvQ{#S0%nC9DlLuHD75LybKBVv zxVj{p$c?^C)vMC9gr0beWKBN8=N9g9F4P;5;bDlb`tO?T#dCbY(Zc6GN>Sf?0QByU z+b(h#yCBl+&Q`9BJ}{YA-iG1tUMhfm586t&c~Pj&-^l)VwgHX5hYCOf>hN87(oe8% zNq3Rdivi^;DKX*<()FSC5qR0f)(w%hFT)Z@^Oe^Q7j$IzjRvZQx`OzYuf{p8Z}N3l zO0T&u~@6F}3qV zWBUVoxCXh?XXLf*8k~38vk{E<9Yef6BQR?xpQt05&4QHC0;*rj|GWRwBJH+&NZVW% zKlpouV5HRTwh|O0@3`YHBjKCR#2YuOS|3ovZKK3Kx{3W{K-P^hABxJ=dcN+ulMd31 zJc_X+DM*qPT?n3)ct8sN8kP;9KNZFHD$U&~mC2d}W(te7EL_G6zKKh9oH9|eL!(F_ z+W6dLB%t1+HRyy_IHS=9e^ZvKG0Ih4C7Vbbx@Wu>dKW1iA1P@vSR zXVRd;(lsS8h9=YXZ+Y31>6=~RB|Y8IUde=2lByaJT-Cwy(0Eqi+QsV zQylN3fT5Uu)P4Hd=zzf``hpBmsU3%#`^51`Z4YsrS2aORw<|i^nEtY~4T0En##sHEg-4|NOt4k>9szq~NyjnxjB~`CBH%Pnu=*(b+g`o21w3dpWZOdlGnL zRIQ|=uMQ)F3Vkr=>NTf@sgsXKm)B2Pldg3Vs!FD{Y9_Zg!_9D)(hrvXKVviu%Z|A6XD8;HyK?OqvAkB3uF(Axig{aDvnbcAd)#~Df@%&?trKP!3oTNG@2ISwlC?{3l_XnsgzQDAU$TKdfW!@4d5^SC`D z{c2?hvmXUnjBX(B{4`Yz#EiM~{26LJUcG5uQ2bDr1?^-pZxPVQVm4J@&^NL> zUWw6SC8@y+B;RIt5Sc5;eK_vkSEgor%sexeIxdA40XCm#ef zRlTv?%%mA$Q%$gP+Fwb1tQn(;?K!l>!%fCOM(!RY-;`MBcX>0?mS!XgOv=!A%UgC> zajo7g;x&OgRpX*_gj?qMk&53yKEldDag+HxO+eZ(Aue~Gc!l9f0g}X3mhP~BAllUFTir_~seUheUEA%@*Onu3)k&Y^dRO(`1(WMi3w zUc@-N5au(Dc)MVLRPO3Y@$4UP`}}vrjRy&jruT^2vf!dwj>qGorxI!U#26B!9#s}N zr=DCv2$wFUt{nX=L2#2K6EkIM10%KBK)hJ#g6Mu3n~bU(k=dy#DJbA|Z07#1HQ%iB zvW1(t@(0E%=7;#4IRhw(cszIF>jRElQv!DxbEW5x>=9qgyPB**G;B9DfnMfHFCSeS z=HsH~zF0Gi>!&EiC6;&=SQR@zKUh*LuTogL8z5`f866+U<1;6MNHUb;oAv*K6PvKW z`Z8rVHaRiObvWr`p#4yV&_&QS_n`*2e!DAhq)h)hf~z;TjMg}AJFWm^fRvxdIjscW zbR&}mUx+Tfe9I6T`kbWQCX0H|1M`UI)Wny|Ir6DR7}wE~+aaqKkHUsYvQpQ3%87{(~_Ys&OGK%@c#4&iNwE|v>1$AC9tu- zRo4aQeeBtNa#o> zOtJeVaB8G@K>ae(j1b9M>a7Rzp=2V%LSKaJ&2muBav6PeC~UM6fm1DptcHtAKXk2ilV`DP_2Bn4A|Gl( z^OB%ZpKHl7HPS@3Vys{c*oxz@^z(NykMp4I$IVfd3%s5R9xT89{6GR)Yj{?V0Eoqj zqy)w_-m0=Qi>izyr!hGV)zi!8yN(870=TwXcdGWwi}9tVEx4Ri!D}MDXm29cch+F5 zd)HU?mtPD<8(uqWHJUmXpvif{@B#PSC+NH@P;MF>8!y6F%rrdYt& zcGF>vf5PqvI)pcCVk(~a!wauYi;M>D)urBa<*Tb93TD`0%nPG4(Sfp-x?{n1*-Eu| zdGdvNwZ(eLO0`8bf;6e94@>KjW*kV6{2{ptFK*+r@|iB!wY0VMbQdCLa5N0pSiOLjx zVD2()%hv7Xa7wa{Z)rPVsUN5JWl@ifzf(ejEq2;L5NK2-F&Wo3zdx(k^D7?ekKpq; zRJ=M7FW1vjzes-bb7Qj`1Y1b;mujF=hQCPpi_JIE%w*0^(h%C>c46%@F7bPI#ATLx zu|3IJ2nl|zT9yH$+BEBN+DRUSN$j{3poNs_+hZHpKl6a!K0=UCH{(EXRE8Qt^N=P% zgFJ0qNQIXleIr@tX&NkxD7q(u2nEWK*2z5tOf(ea!okxn!*P1Gv=t)iWoPv~UeqB@ zq;xEYp0xjzQB{u*eyU)&&Z(^P0z*Rvo9;+e%7Pi1Zl0sGqoH~V!b*i-&5 z<;EV*Pn8T#vKh}RHs801Q~L}_R_Dbpl?aF=Y+ISVJObtBL8)%6O;%AumFcexY~856 z5+cuj@AJeul>UrF`FJAOyC(HCb;3V-R7O2uVo=PRzkx|NQjQ18pmay9zS2j zDS*MfI=5|hsRJ@*`gseC#K=zU$pAAzimq-l-Z`%0%W+syFGsag z6ZMs7U&H?N$+AT5sQ1v61~E;ruQSBdg#qe4;nso(VlpA*GxSCWEoj5 z^zZ?0i_Kl|%hhtSakwl3nsEwJsi=0oVi~{QdI6Bmmcr3DlE<>a_W`Rso({f*~InpTc(R z8lATA?dZ;0yOB)*HyPtgrv)TVlF^eppPAD1izMWR$FFg1(_TY~(|4LCSbD{Cv0Pc{ zd~#a0{A`_1Tf^&JG?W;?7yf;f`<(!dXL5%MsBL z)FnXEvosF@uW=%$D7K9)Spim?dIOim|D5cMX5}L@-lJVdz4EeXQ|Y(HAc5E42>1fS zy15a<5dW+!XsSu(jR}erOnv5^CqZx4tisOBDacT1)HxEMT}u`7nw3tKqg`P!F<$RU(k=c4Qw* z^t7Dk6O`S2hq~N{2MXM6iPxv5WQ%`TI+5Gsbf@8HHAZ7@)Esj5YDXQSmMRwnifxO9G z;Gx*K+?$U)0DUX(wY*Bt9HOGz-jc0u@ zyZ0uef|Ar9rtlbuPh6ZQNGVB_X&<<4d*L1oeTjj&i&)L|+{*d|au>zzNrD!{sMtyv^n&a~MOkAdzlDg&cW{|&)>%7*~x^!-tI)<1unK?&xZ0Wr}a1#}@m!nI6v_GMN1wo?A+{3}**G6}j` zpIwH4gxLg-sGyQA!fDFVZdQ z&Ppp^g?$Z;rH9=pTk@mx*@ZZc*AKqU`0LxA57*g}2eA2{9h19{c{B&e9Wred>^__y zU76?LM-U=KK;FH=2dM>a4t(Y&Qs#G+!D+Q~ zTg3oa?=4m|_2$5>%^uz#kw{dWolvd=^#4=C0jlkc6JUhTI)a(#L)S+hXAUf zX@+rG%5%<>+)v?4PM7YS6J+l?9pcS9d@R*R!|*a(Uk`{9iN@tBn1*lm+j5Vczrv$9 z@`|_avD+}!sx68U&AV+r{IH9d&hALHcn$A0*XEt*{UV5;GT5SJ8qS<)$zZdSOPcrE zAq|%In!gn(rF1#WZag(}6;^p_JFX+)pBFTF!o9^*j1&ISC06j=?D;BeYm{rt;4Q^r zromDtDE>`&2lMr{RoFMa>{K2N_qK-tifbaygnyU7&3s?5(L;cQcm373M)hGNDNoDa z*9)jvb*BA{^V9=R@20<>!G7ZoNLWw^#;5*|J0Z{j$=P9Q0!!TY@$tR`2vS-fNWM`u zKawzrZqbdqcNyBPSOl}mE4Y&i3->OBFCteeb>~nkZ9ce&N{4O+OMl&>M9iBaNUTgJ zXIQ%{1eyC<>sQ%TmY_g(j~s(}7EaT?0Fg&qrHLZZDd3}aseS<#!|av`wK%~_ z%93Y7XiyJrvC(!R_;B4SdoCWXfnsOi!Y@ojI)FUcg;_#Ai1mJ+76Lg()1BxozY?6- z;7+9BJ5<35(Ln&|)$V{i2>SXL?N&p>4-aTzhbcF>n869iC#R0|?$7gVk)+aleEWsg zZ(qU|Rd@{EQxLG6k6XEGVbeE0)jd} zd8Auk&KivWteJoCY(X)72mV)8gOANGC#arRb_fbo?ktULW1f$Z_Ie~2fwS0q$PaRM zxAU@P>K#KM$YCKIL%qxW23L=kV;qJ&ju1cvUb`yE@pHY@XUP)O@g(u4+S2l9|Ka9e zR={T@Z&0EUA2sX`bKw=T$0_B3VpWhc$cu;a?O!waV^D|<;VEA;H8kdf{TAVmvflsD z?$0D9))KxOXZA7YnVGd}xzA)rT1e+K0?&HUYBG-cLqVk0JdsocFn zWzs|M9%wR|PY2!Z^zxm-GexWIe62$+sW|9g2Pu?sFuO(XdyY~0w?)|Q-%Q8Vv{O>@ ztqk{K^BJxrcHaG*eYw%7oo|X6y;o?(Ka?j=ELL^9r%KF*FA?)R;T<%Lyrbd>$qoN; zognCb?&s_?>dLGwb50dIHpyE8?=cWQ7~pOfY`gB5Hn__H)O5?_zRQL&PjDt0`Fkk+ z3H7rAn%Tg67)^)`!p@+DF%Qogod$O;r>nYc-@U1ToQTWzA#OfYg(7{nqo$ zWo^LyA;Yn|@BzMI_;pLi>;(C!Oat0BX5u4`#KWbsRoLliMwsE8_EL>!lt=x0RT5BB zz0thza@3K+gkg%8p6Exr(M&9ldvgjWmtz>Ei6uq%o$~NGW1hj7cij{leP(OweX|su zORJ~;nM7Y8|0jVR(VorCp)XlK0Wu4US--0lbEb*D{CJ<^HR{;iA%~8u9oCdfxnSY^ z>GJ(fp%IqA^ockh{{#jmEy19+qYR>oC?CV`oDF1#lsT$2C;Wg&<=>@`;+f;;qyX#YPtj1lX=(An!+N+E+@mO>nE z`ij%q=|5=tlu}{3c#;F}GpVi({d`o<78`S7kM6N7n|hN-H*lVjQ& zThmwIGcuz(@qX{TzR-XXmU@DB_Sr`m*}ry7Knm%ug8W_42os3cq#G$_Z-tkh-p;72 ztP=vb5*&-JF*0ahw3Tmy&98W^Z?HAz57t#8>$tDaDEcO)$ZHy6c^sD{up(rz?75L4 z@FQ@|<5|D5hW{U3-xyt4w?x}P$5zL7r(@f;ZQFLzv2EMz*d5!pZR?%>uD*NUdt;n& zMt)?Ry?3qJwPwv#HS5!Zmp{RmU(MsQ$p0*Wz?^3cV2OWtQ;8Yy6Ta5$@9#WW<;|1} zE{a(g=15wyFhJQSl-H{9hlu%{>`t35V?CpCyS=h24?>?k!hVzNrFvy-A zmQUFCO*E!};O;aV?SKF(S6uMFR`riNC&Jc#4X3LiY6hI%qiIfpEckX0Bl!w6Qi3kC zDJGt5t0yO?_zN_1YmS}%hTQV$53LvYS4jtiHtWtQ*BSNBi(@!vkYm58eSM2EF-RZP&dwo^!ejms1d_S|q*5U^*X z0x#7yQ#Qzc`|tbAp+;oQr|NlJ7bDfu^b`LE3|k#x&B{<+;@*nUB6 zZ@Dw`?6O_Lkp5wd^5SQvNzujnFrW~84=_Cu`2pV=^ zKoK2n+Bg>O8mY1+0P~^VmX5bIU2S2h(Nszi(K-(y@+z7RanYm0{L60;tU(=BzHtl- zG8X5*9k;ka7KO~JIaX9{3!K7|UzJByxHaC_Y=(`j)K2Sh4c7-+lbPlU z=&;seoS;ubJv&gx&sXX%47h%)uiAgb!e>V9S}n73pymFX&vZi~>Vl^qP@iimRLFS~ zjWc=>!1q97qJFp*hZ;zekPz0Hszxc!a-R7zB(d8kBzs%A*yFgZsuGiU*=o3bFBR2M zt~O1!Km4?W*&PViIfdYl~g{JwCG-sohhPcq)oACowZ7n8`%c|FD%UE#C4ub5$zpz)c9qX`MvZkp!{7cdz% z+JU04lC}&ZdFzN3{jzcbPqw%>pNMn@>*xNY5~@-7;X&?$H#Th&XSq37ik!OAm=Cd` znnP@&?#(^SSvQF#o+Z$pLr4>y_raArmtYI@XYF!%0iaXXReatipVcMHAW^jJrT}O& z#*{9U{mu6tOxeaQj;2#TvAXDEkTT2d-#gl0(QeKR_~7HcWVKtoAKuz!$`{kWk=RZ zUgL!*0~tx}Y&loUr@8>RD~PWUYQ0#Om9kY)LKu6k^P4sa2+jI zbPG`xNRbbqr@r_|V!H%bNi zyPwX8@bLm?PrY9Fa-P#R=b~F4wNvG+Sw1gMbiXyuN76o4jVA80spWI}W}!`#Qv^=# zgXm@`4lw5eI0e z)qV^jc?BQ4s3yUy144(R{GI%+0M>MEU9XUPG-W%OXXIz|6N&}63i(Nd=SfGJ)A@>> zEA!vx5M+Eg4yrzMP+`?wyZMRp{gh)>hKZe%hVS9?F$A7<%}>2OgL)MTs>x`{Zg>~l z(B3S$9BWbT>oFgNm**AL9*M6Us3M!j%GWt2wNU9kwc;MBK42D3LlkSJtT-%+B8NwR zwZ`=N@d6HR?A{jdR}&_VH+;x?^~_U10D%@crJcek=RiIxg2(BmRrkxzM*GjYiS4GO z{!x4h%bhnZ#6@k5x&-+VrsR04Z`wI3q0NB~F;?sDmK6EH$pZGoDItQvZ1A{wYZ}%E zM$uYB?#o6fZinRZ>5O_-6*NB++{H=qTE?{4C6vUU^yp~X8jcUoLWxL}J0%WkUmg#$ zg_RMx)i^?_eb9z~suY2Op_2W5}md9~#L@j_m;PuU{rGgnM? z)Ni`|%8d;j1Bo=BD1#CB{zt6aX-#P@JLAw02gwZ6akbOh3xh;Z$Whg%nyenmOJHI} z1>(Y^oX?6$9uj@Y41=b6;kqz`Ba8*|1s=8itdInng~=x_WW0Vi5(n>Wr!VEzAs~^T zCmtQhxC}^*5_bg{{s2(l6WG*H|!eMtw zQ0|lwDx|1keAiJCrL#im$&O@fVerTA5cKaaP^Ck5`_)-;q;2G3A@SmbRWK$g3C|;n z)aa*5tXY$rM(d-LI!YLX_ZP-i7ARM>G!`HWHfkpIVa;lI28kX5Y9e(Lx~L)>b1+!}HG^g6*wqD+hjjlyUe| znZV=2A7Uv~+$?Y(^<&(j^>*vxUzye-%6tu=t0DL&;Lrb6aedYOSo9b%4{5JRJogK_=}LQxW1^XJjTY56}y zGML*Rd8jLPm1T!|u$iI<*qp+?e`oAPGQp(tS{2CVah{h!Tn3L`g`^Dp0#v*%u*fr) zIMIIh*2R?VLj2L5bHgD=8r<6OU42@a-ay+;S@{j^4h~RU`3fG=t--)&YAtrw9(}1z4Qs! zWopGd4~vM*$xIbFDSXI|qJb=nWoQLViWmTYyh=kgR!{=Id63DQUr?U1<=C_sTnW7| zhB|?z=p3}$L=}#HKHXUoNt|(cZx5vuTj{EFRC*V;L=B~3m*u~=&2iNMJzBGk zX*~E4u5*tja7f^h5B*c*4#+-G{4BNok<0tzSNC&Enjt=jk1maB<+bLY#i6>0InJx- zVU*(et(z8zYsC`MIyPN~6*33W>b^)CClVS|7(R)aTN2*rD0(lCe5l-e%jl-;R*y9Y z??NKju4uk|I$RbBm9{15*~OMsM5%VGKODQVlvFw}&pBM-w=~(1DWo$iUJmx3y}kIZ zoifYLrA%9*eyZ;u#yk8pG@y&63Qx$SH85%CpXiquS}P|LdfQAiN>;V+q+JY4%t+Z| z4r|_ln$dPkB`XgyI!iv>PuYX0;xcM>f(6ljlE21{l!zBCfQa z6y{BSw#1;3A{N8F5@rd%#}$e~HB@(91PdiZvIl;6>4r}h(aDSN3!t8$@y8G@#q;_o zvcIDR`$HSkeDQQ+~*0Ut2EEB-O~ zU~9ifv6;e=VHZ-*D!gdMt4Hr95h(6JR$1KgNOd9JKt?24q=Yql<;5ddT6$J^9ryVb z+(1U&sF=E@Eps}WK=N)IGYaO>cu-t#CX?!JVq5XP@nc*fnG6wU!@JoL<8iKbxAp?} zh<3fFWiU%l>IBp-A;s0IaCLv<*s)bx{5X#LS#|t`+kk-$>a@8x^wCMK1I?NhJHcz4 zwyepNAeF;~^cK04WWBk`s`xCNX4)ku;R8C$D@s4hliwvQ)Ic(?IHQYd9{E^<27M&9 zuK)eEPJGpZfqX}YCC;f^4aMnleQ$fHp;yH?X7_fo)ECD{F5kL1r2Ioh1w(c>q@kB( zV-CmhcdQkm`-~4aPZjbA1k&85S+bjYVXvH9PG(EQAek@-L4Uk<-q)A7j61E|d-NE;p#-!M>Pqoc-pw5q=SLEPz z`~?%wm(Kbpb^R%!h#}U{gEXSA+V*9OEq3bNWM~Tz`8iLnev^%w2dK$zgPEUlngf^e zcl(J|I~HtX#vMN!c}}7JbgO)UP)z$W2)lLZTxh1GC`Xq{B)La!`_U&^J+iD~V{j6q zxZBuj%ss4RhllEl?n|h!gj7Iy?3>ta>lk_qC%vZB;Gi#(|r?A zd>SLhE$5)soAi=tQo3I6sDH`g zzY1(ohbApf(TbR^yzw2DULDNSOY z4aDU>W*a1?O>dWwh`5Xqf0~R~o1A>xrd&yV65RAU?-1*zvKPYSg?(9PKOr%`KDc3@ zmV*ws;2q?U$yE#*pmOT*y3ueJ4Dbvjs(aqdf}Q?eX-e24d7Xq4bZY9V>v8=c8KabN zWoFy~p|e%s7T$I(#PKZ=_Yw<-;fe4s!oWP&b~&1^!{MIP6Wn(0Kdvcd4sO4-SM4jF z#q1Y+tNj?GjOv|KvoTulb(g5U$rZGwW`lkU9jm6ZDD3QqeY-mPs~yNco7evEZo0>Y zgbHxt!zu?%kvXfwGyRKiQ945+$e@C7mo<$FAF0F}SRaP{58v#U1`y3q8^3`Ym?o{2 zi&S+{oMA2H30BGwKUYOogRiqTcn?y_kQ+p{BgfHu?=Q|#U8Nv$jWg zyrCvvz+esx{;IffS|J5szJ5}h)X8rhYBYwC27qm06tK2N>De&K0?xN$$P1%t8nw7a zT4Sw6Piv2-&D)yXAxe2Ozey3F&83^Q2iz$wh)=-)T|JQ`*`AoTZWLQwc>@0=X#Y*= zLBIneiEhJ1m+GSVkpgZ%Cp1Xg2U@1vFqH813UmcSYT-7tq&mdGs>p^wXH{+UUV&O@ z2g0K*!=tmm%fYXty5jc7)+tEEN8aPXQ)&97(FbeTdiGOkG~xeV31A;Gd_H&tgewVr z-r8pGS7Cy`(NF(HUp^tD_b6avjLZi9vn}~wyz;+q`_CVKD`fasyU!Xwm0pvSW`J3T zC?TQd|4l~z&mV6{HP1g_bNTZxeW?B(-_Kbt{6*}ge4w1v!lyeRl%F?;64c#(|Cf@PZ|gsZ;QxHfia@-{bIKG( zjeVP~00t6FRRlsS`(I6pe?4y?9-w)<_cB#q_&+}Yxb)vXO89_fLDkKr{Qp@C0~par zoWJ4=+YtYmUytS=yF-BbZR5eSi1FMCk2zQrpziC+qB1E-B+^LFDU!^ihS*L0Tixj& zH^Q5VZ_6}E&z@icP%OCO?(nu;kzJTnTl`v`S_4q007Qd~<;rnd`Nqk~G0BA`?|Hec zy-FFW+g+CZkSBZhs};wJ2#7f#rWzkry8Z5+M2JidWXQ z#3*x>1~KX%)9u7(iYy5w4;(h?l}fSl5I4N;Xmn1ZG5qO^_k)6rkV<pDlILw9mA5f{kuE? z1R3z;tznHiVMdnemqTE1wJ&;k-~Tc?6EHUIgFowlAU5;jLWcBxCqyG#xAxzz{pTv` zWK$78OqEWRGXjBZV=_TG87q~5g(nKsnScWVcuSB)0r+H6?P!EiL$bh(G2W$G@J_Xa zM{()D&mWj%bSdCl@(+Sx*FuK)&{&fiR}?IxdmiJL={uAoOLBWq>l5vhX|7K3!?E0N zKd*z|l9&v|E8+9qP2DDTT$JZp325;XAm&UA((9ddQB70>9&y#1_fF<+IX>GK@A^Ew z*vdWK>JpWzTx$8vvv|31>#)i>X=GmZ@v~(tbYfzW5bu_YP5C~nfemrnQ{|-eQ>{8e_F$N7+TMy=d-JV4cPxMV z#5V14n+rpxIeRw}ng4M=vD=jiz5mdq=HbEQQzx8x`XUm1e*x>`!eRNcqWW8T&Cf+& z0#MP2Lt^Psk1(d`rb_{ix}$Aeg-{kn)4oUU+YTMicOC8#hPN=Km;O(XAX~z`n{RZd z-zK?Zi%gLnK_ilTau{I1{_*s4a6r$5svAVxUh?Nx@F#NH_|O}Jx6J9kLK?+&d7>To-do{OmAI%6^LEXvO{uWw)+f^X-ZuJ}1d*Sn0&nw9m+MyhCzoXWVli=~g!rm%L`DTAt$gH^OowHi4B5JPb&KZWd%ZRY(l++}K z2jx79o6lnEa6w$I@s)taNS2VK9~$z5Q8=Xlsjoffwo;{X<%F0R-ZYW?wRX*wPA`u0 z&8}=XTuxCAmx}^etUDb)?X|%O{lv}WpB0<+-eZ)&CX$H3Wp9?liIaLb<149++!mpX zRx1f$@VJryh2*Jhg=+1NyZo|}208;GH%{D_+ew~bOXgXCz}oSZ9^^xQd6?3~V*SWw z47{oYv;LODkR8O;X3Xo;KEn>{ye)?yL`j*5UE0{Hz;DeI$iE`0>^rFQ4MIzWR+$fZ z1cLdW>}P|_IZz6@GR`zDo#!a1)F6b5VGvlKvbz_qE-)zAwZAmqUp&`Kf@z*+lJ9_X zV@w{rxOJALutHXGU!SDc6LXQqE~eCYvXZ(@!d@=6Y=*ou+4AmCS#rD*t^dTi)u*HJ z+~=%GuFUSVAgU~{*~$%W2IXVVhdwWEFl%yF<@7AA1bx5-)`-$NF?RN6+eo<#OmPkY$ATG2%j1w|p_@3beQ{F!9i=FLD6`Qag zZ_QlBY&H|f?sOsuP-}r2&*EAoue!M|OxDh0vRIf(*6|jD3nrrA;NS@5i2dxlRqcO% zW41v1UH}YyF<#DHvBhlt6WxzqZ-&5_XY3yZEfB0gU2e>@*L1*ZrG3keHnC4??&x!{C`i5e~gbOFfXQai-!75tR9YA*ew{^+l4KY2=|29tDf%HBOO!XT(gBj zXH-NZOymACkAz&2=DwHCMD`^5>@xpBYLe+Kpc?S!O*C57ZgZ_r=Vg@fg@@wzAxg~y z0wanp7tGPl)`m6s-A^1BEon*)n1O!gkSO=}A>NVkq0z(czE}VV<`G{_@l*j6;*GTv z{W9B?mhzAtkkIn6v77~iY>{qDci?rKKwrJ?-z?<(LG-K>Tf5+L?lu7)-&Pc$x=92ON8mM1~o5{T>bW_Pk6rL3wP&1gOsBB)ZODMpf=eB!XUM?2YWN6nKXt;Z zqf&*(A|ujIWJIHLS~iEVMYtn{To+<*&aj!)F^p6bmILw*8l}w;EHzvlukScPGXyF-A{YE}s9C@+O+S%o!?s z0Enl}j9;A26%;Bdl||6YpAT9mFLM)s92gGJ7_(C)@ez@~8ngn9Eumhy&b0QLWoIon zCh;`7VGNgkDI9Ba z{eqK!rLHIqe&51vJpLZDrqrwH1KR}`O0E_)3xo|I29D> zb%yib@=R9{I-P67_g%iFTJ7Dix)SX3c0NWFrBH8soLStolcE)xM05}qc{?Vz=l`q* zbP|!uNfJ8D(knQt5>>hse_U?`R;DU92|1D3C9F_Kjj`94K#gyS)-$cim7}X@11IB5 zjXvvHlW1j&c9E4Ffn>A>;0x^bQIDqe80L+hhN@hiAoA`b{Cts zMuafz95!5UoRLG^7fXB*T{p&fN;xvB6Cv!DeggHZsxwLVOdgMVpGZw?G3Dq}In9O@ z(!yJ4A3GGawF@4D9qqNJE!`+gi$TceRoh*`iz&-6$7D;9lZF=2Yw~JJ(nb@#EFy+U z9G{+wjbrb_Jc=~I96RFVTfKFZ-QWW^Ka^RlfkNz-H1yA=ZS45Ey$JmrQCp@h3mJBm(ZM*r)>39vPO2NbS~#y|1+cDmpUvYL_U9%HwfoX zFecstrKH@r4Vgz(Uq?T4n>M}E|43KT34OEko%=O3Be8sc1JDDOwQlCq?lmPm6{@)5 zIM9Q2iACsCP*b_Rl{Jh9-JWiDyUj^^oXQmtQ%rK3NM#_)a`~OGsrUTLhF=x~%HY2; zs~j-Mp>#JAtW^rD**dl*_2vs>=AU*^H=#C~{Us;d>?W(g2ALDpvvu`uhEp1}~ z$yU{4uV?%AuN`i;j&_zkX&o^!DPjCUUwEw9V9J|tf~RzOTyLrtUc|$)+$=a(%G#z{ zjZ-qK@sbD1@U;c+kATfJ5EUM{79k7*nRu0Ms#99)XY;MIjQJ3a<8^9EDW&@@qazzc zp7+BN%7_h_ma;fUWc|%Un=atiocldo$*6_gz#rUm3M%k(nbr zqtH;6aiKXOr25>1pj=$9YyvylvRx~;k~DH-B)O@d!0d}qV~7>nynb2MIlfjk-${5SE}IW49U~>-FLA}&QXD0vrMN}b_%1}M(gkkvRuVBVx2*<{>G#_t zJ6wyj=EO64U-WHG5sijwQE2pGFg?UD9RjmZiVhK$R4PFcNvm@6<}ZHN=J!VnwdD2? zcfDuEGX*9_@i0kucQhXxPB#WOG02RuocSwxuS;LWlqdEY`e0vO^wU3etEBjzc6WB} z@Sm-ZiY3!w;NgRfMw5E}D5jn-bW2uedK`6w$0H8j`J@~>eYaWt1)GjF#q{#k-83(%wOAZ`twBMXq->jYRJw4sq8q9%D5 zU?o|Yo>J$U?-zvVMxKx-n3BdIp~Dz2()ajXP?4pI5!!M@YjeE_CfST z<8wNhAegK6Bkr6g{T3aD0T4%fOnq)rwW~g)G}LMcosIzK=O|F@d-R70a?)c`5$t7N28kBE)w>xLcNRg!3?;ATZr`o0Myye<6#r~8xk)QY?Yv}q>k`Dc~T0wY4&$!5*H?=)BtEG&;W?o zW3eQnYUiiBJNbxEW2yT0NMJ28IVAOb*{4(Jabr~n6{^{MjoL=0xt+b?_-%l`;T+Z^ z67{{8%qJh=^4;-lrt!?_DcqqSyp>O1ZM(Pvg_z~ZQY?Rrn@pKb7q{*jtNQ!gv-KOE zx=;9Hgzpcdf7%z|;=HjCdnxqt1vi={afrHp6>;mfGU(P}{Xq=RjLn5kS~aRTFN)%6 zbSAZIITjrDy4^$PDl<-qx72}}B=K)I7Rs`Wk6V39auM?-c>XS<; zSqaxfRUb?E54?9kVwb|U`Nmfi9?bYro!8DwUWZ$sJh8cU(`g$8ZT#d>5G1A5c(ycSx| zrz|gaYW7;{=t#0SEw62=0?5Aw=8HrFSE3(B0z}bzs>dTudg9i4$y>roI)fFF`z=OU^5KHf=|piIn%V;8 zw%vnqd0{qkT4)WKOxkzZa-wsqlEX1O2o_7Y&faRQVZ_qO1_yd|{7HO}ZQoKSeQ`O8l3`pq%XwK0(77*Kq}0w`9QKF+zMk~H z#yy&AP^Q*6Lw|bjms#wc)QH|k9s(WyV)FS4EEoVO|M_0oy=c9a39^{?Fw-#(tHH;- zWZ!r$0WOwY)^i>3L+s6s%jw+zbCm8^x-HKa2kzSMBB}J7nfI5reIXO|3_nYO)uYv1 zg0W|paDw4^#Uy(Wmg-O7mi046NcH^2|(^0*1l?kv)Cd@Ng z%TFzO9M=&;AdOh_X7gc|39Q()LuH;AON{ZZ=F$&ZXRzaIE(MPBBr$6&NJ+I%5Z3(^{#?6Z!LFdjgcJL6K;Z)f~bhHIP_ zu3}w|jY1kVExJY$CAYeWGI+=T{E3jHj_oi_ok=Z}$J2G!rc)1)!saH6LQF1{NbVJY z=K|$4gFQuUp}flwhIDlLs+yVKt`QrIwK;_31sI87!DLov6*gG`LlflHerW+&Sq*Yiyjs#@5Q~o2^hIk#of!PKm1$)? zs0X3W_G`J@q0GK$NtQ=K|9N5CG}SP%Kbo9eM=@F1X*i4o1pyxnkB958oYnglbf~V8 zW7JSq!svXaiq2%N==Zh#y`V?e7X%!RHvkOw3)hHt@3m)8>PE#ghoVhaizUxLJ>73a zs{47L3feI7)k${iXjBltjXF2VQ4BfjtDI$-nRyzojEfrNqo9(~x1HS8uk=z%>=?jh z(KDc@gG)LskcRgjgD;_eAKQH1;%^%=SrSfBnN3xYx)Btn^2JbrplJ*p{dGEYsZP|y zg?-l!!+SWtLm?H>fcK&}e{OJ_3GwMjqab7{?^({5p~Bu8jn&hVP5FHzz9}d}Lq~^* zC!MBp-IM|-Bhu>ig1WxnFCJ&6v@38tU6{HV{#Rs7%;ZglFk}}5IwOxF5iOgMlRaZIu)odc(~g3_1R)X#s)b*k6}C!J@ea z{6su7Gn!|K9WE7_e(zoBEdx<%o&*cOP1pdF$j| z{w(V1>#w9+0-#4;s?k4d0P3%%YbiTjDpAB?gxFl}D5B`U???|eX=gpM$%;6p$i2e0 zE7TZ?8g2>zGZaqsN!?$kEfNnB;mSGl4`b>7ne6-(AyHzxwS=e+Yx{rG|EdVZ#XuIH&=9o6{L;Biti%U(N3Ole;+jF7#1{Gob{}fHBp8!YDj6&PD-z)LofAtu22H zg``XzC*le(NPh%sdhb!KkBoaZ8+&*bir9^;B&wdZ_{KN}`-hxeXq+IMx)M~BT-EZN zN{x-_+?Q|2Lz2Ky%3jmw;enNydI3D}`Lo*shjVe`JxkFKXgC&NpOd@@<=X}0ZB}WO zMP((KwbB5;Kumg&yArE{uR>*wSrNvdLb9?ZqxiKsr0wzzP?MKzU8k~ z*&D>}lFCl&C#p2%K#?O6llm=1{yu%B+DH_}dmq4T;3<@OzZ5aQ^U&qunN*2V#&2lA zscqS@$c#g`(Z7S0ZcxvkylT*feoqO=Kc#hcGchuY^XM(3cu^vz zKqT6~aa9`6Gxo5n#m-oOLIg7jx$)gVS55B?r&>W!4isPp_)q8V-ik;n5IfXSVos8M z$9R{80pa@9a zulEih9SonL_Dy2`_lFOm3#nqMb&-0R5=PO=4_mfn*5D|)E%RW)4U=o=plY@bUB#Wy zYGF$OzB3j%W~SUzvcqUr%CHeSY?c#a^|xu7>}3bTZRp`R3VcwmQT5#XImu)G7wPFE z6GR%-&mOiMbPEv+cQMp*IWO@%L?+mB=iTeClVf2TS7Vz9$N~yN$L18p_9UVO6Og&G)*LQ4Jryyj)C)d522)#ks9J@LnZv z=Uz1*sQyHisqv>JV>*+_3%V34-ft+BPh&*x3a$!@D1mzzf@5?;fV5gAUp^4SYcOnD zq=HX_yD43nnz|IWsGk?B3?(pQ0y%I;S;tX{87@>Bj%|39iL4R=tan-8$stvEFM;Zr zzUD)1^kv|D88E9G+R0C_o*;_ozQP2U8{A>06I36(05jIF@w+kmz48eGLP8XZNk;nc z!tnfNyCh1;FHw;xb>$6KU2UhSWy0~B@Z^FvVb&NNxee!Tu3l-@8xO6gRRLLCUK45! zLq{HWM?fr_o(z^7?JaD|?q6Lq(VzsC@ygSWXq5t5855`lH zW25wcLJ3nRJ~ApCTaI&;cJa%JuPb_qy(;v(!HU||F%fIvle4oftyrRAn~bfFHWqHI zi6chtL@EPI?G7tf1A9%s%WygB`B1U(+OY6EvjfG%7`G1`*RR&r94)$tGRGg%2iFYQ z&9)*ca+k`Y_;O9|( zZQ0}jnb634FrywU#ZG_$B;aON98H5xx2zVU9EW1q7fhb{{u4O0Y{fXc_fU_A>ejCw z=1!ZCiC)KB2c-^_j*qoOCY;e!PxS!LgbigumenD1uAo8Z_lXikfsc%%+DUOKoL7r` zn|`(_T-kK7oPZTAyNeRC<#Bx}s^)kd2N>mP-qMT?Kf$?|$>?@~@p|BW{a5qkqjOoaX}hwD54hhG zVh*(w8_R+1Vk;SVX<#-VLkq~G4Ki&?v>nS!UpJR|TC0v~nrg2Pdij=O@J5B4X)4b-nTp%|Fzt&`+5Lj^Rs zR0Zhl%}51y8u2(zk=o*7gD#&LH^hzw(4IhsyV0Pwzbf?}=L0?$$`NO4g8Ti+G z8?DM$5JX56dyKIZLCWg{)1 z4=>_8kFsqY@m&=plNfm##L-AG+ziVhX}C@1Jg<38l9n=CBuHj(c_fwMftMJJJ(*fa zNHUe>UU!aG3iYDz9+R9aa1oOd|A&Wz&dX1Hv9lj(?=stN5zEP`+#9c!xfi{po zj$jrzGSZUhX^L*o?kWgr_j&KapSWW_7ANAf*QZ)P^6LV>kNL-#e@nC(WXbN_RGSd^ z)j+6!-geOnO-M+{`MV&DAzwU^veji~yDxb7yo1JAi?)(Ayv_qx+{0k-gzlwYMtK1z z4NemPV_97;)^LV=iy&;OE2v!8{`0EABh!NU;g#{X zODQP*hz2o{U1d3>1(^O-(hevMKbo`lKv+YfGw*jh-JOY`%uQ;uO)%k~&oH18ip0c` zk~AXU*UqU-7|eqslz#wO;UeM(MVWe1HGkIb6QTXQ|9B$((d{bC7`lqZ!vvpwDyN=v zO}^4r+=ZQZR1s}AO{qm_lM03&aJImFZ^tB&mY1eMyy|mc9@iUsD>7P_%fCO%6@}bnE+el;lcLh3MfiAR*Y_# zLiFp_rHGB3>?8Q{ILGx{o2xr?((Ko|rSVw6$m_MB>R{uvFratZ&^TQPC^*c}w#wra zq?59EGY^F}uy`S{ia4bO-Kl~_g9dbmh#VSUJ|RwmV;|OwPL9{g-^i9_vRG&|+Azf3 zx_N-P1%xq;L;-(MFt+@FF^up*kDH5`WmsDT(1vei|01UVcrU8Er||x28eD)>>puLt zg$~Vy_bCD_YAyQQExqP|Yujqjh%n}z#iBWXI%j!uC!oO->g3ytZWUw@mTl=!6?qJ! z(m6(GoGGiPf?sBV60y-U!xOAhxnIuXKhI4Yz+hIz=mr z#KrECE$=w~c5n@ReTBu*><+-EbiZhkiGkXw1<|00>cVD`z?ZkCFnHBNQ3aW&$hm9u zy4sfYEl49Jgfq`S#SBwT)@n4U)Qt39p7~U-?7|LIv$C@6*oRP5aX1R- zmu7ghxZ?wA&S>3t9GwPMQU4hox^7m4c)SMe`D z0tF)VOnTfGgvT$Gd7||I*42{3WdG<_*puS#jUIJ0+=njUteO~cZ`pmBT;q|Zat-Qi znRq?FpIIxs!Os2iO{E+B}%p6Ke4V+F`zmijs` z)r1rMAm3Y_N%S_setRe`yXQ?$zEPjKt;Z^u?Snn$m{tok*xE9bO5>dF%uLtzIGK%~ zx)R7%e)g|xl+pI;kUSig-};SV@^g9$M=o@rDkxqsf^K?ceRZleDQ!8f5#ngR7-Jn{ ze!?}x8r&gaDIXHb>BU7jA@1s^zuoY|o3`1Kfz_1VlGBsbeGqtzbcj!S$!E;edz+Q< zaEp4;o4edM#L1Ik32X2ALrNbHo0+K|cpYixR@FOvbTy}*Lc}N>r4*Dr6vI`auBwY4 zAKPgOcpqN1WG%B$1j`UHO`4C3((3>dw#NZnK6wbcc?7WIsql;Rw%O@IWlT~M7+?a^ z563bSdwY67;S&TL+yKT!P%qYx?fLF*UauEzO-=0aYHR;zUC4CGa&xOv0`-oW_2Z-I z{1>m)X1n~2`SYetIKYHtUqGgqtTp@I?0Af{WXb%VrC6=KZuLl7j997OIHRLOuFMl~ zs59yo`I!1QVtX3&D*Vm-K3Cj4dx7pN+a=X-olJ=-M~lxS5idMJMhb5W5(MnSBFI%& z)1FPrf-NVpnG{GD9;1ZnR z76QTD-Q6L$YjA?QyL)g68X#zJhv4oG-!pUHnLBfT>szb;Z0JYMsZ+IU?_Fh+pM&eX zh!xK8q2|pzC8v75x?wv3xiw!)-{qVX&VB&f6B^|9JvM>#QnVtr{3PVE#I(7xd7b21 z&xkLtQ@+}GF8a3>QIXWf5RToOqhkLvXor@Y{4IL8R97s+@^u%1Alc+G<$)(S{kfpM zV0;_usmxt(fOqyi!(|2G4{*Awydb?OFHrIBx!gO|_>ZSI#;F{5e^@*#wBl z>v96blPTcS!I1fNmawlnua>;CgyA02Tz+7uX0Jk}k^6Ndfl5Ez6=6ryz_;r#z7^xgZaPiY@Y3NV zW_{l4*8mNX{^FP-RI8hH%Cf52`IwYBvMoLxp=glD#nD>a$a226;Mmyt?%a4eoL<$B zg}+#%p|q|KZ!`mOA+<*LiU02IuK8ymFuCdb{xr@E`klb@?dItZs;sUET-Iz^V#Hyp zE?@>P9Ka3xIsF0*OsyDz=JC3(KV}y7$DA(O1m+F){J~l&YvVz7zuaB7!s;QgGx)oW z$QuRaO@18*LOjnt=TsbCA^~g~9IWd)?Y$4Xi#Z`fX3*3!QfsN;A(OBz_SSWf)Eced zh4@|*d@*9riEXrw;lSGOAfw%dN`56ImH@Oq7K!d>h$wBywtWNlNdmMV4PfFD>U=~z z%`g&X!EZytvdfe<%AHf)yoBWOpwJLf6TN9~@r_LP&z*3P-iOyUy)*;?LcbHHfM z_y@Ws11L#$&7*EbatnQU*3gfzd_kdwF`nO)us%;qjModAHn%O>R|s(7dyq&l!{Fc# zl(z~;?KSE|OHLzdAQS~wNLT+PuQQMy(j4oFeu(jM3yQ97yaPsgolZ(Jc6*$(CspkM z6GPz{w3=d0+AewzsydP5sAPj9BHrwjILvROBH<@WhE(z(bV)-0PT7*+b<%FwRUO2L zK!<53=5u#)?vu|G?JiX=%Lc%VYW*JAxmFj(dYk1`m1rl}!4RP2>jtJ)<#;%LO%^p) zEmwmc6L{ez;N4xBFJY@-rCW|zY|BATpLP8&wxom#9r6$6_ zSN-B{xSJ(6pC+PyQ>*KK_byjOTF9z>6;r9fVJSiTTZhq&B;W# z9wZKN^jyz%Wfq27aOj6BB3Ze~R^kuPw*K0&eWv4OK*p=*W7yQ4hy#8xe3OjwK~!{` zHjy@cJYxiW7i1nHN*ray?*+$Sp7qvGN;%=C=6HY;n*}u6L@18I>PRUxjegl+3v)J6 z@{o63G0M>%aF20@QV0aLi_ zj!kUE1u5hsHaau-JXBU?#l(E$;~{)1_@RdJ>DOPg#a9g3C}x)TfN`1AMLPxp%S=9p z$Ine(f0vkmvI|@s6vxCAAI6r0)8JWH9EPDu9WON+n6>R6MUno@c;5QFaQO*){pO-M zOVS~6>j@oWH;R3awSJZoYmoP5WmBmtg58*~`C0i@%cO|^``imFkHhCdX81d=c{4SdAxfq- zV)S$yb77ZiF}%-GJM(*rH=@f${`ahi22S-l3Jl zQ>Wg`rGE7mp);*IOZvu9LUEY`+&9@>&M4jiB0l~blwZJL#=5McTMDxOQ|;rUE)zXzlTyBNqqtJb`=Mk{RgbcW+n$pV7F|h$CVCpOJ{&X3fkd`G<%s z3-$O|_H-Yo%h5%>3sJ9;M8}xmc+E zW1|KWA6#k2EiUH2!FHmLP@VBB5m`O9Du?^V5PvHoBMrCY4@1~L!Gq6o;#pf}q>Mu7 zK2N`Y6tt@mI4)Yx9W*8mm*@i|7z8JTmYkxj9p;xvb?m|Y8=Vaoi#-L;0L}er6msjH z5BmLeF5Oha--K3Od|(6UzdztqztkzWP9UiT4AbB?MNp2=JeTq? z%!2)iQ@iM+kF7ytnY2Hs4_+KIp1)ctXuFzNHPMQkE^0JPIPNUxNzK7wGH1=#_dr=* zYOEZAsc|pu90 z&QJevxO7hMrA$kUq->gHHt~_G2ee(P-U2O&PRZ$fSJO_S>S@e3E{M2n><$~fp)G=q zo_7vvjaIn4*K>x|jyp1Z+n5OQzxB5*f<(g{mC>MCQZohbIJJN^5l#f z@v8=3(aMTGo%^!{Hr?jtre9&B0_7Nc>WoC08ZctIPz)<3iP(#5|9H;X=uL%ZYQnMo z@oD0Jd4Q;(x8AkgfoQU)in0Y#l=0nWCL{Zoy~D!&jW2AD6irf)SN1)+r^-V~eGPCw zLGul`Zc+$k;!4IM4G6K9g>Nl3dot784{wut;M^koMA*oQ0?x-C-KcmU1mWh|PT1%l zF625~4I#Z*Z9h1fp=~LAvm}pu?lKwIIMndR)cW8G>$7qPoas)^+JF(6)vjj-UZq5# zvXGmwjj;CfA-XMViJE(S?gO;jt?_$KPe-WG!e88jU&F7>)!%BMVrpX>Lz>xOz*^dG z9V-;t!k|yYo^wA6d@hl~0E>&JyfMAWoq+{;Klu_T7l&%Cl5VhpOJQ^Az1enqGKbRF zRZ`K*vm9qpUX?np`n>RwQ&4p0iiA5c#~q_d`$dL^LZAOWSZ@7xtgn)c1j+UR24K6R zor}>8{C1Y@bRc-`()WK&uP!S1HB&-ul8eypcs*3wxkPxq)DT@IUHa_%B$)I~MJYk5 z7+I3n_`IxAK?p@!f^9@QlLt<(M_wkk>K2#-L6%%Kq&P?_(v+OSy*~~iXyz*Ia0E>FnVwpZ0^Y3oA zj}D}O6%^|a1qDZXHv#r-gb>1~!aw_*_;92t2Dn= zwnqbx zes2gq1nRL#%|NcX>5=9pXV`<^g9QCXl-k#!JUQ_NkBqs8bgb1CCmcakF;}bHJtG`>cqodAQJF^%v-oL#wmIb#H|G{NJ%D#fgbqzXO&4}$O zYD3MuMClcxWX8(L@bIxf8K$vxx$(&&tho+x=v^&4m znBeNrtJ!nYTBZxjW;r9JtELaI`Ns3bF|y|j;wYtusA9g_&E|bV;VUFqK*WD<Du8RSojJDB8YXE_(s%oKp5FQVqy@^F@&K#E z07BgR^lq)$Ww&alJ%vAtgutMnuciq+t+dLE^@xTl4y|8*>`a2S%1OYhT0S2LFTCE2 zS9Q*EBiY?t18TO(_MY9$m>t!XkMwWJ$n-CFC)}5s>g(Cy;j=|jpcE7pb3AU2Pl4iM zmrnN?P|b}4Fi4wp(-G$EtX71I@&_=R0owtsqQe#gjj^SOpjbG+p9SbOzzY-!5>j} zO!CT6$hxeceDa`;B3O-tm`T}tVBbf)`rSQtLKL9(k#Id@VyC~mQ&)Oa|Cj4hRmfcs z!H(-U5Pa`(w5MiF0mA5>8_w{4`LaRUsb`*fRvuTqg4q!P9dyg`hg9#`5{!3I2lIPf zeuwO2O9EcEEiLbjbv1gMX5z-M1cN}a<|<_g9qD%A#bPS~MY35ljs^yA0Yru`3zzM4 zEPAN1jD194El|@DeZg!HQe>g2)N(5XM4Mc#bT=?e>4X>mJtp)(6eMz(2Qe|i;Su%* zpk{brxj1NV1%#0cqD+uSQ1C=DsVmDf`^rznQ|BRc@UnS)+}0vDg=SpHUB&l&a$q#m zslo%_t*1KGf=jT{GCz%~z2b?=9B@=EB5<1+ADaHvMVrgT?h-w=p)Wj=$80J}Fy@sJ zJrwC{_BNW?Bls!3r;=Bng*FYCd_?{l%g{T+!0+m0wR4I7JUGgT>tPH%^oxx&^M^Oh zx0vKn@}1aBx(jutvf8PLDUOKb+VtU<8{v%K=V}bE3b~zjvj@;D7ZX2l7*T5I>0j+n z-*?B!@|W0(%aPe10(0+M2Ihca;7e~;j8oPe0<_X+ow;59QGzl-Gm;3>X;A7TUx75| zdmSiap2!tOpB!9fDfPPF#@EI5km&LP7-c@5Tf@Oyw~(WZD+u@we_f&!;72vVJBJ`n z2Rvn~x0hBw{`|By@$!Jym?zd>G6zcCF$w+bzG5^!$)chrM*b@BLCRRe-PS)z;gkAB%H>~6cD~XGnnD3 zS>izoe=Z18qBSqLrl(a1EYsdQ4Ogw@+;u$UA2P5<1~xD-K)l!{I~%wLFeBVmubX8S zeCEEJho_s>@aYv*T`!n;n&z+uksb5CpXXxg%-_V)!|0-)D<=l9+W5@?#l|?mT(yL} zyW5J1K}`mbyA`Ou0!bMe1|}lhWq|p5oAaA6L0($pkd*Go<(~-|&{4^r#IRf3KiLvV z@t{)^O(MfDz(~8a z8meWQE!;j0Ci6$u^B+9&FkOn_t5I#gL26vqDZMc zGB8Hb9g75~zuSJ4?ek5={gJsvGaIN^s3^>fTY3-evDqY=f6IYoVU{?BPyt#l9ben8oqo@tRU%KiFoSQ~JOcTjG{0oF6B&iF zFL~M_3J5``TR?OItK8xJ+{`{D{ZSbEygcoYQYx3@0w<;&9_qo;neUNNy}pB|XB%9y z!WrdwE=)M~)Z23R^M!=?j8|*#{sFQA^lUqHpqdf?e$faC2#JktK0D4bMW0$b+)}DG zOs~n!Z(50mr-QOK_~e&5i={3gwCLIfg@kzQ^0a3Kjrri`1FooB?P(5NP{ zEa-<=?H(wwt28EjGjv=?v+baf0~jNjSPWXbADQ4>qPS819g0W=yIsuR!~MZAfqHFp z7juf>f_iH+rLPt^MH!wNQ46Mg1-HvcTSc%XrnUB)56s`EOf}&7aP;l87em%HdfKM? z5(tYi4WJ!74mRCX%31xb3p3q!8WWRdY>9>MswySvQljuVWTJzDr?x{puJqHWL+1tVf~?!39-IHSfqnv$&7HRk#ce-_T}+i%`M+hZ<97IwvubN z-#I5WXG|4JJHmPw0a}^(LHs%n++UqOKC-oCOV&cRpTwl3@*h5~I#-9=Hm^b-$NXKZ zP*eEjN}JLeQ)w-GAXLMp+^q0aHKeOlzNylN5#<|4GnX;KtXOdd@!z?@!R_BH zw2lO0f9>K>JMICP%Ck$IJ_7Yt^R-{5Jei|{42@;hlf}jhJllVCExN$8RDKPdhoZ@8?gyvy0VA3x>DY_X zem6dD2lB3$Guz`O=I_iVvhnJRvv_9Tob{lcUnF6#n!In{1wVfh@wnPozQp5KIsf{} zOd6eUVu&ND?wZf~B6Qg%M7>T7ztq#b+aCsyesS$|3E2Hlfbc~yLNkk8q+F2--~;fm zW3H3PF_?`OWD#b8Jn(@_MqqN)d*R@p>**Q^UK|Rnhr4|J_tx)Ya!+`DMTD7rf>JR1 za~;bDW7BFbv~e~|m*?wDa{!sVdqhzfTXEZc;a&kYqfVgL!?k=;{o4(Xt!Wp8dpD$m zHg5ZkkJ=+`XZ??5=ZV?URmC4_)+uF2GwvRlazhwBc@r~Z+UrGeenfdt0sO_ z)Vn#H7w(~;aMU!$<%o{^d-0rdfi9apV73dAGd`q@A;rQeIe_S664EnTEs4G(C+hL9 zA4d(fKINHK?GhQXwNK{AJzB~bi;?8LS==ZbiiQeJEsBkeUCWE+r|s0CYbpYgNrIWm z0y!8#UHFJ%J;$)gC*DhU9>DMd3h1fPNw`%p))@{U?~dmLI1;?F8`$9eSLA4m>GCqW zFw`;Q_=BRCK6N{kLwD?tV1(Wj9}A6v{RCtCrU_R@2VlgVg_3Naxr7Z|v~Dse(c0p9 zdns9fF7d|^E}D~c+{H;y3R*rN+_Rx-AlX0Oo{chheBtTsn(F*PhQbl1~Y) z$}i2P1p&p2;}1gv!J1j7_8_;`lc@Ml{r@Cw%FQEG3#!cPLSr&{hw=PkXbWDsD2C&~ zGV9*Bg40`?xFC^qi?0JoUWik!U4g}LT72DPG&C0M&*mIXIwHOZI}tyl*}d3Cdm_5=&3ga=_x3x4sdd?^_QA_sz)9iWD5o8JwM**aK}HID0((@c$r7y0AV0^fYs%W>VJ+H@il}b)wbUtxV@Vt z{3}SRGyFxspPNXsDIPYg^n;kM`VBgUJi>6AIzAn)X1U0BTB;Mu7~k!0UFrPhljLgk zW?x;RDMPM?6W;~6thj}Y#-QT(rS$LrEWyU@)awRW1K8pUFY>Z;>az@8IRCBQbyfy- z+iA@#V1V$K((6y*Vi!0}5NIO46)equ>pjN^vvOz$s{gB!O`5sJ;tQJp^V35QU_P>U#nu6MGVB5fiz5Lf z{w46DEBqVW__bYamj66pB0~rfIa^py=XFE3n5{$tjF;G8HPoF_XOV-EO#g1tB@1?g z(a+fW;^Z?-c2Aez0@ufY{Kd?1D5dX*QD3r*c5QToQnisWU z_R>-VLX1oDbdI1!Fw!?Q^e`!vI!H1k!LqI#7A9N^`h=P!1$KfYj#2|d2pM}j0CzlA;>6={U@L#;a z0x6Emo$t;I7!N@Nf61t{M`<2REy*Y+`q3`2wuEu0!PqwG9&D)&CP>rk-qNbp(Zet8mobF6!_ZN8vR;@b1CxI+%I z)hoeE{)Aal4~KPrNJWnV?x#%1gyNzZtc6#8CCzkuJaam_rn!Cx$MZdagWV79=34+% zT!hfWU4gp$m6Jz-i~CQ*~N#amC_ttYx;+#$^<0m^99Wev`hT7PR;S z_Ak}+pL8czLg@S2E3T?K?vU>C55Ila^;<_h0vuv(DG>Wto6X^_khoA6`pGkl%Qp=;L@M|Exb{;nsIgnQqKeP~DH1_s>YyyH~4$vqi z!h}2nmPV)j__n)KR~ws~2tVydkeE;Uv{Dp&2o9k}lD>ReZuxxGWVE-phdH`-IA3>q z+Jn*qG+2E<@=Dc%KE4_u|L*WVp_PJSjoMs9wqysQ&)Y}TxJm0r*u?YtBB!j#(~yyE z4h1WsmAFU_@pP0!(@&*=ys8$6aNkueZ~O#cPfmPHCGFg~Z~KdftsxM?bYACMm)512 zB+E}lHQx?ST@0D7S`7LSi`DBWd;9w*^@9pg_-~>1XRE3_q`s1xZOSdxXKQm;2AY#b zH6v{A969qWx$R_dX93xN(+9b90Ux4NHgixUK1VRIPkxNK9g(BU$SY>mHir<=e>CHh z1-VdhBE5JOk)Q0ab;Nv$bNOxxD@3M54W1VrrRh_Q^&9-VQADbf89BPkb)7$G!1jxM z^rNTw35(IWlFPABC{JB!qpqG+CfUzkrh;lypwQ5j(C*je$%gJ@sYl^v4R|dHo0(C2 z-fbmiV^!0Zx57FB37A@wT@L@2BLFJ-5-A{FH@snevQ7z^4{m0ma+tV>XLXBvlDx6_ zRV?4~Y5oss)y?s;jj?c55QSu77oZi-UCjd&`$D3k{rrdV&yBynw~bc7+HL+#ssyz7 zyr8M^d|BQx`bGdutQOoxaLI|CNA;beqVc_9z9e8Zps!80EZ8%^JugPrGeD`8xNccn zPy8Y%TRsh`3n>FQiX1zV`^q?u_tOcd*)C0C2^0+$ z{^h}p!ngIyKFFJ2vdAodL?r4!Cq|FQccBm6QWbqwO%1dqjT~} zpLsD^6cOKoD{z(xHUr7)c&^CnV%4flqBY$_W3%bfgkwyxo%GONxztASQ2)cJGr&4) z4S-Vu08-=k0!fIYuI%sb8yb`IkI#6l9aI0if26WWfP@kAW6)T0LRTh6s2wv6;k|RD zFGIyXZ~)_4A;Tng-U&3}MiYi13Os#=7=;oNEW496+3S*3KOfb8JnTDBTngbgj=))9yw z*&o3xy&CcOhLr(#HRW1ayiwHHtQ2}~Vec9ttnVO#1LXa7w0wT6H3&oTgVN>v+kzvI zZxP2C{6RAWMiyra3ppA~*I@nQFDJ4aZl<2!`kH)TFJwm8W)qI1XMH%kng6-FfsMcb z`j*|SQ^D-kaJF)#LDSgKUJdJp>G{RYu*TBE4knc=#pZKYc1alt>gLyF9C>`hui|#6 zzl=FO?^ytZO`U-5?r9V1iNbUzoIQFyo7(m}K?-}9OcT<;5=`AI)=;x)CdId7)nv(g zwmn(N<_qM~>%W!w!D36fQs&m%lozI=VVM4wuog$h77{nWAc|SntiS7r|Ac-hzd9m4 z;SwppP(bN+Nu!yoQAUT@-O2ddwGwiqVL4$0Phu-soDhgdrf8{&>cTB1kktn=0-+as zAPAu6sCrBEv(Mr8=hzTuG)u>tAwl15DTU`omNzr1{kO-Jrlpf=kdPstZ(du4=bZnL zes_U+!=QY|uZtJI4&!8@bK2Ny5)-vIaKWZAEL1lh%KF+qa}H)2HS3^@aJ!a?6*Qs# zJAwQIQ#wa$!U&P7qvmAx^8~Ar42J~DHU$W7a+98}NlVwIQAXE(N0;t?bbo?Iy@FXq zr28@ZuM0>PtU-`dLqwLAx3VJA?#9iU%8NAGq)qo_y|xU({|@U)1bA5Ro@CzUi=^FT z1W#cr=IBuOWx{F|oWqZ3H^4ng!*qf%&=uVK}-r|^<2#Cc@Cv%kGs-8@|y zD3d4eK}gVfC{wZ`(DM7|I>`nh#4sdA=?hHKjl&+dT!IxOR>9x?@T8bWtxl9?#heXO zv)6VUnsUq$^R6Mz>CI8+`x5s36j>xAg)hIZIkKf}%jO4_%e2$lX>tF+_58JXB zp=aOT{{HD72gjdllF1M2OE#{d9tZ|a52e<>*tNg;&a7WG8FP%O$f|+~*9bT>{1=qX z!yHV|MP{7R=_P6k+*b9I5h!JG1pg;U{P|YY!N_YWeIwycr;=}pfwy{2dX|iFDJeN$ z6bqwTboi^DvhDsO_U>eDC4b|X&mt1>b2XvDJLgP;Jw=x^xPN7Kf7TACoFLw~(M_9B zQyd`rm@dI(ee>5!d5mCbi~2sd=CJ@3?BUx~CqBNDk+t@S`C1G>E6xTypN+d-8T~iO z|1Vv1?JKZ+F9sJkJVcixRt%;@odQxxpQ)qFnJoKc)astVzfh3;VRO3gV5Mq)02Q?U z1U)e>|6e`<|5wHQXZdw)!YA{M<{IGjg4CJ)?9XD+zL?2LV-FNJ`c@i{c{q>PmCbal zI^pO%Ub898fG)%h^IH~@p zlTR}MCC%NEj|4DzvHYoM2g3YmZBv9@{j+w@71j;^xK?0y zn`P6!hd*%=usQ0h{E5j63|HKVdVbv)!N&GQb@)2I^}F+5o9gr<9#@{%e+@CTW||Pk&@TWE`;}-9TcYXHO6JX63)_-oHYI5gN__d9p7Z zhyZ#S6PJJNpQXgVid8uX2wIK0m>Pz!;SEs9yPM82{PkY`cN4k7`TaE8Ha}r%(4$i) zp-X<9_5c3vU$;+<=cl0-!8aJzA0~+z2UYp0k_wAC9HBB6o7jJ|$nC04NtMEf^z`&U zxA(6Fm%R(-6!V!-PjLBBRnt?EjFRXwu#}~Wk^E(5wwo7*74G0>U-L&l>)%wW|6HeB zKiFB%qa@k(-KKLh7cof}C}n5mV`}Tc^o0u_c7?2_$<^M(+1g(Z=&uU}ns!v<9lw>E z3#%~J_B=YgL)<+7_s0MG-5>zSE|=r={QA#O_OGj7!vnYJ=x)#?&NlUUru_eYEn5y0 zeo9+mwAT;{!!p+Xnbb@JX~dMd$h9)X3itmxnEw0)xgQ7-*Vktr?XT0nt@?gABo3Ut z)<0Gj!|ecSdi0p{%(XvP>3@U3U++2F4ya{SIJ3gqBsM6PiLsdJ&KX>FS5dNmcqc2L zux7k7`ELmEjYgGI9zVq8#DNB5c(KuOn$I{r`RkoCHu-X7PP09dhY^cVJg>~)+v-wO zyP-sVH8oTA$JdS}trb*3x6lefaAokz$h5@M{6EmlSX>YSW?H)_3LDFHhVg;KRKw#? zkhZ=d#6QL1a3?HH^0NiHR8_MzWm=IFCm>uI?tLx{y33}#oGA2!B;|eF8IDbVN<8~pA3gxZf*my zG0YD78te7|wJ9hi4chVI&-XedM9lV=7r=j3I<+N;T<*$@@QtudB{UU!#py;YhF+{A zPljTOO2bdosWSdv-Nx=)+fC9g^6y2mfd?Bi+lD1fUPtQMOpXhm3>M0v*GV?7+Y=C~ zsk(D@E!39bT^iwoO&Uf>^4y)@PQV_ZQ1s}u9UqNdj@AJE;&=+^98n}DzVm7Z?}9hG zx^`2fqExnTl7WmOigYIp;pV@(cnlUYGQg8Tq%SEfEl;Ry=R!t`R9v4|VY~ zn{9D#`5F@GhhO33apNf>TsJb8fiTnKizFnR;ZlRS0{HBb~b6*pd9P zcI`#?woYXI31NWLb9Tk8$`u zZ!dd%f5R&wSk^S%_` z0<9(o}P`lU~{HiV=t$d>^H zavzf4?gc0Nj$ydI`Sj(YubrbtaJ%_t^v-;0^W>|)4>_wDe=w3cRCaKeaL}TLlLG)*mKCrwisPKQfm)LbRLD9-3@GTf8<|jo$06d zqLE$P_+iocqHlZ7n}_43`{$n#)ze=PVEs}R9!I-#T^kv(XW_`9!3ovnY+G?wCE2L1 z>fXRAo(Z{v!;Isxp1sh}rwH!ZMl`x%PlXv9jKV>TUf3u}QBgB~Ab z3+IANVSIex(|O=N$osx-(7b$KdOVNR4`+W=R6cg1A&M7hHSa&G#B{ljAjFvwYsDH# z0PPUK1X+s}YPUlx7kG-{NAN3uXCc>vF<(#%F^-o0pkDKE3iZH{t6dflo0SP ztT9rru}q>-TD&b=`%iy+acIT;ZkD7uy-(LY#XopV&3e^Yk;<~KUySF;ZXzUvggB00 z=t=O}va#yjE=xW+MNd|ElAwkAIw<)EDwy#nN)HYlj88Gjc^R}J(Q`YzL6WcWVaq%1 z#Wx{~nkWH2$c(|d-%P#XX~xBFG9NM1Pwons)7^jrAyU%mz}~zL_r4RJlqb?Wz1;`@*odV$5b#CLDwmqIk(^ z>Rta6lR9#o-BUPCcERd!U8S(mITp2@-jaa+k@#oP*{$LXx8&S3b78QFW>V= zA6Jb$8NAMIK?h>~Q&mMakpOc4O0?hV;JRMh4N=n)qG7~=jxny>=L20%FJ>{@g%$_Q z!J|b#rEd)2wilw}ozVd6J_$KS-H9OfZapZZ&GeF6H!fCWT(=-~Qo9u`rkfKu4Glas zA9ZMjsX`K$efVz4PQQ4+fbJHfl9dj*$PP)=>>-!oN@NNOm|uZlv2tZq0jh1SNQ?&z z;j)7r!k6dQBo}!*%64t4e&Cg!4w_&=DVT*WZ4YW_@1*l_`~VZ{A48dz`pCm!wcDc& zL*pC^v$S#Z#_I(vv)i;1DFm;m?UEL#+cMgw!34e5)UoVUi7Q2J&{^>U|sV0BP~q^i`C4f6YQZ zL8H|5?dLoq!ae*Le`v92_CHcQf#eI6r`8Axo0V=w^Hd5(tB}fZ^uqt95j7vFbrqDB z((Q|l!l;~E*M2i2Tc)9~<}Gn4rHenzQJgw|{bShX4SnT}S_`pJgG8Ap(V|8U!%6`@ z%q#-}v{p1brfyABOyKyS6`OT5A8NaZa{L@~MN!;$MFw4RMA27)xu;5cAE0S$C&x9* z_oH5%?CH0HCSsU3h1wHk8e>{GiZU6(l}ndOvs}$S<|Wk24;E8&L@{d$d~IvletCadwO3Y&_Nyx1o-uVD73#bRBl}n zEt1CmtTPbFeQl|7shwgXgIyK7Q$P7l-{xC_XlaF_%FX?q>4Kn9E#PBM_amBW46&tG z;55BC4_j*Cv~xK1g>8SP6R8u_i4or3pM3n%Q52x~!L%k#S1`{%%;ZlP9l-S0(fJ3fnzmeoS%F)8pN(s-YCEZow zOT?AiF_h^K|B<)I9Mo@)5rgsE^@69E01xBx}^q_{9XJIlt<> z50p?(D?lEfBt@jZzthP6mN#KeANJWC%t_?++f}`XPp;b7Dr59u#*7=r>(E_X+w?76 zHScwZvvI=Iu;R-c?7}A%J>rzU5?gv%!(WS(5BBwQ?_(#dv#+b$MneyKwafjtSYC%Y z`O%`X?#!_=<|rE>tB`2O1iP*}Avwmsw6dmnj86OHG+(lDC&${@piKEcXz#+MkuXm@#Qt{Sa~ zO$TbRRJZ$?N`|*vP0v92Q7cRlpU~rGf)9$BmjG>?4)kJ{Tx%kY6G6h+-o|7ViU7kR zv3HWIh}2%XZ{+UJ)o2Ctjs_uLRR7LOw)@uLTHw7=CZpi*ybdIn6&Rr`pTcj}DZIFR zGnKnX2UJTJ!Vhn+-i6EybnfnE9`k-v7GT=-Sve2Xj2B3(N9=+ zvtFH5e_jo>#FNR=B1aG?g2e1TF6ut2bS0D({pnhm0#`eK#Wo`7#C#ID8q?5j{~9ME zMUl9w(e87!f9ND_e5Hbh=v%4#W9K$073}mG5d_GXG`bi!2MnZ@iJD@`_-Wy0I z(gFObr1vn2n{_~j86POIuHkM6>&HD@Vjc&}nzv_lQRW#vM4nk><9Zxly&IPPDOmXb;D{NVCA_jfp#~ z!kIm71>qi2KPNJ11Wjo~6&WHN>;V;!2)%QIMhzWjSxcpuYS@q;*e;FYW~ZB;kAX8^ zM(@jkYRgpAq^&vTu;k~1094eNGUk2Faz(h(qt&>Bwxz;IZ+e_axhF(7M{Q|;F2&!m zi;W_%wA71@!WKplu=gWcG8`X*f0nL^K-07?wfc#*s^(x_h(C^7o;;%G2_O7z=eOF| z*&|{5P2pohrLo!!C&<$CZ2FJ&*%aYdl6!H5zA%nlQdJNutO0w?)QYu`$hKe=f5X0< zz;uhVw4b59c?u1Hs|FDQ@+B5G@NI>t1Oy=@kddaO^?tvZUy^u z)Ry6bL{wWr%I-uXvGK0vVj4e*+h?#Z_Z)4|RdVGE$t zUqt2&F)n7vR6Gg`*5!@0PNk(wMR<`Xq!(Qwr%h-GAUr85_x~ByE|w}Ed-bIhF){9x z*xJ}m@1V)n$*k5pYSi?YC&FQyUnl8I3swdlH6qo5Nj$YL)~{wy%7U9e5nrj*M9KK) zvFX&fr8rEwxtVHts&s=zKU9E;N@S`!v-wWKo25caT;#ZI?2RId?;^$e6@{>iaW7fz ztk4mq+>56!$L*|f30|vlBwzmYx~JkPx8Hp7dD4U0ZHI(%$k%5Oe*#}mFA`c=2%5vH4r6VCz7 zGB|ByW9Q_-!YYY&yR2^hG3YABi|f_SZ!RlIjP9|j!)n-(vNzstn-sz3&&p-#_XBE+ z&f#S26L2;QCmj#+$0~zjMg2*(afXC+Bf&*#KciMrpe+X&yk0okbWoW(@7Gs*7Ryv1 zQ!sq>K>`M2FI`pGoL5SF_t8mDhNgQ8p$v~73BcDJr~&j_57>8iJkRErnShkw{`mp@ z>Cv$byK%9^TJBV=XSi9O6-mI-0Ncy{@Bv#gspxsbbei`mLPQzN9lN#i)%mLT0?EszfUZqk zlE5;(tXAqfvirh8<_D**&B->nSszeWiCQ@&-SagSkeH8JOeKvJVTdyEopE(3^rNn# zU3D4e=%-{{FG%N`>esu_`z7!uDZ!TzjW)(?R24(^tJkecN*(rB>(K*RL(b>nmT78J zxuQpm7JNex@6!swM@BMu+$x5Y5Q4Ye7*QBEi|m8)v3ErR_o}7G4;4z%Y0y{o*PC>9 zhs@quHEMj+S{=UmW1m~E5VrD&z33M#-<^-PDzIB(1&(qHA%9mJN)TA$XRqr}V1+x4 zm+Z7`%wKj7*UdKAcPjDIK3d~av6M4Js={SWkp_{E7lQlquE=jq#(4vpWCyojpAG4d z+7Y?8LoZ5M@nm^4sg4*Ham%D+YNY68LG!qHY+92+qmw|R7gPD-80o6mRE|FN4L%_5 zv+{KNWq!EZG_){GOh^>jT7Oi~-F5#wfyMQ-cI@yJ*ioC^1j^)J*b&di{#Y|_ihSNz zlaSWKvN=6n3qz+y8NfNS=IneBELec#;5yljpy&J@Z}Gz0VXC9kjY&Otzmgse<*fHI zEG`9wpB|G#3vQ}G6vD2c26MK8+axPdKAMEt1DF8 zSQ7fxuf~D_ELOSy()_&%Q18Xu=WUcVY=f>MW{nz!3S-0iOa|w&L zD`hXE?D6wW>iwMYn0LLn3{P=u{@dLb!XlJ-O&y)cY1(&gkW9k`3~Mspej9J|gpU(q zBn;G@-Yd)MOSO%}m>wj4g$PTW8yqj)nC%qO3Myhlqnwg09cL~1IzAO(S$=VgOgF4d z!-0n~g?;mgiEk_CeMN)#wqm>fugNcdP?0w%3jO6`bib?Y#e-`PFOiFWoI@ghNb_r^*7|97d-iqEV<%v>7SF zA+p~m=OU(I;9nhpW0?s zhZ&i#9E@JrdZ{E6YUXjTrr11;)y|g^dNwtx_|ogzq`aT18m_jSsk`2w?*@V&ZwT6& zleUZ%ATzHib!HST#czjH{?y%x6bP>-S(fObtv z>krp8Z-fkws6fgrO9X(oBLl0cnf@#^Q^06QkEl22=&7sp6 z@Iq+U^Dfoicba;PG?`T-m|B?^^xa#i%dmudY}Tg(WzR`P^3P#ot2!z^6`_1Gw|%!E zU@_l6KImb-*SiaHHp4^GUkUy92j09Zvp4o~Q&O>wyk$+h-orRI3^6~cNwM{$>u$od zu1}gPDl7f{WIIGr2&7W+GlM4(mNKYC;Ol=(#Zy_NwXU<`(DnbBFos_7l<=Z@Gwa zed;=hG0Bj+P-i8PZrgy$bo`z|3182?!U%)yg$YfXCqKa9G&rn_cGdj$MY9EK2la-e zcQ1d~hKM`=TCEC9j6Gbw5u7Dh-n!D+SSL{P?5q+zE0#?N?>UX^RUJ-FA?QahZcFgf zhRaX5ZZv-BYtrzAqiuYoF@?8CPxbGGINk62n#$+n2Swu4F@A7xs`ocI6xf6pzPxwW zn6AERC7lLrQ)pNL4sX@aXqw_!ekvKi|6~0DpF>h;!f4&s>UYDJprQsksrNPtNi++P zM7n2%-@22ax$l_D=knq;K17UBef#@9SC{pY85UXH32kS0Dlcu#0*EjGt@USuK0ksJ zH|P|sXp*rzJ-At#3A4g;22%0r;P+K4sB+V6yiG&2MwubjWk@{v|LFP(pg5DQ?O*|d z1b5fq?j8uPA-KD{yCo3Z-QC?~aCZyt4uiY%Pj>h2w^#nEx2Bq6px^G(edIaM>8|L# zo8ttXs&sbWo%C8nc6z}uUa?@FY;AY4FYKw|jrusS*L1s`s+4__VSe$zgEw%~*u!T0 zd%Hk_W9lPmRq-X11(+RtjV;T~S|zJO1qU|J$9u2d#VZ8JeGZE&(y?8!6PlgFO( zsDnS?|KO&Yjz1rPW0!qBh)1hIGd~Nbvs6mEXN3WzAYV|TPrrsf%-}Z-vT)CLSx2Q) zZkqBona4rjrTS}%SfvEvRMoGiH5?>#f*EFFxT`NgKgkS?r!Jr6J?)L^^TS37O2Hm* zfT-y4>R*jjtPX&5Cza$@)$pKSn2?@(z~4_960kzm7`A)V0lq*=&iRo2ie~2G1YWl& zQGTg;B#7APMN~d0iS7x^goqyL5%wj|4NN2ZVX-gOTXDPWtf?_A%$H}=h|D>_ym>EI zh~}Dx&wK0*BJg3_Me}Q zNZ^`%H32JwB6xur>x>_l14UTrlPzn)vOKTHu9z=6E%H73b&HD@KM6nK(HkiNU$!@2 z9CkZGPZPD1d(LT^sYd2TyVgKZBVr6(|2l(L3&KTWOXO!(kDDG|wQBF|75CHSnsWQl z=j*zTk%Y^y$v54b_NKRL_D7pDlVUn=1C22^nImri7m#IXkGGKZR%j`>0o`ukn@rN- zc`Dx#$-Z&_v5pIG*>3^Mvj*W^t;BC5{C|&M9yude0uY^fkKzQz)HM}33tBBTdv!Z7 zQw(mq8c;t#=XCj=wiouP?f`w=!}6_*@?Md|;W0e=Vn@nQrg;asnIAU zTmc9s@_~pJoC`5Uva>hD#MC!larVT@1iPn~cTVkb%Nf{o$!4p&Yl(7k$rFSWX4%%4 zfFA4HzABA3=FbGKCzjHicq9V^w)JOh1$@)srWSTX0=pTBly&i(1!vbn3)jQ6(jbfY z9}fa-Tj&fILanPPcs*pp1Oh2C%0U%m+!<9vxp}&&UKw>8l)jQYzve6NR^&)yb0&8ZZ>LW~-T@R3ic}Uk%Xun^mhzXq%)+m<5`fCpujG9B zg>mziQHqEiN_f)ivoq{ZxnA>5o;AQm`a}-Q*Wt=aC{Buh%K(l|_~5ap%@ z;B3+M(#5nl+X3OrXUuBN-qj*G@im?&ngWQ6#A+sjytp*$y6#4tU%|lW(7~P<0tXhz zYxFdt8LILNuYEtwaXMq=#OU2fo%|^1W5XY?e5c$!3%<~pDv%g*B!jeU-p7c4H7ZEU z3@{=2yZ?VQ#PPt>^piR7{1fVk!RYkq)NU{e6i!KBJdP>9(>UcCgS#sq!OPFC5>&XxgTjIhIa$=&PH*Xj=$ zCWu=Rt0X~Z4g|`8GLhq&FZRcN0)g|3yY}5(0vV^aKfNNp*k z`MDlO*Q}YKYEEcTPE2XxhkFVFDp=~l{c1pG`y(Jr$pz5*Ar-`846&<}#W5CWRIFYa z0fJO#ixcMg+k{*4ZsOm%hU1bK-hi7I*5q?5Py90WG z<#_D&$JbyymLo|jbq<}^hYMQQVzqYp0>DDjpEmj5KC>WDTnP;xRhT0~nFdkrPItTO zmaEbAHPV`lDI$%8XvQ+>4?h=ufST5IJ((S-Il55|wy$G|fZEVgPK~40ie+dN@bFa? zg`v@%yZ=1T6G|AD6(2!DP@vdOpNZBUX7Ho!n{*-k$jrn1&tmYVqsKMl7CyRezSlV@ z?`LNG<+5-^uqB<)a=WTJ_S1y1*gtICKb2pU;3Z0yU_#R!J;qSFwOF}pWC}`Aoo{|$ zZNCKcEQj>WM8vO|3b}Ag@KmaTgPM9X4{zGSv0KbWwGqptznqc32Og8! z$)MYgh&Cny&Vyun@7U2}^xIzjk4G?w$W|u^3krIQ0un2rOs1+v;eTyFFq8vU$oG1A zJuz-|*F;i#tzyd(fA~LtcllRC_Y5@&4mD7}6yX+dcd}>&_bKXxomu&BU3mZx8HXNc z!|y7AmzpgZbK^~uPxiH*PHn7k^)H1KB@L-hvql~}hG3a5IQLHMbNYc2jFqKkIAgZ8 zR5z{z3+-``40Ps>1?8pLNYEhpZd;Gs z;65CL2SYjs2|BubsHOd5z6Gx!>o}deDlx_D3Thrcx5Z3C#_0{V$~}K;C})r`QUeSy1lXUqtFULP7Oc4A;-1l|w1SVB0 z`hUw9nhZ(&mT4-0cJYkpgz_KC>~FCU`2nOh_#M|B&QhvO0E$G*EZ$aX8qn$!-3@5V zfept$t^UW#1$EX*fRqG)c0$dehN}7Agdl@sM{IxA%2zwmY*~kMJ z4*jQ{OX&=231{U-1pAR~Iv_}aVjAJ=9iBG3@?|gfol5nN#eXkpqD5GFJgHlrh1=UJ zw&~lj*cNC|2VPI6y#H%Af!tRoaE5OG2anYdK1N?f+PilIrGJK2|D1zA+k;cOlN`xh zlL4SXYfHDWqRxw`C-_3z>&0fMRPN8S{I@V?Q8rjmbYOYNkx=!V)qi5%eehd2!XZ44 zUt2V*L2(5O{McFWLutHPZmw0l^*{EfdaAEC|05hb!1IKye3f%q${QRFqz?(`{mtAB zNc>ibkrvatX{G+t)TICH%y%*XZNy+}cd4LZssuXnKKp-8=|9`}G4G&i5ZyGmKK*YU zLgbf#vVk^|5Tnoa|NBA#jA!5|v5b~@0xu{;^(d@!$y{4$p|hjQH~ME!f49?kiH@kGk! zQHnqI(g6|VHGH`hz9B81C=F_s;M}m_A|C9q5jex~?&~7oUkind-49kj{9tk4O=UJ1 z#yEPj`4YXC>9s0u!6kg|x2y#%Kq?^cK6zc8l}D8aZIF8bVb@kXD<6_#2SF;Bv)_{Z zh=gYBpwNe-(P9O?VxN!wASJSE!YQ&(rQ_A&S8iYml|bNdr1r& z->>th!($qiB>C=az3=WfFAY_4qbX3a$^x?XR}c30&!miurK~(J8=#u|f|Z=Nv8(HC zWcK_v`ls}nJ8%{9ehAd9%x|9MlIwApJmVcb9?n;)slV-wdVFVrvQ>Dv(sGUY@MmmM1M#eF6!}Ohx$DY0iNxa~Dz6~Rn3Ryb*}Iqs zmu1{CvRsWHhK|LrkX}?a(5s3F-(_!oTmcj!{@7o7m;+xLMI@c8Sc&2DkpEgIldNF) zr-jfB%c<>Y_syVn|3)zN8O0P8JdnALlj6!4{XLLpFgDbI+$5iQ_TcBvMo+ozOE;Ls zF)S5npxE$9_RVgJfPB+zK_phQVy*1C^ftF0pw55s3njaaWVfc70|hM0SDeq_7myp_ z0TU<}mNxG-9lI|QQC=46(o$|p-FcA^mV>RSPb`qZ!tC9Y-a0tNY!VX;*+%w~X!PU0b*k9y9cD+@K==%qE zr==q;C06hohUyb=L~t~ZnN?imL?ih|nf`{08T*3P22EoXF4y+>(t)u-F6@q~4n&2M zJ-eCO%>IuTq*h{DHtMJFyTi&b%9z0BXdS*eM%5b7 zCS+8+7M`XG%y;K$Q29y4$uX9xbbn{(#^n`9YSYP2$aV%KH!fJ49B~v^hsNVk|D_>rYyHF|bei~da=K;G@|d>g_{i|? z3+BirlmMRXk}obZ#5nH#F#$9L_`T+O-Sw{1-Nu3iM?lJf>FmxLc7wq)>uF{wg(Oqx zzW|>EK{7o$i)_6|p%f}6SlR$J20e}Bq`j#UNb)SG6r}~{hc)N!wT>Is*cI$Wi~g~} z6t~{Gv@UmPB*#~FALFSP8~!HVW9&6iUPUwuCwN81#*IbL%?m5@KzqsJmnbh)#tn2q zRvroXO4a?yAGXGUqfheIskjYqzFzg%jD)_Xw^8jCvAA{9#DYdN6Ru=Y#{BSIBcd9m zqgi&F3x{epxj!q7!;{&Mq4lvHh^+A=+cQN7)mqvw+uGb%T&@Xt?1#Pi=%l<1p`>9{ z*DR_!rr}fGL|<|p0jmCrQUWx(@GA9DIE`Ir_o8+;i`r~1{k*Q3_;B`EMip(4d1Al5iL473E ztEH5Ze1=3>q#mwG)U%IO)WVnMZ>tjUaAr_{dO)}+O)yq+);+h2jwq-LyTXnrk#vhP zKKKxvn9FH~gfJ&ID&P>48WR}TMehkTT}$>`&OSdJA%a=BtG866Xv^tDq^8`w1Xv%X zRxJC%+#G}qpC1~-5TaMyeeN2~nb{koZEI}sDtr1t2{(VUfv;?cF-n`kZY(NGzm=r5 z^L;fExzh4n(^c*e04GOFGuC!GNCn}w90w5$M~y}x)*v#cQkeZ3eLyo3-eQ5E@U*wq zW)pbY!y2a39MOnCpy4%nN2gb7jCNeq+g849Ze@BuUz0PTDwG?FYiBL!QHZ&s;25W9 zpo7SO(E(63b8XG`!FsnkQ5piD#8Yeg7KOhc5M^DUpY5-qcXhY7s&MVFz$_t$jH;Nv zHLtWrSjIYn=K-ubJG>HHI0@1soVcMTIt>rCL!IChP~R4%UG|)*91!?Z-@%IM9){v3 z(tDV_C@oVPl0nwEPwakYiF^O2OHoJRXEM0}SH4KJe8dUzU7gZAhN7c7Pm#{AspBEq z>AAaXXLWgwR&`ZcEk=ED%vAdolZy}G4D{txqGy)^`7DAEbAsNu3>nLwWY>xa{$nE(*}=xOE2$?p%}s%hk@(Xk_PZsi+J><4xEb{Z*|%G6 zKg<{MokXA!etp*Uq=K6ov|5FG7Yx=@0W`}%XxyuZis<`B>?O=mIr%2U-e)l;J`)*Q zxwH6f`ZAiVfFprZ!tKaSHccsbL^A{u{hlG98BI}pq*>y8b4D|xc`GUtdM(YRKv+HU zau1Lebx95BQOS9MjvXk2sM z8nWpv4=I`f8M!xj@P(;IyzLi>yy=Ht`%w<9k3o}ORqz~q2dUXPgr*O8T=?bxHmz{A*g}3;YU3E{;mOShOW{=(`#*2 zY9t!^3QE={9A#x3XZP3^r+Q3{T37qbeQg9-3PCJQE>jvT-eAV|ob>(m?t`UZS$!Fy z{t?KUI1nT`%wHlPos_@}Q?wZC*2L?Fkh1;M1I0*xyMKQ+()|Si+afg)4V$q#H4*;K zpjhP?18mtLbOQ=1T}+FOTFxJVx^+$B{QLCGdzFLds`RhL6T~SeGg+Fblo`Q9ua5=_ z5)gJ{C^jj|MR>udbDlmU5)bc7bt`NwO{8aUZ)FfXR*@ntJ1AOiYk4QM{8e=W9BiSvtQZk>F>of)==wn}N=Ub3@4-bmluL$^XEr661A}ds&%0K zs*bMY4iAVd;dvZaMiXxoCfHV!x~Za!>)E`2TXP|XG`M0nlb%1!8VUbJk$`~5Xd#x9 zAKKIUyiW+8TiJVjwA-zD#-XlIU1z8Kq9g}+Q_oE4ZgUqm`EB6heF#uCG=3kHhW_^$ zjT_9bGS({$gh{OMxWY)czF+>?%NCtLPUNSeJ0rDl#Fvp$Yqv4oEsP}d<}5DYOtjiW z9;Np(=gYY#vdUpOfX^ggbF8zNNCeZt5n7UzkmrJ{2Qdqcl<7Q?E>~J8KC}{@Js7 z&h=MUAdmUtqKzK+ZDeyYYxd#O?Um}og7<4#_kiuQD!p<}n#*0k>U#CaYrC#-wf3hh z@9KBy$!~-2%k&%cRu(c5B-1i2$i~yyJ@QDH3Ggd*4o+zz-RnN zm`#S_2d5_;3+LL0Yo=kGzuR;_rSXdabW0Zi5W!^8SH5VR8RAOs{E6ip<1w5swJs6% z=B3lE)4ohFNc$lfTAu z0LT8i(&deU;?p(T*ysRJL@?d>x&Z=5y^SoMMTwx#f#txlMxcv=_uKL%)^5J8SW(MOW@tXmLa^Cv#N8Q$XNY1{PN=OtUzwcyK z8?t3Zhnxam&Y!f4`9AB&w7BRnfbOt$x*yu{aGrI8)YhFR&)Z}T`LEqV2!>B#F3tQg zmB4Vw^Ujn9*3lLms#c1i6NwPZD?U%6GFuCCtvLs6zxr?h-}8Fi&NZOFzf;4^R4<1Hwj>BlLpHM1IbaJ85}x@#=z~ZB z<~or0k;}{=2TOzpB?yF1^fqIL(><^-JHG2e1W1Z(3tf3e!XCuq2kAcXvwtx32xH;G z9Xw!?k7&Lon402Ej)@f{ilg^wwenXjP9EZVrw$@+{40ABETPLcx)fNhp`RjFdP@NY z`t#V=@uxWg!#^< zP9@sdR}A}ifB*mgXn1_ia^f4Lkgl3;9DcCXf|#vo|Ccqwdx4WYD z4ezLk+qQ8|uvrL)F-fTaWDPO!Ul+ykWu z#%sW*vyXv=+TZlOgpZm*n z)m_g|)ma{^r~F{udRoHU%^iu98&kF7$pfF~l#bd}3GV)5{A80Mkae9mR}@P#G-Tzy z1(h820PVy`EwAa_jLx}lZs{sN%Ap(j4@nGJRmAo)XPUsrpY%iZEh^SXo#v}bTLRj>hZa_m(Uw-c)B~xL6Mv$a#RJ%@$*e zFrPv@H%asE%pHcvQct}{VpIks#q_V49PW^MD{~q*xq7c1ja?Yw z5Jsl6>t~_cwkqG8J&tvkVjk&mKC@bzkAvwK9r>$`z(po!0OV$4vxhr#3v+93>cdVC z`=aZUH>>K+ns<|V$~L&YC2wH{lmNW5OfoLpZOMHjA`czk?@uC=`5p5p3jn&xQygNu z2!w`TR=q_#LoG{-s5L!p4iQg&CHMUS@Uu#EN&-nH);uAhYA6oL%zWg!6{#F{g%TJqP~|)BS-G54?s5EJ z(LL_;=}FvOx97E?C{mI95^j4EwgX8{=$Sd7XIPl0bY5)!3$M2vqW7Z&~o>yv4rCcO6P2FmCzeX^~f&VfE*!zmqXh;k*X2o zd8!{OQ|};*7u?kGdE}+!HFV{U?8K4^S3h`lTSd|%ZAG}mtgPwGlwx3H+HaSu%d_no* z=|}5$Zb!gy8#e4BtW-o`m{EGw-YtRr~EA(^Bcw;-U~Rd0y*vB)UWV;!&3 z6LywGWO|C%kDA$s3L3(e2o|m8;hvk+vTwi?yBP;|%u(tz?PWMgY;&E7o5$^D=K_n? zQ22cBA72Czxy;SyVBt<_UniU`T>l8V#!%#Mr=bw|J5uO|;Qpzc@%;r;VGJdyAc41x zbn;zXeJ*^xiPAR0->{=y%|CSPaXIf${t8|5klzPVKE(-BvT^Z^*7n>f(YaTiW^0kX zY{2gKXuamCdIZ`}%-T^dz_?t7&w)r!Q*;=x1TV`wa0ZY*7k)n%owj8^B9OZbl@=Z6IA!-HbsCk|_5LiEN%BtfV%hKYF33jht}j?7@B za-}Ae20gZS&w26)p(erH4P!S*_=n_IoM<8~p!Vo6vW7ZOBdkI@Ii@XgCKNbxKx z@aOhPH1xgy3%CWLDc%)EH;AvP$a3tBw>(&dWm>KxrWHG5Iag`ylV`MmZkG6LtQnGd90NI%7rDAY84J z?>7%pYz0{N-7sGj31FWPnVRu}mH}gvAy6yt5e56)I6gTOidUT(MuFpzpZqFZ~o=04d(;+$E!$#$GO_etxvBwZTJ|G;)481YR~?tPJxTa zcN3k_1tRGtsz)bwEP*c~+ZVm(>x#y+%X9A%50A2*2ny)+%^K$lOZYy?0ZK8!i!Rw^iq+%ZwihTWK#_1l6{nzf>FqpXcpt%iW9v}8aN#k@ zA*?CbqrD}9NKSt)jt?;EblZ6687#Z|mL^Hbu}_)BC~1FTz+8^zV6X9Mm+y|vRVTzM zDRv9!fqWP`M|ZHweeUITX-Pxp3?7ZQ34H@^L3zs^p*2kiVfPtbdyTW_t?L8|hk)*R zEG68zxA=3a7+aG+RgV1Nd;LZ_fWCz=a;1>ae+EFm2ma9v;mJpL;!M*5# zn``qD8L@mwB*HQd0$(2pFsWeRxQzm7tH$8@YXDUOMG4os@Nvj zBiKwVlgEBLR~jD z53$XH|8pC;hS)xWDq08$JAW^ORtvM{O`x9f0Xp+5qqFF(|M1ap4!yCvTvG(!Zbv+y zO9lPDd%rnqJp2p~us8M5`*seHAD=X?8sfqu!rB+DX!y5MR zvTx1gau_M`62L0oFp(!(f7LPolKhy8rl^8|gB0k}fZyCxCPaGZ7h0ahG#%V@cEFGO zZDUBAG)u=3aYw;R=Y~ledg0kM?!-K{oLrqu3b*sW)<`SSq$q)*c^+{Pu>^bCfPPaVwsaA&hrEmLK`CfwR#7f>NZ#ulue5G*@xvYg!W{+K{9ND{a8(^`4 zhw0o+J|{C@X|8|C%J`<5Dwi%53pmd(!OYefV!;t6nl_PeqgwDsgmCYrT(HThu%`(b%o{knKF_oA_jz2HYVq?^&1-*m;0zjKXcN?R>eZg=)F^Ma zG`csjo)vCm+NK1fG0Kld{%q^L4MurPpUhEv-t=g%Zec-RKUJvZuIDe2kBqt9xXn#=~MOWdSAxa@;JoMlL{X89~F>vaMzNLKC0&7_sM~pW&Hl$t*^ep=v|x?s76QR+OF_ zFJ@}xd>1^KMQWs_RVwUzE+ZdY654QSVly*s2-);Dka}Rf*3a`OK2~>$pfeEg7~Ja> zT(*Cd%Ry=r6HweNpO7jV`PP;QT#s}()vUfPq7p`Alm^1PVjFQ5VZj^6fo_E3n;Z)< z@cP3W1_fiHE>0ztC@SH-mgcx8^p!=$u4qPepOh(mJ!+@Coys!SG#UVOU#(sduUcP} zn71o0nr>hYQqMamZxao~9L**VHVBBhb~{gN+Iguzw$;jFR94KfxGb~gzn9@U**N`* z?7Slfg`0o6pTvF=8~z`34l)iuc5i73YtA7@FBI+y7HRvl1%|UUrec;s@E6NwQIwt*# zm0$Ap7V0P_1qJ)L6vZeR&A$brOVFH6TU6nB9ab^{+q|^Fk(XlyG?}H}JI!}U{X|2H zA5kQSk!cl<1WHz095HZMLES&|dT84w znKib?M@2D|QtCVC8Se^aK;Q}pAM;};t6JY_PYR>lmC~WMx|edqyq9cpc??!|X~rK@>ADDm`G$?^F5btokTdwaG2-PCB8q zWJA3qiof=K3>)~TqXlY@p_r~xC5$=uz^(fUQXl0R%yhKO^Q(CWiK_kpaRtJMe~OeM z!r2@!`D|{(<&u2{FP8vCDg(!{ha3;5)6B^#s>DJTt#O7Vfm)E@3+oux3Am9!%GDX# z!vy0HjFvjue1n8^AJ>muS5X8v#^^TiBe`dZ!-*Ca&qQz4a4oBFkq5;41apdI+?7?o zG)OM*)4r;GFHUWhOu9VtsMF%e&b&UuIS)+))uAMRvI3I^I=r_zd?C|(3DYF>(@M- zJ7=72BMLUy4Kp1EUdq5UNd1d9&%~r^mKy62PPUPFy=NUpK%BqpzNbvxVG%xOKsZj+ z-W^j%Q8p(AP<6!U>chxxkPltV$lKQ)IOA%INH2j>Z9I+Ea%Xi%<;tk%$75pq@$A)V zNpmX+0QK%M%li>1v1?Gh44qPgkDmNp){5PuZZ8*5U59WOPqdHR14AtD%;w<(%W6At|6+QvyVF zICfCV#N8V8u)VZ;gFdTgL#$H1opbu9qo`hf$q(bSFZ`nZloOn(R>zTuk;Seu7ose4 zlU-?3?))sp^u`PrHcX6Dx2Y-3pvO2tC}1nHh&UuQ!T%svJ;k@Pu48>w$U)F(gNZ%G z(RauDv0Q?!M{FdxdDJLE8%{L^YebYYpWN1tv5_H zB|gp)Dz-ms7T$G)ym{N-*D%889e#F!moz_X%(-E^6!$Ksr7Mq?PczX}G_V|Li#EC8 z>r_}nun}d44u0` z6DhLqX7L_0lL8nz(JvFghn(%fMOEabpMXL(oBS{y(X`e523HT%W6r&)6kX#fyg&5N zzo7bs=uQGyLkJ4ZUlua2+qf>430xp0a3Zr&bh!AGH33(a^?_SK9FvIy%L*o=tUOA{ zruVj;>SQIehM4D<`-$mG@mTCB*&ccUptA@_468NKDPzKpgOVUrV1|Yx&j?+_$C_C04{{~cV!FXc&N#F2q)wIW$x1%_%xJ=H~HQl<3 zgOgPy!hlFA*9a~A)}v#>VmLxqY`s!AfmcgGCUVMc94EM5o9s|8&>z%_>;aY#jP8xJRR<%n5DEGW>CRyG&A(Ytc_vu^I{ zxfw@A(D_OjfEL5HrJP8oH*P41PxG?}(U_>a-AIv%21|yDX86i$VR9EQz59*(7jxlS zhTG3ZFLzw6XO6l}*j`UGB%jin8dy$%nCvTy8MjpoAj9HJB0nwrQic03@%p)gw~|NQ z;JgJ)Nvhp#`e(_W+oiKL3Gg;S&^n>qw2FDZc z^O+IVKCk37u;|Yg!54=+tUG(Lnybly*6WviX^}8S08V}eIH=-hD;A@iV)K#T>+7>6 zU5we3RHzn!c^0u6U^^5kQvg@_!zGzMlF(4Tg5CC&8Ag-aJ4x1^ZS>^{_{HaoWTTl} z-ARq|vHaPMc|3hvmn2e}&!d3ye5j?m9PTV15f{v0hoQ_?;NjEW?zEb)?8&3;CoJ9> z#@TJ8L0UUBz~>j92?4UnDHzu-!HJ`Qpv;-pbb%=z9I=Q$s;9W$f%U+0FbU;Az|%4X zO_KPPiJivh!uO;bsr#|;6|%#y_(K4Z5e|7?FLO1eevNxcj51N=D`^P0(b1?k2>J5` zh8?(1UTnLt{S^PTx9M!SToH2NK0rB%DLi{N@C!sOqwjsq%Pla9dWcZLh?xoZognn9+{^AT`2hrO@kp zmxNIosIR_rgZeZaK2doM2Q&+ZMi7rbUL)4Qn(=$OnH}^YEow*1^SMyq+f6qz$|4_Svr8HNW_8={oC)p)4<3gp@e+? zpIm-t5)Ng55;vocA(3M8oTusoI|M18s!(QMwO|b0M9&PAy}~4Df{rK?lQ{PxzrYzv zYVBdq8L1YB6vx9F!t;DJXUC8-??7&(nrO3984LRA4hvPIrEr8czuxufZlFLw@PYze zjj`2~-ubHPBE7_qv7XzH0r~zl`c86p$>3>tAx)ag)wTuArbKAN3wldc{?QrsQRtM0 z=&Knrbpy`M3>^D#HEErRTFLY7C7wWnYNVr-=xyl@%oF@Yzhf8+KGECI1McqkmsXPT zWOAPtRw;hkVv$y`UbIrWr!@|vSL!5n>1E|zP=yvj5&hG2p~myZOJ+q$Of}4_&%Bzj zXJsQ4Il6uKApNo#*xVToUakZ$VtG**Vo4A+uU z%uRts{Ot;RwBP>KBsb)F<>xI~C)0Fd3K5!CltGZq9?=6tK1?}P-E#LZin&``m0`ZH z@OcAX2hZCcn$*6rXgoN-cL&j>CWwEIL9A6&Ivm$Q4;0Vk4ECmDZ1s%`X5C8o3`Ae1 z-bP3#%}7FCI}UO1uTv`}F}w8>uf7z=|>Wxx8pojFfTJc2M7=2e_KaIvl1 z-i^!$aAsC;LqgTjnq44!5WCh6sPFtvRA9IKQ%n zP=vF$3q3r(6`s40t2jMgD3h8j!u>v{b=vf+-ji}YnB@f)aI+xk7L>?nP)ZM;)B+TA zc#46mlcmQ1K!zx8*%_iG&Iz5fkd_9!o-XVeCAzcW~^m5hZ zB1v2W`hcF_gQxbd6+s^k0X1q26(t2Drq z1&*IRJ#u#h0=7-u37r=C@u8u_KL3$woK%Obv#saBiPdrDpgxusu^7U-t@D<8)W!an zNdDq4?GRTzfvc@7u+zH6T=;Fff+}18jV_TT16#CmxLC_4cX;1gFENpGXV{Fj)(Yi< z_QH}d<^@E0cUD{N+Iti2>o6teMh;XjRBHI+6@taG*gr{aEq)0Vk%j)QQiU&!MOK9i zc`JUs7VfPY_kX=CWeR3|D(kaPW8Q@~8mt~Zj|hagtPafg)>GdDA~{@3C(5SV7w$=q zrf1aKSO|4?TL!&n9t&2kpL%Mo;{s8&Y>(^G?8?n%T&e?{e!sh}%J1Tekjb^yjDU)X zUUt{B<@b17m`fNTT%GAtEbV)kjLW*K(xGWlp`1w6#`0 z0&kx+;_L=a=dDw5nmJ5Pc&tUojbbT77uor$lt9YFkWA1f*09Z@E*Ua~($?fE(e)p0 z3^xpzI2+=e)x$w!X(>>&&VJNNc;bS08$l)PD(bFsL406hkQ>WwfOX>5l$zH zw^$3ipklJAEsa0p1RoaG6lS>053>uRi+*my)@^6~)8$7k%az$<#cHInPmqg_ycY@M znM&90T24Y9q7~_06ue7037apJMmHmTmzb55SaI$dbr%P^Y^;0Qcs{KEns+GtD6ooZLI@)pRo1o( zVn_qZt_I|W2$0#x=YKd=F_|1&En?zSC~(=#U8>yN#M)pHPy@FzLY;Dg+yh&Gp}TBC zvq)haSX>M>j>$J;Nf%L#a^p7#JpjhAtCr|#!6Y4D# z`soNn(XhbZtgR&ffHUxl9K)UMu@x>%u24a$CrRx^z=tykGSE8O0_6_hFlhEcwW-$b zb5oCNRP!3A$wwt1LXCA^H+}g5$Fo}WF2boyKnpK1sN(6{Z*FAC@>thDSqf30W5;zl z=lH4L`A|kba;g~=!t>0*q&#Sg)D3QLsGFvxU#Z+FoAg3=P*Y8jiOLbCK_Kr*2^SMl zi^)^`v}~>6wI2*(3B^2QPHK6GVxD!%phWhv#uBw%xjt?dDmB&j&RC$d!6Enr+*^%Y zFwHI^H8Z7zNDcp@SGEGW%XcXyk=s#X4W;8WiE7bALui|PVyZiKGb!*pQFA=LtHr!t z=9~F)!moh$P{c5NS^E%>qo#6$y2Q)31pE(vn@~nTz%QgtcOlPEbDlUeNKmXqo5@jY zOE?f0OP7D@a$Pbhi4RkKW+vzM4Q4`wj=t zLM8MsP;r@zb(Ef;d238{+8TAqlG~_?4pSjP5eN8%TuT3RiX6{Wa}1~FxHSp*`pYVt zff0%W*Wl;RVt;qyY|}4015mY+1Xs&jgunG=5vlJ4?|)U9ij#m#jtYJY$8xxuj5e^r zcRyG-haxh8u_MQuX z+f+58ay9GN-sMd2G#JQLX_`2@6+f};+}LY2bG_?=R+JVKymmu&3MAwsrJS!16A0pS z5uo|y`<~(qry%jiiR-GdB+*lgjxr|OhP%ACVP^(9ry;5 z1ctu`-KDtJUJDF#N^t{nc1a>MZGP(V;m`9d+pb)2uPa{`C+p{R&J9&q81qDU6agih zvA^QWFKrWm#pbq%pIt_or~_y|oKkrcZfE>Z`qH|yZ&ieqnW*;D z!*?;J#PU?@%Q^gVMqQSYwjz`31t>Se?J_!>9VB#pbg10w<6B-`9J9VRj>U|D=&jRv zaYBMd^Rr%eo--0-l)8n=Il_DWV?pYGUx%NJTrMB}ni=zc!|uml@ry7_H|t%Fxk_8J zeUG~!o@bT;5~z%RpeYjX&-wt6od0+nDZtsLB<}`fTJh*^>@b@?EL z5hn*!FD_v#seL(h{7_uc%75G`ya|>}kt@@GJ&Y6D;HrP+O<|LjqxzmYMd|I8AE-|l z#%)||Eo_rC*Mz-=xU#U5r3zRAgh>0c2N}>s5f{@TWn_&e7rUFZ92xQ)hnQhas75rF ztWiYIT+a&hm{kd?AoOxe&Ci&vm360XO5E^_d;p|c?8kLx#0OHoev`(0HLlNhehxj1 z2W)_u=!dy;Z_h1YZD?^6Hq`M*o^8hF16Fd*fz(s`)!(~DRhF6xP((fr*jayX!mOj+ zPSy=+wp! z)`U$6xV%i~f@+n~=07UC`23I13qI}Vf%OU5b#gor0 z{)m&2?HAavE&Dcf11`V;KIiG$?FGM#IM27xFx&sdViHRZ8}BCO?xvn0Fe#VYaaO2*P5<*L?Xt&i35&;;PPLo$)`8e-V^9FwkVtp`6hLA{;YI~ zrwt5>Z$eJycvJZ?(fBtDNXbtqe8uS`ldN zP;3VM8VZ~H>e#bT-vUUeq!%H2{@O^w?CiaIlnzrPfL)=-17nCK)2RCLqeHD0c}oY< z;PeASox29*PWS=3-456F6aDZj<_SX&$Dl88F~F{!Oqr1So6aXinF82z+fYs}hV1al z>fs%_PuGm>A4730-%p?6a~bK%k(MA`=s>@gpzozv7SW@kEO-o`EA^p9--wo3%71ak zRhE2TFzpSG%a@GYT5|?-&qKQ7O`Um0kl#sG)bHcZdWuQlx|uI!JCj=brnWbJzFt`}ys) z*Q{ASSUUeOBS4Q;V_^2}&@mV!}B1Sb>(m5SjAu?@AGPvCR)i zyX-x+MW{Ex+Omn>so?f04jvwq0CT;&*tJJI%j%kih5c*H;5#rkrAw8u{m80>%g%>* zo5~M%<1hsV#-EP%q{^0F-F7j|(hMaKS&R|t1NfF%Q=wVuvTqiwfnBy=&^Yuy!Q8l8 zYu_=gW_|Fgz^&`==W5Bv2C$zbhQ2h&<)KI(yh~28@>_jp`Zm*w^ee^2Ni(!h0y)m* zRn!e7Nvi{MQu-ildIqi^?Eap?ho>e;Fy3UgZz~LhW~)BD$~5Zwc<$E5NKU>z=MTSV z7dg~AS|h@AYz1%60uMKGVsHIkP!UvF3`X2qL(9^l&$DG(ICO#>i_Ja!3f;rO%c4sj z_g`ywLiC6_egu!biPCxxoRIlfGZ|tCHl?Q~$GFibM9 zS3;XcV5P8lgN&v3Dx3S!fN|NsPRH=vtrhS>s{#gI8-OXP{<;kR*ztBGB4Vlz zfB)Jp9fX0)-NUkDepN#1wnj4rd4%2Df_%$2{hTMnS<)_k7Z!dj>sw$)DzqMAV~@io z-}p?rO!MaYz946A&~M^olISt{94E)s&6Gry1 z|G`$CV3^stggU3E`G-g0x1Tzg;>XXdZCa^LnLN-Ku*WnZ(~!QKK77SD-)|~m17~oO za_~&o-m{Lu>kgXk)KTPn8ij_vZCiSm*7`d3zS8Iw))sG0;BaIiolfJOFJ6P+GVZbP z@HAhqaVro8xz4qMQP&ql8N3BVG6R9$ZDqZ?3$qA?McgoGZdFu)v z0>12^ux2I7VbXyqawR`3M%)g3%nr?@zMFF*=NZ(8xpYJuS8qcs1E(jqt@UER9eNrE znwMGT<10J5zf)Gg&&%77;?T}+>2do^eP@Q zB}P-}nnT<~S2bQAd0r!J!QW`-G`NIi_ty5c2 zZXJ7inWJ95R6e&pKSnkPz09Rs<(ZrDI>59G9hA-r%(CX7=^!T@y=~KQ8=4rU3}=hC zQX1#2FU;Dbl=_=z#xqL61DGDn z&kKZ2`#KELFg4$$UA)6n4%V<;r38y^lcUPdAG+9Ep%RTgT?;-CS6qBY;G7aE3$QGM zETK@7peF6G*A1&lKNVF{`u({>^Ca@-sUb&4?av-)h!P{%vtsEgBxikNp>?6{GdnYw zc5us@rr5QYHN+Lg1}90p>Gab!kJ_#Dp;@OAx{;f#e{%Na(eyJlMH|u1GGZ=@i)V)J z>1o#}&Lfp>-hNqUDkFmyBzPl3eC^cxR2OpsKvPzBgDi_=KIXuJe8#Z5mZN0q!Cls5 z;9|ND;8_+u4z+G6wbZ9sn-tnSPEs`ibGtjw7M-8VwJHZc2SVB2hTN|HRyVH@YXw+m z-D7$?V9_SI3SU^NX#ZANpvxWVWW_6c-YvXdDgT&A?Pc4%+#giHu!|cZsxJgbKDPX= z0l@2`nCRv`G)$YicA=9?(ac|<16U@qJbd>>X-@d(*hs}2<#Wfxxl_xBhtMQYu~=(1 z`9d^U`4$dKTtrO!=n8WX_g>{6kREk4vWikh!KP;`Qt#4m6>f{jhpOM>NE9N#7Z@{ej&Bdfl1`x+jdL}Y} z$MO4MVM5AAx~1bypI^sA8z`*fyb^xQ z(G9h&2?0~FMCF(NY?J?)dG>&)L@;+1b_#gTU&~SQjZyH;%m(L-8%S?|<5}(UY69U9 zp(5@Ras2lm{@?R`N{bGnhK*zvX=e80CjVXhpD$4rCQ)`zR&)AasE|aXCEi(Hl*HbI zWR%UCY+yPM%4PxRwK~%B_K|$+^+g7l6z+m0D&fTW4$|B@okXXR0k8y#2@&+#5Eg%3?o^h_fdu$ApO?%cGjd@GIyobp+!ittdvn$*rFR|_{v2g>5=gwa~Sg48h*P4KI zl*|6~CPbFGomS$R>ZT?hv&hXp<%IZ1*K|X9#jH00$YCe5Y~0zn@_zNhe}sFETp`^k zrp-u{ezBmZ0bg-?!*u{==+}MQeq|@)T*s?+jOZWiS<)Ofz8JmPX{2=Pdc@tO_;0Ak z&WA1I-NJ6f$Rv4^r;1r2@DaXihg*)%`Ze$713d1Hs0Ru)3Pe{Ip^W^RT*?QRpO1K@ ze3mH`0mSY}Y<5jP++6cQf8DR{zSflvbWq_hSH~*KVDdMdBDG2b^(>xvccC7greN9D z!~Py=7rdp~8Y3#5&GrK6G{{O`Haq3R@eOB)7h)1um*8cKKH2KSx_zqVN%(7r0L*T z1WN}M*f~NAYSabkVl6@RStiAi3B{b7+$NcD^&Urfgrqe+sf9uQzJl{B&q5~dXqDb3 zpTo5CAq@cU4d(7-)$Tx_Wz4)xz|idHOgY~(+*Vo>swvL{e`a8};#jg(1C{{E0lT#H z^(-pK_3rl0ZdVTuqNO0mP{9&=vS4`kXB~)>$8?z+q!wYkGfN3bK7Dhh+$eruldm0a zZ}?-@31p$QaL{_mJPv_g{YjQ@9Uoo~(BWWQK6n#tfH@fmx-;40il%yG$Y)?tIiR(A z%?e;Rj;U%YLzGq-2c9(XR(yLjy!SD$<1tb-NaUkm_n<5GPkf6?zAV4%D^tjpo%~hKv9FBiHA<+F$g8becUF_o zZ{~;NcEV4x>&jxV2vq|2lWxFs$EOQphG8zGwDOjFWYqCUv6EYi)S-e=1hr*X!H=W< z9hp`~$I)B|+dEg~Y?zgL_Tq z1ZK+_lW0WjY|k?)rB9687GvXH??bc&TF8i}nCNV9$0BD#u~oh4HI%Cj2aqO#pI`i|Wi(5&7Vp#5U)iI^loXjDkmWnT*%<^Bp-=8~fUt+(fkmJwL|eb>8bg?3u34*-qsNRvrlV zao}&;I6`$1%63u?<(ggqm2FxzzLBe8n>dGRBo`)Q&PATNfEJ>50#>fC+aaec+$SBu zaS6OVm%s1^2+hw;=*%`Z*u+csJk=SJ-r$>HrH?yZ8^MvM3sAx$&hQn2gBDOodb7!^ zSJMsqUrI-(`(zTTfiMrJ?+t@U?$sMQglfOoWvtx;hCS z!f?&UUT_U!=*d%Aiw9F@y)>kc_>Yv$B4miBBsxbz+N*4ubWSIKf!Jz)CYA$Kz196# z-lNZzWqBR}(nbtNyN?CX44FLvlmNfk@S=~y3{sM82b=Q}IGNkY$tm@~N$o(Jk(@iV5?+kLyHd@#wOL!&Y|^hk%rV1ntM$xtNH=(A)zZas z&ur3E#RY#pHUV)Pg*?x7{!y<*^LaUa3JFc5OIVnj_HMd!IrRtDkl?vo3C}y;sx?iO zJSB<4Sx5P4zYiJ9_tfU3CRRCEhlxiXP>k}7{P(!}!&+=0i`DVt4ol=dZ!Ft+Qx;FZX`m>cV_ zVDQe=`#lCV8RgOEyeeM;yj*S8Q)S$R*FP40Xj{thdU4WUEMW5ZCH)%vngEb7+Q-gN zU%X9fEeD6dJUGQqm*y5%-XLN`X4Hj?Raz7&=>Gr)v0|+=jK0pBrUchfj^yTt*hM-n zN#z_8f?J<&@zCDu1$5n-#%*Ir9{p7*t;?6s8z*ZiS~e5TS2ScKO*j~#h@Ro|2AD=q zlkn#Os#hlUX8N^<=LgG|)`{vV+QfY&#kLOgLvrjvtm&9gyEy`c` zGC{?S`~3~>a96Vj)^EKy>+OoWNvv6pfa{{x#wdyke(NtR{Ik!Dcs3{-?Q|E6Cy`7H zSgW<%dZWltp)qcYutPqznmF}(X$9t$AR9f86&I|#78)tMZKp1-&8zp&B_T=w{qEj= zJzVD`ubWe9zRR`6Pv4uRul+DA97)^@otcyxDI{1bZ0)@_SNQ%br{kAT-KTr&7*H+H z$Lyw_xFR%hm?xHOkjq_<;r5o}uf+r9BEhBRe*V)>f{9;i=Y^w~mCcv3{2P}ZQp6`S zCk~bfw)7f&9G*89!yfcAYHPLI=v_{vkxS^o+!VfjTpMs;A#FUR8e1a(<$?*#9ALHq zX}>G0qAQO9w7>nkrBa*)QK@dPC7P5hx92*lO&WX^Rw(fJ^%$-h_efO3oi{s?*YxhB z_D*2b!~?y3ru8@n$+8%rhKa7mMrj7Y!oa334=u+G1VZFXVa+-WPHY=22_h*}a6B($61qaZHgK!_Zwb?q2()N`#wr5VRT7g+303?17R2z= z@>*rEl^#aRcA$B$>4`aLB=a*MmzD3gjngskglxPU^|1L1%zne#bwLu?VEVn7CZvLU zj%FOsgnx;?SKUIRkesX?!_LF`s?%L>WlAx-jyj<^n8O=Z{8m;gVzTcwX|uke%bnBX zAEbRMQL11wgqm2#r&2#-60r0A@`CT~!-e;%@{{?dG4r0A2SL=LTx-@)Frav3q;we(0s)yurFGZIO72nIrzI!#+0gEE>$6Fkt!57fMrLQ(WeU8Fu%*?)tu&x6LjpNC7WO#E9q8xa0C!D#aOjB-R`bjgaS>s!4 zJ%=aSb~QXc0C9o+&*{V;L+G!hl}|)_$_T(~`QRBT;en;zypgVSA+C@su-vWxhT)pY z(de*SVP(W#+O2rb;>@%CDwi;a^TG5`1GsPANBF`wPPc!c>0ibE!*2hMOaK4i|F|_W cg@Xws5jj`UJ%vmZ7l_wWCCw-0k1b#Q3nGwg-~a#s literal 0 HcmV?d00001 diff --git a/docs/_static/unit_models/fo_unit_model.png b/docs/_static/unit_models/fo_unit_model.png new file mode 100644 index 0000000000000000000000000000000000000000..3b896c53ac45e2ecf8f0297979303bb39df912de GIT binary patch literal 90743 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 model from IDAES + * two `Separator `_ models from IDAES + * two `Heat exchanger(0D) `_ models from IDAES + * three `Heater `_ models from IDAES + * supports steady-state only + * is verified against the Trevi's operational data and modeling results + +.. figure:: ../../../_static/unit_models/Trevi_fo_flowsheet.png + :width: 800 + :align: center + + Figure 1. Diagram of the Trevi System's FO configuration + +A discussion of each process component is given as following: + * The FO membrane separates the feed seawater by the concentrated draw solution. Along the membranes, driven by the osmosis gradient, water from the feed side permeates the FO membrane and dilutes the draw solution on the draw solution side of the membrane. + * FO brine exits the FO membrane module and is discharged after heat recovery in the heat exchanger and after chemical neutralization. + * The diluted draw solution (approximately 40-50%) exits the membrane module and is circulated through the heat exchangers to the coalescer, where the draw solution polymer will be separated. + * The draw solution is circulated in a loop between the coalescer and the FO membrane. In the coalescer the draw solution is separated, by means of the heat provided by the waste heat exchanger. Hot concentrated draw solution at 80 wt% draw solution polymer exits the coalescer and, after giving heat to the diluted draw solution from the FO module, enters the FO membrane module. + * From the membrane module to the coalescer, the circulation loop of the diluted raw solution is as described above. During this circulation, the stream receives the brine generated by the final nanofiltration (NF) process. + * In the coalescer the draw solution stream is separated into two components, (1) concentrated draw solution at 80 wt % draw solution polymer, and two (2) NF feed at < 1 wt% draw solution polymer. + * Concentrated draw solution at 80 wt % draw solution polymer goes through the heat exchanger and enters permeate side of FO membrane module. + * From the coalescer, the product stream is hot NF feed at <1 wt % draw solution polymer, that after pre-heating the diluted draw solution as described above, is pumped to the low pressure NF unit by the NF feed pump. + * An additional RO filtration step is installed for the NF permeate in case product water quality does not fullfil requirements, with special reference to rejection of Boron. + * The NF brine stream, which is a warm stream containing the NF-rejected draw solution polymer, is recirculated and combined with cold dilute draw solution downstream of the FO membrane. + * According to the efficiency of the heat exchangers, both the NF permeate and brine will be slightly warmer than the original feed seawater finally resulting in loss of heat. + +Flowsheet inputs +---------------- +In addition to the input variables as described in the :any:`FO_homepage` model, the following operational parameters should be further specified: + +.. csv-table:: + :header: "Input Variables", "Variable name", "Symbol", "Unit" + + "RO recovery ratio (if applied)", "fs.ro_recovery_ratio", ":math:`RR_{RO}`", "None" + "Outlet temperature of weak draw from heat exchanger HX1", "fs.HX1.weak_draw_outlet.temperature",":math:`T_{HX1\_cold\_out}`", ":math:`^o\text{C}`" + "Outlet temperature of product water from heat exchanger HX1", "fs.HX1.product_water_outlet.temperature", ":math:`T_{HX1\_hot\_out}`", ":math:`^o\text{C}`" + + + diff --git a/docs/technical_reference/unit_models/treatment_models/index.rst b/docs/technical_reference/unit_models/treatment_models/index.rst index 5001cfda..f1022777 100644 --- a/docs/technical_reference/unit_models/treatment_models/index.rst +++ b/docs/technical_reference/unit_models/treatment_models/index.rst @@ -9,4 +9,6 @@ Treatment Models lt_med_surrogate med_md_semibatch med_tvc_surrogate - vagmd_surrogate_base \ No newline at end of file + vagmd_surrogate_base + forward_osmosis + forward_osmosis_trevi_flowsheet \ No newline at end of file diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py index 3adf7479..5d188d4f 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py @@ -15,25 +15,19 @@ ConcreteModel, TransformationFactory, units as pyunits, -<<<<<<< Updated upstream -======= Expression, Reference, ->>>>>>> Stashed changes Var, value, assert_optimal_termination, ) -<<<<<<< Updated upstream -======= from idaes.core.util.model_statistics import ( degrees_of_freedom, number_variables, number_total_constraints, number_unused_variables, unused_variables_set, - ) ->>>>>>> Stashed changes +) from pyomo.network import Arc from pyomo.util.calc_var_value import calculate_variable_from_constraint @@ -42,45 +36,49 @@ import idaes.logger as idaeslog from idaes.core.util.scaling import ( calculate_scaling_factors, -<<<<<<< Updated upstream -) -from idaes.core import FlowsheetBlock, MaterialBalanceType -======= list_badly_scaled_variables, ) from idaes.core import FlowsheetBlock, MaterialBalanceType, EnergyBalanceType ->>>>>>> Stashed changes from idaes.core.solvers.get_solver import get_solver +from idaes.core import UnitModelCostingBlock from idaes.models.unit_models import ( Mixer, Separator, -<<<<<<< Updated upstream - HeatExchanger -) -# from idaes.models.unit_models.heat_exchanger import HX0DInitializer -from idaes.models.unit_models.heat_exchanger import delta_temperature_lmtd_callback -======= HeatExchanger, Heater, ) -# from idaes.models.unit_models.heat_exchanger import HX0DInitializer -from idaes.models.unit_models.heat_exchanger import delta_temperature_lmtd_callback, delta_temperature_underwood_callback ->>>>>>> Stashed changes +from idaes.models.unit_models.heat_exchanger import ( + delta_temperature_lmtd_callback, + delta_temperature_underwood_callback, +) # WaterTAP imports from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock -from watertap_contrib.reflo.property_models.fo_draw_solution_properties import FODrawSolutionParameterBlock -from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ForwardOsmosisZO +from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( + FODrawSolutionParameterBlock, +) +from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ( + ForwardOsmosisZO, +) +from watertap.core.util.initialization import check_dof +from watertap_contrib.reflo.costing import TreatmentCosting, REFLOCosting -<<<<<<< Updated upstream -def build_fo_treviflowsheet( -======= -from idaes.core.util import DiagnosticsToolbox def build_fo_trevi_flowsheet( ->>>>>>> Stashed changes m=None, - + recovery_ratio=0.3, # Assumed FO recovery ratio + RO_recovery_ratio=0.9, # RO recovery ratio + NF_recovery_ratio=0.8, # Nanofiltration recovery ratio + dp_brine=0, # Required pressure over brine osmotic pressure (Pa) + heat_mixing=105, # Heat of mixing in the membrane (MJ/m3 product) + separation_temp=90, # Separation temperature of the draw solution (C) + separator_temp_loss=1, # Temperature loss in the separator (K) + feed_temperature=13, # Feed water temperature (C) + feed_vol_flow=3.704, # Feed water volumetric flow rate (m3/s) + feed_TDS_mass=0.035, # TDS mass fraction of feed + strong_draw_temp=20, # Strong draw solution inlet temperature (C) + strong_draw_mass=0.8, # Strong draw solution mass fraction + product_draw_mass=0.01, # Mass fraction of draw in the product water ): """ This function builds a flowsheet as a representative of Trevi's FO system configuration @@ -94,94 +92,87 @@ def build_fo_trevi_flowsheet( m.fs = FlowsheetBlock(dynamic=False) m.fs.seawater_properties = SeawaterParameterBlock() m.fs.draw_solution_properties = FODrawSolutionParameterBlock() -<<<<<<< Updated upstream - - add_fo(m.fs) - - add_HX(m.fs) - - return m - -def add_HX(fs): - fs.HX1A = HeatExchanger( - delta_temperature_callback=delta_temperature_lmtd_callback, - hot_side_name="product_water", - cold_side_name="weak_draw", - product_water={"property_package": fs.draw_solution_properties}, - weak_draw={"property_package": fs.draw_solution_properties} - ) -======= mfs = m.fs - RO_recovery = 0.9 - NF_recovery = 0.8 - - # Add FO module - add_fo(mfs) + add_fo( + mfs, + recovery_ratio, + NF_recovery_ratio, + dp_brine, + heat_mixing, + separation_temp, + separator_temp_loss, + feed_temperature, + feed_vol_flow, + feed_TDS_mass, + strong_draw_temp, + strong_draw_mass, + product_draw_mass, + ) - def separation_heat(t): - return 105000 - # Add heat exchangers - mfs.HX1A = HeatExchanger( + mfs.HX1 = HeatExchanger( delta_temperature_callback=delta_temperature_underwood_callback, hot_side_name="product_water", cold_side_name="weak_draw", product_water={"property_package": m.fs.draw_solution_properties}, weak_draw={"property_package": m.fs.draw_solution_properties}, - ) - - # mfs.HX1A.cold_side.properties_in[0].liquid_separation = 0 - # mfs.HX1A.cold_side.properties_out[0].liquid_separation = 0 + ) - mfs.HX2A = HeatExchanger( + mfs.HX2 = HeatExchanger( delta_temperature_callback=delta_temperature_underwood_callback, hot_side_name="reg_draw", cold_side_name="weak_draw", reg_draw={"property_package": m.fs.draw_solution_properties}, - weak_draw={"property_package": m.fs.draw_solution_properties} + weak_draw={"property_package": m.fs.draw_solution_properties}, ) - - @mfs.Constraint(doc = 'Same outlet temperature from HX 1A and 2A') + @mfs.Constraint(doc="Same outlet temperature from HX1 and HX2") def outlet_temp_HX1(b): - return (b.HX1A.weak_draw_outlet.temperature[0] == b.HX2A.weak_draw_outlet.temperature[0]) - + return ( + b.HX1.weak_draw_outlet.temperature[0] + == b.HX2.weak_draw_outlet.temperature[0] + ) + # Add a separator to represent RO and NF fed with the product water to remove remained # draw solution (NF) and boron (RO), if existing m.fs.S2 = Separator( property_package=m.fs.draw_solution_properties, mixed_state_block=None, outlet_list=["RO_reject", "NF_reject", "fresh_water"], - split_basis = 3, # Component flow - ) - - @mfs.Constraint(mfs.draw_solution_properties.component_list, - doc = 'NF permeate is sent to RO for further treatment') - def S2_RO_reject(b,j): - permeate_coeff = {"H2O": NF_recovery * (1 - RO_recovery), - "DrawSolution": 0} - return (b.S2.RO_reject.flow_mass_phase_comp[0, "Liq", j] - == b.S2.inlet.flow_mass_phase_comp[0, "Liq", j] - * permeate_coeff[j] - ) - - @mfs.Constraint(mfs.draw_solution_properties.component_list, - doc = 'NF removes all draw solution and the reject is recirculated') - def S2_NF_reject(b,j): - permeate_coeff = {"H2O": 1 - NF_recovery, - "DrawSolution": 1} - return (b.S2.NF_reject.flow_mass_phase_comp[0, "Liq", j] - == b.S2.inlet.flow_mass_phase_comp[0, "Liq", j] - * permeate_coeff[j] - ) + split_basis=3, # Component flow + ) + + @mfs.Constraint( + mfs.draw_solution_properties.component_list, + doc="NF permeate is sent to RO for further treatment", + ) + def S2_RO_reject(b, j): + permeate_coeff = { + "H2O": NF_recovery_ratio * (1 - RO_recovery_ratio), + "DrawSolution": 0, + } + return ( + b.S2.RO_reject.flow_mass_phase_comp[0, "Liq", j] + == b.S2.inlet.flow_mass_phase_comp[0, "Liq", j] * permeate_coeff[j] + ) + + @mfs.Constraint( + mfs.draw_solution_properties.component_list, + doc="NF removes all draw solution and the reject is recirculated", + ) + def S2_NF_reject(b, j): + permeate_coeff = {"H2O": 1 - NF_recovery_ratio, "DrawSolution": 1} + return ( + b.S2.NF_reject.flow_mass_phase_comp[0, "Liq", j] + == b.S2.inlet.flow_mass_phase_comp[0, "Liq", j] * permeate_coeff[j] + ) # Add mixer to mix NF reject that contains draw solution and weak draw mfs.M1 = Mixer( property_package=m.fs.draw_solution_properties, material_balance_type=MaterialBalanceType.componentPhase, - # momentum_mixing_type = 2, energy_mixing_type=1, inlet_list=["NF_reject", "weak_draw"], ) @@ -190,72 +181,109 @@ def S2_NF_reject(b,j): mfs.S1 = Separator( property_package=m.fs.draw_solution_properties, mixed_state_block=m.fs.M1.mixed_state, - outlet_list=["to_HX1A", "to_HX2A"], - split_basis = 1, # Total flow + outlet_list=["to_HX1", "to_HX2"], + split_basis=1, # Total flow ) - # Add mixer to combine pre-heated weak draw solution - mfs.M2 = Mixer( + # Add a heater to heat up weak draw solution from HX1 and HX2 + mfs.H1 = Heater( property_package=m.fs.draw_solution_properties, - material_balance_type=MaterialBalanceType.componentPhase, - energy_mixing_type=1, - inlet_list=["HX1A", "HX2A"], - ) - - # Add connections - mfs.HX1A_cold_inlet = Arc( - source=m.fs.S1.to_HX1A, destination=m.fs.HX1A.cold_side_inlet ) - mfs.HX1A_hot_inlet = Arc( - source=m.fs.fo.product, destination=m.fs.HX1A.hot_side_inlet - ) - mfs.HX2A_cold_inlet = Arc( - source=m.fs.S1.to_HX2A, destination=m.fs.HX2A.cold_side_inlet - ) - mfs.HX2A_hot_inlet = Arc( - source=m.fs.fo.reg_draw, destination=m.fs.HX2A.hot_side_inlet + mfs.H2 = Heater( + property_package=m.fs.draw_solution_properties, ) - mfs.S2_inlet = Arc( - source=m.fs.HX1A.hot_side_outlet, destination=m.fs.S2.inlet + + @mfs.Constraint(doc="Temperature at the separator") + def H1_out_temp(b): + return b.H1.outlet.temperature[0] == b.fo.regeneration_temp + + @mfs.Constraint(doc="Temperature at the separator") + def H2_out_temp(b): + return b.H2.outlet.temperature[0] == b.fo.regeneration_temp + + # Add a cooler that cools regenerated draw solution using supplemental source water + mfs.Cooler = Heater( + property_package=m.fs.draw_solution_properties, ) - mfs.NF_reject_to_M1 = Arc( - source=m.fs.S2.NF_reject, destination=m.fs.M1.NF_reject + + @mfs.Constraint(doc="Strong draw solution temperature entering FO module") + def Cooler_out_temp(b): + return b.Cooler.outlet.temperature[0] == b.fo.feed_props[0].temperature + + # Add connections + mfs.HX1_cold_inlet = Arc( + source=m.fs.S1.to_HX1, destination=m.fs.HX1.cold_side_inlet ) - mfs.weak_draw_to_M1 = Arc( - source=m.fs.fo.weak_draw, destination=m.fs.M1.weak_draw + mfs.HX1_hot_inlet = Arc(source=m.fs.fo.product, destination=m.fs.HX1.hot_side_inlet) + mfs.HX2_cold_inlet = Arc( + source=m.fs.S1.to_HX2, destination=m.fs.HX2.cold_side_inlet ) - mfs.HX1A_to_M2 = Arc( - source=m.fs.HX1A.cold_side_outlet, destination=m.fs.M2.HX1A + mfs.HX2_hot_inlet = Arc( + source=m.fs.fo.reg_draw, destination=m.fs.HX2.hot_side_inlet ) - mfs.HX2A_to_M2 = Arc( - source=m.fs.HX2A.cold_side_outlet, destination=m.fs.M2.HX2A + mfs.S2_inlet = Arc(source=m.fs.HX1.hot_side_outlet, destination=m.fs.S2.inlet) + mfs.NF_reject_to_M1 = Arc(source=m.fs.S2.NF_reject, destination=m.fs.M1.NF_reject) + mfs.weak_draw_to_M1 = Arc(source=m.fs.fo.weak_draw, destination=m.fs.M1.weak_draw) + mfs.HX1_to_H1 = Arc(source=m.fs.HX1.cold_side_outlet, destination=m.fs.H1.inlet) + mfs.HX2_to_H2 = Arc(source=m.fs.HX2.cold_side_outlet, destination=m.fs.H2.inlet) + mfs.HX2_to_Cooler = Arc( + source=m.fs.HX2.hot_side_outlet, destination=m.fs.Cooler.inlet ) TransformationFactory("network.expand_arcs").apply_to(m) - - mfs.HX1A.area.fix(50000) - mfs.HX1A.overall_heat_transfer_coefficient[0].fix(100) - mfs.HX2A.area.fix(50000) - mfs.HX2A.overall_heat_transfer_coefficient[0].fix(100) - # mfs.HX1A.heat_transfer_equation.deactivate() - # mfs.HX2A.heat_transfer_equation.deactivate() - - iscale.set_scaling_factor(mfs.HX1A.area, 1e-5) - iscale.set_scaling_factor(mfs.HX1A.overall_heat_transfer_coefficient, 1e-3) - iscale.set_scaling_factor(mfs.HX1A.hot_side.heat, 1e-7) - iscale.set_scaling_factor(mfs.HX1A.cold_side.heat, 1e-7) - iscale.set_scaling_factor(mfs.HX2A.area, 1e-5) - iscale.set_scaling_factor(mfs.HX2A.overall_heat_transfer_coefficient, 1e-3) - iscale.set_scaling_factor(mfs.HX2A.hot_side.heat, 1e-7) - iscale.set_scaling_factor(mfs.HX2A.cold_side.heat, 1e-7) + # Calculate system capacity (m3/day) + fo = mfs.fo + + @fo.Expression(doc="Calculate system capacity (m3/day)") + def system_capacity(b): + return pyunits.convert( + mfs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "H2O"] + / (1000 * pyunits.kg / pyunits.m**3), # Fresh water density + to_units=pyunits.m**3 / pyunits.day, + ) + + # Calculate specific thermal energy consumption of the system (k) + @fo.Expression(doc="Calculate specific thermal energy consumption (kWh/m3)") + def specific_energy_consumption_thermal(b): + return pyunits.convert( + (mfs.H1.heat_duty[0] + mfs.H2.heat_duty[0]) / fo.system_capacity, + to_units=pyunits.kWh / pyunits.m**3, + ) + + # Specify heat exchanger parameters + mfs.HX1.area.fix(50000) + mfs.HX1.overall_heat_transfer_coefficient[0].fix(100) + mfs.HX2.area.fix(50000) + mfs.HX2.overall_heat_transfer_coefficient[0].fix(100) + + iscale.set_scaling_factor(mfs.HX1.area, 1e-5) + iscale.set_scaling_factor(mfs.HX1.overall_heat_transfer_coefficient, 1e-3) + iscale.set_scaling_factor(mfs.HX1.hot_side.heat, 1e-7) + iscale.set_scaling_factor(mfs.HX1.cold_side.heat, 1e-7) + iscale.set_scaling_factor(mfs.HX2.area, 1e-5) + iscale.set_scaling_factor(mfs.HX2.overall_heat_transfer_coefficient, 1e-3) + iscale.set_scaling_factor(mfs.HX2.hot_side.heat, 1e-7) + iscale.set_scaling_factor(mfs.HX2.cold_side.heat, 1e-7) return m ->>>>>>> Stashed changes - -def add_fo(fs): +def add_fo( + fs, + recovery_ratio=0.3, # Assumed FO recovery ratio + NF_recovery_ratio=0.8, # Nanofiltration recovery ratio + dp_brine=0, # Required pressure over brine osmotic pressure (Pa) + heat_mixing=105, # Heat of mixing in the membrane (MJ/m3 product) + separation_temp=90, # Separation temperature of the draw solution (C) + separator_temp_loss=1, # Temperature loss in the separator (K) + feed_temperature=13, # Feed water temperature (C) + feed_vol_flow=3.704, # Feed water volumetric flow rate (m3/s) + feed_TDS_mass=0.035, # TDS mass fraction of feed + strong_draw_temp=20, # Strong draw solution inlet temperature (C) + strong_draw_mass=0.8, # Strong draw solution mass fraction + product_draw_mass=0.01, # Mass fraction of draw in the product water +): """ This function adds a FO module to the current flowsheet @@ -263,38 +291,17 @@ def add_fo(fs): object: A FO zero-order model """ fs.fo = ForwardOsmosisZO( - property_package_water = fs.seawater_properties, - property_package_draw_solution = fs.draw_solution_properties, + property_package_water=fs.seawater_properties, + property_package_draw_solution=fs.draw_solution_properties, ) fo = fs.fo - # System specifications - recovery_ratio = 0.3 # Assumed FO recovery ratio -<<<<<<< Updated upstream -======= - nanofiltration_recovery_ratio = 0.8 # Nanofiltration recovery ratio ->>>>>>> Stashed changes - dp_brine = 0 # Required pressure over brine osmotic pressure (Pa) - heat_mixing = 105 # Heat of mixing in the membrane (MJ/m3 product) - reneration_temp = 90 # Separation temperature of the draw solution (C) - separator_temp_loss = 1 # Temperature loss in the separator (K) - feed_temperature = 13 # Feed water temperature (C) - feed_vol_flow = 3.704 # Feed water volumetric flow rate (m3/s) - feed_TDS_mass = 0.035 # TDS mass fraction of feed - strong_draw_temp = 20 # Strong draw solution inlet temperature (C) - strong_draw_mass = 0.8 # Strong draw solution mass fraction - product_draw_mass = 0.01 # Mass fraction of draw in the product water - - fo.recovery_ratio.fix(recovery_ratio) -<<<<<<< Updated upstream -======= - fo.nanofiltration_recovery_ratio.fix(nanofiltration_recovery_ratio) ->>>>>>> Stashed changes + fo.nanofiltration_recovery_ratio.fix(NF_recovery_ratio) fo.dp_brine.fix(dp_brine) fo.heat_mixing.fix(heat_mixing) - fo.regeneration_temp.fix(reneration_temp + 273.15) + fo.regeneration_temp.fix(separation_temp + 273.15) fo.separator_temp_loss.fix(separator_temp_loss) # Specifyf strong draw solution properties @@ -315,7 +322,7 @@ def add_fo(fs): var_args={ ("flow_vol_phase", "Liq"): 1, ("mass_frac_phase_comp", ("Liq", "DrawSolution")): product_draw_mass, - ("temperature", None): reneration_temp - separator_temp_loss + 273.15, + ("temperature", None): separation_temp - separator_temp_loss + 273.15, ("pressure", None): 101325, }, hold_state=True, @@ -335,20 +342,6 @@ def add_fo(fs): hold_state=True, ) -<<<<<<< Updated upstream - # Set scaling factors for mass flow rates - fs.seawater_properties.set_default_scaling( - "flow_mass_phase_comp", 1, index=("Liq", "H2O") - ) - fs.seawater_properties.set_default_scaling( - "flow_mass_phase_comp", 1e2, index=("Liq", "TDS") - ) - fs.draw_solution_properties.set_default_scaling( - "flow_mass_phase_comp", 1, index=("Liq", "H2O") - ) - fs.draw_solution_properties.set_default_scaling( - "flow_mass_phase_comp", 1, index=("Liq", "DrawSolution") -======= fo.weak_draw_props[0].pressure.fix(101325) fo.reg_draw_props[0].pressure.fix(101325) @@ -364,347 +357,326 @@ def add_fo(fs): ) fs.draw_solution_properties.set_default_scaling( "flow_mass_phase_comp", 1e-2, index=("Liq", "DrawSolution") ->>>>>>> Stashed changes ) -def fix_dof_and_initialize(m, outlvl=idaeslog.WARNING): +def fix_dof_and_initialize(m, strong_draw_mass_frac=0.8, product_draw_mass_frac=0.01): + calculate_scaling_factors(m) m.fs.fo.initialize() # Unfix the state variables and fix mass fractrion of two state blocks - strong_draw_mass = 0.8 # Strong draw solution mass fraction - product_draw_mass = 0.01 # Mass fraction of draw in the product water - m.fs.fo.unfix_and_fix_freedom(strong_draw_mass, product_draw_mass) - - -<<<<<<< Updated upstream -if __name__=="__main__": - m = build_fo_treviflowsheet() - fix_dof_and_initialize(m) -======= - m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value - m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value - m.fs.S1.to_HX1A.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value / 2 - m.fs.S1.to_HX1A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value / 2 - m.fs.S1.to_HX2A.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value / 2 - m.fs.S1.to_HX2A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value / 2 - m.fs.S1.to_HX1A_state[0].flow_vol_phase["Liq"] - m.fs.S1.to_HX2A_state[0].flow_vol_phase["Liq"] - m.fs.S1.to_HX1A_state[0].cp_mass_phase["Liq"] + m.fs.fo.unfix_and_fix_freedom(strong_draw_mass_frac, product_draw_mass_frac) + + m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + ) + m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + ) + m.fs.S1.to_HX1.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value / 2 + ) + m.fs.S1.to_HX1.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value / 2 + ) + m.fs.S1.to_HX2.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value / 2 + ) + m.fs.S1.to_HX2.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value / 2 + ) + m.fs.S1.to_HX1_state[0].flow_vol_phase["Liq"] + m.fs.S1.to_HX2_state[0].flow_vol_phase["Liq"] + m.fs.S1.to_HX1_state[0].cp_mass_phase["Liq"] m.fs.S1.initialize() - state_args_1A_hot = {"flow_mass_phase_comp":{("Liq", "H2O"): m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value, - ("Liq", "DrawSolution"): m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value}, - "temperature": m.fs.fo.product_props[0].temperature.value, - "pressure": m.fs.fo.product_props[0].pressure.value} - state_args_1A_cold = {"flow_mass_phase_comp":{("Liq", "H2O"): m.fs.S1.to_HX1A.flow_mass_phase_comp[0, "Liq", "H2O"].value, - ("Liq", "DrawSolution"): m.fs.S1.to_HX1A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value}, - "temperature": m.fs.S1.to_HX1A.temperature[0].value, - "pressure": m.fs.S1.to_HX1A.pressure[0].value} - m.fs.HX1A.initialize(state_args_1 = state_args_1A_hot, state_args_2 = state_args_1A_cold) - m.fs.HX1A.cold_side.properties_out[0].liquid_separation = 1 - - state_args_2A_hot = {"flow_mass_phase_comp":{("Liq", "H2O"): m.fs.fo.reg_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value, - ("Liq", "DrawSolution"): m.fs.fo.reg_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value}, - "temperature": m.fs.fo.reg_draw_props[0].temperature.value, - "pressure": m.fs.fo.reg_draw_props[0].pressure.value} - state_args_2A_cold = {"flow_mass_phase_comp":{("Liq", "H2O"): m.fs.S1.to_HX2A.flow_mass_phase_comp[0, "Liq", "H2O"].value, - ("Liq", "DrawSolution"): m.fs.S1.to_HX2A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value}, - "temperature": m.fs.S1.to_HX2A.temperature[0].value, - "pressure": m.fs.S1.to_HX2A.pressure[0].value} - m.fs.HX2A.initialize(state_args_1 = state_args_2A_hot, state_args_2 = state_args_2A_cold) - m.fs.HX2A.cold_side.properties_out[0].liquid_separation = 1 - - m.fs.S2.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value - m.fs.S2.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value - m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value - m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value - m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value * 1e-1 - m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value * 1e-1 - m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value - m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + state_args_HX1_hot = { + "flow_mass_phase_comp": { + ("Liq", "H2O"): m.fs.fo.product_props[0] + .flow_mass_phase_comp["Liq", "H2O"] + .value, + ("Liq", "DrawSolution"): m.fs.fo.product_props[0] + .flow_mass_phase_comp["Liq", "DrawSolution"] + .value, + }, + "temperature": m.fs.fo.product_props[0].temperature.value, + "pressure": m.fs.fo.product_props[0].pressure.value, + } + state_args_HX1_cold = { + "flow_mass_phase_comp": { + ("Liq", "H2O"): m.fs.S1.to_HX1.flow_mass_phase_comp[0, "Liq", "H2O"].value, + ("Liq", "DrawSolution"): m.fs.S1.to_HX1.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value, + }, + "temperature": m.fs.S1.to_HX1.temperature[0].value, + "pressure": m.fs.S1.to_HX1.pressure[0].value, + } + m.fs.HX1.initialize( + state_args_1=state_args_HX1_hot, state_args_2=state_args_HX1_cold + ) + m.fs.HX1.cold_side.properties_out[0].liquid_separation = 1 + m.fs.HX1.cold_side.properties_out[ + 0 + ].mass_frac_after_separation = strong_draw_mass_frac + + state_args_HX2_hot = { + "flow_mass_phase_comp": { + ("Liq", "H2O"): m.fs.fo.reg_draw_props[0] + .flow_mass_phase_comp["Liq", "H2O"] + .value, + ("Liq", "DrawSolution"): m.fs.fo.reg_draw_props[0] + .flow_mass_phase_comp["Liq", "DrawSolution"] + .value, + }, + "temperature": m.fs.fo.reg_draw_props[0].temperature.value, + "pressure": m.fs.fo.reg_draw_props[0].pressure.value, + } + state_args_HX2_cold = { + "flow_mass_phase_comp": { + ("Liq", "H2O"): m.fs.S1.to_HX2.flow_mass_phase_comp[0, "Liq", "H2O"].value, + ("Liq", "DrawSolution"): m.fs.S1.to_HX2.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value, + }, + "temperature": m.fs.S1.to_HX2.temperature[0].value, + "pressure": m.fs.S1.to_HX2.pressure[0].value, + } + m.fs.HX2.initialize( + state_args_1=state_args_HX2_hot, state_args_2=state_args_HX2_cold + ) + m.fs.HX2.cold_side.properties_out[0].liquid_separation = 1 + m.fs.HX2.cold_side.properties_out[ + 0 + ].mass_frac_after_separation = strong_draw_mass_frac + + m.fs.S2.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + ) + m.fs.S2.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + ) + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + ) + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + ) + m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value * 1e-1 + ) + m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + * 1e-1 + ) + m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + ) + m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + ) m.fs.S2.initialize() - m.fs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value - m.fs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value - m.fs.M1.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value - m.fs.M1.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value - m.fs.M1.outlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value - m.fs.M1.outlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + m.fs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value + ) + m.fs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + ) + m.fs.M1.NF_reject.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value + m.fs.M1.NF_reject.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value + m.fs.M1.outlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value + ) + m.fs.M1.outlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + ) m.fs.M1.mixed_state[0].flow_vol_phase["Liq"] - - m.fs.M1.initialize(outlvl = idaeslog.DEBUG) - - - - m.fs.M2.HX1A.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value - m.fs.M2.HX1A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value - m.fs.M2.HX2A.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value - m.fs.M2.HX2A.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value - m.fs.M2.outlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value - m.fs.M2.outlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value - m.fs.M2.initialize() - - print('flowsheet initalization completed') + m.fs.M1.initialize() -if __name__=="__main__": - # from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP - # from pyomo.environ import Objective - # m.fs.o = Objective(expr = 0) - # nlp = PyomoNLP(m) - # jac = nlp.evaluate_jacobian() - # var_list = nlp.primals_names() - # con_list = nlp.constraint_names() - # jac_array = jac.toarray() - # import pandas as pd - # df = pd.DataFrame(jac_array, index = con_list, columns=var_list) - # # if has_touched_vars: - # # msg= "_with_touched_vars" - # # else: - # # msg = "" - # df.to_csv(f'jacobian.csv') + m.fs.H1.initialize() + m.fs.H2.initialize() + m.fs.Cooler.initialize() - m = build_fo_trevi_flowsheet() - fix_dof_and_initialize(m) - - # dt = DiagnosticsToolbox(m) - # dt.report_structural_issues() - # try: - # fix_dof_and_initialize(m) - # except: - # pass +if __name__ == "__main__": - # dt.report_numerical_issues() - - # display_constraints_with_large_residuals() - # display_variables_at_or_outside_bounds() - # display_variables_with_extreme_jacobians() - # display_constraints_with_extreme_jacobians() - - - - - # print(m.fs.HX1A._get_stream_table_contents()) - # print(m.fs.HX1A.heat_duty[0].value) + """ + Build flowsheet with specified model configs + """ + # For feed water state variables, i.e. feed temperature, volume flow rate, concentration + # they can be unfixed after initialization, so as to connect with other unit models + m = build_fo_trevi_flowsheet( + recovery_ratio=0.3, # Assumed FO recovery ratio + RO_recovery_ratio=0.9, # RO recovery ratio + NF_recovery_ratio=0.8, # Nanofiltration recovery ratio + dp_brine=0, # Required pressure over brine osmotic pressure (Pa) + heat_mixing=105, # Heat of mixing in the membrane (MJ/m3 product) + separation_temp=90, # Separation temperature of the draw solution (C) + separator_temp_loss=1, # Temperature loss in the separator (K) + feed_temperature=13, # Feed water temperature (C) + feed_vol_flow=3.704, # Feed water volumetric flow rate (m3/s) + feed_TDS_mass=0.035, # TDS mass fraction of feed + strong_draw_temp=20, # Strong draw solution inlet temperature (C) + strong_draw_mass=0.8, # Strong draw solution mass fraction + product_draw_mass=0.01, # Mass fraction of draw in the product water + ) - print('no. of var', number_variables(m)) - print('no. of const', - number_total_constraints(m),) - print('no of unused var', number_unused_variables(m)) + fix_dof_and_initialize( + m, strong_draw_mass_frac=0.8, product_draw_mass_frac=0.01 # same input as above + ) # same input as above ->>>>>>> Stashed changes + # Specify the temperature of the weak draw solution and product water after going through HX1 + m.fs.HX1.overall_heat_transfer_coefficient[0].unfix() + m.fs.HX2.overall_heat_transfer_coefficient[0].unfix() + m.fs.HX1.weak_draw_outlet.temperature.fix(80 + 273.15) + m.fs.HX1.product_water_outlet.temperature.fix(28 + 273.15) - from watertap.core.util.initialization import check_dof + # Solve check_dof(m, fail_flag=True) - solver = get_solver() - -<<<<<<< Updated upstream results = solver.solve(m) assert_optimal_termination(results) - print(m.fs.fo.strong_draw_props[0].flow_vol_phase["Liq"].value) - -======= - # results = solver.solve(m) - # assert_optimal_termination(results) + # Add cost package of Trevi FO system + m.fs.costing = TreatmentCosting() + m.fs.costing.base_currency = pyunits.USD_2020 - m.fs.HX1A.overall_heat_transfer_coefficient[0].unfix() - m.fs.HX2A.overall_heat_transfer_coefficient[0].unfix() - m.fs.HX1A.weak_draw_outlet.temperature.fix(70 + 273.15) - m.fs.HX1A.product_water_outlet.temperature.fix(23 + 273.15) - # m.fs.HX2A.reg_draw_outlet.temperature.fix(23 + 273.15) + # Create cost block for FO + m.fs.fo.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing) + # Add LCOW component + m.fs.costing.cost_process() + m.fs.costing.add_annual_water_production(m.fs.fo.system_capacity) + m.fs.costing.add_LCOW(m.fs.fo.system_capacity) + # Solve check_dof(m, fail_flag=True) + solver = get_solver() results = solver.solve(m) assert_optimal_termination(results) - # def separation_heat(t): - # return 0 - - # mfs = m.fs - # cold_side = m.fs.HX1A.cold_side - # # cold_side.del_component(cold_side.heat) - # # mfs.HX1A.del_component(mfs.HX1A.heat_duty) - # cold_side.del_component(cold_side.enthalpy_balances) - - # cold_side.add_total_enthalpy_balances( - # # balance_type=EnergyBalanceType.enthalpyTotal, - # has_heat_transfer=True, - # custom_term = separation_heat, - # ) - # mfs.HX1A.heat_duty = Reference(cold_side.heat) - - # mfs = m.fs - # mfs.HX1A.cold_side.del_component(mfs.HX1A.cold_side.enthalpy_balances) - - # mfs.HX1A.cold_side.add_energy_balances( - # balance_type=EnergyBalanceType.enthalpyTotal, - # has_heat_transfer=True, - # custom_term = separation_heat, - # ) - # mfs.HX1A.heat_duty = Reference(cold_side.heat) - - # cold_side = mfs.HX1A.cold_side - - # results = solver.solve(m) - # assert_optimal_termination(results) - - - - print('1A cold in temp', m.fs.HX1A.weak_draw_inlet.temperature[0].value - 273.15) - print('1A cold out temp', m.fs.HX1A.weak_draw_outlet.temperature[0].value - 273.15) - print('1A hot in temp', m.fs.HX1A.product_water_inlet.temperature[0].value - 273.15) - print('1A hot out temp', m.fs.HX1A.product_water_outlet.temperature[0].value - 273.15) - print('') - print('2A cold in temp', m.fs.HX2A.weak_draw_inlet.temperature[0].value - 273.15) - print('2A cold out temp', m.fs.HX2A.weak_draw_outlet.temperature[0].value - 273.15) - print('2A hot in temp', m.fs.HX2A.reg_draw_inlet.temperature[0].value - 273.15) - print('2A hot out temp', m.fs.HX2A.reg_draw_outlet.temperature[0].value - 273.15) - print('') - print('FO product vol', m.fs.fo.product_props[0].flow_vol_phase["Liq"].value) - print('FO product cp', m.fs.fo.product_props[0].cp_mass_phase["Liq"].value) - print('FO reg draw vol', m.fs.fo.reg_draw_props[0].flow_vol_phase["Liq"].value) - print('FO reg draw cp', m.fs.fo.reg_draw_props[0].cp_mass_phase["Liq"].value) - print('FO weak draw vol', m.fs.fo.weak_draw_props[0].flow_vol_phase["Liq"].value) - print('FO weak draw cp', m.fs.fo.weak_draw_props[0].cp_mass_phase["Liq"].value) - print('') - print('FO weak H2O', m.fs.fo.weak_draw.flow_mass_phase_comp[0,"Liq","H2O"].value) - print('FO weak draw', m.fs.fo.weak_draw.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) - print('FO weak temp', m.fs.fo.weak_draw.temperature[0].value - 273.15) - print('') - print('FO mixed H2O', m.fs.S1.inlet.flow_mass_phase_comp[0,"Liq","H2O"].value) - print('FO mixed draw', m.fs.S1.inlet.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) - print('FO mixed temp', m.fs.S1.inlet.temperature[0].value - 273.15) - - print('') - print('1A cold in H2O', m.fs.HX1A.cold_side_inlet.flow_mass_phase_comp[0,"Liq","H2O"].value) - print('1A cold in Draw', m.fs.HX1A.cold_side_inlet.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) - print('1A cold vol', m.fs.S1.to_HX1A_state[0].flow_vol_phase["Liq"].value) - print('1A hot in H2O', m.fs.HX1A.hot_side_inlet.flow_mass_phase_comp[0,"Liq","H2O"].value) - print('1A hot in Draw', m.fs.HX1A.hot_side_inlet.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) - print('') - print('2A cold in H2O', m.fs.HX2A.cold_side_inlet.flow_mass_phase_comp[0,"Liq","H2O"].value) - print('2A cold in Draw', m.fs.HX2A.cold_side_inlet.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) - print('2A cold vol', m.fs.S1.to_HX2A_state[0].flow_vol_phase["Liq"].value) - print('2A hot in H2O', m.fs.HX2A.hot_side_inlet.flow_mass_phase_comp[0,"Liq","H2O"].value) - print('2A hot in Draw', m.fs.HX2A.hot_side_inlet.flow_mass_phase_comp[0,"Liq","DrawSolution"].value) - - print('') - print('HX1A hot side heat load (MJ)', value(m.fs.HX1A.hot_side.heat[0]) / 1e6) - print('HX1A cold side heat load (MJ)', value(m.fs.HX1A.cold_side.heat[0]) / 1e6) - print('HX2A hot side heat load (MJ)', value(m.fs.HX2A.hot_side.heat[0]) / 1e6) - print('HX2A cold side heat load (MJ)', value(m.fs.HX2A.cold_side.heat[0]) / 1e6) - print('HX1A approach T', value(m.fs.HX1A.delta_temperature[0])) - - print('') - print('HX1A area', m.fs.HX1A.area.value) - print('HX2A area', m.fs.HX2A.area.value) - print('HX1A coef', m.fs.HX2A.overall_heat_transfer_coefficient[0].value) - print('HX2A coef', m.fs.HX2A.overall_heat_transfer_coefficient[0].value) - - print('') - print('Fresh water mass', m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "H2O"].value) - print('RO reject mass', m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value) - print('NF reject H2O mass', m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value) - print('NF reject draw mass', m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value) - - print('') - print('NF reject temp', m.fs.S2.NF_reject.temperature[0].value - 273.15) - print('weak draw temp', m.fs.fo.weak_draw_props[0].temperature.value - 273.15) - print('M1 temperature', m.fs.M1.mixed_state[0].temperature.value -273.15) - print('M1 H2O mass', m.fs.M1.mixed_state[0].flow_mass_phase_comp['Liq', 'H2O'].value) - print('M1 H2O mass', m.fs.M1.mixed_state[0].flow_mass_phase_comp['Liq', 'DrawSolution'].value) - - print('') - print('M2 mixed H2O mass', m.fs.M2.mixed_state[0].flow_mass_phase_comp["Liq", "H2O"].value) - print('M2 mixed draw mass', m.fs.M2.mixed_state[0].flow_mass_phase_comp["Liq", "DrawSolution"].value) - print('M2 mixed temp', m.fs.M2.mixed_state[0].temperature.value - 273.15) - - - - # m = ConcreteModel() - # m.fs = FlowsheetBlock(dynamic=False) - # m.fs.seawater_properties = SeawaterParameterBlock() - # m.fs.draw_solution_properties = FODrawSolutionParameterBlock() - # mfs = m.fs - # # Add mixer to mix NF reject that contains draw solution and weak draw - # mfs.M1 = Mixer( - # property_package=m.fs.draw_solution_properties, - # material_balance_type=MaterialBalanceType.componentTotal, - # energy_mixing_type=1, - # inlet_list=["NF_reject", "weak_draw"], - # ) - - # mfs.M1.NF_reject.temperature[0].fix(47.8+273.15) - # mfs.M1.NF_reject.pressure[0].fix(101325) - # mfs.M1.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].fix(223.2) - # mfs.M1.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].fix(0.01) - - # mfs.M1.weak_draw.temperature[0].fix(20.81+273.15) - # mfs.M1.weak_draw.pressure[0].fix(101325) - # mfs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "H2O"].fix(1479.96) - # mfs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "DrawSolution"].fix(1516.85) - - # # Set scaling factors for mass flow rates - # mfs.seawater_properties.set_default_scaling( - # "flow_mass_phase_comp", 1e-2, index=("Liq", "H2O") - # ) - # mfs.seawater_properties.set_default_scaling( - # "flow_mass_phase_comp", 1e-3, index=("Liq", "TDS") - # ) - - # mfs.draw_solution_properties.set_default_scaling( - # "flow_mass_phase_comp", 1e-2, index=("Liq", "H2O") - # ) - # mfs.draw_solution_properties.set_default_scaling( - # "flow_mass_phase_comp", 1e-1, index=("Liq", "DrawSolution") - # ) - - # from idaes.core.util.scaling import ( - # calculate_scaling_factors, - # constraint_scaling_transform, - # unscaled_variables_generator, - # unscaled_constraints_generator, - # badly_scaled_var_generator, - # list_badly_scaled_variables, - # ) - # badly_scaled_var_lst = list(badly_scaled_var_generator(m)) - # list_badly_scaled_variables(m) - - # # @mfs.Constraint( - # # doc="Temperature difference") - # # def temp_bound2(b): - # # return b.M1.weak_draw_state[0].mass_frac_phase_comp["Liq", "DrawSolution"] <= 0.6 - - # # @mfs.Constraint( - # # doc="Temperature difference") - # # def temp_bound3(b): - # # return b.M1.mixed_state[0].mass_frac_phase_comp["Liq", "DrawSolution"] <= 0.6 - - - - # mfs.M1.weak_draw.temperature[0].value = 273.15 + 23 - # mfs.M1.weak_draw.pressure[0].value = 101325 - # mfs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "H2O"].value = 1700 - # mfs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = 1528 - # mfs.M1.initialize() - - # from watertap.core.util.initialization import check_dof - # check_dof(m, fail_flag=True) - - # solver = get_solver() - - # results = solver.solve(m) - # assert_optimal_termination(results) - - # print(mfs.M1.mixed_state[0].temperature.value - 273.15) - # print(mfs.M1.mixed_state[0].flow_mass_phase_comp["Liq", "H2O"].value) - # print(mfs.M1.mixed_state[0].flow_mass_phase_comp["Liq", "DrawSolution"].value) - # print(mfs.M1.NF_reject_state[0].mass_frac_phase_comp["Liq", "DrawSolution"].value) - # print(mfs.M1.weak_draw_state[0].mass_frac_phase_comp["Liq", "DrawSolution"].value) - # print(mfs.M1.mixed_state[0].mass_frac_phase_comp["Liq", "DrawSolution"].value) ->>>>>>> Stashed changes + # Retrieve outputs + overall_performance = { + "Production capacity (m3/day)": value(m.fs.fo.system_capacity), + "Specific thermal energy consumption (kWh/m3)": value( + m.fs.fo.specific_energy_consumption_thermal + ), + "Thermal power requirement (kW)": value( + m.fs.fo.specific_energy_consumption_thermal + ) + * value(m.fs.fo.system_capacity) + / 24, + "LCOW ($/m3)": m.fs.costing.LCOW(), + } + + for i, v in overall_performance.items(): + print(i, round(v, 2)) + + operational_parameters = { + # Heat exchanger HX1 temperatures + "HX1 cold in temp": m.fs.HX1.weak_draw_inlet.temperature[0].value - 273.15, + "HX1 cold out temp": m.fs.HX1.weak_draw_outlet.temperature[0].value - 273.15, + "HX1 hot in temp": m.fs.HX1.product_water_inlet.temperature[0].value - 273.15, + "HX1 hot out temp": m.fs.HX1.product_water_outlet.temperature[0].value - 273.15, + # Heat exchanger HX2 temperatures + "HX2 cold in temp": m.fs.HX2.weak_draw_inlet.temperature[0].value - 273.15, + "HX2 cold out temp": m.fs.HX2.weak_draw_outlet.temperature[0].value - 273.15, + "HX2 hot in temp": m.fs.HX2.reg_draw_inlet.temperature[0].value - 273.15, + "HX2 hot out temp": m.fs.HX2.reg_draw_outlet.temperature[0].value - 273.15, + # Product water flow rates + "FO product vol (m3/s)": m.fs.fo.product_props[0].flow_vol_phase["Liq"].value, + "FO product H2O mass (kg/s)": m.fs.fo.product_props[0] + .flow_mass_phase_comp["Liq", "H2O"] + .value, + "FO product draw mass (kg/s)": m.fs.fo.product_props[0] + .flow_mass_phase_comp["Liq", "DrawSolution"] + .value, + # Regenerated draw solution flow rates + "FO reg draw vol (m3/s)": m.fs.fo.reg_draw_props[0].flow_vol_phase["Liq"].value, + "FO reg H2O mass (kg/s)": m.fs.fo.reg_draw_props[0] + .flow_mass_phase_comp["Liq", "H2O"] + .value, + "FO reg draw mass (kg/s)": m.fs.fo.reg_draw_props[0] + .flow_mass_phase_comp["Liq", "DrawSolution"] + .value, + # Weak draw solution properties at FO module outlet + "FO weak H2O mass(kg/s)": m.fs.fo.weak_draw.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value, + "FO weak draw mass(kg/s)": m.fs.fo.weak_draw.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value, + "FO weak temp": m.fs.fo.weak_draw.temperature[0].value - 273.15, + # Weak draw solution properties after mixing with NF reject + "Mixed weak H2O mass(kg/s)": m.fs.S1.inlet.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value, + "Mixed weak draw mass (kg/s)": m.fs.S1.inlet.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value, + "Mixed weak temp": m.fs.S1.inlet.temperature[0].value - 273.15, + # Flows going into HX1 + "HX1 cold inlet H2O(kg/s)": m.fs.HX1.cold_side_inlet.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value, + "HX1 cold inlet Draw(kg/s)": m.fs.HX1.cold_side_inlet.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value, + "HX1 hot inlet H2O(kg/s)": m.fs.HX1.hot_side_inlet.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value, + "HX1 hot inlet Draw(kg/s)": m.fs.HX1.hot_side_inlet.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value, + # Flows going into HX2 + "HX2 cold in H2O(kg/s)": m.fs.HX2.cold_side_inlet.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value, + "HX2 cold in Draw(kg/s)": m.fs.HX2.cold_side_inlet.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value, + "HX2 hot in H2O(kg/s)": m.fs.HX2.hot_side_inlet.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value, + "HX2 hot in Draw(kg/s)": m.fs.HX2.hot_side_inlet.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value, + # Energy balances in heat exchangers + "HX1 hot side heat load (MJ)": value(m.fs.HX1.hot_side.heat[0]) / 1e6, + "HX1 cold side heat load (MJ)": value(m.fs.HX1.cold_side.heat[0]) / 1e6, + "HX2 hot side heat load (MJ)": value(m.fs.HX2.hot_side.heat[0]) / 1e6, + "HX2 cold side heat load (MJ)": value(m.fs.HX2.cold_side.heat[0]) / 1e6, + "HX1 approach temp": value(m.fs.HX1.delta_temperature[0]), + "HX2 approach temp": value(m.fs.HX2.delta_temperature[0]), + # Separated flow rates after S2 + "Fresh water H2O mass (kg/s)": m.fs.S2.fresh_water.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value, + "RO reject H2O mass (kg/s)": m.fs.S2.RO_reject.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value, + "NF reject H2O mass (kg/s)": m.fs.S2.NF_reject.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value, + "NF reject draw mass (kg/s)": m.fs.S2.NF_reject.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value, + # Mixed flow properties after M1 + "M1 temperature": m.fs.M1.mixed_state[0].temperature.value - 273.15, + "M1 H2O mass (kg/s)": m.fs.M1.mixed_state[0] + .flow_mass_phase_comp["Liq", "H2O"] + .value, + "M1 draw mass (kg/s)": m.fs.M1.mixed_state[0] + .flow_mass_phase_comp["Liq", "DrawSolution"] + .value, + # Heat loads + "Heater 1 heat load": m.fs.H1.heat_duty[0].value, + "Heater 2 heat load": m.fs.H2.heat_duty[0].value, + "Cooler heat load": m.fs.Cooler.heat_duty[0].value, + } diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py new file mode 100644 index 00000000..ad9e28e8 --- /dev/null +++ b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py @@ -0,0 +1,151 @@ +################################################################################# +# WaterTAP Copyright (c) 2020-2023, 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 pyomo.environ as pyo +import idaes.core.util.scaling as iscale +from watertap.costing.util import register_costing_parameter_block +from watertap_contrib.reflo.costing.util import ( + make_capital_cost_var, + make_fixed_operating_cost_var, +) + +# Costing numbers are from data provided by Trevi (2021), +# and should be applied to a Trevi FO flowsheet. +# Mass and energy flows should be calculated from a Trevi FO flowsheet. + + +def build_forward_osmosis_cost_param_block(blk): + + costing = blk.parent_block() + + blk.base_unit_cost = pyo.Var( + initialize=26784, + units=costing.base_currency / (pyo.units.m**3 / pyo.units.day), + bounds=(0, None), + doc="Base price of Trevi FO system ($/m3/day), including membranes, heat exchangers, \ + construction, draw solution, coalescers, structural, polishing, pipes, plumbing, \ + pre-filtration, control, pumps, instrumentation, valves, CIP and tanks", + ) + + blk.unit_cost_index = pyo.Var( + initialize=-0.428, + units=pyo.units.dimensionless, + bounds=(None, 0), + doc="Scaling factor of Trevi FO system capital cost", + ) + + blk.base_labor_cost = pyo.Var( + initialize=0.4757, + units=costing.base_currency / pyo.units.m**3, + bounds=(0, None), + doc="Base price of the labor cost ($/m3)", + ) + + blk.labor_cost_index = pyo.Var( + initialize=-0.178, + units=pyo.units.dimensionless, + bounds=(None, 0), + doc="Scaling factor of Trevi FO system capital cost", + ) + + blk.specific_energy_consumption_electric = pyo.Var( + initialize=1, + units=pyo.units.kWh / pyo.units.m**3, + bounds=(0, None), + doc="Specific electric energy consumption (kWh/m3)", + ) + + blk.cost_chemicals = pyo.Var( + initialize=0.07, + units=costing.base_currency / pyo.units.m**3, + bounds=(0, None), + doc="Cost of chemicals per m3 product", + ) + + blk.cost_durable_goods = pyo.Var( + initialize=0.05, + units=costing.base_currency / pyo.units.m**3, + bounds=(0, None), + doc="Cost of chemicals per m3 product", + ) + + blk.cost_disposal = pyo.Var( + initialize=0.02, + units=costing.base_currency / pyo.units.m**3, + bounds=(0, None), + doc="Cost of disposal per m3 brine", + ) + + blk.cost_fraction_insurance = pyo.Var( + initialize=0.005, + units=pyo.units.dimensionless, + bounds=(0, None), + doc="Fraction of capital cost for insurance", + ) + + +@register_costing_parameter_block( + build_rule=build_forward_osmosis_cost_param_block, + parameter_block_name="forward_osmosis", +) +def cost_forward_osmosis(blk): + + fo_params = blk.costing_package.forward_osmosis + make_capital_cost_var(blk) + make_fixed_operating_cost_var(blk) + + fo = blk.unit_model + brine = fo.brine_props[0] + + blk.costing_package.add_cost_factor(blk, None) + blk.annual_dist_production = pyo.units.convert( + fo.system_capacity, to_units=pyo.units.m**3 / pyo.units.year + ) + blk.capital_cost_constraint = pyo.Constraint( + expr=blk.capital_cost + == fo_params.base_unit_cost * fo.system_capacity**fo_params.unit_cost_index + ) + + blk.labor_cost = pyo.Expression( + expr=fo_params.base_labor_cost + * fo.system_capacity**fo_params.labor_cost_index + ) + + blk.fixed_operating_cost_constraint = pyo.Constraint( + expr=( + blk.fixed_operating_cost + == blk.annual_dist_production + * (blk.labor_cost + fo_params.cost_chemicals + fo_params.cost_durable_goods) + + blk.capital_cost * fo_params.cost_fraction_insurance + + pyo.units.convert( + brine.flow_vol_phase["Liq"], to_units=pyo.units.m**3 / pyo.units.year + ) + * fo_params.cost_disposal + ) + ) + + blk.thermal_energy_flow = pyo.Expression( + expr=fo.specific_energy_consumption_thermal + * pyo.units.convert( + fo.system_capacity, to_units=pyo.units.m**3 / pyo.units.hr + ) + ) + + blk.electricity_flow = pyo.Expression( + expr=fo_params.specific_energy_consumption_electric + * pyo.units.convert( + fo.system_capacity, to_units=pyo.units.m**3 / pyo.units.hr + ) + ) + + blk.costing_package.cost_flow(blk.thermal_energy_flow, "heat") + blk.costing_package.cost_flow(blk.electricity_flow, "electricity") diff --git a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py index 5ae1127d..fee4c599 100644 --- a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py @@ -47,11 +47,7 @@ from pyomo.environ import ( Param, -<<<<<<< Updated upstream - PositiveReals, -======= Binary, ->>>>>>> Stashed changes Expression, units as pyunits, Reals, @@ -128,55 +124,25 @@ def build(self): # osmotic coefficient parameters, equation derived from experimental data self.osm_coeff_param_0 = Param( -<<<<<<< Updated upstream - initialize=-1.2370854e5, -======= initialize=-2.31586e5, ->>>>>>> Stashed changes units=pyunits.Pa, doc="Osmotic coefficient parameter 0", ) self.osm_coeff_param_1 = Param( -<<<<<<< Updated upstream - initialize=1.2961975e7, -======= initialize=9.16006e6, ->>>>>>> Stashed changes units=pyunits.Pa, doc="Osmotic coefficient parameter 1", ) self.osm_coeff_param_2 = Param( -<<<<<<< Updated upstream - initialize=-1.386231e8, -======= initialize=-3.25759e7, ->>>>>>> Stashed changes units=pyunits.Pa, doc="Osmotic coefficient parameter 2", ) self.osm_coeff_param_3 = Param( -<<<<<<< Updated upstream - initialize=6.356857e8, - units=pyunits.Pa, - doc="Osmotic coefficient parameter 3", - ) - self.osm_coeff_param_4 = Param( - initialize=-1.10696e9, - units=pyunits.Pa, - doc="Osmotic coefficient parameter 4", - ) - self.osm_coeff_param_5 = Param( - initialize=6.9232e8, - units=pyunits.Pa, - doc="Osmotic coefficient parameter 5", - ) - -======= initialize=5.75176e7, units=pyunits.Pa, doc="Osmotic coefficient parameter 3", ) ->>>>>>> Stashed changes # specific heat parameters, derived from experimental data cp_units = pyunits.J / (pyunits.kg * pyunits.K) self.cp_phase_param_A0 = Param( @@ -194,15 +160,11 @@ def build(self): units=cp_units, doc="Specific heat of seawater parameter A2", ) -<<<<<<< Updated upstream - -======= self.separation_heat = Param( - initialize = 105, - units = pyunits.MJ / pyunits.m**3, - doc = "Separation heat per m3 of the separated water", + initialize=105, + units=pyunits.MJ / pyunits.m**3, + doc="Separation heat per m3 of the separated water", ) ->>>>>>> Stashed changes # --------------------------------------------------------------------- # Set default scaling factors self.set_default_scaling("temperature", 1e-2) @@ -210,6 +172,7 @@ def build(self): self.set_default_scaling("dens_mass_phase", 1e-3, index="Liq") self.set_default_scaling("cp_mass_phase", 1e-3, index="Liq") self.set_default_scaling("enth_mass_phase", 1e-5, index="Liq") + self.set_default_scaling("heat_separation_phase", 1e-5, index="Liq") @classmethod def define_metadata(cls, obj): @@ -235,13 +198,8 @@ def define_metadata(cls, obj): "pressure_osm_phase": {"method": "_pressure_osm_phase"}, "cp_mass_phase": {"method": "_cp_mass_phase"}, "enth_mass_phase": {"method": "_enth_mass_phase"}, -<<<<<<< Updated upstream + "heat_separation_phase": {"method": "_heat_separation_phase"}, "enth_flow": {"method": "_enth_flow"}, - # "visc_d": {"method": "_visc_d"}, -======= - "heat_separation_phase": {"method":"_heat_separation_phase"}, - "enth_flow": {"method": "_enth_flow"}, ->>>>>>> Stashed changes } ) @@ -605,11 +563,7 @@ def _pressure_osm_phase(self): self.pressure_osm_phase = Var( self.params.phase_list, initialize=1e6, -<<<<<<< Updated upstream - bounds=(1, 1e8), -======= bounds=(-1e6, 1e8), ->>>>>>> Stashed changes units=pyunits.Pa, doc="Osmotic pressure", ) @@ -623,13 +577,6 @@ def rule_pressure_osm_phase( + b.params.osm_coeff_param_1 * s + b.params.osm_coeff_param_2 * s**2 + b.params.osm_coeff_param_3 * s**3 -<<<<<<< Updated upstream - + b.params.osm_coeff_param_4 * s**4 - + b.params.osm_coeff_param_5 * s**5 -======= - # + b.params.osm_coeff_param_4 * s**4 - # + b.params.osm_coeff_param_5 * s**5 ->>>>>>> Stashed changes ) return b.pressure_osm_phase[p] == pressure_osm_phase @@ -662,64 +609,63 @@ def rule_cp_mass_phase( self.params.phase_list, rule=rule_cp_mass_phase ) -<<<<<<< Updated upstream -======= def _heat_separation_phase(self): self.heat_separation_phase = Var( self.params.phase_list, - initialize = 1e6, - bounds = (0,1e10), - units = pyunits.J / pyunits.s, - doc="Heat of liquid separation" + initialize=1e6, + bounds=(0, 1e10), + units=pyunits.J / pyunits.s, + doc="Heat of liquid separation", ) self.liquid_separation = Param( - initialize = 1e-6, + initialize=1e-6, mutable=True, - units = pyunits.dimensionless, - doc = 'Indication of whether liquid separation will happen', + units=pyunits.dimensionless, + doc="Indication of whether liquid separation will happen", ) - def rule_heat_separation_phase( - b,p - ): - test = (b.liquid_separation - * b.params.separation_heat - * b.flow_mass_phase_comp[p, "H2O"] - - * 1e-3 * pyunits.m**3/ pyunits.kg - - # * b.mass_frac_phase_comp[p,"H2O"] - # / (1-b.mass_frac_phase_comp[p,"H2O"]) - # * (1-0.8) / 0.8 + self.mass_frac_after_separation = Param( + initialize=0.8, + mutable=True, + units=pyunits.dimensionless, + doc="The strong draw mass fraction after separation", + ) + + def rule_heat_separation_phase(b, p): + heat = ( + b.liquid_separation + * b.params.separation_heat + * b.flow_mass_phase_comp[p, "H2O"] + * 1e-3 + * pyunits.m**3 + / pyunits.kg # Density of pure water + * b.mass_frac_phase_comp[p, "H2O"] + / (1 - b.mass_frac_phase_comp[p, "H2O"]) + * (1 - b.mass_frac_after_separation) + / b.mass_frac_after_separation ) - # Expr_if & Smooth_min - - - return b.heat_separation_phase[p] == pyunits.convert(test, to_units = pyunits.J / pyunits.s) + return b.heat_separation_phase[p] == pyunits.convert( + heat, to_units=pyunits.J / pyunits.s + ) self.eq_heat_separation_phase = Constraint( self.params.phase_list, rule=rule_heat_separation_phase ) ->>>>>>> Stashed changes def _enth_mass_phase(self): self.enth_mass_phase = Var( self.params.phase_list, initialize=1e6, -<<<<<<< Updated upstream - bounds=(1, 1e9), -======= bounds=(1, 1e10), ->>>>>>> Stashed changes units=pyunits.J * pyunits.kg**-1, doc="Specific enthalpy", ) - def rule_enth_mass_phase( - b, p - ): - return (b.enth_mass_phase[p] == b.cp_mass_phase[p] * (b.temperature - 273.15 * pyunits.K)) - + def rule_enth_mass_phase(b, p): + return b.enth_mass_phase[p] == b.cp_mass_phase[p] * ( + b.temperature - 273.15 * pyunits.K + ) + self.eq_enth_mass_phase = Constraint( self.params.phase_list, rule=rule_enth_mass_phase ) @@ -730,15 +676,12 @@ def _enth_flow(self): def rule_enth_flow(b): # enthalpy flow [J/s] return ( sum(b.flow_mass_phase_comp["Liq", j] for j in b.params.component_list) -<<<<<<< Updated upstream * b.enth_mass_phase["Liq"] -======= - * b.enth_mass_phase["Liq"] + b.heat_separation_phase["Liq"] ->>>>>>> Stashed changes ) self.enth_flow = Expression(rule=rule_enth_flow) + # ----------------------------------------------------------------------------- # General Methods def get_material_flow_terms(self, p, j): @@ -748,7 +691,7 @@ def get_material_flow_terms(self, p, j): def get_enthalpy_flow_terms(self, p): """Create enthalpy flow terms.""" return self.enth_flow - + def default_material_balance_type(self): return MaterialBalanceType.componentTotal @@ -756,11 +699,7 @@ def get_material_flow_basis(self): return MaterialFlowBasis.mass def default_energy_balance_type(self): -<<<<<<< Updated upstream - return EnergyBalanceType.none -======= return EnergyBalanceType.enthalpyTotal ->>>>>>> Stashed changes def define_state_vars(self): """Define state vars.""" @@ -849,14 +788,12 @@ def calculate_scaling_factors(self): ), ) -<<<<<<< Updated upstream -======= - if self.is_property_constructed("heat_separation_phase"): - iscale.set_scaling_factor( - self.heat_separation_phase, - 1 / self.liquid_separation.value - * iscale.get_scaling_factor(self.flow_mass_phase_comp["Liq", "H2O"]), - ) + # if self.is_property_constructed("heat_separation_phase"): + # iscale.set_scaling_factor( + # self.heat_separation_phase, + # 1 / self.liquid_separation.value + # * iscale.get_scaling_factor(self.heat_separation_phase["Liq"]), + # ) if self.is_property_constructed("enth_mass_phase"): iscale.set_scaling_factor( @@ -865,7 +802,6 @@ def calculate_scaling_factors(self): * iscale.get_scaling_factor(self.temperature), ) ->>>>>>> Stashed changes if self.is_property_constructed("enth_flow"): iscale.set_scaling_factor( self.enth_flow, diff --git a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py index 17b242b2..a0cdf557 100644 --- a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py @@ -63,13 +63,8 @@ def configure(self): ("flow_mass_phase_comp", ("Liq", "DrawSolution")): 1, } self.stateblock_statistics = { -<<<<<<< Updated upstream - "number_variables": 13, - "number_total_constraints": 9, -======= "number_variables": 14, "number_total_constraints": 10, ->>>>>>> Stashed changes "number_unused_variables": 1, "default_degrees_of_freedom": 3, } # 4 state vars, but pressure is not active @@ -80,16 +75,12 @@ def configure(self): ("flow_vol_phase", "Liq"): 9.2170e-4, ("conc_mass_phase_comp", ("Liq", "H2O")): 216.975, ("conc_mass_phase_comp", ("Liq", "DrawSolution")): 867.899, -<<<<<<< Updated upstream - ("pressure_osm_phase", "Liq"): 2.0447e7, - ("cp_mass_phase", "Liq"): 2257.78, -======= ("pressure_osm_phase", "Liq"): 1.5697e7, ("cp_mass_phase", "Liq"): 2257.78, # ("heat_separation_phase", "Liq"): 1e-5, ->>>>>>> Stashed changes } + @pytest.mark.unit def test_parameter_block(m): assert isinstance(m.fs.properties.component_list, Set) @@ -149,12 +140,9 @@ def test_parameters(m): m.fs.stream[0].temperature.fix(25 + 273.15) m.fs.stream[0].pressure.fix(101325) -<<<<<<< Updated upstream -======= # Active liquid separation m.fs.stream[0].liquid_separation = 1 ->>>>>>> Stashed changes m.fs.properties.set_default_scaling("flow_mass_phase_comp", 1, index=("Liq", "H2O")) m.fs.properties.set_default_scaling( "flow_mass_phase_comp", 1, index=("Liq", "DrawSolution") @@ -181,11 +169,7 @@ def test_parameters(m): 6.0171e4, rel=1e-3 ) assert value(m.fs.stream[0].pressure_osm_phase["Liq"]) == pytest.approx( -<<<<<<< Updated upstream - 9.64168e6, rel=1e-3 -======= 9.9468e6, rel=1e-3 ->>>>>>> Stashed changes ) assert value(m.fs.stream[0].flow_vol_phase["Liq"]) == pytest.approx( 1.8459e-3, rel=1e-3 @@ -202,15 +186,12 @@ def test_parameters(m): assert value( m.fs.stream[0].mass_frac_phase_comp["Liq", "DrawSolution"] ) == pytest.approx(0.7, rel=1e-3) -<<<<<<< Updated upstream -======= - assert value( - m.fs.stream[0].enth_mass_phase["Liq"] - ) == pytest.approx(60171.0, rel=1e-3) - assert value( - m.fs.stream[0].heat_separation_phase["Liq"] - ) == pytest.approx(6750, rel=1e-3) ->>>>>>> Stashed changes + assert value(m.fs.stream[0].enth_mass_phase["Liq"]) == pytest.approx( + 60171.0, rel=1e-3 + ) + assert value(m.fs.stream[0].heat_separation_phase["Liq"]) == pytest.approx( + 6750, rel=1e-3 + ) @pytest.mark.component diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py index 2b4d1f13..8f5fcf28 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py @@ -37,9 +37,9 @@ ) import idaes.logger as idaeslog -# from watertap_contrib.reflo.costing.units.forward_osmosis_zo import ( -# cost_forward_osmosis, -# ) +from watertap_contrib.reflo.costing.units.forward_osmosis_zo import ( + cost_forward_osmosis, +) _log = idaeslog.getLogger(__name__) __author__ = "Zhuoran Zhang" @@ -124,9 +124,6 @@ def build(self): initialize=0.3, bounds=(0, 1), units=pyunits.dimensionless, -<<<<<<< Updated upstream - doc="Recovery ratio", -======= doc="Recovery ratio of FO", ) @@ -135,7 +132,6 @@ def build(self): bounds=(0, 1), units=pyunits.dimensionless, doc="Recovery ratio of nanofiltration", ->>>>>>> Stashed changes ) self.regeneration_temp = Var( @@ -281,27 +277,25 @@ def build(self): def eq_brine_vol_flow(b): return b.brine_props[0].flow_vol_phase["Liq"] == b.feed_props[ 0 - ].flow_vol_phase["Liq"] * (1 - b.recovery_ratio) + ].flow_vol_phase["Liq"] * ( + 1 - b.recovery_ratio / b.nanofiltration_recovery_ratio + ) @self.Constraint(doc="Brine salinity") def eq_brine_salinity(b): return b.brine_props[0].conc_mass_phase_comp["Liq", "TDS"] == b.feed_props[ 0 - ].conc_mass_phase_comp["Liq", "TDS"] / (1 - b.recovery_ratio) + ].conc_mass_phase_comp["Liq", "TDS"] / ( + 1 - b.recovery_ratio / b.nanofiltration_recovery_ratio + ) @self.Constraint(doc="Product water flow rate") def eq_product_water_mass_flow(b): return ( -<<<<<<< Updated upstream - b.product_props[0].flow_mass_phase_comp["Liq", "H2O"] - == b.feed_props[0].flow_mass_phase_comp["Liq", "H2O"] - - b.brine_props[0].flow_mass_phase_comp["Liq", "H2O"] -======= b.product_props[0].flow_vol_phase["Liq"] == b.feed_props[0].flow_vol_phase["Liq"] * b.recovery_ratio / b.nanofiltration_recovery_ratio ->>>>>>> Stashed changes ) @self.Constraint(doc="Draw solution mass remains same in the system") @@ -348,11 +342,8 @@ def eq_heat_mixing(b): def eq_temp_dif_membrane1(b): return b.delta_temp_membrane == pyunits.convert( b.heat_transfer_to_weak_draw -<<<<<<< Updated upstream - * b.product_props[0].flow_vol_phase["Liq"] -======= - * pyunits.m**3 / pyunits.s ->>>>>>> Stashed changes + * pyunits.m**3 + / pyunits.s / b.weak_draw_props[0].dens_mass_phase["Liq"] / b.weak_draw_props[0].flow_vol_phase["Liq"] / b.weak_draw_props[0].cp_mass_phase["Liq"], @@ -365,11 +356,8 @@ def eq_temp_dif_membrane1(b): def eq_temp_dif_membrane2(b): return b.delta_temp_membrane == pyunits.convert( b.heat_transfer_to_brine -<<<<<<< Updated upstream - * b.product_props[0].flow_vol_phase["Liq"] -======= - * pyunits.m**3 / pyunits.s ->>>>>>> Stashed changes + * pyunits.m**3 + / pyunits.s / b.brine_props[0].dens_mass_phase["Liq"] / b.brine_props[0].flow_vol_phase["Liq"] / b.brine_props[0].cp_mass_phase["Liq"], @@ -482,16 +470,13 @@ def initialize_build( else: state_args[k] = state_dict[k].value -<<<<<<< Updated upstream -======= # Initial guess of brine properties ->>>>>>> Stashed changes state_args_brine = deepcopy(state_args) for p, j in blk.brine_props.phase_component_set: if p == "Liq" and j == "H2O": state_args_brine["flow_mass_phase_comp"][(p, j)] = ( state_args["flow_mass_phase_comp"][(p, j)] - * (1 - blk.recovery_ratio) + * (1 - blk.recovery_ratio / blk.nanofiltration_recovery_ratio) * pyunits.kg / pyunits.s ) @@ -518,41 +503,28 @@ def initialize_build( ].value else: state_args_draw_solution[k] = state_dict_draw_solution[k].value -<<<<<<< Updated upstream - blk.strong_draw_props.initialize( - outlvl=outlvl, - optarg=optarg, - solver=solver, - state_args=state_args_draw_solution, - ) -======= - # Initial guess of weak draw properties state_args_weak_draw = deepcopy(state_args_draw_solution) for p, j in blk.weak_draw_props.phase_component_set: if p == "Liq" and j == "H2O": state_args_weak_draw["flow_mass_phase_comp"][(p, j)] = ( state_args["flow_mass_phase_comp"][(p, j)] - * blk.recovery_ratio * 1.5 + * blk.recovery_ratio + / blk.nanofiltration_recovery_ratio + * 1.5 * pyunits.kg / pyunits.s ) elif p == "Liq" and j == "DrawSolution": - state_args_weak_draw["flow_mass_phase_comp"][(p, j)] = ( - state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] - ) ->>>>>>> Stashed changes + state_args_weak_draw["flow_mass_phase_comp"][ + (p, j) + ] = state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] blk.weak_draw_props.initialize( outlvl=outlvl, optarg=optarg, solver=solver, -<<<<<<< Updated upstream - state_args=state_args_draw_solution, - ) - -======= state_args=state_args_weak_draw, ) @@ -560,13 +532,13 @@ def initialize_build( state_args_strong_draw = deepcopy(state_args_draw_solution) for p, j in blk.strong_draw_props.phase_component_set: if p == "Liq" and j == "DrawSolution": - state_args_strong_draw["flow_mass_phase_comp"][(p, j)] = ( - state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] - ) + state_args_strong_draw["flow_mass_phase_comp"][ + (p, j) + ] = state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] elif p == "Liq" and j == "H2O": state_args_strong_draw["flow_mass_phase_comp"][(p, j)] = ( state_args_strong_draw["flow_mass_phase_comp"][(p, "DrawSolution")] - * 0.25 # typical draw : water in strong draw solution + * 0.25 # typical draw : water in strong draw solution ) blk.strong_draw_props.initialize( @@ -580,24 +552,15 @@ def initialize_build( state_args_reg_draw = deepcopy(state_args_draw_solution) for p, j in blk.reg_draw_props.phase_component_set: if p == "Liq": - state_args_reg_draw["flow_mass_phase_comp"][(p, j)] = ( - state_args_strong_draw["flow_mass_phase_comp"][(p, j)] - ) + state_args_reg_draw["flow_mass_phase_comp"][ + (p, j) + ] = state_args_strong_draw["flow_mass_phase_comp"][(p, j)] + state_args_strong_draw["temperature"] = 90 + 273.15 ->>>>>>> Stashed changes blk.reg_draw_props.initialize( outlvl=outlvl, optarg=optarg, solver=solver, -<<<<<<< Updated upstream - state_args=state_args_draw_solution, - ) - - state_args_prod = deepcopy(state_args) - for p, j in blk.product_props.phase_component_set: - if j == "DrawSolution": - state_args_prod["flow_mass_phase_comp"][(p, j)] = 0 -======= state_args=state_args_reg_draw, ) @@ -607,28 +570,23 @@ def initialize_build( if p == "Liq" and j == "H2O": state_args_product["flow_mass_phase_comp"][(p, j)] = ( state_args["flow_mass_phase_comp"][(p, j)] - * (blk.recovery_ratio) + * (blk.recovery_ratio / blk.nanofiltration_recovery_ratio) * pyunits.kg / pyunits.s ) elif p == "Liq" and j == "DrawSolution": state_args_product["flow_mass_phase_comp"][(p, j)] = ( state_args_product["flow_mass_phase_comp"][(p, "H2O")] - * 0.01 # Typical mass fraction of draw in product + * 0.01 # Typical mass fraction of draw in product * pyunits.kg / pyunits.s ) ->>>>>>> Stashed changes blk.product_props.initialize( outlvl=outlvl, optarg=optarg, solver=solver, -<<<<<<< Updated upstream - state_args=state_args_draw_solution, -======= state_args=state_args_product, ->>>>>>> Stashed changes ) # Check degree of freedom @@ -662,13 +620,10 @@ def unfix_and_fix_freedom(self, mass_frac_strong_draw, mass_frac_product): # Touch properties that need to be calculated self.reg_draw_props[0].flow_vol_phase["Liq"] self.reg_draw_props[0].mass_frac_phase_comp["Liq", "DrawSolution"] -<<<<<<< Updated upstream -======= self.reg_draw_props[0].cp_mass_phase["Liq"] self.product_props[0].flow_vol_phase["Liq"] self.product_props[0].mass_frac_phase_comp["Liq", "DrawSolution"] self.product_props[0].cp_mass_phase["Liq"] ->>>>>>> Stashed changes def calculate_scaling_factors(self): super().calculate_scaling_factors() @@ -790,6 +745,6 @@ def _get_performance_contents(self, time_point=0): return {"vars": var_dict} - # @property - # def default_costing_method(self): - # return cost_forward_osmosis + @property + def default_costing_method(self): + return cost_forward_osmosis diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py index ff5d8640..5f76a46b 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py @@ -20,8 +20,12 @@ import re from pyomo.network import Port from idaes.core import FlowsheetBlock, UnitModelCostingBlock -from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ForwardOsmosisZO -from watertap_contrib.reflo.property_models.fo_draw_solution_properties import FODrawSolutionParameterBlock +from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ( + ForwardOsmosisZO, +) +from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( + FODrawSolutionParameterBlock, +) from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock from watertap_contrib.reflo.costing import REFLOCosting @@ -72,10 +76,7 @@ def FO_frame(self): # System specifications recovery_ratio = 0.3 # Assumed FO recovery ratio -<<<<<<< Updated upstream -======= - nanofiltration_recovery_ratio = 0.8 # Nanofiltration recovery ratio ->>>>>>> Stashed changes + nanofiltration_recovery_ratio = 0.8 # Nanofiltration recovery ratio dp_brine = 0 # Required pressure over brine osmotic pressure (Pa) heat_mixing = 105 # Heat of mixing in the membrane (MJ/m3 product) reneration_temp = 90 # Separation temperature of the draw solution (C) @@ -84,24 +85,24 @@ def FO_frame(self): feed_vol_flow = 3.704 # Feed water volumetric flow rate (m3/s) feed_TDS_mass = 0.035 # TDS mass fraction of feed strong_draw_temp = 20 # Strong draw solution inlet temperature (C) - strong_draw_mass = 0.8 # Strong draw solution mass fraction - product_draw_mass = 0.01 # Mass fraction of draw in the product water + strong_draw_mass_frac = 0.8 # Strong draw solution mass fraction + product_draw_mas_frac = 0.01 # Mass fraction of draw in the product water fo.recovery_ratio.fix(recovery_ratio) -<<<<<<< Updated upstream -======= fo.nanofiltration_recovery_ratio.fix(nanofiltration_recovery_ratio) ->>>>>>> Stashed changes fo.dp_brine.fix(dp_brine) fo.heat_mixing.fix(heat_mixing) fo.regeneration_temp.fix(reneration_temp + 273.15) fo.separator_temp_loss.fix(separator_temp_loss) - # Specifyf strong draw solution properties + # Specify strong draw solution properties fo.strong_draw_props.calculate_state( var_args={ ("flow_vol_phase", "Liq"): 1, - ("mass_frac_phase_comp", ("Liq", "DrawSolution")): strong_draw_mass, + ( + "mass_frac_phase_comp", + ("Liq", "DrawSolution"), + ): strong_draw_mass_frac, ("temperature", None): strong_draw_temp + 273.15, ("pressure", None): 101325, }, @@ -110,11 +111,14 @@ def FO_frame(self): strong_draw.flow_mass_phase_comp["Liq", "DrawSolution"].unfix() - # Specifyf product water properties + # Specify product water properties fo.product_props.calculate_state( var_args={ ("flow_vol_phase", "Liq"): 1, - ("mass_frac_phase_comp", ("Liq", "DrawSolution")): product_draw_mass, + ( + "mass_frac_phase_comp", + ("Liq", "DrawSolution"), + ): product_draw_mas_frac, ("temperature", None): reneration_temp - separator_temp_loss + 273.15, ("pressure", None): 101325, }, @@ -137,10 +141,10 @@ def FO_frame(self): # Set scaling factors for mass flow rates m.fs.water_prop.set_default_scaling( - "flow_mass_phase_comp", 1, index=("Liq", "H2O") + "flow_mass_phase_comp", 1e-1, index=("Liq", "H2O") ) m.fs.water_prop.set_default_scaling( - "flow_mass_phase_comp", 1e2, index=("Liq", "TDS") + "flow_mass_phase_comp", 1e1, index=("Liq", "TDS") ) m.fs.draw_solution_prop.set_default_scaling( "flow_mass_phase_comp", 1, index=("Liq", "H2O") @@ -150,7 +154,7 @@ def FO_frame(self): ) return m - + @pytest.mark.unit def test_build(self, FO_frame): m = FO_frame @@ -163,11 +167,7 @@ def test_build(self, FO_frame): assert len(port.vars) == 3 # test statistics -<<<<<<< Updated upstream - assert number_variables(m) == 163 -======= assert number_variables(m) == 164 ->>>>>>> Stashed changes assert number_total_constraints(m) == 50 assert number_unused_variables(m) == 70 # vars from property package parameters @@ -204,11 +204,7 @@ def test_initialize(self, FO_frame): def test_solve(self, FO_frame): m = FO_frame -<<<<<<< Updated upstream - # Unfix the state variables and fix mass fractrion of two state blocks -======= # Unfix the state variables and fix mass fractrion of draw solution state blocks ->>>>>>> Stashed changes strong_draw_mass = 0.8 # Strong draw solution mass fraction product_draw_mass = 0.01 # Mass fraction of draw in the product water m.fs.fo.unfix_and_fix_freedom(strong_draw_mass, product_draw_mass) @@ -229,77 +225,34 @@ def test_solution(self, FO_frame): assert value( strong_draw.flow_mass_phase_comp["Liq", "DrawSolution"] -<<<<<<< Updated upstream - ) == pytest.approx(1516.85, rel=1e-3) - assert value(strong_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 379.21, rel=1e-3 - ) - assert value(strong_draw.flow_vol_phase["Liq"]) == pytest.approx( - 1.748, rel=1e-3 - ) - assert value( - weak_draw.flow_mass_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(1516.85, rel=1e-3) - assert value(weak_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 1479.86, rel=1e-3 - ) - assert value(weak_draw.flow_vol_phase["Liq"]) == pytest.approx(2.804, rel=1e-3) - assert value( - weak_draw.mass_frac_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(0.5062, rel=1e-3) -======= - ) == pytest.approx(1585.94, rel=1e-3) + ) == pytest.approx(1782.05, rel=1e-3) assert value(strong_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 396.48, rel=1e-3 + 445.51, rel=1e-3 ) assert value(strong_draw.flow_vol_phase["Liq"]) == pytest.approx( - 1.827, rel=1e-3 + 2.053, rel=1e-3 ) assert value( weak_draw.flow_mass_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(1585.94, rel=1e-3) + ) == pytest.approx(1782.05, rel=1e-3) assert value(weak_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 1497.13, rel=1e-3 + 1546.16, rel=1e-3 ) - assert value(weak_draw.flow_vol_phase["Liq"]) == pytest.approx(2.883, rel=1e-3) - assert value(weak_draw.temperature) == pytest.approx(293.26, rel=1e-3) + assert value(weak_draw.flow_vol_phase["Liq"]) == pytest.approx(3.106, rel=1e-3) + assert value(weak_draw.temperature) == pytest.approx(293.61, rel=1e-3) assert value( weak_draw.mass_frac_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(0.5144, rel=1e-3) ->>>>>>> Stashed changes + ) == pytest.approx(0.5354, rel=1e-3) assert value(brine.mass_frac_phase_comp["Liq", "TDS"]) == pytest.approx( - 0.04954, rel=1e-3 + 0.05525, rel=1e-3 ) assert value(brine.conc_mass_phase_comp["Liq", "TDS"]) == pytest.approx( - 51.321, rel=1e-3 + 57.48, rel=1e-3 ) assert value(brine.pressure_osm_phase["Liq"]) == pytest.approx( -<<<<<<< Updated upstream - 3699254, rel=1e-3 - ) - assert value(product.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 1116.09, rel=1e-3 - ) - assert value( - product.flow_mass_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(11.273, rel=1e-3) - assert value(product.flow_vol_phase["Liq"]) == pytest.approx(1.1255, rel=1e-3) - assert value(reg_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 379.21, rel=1e-3 - ) - assert value( - reg_draw.flow_mass_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(1516.85, rel=1e-3) - assert value(m.fs.fo.delta_temp_membrane) == pytest.approx(6.2687, rel=1e-3) - assert value(m.fs.fo.membrane_temp) == pytest.approx(287.689, rel=1e-3) - assert value(m.fs.fo.heat_transfer_to_weak_draw) == pytest.approx( - 46.267, rel=1e-3 - ) - assert value(m.fs.fo.heat_transfer_to_brine) == pytest.approx(58.733, rel=1e-3) -======= - 3689527, rel=1e-3 + 4163102, rel=1e-3 ) - assert value(brine.temperature) == pytest.approx(293.26, rel=1e-3) + assert value(brine.temperature) == pytest.approx(293.61, rel=1e-3) assert value(product.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( 1377.39, rel=1e-3 ) @@ -308,15 +261,14 @@ def test_solution(self, FO_frame): ) == pytest.approx(13.913, rel=1e-3) assert value(product.flow_vol_phase["Liq"]) == pytest.approx(1.389, rel=1e-3) assert value(reg_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 396.48, rel=1e-3 + 445.51, rel=1e-3 ) assert value( reg_draw.flow_mass_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(1585.94, rel=1e-3) - assert value(m.fs.fo.delta_temp_membrane) == pytest.approx(5.52, rel=1e-3) + ) == pytest.approx(1782.05, rel=1e-3) + assert value(m.fs.fo.delta_temp_membrane) == pytest.approx(5.70, rel=1e-3) assert value(m.fs.fo.membrane_temp) == pytest.approx(287.689, rel=1e-3) assert value(m.fs.fo.heat_transfer_to_weak_draw) == pytest.approx( - 46.84, rel=1e-3 + 51.45, rel=1e-3 ) - assert value(m.fs.fo.heat_transfer_to_brine) == pytest.approx(58.16, rel=1e-3) ->>>>>>> Stashed changes + assert value(m.fs.fo.heat_transfer_to_brine) == pytest.approx(53.55, rel=1e-3) From 1d7208cc65d0694dc6e4a9185b17bd36f1fcc665 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Mon, 1 Jul 2024 09:24:04 -0400 Subject: [PATCH 10/34] add test --- .../example_flowsheets/fo_trevi_flowsheet.py | 80 ++--------- .../test/test_fo_trevi_flowsheet.py | 129 ++++++++++++++++++ .../reflo/costing/units/forward_osmosis_zo.py | 15 +- 3 files changed, 149 insertions(+), 75 deletions(-) create mode 100644 src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py index 5d188d4f..332a9fa2 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py @@ -61,7 +61,7 @@ ForwardOsmosisZO, ) from watertap.core.util.initialization import check_dof -from watertap_contrib.reflo.costing import TreatmentCosting, REFLOCosting +from watertap_contrib.reflo.costing import TreatmentCosting def build_fo_trevi_flowsheet( @@ -235,7 +235,7 @@ def Cooler_out_temp(b): # Calculate system capacity (m3/day) fo = mfs.fo - @fo.Expression(doc="Calculate system capacity (m3/day)") + @mfs.Expression(doc="Calculate system capacity (m3/day)") def system_capacity(b): return pyunits.convert( mfs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "H2O"] @@ -244,10 +244,10 @@ def system_capacity(b): ) # Calculate specific thermal energy consumption of the system (k) - @fo.Expression(doc="Calculate specific thermal energy consumption (kWh/m3)") + @mfs.Expression(doc="Calculate specific thermal energy consumption (kWh/m3)") def specific_energy_consumption_thermal(b): return pyunits.convert( - (mfs.H1.heat_duty[0] + mfs.H2.heat_duty[0]) / fo.system_capacity, + (mfs.H1.heat_duty[0] + mfs.H2.heat_duty[0]) / mfs.system_capacity, to_units=pyunits.kWh / pyunits.m**3, ) @@ -504,80 +504,20 @@ def fix_dof_and_initialize(m, strong_draw_mass_frac=0.8, product_draw_mass_frac= m.fs.Cooler.initialize() -if __name__ == "__main__": - - """ - Build flowsheet with specified model configs - """ - # For feed water state variables, i.e. feed temperature, volume flow rate, concentration - # they can be unfixed after initialization, so as to connect with other unit models - m = build_fo_trevi_flowsheet( - recovery_ratio=0.3, # Assumed FO recovery ratio - RO_recovery_ratio=0.9, # RO recovery ratio - NF_recovery_ratio=0.8, # Nanofiltration recovery ratio - dp_brine=0, # Required pressure over brine osmotic pressure (Pa) - heat_mixing=105, # Heat of mixing in the membrane (MJ/m3 product) - separation_temp=90, # Separation temperature of the draw solution (C) - separator_temp_loss=1, # Temperature loss in the separator (K) - feed_temperature=13, # Feed water temperature (C) - feed_vol_flow=3.704, # Feed water volumetric flow rate (m3/s) - feed_TDS_mass=0.035, # TDS mass fraction of feed - strong_draw_temp=20, # Strong draw solution inlet temperature (C) - strong_draw_mass=0.8, # Strong draw solution mass fraction - product_draw_mass=0.01, # Mass fraction of draw in the product water - ) - - fix_dof_and_initialize( - m, strong_draw_mass_frac=0.8, product_draw_mass_frac=0.01 # same input as above - ) # same input as above - - # Specify the temperature of the weak draw solution and product water after going through HX1 - m.fs.HX1.overall_heat_transfer_coefficient[0].unfix() - m.fs.HX2.overall_heat_transfer_coefficient[0].unfix() - m.fs.HX1.weak_draw_outlet.temperature.fix(80 + 273.15) - m.fs.HX1.product_water_outlet.temperature.fix(28 + 273.15) - - # Solve - check_dof(m, fail_flag=True) - solver = get_solver() - results = solver.solve(m) - assert_optimal_termination(results) - - # Add cost package of Trevi FO system - m.fs.costing = TreatmentCosting() - m.fs.costing.base_currency = pyunits.USD_2020 - - # Create cost block for FO - m.fs.fo.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing) - - # Add LCOW component - m.fs.costing.cost_process() - m.fs.costing.add_annual_water_production(m.fs.fo.system_capacity) - m.fs.costing.add_LCOW(m.fs.fo.system_capacity) - - # Solve - check_dof(m, fail_flag=True) - solver = get_solver() - results = solver.solve(m) - assert_optimal_termination(results) - - # Retrieve outputs +def get_flowsheet_performance(m): overall_performance = { - "Production capacity (m3/day)": value(m.fs.fo.system_capacity), + "Production capacity (m3/day)": value(m.fs.system_capacity), "Specific thermal energy consumption (kWh/m3)": value( - m.fs.fo.specific_energy_consumption_thermal + m.fs.specific_energy_consumption_thermal ), "Thermal power requirement (kW)": value( - m.fs.fo.specific_energy_consumption_thermal + m.fs.specific_energy_consumption_thermal ) - * value(m.fs.fo.system_capacity) + * value(m.fs.system_capacity) / 24, "LCOW ($/m3)": m.fs.costing.LCOW(), } - for i, v in overall_performance.items(): - print(i, round(v, 2)) - operational_parameters = { # Heat exchanger HX1 temperatures "HX1 cold in temp": m.fs.HX1.weak_draw_inlet.temperature[0].value - 273.15, @@ -680,3 +620,5 @@ def fix_dof_and_initialize(m, strong_draw_mass_frac=0.8, product_draw_mass_frac= "Heater 2 heat load": m.fs.H2.heat_duty[0].value, "Cooler heat load": m.fs.Cooler.heat_duty[0].value, } + + return overall_performance, operational_parameters diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py new file mode 100644 index 00000000..3f466cc8 --- /dev/null +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -0,0 +1,129 @@ +################################################################################# +# WaterTAP Copyright (c) 2020-2023, 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 pytest +from pyomo.environ import ( + ConcreteModel, + assert_optimal_termination, + units as pyunits, +) + +from watertap_contrib.reflo.analysis.example_flowsheets.fo_trevi_flowsheet import ( + build_fo_trevi_flowsheet, + fix_dof_and_initialize, + get_flowsheet_performance, +) +from watertap_contrib.reflo.costing import TreatmentCosting +from idaes.core import UnitModelCostingBlock +from idaes.core.solvers import get_solver +from idaes.core.util.exceptions import ( + ConfigurationError, +) +from idaes.core.util.model_statistics import ( + degrees_of_freedom, +) + +solver = get_solver() + + +class TestVAGMDbatch: + @pytest.fixture(scope="class") + def fo_trevi_frame(self): + + m = build_fo_trevi_flowsheet( + recovery_ratio=0.3, # Assumed FO recovery ratio + RO_recovery_ratio=0.9, # RO recovery ratio + NF_recovery_ratio=0.8, # Nanofiltration recovery ratio + dp_brine=0, # Required pressure over brine osmotic pressure (Pa) + heat_mixing=105, # Heat of mixing in the membrane (MJ/m3 product) + separation_temp=90, # Separation temperature of the draw solution (C) + separator_temp_loss=1, # Temperature loss in the separator (K) + feed_temperature=13, # Feed water temperature (C) + feed_vol_flow=3.704, # Feed water volumetric flow rate (m3/s) + feed_TDS_mass=0.035, # TDS mass fraction of feed + strong_draw_temp=20, # Strong draw solution inlet temperature (C) + strong_draw_mass=0.8, # Strong draw solution mass fraction + product_draw_mass=0.01, # Mass fraction of draw in the product water + ) + + fix_dof_and_initialize( + m, strong_draw_mass_frac=0.8, product_draw_mass_frac=0.01 + ) # same input as above + + # Specify the temperature of the weak draw solution and product water after going through HX1 + m.fs.HX1.overall_heat_transfer_coefficient[0].unfix() + m.fs.HX2.overall_heat_transfer_coefficient[0].unfix() + m.fs.HX1.weak_draw_outlet.temperature.fix(80 + 273.15) + m.fs.HX1.product_water_outlet.temperature.fix(28 + 273.15) + + return m + + @pytest.mark.unit + def test_dof(self, fo_trevi_frame): + m = fo_trevi_frame + assert degrees_of_freedom(m) == 0 + + @pytest.mark.component + def test_solve(self, fo_trevi_frame): + m = fo_trevi_frame + + results = solver.solve(m) + assert_optimal_termination(results) + + @pytest.mark.component + def test_costing(self, fo_trevi_frame): + m = fo_trevi_frame + # Add cost package of Trevi FO system + m.fs.costing = TreatmentCosting() + m.fs.costing.base_currency = pyunits.USD_2020 + + # Create cost block for FO + m.fs.fo.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing) + + # Add LCOW component + m.fs.costing.cost_process() + m.fs.costing.add_annual_water_production(m.fs.system_capacity) + m.fs.costing.add_LCOW(m.fs.system_capacity) + + solver = get_solver() + results = solver.solve(m) + assert_optimal_termination(results) + + @pytest.mark.component + def test_solution(self, fo_trevi_frame): + m = fo_trevi_frame + + overall_performance, operational_parameters = get_flowsheet_performance(m) + + assert overall_performance["Production capacity (m3/day)"] == pytest.approx( + 85683.32, abs=1e-3 + ) + assert overall_performance[ + "Specific thermal energy consumption (kWh/m3)" + ] == pytest.approx(28.3, rel=1e-3) + assert overall_performance["Thermal power requirement (kW)"] == pytest.approx( + 101038.77, rel=1e-3 + ) + assert overall_performance["LCOW ($/m3)"] == pytest.approx(0.579, rel=1e-3) + + assert operational_parameters["HX1 cold in temp"] == pytest.approx( + 21.49, rel=1e-3 + ) + assert operational_parameters["HX2 hot out temp"] == pytest.approx( + 32.09, rel=1e-3 + ) + assert operational_parameters["HX1 cold side heat load (MJ)"] == pytest.approx( + 353.49, rel=1e-3 + ) + assert operational_parameters["HX2 cold side heat load (MJ)"] == pytest.approx( + 286.20, rel=1e-3 + ) diff --git a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py index ad9e28e8..0fc57716 100644 --- a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py @@ -104,20 +104,23 @@ def cost_forward_osmosis(blk): make_fixed_operating_cost_var(blk) fo = blk.unit_model + test_fs = fo.flowsheet() + brine = fo.brine_props[0] blk.costing_package.add_cost_factor(blk, None) blk.annual_dist_production = pyo.units.convert( - fo.system_capacity, to_units=pyo.units.m**3 / pyo.units.year + test_fs.system_capacity, to_units=pyo.units.m**3 / pyo.units.year ) blk.capital_cost_constraint = pyo.Constraint( expr=blk.capital_cost - == fo_params.base_unit_cost * fo.system_capacity**fo_params.unit_cost_index + == fo_params.base_unit_cost + * test_fs.system_capacity**fo_params.unit_cost_index ) blk.labor_cost = pyo.Expression( expr=fo_params.base_labor_cost - * fo.system_capacity**fo_params.labor_cost_index + * test_fs.system_capacity**fo_params.labor_cost_index ) blk.fixed_operating_cost_constraint = pyo.Constraint( @@ -134,16 +137,16 @@ def cost_forward_osmosis(blk): ) blk.thermal_energy_flow = pyo.Expression( - expr=fo.specific_energy_consumption_thermal + expr=test_fs.specific_energy_consumption_thermal * pyo.units.convert( - fo.system_capacity, to_units=pyo.units.m**3 / pyo.units.hr + test_fs.system_capacity, to_units=pyo.units.m**3 / pyo.units.hr ) ) blk.electricity_flow = pyo.Expression( expr=fo_params.specific_energy_consumption_electric * pyo.units.convert( - fo.system_capacity, to_units=pyo.units.m**3 / pyo.units.hr + test_fs.system_capacity, to_units=pyo.units.m**3 / pyo.units.hr ) ) From d969c20c2673fe0dd9ff711fed0f7d48a6a916e8 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Mon, 1 Jul 2024 09:28:46 -0400 Subject: [PATCH 11/34] fix typo --- src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py index 0fc57716..4ec6da50 100644 --- a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py @@ -75,7 +75,7 @@ def build_forward_osmosis_cost_param_block(blk): initialize=0.05, units=costing.base_currency / pyo.units.m**3, bounds=(0, None), - doc="Cost of chemicals per m3 product", + doc="Cost of durable goods per m3 product", ) blk.cost_disposal = pyo.Var( From a456f029bd83032c31997c8d4f471ee08d0aca07 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Thu, 22 Aug 2024 13:28:22 -0400 Subject: [PATCH 12/34] fix convergence error --- .../unit_models/Trevi_fo_flowsheet.png | Bin 95399 -> 95445 bytes .../example_flowsheets/fo_trevi_flowsheet.py | 58 +++++-- .../test/test_fo_trevi_flowsheet.py | 15 +- .../fo_draw_solution_properties.py | 71 +++++---- .../tests/test_FO_draw_solution_properties.py | 21 ++- .../zero_order/forward_osmosis_zo.py | 148 +++++++++++++++++- .../zero_order/tests/test_forward_osmosis.py | 24 ++- 7 files changed, 253 insertions(+), 84 deletions(-) diff --git a/docs/_static/unit_models/Trevi_fo_flowsheet.png b/docs/_static/unit_models/Trevi_fo_flowsheet.png index 8aae891e3258df81fa3b5f889ca5c90c87ad0157..badca09421fe559cc374550bbacba6a641926dfd 100644 GIT binary patch literal 95445 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%0D6B=Sub^||S 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

a#=8^@#Tk-+@+Q!N9Fo~dP)AGs` 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 diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py index 332a9fa2..5228aaf0 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py @@ -39,7 +39,6 @@ list_badly_scaled_variables, ) from idaes.core import FlowsheetBlock, MaterialBalanceType, EnergyBalanceType -from idaes.core.solvers.get_solver import get_solver from idaes.core import UnitModelCostingBlock from idaes.models.unit_models import ( Mixer, @@ -54,13 +53,17 @@ # WaterTAP imports from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock +from watertap.core.solvers import get_solver +from watertap.core.util.initialization import check_dof + +# WaterTAP REFLO imports from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( FODrawSolutionParameterBlock, ) + from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ( ForwardOsmosisZO, ) -from watertap.core.util.initialization import check_dof from watertap_contrib.reflo.costing import TreatmentCosting @@ -360,12 +363,18 @@ def add_fo( ) -def fix_dof_and_initialize(m, strong_draw_mass_frac=0.8, product_draw_mass_frac=0.01): +def fix_dof_and_initialize( + m, + strong_draw_mass_frac=0.8, + product_draw_mass_frac=0.01, + NF_recovery_ratio=0.8, + RO_recovery_ratio=0.9, +): calculate_scaling_factors(m) m.fs.fo.initialize() - # Unfix the state variables and fix mass fractrion of two state blocks + # Unfix the state variables and fix mass fraction of two state blocks m.fs.fo.unfix_and_fix_freedom(strong_draw_mass_frac, product_draw_mass_frac) m.fs.S1.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( @@ -416,10 +425,15 @@ def fix_dof_and_initialize(m, strong_draw_mass_frac=0.8, product_draw_mass_frac= m.fs.HX1.initialize( state_args_1=state_args_HX1_hot, state_args_2=state_args_HX1_cold ) - m.fs.HX1.cold_side.properties_out[0].liquid_separation = 1 + # Cold side has liquid separation + m.fs.HX1.cold_side.properties_out[0].liquid_separation.fix(1) m.fs.HX1.cold_side.properties_out[ 0 ].mass_frac_after_separation = strong_draw_mass_frac + iscale.set_scaling_factor( + m.fs.HX1.cold_side.properties_out[0].heat_separation_phase, + 1e-5, + ) state_args_HX2_hot = { "flow_mass_phase_comp": { @@ -446,38 +460,54 @@ def fix_dof_and_initialize(m, strong_draw_mass_frac=0.8, product_draw_mass_frac= m.fs.HX2.initialize( state_args_1=state_args_HX2_hot, state_args_2=state_args_HX2_cold ) - m.fs.HX2.cold_side.properties_out[0].liquid_separation = 1 + # Cold side has liquid separation + m.fs.HX2.cold_side.properties_out[0].liquid_separation.fix(1) m.fs.HX2.cold_side.properties_out[ 0 ].mass_frac_after_separation = strong_draw_mass_frac + # Initialize separator S2 m.fs.S2.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value ) m.fs.S2.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value ) - m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( - m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + m.fs.S2.NF_reject.flow_mass_phase_comp[ + 0, "Liq", "H2O" + ].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value * ( + 1 - NF_recovery_ratio ) - m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( - m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + m.fs.S2.NF_reject.flow_mass_phase_comp[ + 0, "Liq", "DrawSolution" + ].value = m.fs.fo.product_props[0].flow_mass_phase_comp[ + "Liq", "DrawSolution" + ].value * ( + 1 - NF_recovery_ratio ) m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( - m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value * 1e-1 + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + * NF_recovery_ratio + * (1 - RO_recovery_ratio) ) m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value - * 1e-1 + * NF_recovery_ratio + * (1 - RO_recovery_ratio) ) m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + * NF_recovery_ratio + * RO_recovery_ratio ) m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + * NF_recovery_ratio + * RO_recovery_ratio ) m.fs.S2.initialize() + # Initialize mixer M1 m.fs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value ) @@ -492,13 +522,17 @@ def fix_dof_and_initialize(m, strong_draw_mass_frac=0.8, product_draw_mass_frac= ].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value m.fs.M1.outlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value + + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value ) m.fs.M1.outlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value ) + m.fs.M1.mixed_state[0].flow_vol_phase["Liq"] m.fs.M1.initialize() + # Initialize heaters m.fs.H1.initialize() m.fs.H2.initialize() m.fs.Cooler.initialize() diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py index 3f466cc8..70664607 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -16,21 +16,20 @@ assert_optimal_termination, units as pyunits, ) - -from watertap_contrib.reflo.analysis.example_flowsheets.fo_trevi_flowsheet import ( - build_fo_trevi_flowsheet, - fix_dof_and_initialize, - get_flowsheet_performance, -) -from watertap_contrib.reflo.costing import TreatmentCosting from idaes.core import UnitModelCostingBlock -from idaes.core.solvers import get_solver from idaes.core.util.exceptions import ( ConfigurationError, ) from idaes.core.util.model_statistics import ( degrees_of_freedom, ) +from watertap_contrib.reflo.analysis.example_flowsheets.fo_trevi_flowsheet import ( + build_fo_trevi_flowsheet, + fix_dof_and_initialize, + get_flowsheet_performance, +) +from watertap_contrib.reflo.costing import TreatmentCosting +from watertap.core.solvers import get_solver solver = get_solver() diff --git a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py index fee4c599..7fb30d05 100644 --- a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py @@ -14,6 +14,22 @@ This module contains a basic property package for simple water treatment models. Volumetric flow and component concentration are used to determine mass flow. """ +from pyomo.environ import ( + Param, + Binary, + Expression, + units as pyunits, + Reals, + NonNegativeReals, + Var, + Constraint, + Suffix, + value, + check_optimal_termination, + Expr_if, +) +from pyomo.common.config import ConfigValue + from idaes.core import ( EnergyBalanceType, MaterialBalanceType, @@ -25,7 +41,6 @@ ) from idaes.core.base.components import Solvent, Solute from idaes.core.base.phases import LiquidPhase -from idaes.core.solvers.get_solver import get_solver from idaes.core.util.misc import add_object_reference from idaes.core.util.initialization import ( fix_state_vars, @@ -38,29 +53,14 @@ ) import idaes.logger as idaeslog import idaes.core.util.scaling as iscale - from idaes.core.util.exceptions import ( ConfigurationError, InitializationError, PropertyPackageError, ) -from pyomo.environ import ( - Param, - Binary, - Expression, - units as pyunits, - Reals, - NonNegativeReals, - Var, - Constraint, - Suffix, - value, - check_optimal_termination, -) -from pyomo.common.config import ConfigValue - from watertap.core.util.scaling import transform_property_constraints +from watertap.core.solvers import get_solver __author__ = "Zhuoran Zhang" @@ -173,6 +173,7 @@ def build(self): self.set_default_scaling("cp_mass_phase", 1e-3, index="Liq") self.set_default_scaling("enth_mass_phase", 1e-5, index="Liq") self.set_default_scaling("heat_separation_phase", 1e-5, index="Liq") + self.set_default_scaling("liquid_separation", 1, index="Liq") @classmethod def define_metadata(cls, obj): @@ -203,6 +204,12 @@ def define_metadata(cls, obj): } ) + obj.define_custom_properties( + { + # "heat_separation_phase": {"method": "_heat_separation_phase"}, + } + ) + class _FODrawSolutionStateBlock(StateBlock): """ @@ -520,7 +527,7 @@ def rule_dens_mass_phase(b, p): # density, eqn derived from experimental data def _flow_vol_phase(self): self.flow_vol_phase = Var( self.params.phase_list, - initialize=1, + initialize=0.001, bounds=(0.0, None), units=pyunits.m**3 / pyunits.s, doc="Volumetric flow rate", @@ -541,7 +548,7 @@ def _conc_mass_phase_comp(self): self.conc_mass_phase_comp = Var( self.params.phase_list, self.params.component_list, - initialize=10, + initialize=100, bounds=(0.0, 1e6), units=pyunits.kg * pyunits.m**-3, doc="Mass concentration", @@ -613,16 +620,18 @@ def _heat_separation_phase(self): self.heat_separation_phase = Var( self.params.phase_list, initialize=1e6, - bounds=(0, 1e10), + bounds=(-1e10, 1e10), units=pyunits.J / pyunits.s, doc="Heat of liquid separation", ) - self.liquid_separation = Param( - initialize=1e-6, - mutable=True, + self.liquid_separation = Var( + initialize=0.1, units=pyunits.dimensionless, doc="Indication of whether liquid separation will happen", ) + # Fix this flag to default value to not consider separation heat in most cases + self.liquid_separation.fix() + self.mass_frac_after_separation = Param( initialize=0.8, mutable=True, @@ -632,8 +641,7 @@ def _heat_separation_phase(self): def rule_heat_separation_phase(b, p): heat = ( - b.liquid_separation - * b.params.separation_heat + b.params.separation_heat * b.flow_mass_phase_comp[p, "H2O"] * 1e-3 * pyunits.m**3 @@ -644,8 +652,10 @@ def rule_heat_separation_phase(b, p): / b.mass_frac_after_separation ) - return b.heat_separation_phase[p] == pyunits.convert( - heat, to_units=pyunits.J / pyunits.s + return b.heat_separation_phase[p] == Expr_if( + IF_=b.liquid_separation >= 0.5, + THEN_=pyunits.convert(heat, to_units=pyunits.J / pyunits.s), + ELSE_=0 * pyunits.J / pyunits.s, ) self.eq_heat_separation_phase = Constraint( @@ -788,13 +798,6 @@ def calculate_scaling_factors(self): ), ) - # if self.is_property_constructed("heat_separation_phase"): - # iscale.set_scaling_factor( - # self.heat_separation_phase, - # 1 / self.liquid_separation.value - # * iscale.get_scaling_factor(self.heat_separation_phase["Liq"]), - # ) - if self.is_property_constructed("enth_mass_phase"): iscale.set_scaling_factor( self.enth_mass_phase, diff --git a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py index a0cdf557..08bd88fb 100644 --- a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py @@ -22,25 +22,21 @@ ) from pyomo.util.check_units import assert_units_consistent from idaes.core import FlowsheetBlock - -from idaes.core.solvers.get_solver import get_solver from idaes.core.util.model_statistics import ( number_variables, number_total_constraints, number_unused_variables, ) - from idaes.core.util.scaling import calculate_scaling_factors - +from watertap.core.solvers import get_solver from watertap.core.util.initialization import check_dof -from watertap.property_models.tests.property_test_harness import PropertyAttributeError - -import watertap_contrib.reflo.property_models.fo_draw_solution_properties as ds_props - from watertap.property_models.tests.property_test_harness import ( PropertyTestHarness, + PropertyAttributeError, ) +import watertap_contrib.reflo.property_models.fo_draw_solution_properties as ds_props + solver = get_solver() @@ -54,7 +50,10 @@ def m(): return m -class TestSeawaterProperty(PropertyTestHarness): +# The TestHarness fails because the sol + + +class TestDrawSolutionProperty(PropertyTestHarness): def configure(self): self.prop_pack = ds_props.FODrawSolutionParameterBlock self.param_args = {} @@ -63,7 +62,7 @@ def configure(self): ("flow_mass_phase_comp", ("Liq", "DrawSolution")): 1, } self.stateblock_statistics = { - "number_variables": 14, + "number_variables": 15, "number_total_constraints": 10, "number_unused_variables": 1, "default_degrees_of_freedom": 3, @@ -77,7 +76,7 @@ def configure(self): ("conc_mass_phase_comp", ("Liq", "DrawSolution")): 867.899, ("pressure_osm_phase", "Liq"): 1.5697e7, ("cp_mass_phase", "Liq"): 2257.78, - # ("heat_separation_phase", "Liq"): 1e-5, + ("heat_separation_phase", "Liq"): 0, } diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py index 8f5fcf28..23dd5e5f 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py @@ -11,8 +11,6 @@ ################################################################################# from copy import deepcopy - -# Import Pyomo libraries from pyomo.environ import ( Var, Suffix, @@ -20,8 +18,6 @@ units as pyunits, ) from pyomo.common.config import ConfigBlock, ConfigValue, In, PositiveInt - -# Import IDAES cores from idaes.core import ( declare_process_block_class, UnitModelBlockData, @@ -30,13 +26,12 @@ from idaes.core.util.model_statistics import degrees_of_freedom from idaes.core.util.config import is_physical_parameter_block import idaes.core.util.scaling as iscale -from idaes.core.solvers import get_solver from idaes.core.util.exceptions import InitializationError from idaes.core.util.tables import ( create_stream_table_dataframe, ) import idaes.logger as idaeslog - +from watertap.core.solvers import get_solver from watertap_contrib.reflo.costing.units.forward_osmosis_zo import ( cost_forward_osmosis, ) @@ -748,3 +743,144 @@ def _get_performance_contents(self, time_point=0): @property def default_costing_method(self): return cost_forward_osmosis + + +if __name__ == "__main__": + from pyomo.environ import ( + ConcreteModel, + value, + assert_optimal_termination, + units as pyunits, + ) + from idaes.core import FlowsheetBlock, UnitModelCostingBlock + from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ( + ForwardOsmosisZO, + ) + from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( + FODrawSolutionParameterBlock, + ) + + from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock + + from watertap.core.solvers import get_solver + from idaes.core.util.model_statistics import ( + degrees_of_freedom, + number_variables, + number_total_constraints, + number_unused_variables, + unused_variables_set, + ) + from idaes.core.util.testing import initialization_tester + from idaes.core.util.scaling import ( + calculate_scaling_factors, + constraint_scaling_transform, + unscaled_variables_generator, + unscaled_constraints_generator, + badly_scaled_var_generator, + ) + + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + m.fs.water_prop = SeawaterParameterBlock() + m.fs.draw_solution_prop = FODrawSolutionParameterBlock() + m.fs.fo = ForwardOsmosisZO( + property_package_water=m.fs.water_prop, + property_package_draw_solution=m.fs.draw_solution_prop, + ) + + fo = m.fs.fo + strong_draw = fo.strong_draw_props[0] + product = fo.product_props[0] + + # System specifications + recovery_ratio = 0.3 # Assumed FO recovery ratio + nanofiltration_recovery_ratio = 0.8 # Nanofiltration recovery ratio + dp_brine = 0 # Required pressure over brine osmotic pressure (Pa) + heat_mixing = 105 # Heat of mixing in the membrane (MJ/m3 product) + reneration_temp = 90 # Separation temperature of the draw solution (C) + separator_temp_loss = 1 # Temperature loss in the separator (K) + feed_temperature = 13 # Feed water temperature (C) + feed_vol_flow = 3.704 # Feed water volumetric flow rate (m3/s) + feed_TDS_mass = 0.035 # TDS mass fraction of feed + strong_draw_temp = 20 # Strong draw solution inlet temperature (C) + strong_draw_mass_frac = 0.8 # Strong draw solution mass fraction + product_draw_mas_frac = 0.01 # Mass fraction of draw in the product water + + fo.recovery_ratio.fix(recovery_ratio) + fo.nanofiltration_recovery_ratio.fix(nanofiltration_recovery_ratio) + fo.dp_brine.fix(dp_brine) + fo.heat_mixing.fix(heat_mixing) + fo.regeneration_temp.fix(reneration_temp + 273.15) + fo.separator_temp_loss.fix(separator_temp_loss) + + # Specify strong draw solution properties + fo.strong_draw_props.calculate_state( + var_args={ + ("flow_vol_phase", "Liq"): 1, + ( + "mass_frac_phase_comp", + ("Liq", "DrawSolution"), + ): strong_draw_mass_frac, + ("temperature", None): strong_draw_temp + 273.15, + ("pressure", None): 101325, + }, + hold_state=True, + ) + + strong_draw.flow_mass_phase_comp["Liq", "DrawSolution"].unfix() + + # Specify product water properties + fo.product_props.calculate_state( + var_args={ + ("flow_vol_phase", "Liq"): 1, + ( + "mass_frac_phase_comp", + ("Liq", "DrawSolution"), + ): product_draw_mas_frac, + ("temperature", None): reneration_temp - separator_temp_loss + 273.15, + ("pressure", None): 101325, + }, + hold_state=True, + ) + + product.flow_mass_phase_comp["Liq", "H2O"].unfix() + product.temperature.unfix() + + # Specify feed properties + fo.feed_props.calculate_state( + var_args={ + ("flow_vol_phase", "Liq"): feed_vol_flow, + ("mass_frac_phase_comp", ("Liq", "TDS")): feed_TDS_mass, + ("temperature", None): feed_temperature + 273.15, + ("pressure", None): 101325, + }, + hold_state=True, + ) + + # Set scaling factors for mass flow rates + m.fs.water_prop.set_default_scaling( + "flow_mass_phase_comp", 1e-1, index=("Liq", "H2O") + ) + m.fs.water_prop.set_default_scaling( + "flow_mass_phase_comp", 1e1, index=("Liq", "TDS") + ) + m.fs.draw_solution_prop.set_default_scaling( + "flow_mass_phase_comp", 1, index=("Liq", "H2O") + ) + m.fs.draw_solution_prop.set_default_scaling( + "flow_mass_phase_comp", 1, index=("Liq", "DrawSolution") + ) + calculate_scaling_factors(m) + badly_scaled_var_lst = list(badly_scaled_var_generator(m)) + + m.fs.fo.initialize() + + strong_draw_mass = 0.8 # Strong draw solution mass fraction + product_draw_mass = 0.01 # Mass fraction of draw in the product water + m.fs.fo.unfix_and_fix_freedom(strong_draw_mass, product_draw_mass) + + solver = get_solver() + results = solver.solve(m) + + # Check for optimal solution + assert_optimal_termination(results) diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py index 5f76a46b..1521111a 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py @@ -11,29 +11,17 @@ ################################################################################# import pytest +import re from pyomo.environ import ( ConcreteModel, value, assert_optimal_termination, units as pyunits, ) -import re from pyomo.network import Port from idaes.core import FlowsheetBlock, UnitModelCostingBlock -from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ( - ForwardOsmosisZO, -) -from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( - FODrawSolutionParameterBlock, -) - -from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock -from watertap_contrib.reflo.costing import REFLOCosting from idaes.core.util.testing import initialization_tester from idaes.core.util.exceptions import ConfigurationError - - -from idaes.core.solvers import get_solver from idaes.core.util.model_statistics import ( degrees_of_freedom, number_variables, @@ -52,6 +40,16 @@ import idaes.logger as idaeslog +from watertap.core.solvers import get_solver +from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock +from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ( + ForwardOsmosisZO, +) +from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( + FODrawSolutionParameterBlock, +) +from watertap_contrib.reflo.costing import REFLOCosting + # ----------------------------------------------------------------------------- # Get default solver for testing solver = get_solver() From 797ca485e4d9c1d91c26d35a92a1ced4c79e7d81 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Thu, 22 Aug 2024 13:45:25 -0400 Subject: [PATCH 13/34] minor change --- .../example_flowsheets/test/test_fo_trevi_flowsheet.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py index 70664607..b9815d98 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -55,7 +55,10 @@ def fo_trevi_frame(self): ) fix_dof_and_initialize( - m, strong_draw_mass_frac=0.8, product_draw_mass_frac=0.01 + m, strong_draw_mass_frac=0.8, + product_draw_mass_frac=0.01, + RO_recovery_ratio=0.9, + NF_recovery_ratio=0.8, ) # same input as above # Specify the temperature of the weak draw solution and product water after going through HX1 From 1a31af8d30be76470c351d9d9ea745fb84a24fb2 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Thu, 22 Aug 2024 13:46:55 -0400 Subject: [PATCH 14/34] run black --- .../example_flowsheets/test/test_fo_trevi_flowsheet.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py index b9815d98..80ca58fd 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -55,10 +55,11 @@ def fo_trevi_frame(self): ) fix_dof_and_initialize( - m, strong_draw_mass_frac=0.8, - product_draw_mass_frac=0.01, - RO_recovery_ratio=0.9, - NF_recovery_ratio=0.8, + m, + strong_draw_mass_frac=0.8, + product_draw_mass_frac=0.01, + RO_recovery_ratio=0.9, + NF_recovery_ratio=0.8, ) # same input as above # Specify the temperature of the weak draw solution and product water after going through HX1 From 6a2b195743eb6cd88355aa2b53696f38e77408ee Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Thu, 22 Aug 2024 20:22:13 -0400 Subject: [PATCH 15/34] fix typo --- .../analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py | 2 +- .../reflo/unit_models/zero_order/tests/test_forward_osmosis.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py index 80ca58fd..b37d81c8 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -34,7 +34,7 @@ solver = get_solver() -class TestVAGMDbatch: +class TestTreviFO: @pytest.fixture(scope="class") def fo_trevi_frame(self): diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py index 1521111a..8110fb5d 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py @@ -55,7 +55,7 @@ solver = get_solver() -class TestMEDTVC: +class TestFO: @pytest.fixture(scope="class") def FO_frame(self): # create model, flowsheet From 888bfddf87344bdb60b65cffe845f4039b350e48 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Thu, 22 Aug 2024 20:24:08 -0400 Subject: [PATCH 16/34] remove comments --- .../property_models/tests/test_FO_draw_solution_properties.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py index 08bd88fb..e9aba911 100644 --- a/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/tests/test_FO_draw_solution_properties.py @@ -50,9 +50,6 @@ def m(): return m -# The TestHarness fails because the sol - - class TestDrawSolutionProperty(PropertyTestHarness): def configure(self): self.prop_pack = ds_props.FODrawSolutionParameterBlock From 47f5303c61bb67eabd511ae78713d7279947e0ba Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Fri, 23 Aug 2024 10:24:32 -0400 Subject: [PATCH 17/34] empty commit From 3ecb0ab24e91ccc454f71c57713c5ef316155f06 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Fri, 23 Aug 2024 10:35:53 -0400 Subject: [PATCH 18/34] update doc --- .../unit_models/treatment_models/forward_osmosis.rst | 2 +- .../treatment_models/forward_osmosis_trevi_flowsheet.rst | 2 +- .../unit_models/treatment_models/med_md_semibatch.rst | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/technical_reference/unit_models/treatment_models/forward_osmosis.rst b/docs/technical_reference/unit_models/treatment_models/forward_osmosis.rst index e00ea9f6..057dcb97 100644 --- a/docs/technical_reference/unit_models/treatment_models/forward_osmosis.rst +++ b/docs/technical_reference/unit_models/treatment_models/forward_osmosis.rst @@ -48,7 +48,7 @@ Typically, the following variables are fixed, including the state variables at t Model Structure --------------- -This FO model consists of 6 StateBlocks (as 4 Ports in parenthesis below). +This FO model consists of 6 StateBlocks (as 6 Ports in parenthesis below). * Feed flow (feed) * Product water (product) diff --git a/docs/technical_reference/unit_models/treatment_models/forward_osmosis_trevi_flowsheet.rst b/docs/technical_reference/unit_models/treatment_models/forward_osmosis_trevi_flowsheet.rst index 1abf7b45..0ddc9cb1 100644 --- a/docs/technical_reference/unit_models/treatment_models/forward_osmosis_trevi_flowsheet.rst +++ b/docs/technical_reference/unit_models/treatment_models/forward_osmosis_trevi_flowsheet.rst @@ -32,7 +32,7 @@ A discussion of each process component is given as following: * From the membrane module to the coalescer, the circulation loop of the diluted raw solution is as described above. During this circulation, the stream receives the brine generated by the final nanofiltration (NF) process. * In the coalescer the draw solution stream is separated into two components, (1) concentrated draw solution at 80 wt % draw solution polymer, and two (2) NF feed at < 1 wt% draw solution polymer. * Concentrated draw solution at 80 wt % draw solution polymer goes through the heat exchanger and enters permeate side of FO membrane module. - * From the coalescer, the product stream is hot NF feed at <1 wt % draw solution polymer, that after pre-heating the diluted draw solution as described above, is pumped to the low pressure NF unit by the NF feed pump. + * From the coalescer, the product stream is hot NF feed at ~ 1 wt % draw solution polymer, that after pre-heating the diluted draw solution as described above, is pumped to the low pressure NF unit by the NF feed pump. * An additional RO filtration step is installed for the NF permeate in case product water quality does not fullfil requirements, with special reference to rejection of Boron. * The NF brine stream, which is a warm stream containing the NF-rejected draw solution polymer, is recirculated and combined with cold dilute draw solution downstream of the FO membrane. * According to the efficiency of the heat exchangers, both the NF permeate and brine will be slightly warmer than the original feed seawater finally resulting in loss of heat. diff --git a/docs/technical_reference/unit_models/treatment_models/med_md_semibatch.rst b/docs/technical_reference/unit_models/treatment_models/med_md_semibatch.rst index c7dd764b..4edfa871 100644 --- a/docs/technical_reference/unit_models/treatment_models/med_md_semibatch.rst +++ b/docs/technical_reference/unit_models/treatment_models/med_md_semibatch.rst @@ -1,5 +1,5 @@ Low Temperature Multi-effect Distillation - Semibatch, Vacuum Air-gap Membrane Distillation (LTMED - VAGMD) Flowsheet -=================================================================================================================== +===================================================================================================================== Implementation -------------- @@ -110,7 +110,7 @@ Equations Variables connected between periods ----------------------------------- +----------------------------------- This table lists pairs of variables that need to be connected across two time periods during processing phase .. csv-table:: From 310e8e796480e481134123b52ff7cf9e70c44898 Mon Sep 17 00:00:00 2001 From: kurbansitterley Date: Wed, 11 Sep 2024 09:40:36 -0600 Subject: [PATCH 19/34] new black --- .../example_flowsheets/fo_trevi_flowsheet.py | 40 +++++++++---------- .../reflo/costing/units/forward_osmosis_zo.py | 3 +- .../zero_order/forward_osmosis_zo.py | 30 +++++++------- 3 files changed, 34 insertions(+), 39 deletions(-) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py index 5228aaf0..4e30e09b 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py @@ -427,9 +427,9 @@ def fix_dof_and_initialize( ) # Cold side has liquid separation m.fs.HX1.cold_side.properties_out[0].liquid_separation.fix(1) - m.fs.HX1.cold_side.properties_out[ - 0 - ].mass_frac_after_separation = strong_draw_mass_frac + m.fs.HX1.cold_side.properties_out[0].mass_frac_after_separation = ( + strong_draw_mass_frac + ) iscale.set_scaling_factor( m.fs.HX1.cold_side.properties_out[0].heat_separation_phase, 1e-5, @@ -462,9 +462,9 @@ def fix_dof_and_initialize( ) # Cold side has liquid separation m.fs.HX2.cold_side.properties_out[0].liquid_separation.fix(1) - m.fs.HX2.cold_side.properties_out[ - 0 - ].mass_frac_after_separation = strong_draw_mass_frac + m.fs.HX2.cold_side.properties_out[0].mass_frac_after_separation = ( + strong_draw_mass_frac + ) # Initialize separator S2 m.fs.S2.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( @@ -473,17 +473,13 @@ def fix_dof_and_initialize( m.fs.S2.inlet.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value ) - m.fs.S2.NF_reject.flow_mass_phase_comp[ - 0, "Liq", "H2O" - ].value = m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value * ( - 1 - NF_recovery_ratio + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value + * (1 - NF_recovery_ratio) ) - m.fs.S2.NF_reject.flow_mass_phase_comp[ - 0, "Liq", "DrawSolution" - ].value = m.fs.fo.product_props[0].flow_mass_phase_comp[ - "Liq", "DrawSolution" - ].value * ( - 1 - NF_recovery_ratio + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value + * (1 - NF_recovery_ratio) ) m.fs.S2.RO_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( m.fs.fo.product_props[0].flow_mass_phase_comp["Liq", "H2O"].value @@ -514,12 +510,12 @@ def fix_dof_and_initialize( m.fs.M1.weak_draw.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].value ) - m.fs.M1.NF_reject.flow_mass_phase_comp[ - 0, "Liq", "H2O" - ].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value - m.fs.M1.NF_reject.flow_mass_phase_comp[ - 0, "Liq", "DrawSolution" - ].value = m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value + m.fs.M1.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value + ) + m.fs.M1.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value = ( + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "DrawSolution"].value + ) m.fs.M1.outlet.flow_mass_phase_comp[0, "Liq", "H2O"].value = ( m.fs.fo.weak_draw_props[0].flow_mass_phase_comp["Liq", "H2O"].value + m.fs.S2.NF_reject.flow_mass_phase_comp[0, "Liq", "H2O"].value diff --git a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py index 4ec6da50..990fe8d9 100644 --- a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py @@ -114,8 +114,7 @@ def cost_forward_osmosis(blk): ) blk.capital_cost_constraint = pyo.Constraint( expr=blk.capital_cost - == fo_params.base_unit_cost - * test_fs.system_capacity**fo_params.unit_cost_index + == fo_params.base_unit_cost * test_fs.system_capacity**fo_params.unit_cost_index ) blk.labor_cost = pyo.Expression( diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py index 23dd5e5f..3d857619 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py @@ -512,9 +512,9 @@ def initialize_build( / pyunits.s ) elif p == "Liq" and j == "DrawSolution": - state_args_weak_draw["flow_mass_phase_comp"][ - (p, j) - ] = state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] + state_args_weak_draw["flow_mass_phase_comp"][(p, j)] = ( + state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] + ) blk.weak_draw_props.initialize( outlvl=outlvl, @@ -527,9 +527,9 @@ def initialize_build( state_args_strong_draw = deepcopy(state_args_draw_solution) for p, j in blk.strong_draw_props.phase_component_set: if p == "Liq" and j == "DrawSolution": - state_args_strong_draw["flow_mass_phase_comp"][ - (p, j) - ] = state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] + state_args_strong_draw["flow_mass_phase_comp"][(p, j)] = ( + state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] + ) elif p == "Liq" and j == "H2O": state_args_strong_draw["flow_mass_phase_comp"][(p, j)] = ( state_args_strong_draw["flow_mass_phase_comp"][(p, "DrawSolution")] @@ -547,9 +547,9 @@ def initialize_build( state_args_reg_draw = deepcopy(state_args_draw_solution) for p, j in blk.reg_draw_props.phase_component_set: if p == "Liq": - state_args_reg_draw["flow_mass_phase_comp"][ - (p, j) - ] = state_args_strong_draw["flow_mass_phase_comp"][(p, j)] + state_args_reg_draw["flow_mass_phase_comp"][(p, j)] = ( + state_args_strong_draw["flow_mass_phase_comp"][(p, j)] + ) state_args_strong_draw["temperature"] = 90 + 273.15 blk.reg_draw_props.initialize( @@ -724,12 +724,12 @@ def _get_stream_table_contents(self, time_point=0): def _get_performance_contents(self, time_point=0): var_dict = {} - var_dict[ - "Strong draw solution volumetric flow rate (m3/s)" - ] = self.strong_draw_props[0].flow_vol_phase["Liq"] - var_dict[ - "Weak draw solution volumetric flow rate (m3/s)" - ] = self.weak_draw_props[0].flow_vol_phase["Liq"] + var_dict["Strong draw solution volumetric flow rate (m3/s)"] = ( + self.strong_draw_props[0].flow_vol_phase["Liq"] + ) + var_dict["Weak draw solution volumetric flow rate (m3/s)"] = ( + self.weak_draw_props[0].flow_vol_phase["Liq"] + ) var_dict["Mass fraction of weak draw solution"] = self.weak_draw_props[ 0 ].mass_frac_phase_comp["Liq", "DrawSolution"] From d145b297efc96b420d4a2cf9317addd5c95a1473 Mon Sep 17 00:00:00 2001 From: kurbansitterley Date: Wed, 11 Sep 2024 12:23:21 -0600 Subject: [PATCH 20/34] remove unused imports; spelling --- .../example_flowsheets/fo_trevi_flowsheet.py | 35 ++++--------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py index 4e30e09b..9b5a1e70 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py @@ -15,46 +15,24 @@ ConcreteModel, TransformationFactory, units as pyunits, - Expression, - Reference, - Var, value, - assert_optimal_termination, -) -from idaes.core.util.model_statistics import ( - degrees_of_freedom, - number_variables, - number_total_constraints, - number_unused_variables, - unused_variables_set, ) from pyomo.network import Arc -from pyomo.util.calc_var_value import calculate_variable_from_constraint # IDAES imports import idaes.core.util.scaling as iscale -import idaes.logger as idaeslog -from idaes.core.util.scaling import ( - calculate_scaling_factors, - list_badly_scaled_variables, -) -from idaes.core import FlowsheetBlock, MaterialBalanceType, EnergyBalanceType -from idaes.core import UnitModelCostingBlock +from idaes.core import FlowsheetBlock, MaterialBalanceType from idaes.models.unit_models import ( Mixer, Separator, HeatExchanger, Heater, ) -from idaes.models.unit_models.heat_exchanger import ( - delta_temperature_lmtd_callback, - delta_temperature_underwood_callback, -) +from idaes.models.unit_models.heat_exchanger import delta_temperature_underwood_callback # WaterTAP imports from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock from watertap.core.solvers import get_solver -from watertap.core.util.initialization import check_dof # WaterTAP REFLO imports from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( @@ -64,7 +42,6 @@ from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ( ForwardOsmosisZO, ) -from watertap_contrib.reflo.costing import TreatmentCosting def build_fo_trevi_flowsheet( @@ -307,7 +284,7 @@ def add_fo( fo.regeneration_temp.fix(separation_temp + 273.15) fo.separator_temp_loss.fix(separator_temp_loss) - # Specifyf strong draw solution properties + # Specify strong draw solution properties fo.strong_draw_props.calculate_state( var_args={ ("flow_vol_phase", "Liq"): 1, @@ -320,7 +297,7 @@ def add_fo( fo.strong_draw_props[0].flow_mass_phase_comp["Liq", "DrawSolution"].unfix() - # Specifyf product water properties + # Specify product water properties fo.product_props.calculate_state( var_args={ ("flow_vol_phase", "Liq"): 1, @@ -371,7 +348,7 @@ def fix_dof_and_initialize( RO_recovery_ratio=0.9, ): - calculate_scaling_factors(m) + iscale.calculate_scaling_factors(m) m.fs.fo.initialize() # Unfix the state variables and fix mass fraction of two state blocks @@ -545,7 +522,7 @@ def get_flowsheet_performance(m): ) * value(m.fs.system_capacity) / 24, - "LCOW ($/m3)": m.fs.costing.LCOW(), + "LCOW ($/m3)": value(m.fs.costing.LCOW), } operational_parameters = { From 269a7cb71933695ae1efcd4ce232cfae74f84973 Mon Sep 17 00:00:00 2001 From: kurbansitterley Date: Wed, 11 Sep 2024 12:46:14 -0600 Subject: [PATCH 21/34] unused imports --- .../reflo/unit_models/zero_order/forward_osmosis_zo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py index 3d857619..713fa0d2 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py @@ -17,7 +17,7 @@ check_optimal_termination, units as pyunits, ) -from pyomo.common.config import ConfigBlock, ConfigValue, In, PositiveInt +from pyomo.common.config import ConfigBlock, ConfigValue, In from idaes.core import ( declare_process_block_class, UnitModelBlockData, From 3b964d15838098d48c132677da3b8266d024bd9a Mon Sep 17 00:00:00 2001 From: kurbansitterley Date: Wed, 11 Sep 2024 12:49:51 -0600 Subject: [PATCH 22/34] unused imports --- .../zero_order/tests/test_forward_osmosis.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py index 8110fb5d..335b3120 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py @@ -11,28 +11,23 @@ ################################################################################# import pytest -import re from pyomo.environ import ( ConcreteModel, value, assert_optimal_termination, - units as pyunits, ) from pyomo.network import Port -from idaes.core import FlowsheetBlock, UnitModelCostingBlock +from idaes.core import FlowsheetBlock from idaes.core.util.testing import initialization_tester -from idaes.core.util.exceptions import ConfigurationError from idaes.core.util.model_statistics import ( degrees_of_freedom, number_variables, number_total_constraints, number_unused_variables, - unused_variables_set, ) from idaes.core.util.testing import initialization_tester from idaes.core.util.scaling import ( calculate_scaling_factors, - constraint_scaling_transform, unscaled_variables_generator, unscaled_constraints_generator, badly_scaled_var_generator, @@ -48,7 +43,6 @@ from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( FODrawSolutionParameterBlock, ) -from watertap_contrib.reflo.costing import REFLOCosting # ----------------------------------------------------------------------------- # Get default solver for testing @@ -202,7 +196,7 @@ def test_initialize(self, FO_frame): def test_solve(self, FO_frame): m = FO_frame - # Unfix the state variables and fix mass fractrion of draw solution state blocks + # Unfix the state variables and fix mass fraction of draw solution state blocks strong_draw_mass = 0.8 # Strong draw solution mass fraction product_draw_mass = 0.01 # Mass fraction of draw in the product water m.fs.fo.unfix_and_fix_freedom(strong_draw_mass, product_draw_mass) From 91c7459a4cc2533f5e2bf38269d8a6252cf55978 Mon Sep 17 00:00:00 2001 From: kurbansitterley Date: Wed, 11 Sep 2024 12:50:54 -0600 Subject: [PATCH 23/34] unused imports --- .../example_flowsheets/test/test_fo_trevi_flowsheet.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py index b37d81c8..60cb91f0 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -12,14 +12,10 @@ import pytest from pyomo.environ import ( - ConcreteModel, assert_optimal_termination, units as pyunits, ) from idaes.core import UnitModelCostingBlock -from idaes.core.util.exceptions import ( - ConfigurationError, -) from idaes.core.util.model_statistics import ( degrees_of_freedom, ) From dd1a334dca1eb9fed485c091df44c1e4bb506ae8 Mon Sep 17 00:00:00 2001 From: kurbansitterley Date: Wed, 11 Sep 2024 13:15:00 -0600 Subject: [PATCH 24/34] unused import --- src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py index 990fe8d9..2e474ad6 100644 --- a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py @@ -11,7 +11,6 @@ ################################################################################# import pyomo.environ as pyo -import idaes.core.util.scaling as iscale from watertap.costing.util import register_costing_parameter_block from watertap_contrib.reflo.costing.util import ( make_capital_cost_var, From 9a74d2ed969c0cb755b7f0bd48b7eb9260cd14ff Mon Sep 17 00:00:00 2001 From: kurbansitterley Date: Wed, 11 Sep 2024 13:26:24 -0600 Subject: [PATCH 25/34] imports --- .../reflo/property_models/fo_draw_solution_properties.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py index 7fb30d05..eb6b1cb7 100644 --- a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py @@ -16,7 +16,6 @@ """ from pyomo.environ import ( Param, - Binary, Expression, units as pyunits, Reals, @@ -27,8 +26,8 @@ value, check_optimal_termination, Expr_if, + units as pyunits, ) -from pyomo.common.config import ConfigValue from idaes.core import ( EnergyBalanceType, @@ -41,7 +40,6 @@ ) from idaes.core.base.components import Solvent, Solute from idaes.core.base.phases import LiquidPhase -from idaes.core.util.misc import add_object_reference from idaes.core.util.initialization import ( fix_state_vars, revert_state_vars, @@ -56,7 +54,6 @@ from idaes.core.util.exceptions import ( ConfigurationError, InitializationError, - PropertyPackageError, ) from watertap.core.util.scaling import transform_property_constraints From 86c86f2e2fd81b546e2b201f7e7ab0c5c184be12 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Thu, 12 Sep 2024 17:46:13 -0400 Subject: [PATCH 26/34] address comments --- .../test/test_fo_trevi_flowsheet.py | 6 +- .../reflo/costing/units/forward_osmosis_zo.py | 55 ++----- .../fo_draw_solution_properties.py | 18 +-- .../zero_order/forward_osmosis_zo.py | 143 +----------------- 4 files changed, 19 insertions(+), 203 deletions(-) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py index 60cb91f0..d58fc8ee 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -83,17 +83,17 @@ def test_costing(self, fo_trevi_frame): m = fo_trevi_frame # Add cost package of Trevi FO system m.fs.costing = TreatmentCosting() - m.fs.costing.base_currency = pyunits.USD_2020 + m.fs.costing.base_currency = pyunits.USD_2021 # Create cost block for FO m.fs.fo.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing) # Add LCOW component m.fs.costing.cost_process() + m.fs.costing.maintenance_labor_chemical_factor.fix(0) m.fs.costing.add_annual_water_production(m.fs.system_capacity) m.fs.costing.add_LCOW(m.fs.system_capacity) - solver = get_solver() results = solver.solve(m) assert_optimal_termination(results) @@ -112,7 +112,7 @@ def test_solution(self, fo_trevi_frame): assert overall_performance["Thermal power requirement (kW)"] == pytest.approx( 101038.77, rel=1e-3 ) - assert overall_performance["LCOW ($/m3)"] == pytest.approx(0.579, rel=1e-3) + assert overall_performance["LCOW ($/m3)"] == pytest.approx(0.511, rel=1e-3) assert operational_parameters["HX1 cold in temp"] == pytest.approx( 21.49, rel=1e-3 diff --git a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py index 2e474ad6..80ec9070 100644 --- a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py @@ -28,7 +28,7 @@ def build_forward_osmosis_cost_param_block(blk): blk.base_unit_cost = pyo.Var( initialize=26784, - units=costing.base_currency / (pyo.units.m**3 / pyo.units.day), + units=pyo.units.USD_2021 / (pyo.units.m**3 / pyo.units.day), bounds=(0, None), doc="Base price of Trevi FO system ($/m3/day), including membranes, heat exchangers, \ construction, draw solution, coalescers, structural, polishing, pipes, plumbing, \ @@ -42,20 +42,6 @@ def build_forward_osmosis_cost_param_block(blk): doc="Scaling factor of Trevi FO system capital cost", ) - blk.base_labor_cost = pyo.Var( - initialize=0.4757, - units=costing.base_currency / pyo.units.m**3, - bounds=(0, None), - doc="Base price of the labor cost ($/m3)", - ) - - blk.labor_cost_index = pyo.Var( - initialize=-0.178, - units=pyo.units.dimensionless, - bounds=(None, 0), - doc="Scaling factor of Trevi FO system capital cost", - ) - blk.specific_energy_consumption_electric = pyo.Var( initialize=1, units=pyo.units.kWh / pyo.units.m**3, @@ -63,34 +49,20 @@ def build_forward_osmosis_cost_param_block(blk): doc="Specific electric energy consumption (kWh/m3)", ) - blk.cost_chemicals = pyo.Var( - initialize=0.07, - units=costing.base_currency / pyo.units.m**3, - bounds=(0, None), - doc="Cost of chemicals per m3 product", - ) - blk.cost_durable_goods = pyo.Var( initialize=0.05, - units=costing.base_currency / pyo.units.m**3, + units=pyo.units.USD_2021 / pyo.units.m**3, bounds=(0, None), doc="Cost of durable goods per m3 product", ) blk.cost_disposal = pyo.Var( initialize=0.02, - units=costing.base_currency / pyo.units.m**3, + units=pyo.units.USD_2021 / pyo.units.m**3, bounds=(0, None), doc="Cost of disposal per m3 brine", ) - blk.cost_fraction_insurance = pyo.Var( - initialize=0.005, - units=pyo.units.dimensionless, - bounds=(0, None), - doc="Fraction of capital cost for insurance", - ) - @register_costing_parameter_block( build_rule=build_forward_osmosis_cost_param_block, @@ -103,30 +75,23 @@ def cost_forward_osmosis(blk): make_fixed_operating_cost_var(blk) fo = blk.unit_model - test_fs = fo.flowsheet() + fo_fs = fo.flowsheet() brine = fo.brine_props[0] blk.costing_package.add_cost_factor(blk, None) blk.annual_dist_production = pyo.units.convert( - test_fs.system_capacity, to_units=pyo.units.m**3 / pyo.units.year + fo_fs.system_capacity, to_units=pyo.units.m**3 / pyo.units.year ) blk.capital_cost_constraint = pyo.Constraint( expr=blk.capital_cost - == fo_params.base_unit_cost * test_fs.system_capacity**fo_params.unit_cost_index - ) - - blk.labor_cost = pyo.Expression( - expr=fo_params.base_labor_cost - * test_fs.system_capacity**fo_params.labor_cost_index + == fo_params.base_unit_cost * fo_fs.system_capacity**fo_params.unit_cost_index ) blk.fixed_operating_cost_constraint = pyo.Constraint( expr=( blk.fixed_operating_cost - == blk.annual_dist_production - * (blk.labor_cost + fo_params.cost_chemicals + fo_params.cost_durable_goods) - + blk.capital_cost * fo_params.cost_fraction_insurance + == blk.annual_dist_production * fo_params.cost_durable_goods + pyo.units.convert( brine.flow_vol_phase["Liq"], to_units=pyo.units.m**3 / pyo.units.year ) @@ -135,16 +100,16 @@ def cost_forward_osmosis(blk): ) blk.thermal_energy_flow = pyo.Expression( - expr=test_fs.specific_energy_consumption_thermal + expr=fo_fs.specific_energy_consumption_thermal * pyo.units.convert( - test_fs.system_capacity, to_units=pyo.units.m**3 / pyo.units.hr + fo_fs.system_capacity, to_units=pyo.units.m**3 / pyo.units.hr ) ) blk.electricity_flow = pyo.Expression( expr=fo_params.specific_energy_consumption_electric * pyo.units.convert( - test_fs.system_capacity, to_units=pyo.units.m**3 / pyo.units.hr + fo_fs.system_capacity, to_units=pyo.units.m**3 / pyo.units.hr ) ) diff --git a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py index eb6b1cb7..6425f9dd 100644 --- a/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py +++ b/src/watertap_contrib/reflo/property_models/fo_draw_solution_properties.py @@ -11,8 +11,8 @@ ################################################################################# """ -This module contains a basic property package for simple water treatment models. -Volumetric flow and component concentration are used to determine mass flow. +This module contains a property package for a proprietary draw solution used in +Trevi's FO system. """ from pyomo.environ import ( Param, @@ -28,7 +28,7 @@ Expr_if, units as pyunits, ) - +from pyomo.gdp import Disjunction from idaes.core import ( EnergyBalanceType, MaterialBalanceType, @@ -68,10 +68,8 @@ @declare_process_block_class("FODrawSolutionParameterBlock") class FODrawSolutionParameterBlockData(PhysicalParameterBlock): """ - Property Parameter Block Class - - Defines component lists, along with base units and constant - parameters. + Property parameters derived from experimental data of the draw + solution that is used in the Trevi's FO system """ CONFIG = PhysicalParameterBlock.CONFIG() @@ -201,12 +199,6 @@ def define_metadata(cls, obj): } ) - obj.define_custom_properties( - { - # "heat_separation_phase": {"method": "_heat_separation_phase"}, - } - ) - class _FODrawSolutionStateBlock(StateBlock): """ diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py index 713fa0d2..0945d9d5 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py @@ -507,7 +507,7 @@ def initialize_build( state_args["flow_mass_phase_comp"][(p, j)] * blk.recovery_ratio / blk.nanofiltration_recovery_ratio - * 1.5 + * 1.5 # Approximated ratio of the weak draw flow to the feed flow * pyunits.kg / pyunits.s ) @@ -743,144 +743,3 @@ def _get_performance_contents(self, time_point=0): @property def default_costing_method(self): return cost_forward_osmosis - - -if __name__ == "__main__": - from pyomo.environ import ( - ConcreteModel, - value, - assert_optimal_termination, - units as pyunits, - ) - from idaes.core import FlowsheetBlock, UnitModelCostingBlock - from watertap_contrib.reflo.unit_models.zero_order.forward_osmosis_zo import ( - ForwardOsmosisZO, - ) - from watertap_contrib.reflo.property_models.fo_draw_solution_properties import ( - FODrawSolutionParameterBlock, - ) - - from watertap.property_models.seawater_prop_pack import SeawaterParameterBlock - - from watertap.core.solvers import get_solver - from idaes.core.util.model_statistics import ( - degrees_of_freedom, - number_variables, - number_total_constraints, - number_unused_variables, - unused_variables_set, - ) - from idaes.core.util.testing import initialization_tester - from idaes.core.util.scaling import ( - calculate_scaling_factors, - constraint_scaling_transform, - unscaled_variables_generator, - unscaled_constraints_generator, - badly_scaled_var_generator, - ) - - m = ConcreteModel() - m.fs = FlowsheetBlock(dynamic=False) - m.fs.water_prop = SeawaterParameterBlock() - m.fs.draw_solution_prop = FODrawSolutionParameterBlock() - m.fs.fo = ForwardOsmosisZO( - property_package_water=m.fs.water_prop, - property_package_draw_solution=m.fs.draw_solution_prop, - ) - - fo = m.fs.fo - strong_draw = fo.strong_draw_props[0] - product = fo.product_props[0] - - # System specifications - recovery_ratio = 0.3 # Assumed FO recovery ratio - nanofiltration_recovery_ratio = 0.8 # Nanofiltration recovery ratio - dp_brine = 0 # Required pressure over brine osmotic pressure (Pa) - heat_mixing = 105 # Heat of mixing in the membrane (MJ/m3 product) - reneration_temp = 90 # Separation temperature of the draw solution (C) - separator_temp_loss = 1 # Temperature loss in the separator (K) - feed_temperature = 13 # Feed water temperature (C) - feed_vol_flow = 3.704 # Feed water volumetric flow rate (m3/s) - feed_TDS_mass = 0.035 # TDS mass fraction of feed - strong_draw_temp = 20 # Strong draw solution inlet temperature (C) - strong_draw_mass_frac = 0.8 # Strong draw solution mass fraction - product_draw_mas_frac = 0.01 # Mass fraction of draw in the product water - - fo.recovery_ratio.fix(recovery_ratio) - fo.nanofiltration_recovery_ratio.fix(nanofiltration_recovery_ratio) - fo.dp_brine.fix(dp_brine) - fo.heat_mixing.fix(heat_mixing) - fo.regeneration_temp.fix(reneration_temp + 273.15) - fo.separator_temp_loss.fix(separator_temp_loss) - - # Specify strong draw solution properties - fo.strong_draw_props.calculate_state( - var_args={ - ("flow_vol_phase", "Liq"): 1, - ( - "mass_frac_phase_comp", - ("Liq", "DrawSolution"), - ): strong_draw_mass_frac, - ("temperature", None): strong_draw_temp + 273.15, - ("pressure", None): 101325, - }, - hold_state=True, - ) - - strong_draw.flow_mass_phase_comp["Liq", "DrawSolution"].unfix() - - # Specify product water properties - fo.product_props.calculate_state( - var_args={ - ("flow_vol_phase", "Liq"): 1, - ( - "mass_frac_phase_comp", - ("Liq", "DrawSolution"), - ): product_draw_mas_frac, - ("temperature", None): reneration_temp - separator_temp_loss + 273.15, - ("pressure", None): 101325, - }, - hold_state=True, - ) - - product.flow_mass_phase_comp["Liq", "H2O"].unfix() - product.temperature.unfix() - - # Specify feed properties - fo.feed_props.calculate_state( - var_args={ - ("flow_vol_phase", "Liq"): feed_vol_flow, - ("mass_frac_phase_comp", ("Liq", "TDS")): feed_TDS_mass, - ("temperature", None): feed_temperature + 273.15, - ("pressure", None): 101325, - }, - hold_state=True, - ) - - # Set scaling factors for mass flow rates - m.fs.water_prop.set_default_scaling( - "flow_mass_phase_comp", 1e-1, index=("Liq", "H2O") - ) - m.fs.water_prop.set_default_scaling( - "flow_mass_phase_comp", 1e1, index=("Liq", "TDS") - ) - m.fs.draw_solution_prop.set_default_scaling( - "flow_mass_phase_comp", 1, index=("Liq", "H2O") - ) - m.fs.draw_solution_prop.set_default_scaling( - "flow_mass_phase_comp", 1, index=("Liq", "DrawSolution") - ) - calculate_scaling_factors(m) - badly_scaled_var_lst = list(badly_scaled_var_generator(m)) - - m.fs.fo.initialize() - - strong_draw_mass = 0.8 # Strong draw solution mass fraction - product_draw_mass = 0.01 # Mass fraction of draw in the product water - m.fs.fo.unfix_and_fix_freedom(strong_draw_mass, product_draw_mass) - - solver = get_solver() - results = solver.solve(m) - - # Check for optimal solution - assert_optimal_termination(results) From 5c402777e06b6b59c1c06d462c258cd7a3476958 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Thu, 12 Sep 2024 18:40:49 -0400 Subject: [PATCH 27/34] add check --- .../example_flowsheets/test/test_fo_trevi_flowsheet.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py index d58fc8ee..c4aec948 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -12,6 +12,7 @@ import pytest from pyomo.environ import ( + value, assert_optimal_termination, units as pyunits, ) @@ -103,6 +104,12 @@ def test_solution(self, fo_trevi_frame): overall_performance, operational_parameters = get_flowsheet_performance(m) + assert value(m.fs.system_capacity) == pytest.approx( + m.fs.S2.fresh_water.flow_mass_phase_comp[0, "Liq", "H2O"].value + / 1000 # Fresh water density (kg/m3) + * 86400, # convert from sec to day (s/day) + rel=1e-3, + ) assert overall_performance["Production capacity (m3/day)"] == pytest.approx( 85683.32, abs=1e-3 ) From 96966607926fa1972c921f7f0f64e0d6ba228570 Mon Sep 17 00:00:00 2001 From: kurbansitterley Date: Mon, 23 Sep 2024 17:56:02 -0600 Subject: [PATCH 28/34] watertap==1.0.0rc0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 750b967f..d8565034 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ python_requires=">=3.8", install_requires=[ # "watertap @ https://github.com/watertap-org/watertap/archive/main.zip", # uncomment if we need to point to main mid release cycle - "watertap>=1.0.0rc0", + "watertap==1.0.0rc0", "idaes-pse==2.5.0", "pyomo==6.7.3", "nrel-pysam == 5.1.0", From 0bc579d3a33f8312122473a993a54366320da564 Mon Sep 17 00:00:00 2001 From: kurbansitterley Date: Wed, 25 Sep 2024 12:57:11 -0600 Subject: [PATCH 29/34] revert to watertap>=1.0.0rc0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d8565034..750b967f 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ python_requires=">=3.8", install_requires=[ # "watertap @ https://github.com/watertap-org/watertap/archive/main.zip", # uncomment if we need to point to main mid release cycle - "watertap==1.0.0rc0", + "watertap>=1.0.0rc0", "idaes-pse==2.5.0", "pyomo==6.7.3", "nrel-pysam == 5.1.0", From 938ad1e8f426d759f923ec48f155c91ae6a8cf0a Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Fri, 11 Oct 2024 11:24:55 -0400 Subject: [PATCH 30/34] conver to base currency/period --- .../reflo/costing/units/forward_osmosis_zo.py | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py index 80ec9070..adb28b8e 100644 --- a/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/costing/units/forward_osmosis_zo.py @@ -71,6 +71,8 @@ def build_forward_osmosis_cost_param_block(blk): def cost_forward_osmosis(blk): fo_params = blk.costing_package.forward_osmosis + base_currency = blk.config.flowsheet_costing_block.base_currency + base_period = blk.config.flowsheet_costing_block.base_period make_capital_cost_var(blk) make_fixed_operating_cost_var(blk) @@ -85,17 +87,27 @@ def cost_forward_osmosis(blk): ) blk.capital_cost_constraint = pyo.Constraint( expr=blk.capital_cost - == fo_params.base_unit_cost * fo_fs.system_capacity**fo_params.unit_cost_index + == pyo.units.convert( + fo_params.base_unit_cost + * (pyo.units.m**3 / pyo.units.day) + * (fo_fs.system_capacity * pyo.units.day / pyo.units.m**3) + ** fo_params.unit_cost_index, + to_units=base_currency, + ) ) blk.fixed_operating_cost_constraint = pyo.Constraint( expr=( blk.fixed_operating_cost - == blk.annual_dist_production * fo_params.cost_durable_goods - + pyo.units.convert( - brine.flow_vol_phase["Liq"], to_units=pyo.units.m**3 / pyo.units.year + == pyo.units.convert( + blk.annual_dist_production * fo_params.cost_durable_goods + + pyo.units.convert( + brine.flow_vol_phase["Liq"], + to_units=pyo.units.m**3 / pyo.units.year, + ) + * fo_params.cost_disposal, + to_units=base_currency / base_period, ) - * fo_params.cost_disposal ) ) From a0f97fd47786f54e9b31f7fd2495c02931d75047 Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Fri, 11 Oct 2024 11:36:57 -0400 Subject: [PATCH 31/34] empty commit to trigger test From 64e97ea11f4dd656d0632ea1b1d51b450c2f8f05 Mon Sep 17 00:00:00 2001 From: kurbansitterley Date: Fri, 15 Nov 2024 11:20:19 -0700 Subject: [PATCH 32/34] fix test LCOW --- .../analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py index c4aec948..d86865c3 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -119,7 +119,7 @@ def test_solution(self, fo_trevi_frame): assert overall_performance["Thermal power requirement (kW)"] == pytest.approx( 101038.77, rel=1e-3 ) - assert overall_performance["LCOW ($/m3)"] == pytest.approx(0.511, rel=1e-3) + assert overall_performance["LCOW ($/m3)"] == pytest.approx(0.096688, rel=1e-3) assert operational_parameters["HX1 cold in temp"] == pytest.approx( 21.49, rel=1e-3 From dfa7021e0e019dc4ab8b3222da8893b3f0a0b0db Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Fri, 15 Nov 2024 14:22:11 -0500 Subject: [PATCH 33/34] add energy cost --- .../example_flowsheets/test/test_fo_trevi_flowsheet.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py index c4aec948..94d0270b 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -86,6 +86,8 @@ def test_costing(self, fo_trevi_frame): m.fs.costing = TreatmentCosting() m.fs.costing.base_currency = pyunits.USD_2021 + m.fs.costing.heat_cost.set_value(0.01) + m.fs.costing.electricity_cost.fix(0.07) # Create cost block for FO m.fs.fo.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing) @@ -119,7 +121,7 @@ def test_solution(self, fo_trevi_frame): assert overall_performance["Thermal power requirement (kW)"] == pytest.approx( 101038.77, rel=1e-3 ) - assert overall_performance["LCOW ($/m3)"] == pytest.approx(0.511, rel=1e-3) + assert overall_performance["LCOW ($/m3)"] == pytest.approx(0.462, rel=1e-3) assert operational_parameters["HX1 cold in temp"] == pytest.approx( 21.49, rel=1e-3 From a0326d5bfa150c110698c6b6c026d4be8514938f Mon Sep 17 00:00:00 2001 From: zhuoran29 Date: Mon, 18 Nov 2024 11:14:43 -0500 Subject: [PATCH 34/34] modify initialization --- .../example_flowsheets/fo_trevi_flowsheet.py | 14 ++--- .../test/test_fo_trevi_flowsheet.py | 20 +++---- .../zero_order/forward_osmosis_zo.py | 52 +++++++++---------- .../zero_order/tests/test_forward_osmosis.py | 48 ++++++++--------- 4 files changed, 65 insertions(+), 69 deletions(-) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py index 9b5a1e70..1f79522f 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/fo_trevi_flowsheet.py @@ -54,7 +54,7 @@ def build_fo_trevi_flowsheet( separation_temp=90, # Separation temperature of the draw solution (C) separator_temp_loss=1, # Temperature loss in the separator (K) feed_temperature=13, # Feed water temperature (C) - feed_vol_flow=3.704, # Feed water volumetric flow rate (m3/s) + feed_vol_flow=0.022, # Feed water volumetric flow rate (m3/s) feed_TDS_mass=0.035, # TDS mass fraction of feed strong_draw_temp=20, # Strong draw solution inlet temperature (C) strong_draw_mass=0.8, # Strong draw solution mass fraction @@ -287,7 +287,7 @@ def add_fo( # Specify strong draw solution properties fo.strong_draw_props.calculate_state( var_args={ - ("flow_vol_phase", "Liq"): 1, + ("flow_vol_phase", "Liq"): feed_vol_flow, ("mass_frac_phase_comp", ("Liq", "DrawSolution")): strong_draw_mass, ("temperature", None): strong_draw_temp + 273.15, ("pressure", None): 101325, @@ -300,7 +300,7 @@ def add_fo( # Specify product water properties fo.product_props.calculate_state( var_args={ - ("flow_vol_phase", "Liq"): 1, + ("flow_vol_phase", "Liq"): feed_vol_flow, ("mass_frac_phase_comp", ("Liq", "DrawSolution")): product_draw_mass, ("temperature", None): separation_temp - separator_temp_loss + 273.15, ("pressure", None): 101325, @@ -327,16 +327,16 @@ def add_fo( # Set scaling factors for mass flow rates fs.seawater_properties.set_default_scaling( - "flow_mass_phase_comp", 1e-1, index=("Liq", "H2O") + "flow_mass_phase_comp", 1 / feed_vol_flow / 100, index=("Liq", "H2O") ) fs.seawater_properties.set_default_scaling( - "flow_mass_phase_comp", 1e1, index=("Liq", "TDS") + "flow_mass_phase_comp", 1 / feed_vol_flow / 10, index=("Liq", "TDS") ) fs.draw_solution_properties.set_default_scaling( - "flow_mass_phase_comp", 1e-2, index=("Liq", "H2O") + "flow_mass_phase_comp", 1 / feed_vol_flow / 100, index=("Liq", "H2O") ) fs.draw_solution_properties.set_default_scaling( - "flow_mass_phase_comp", 1e-2, index=("Liq", "DrawSolution") + "flow_mass_phase_comp", 1 / feed_vol_flow / 10, index=("Liq", "DrawSolution") ) diff --git a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py index 94d0270b..26dbad2d 100644 --- a/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py +++ b/src/watertap_contrib/reflo/analysis/example_flowsheets/test/test_fo_trevi_flowsheet.py @@ -40,11 +40,11 @@ def fo_trevi_frame(self): RO_recovery_ratio=0.9, # RO recovery ratio NF_recovery_ratio=0.8, # Nanofiltration recovery ratio dp_brine=0, # Required pressure over brine osmotic pressure (Pa) - heat_mixing=105, # Heat of mixing in the membrane (MJ/m3 product) + heat_mixing=75.6, # Heat of mixing in the membrane (MJ/m3 product) separation_temp=90, # Separation temperature of the draw solution (C) separator_temp_loss=1, # Temperature loss in the separator (K) feed_temperature=13, # Feed water temperature (C) - feed_vol_flow=3.704, # Feed water volumetric flow rate (m3/s) + feed_vol_flow=0.022, # Feed water volumetric flow rate (m3/s) feed_TDS_mass=0.035, # TDS mass fraction of feed strong_draw_temp=20, # Strong draw solution inlet temperature (C) strong_draw_mass=0.8, # Strong draw solution mass fraction @@ -113,25 +113,25 @@ def test_solution(self, fo_trevi_frame): rel=1e-3, ) assert overall_performance["Production capacity (m3/day)"] == pytest.approx( - 85683.32, abs=1e-3 + 508.918, abs=1e-3 ) assert overall_performance[ "Specific thermal energy consumption (kWh/m3)" - ] == pytest.approx(28.3, rel=1e-3) + ] == pytest.approx(26.84, rel=1e-3) assert overall_performance["Thermal power requirement (kW)"] == pytest.approx( - 101038.77, rel=1e-3 + 569.175, rel=1e-3 ) - assert overall_performance["LCOW ($/m3)"] == pytest.approx(0.462, rel=1e-3) + assert overall_performance["LCOW ($/m3)"] == pytest.approx(0.454, rel=1e-3) assert operational_parameters["HX1 cold in temp"] == pytest.approx( - 21.49, rel=1e-3 + 21.23, rel=1e-3 ) assert operational_parameters["HX2 hot out temp"] == pytest.approx( - 32.09, rel=1e-3 + 30.636, rel=1e-3 ) assert operational_parameters["HX1 cold side heat load (MJ)"] == pytest.approx( - 353.49, rel=1e-3 + 2.0996, rel=1e-3 ) assert operational_parameters["HX2 cold side heat load (MJ)"] == pytest.approx( - 286.20, rel=1e-3 + 1.5516, rel=1e-3 ) diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py index 0945d9d5..3671e8b2 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/forward_osmosis_zo.py @@ -16,6 +16,7 @@ Suffix, check_optimal_termination, units as pyunits, + value, ) from pyomo.common.config import ConfigBlock, ConfigValue, In from idaes.core import ( @@ -151,10 +152,10 @@ def build(self): ) self.heat_mixing = Var( - initialize=105, + initialize=75.6, bounds=(0, None), units=pyunits.MJ / pyunits.m**3, - doc="Heat of mixing in membrane (per m3 of product water)", + doc="Heat of mixing in membrane (per m3 of separated water)", ) """ @@ -246,7 +247,7 @@ def build(self): ) """ - Add block for the product water from the separator + Add block for the product water (contaminated with drawsolution) from the separator """ self.product_props = ( self.config.property_package_draw_solution.state_block_class( @@ -272,17 +273,13 @@ def build(self): def eq_brine_vol_flow(b): return b.brine_props[0].flow_vol_phase["Liq"] == b.feed_props[ 0 - ].flow_vol_phase["Liq"] * ( - 1 - b.recovery_ratio / b.nanofiltration_recovery_ratio - ) + ].flow_vol_phase["Liq"] * (1 - b.recovery_ratio) @self.Constraint(doc="Brine salinity") def eq_brine_salinity(b): return b.brine_props[0].conc_mass_phase_comp["Liq", "TDS"] == b.feed_props[ 0 - ].conc_mass_phase_comp["Liq", "TDS"] / ( - 1 - b.recovery_ratio / b.nanofiltration_recovery_ratio - ) + ].conc_mass_phase_comp["Liq", "TDS"] / (1 - b.recovery_ratio) @self.Constraint(doc="Product water flow rate") def eq_product_water_mass_flow(b): @@ -337,8 +334,7 @@ def eq_heat_mixing(b): def eq_temp_dif_membrane1(b): return b.delta_temp_membrane == pyunits.convert( b.heat_transfer_to_weak_draw - * pyunits.m**3 - / pyunits.s + * b.product_props[0].flow_vol_phase["Liq"] / b.weak_draw_props[0].dens_mass_phase["Liq"] / b.weak_draw_props[0].flow_vol_phase["Liq"] / b.weak_draw_props[0].cp_mass_phase["Liq"], @@ -351,8 +347,7 @@ def eq_temp_dif_membrane1(b): def eq_temp_dif_membrane2(b): return b.delta_temp_membrane == pyunits.convert( b.heat_transfer_to_brine - * pyunits.m**3 - / pyunits.s + * b.product_props[0].flow_vol_phase["Liq"] / b.brine_props[0].dens_mass_phase["Liq"] / b.brine_props[0].flow_vol_phase["Liq"] / b.brine_props[0].cp_mass_phase["Liq"], @@ -414,6 +409,14 @@ def eq_product_temp(b): == b.regeneration_temp - b.separator_temp_loss ) + # Touch properties that need to be calculated + self.reg_draw_props[0].flow_vol_phase["Liq"] + self.reg_draw_props[0].mass_frac_phase_comp["Liq", "DrawSolution"] + self.reg_draw_props[0].cp_mass_phase["Liq"] + self.product_props[0].flow_vol_phase["Liq"] + self.product_props[0].mass_frac_phase_comp["Liq", "DrawSolution"] + self.product_props[0].cp_mass_phase["Liq"] + def initialize_build( blk, state_args=None, @@ -448,7 +451,7 @@ def initialize_build( state_args=state_args, hold_state=True, ) - init_log.info("Initialization Step 1 Complete.") + init_log.info("FO Initialization Step 1 Complete.") # --------------------------------------------------------------------- # Initialize other state blocks if state_args is None: @@ -528,11 +531,11 @@ def initialize_build( for p, j in blk.strong_draw_props.phase_component_set: if p == "Liq" and j == "DrawSolution": state_args_strong_draw["flow_mass_phase_comp"][(p, j)] = ( - state_args_weak_draw["flow_mass_phase_comp"][(p, "H2O")] + state_args_weak_draw["flow_mass_phase_comp"][(p, "DrawSolution")] ) elif p == "Liq" and j == "H2O": state_args_strong_draw["flow_mass_phase_comp"][(p, j)] = ( - state_args_strong_draw["flow_mass_phase_comp"][(p, "DrawSolution")] + state_args_weak_draw["flow_mass_phase_comp"][(p, "DrawSolution")] * 0.25 # typical draw : water in strong draw solution ) @@ -550,7 +553,7 @@ def initialize_build( state_args_reg_draw["flow_mass_phase_comp"][(p, j)] = ( state_args_strong_draw["flow_mass_phase_comp"][(p, j)] ) - state_args_strong_draw["temperature"] = 90 + 273.15 + state_args_reg_draw["temperature"] = 90 + 273.15 blk.reg_draw_props.initialize( outlvl=outlvl, @@ -571,7 +574,8 @@ def initialize_build( ) elif p == "Liq" and j == "DrawSolution": state_args_product["flow_mass_phase_comp"][(p, j)] = ( - state_args_product["flow_mass_phase_comp"][(p, "H2O")] + state_args["flow_mass_phase_comp"][(p, "H2O")] + * (blk.recovery_ratio / blk.nanofiltration_recovery_ratio) * 0.01 # Typical mass fraction of draw in product * pyunits.kg / pyunits.s @@ -590,11 +594,11 @@ def initialize_build( # Solve unit with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) - init_log.info("Initialization Step 2 {}.".format(idaeslog.condition(res))) + init_log.info("FO Initialization Step 2 {}.".format(idaeslog.condition(res))) # --------------------------------------------------------------------- # Release Inlet state blk.feed_props.release_state(flags, outlvl=outlvl) - init_log.info("Initialization Complete: {}".format(idaeslog.condition(res))) + init_log.info("FO Initialization Complete: {}".format(idaeslog.condition(res))) if not check_optimal_termination(res): raise InitializationError(f"Unit model {blk.name} failed to initialize") @@ -612,14 +616,6 @@ def unfix_and_fix_freedom(self, mass_frac_strong_draw, mass_frac_product): mass_frac_product ) - # Touch properties that need to be calculated - self.reg_draw_props[0].flow_vol_phase["Liq"] - self.reg_draw_props[0].mass_frac_phase_comp["Liq", "DrawSolution"] - self.reg_draw_props[0].cp_mass_phase["Liq"] - self.product_props[0].flow_vol_phase["Liq"] - self.product_props[0].mass_frac_phase_comp["Liq", "DrawSolution"] - self.product_props[0].cp_mass_phase["Liq"] - def calculate_scaling_factors(self): super().calculate_scaling_factors() diff --git a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py index 335b3120..b23d4e07 100644 --- a/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py +++ b/src/watertap_contrib/reflo/unit_models/zero_order/tests/test_forward_osmosis.py @@ -70,7 +70,7 @@ def FO_frame(self): recovery_ratio = 0.3 # Assumed FO recovery ratio nanofiltration_recovery_ratio = 0.8 # Nanofiltration recovery ratio dp_brine = 0 # Required pressure over brine osmotic pressure (Pa) - heat_mixing = 105 # Heat of mixing in the membrane (MJ/m3 product) + heat_mixing = 75.6 # Heat of mixing in the membrane (MJ/m3 product) reneration_temp = 90 # Separation temperature of the draw solution (C) separator_temp_loss = 1 # Temperature loss in the separator (K) feed_temperature = 13 # Feed water temperature (C) @@ -90,7 +90,7 @@ def FO_frame(self): # Specify strong draw solution properties fo.strong_draw_props.calculate_state( var_args={ - ("flow_vol_phase", "Liq"): 1, + ("flow_vol_phase", "Liq"): feed_vol_flow, ( "mass_frac_phase_comp", ("Liq", "DrawSolution"), @@ -106,7 +106,7 @@ def FO_frame(self): # Specify product water properties fo.product_props.calculate_state( var_args={ - ("flow_vol_phase", "Liq"): 1, + ("flow_vol_phase", "Liq"): feed_vol_flow, ( "mass_frac_phase_comp", ("Liq", "DrawSolution"), @@ -159,8 +159,8 @@ def test_build(self, FO_frame): assert len(port.vars) == 3 # test statistics - assert number_variables(m) == 164 - assert number_total_constraints(m) == 50 + assert number_variables(m) == 170 + assert number_total_constraints(m) == 56 assert number_unused_variables(m) == 70 # vars from property package parameters @pytest.mark.unit @@ -217,50 +217,50 @@ def test_solution(self, FO_frame): assert value( strong_draw.flow_mass_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(1782.05, rel=1e-3) + ) == pytest.approx(1585.94, rel=1e-3) assert value(strong_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 445.51, rel=1e-3 + 396.48, rel=1e-3 ) assert value(strong_draw.flow_vol_phase["Liq"]) == pytest.approx( - 2.053, rel=1e-3 + 1.827, rel=1e-3 ) assert value( weak_draw.flow_mass_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(1782.05, rel=1e-3) + ) == pytest.approx(1585.94, rel=1e-3) assert value(weak_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 1546.16, rel=1e-3 + 1497.13, rel=1e-3 ) - assert value(weak_draw.flow_vol_phase["Liq"]) == pytest.approx(3.106, rel=1e-3) - assert value(weak_draw.temperature) == pytest.approx(293.61, rel=1e-3) + assert value(weak_draw.flow_vol_phase["Liq"]) == pytest.approx(2.883, rel=1e-3) + assert value(weak_draw.temperature) == pytest.approx(293.25, rel=1e-3) assert value( weak_draw.mass_frac_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(0.5354, rel=1e-3) + ) == pytest.approx(0.5140, rel=1e-3) assert value(brine.mass_frac_phase_comp["Liq", "TDS"]) == pytest.approx( - 0.05525, rel=1e-3 + 0.04956, rel=1e-3 ) assert value(brine.conc_mass_phase_comp["Liq", "TDS"]) == pytest.approx( - 57.48, rel=1e-3 + 51.32, rel=1e-3 ) assert value(brine.pressure_osm_phase["Liq"]) == pytest.approx( - 4163102, rel=1e-3 + 3689472, rel=1e-3 ) - assert value(brine.temperature) == pytest.approx(293.61, rel=1e-3) + assert value(brine.temperature) == pytest.approx(293.25, rel=1e-3) assert value(product.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 1377.39, rel=1e-3 + 1377.37, rel=1e-3 ) assert value( product.flow_mass_phase_comp["Liq", "DrawSolution"] ) == pytest.approx(13.913, rel=1e-3) assert value(product.flow_vol_phase["Liq"]) == pytest.approx(1.389, rel=1e-3) assert value(reg_draw.flow_mass_phase_comp["Liq", "H2O"]) == pytest.approx( - 445.51, rel=1e-3 + 396.48, rel=1e-3 ) assert value( reg_draw.flow_mass_phase_comp["Liq", "DrawSolution"] - ) == pytest.approx(1782.05, rel=1e-3) - assert value(m.fs.fo.delta_temp_membrane) == pytest.approx(5.70, rel=1e-3) - assert value(m.fs.fo.membrane_temp) == pytest.approx(287.689, rel=1e-3) + ) == pytest.approx(1585.94, rel=1e-3) + assert value(m.fs.fo.delta_temp_membrane) == pytest.approx(5.52, rel=1e-3) + assert value(m.fs.fo.membrane_temp) == pytest.approx(287.743, rel=1e-3) assert value(m.fs.fo.heat_transfer_to_weak_draw) == pytest.approx( - 51.45, rel=1e-3 + 33.72, rel=1e-3 ) - assert value(m.fs.fo.heat_transfer_to_brine) == pytest.approx(53.55, rel=1e-3) + assert value(m.fs.fo.heat_transfer_to_brine) == pytest.approx(41.88, rel=1e-3)