Skip to content

Commit

Permalink
modify helmholtz initialization to match fix_state_vars and solve
Browse files Browse the repository at this point in the history
  • Loading branch information
alma-walmsley committed Jan 1, 2025
1 parent eed0162 commit 12b4e25
Showing 1 changed file with 127 additions and 55 deletions.
182 changes: 127 additions & 55 deletions property_packages/helmholtz/helmholtz_extended.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,105 @@
# extends the IDAES helmholtz property package to include additional properties and methods.
from pyomo.environ import Constraint, Block, SolverFactory
from pyomo.core.base.var import IndexedVar, ScalarVar, Var, _GeneralVarData, VarData
from pyomo.core.base.expression import Expression, ScalarExpression, _GeneralExpressionData, ExpressionData

from idaes.models.properties.general_helmholtz.helmholtz_state import HelmholtzStateBlockData, _StateBlock
from idaes.models.properties.general_helmholtz.helmholtz_functions import HelmholtzParameterBlockData
from idaes.core import declare_process_block_class
from property_packages.utils.add_extra_expressions import add_extra_expressions
from pyomo.environ import Constraint, Block
from pyomo.core.base.expression import Expression, ScalarExpression, _GeneralExpressionData, ExpressionData
from pyomo.core.base.var import IndexedVar, ScalarVar, Var, _GeneralVarData,VarData
from idaes.core.util.initialization import solve_indexed_blocks, revert_state_vars
from idaes.core.util.model_statistics import degrees_of_freedom
from idaes.core.util.exceptions import InitializationError
import idaes.logger as idaeslog

from property_packages.utils.add_extra_expressions import add_extra_expressions


def fix_state_vars(blk, state_args=None):
"""
Method for fixing state variables within StateBlocks. Method takes an
optional argument of values to use when fixing variables.
This method is analogous to the fix_state_vars method in
idaes.core.util.initialization, but it allows us to handle
the case where we have constraints that define the state.
Args:
blk : An IDAES StateBlock object in which to fix the state variables
state_args : a dict containing values to use when fixing state
variables. Keys must match with names used in the
define_state_vars method, and indices of any variables must
agree.
Returns:
A dict keyed by block index, state variable name (as defined by
define_state_variables) and variable index indicating the fixed status
of each variable before the fix_state_vars method was applied.
"""
if state_args is None:
state_args = {}

flags = {}
for k, b in blk.items():
available_constraints = [
"temperature",
"smooth_temperature",
"enth_mass",
"entr_mol",
"entr_mass",
"total_energy_flow",
"custom_vapor_frac",
]
for n, v in b.define_state_vars().items():
fix_var = True
if n == "flow_mol":
for prop_name in ["flow_mass", "flow_vol"]:
if hasattr(b.constraints, prop_name):
fix_var = False
break
elif n in ["enth_mol", "pressure"]:
if not v.is_fixed():
# check if any of the constraints exist
for prop_name in available_constraints:
if hasattr(b.constraints, prop_name):
# don't fix this variable - it is defined by a constraint
available_constraints.remove(prop_name)
fix_var = False
break
for i in v:
flags[k, n, i] = v[i].is_fixed()
if fix_var and not v[i].is_fixed():
# If not fixed, fix at either guess provided or current value
if n in state_args:
# Try to get initial guess from state_args
try:
if i is None:
val = state_args[n]
else:
val = state_args[n][i]
except KeyError:
raise ConfigurationError(
"Indexes in state_args did not agree with "
"those of state variable {}. Please ensure "
"that indexes for initial guesses are correct.".format(
n
)
)
v[i].fix(val)
else:
# No guess, try to use current value
if v[i].value is not None:
v[i].fix()
else:
# No initial value - raise Exception before this
# gets to a solver.
raise ConfigurationError(
"State variable {} does not have a value "
"assigned. This usually occurs when a Var "
"is not assigned an initial value when it is "
"created. Please ensure all variables have "
"valid values before fixing them.".format(v.name)
)
return flags


class _ExtendedStateBlock(_StateBlock):
"""
Expand All @@ -17,57 +109,37 @@ class _ExtendedStateBlock(_StateBlock):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def initialize(self, *args, **kwargs):
hold_state = kwargs.pop("hold_state", False)
for i, v in self.items():
v.constraints.deactivate()
res = super().initialize(*args, **kwargs)
flags = {}
for i, v in self.items():
v.constraints.activate()
flags[i] = {}
if hold_state:
# Fix the required state variables for zero degrees of freedom, and return a dictionary of the flags.
if not hasattr(v.constraints,"flow_mass") and not v.flow_mol.is_fixed():
# We need to fix the flow_mol variable
flags[i]["flow_mol"] = True
v.flow_mol.fix()
avaliable_constraints = ["enth_mass","temperature","total_energy_flow","entr_mass","entr_mol","smooth_temperature","custom_vapor_frac"]
if not v.enth_mol.is_fixed():
# check if any of the constraints exist
found_constraint = False
for constraint in avaliable_constraints:
if hasattr(v.constraints,constraint):
# we don't need to fix the variable
# but we need to remove this from the list of constraints (it can't be used to define pressure)
avaliable_constraints.remove(constraint)
found_constraint = True
break
if not found_constraint:
# we need to fix the variable
flags[i]["enth_mol"] = True
v.enth_mol.fix()
if not v.pressure.is_fixed():
# check if any of the constraints exist
found_constraint = False
for constraint in avaliable_constraints:
if hasattr(v.constraints,constraint):
# we don't need to fix the variable
# but we need to remove this from the list of constraints (it can't be used to define pressure)
avaliable_constraints.remove(constraint)
found_constraint = True
break
if not found_constraint:
# we need to fix the variable
flags[i]["pressure"] = True
v.pressure.fix()
return flags

def release_state(self, flags, outlvl=idaeslog.NOTSET):
for i, v in self.items():
for key in flags[i]:
getattr(v,key).unfix()
def initialize(blk, *args, **kwargs):
outlvl = kwargs.get("outlvl", idaeslog.NOTSET)
solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="properties")

flag_dict = fix_state_vars(blk, kwargs.get("state_args", None))

dof = degrees_of_freedom(blk)
if dof != 0:
raise InitializationError(
f"{blk.name} Unexpected degrees of freedom during "
f"initialization at property initialization step: {dof}."
)
opt = SolverFactory('ipopt')
with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
try:
res = solve_indexed_blocks(opt, [blk], tee=slc.tee)
except ValueError as e:
# https://github.com/Pyomo/pyomo/pull/3445
if str(e).startswith("No variables appear")
pass
else:
raise e

if kwargs.get("hold_state") is True:
return flag_dict
else:
blk.release_state(flag_dict)

def release_state(blk, flags, outlvl=idaeslog.NOTSET):
revert_state_vars(blk, flags)


@declare_process_block_class("HelmholtzExtendedStateBlock", block_class=_ExtendedStateBlock)
class HelmholtzExtendedStateBlockData(HelmholtzStateBlockData):
Expand Down

0 comments on commit 12b4e25

Please sign in to comment.