Source code for idaes.models.gibbs_reactor

##############################################################################
# Institute for the Design of Advanced Energy Systems Process Systems
# Engineering Framework (IDAES PSE Framework) Copyright (c) 2018, 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.txt and LICENSE.txt for full copyright and
# license information, respectively. Both files are also available online
# at the URL "https://github.com/IDAES/idaes".
##############################################################################
"""
Standard IDAES Gibbs reactor model.
"""
from __future__ import division

# Import Python libraries
import logging

# Import Pyomo libraries
from pyomo.environ import log, Reals,  Var
from pyomo.common.config import ConfigValue, In

# Import IDAES cores
from idaes.core import UnitBlockData, declare_process_block_class, \
                        Holdup0D, CONFIG_Base
from idaes.core.util.config import is_parameter_block, list_of_strings
from idaes.core.util.misc import add_object_ref

__author__ = "Jinliang Ma, Andrew Lee"


# Set up logger
logger = logging.getLogger('idaes.unit_model')


[docs]@declare_process_block_class("GibbsReactor") class GibbsReactorData(UnitBlockData): """ Standard Gibbs Reactor Unit Model Class This model assume all possible reactions reach equilibrium such that the system partial molar Gibbs free energy is minimized. Since some species mole flow rate might be very small, the natural log of the species molar flow rate is used. Instead of specifying the system Gibbs free energy as an objective function, the equations for zero partial derivatives of the grand function with Lagrangian multiple terms with repect to product species mole flow rates and the multiples are specified as constraints. """ CONFIG = CONFIG_Base() # TODO: Does a dynamic equilibrium reactor make sense at all? # Set dynamic flag, as model is only steady-state CONFIG.dynamic = False CONFIG.get('dynamic')._default = False CONFIG.get('dynamic')._domain = In([False]) # Set default values of inherited attributes CONFIG.get("material_balance_type")._default = 'element_total' CONFIG.get("has_equilibrium_reactions")._default = True CONFIG.get("has_phase_equilibrium")._default = True CONFIG.get("has_heat_transfer")._default = True # Add unit model attributes CONFIG.declare("property_package", ConfigValue( default=None, domain=is_parameter_block, description="Property package to use for holdup", doc="""Property parameter object used to define property calculations (default = 'use_parent_value') - 'use_parent_value' - get package from parent (default = None) - a ParameterBlock object""")) CONFIG.declare("property_package_args", ConfigValue( default={}, description="Arguments to use for constructing property packages", doc="""A dict of arguments to be passed to the PropertyBlockData and used when constructing these (default = 'use_parent_value') - 'use_parent_value' - get package from parent (default = None) - a dict (see property package for documentation)""")) CONFIG.declare("inlet_list", ConfigValue( domain=list_of_strings, description="List of inlet names", doc="""A list containing names of inlets (default = None) - None - default single inlet - list - a list of names for inlets""")) CONFIG.declare("num_inlets", ConfigValue( domain=int, description="Number of inlets to unit", doc="""Argument indication number (int) of inlets to construct (default = None). Not used if inlet_list arg is provided. - None - use inlet_list arg instead - int - Inlets will be named with sequential numbers from 1 to num_inlets""")) CONFIG.declare("outlet_list", ConfigValue( domain=list_of_strings, description="List of outlet names", doc="""A list containing names of outlets (default = None) - None - default single outlet - list - a list of names for outlets""")) CONFIG.declare("num_outlets", ConfigValue( domain=int, description="Number of outlets to unit", doc="""Argument indication number (int) of outlets to construct (default = None). Not used if outlet_list arg is provided. - None - use outlet_list arg instead - int - Outlets will be named with sequential numbers from 1 to num_outlets"""))
[docs] def build(self): """ Begin building model (pre-DAE transformation). Args: None Returns: None """ # Call UnitModel.build to setup dynamics super(GibbsReactorData, self).build() # Build Holdup Block self.holdup = Holdup0D() # Set Unit Geometry and holdup Volume self._set_geometry() # Construct performance equations self._make_performance()
[docs] def post_transform_build(self): """ Continue model construction after DAE transformation. Args: None Returns: None """ # Construct Inlets self.build_inlets(inlets=self.config.inlet_list, num_inlets=self.config.num_inlets) # Build Outlets self.build_outlets(outlets=self.config.outlet_list, num_outlets=self.config.num_outlets)
def _set_geometry(self): """ Define the geometry of the unit as necessary, and link to holdup volume Args: None Returns: None """ # For this case, just create a reference to holdup volume if self.config.include_holdup is True: add_object_ref(self, "volume", self.holdup.volume) def _make_performance(self): """ Define constraints which describe the behaviour of the unit model. Args: None Returns: None """ add_object_ref(self, "component_list", self.holdup.component_list) add_object_ref(self, "element_list", self.holdup.element_list) # Add Lagrangian multiplier variables self.lagrange_mult = Var(self.time, self.element_list, domain=Reals, initialize=100, doc="Lagrangian multipliers") # TODO: Need to add support for multiple phases # Use Lagrangian multiple method to derive equations for Out_Fi # Use RT*lagrange as the Lagrangian multiple such that lagrange is in # a similar order of magnitude as log(Yi) @self.Constraint(self.time, self.component_list, doc="Gibbs energy minimisation constraint") def gibbs_minimization(b, t, j): # Use natural log of species mole flow to avoid Pyomo solver # warnings of reaching infeasible point return 0 == (b.holdup.properties_out[t].gibbs_pc[j] + b.holdup.properties_out[t].gas_const * b.holdup.properties_out[t].temperature * (log(b.holdup.properties_out[t].mole_frac[j]) + sum(b.lagrange_mult[t, e] * b.holdup.properties_out[t].element_comp[j][e] for e in b.element_list))) # Set references to balance terms at unit level if (self.config.has_heat_transfer is True and self.config.energy_balance_type != 'none'): add_object_ref(self, "heat", self.holdup.heat)