Source code for tespy.components.turbomachinery.compressor

# -*- coding: utf-8

"""Module of class Compressor.


This file is part of project TESPy (github.com/oemof/tespy). It's copyrighted
by the contributors recorded in the version control history of the file,
available from its original location
tespy/components/turbomachinery/compressor.py

SPDX-License-Identifier: MIT
"""

import warnings

import numpy as np

from tespy.components.component import component_registry
from tespy.components.turbomachinery.base import Turbomachine
from tespy.tools import logger
from tespy.tools.data_containers import ComponentCharacteristicMaps as dc_cm
from tespy.tools.data_containers import ComponentCharacteristics as dc_cc
from tespy.tools.data_containers import ComponentMandatoryConstraints as dc_cmc
from tespy.tools.data_containers import ComponentProperties as dc_cp
from tespy.tools.data_containers import GroupedComponentProperties as dc_gcp
from tespy.tools.fluid_properties import h_mix_pQ
from tespy.tools.fluid_properties import h_mix_pT
from tespy.tools.fluid_properties import isentropic
from tespy.tools.fluid_properties import single_fluid
from tespy.tools.helpers import _get_dependents


[docs] @component_registry class Compressor(Turbomachine): r""" Class for a basic compressor. .. image:: /api/_images/components/Compressor.svg :alt: flowsheet of the compressor :align: center :class: only-light .. image:: /api/_images/components/Compressor_darkmode.svg :alt: flowsheet of the compressor :align: center :class: only-dark Ports ----- - Fluid inlets: in1 - Fluid outlets: out1 - Power inlets: power Mandatory Equations ------------------- - mass flow equality constraint(s): :py:meth:`variable_equality_structure_matrix <tespy.components.component.Component.variable_equality_structure_matrix>` - fluid composition equality constraint(s): :py:meth:`variable_equality_structure_matrix <tespy.components.component.Component.variable_equality_structure_matrix>` When a power or heat connector is attached: - energy_connector_balance: :py:meth:`energy_connector_balance_func <tespy.components.turbomachinery.compressor.Compressor.energy_connector_balance_func>` Parameters ---------- char_warnings : bool Ignore warnings on default characteristics usage for this component. design : list List containing design parameters (stated as String). design_path : str Path to the components design case. dp : float, dict Inlet to outlet absolute pressure change. Quantity: :code:`pressure_difference`. Equation: :py:meth:`dp_structure_matrix <tespy.components.component.Component.dp_structure_matrix>`. eta_s : float, dict Isentropic efficiency. Quantity: :code:`efficiency`. Equation: :py:meth:`eta_s_func <tespy.components.turbomachinery.compressor.Compressor.eta_s_func>`. eta_s_char : tespy.tools.characteristics.CharLine, dict Isentropic efficiency lookup table for offdesign. Equation: :py:meth:`eta_s_char_func <tespy.components.turbomachinery.compressor.Compressor.eta_s_char_func>`. igva : float, dict, :code:`"var"` Inlet guide vane angle. Quantity: :code:`angle`. Can be set as a system variable by passing :code:`"var"` as its value. label : str The label of the component. local_design : bool Treat this component in design mode in an offdesign calculation. local_offdesign : bool Treat this component in offdesign mode in a design calculation. offdesign : list List containing offdesign parameters (stated as String). P : float, dict Power input/output of the component. Quantity: :code:`power`. Equation: :py:meth:`energy_balance_func <tespy.components.turbomachinery.base.Turbomachine.energy_balance_func>`. pr : float, dict Outlet to inlet pressure ratio. Quantity: :code:`ratio`. Equation: :py:meth:`pr_structure_matrix <tespy.components.component.Component.pr_structure_matrix>`. printout : bool Include this component in the network's results printout. Example ------- Create an air compressor model and calculate the power required for compression of 50 l/s of ambient air to 5 bars. >>> from tespy.components import Sink, Source, Compressor >>> from tespy.connections import Connection >>> from tespy.networks import Network >>> nw = Network(iterinfo=False) >>> nw.units.set_defaults(**{ ... "pressure": "bar", "pressure_difference": "bar", ... "temperature": "degC", "volumetric_flow": "l/s", "enthalpy": "kJ/kg" ... }) >>> si = Sink('sink') >>> so = Source('source') >>> comp = Compressor('compressor') >>> inc = Connection(so, 'out1', comp, 'in1') >>> outg = Connection(comp, 'out1', si, 'in1') >>> nw.add_conns(inc, outg) Specify the compressor parameters: nominal efficiency and pressure ratio. For offdesign mode the efficiency characteristic line is selected instead of the isentropic efficiency. >>> comp.set_attr(pr=5, eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char']) >>> inc.set_attr(fluid={'air': 1}, p=1, T=20, v=50) >>> nw.solve('design') >>> design_state = nw.save(as_dict=True) >>> round(comp.P.val, 0) 12772.0 >>> round(comp.eta_s.val, 2) 0.8 >>> inc.set_attr(v=45) >>> nw.solve('offdesign', design_path=design_state) >>> round(comp.eta_s.val, 2) 0.79 """ _initial_pr_guess = 3.0 _initial_dh_fallback = 5e4
[docs] def initial_state(self, port): if port in ('in1', 'out1'): return {"phase": "gas"} return None
[docs] @staticmethod def powerinlets(): return ["power"]
[docs] def get_mandatory_constraints(self): constraints = super().get_mandatory_constraints() if len(self.power_inl) > 0: constraints["energy_connector_balance"] = dc_cmc(**{ "func": self.energy_connector_balance_func, "dependents": self.energy_connector_dependents, "num_eq_sets": 1 }) return constraints
[docs] def get_parameters(self): parameters = super().get_parameters() parameters["P"].min_val = 0 parameters["pr"].min_val = 1 parameters["dp"].max_val = 0 parameters.update({ 'eta_s': dc_cp( min_val=0, max_val=1, num_eq_sets=1, func=self.eta_s_func, deriv=self.eta_s_deriv, dependents=self.eta_s_dependents, quantity="efficiency", description="isentropic efficiency", calc=self._calc_eta_s ), 'eta_s_char': dc_cc( param='m', num_eq_sets=1, func=self.eta_s_char_func, dependents=self.eta_s_char_dependents, description="isentropic efficiency lookup table for offdesign" ), 'igva': dc_cp( min_val=-90, max_val=90, val=0, quantity="angle", description="inlet guide vane angle", _allows_var=True ), }) return parameters
[docs] def energy_connector_balance_func(self): r""" (optional) energy balance equation connecting the power connector to the component's power Returns ------- residual : float Residual value of equation .. math:: 0=\dot E - \dot{m}_{in}\cdot\left(h_{out}-h_{in}\right) """ return self.power_inl[0].E.val_SI - self.inl[0].m.val_SI * ( self.outl[0].h.val_SI - self.inl[0].h.val_SI )
[docs] def energy_connector_dependents(self): return [ self.power_inl[0].E, self.inl[0].m, self.outl[0].h, self.inl[0].h ]
[docs] def eta_s_func(self): r""" Equation for given isentropic efficiency of a compressor. Returns ------- residual : float Residual value of equation. .. math:: 0 = -\left( h_{out} - h_{in} \right) \cdot \eta_{s} + \left( h_{out,s} - h_{in} \right) """ i = self.inl[0] o = self.outl[0] return ( (o.h.val_SI - i.h.val_SI) * self.eta_s.val_SI - ( isentropic( i.p.val_SI, i.h.val_SI, o.p.val_SI, i.fluid_data, i.mixing_rule, T0=i.T.val_SI, T0_out=o.T.val_SI ) - self.inl[0].h.val_SI ) )
[docs] def eta_s_deriv(self, increment_filter, k, dependents=None): r""" Partial derivatives for isentropic efficiency. Parameters ---------- increment_filter : ndarray Matrix for filtering non-changing variables. k : int Position of derivatives in Jacobian matrix (k-th equation). """ dependents = dependents["scalars"][0] i = self.inl[0] o = self.outl[0] f = self.eta_s_func if o.h.is_var and not i.h.is_var: self._partial_derivative(o.h, k, self.eta_s.val_SI, increment_filter) # remove o.h from the dependents dependents = dependents.difference(_get_dependents([o.h])[0]) for dependent in dependents: self._partial_derivative(dependent, k, f, increment_filter)
[docs] def eta_s_dependents(self): return [ self.inl[0].p, self.inl[0].h, self.outl[0].p, self.outl[0].h, ]
[docs] def eta_s_char_func(self): r""" Equation for given isentropic efficiency characteristic. Returns ------- residual : float Residual value of equation. .. math:: 0 = \left(h_{out}-h_{in}\right) \cdot \eta_{s,design} \cdot f\left( expr \right) -\left( h_{out,s} - h_{in} \right) """ p = self.eta_s_char.param expr = self.get_char_expr(p, **self.eta_s_char.char_params) if not expr: msg = ( 'Please choose a valid parameter, you want to link the ' f'isentropic efficiency to at component {self.label}.' ) logger.error(msg) raise ValueError(msg) i = self.inl[0] o = self.outl[0] return ( (o.h.val_SI - i.h.val_SI) * self.eta_s.design * self.eta_s_char.char_func.evaluate(expr) - ( isentropic( i.p.val_SI, i.h.val_SI, o.p.val_SI, i.fluid_data, i.mixing_rule, T0=i.T.val_SI, T0_out=o.T.val_SI ) - i.h.val_SI ) )
[docs] def eta_s_char_dependents(self): return [ self.inl[0].m, self.inl[0].p, self.inl[0].h, self.outl[0].p, self.outl[0].h, ]
def _isentropic_equation_is_set(self): return self.eta_s.is_set or self.eta_s_char.is_set
[docs] def convergence_check(self): r""" Perform a convergence check. Note ---- Manipulate enthalpies/pressure at inlet and outlet if not specified by user to match physically feasible constraints. """ i, o = self.inl[0], self.outl[0] if o.p.is_var and o.p.val_SI < i.p.val_SI: o.p.set_reference_val_SI(i.p.val_SI * 1.5) if o.h.is_var and o.h.val_SI < i.h.val_SI: o.h.set_reference_val_SI(i.h.val_SI + 100e3) if i.p.is_var and o.p.val_SI < i.p.val_SI: i.p.set_reference_val_SI(o.p.val_SI * 2 / 3) i.p.val_SI = o.p.val_SI * 0.9 if i.h.is_var and o.h.val_SI < i.h.val_SI: i.h.set_reference_val_SI(o.h.val_SI - 100e3)
[docs] def initialise_source(self, c, key): r""" Return a generic pressure starting value at the outlet. Only consulted as the last fallback tier when neither user values, propagation nor the temperature field anchors covered the pressure. Enthalpy starting values are generated by the automatic starting value machinery instead. Parameters ---------- c : tespy.connections.connection.Connection Connection to perform initialisation on. key : str Fluid property to retrieve. Returns ------- val : float Starting value for pressure in SI units, 0 for no information. """ if key == 'p': fluid = single_fluid(c.fluid_data) if fluid is not None: return c.fluid.wrapper[fluid]._p_crit / 2 else: return 10e5 return 0
[docs] @staticmethod def initialise_target(c, key): r""" Return a generic pressure starting value at the inlet. Only consulted as the last fallback tier when neither user values, propagation nor the temperature field anchors covered the pressure. Enthalpy starting values are generated by the automatic starting value machinery instead. Parameters ---------- c : tespy.connections.connection.Connection Connection to perform initialisation on. key : str Fluid property to retrieve. Returns ------- val : float Starting value for pressure in SI units, 0 for no information. """ if key == 'p': fluid = single_fluid(c.fluid_data) if fluid is not None: return c.fluid.wrapper[fluid]._p_crit / 3 else: return 1e5 return 0
def _calc_eta_s(self): i, o = self.inl[0], self.outl[0] return ( isentropic( i.p.val_SI, i.h.val_SI, o.p.val_SI, i.fluid_data, i.mixing_rule, T0=i.T.val_SI, T0_out=o.T.val_SI ) - i.h.val_SI ) / (o.h.val_SI - i.h.val_SI)