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

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 

8 

9 

10class _ExtendedSeawaterStateBlock(FlexibleStateBlock, _SeawaterStateBlock): 

11 pass 

12 

13 

14@declare_process_block_class("SeawaterExtendedStateBlock", block_class=_ExtendedSeawaterStateBlock) 

15class SeawaterExtendedStateBlockData(SeawaterStateBlockData, FlexibleStateBlockData): 

16 

17 def build(blk, *args): 

18 SeawaterStateBlockData.build(blk, *args) 

19 FlexibleStateBlockData.build(blk, *args) 

20 

21 

22 

23 

24 

25 def add_extra_expressions(blk): 

26 """ 

27 Override base add_extra_expressions for seawater. 

28 

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. 

35 

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 

43 

44 def _enth_mass(blk): 

45 blk.enth_mass = Expression(expr=blk.enth_mass_phase["Liq"]) 

46 

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 ) 

55 

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) 

62 

63 def _entr_mol(blk): 

64 blk.entr_mol = Expression(expr=1 * pyo.units.J/pyo.units.mol / pyo.units.K) 

65 

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 ) 

70 

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 ) 

75 

76 def _mole_frac_comp(blk): 

77 blk.mole_frac_comp = Var( 

78 blk.params.component_list, 

79 bounds=(0,1),initialize=0.5) 

80 

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] 

84 

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() 

88 

89 def _vapor_frac(blk): 

90 # Seawater package is liquid-only for this application. 

91 blk.vapor_frac = Expression(expr=0.0) 

92 

93 def _total_energy_flow(blk): 

94 blk.total_energy_flow = Expression(expr=blk.enth_flow) 

95 

96 

97@declare_process_block_class("SeawaterExtendedParameterBlock") 

98class SeawaterExtendedParameterBlockData(SeawaterParameterData): 

99 

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 ) 

122 

123 def build(self): 

124 super().build() 

125 self._state_block_class = SeawaterExtendedStateBlock # noqa: F821