Source code for idaes.core.initialization.general_hierarchical

#################################################################################
# The Institute for the Design of Advanced Energy Systems Integrated Platform
# Framework (IDAES IP) was produced under the DOE Institute for the
# Design of Advanced Energy Systems (IDAES).
#
# Copyright (c) 2018-2024 by the software owners: The Regents of the
# University of California, through Lawrence Berkeley National Laboratory,
# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon
# University, West Virginia University Research Corporation, et al.
# All rights reserved.  Please see the files COPYRIGHT.md and LICENSE.md
# for full copyright and license information.
#################################################################################
"""
Initializer class for implementing Hierarchical initialization routines for
IDAES models with standard forms (e.g. units with 1 control volume)
"""
from pyomo.environ import Block
from pyomo.common.config import ConfigValue, Bool

from idaes.core.initialization.initializer_base import ModularInitializerBase
from idaes.core.util import to_json, from_json, StoreSpec
import idaes.logger as idaeslog
from idaes.core.util.model_statistics import variables_in_activated_constraints_set

__author__ = "Andrew Lee"


[docs] class SingleControlVolumeUnitInitializer(ModularInitializerBase): """ This is a general purpose Initializer object for unit models which have a single Control Volume named 'control_volume'. For details of the initialization routine, please see the documentation. """ CONFIG = ModularInitializerBase.CONFIG() CONFIG.declare( "always_estimate_states", ConfigValue( default=False, domain=Bool, doc="Whether initialization routine should estimate values for " "state variables that already have values. Note that if set to True, this will " "overwrite any initial guesses provided.", ), ) def initialization_routine( self, model: Block, plugin_initializer_args: dict = None, copy_inlet_state: bool = False, ): """ Common initialization routine for models with one control volume. Args: model: Pyomo Block to be initialized plugin_initializer_args: dict-of-dicts containing arguments to be passed to plug-in Initializers. Keys should be submodel components. copy_inlet_state: bool (default=False). Whether to copy inlet state to other states or not (0-D control volumes only). Copying will generally be faster, but inlet states may not contain all properties required elsewhere. Returns: Pyomo solver results object """ # The default initialization_routine is sufficient return super().initialization_routine( model=model, plugin_initializer_args=plugin_initializer_args, copy_inlet_state=copy_inlet_state, ) def initialize_main_model(self, model: Block, copy_inlet_state: bool = False): """ Initialization routine for models with a single control volume. Args: model: current model being initialized copy_inlet_state: bool (default=False). Whether to copy inlet state to other states or not (0-D control volumes only). Copying will generally be faster, but inlet states may not contain all properties required elsewhere. Returns: Pyomo solver results object from solve of main model """ # Check to make sure model has something named "control_volume" if not hasattr(model, "control_volume"): raise TypeError( f"Model {model.name} does not appear to be a standard form unit model. " f"Please use an Initializer specific to the model being initialized." ) # Get logger _log = self.get_logger(model) self.initialize_control_volume(model.control_volume, copy_inlet_state) # Solve main model solve_log = idaeslog.getSolveLogger( model.name, self.get_output_level(), tag="unit" ) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: results = self._get_solver().solve(model, tee=slc.tee) _log.info_high(f"Control volume initialization {idaeslog.condition(results)}.") return results def initialize_control_volume( self, control_volume: Block, copy_inlet_state: bool = False ): """ Initialization routine for control volumes (supports both 0D and 1D control volumes). This routine initialized the state and reaction blocks associated with the control volume. Args: control_volume: control_volume to be initialized copy_inlet_state: bool (default=False). Whether to copy inlet state to other states or not (0-D control volumes only). Copying will generally be faster, but inlet states may not contain all properties required elsewhere. Returns: None """ # Get logger _log = self.get_logger(control_volume) # Initialize properties if hasattr(control_volume, "properties_in"): # 0-D control volume self._init_props_0D(control_volume, copy_inlet_state) else: # 1-D control volume self._init_props_1D(control_volume) _log.info_high("Control volume properties initialization complete") # Initialize reactions if they exist if hasattr(control_volume, "reactions"): self._init_rxns(control_volume) _log.info_high("Control volume reactions initialization complete") def _init_props_0D(self, control_volume, copy_inlet_state): # Initialize inlet properties - inlet state should already be fixed prop_init = self.get_submodel_initializer(control_volume.properties_in) if prop_init is not None: prop_init.initialize( model=control_volume.properties_in, output_level=self.get_output_level(), ) if not copy_inlet_state: # Just in case the user set a different initializer for the outlet prop_init = self.get_submodel_initializer(control_volume.properties_out) # Estimate missing values for outlet state control_volume.estimate_outlet_state( always_estimate=self.config.always_estimate_states ) if prop_init is not None: prop_init.initialize( model=control_volume.properties_out, output_level=self.get_output_level(), ) else: # Map solution from inlet properties to outlet properties state = to_json( control_volume.properties_in, wts=StoreSpec().value(), return_dict=True, ) from_json( control_volume.properties_out, sd=state, wts=StoreSpec().value(only_not_fixed=True), ) def _init_props_1D(self, control_volume): prop_init = self.get_submodel_initializer(control_volume.properties) # Estimate missing values for states control_volume.estimate_states( always_estimate=self.config.always_estimate_states ) if prop_init is not None: prop_init.initialize( control_volume.properties, output_level=self.get_output_level(), ) def _init_rxns(self, control_volume): rxn_init = self.get_submodel_initializer(control_volume.reactions) if rxn_init is not None: # Reaction blocks depend on vars from other blocks # Need to make sure these are all fixed before initializing fixed_vars = [] vset = variables_in_activated_constraints_set(control_volume.reactions) for v in vset: if v.parent_block().parent_component() is not control_volume.reactions: # Variable external to reactions if not v.fixed: v.fix() fixed_vars.append(v) rxn_init.initialize( control_volume.reactions, output_level=self.get_output_level(), ) # Unfix any vars we fixed previously for v in fixed_vars: v.unfix()