Coverage for backend/ahuora-compounds/ahuora_property_packages/helmholtz/helmholtz_extended.py: 38%
35 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 pyomo.environ import (
2 Expression,
3 units,
4 Constraint,
5 Var
6)
7from idaes.models.properties.general_helmholtz.helmholtz_state import HelmholtzStateBlockData, _StateBlock
8from idaes.models.properties.general_helmholtz.helmholtz_functions import HelmholtzParameterBlockData
9from idaes.core import declare_process_block_class
11from ahuora_property_packages.base.flexible_state_block import FlexibleStateBlockData, FlexibleStateBlock
14class _ExtendedStateBlock(FlexibleStateBlock,_StateBlock):
15 pass
17@declare_process_block_class("HelmholtzExtendedStateBlock", block_class=_ExtendedStateBlock)
18class HelmholtzExtendedStateBlockData(HelmholtzStateBlockData, FlexibleStateBlockData):
20 def build(blk, *args):
21 HelmholtzStateBlockData.build(blk, *args)
22 FlexibleStateBlockData.build(blk, *args)
24 def constrain_component(blk, component: Var | Expression, value: float) -> Var | None:
25 """Constrain a Helmholtz property using thermodynamic vapour quality."""
27 if component.local_name.startswith("mole_frac_comp"):
28 # Helmholtz packages are pure-component, so composition is invariant.
29 return None
31 if component.local_name == "vapor_frac":
32 # The native P-H vapour-fraction expression is clipped to zero
33 # throughout the liquid region and one throughout the vapour region.
34 # Such endpoint constraints cannot define saturation or determine
35 # pressure from temperature, so constrain thermodynamic quality using
36 # the saturated liquid and vapour enthalpies instead.
37 blk.add_component("vapor_frac_var", Var(units=units.dimensionless))
38 blk.add_component(
39 "vapor_frac_constraint",
40 Constraint(
41 expr=blk.enth_mol
42 == blk.enth_mol_sat_phase["Liq"]
43 + blk.vapor_frac_var
44 * (
45 blk.enth_mol_sat_phase["Vap"]
46 - blk.enth_mol_sat_phase["Liq"]
47 )
48 ),
49 )
50 blk.vapor_frac_var.fix(value)
51 blk.__dict__["vars_to_deactivate"].append(blk.vapor_frac_var)
52 return blk.vapor_frac_var
54 return super().constrain_component(component, value)
56 def add_extra_expressions(blk):
57 super().add_extra_expressions()
59 # Generic property packages support temperature_bubble and temperature_dew,
60 # Helmholtz is pure so these are the same, and so it only has
61 # temperature_sat. https://idaes-pse.readthedocs.io/en/stable/reference_guides/model_libraries/generic/property_models/helmholtz.html#expressions
62 # We add them here so that the property package interface is consistent.
63 blk.add_component("temperature_bubble", Expression(expr=blk.temperature_sat))
64 blk.add_component("temperature_dew", Expression(expr=blk.temperature_sat))
66 # For numerical stability when constraining temperature, we actually smooth out the temperature equilibrium curve.
67 # Otherwise, the solver has difficulty solving through the vapor fraction region as there is zero gradient.
69 # rename temperature to old_temperature
70 old_temperature = blk.temperature
71 blk.del_component("temperature")
72 blk.add_component("old_temperature", old_temperature)
73 # create smooth temperature expression
74 blk.add_component("temperature", Expression(
75 expr=blk.old_temperature + (blk.enth_mol / (1 * units.J/units.mol))*0.000001 * units.K
76 ))
79@declare_process_block_class("HelmholtzExtendedParameterBlock")
80class HelmholtzExtendedParameterBlockData(HelmholtzParameterBlockData):
81 def build(self):
82 super().build()
83 self._state_block_class = HelmholtzExtendedStateBlock # noqa: F821