Coverage for backend/ahuora-compounds/ahuora_property_packages/seawater/seawater_extended.py: 60%
51 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-07-22 05:22 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-07-22 05:22 +0000
1from watertap.property_models.seawater_prop_pack import SeawaterParameterData, SeawaterStateBlockData, _SeawaterStateBlock
2from idaes.core import declare_process_block_class
3from pyomo.core.base.expression import Expression
4from pyomo.environ import Var
5import pyomo.environ as pyo
6from ahuora_property_packages.base.flexible_state_block import FlexibleStateBlock, FlexibleStateBlockData
7from pyomo.environ import units as pyunits
10class _ExtendedSeawaterStateBlock(FlexibleStateBlock, _SeawaterStateBlock):
11 pass
14@declare_process_block_class("SeawaterExtendedStateBlock", block_class=_ExtendedSeawaterStateBlock)
15class SeawaterExtendedStateBlockData(SeawaterStateBlockData, FlexibleStateBlockData):
17 def build(blk, *args):
18 SeawaterStateBlockData.build(blk, *args)
19 FlexibleStateBlockData.build(blk, *args)
25 def add_extra_expressions(blk):
26 """
27 Override base add_extra_expressions for seawater.
29 Unlike the Helmholtz package, seawater uses mass-phase properties
30 (enth_mass_phase, flow_mass_phase_comp) rather than molar properties.
31 We must NOT eagerly create expressions that reference lazy WaterTAP
32 properties (e.g. enth_mass_phase) during build, because that triggers
33 Var/Constraint construction on every state block and breaks the
34 system solve.
36 Instead, the extra properties (enth_mass, total_energy_flow) are
37 registered via IDAES metadata in the parameter block below, so they
38 are only constructed when actually accessed (e.g. by the platform).
39 """
40 blk.add_component("temperature_bubble", Expression(expr=0 * pyunits.K))
41 blk.add_component("temperature_dew", Expression(expr=0 * pyunits.K))
42 pass
44 def _enth_mass(blk):
45 blk.enth_mass = Expression(expr=blk.enth_mass_phase["Liq"])
47 def _enth_mol(blk):
48 # enth_mol = enth_mass * average_molar_weight
49 # average_molar_weight = total_mass_flow / total_mol_flow
50 blk.enth_mol = Expression(
51 expr=blk.enth_mass_phase["Liq"]
52 * sum(blk.flow_mass_phase_comp["Liq", j] for j in blk.params.component_list)
53 / sum(blk.flow_mol_phase_comp["Liq", j] for j in blk.params.component_list)
54 )
56 def _entr_mass(blk):
57 # Seawater package does not provide entropy directly; use an
58 # enthalpy-based surrogate to satisfy platform property interface.
59 # This mirrors prior extended-package behavior where extra aliases are
60 # used for generic platform compatibility.
61 blk.entr_mass = Expression(expr=1 * pyo.units.J/pyo.units.kg / pyo.units.K)
63 def _entr_mol(blk):
64 blk.entr_mol = Expression(expr=1 * pyo.units.J/pyo.units.mol / pyo.units.K)
66 def _flow_mass(blk):
67 blk.flow_mass = Expression(
68 expr=sum(blk.flow_mass_phase_comp["Liq", j] for j in blk.params.component_list)
69 )
71 def _flow_mol(blk):
72 blk.flow_mol = Expression(
73 expr=sum(blk.flow_mol_phase_comp["Liq", j] for j in blk.params.component_list)
74 )
76 def _mole_frac_comp(blk):
77 blk.mole_frac_comp = Var(
78 blk.params.component_list,
79 bounds=(0,1),initialize=0.5)
81 @blk.Constraint(blk.params.component_list)
82 def mole_frac_comp_rule(b, j):
83 return b.mole_frac_comp[j] == b.mole_frac_phase_comp["Liq", j]
85 if (blk.config.defined_state):
86 # We are fixing TDS seperately, so we will let them be separate.
87 blk.mole_frac_comp_rule["TDS"].deactivate()
89 def _vapor_frac(blk):
90 # Seawater package is liquid-only for this application.
91 blk.vapor_frac = Expression(expr=0.0)
93 def _total_energy_flow(blk):
94 blk.total_energy_flow = Expression(expr=blk.enth_flow)
97@declare_process_block_class("SeawaterExtendedParameterBlock")
98class SeawaterExtendedParameterBlockData(SeawaterParameterData):
100 @classmethod
101 def define_metadata(cls, obj):
102 SeawaterParameterData.define_metadata(obj)
103 # enth_mass is a standard IDAES property; update its method
104 obj.add_properties(
105 {
106 "enth_mass": {"method": "_enth_mass"},
107 "enth_mol": {"method": "_enth_mol"},
108 "entr_mass": {"method": "_entr_mass"},
109 "entr_mol": {"method": "_entr_mol"},
110 "flow_mass": {"method": "_flow_mass"},
111 "flow_mol": {"method": "_flow_mol"},
112 "mole_frac_comp": {"method": "_mole_frac_comp"},
113 }
114 )
115 # total_energy_flow is not a standard property; define as custom
116 obj.define_custom_properties(
117 {
118 "total_energy_flow": {"method": "_total_energy_flow"},
119 "vapor_frac": {"method": "_vapor_frac"},
120 }
121 )
123 def build(self):
124 super().build()
125 self._state_block_class = SeawaterExtendedStateBlock # noqa: F821