From 3a1d54a68ab02e2a09f9d9831b6ff40a69e03362 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Tue, 16 Apr 2024 08:55:17 -0400 Subject: [PATCH] Adding attributes to hold units used by MSContactor (#1396) * Adding attributes to hold units used by MSContactor * Removing unneccessary argument and fixing tests --- idaes/models/unit_models/mscontactor.py | 65 +++++---- .../unit_models/tests/test_mscontactor.py | 130 +++++++++--------- 2 files changed, 98 insertions(+), 97 deletions(-) diff --git a/idaes/models/unit_models/mscontactor.py b/idaes/models/unit_models/mscontactor.py index 68d839e87f..5ba55666ef 100644 --- a/idaes/models/unit_models/mscontactor.py +++ b/idaes/models/unit_models/mscontactor.py @@ -385,17 +385,21 @@ def build(self): # Call UnitModel.build super().build() + # Placeholders for things we will get from first StateBlock + self.flow_basis = None + self.uom = None + self._verify_inputs() - flow_basis, uom = self._build_state_blocks() + self._build_state_blocks() if self.config.heterogeneous_reactions is not None: self._build_heterogeneous_reaction_blocks() - self._add_geometry(uom) + self._add_geometry() - self._build_material_balance_constraints(flow_basis, uom) - self._build_energy_balance_constraints(uom) - self._build_pressure_balance_constraints(uom) + self._build_material_balance_constraints() + self._build_energy_balance_constraints() + self._build_pressure_balance_constraints() self._build_ports() def _verify_inputs(self): @@ -461,10 +465,6 @@ def _verify_inputs(self): def _build_state_blocks(self): # Build state blocks - # Placeholders for things we will get from first StateBlock - flow_basis = None - uom = None - for stream, pconfig in self.config.streams.items(): ppack = pconfig.property_package @@ -514,19 +514,19 @@ def _build_state_blocks(self): tref = self.flowsheet().time.first() sref = self.elements.first() - if flow_basis is None: + if self.flow_basis is None: # Set unit level flow basis and units from first stream - flow_basis = state[tref, sref].get_material_flow_basis() - uom = state[tref, sref].params.get_metadata().derived_units + self.flow_basis = state[tref, sref].get_material_flow_basis() + self.uom = state[tref, sref].params.get_metadata().derived_units else: # Check that flow bases are consistent - if not state[tref, sref].get_material_flow_basis() == flow_basis: + if not state[tref, sref].get_material_flow_basis() == self.flow_basis: raise ConfigurationError( f"Property packages use different flow bases: ExtractionCascade " f"requires all property packages to use the same basis. " f"{stream} uses {state[tref, sref].get_material_flow_basis()}, " - f"whilst first stream uses {flow_basis}." + f"whilst first stream uses {self.flow_basis}." ) # Build reactions blocks if provided @@ -543,8 +543,6 @@ def _build_state_blocks(self): ) self.add_component(stream + "_reactions", reactions) - return flow_basis, uom - def _build_heterogeneous_reaction_blocks(self): rpack = self.config.heterogeneous_reactions rpack_args = self.config.heterogeneous_reactions_args @@ -569,14 +567,14 @@ def _build_heterogeneous_reaction_blocks(self): "reactions (reaction_idx)." ) - def _add_geometry(self, uom): + def _add_geometry(self): if self.config.has_holdup: # Add volume for each element # TODO: Assuming constant volume for now self.volume = Var( self.elements, initialize=1, - units=uom.VOLUME, + units=self.uom.VOLUME, doc="Volume of element", ) self.volume_frac_stream = Var( @@ -600,14 +598,14 @@ def sum_volume_frac(b, t, e): phase_list = getattr(self, stream).phase_list _add_phase_fractions(self, stream, phase_list) - def _build_material_balance_constraints(self, flow_basis, uom): + def _build_material_balance_constraints(self): # Get units for transfer terms - if flow_basis is MaterialFlowBasis.molar: - mb_units = uom.FLOW_MOLE - hu_units = uom.AMOUNT - elif flow_basis is MaterialFlowBasis.mass: - mb_units = uom.FLOW_MASS - hu_units = uom.MASS + if self.flow_basis is MaterialFlowBasis.molar: + mb_units = self.uom.FLOW_MOLE + hu_units = self.uom.AMOUNT + elif self.flow_basis is MaterialFlowBasis.mass: + mb_units = self.uom.FLOW_MASS + hu_units = self.uom.MASS else: # Flow type other, so cannot determine units mb_units = None @@ -895,9 +893,8 @@ def _build_material_balance_constraints(self, flow_basis, uom): ) self.add_component(stream + "_material_balance", mbal) - def _build_energy_balance_constraints(self, uom): + def _build_energy_balance_constraints(self): # Energy Balances - for stream, pconfig in self.config.streams.items(): if pconfig.has_energy_balance: state_block = getattr(self, stream) @@ -912,7 +909,7 @@ def _build_energy_balance_constraints(self, uom): domain=Reals, initialize=1.0, doc="Energy holdup of stream in element", - units=uom.ENERGY, + units=self.uom.ENERGY, ) self.add_component( stream + "_energy_holdup", @@ -939,7 +936,7 @@ def _build_energy_balance_constraints(self, uom): energy_holdup, wrt=self.flowsheet().time, doc="Energy accumulation for in element", - units=uom.POWER, + units=self.uom.POWER, ) self.add_component( stream + "_energy_accumulation", @@ -951,7 +948,7 @@ def _build_energy_balance_constraints(self, uom): self.flowsheet().time, self.elements, initialize=0, - units=uom.POWER, + units=self.uom.POWER, doc=f"External heat transfer term for stream {stream}", ) self.add_component(stream + "_heat", heat) @@ -962,12 +959,12 @@ def _build_energy_balance_constraints(self, uom): rule=partial( _energy_balance_rule, stream=stream, - uom=uom, + uom=self.uom, ), ) self.add_component(stream + "_energy_balance", ebal) - def _build_pressure_balance_constraints(self, uom): + def _build_pressure_balance_constraints(self): # Pressure Balances for stream, pconfig in self.config.streams.items(): if pconfig.has_pressure_balance: @@ -977,7 +974,7 @@ def _build_pressure_balance_constraints(self, uom): self.flowsheet().time, self.elements, initialize=0, - units=uom.PRESSURE, + units=self.uom.PRESSURE, doc=f"DeltaP term for stream {stream}", ) self.add_component(stream + "_deltaP", deltaP) @@ -988,7 +985,7 @@ def _build_pressure_balance_constraints(self, uom): rule=partial( _pressure_balance_rule, stream=stream, - uom=uom, + uom=self.uom, ), ) self.add_component(stream + "_pressure_balance", pbal) diff --git a/idaes/models/unit_models/tests/test_mscontactor.py b/idaes/models/unit_models/tests/test_mscontactor.py index 028d1e8b3f..0cdfd86d43 100644 --- a/idaes/models/unit_models/tests/test_mscontactor.py +++ b/idaes/models/unit_models/tests/test_mscontactor.py @@ -314,6 +314,10 @@ class ECFrameData(MSContactorData): def build(self): super(MSContactorData, self).build() + # Add placeholders that would normally be built in build() + self.flow_basis = None + self.uom = None + # ----------------------------------------------------------------------------- class TestBuild: @@ -507,10 +511,10 @@ def test_verify_inputs_construct_components(self, model): @pytest.mark.unit def test_build_state_blocks(self, model): model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() + model.fs.unit._build_state_blocks() - assert flow_basis == MaterialFlowBasis.molar - assert uom == model.fs.properties1.get_metadata().derived_units + assert model.fs.unit.flow_basis == MaterialFlowBasis.molar + assert model.fs.unit.uom == model.fs.properties1.get_metadata().derived_units assert isinstance(model.fs.unit.stream1, StateBlock1) assert len(model.fs.unit.stream1) == 2 @@ -540,10 +544,10 @@ def test_build_state_blocks(self, model): def test_build_state_blocks_no_feed(self, model): model.fs.unit.config.streams["stream2"].has_feed = False model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() + model.fs.unit._build_state_blocks() - assert flow_basis == MaterialFlowBasis.molar - assert uom == model.fs.properties1.get_metadata().derived_units + assert model.fs.unit.flow_basis == MaterialFlowBasis.molar + assert model.fs.unit.uom == model.fs.properties1.get_metadata().derived_units assert isinstance(model.fs.unit.stream1, StateBlock1) assert len(model.fs.unit.stream1) == 2 @@ -570,10 +574,10 @@ def test_build_state_blocks_no_feed(self, model): def test_build_state_blocks_side_stream(self, model): model.fs.unit.config.streams["stream2"].side_streams = [1] model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() + model.fs.unit._build_state_blocks() - assert flow_basis == MaterialFlowBasis.molar - assert uom == model.fs.properties1.get_metadata().derived_units + assert model.fs.unit.flow_basis == MaterialFlowBasis.molar + assert model.fs.unit.uom == model.fs.properties1.get_metadata().derived_units assert isinstance(model.fs.unit.stream1, StateBlock1) assert len(model.fs.unit.stream1) == 2 @@ -748,8 +752,8 @@ def test_get_state_blocks_side_streams(self, model): @pytest.mark.unit def test_add_geometry_no_holdup(self, model): model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() - model.fs.unit._add_geometry(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._add_geometry() assert not hasattr(model.fs.unit, "volume") assert not hasattr(model.fs.unit, "volume_frac_stream") @@ -764,8 +768,8 @@ def test_add_geometry_no_holdup(self, model): @pytest.mark.unit def test_add_geometry_holdup_single_phase(self, dynamic): dynamic.fs.unit._verify_inputs() - flow_basis, uom = dynamic.fs.unit._build_state_blocks() - dynamic.fs.unit._add_geometry(uom) + dynamic.fs.unit._build_state_blocks() + dynamic.fs.unit._add_geometry() assert isinstance(dynamic.fs.unit.volume, Var) assert len(dynamic.fs.unit.volume) == 2 @@ -808,8 +812,8 @@ def test_add_geometry_holdup_multi_phase(self): ) m.fs.unit._verify_inputs() - flow_basis, uom = m.fs.unit._build_state_blocks() - m.fs.unit._add_geometry(uom) + m.fs.unit._build_state_blocks() + m.fs.unit._add_geometry() assert isinstance(m.fs.unit.volume, Var) assert len(m.fs.unit.volume) == 2 @@ -838,8 +842,8 @@ def test_add_geometry_holdup_multi_phase(self): @pytest.mark.unit def test_material_balances(self, model): model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_material_balance_constraints(flow_basis, uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_material_balance_constraints() assert isinstance(model.fs.unit.material_transfer_term, Var) # One stream pair with two common components over two elements and 1 time point @@ -908,9 +912,9 @@ def test_material_balances(self, model): @pytest.mark.unit def test_material_balances_dynamic(self, dynamic): dynamic.fs.unit._verify_inputs() - flow_basis, uom = dynamic.fs.unit._build_state_blocks() - dynamic.fs.unit._add_geometry(uom) - dynamic.fs.unit._build_material_balance_constraints(flow_basis, uom) + dynamic.fs.unit._build_state_blocks() + dynamic.fs.unit._add_geometry() + dynamic.fs.unit._build_material_balance_constraints() assert isinstance(dynamic.fs.unit.material_transfer_term, Var) # One stream pair with two common components over two elements and 2 time point @@ -1059,8 +1063,8 @@ def test_material_balances_dynamic(self, dynamic): def test_build_material_balances_no_feed(self, model): model.fs.unit.config.streams["stream2"].has_feed = False model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_material_balance_constraints(flow_basis, uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_material_balance_constraints() assert isinstance(model.fs.unit.material_transfer_term, Var) # One stream pair with two common components over two elements and 1 time point @@ -1128,8 +1132,8 @@ def test_build_material_balances_no_feed(self, model): def test_material_balances_side_stream(self, model): model.fs.unit.config.streams["stream2"].side_streams = [1] model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_material_balance_constraints(flow_basis, uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_material_balance_constraints() assert isinstance(model.fs.unit.material_transfer_term, Var) # One stream pair with two common components over two elements and 1 time point @@ -1204,8 +1208,8 @@ def test_material_balances_side_stream(self, model): @pytest.mark.unit def test_energy_balances(self, model): model.fs.unit._verify_inputs() - _, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_energy_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_energy_balance_constraints() assert isinstance(model.fs.unit.energy_transfer_term, Var) # 1 stream interaction, 2 elements @@ -1265,9 +1269,9 @@ def test_energy_balances(self, model): @pytest.mark.unit def test_energy_balances_dynamic(self, dynamic): dynamic.fs.unit._verify_inputs() - _, uom = dynamic.fs.unit._build_state_blocks() - dynamic.fs.unit._add_geometry(uom) - dynamic.fs.unit._build_energy_balance_constraints(uom) + dynamic.fs.unit._build_state_blocks() + dynamic.fs.unit._add_geometry() + dynamic.fs.unit._build_energy_balance_constraints() assert isinstance(dynamic.fs.unit.energy_transfer_term, Var) # 1 stream interaction, 2 elements @@ -1370,8 +1374,8 @@ def test_energy_balances_dynamic(self, dynamic): def test_energy_balances_has_heat_transfer(self, model): model.fs.unit.config.streams["stream2"].has_heat_transfer = True model.fs.unit._verify_inputs() - _, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_energy_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_energy_balance_constraints() assert not hasattr(model.fs.unit, "stream1_heat") assert isinstance(model.fs.unit.stream1_energy_balance, Constraint) @@ -1430,8 +1434,8 @@ def test_energy_balances_has_heat_transfer(self, model): def test_energy_balances_no_feed(self, model): model.fs.unit.config.streams["stream2"].has_feed = False model.fs.unit._verify_inputs() - _, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_energy_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_energy_balance_constraints() assert isinstance(model.fs.unit.stream1_energy_balance, Constraint) # 1 time point, 2 elements @@ -1482,8 +1486,8 @@ def test_energy_balances_no_feed(self, model): def test_energy_balances_has_energy_balance_false(self, model): model.fs.unit.config.streams["stream2"].has_energy_balance = False model.fs.unit._verify_inputs() - _, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_energy_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_energy_balance_constraints() assert isinstance(model.fs.unit.stream1_energy_balance, Constraint) # 1 time point, 2 elements @@ -1514,8 +1518,8 @@ def test_energy_balances_has_energy_balance_false(self, model): def test_energy_balances_side_stream(self, model): model.fs.unit.config.streams["stream2"].side_streams = [1] model.fs.unit._verify_inputs() - _, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_energy_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_energy_balance_constraints() assert isinstance(model.fs.unit.stream1_energy_balance, Constraint) # 1 time point, 2 elements @@ -1567,8 +1571,8 @@ def test_energy_balances_side_stream(self, model): @pytest.mark.unit def test_pressure_balances(self, model): model.fs.unit._verify_inputs() - _, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_pressure_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_pressure_balance_constraints() assert isinstance(model.fs.unit.stream1_pressure_balance, Constraint) # 1 time point, 2 elements @@ -1619,8 +1623,8 @@ def test_pressure_balances(self, model): def test_pressure_balances_deltaP(self, model): model.fs.unit.config.streams["stream2"].has_pressure_change = True model.fs.unit._verify_inputs() - _, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_pressure_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_pressure_balance_constraints() assert not hasattr(model.fs.unit, "stream1_deltaP") assert isinstance(model.fs.unit.stream1_pressure_balance, Constraint) @@ -1678,8 +1682,8 @@ def test_pressure_balances_deltaP(self, model): def test_pressure_balances_no_feed(self, model): model.fs.unit.config.streams["stream2"].has_feed = False model.fs.unit._verify_inputs() - _, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_pressure_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_pressure_balance_constraints() assert isinstance(model.fs.unit.stream1_pressure_balance, Constraint) # 1 time point, 2 elements @@ -1722,8 +1726,8 @@ def test_pressure_balances_no_feed(self, model): def test_pressure_balances_has_pressure_balance_false(self, model): model.fs.unit.config.streams["stream2"].has_pressure_balance = False model.fs.unit._verify_inputs() - _, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_pressure_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_pressure_balance_constraints() assert isinstance(model.fs.unit.stream1_pressure_balance, Constraint) # 1 time point, 2 elements @@ -1755,8 +1759,8 @@ def test_pressure_balances_has_pressure_balance_false(self, model): def test_pressure_balances_side_stream(self, model): model.fs.unit.config.streams["stream2"].side_streams = [1] model.fs.unit._verify_inputs() - _, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_pressure_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_pressure_balance_constraints() assert isinstance(model.fs.unit.stream1_pressure_balance, Constraint) # 1 time point, 2 elements @@ -1961,8 +1965,8 @@ def test_inherent_reactions(self, model): model.fs.properties._has_inherent_reactions = True model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_material_balance_constraints(flow_basis, uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_material_balance_constraints() assert isinstance(model.fs.unit.stream1_inherent_reaction_extent, Var) assert len(model.fs.unit.stream1_inherent_reaction_extent) == 4 @@ -2135,8 +2139,8 @@ def test_equilibrium_reactions(self, model): model.fs.unit.config.streams["stream2"].has_equilibrium_reactions = True model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_material_balance_constraints(flow_basis, uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_material_balance_constraints() assert not hasattr(model.fs.unit, "stream1_equilibrium_reaction_extent") assert not hasattr(model.fs.unit, "stream1_equilibrium_reaction_generation") @@ -2248,7 +2252,7 @@ def test_heterogeneous_reactions_no_build_method(self, model): model.fs.unit.config.heterogeneous_reactions = True model.fs.unit._verify_inputs() - _, _ = model.fs.unit._build_state_blocks() + model.fs.unit._build_state_blocks() with pytest.raises( ConfigurationError, @@ -2272,7 +2276,7 @@ def build_reaction_block(*args, **kwargs): model.fs.unit.config.heterogeneous_reactions = model.hetero_dummy model.fs.unit._verify_inputs() - _, _ = model.fs.unit._build_state_blocks() + model.fs.unit._build_state_blocks() with pytest.raises( PropertyNotSupportedError, @@ -2295,7 +2299,7 @@ def test_heterogeneous_reactions(self, model): model.fs.unit.config.heterogeneous_reactions = model.fs.hetero_dummy model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() + model.fs.unit._build_state_blocks() model.fs.unit.heterogeneous_reactions = Block( model.fs.time, @@ -2308,7 +2312,7 @@ def test_heterogeneous_reactions(self, model): model.fs.hetero_dummy, ) - model.fs.unit._build_material_balance_constraints(flow_basis, uom) + model.fs.unit._build_material_balance_constraints() assert isinstance(model.fs.unit.heterogeneous_reaction_extent, Var) for k in model.fs.unit.heterogeneous_reaction_extent.keys(): @@ -2446,8 +2450,8 @@ def test_rate_reactions(self, model): model.fs.unit.config.streams["stream2"].has_rate_reactions = True model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_material_balance_constraints(flow_basis, uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_material_balance_constraints() assert not hasattr(model.fs.unit, "stream1_rate_reaction_extent") assert not hasattr(model.fs.unit, "stream1_rate_reaction_generation") @@ -2559,9 +2563,9 @@ def test_heat_of_reaction_rate(self, model): model.fs.unit.config.streams["stream2"].has_heat_of_reaction = True model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_material_balance_constraints(flow_basis, uom) - model.fs.unit._build_energy_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_material_balance_constraints() + model.fs.unit._build_energy_balance_constraints() assert str(model.fs.unit.stream1_energy_balance[0, 1].expr) == str( 0 * (units.kg * units.m**2 * units.s**-3) @@ -2642,9 +2646,9 @@ def test_heat_of_reaction_equilibrium(self, model): model.fs.unit.config.streams["stream2"].has_heat_of_reaction = True model.fs.unit._verify_inputs() - flow_basis, uom = model.fs.unit._build_state_blocks() - model.fs.unit._build_material_balance_constraints(flow_basis, uom) - model.fs.unit._build_energy_balance_constraints(uom) + model.fs.unit._build_state_blocks() + model.fs.unit._build_material_balance_constraints() + model.fs.unit._build_energy_balance_constraints() assert str(model.fs.unit.stream1_energy_balance[0, 1].expr) == str( 0 * (units.kg * units.m**2 * units.s**-3)