Source code for idaes.unit_models.power_generation.valve_steam

##############################################################################
# Institute for the Design of Advanced Energy Systems Process Systems
# Engineering Framework (IDAES PSE Framework) Copyright (c) 2018-2019, 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-pse".
##############################################################################
"""
This provides valve models for steam and liquid water.  These are for
steam cycle control valves and the turbine throttle valves.
"""

__Author__ = "John Eslick"

from enum import Enum

from pyomo.common.config import ConfigBlock, ConfigValue, In, ConfigList
from pyomo.environ import (Var, Expression, SolverFactory, value,
                           Constraint, sqrt, Param)
from pyomo.opt import TerminationCondition

from idaes.core import declare_process_block_class
from idaes.unit_models.pressure_changer import (
    PressureChangerData,
    ThermodynamicAssumption,
    MaterialBalanceType,
)
from idaes.core.util import from_json, to_json, StoreSpec
from idaes.core.util.model_statistics import degrees_of_freedom
from idaes.logger import getIdaesLogger, getInitLogger, init_tee, condition

_log = getIdaesLogger(__name__)


[docs]class ValveFunctionType(Enum): linear = 1 quick_opening = 2 equal_percentage = 3 custom = 4
def _define_config(config): config.compressor = False config.get('compressor')._default = False config.get('compressor')._domain = In([False]) config.material_balance_type = MaterialBalanceType.componentTotal config.get('material_balance_type')._default = \ MaterialBalanceType.componentTotal config.thermodynamic_assumption = ThermodynamicAssumption.adiabatic config.get('thermodynamic_assumption')._default = \ ThermodynamicAssumption.adiabatic config.get('thermodynamic_assumption')._domain = \ In([ThermodynamicAssumption.adiabatic]) config.declare("valve_function", ConfigValue( default=ValveFunctionType.linear, domain=In(ValveFunctionType), description="Valve function type, if custom provide an expression rule", doc="""The type of valve function, if custom provide an expression rule with the valve_function_rule argument. **default** - ValveFunctionType.linear **Valid values** - { ValveFunctionType.linear, ValveFunctionType.quick_opening, ValveFunctionType.equal_percentage, ValveFunctionType.custom}""")) config.declare("valve_function_rule", ConfigValue( default=None, description="This is a rule that returns a time indexed valve function expression.", doc="""This is a rule that returns a time indexed valve function expression. This is required only if valve_function==ValveFunctionType.custom""")) config.declare("phase", ConfigValue( default="Vap", domain=In(("Vap", "Liq")), description='Expected phase of fluid in valve in {"Liq", "Vap"}')) def _linear_rule(b, t): return b.valve_opening[t] def _quick_open_rule(b, t): return sqrt(b.valve_opening[t]) def _equal_percentage_rule(b, t): return b.alpha**(b.valve_opening[t] - 1) def _liquid_pressure_flow_rule(b, t): """ For liquid F = Cv*sqrt(Pi**2 - Po**2)*f(x) """ Po = b.control_volume.properties_out[t].pressure Pi = b.control_volume.properties_in[t].pressure F = b.control_volume.properties_in[t].flow_mol Cv = b.Cv fun = b.valve_function[t] return (1/b.flow_scale**2)*F**2 == (1/b.flow_scale**2)*Cv**2*(Pi - Po)*fun**2 def _vapor_pressure_flow_rule(b, t): """ For vapor F = Cv*sqrt(Pi**2 - Po**2)*f(x) """ Po = b.control_volume.properties_out[t].pressure Pi = b.control_volume.properties_in[t].pressure F = b.control_volume.properties_in[t].flow_mol Cv = b.Cv fun = b.valve_function[t] return (1/b.flow_scale**2)*F**2 == \ (1/b.flow_scale**2)*Cv**2*(Pi**2 - Po**2)*fun**2
[docs]@declare_process_block_class("SteamValve", doc="Basic steam valve models") class SteamValveData(PressureChangerData): # Same settings as the default pressure changer, but force to expander with # isentropic efficiency CONFIG = PressureChangerData.CONFIG() _define_config(CONFIG)
[docs] def build(self): super().build() self.valve_opening = Var(self.flowsheet().config.time, initialize=1, doc="Fraction open for valve from 0 to 1") self.Cv = Var(initialize=0.1, doc="Valve flow coefficent, for vapor " "[mol/s/Pa] for liquid [mol/s/Pa^0.5]") self.flow_scale = Param(mutable=True, default=1e3, doc= "Scaling factor for pressure flow relation should be approximatly" " the same order of magnitude as the expected flow.") self.Cv.fix() self.valve_opening.fix() # set up the valve function rule. I'm not sure these matter too much # for us, but the options are easy enough to provide. if self.config.valve_function == ValveFunctionType.linear: rule = _linear_rule elif self.config.valve_function == ValveFunctionType.quick_opening: rule = _quick_open_rule elif self.config.valve_function == ValveFunctionType.equal_percentage: self.alpha = Var(initialize=1, doc="Valve function parameter") self.alpha.fix() rule = equal_percentage_rule else: rule = self.config.valve_function_rule self.valve_function = Expression(self.flowsheet().config.time, rule=rule, doc="Valve function expression") if self.config.phase == "Liq": rule = _liquid_pressure_flow_rule else: rule = _vapor_pressure_flow_rule self.pressure_flow_equation = Constraint(self.flowsheet().config.time, rule=rule)
[docs] def initialize(self, state_args={}, outlvl=6, solver='ipopt', optarg={'tol': 1e-6, 'max_iter':30}): """ Initialize the turbine stage model. This deactivates the specialized constraints, then does the isentropic turbine initialization, then reactivates the constraints and solves. Args: state_args (dict): Initial state for property initialization outlvl : sets output level of initialization routine * 0 = Use default idaes.init logger setting * 1 = Maximum output * 2 = Include solver output * 3 = Return solver state for each step in subroutines * 4 = Return solver state for each step in routine * 5 = Final initialization status and exceptions * 6 = No output solver (str): Solver to use for initialization optarg (dict): Solver arguments dictionary """ # sp is what to save to make sure state after init is same as the start # saves value, fixed, and active state, doesn't load originally free # values, this makes sure original problem spec is same but initializes # the values of free vars sp = StoreSpec.value_isfixed_isactive(only_fixed=True) istate = to_json(self, return_dict=True, wts=sp) self.deltaP[:].unfix() self.ratioP[:].unfix() # fix inlet and free outlet for t in self.flowsheet().config.time: for k, v in self.inlet.vars.items(): v[t].fix() for k, v in self.outlet.vars.items(): v[t].unfix() # to calculate outlet pressure Pout = self.outlet.pressure[t] Pin = self.inlet.pressure[t] if self.deltaP[t].value is not None: prdp = value((self.deltaP[t] - Pin)/Pin) else: prdp = -100 # crazy number to say don't use deltaP as guess if value(Pout/Pin) > 1 or value(Pout/Pin) < 0.0: if value(self.ratioP[t]) <= 1 and value(self.ratioP[t]) >= 0: Pout.value = value(Pin*self.ratioP[t]) elif prdp <= 1 and prdp >= 0: Pout.value = value(prdp*Pin) else: Pout.value = value(Pin*0.95) self.deltaP[t] = value(Pout - Pin) self.ratioP[t] = value(Pout/Pin) # Make sure the initialization problem has no degrees of freedom # This shouldn't happen here unless there is a bug in this dof = degrees_of_freedom(self) try: assert(dof == 0) except: _log.exception("degrees_of_freedom = {}".format(dof)) raise # one bad thing about reusing this is that the log messages aren't # really compatible with being nested inside another initialization super().initialize(state_args=state_args, outlvl=outlvl, solver=solver, optarg=optarg) # reload original spec from_json(self, sd=istate, wts=sp)
def _get_performance_contents(self, time_point=0): pc = super()._get_performance_contents(time_point=time_point) pc["vars"]["Opening"] = self.valve_opening[time_point] pc["vars"]["Valve Coefficient"] = self.Cv if self.config.valve_function == ValveFunctionType.equal_percentage: pc["vars"]["alpha"] = self.alpha pc["params"] = {} pc["params"]["Flow Scaling"] = self.flow_scale return pc