Source code for watertap.unit_models.pressure_changer

#################################################################################
# WaterTAP Copyright (c) 2020-2024, The Regents of the University of California,
# through Lawrence Berkeley National Laboratory, Oak Ridge National Laboratory,
# National Renewable Energy Laboratory, and National Energy Technology
# Laboratory (subject to receipt of any required approvals from the U.S. Dept.
# of Energy). All rights reserved.
#
# Please see the files COPYRIGHT.md and LICENSE.md for full copyright and license
# information, respectively. These files are also available online at the URL
# "https://github.com/watertap-org/watertap/"
#################################################################################

from pyomo.common.config import ConfigValue, In
from pyomo.environ import Var, units as pyunits, Expr_if, value

from enum import Enum, auto

# Import IDAES cores
from idaes.models.unit_models.pressure_changer import PumpData
from idaes.core import declare_process_block_class
import idaes.core.util.scaling as iscale

import idaes.logger as idaeslog

from watertap.core import InitializationMixin
from watertap.costing.unit_models.pump import cost_pump
from watertap.costing.unit_models.energy_recovery_device import (
    cost_energy_recovery_device,
)

_log = idaeslog.getLogger(__name__)


[docs]class VariableEfficiency(Enum): none = auto() # default is constant efficiency flow = auto() # flow-only correlation flow_head = auto() # flow and head correlation
[docs]@declare_process_block_class("Pump") class PumpIsothermalData(InitializationMixin, PumpData): """ Standard Isothermal Pump Unit Model Class """ CONFIG = PumpData.CONFIG() CONFIG.declare( "variable_efficiency", ConfigValue( default=VariableEfficiency.none, domain=In(VariableEfficiency), description="Variable pump efficiency flag", doc="""Indicates the relationship used to define pump efficiency **VariableEfficiency.none** - uses default pump efficiency at BEP **VariableEfficiency.flow** - uses an efficiency correlation scaled to the BEP flow rate **VariableEfficiency.flow_head** - uses an efficiency correlation scaled to the BEP flow rate and head """, ), )
[docs] def build(self): super().build() # --------------------------------------- # Isothermal pump set up # --------------------------------------- if hasattr(self.control_volume, "enthalpy_balances"): self.control_volume.del_component(self.control_volume.enthalpy_balances) @self.control_volume.Constraint( self.flowsheet().config.time, doc="Isothermal constraint" ) def isothermal_balance(b, t): return b.properties_in[t].temperature == b.properties_out[t].temperature # --------------------------------------- # Variable efficiency pump set-up # --------------------------------------- if self.config.variable_efficiency is not VariableEfficiency.none: # create additional pyomo variables self.bep_flow = Var( initialize=1.0, doc="Best efficiency point flowrate of the centrifugal pump", units=pyunits.m**3 / pyunits.s, ) self.bep_eta = Var( initialize=0.8, doc="Best efficiency of the centrifugal pump", units=pyunits.dimensionless, ) self.flow_ratio = Var( self.flowsheet().time, initialize=1.0, doc="Ratio of pump flowrate to best efficiency point flowrate", units=pyunits.dimensionless, ) # add constraints @self.Constraint(self.flowsheet().time, doc="Pump flow ratio") def flow_ratio_constraint(b, t): return ( b.flow_ratio[t] * b.bep_flow == b.control_volume.properties_in[t].flow_vol ) if self.config.variable_efficiency is VariableEfficiency.flow: @self.Expression( self.flowsheet().time, doc="Expression for variable pump efficiency based on flow only", ) def eta_ratio(b, t): return Expr_if( b.flow_ratio[t] < 0.6, 0.4, Expr_if( b.flow_ratio[t] > 1.4, 0.4, -0.995 * b.flow_ratio[t] ** 2 + 1.977 * b.flow_ratio[t] + 0.018, ), ) elif self.config.variable_efficiency is VariableEfficiency.flow_head: raise NotImplementedError( "Config option 'VariableEfficiency.flow_head' is not fully implemented yet" ) # TODO - Implement pump efficiency expression based on flow and head (bep_head, head_ratio) else: pass if self.config.variable_efficiency is not VariableEfficiency.none: # replace the constant efficiency assumption using eta_ratio # must be done after the eta_ratio expression is created. @self.Constraint(self.flowsheet().time, doc="Actual pump efficiency") def eta_constraint(b, t): return b.efficiency_pump[t] == (b.bep_eta * b.eta_ratio[t])
def calculate_scaling_factors(self): super().calculate_scaling_factors() for ind, c in self.control_volume.isothermal_balance.items(): sf = iscale.get_scaling_factor( self.control_volume.properties_in[0].temperature ) iscale.constraint_scaling_transform(c, sf) if hasattr(self, "bep_flow"): if iscale.get_scaling_factor(self.bep_flow) is None: sf = value(self.bep_flow) ** -1 iscale.set_scaling_factor(self.bep_flow, sf) if hasattr(self, "bep_head"): if iscale.get_scaling_factor(self.bep_head) is None: sf = value(self.bep_head) ** -1 iscale.set_scaling_factor(self.bep_head, sf) if hasattr(self, "bep_eta"): if iscale.get_scaling_factor(self.bep_eta) is None: iscale.set_scaling_factor(self.bep_eta, 1) for t in self.flowsheet().time: if hasattr(self, "flow_ratio"): if iscale.get_scaling_factor(self.flow_ratio[t]) is None: iscale.set_scaling_factor(self.flow_ratio[t], 1) if hasattr(self, "efficiency_pump"): if iscale.get_scaling_factor(self.efficiency_pump[t]) is None: iscale.set_scaling_factor(self.efficiency_pump[t], 1) # scale constraints if hasattr(self, "flow_ratio_constraint"): if iscale.get_scaling_factor(self.flow_ratio_constraint[t]) is None: iscale.set_scaling_factor(self.flow_ratio_constraint[t], 1) if hasattr(self, "eta_constraint"): if iscale.get_scaling_factor(self.eta_constraint[t]) is None: iscale.set_scaling_factor(self.eta_constraint[t], 1) @property def default_costing_method(self): return cost_pump
[docs]@declare_process_block_class("EnergyRecoveryDevice") class EnergyRecoveryDeviceData(PumpIsothermalData): """ Turbine-type isothermal energy recovery device """ # switch compressor to False CONFIG = PumpIsothermalData.CONFIG() CONFIG.get("compressor")._default = False CONFIG.get("compressor")._domain = In([False]) CONFIG.compressor = False @property def default_costing_method(self): return cost_energy_recovery_device