Source code for tespy.components.turbomachinery.turbocompressor

# -*- coding: utf-8

"""Module of class TurboCompressor.


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/turbocompressor.py

SPDX-License-Identifier: MIT
"""

import numpy as np

from tespy.components.component import component_registry
from tespy.components.turbomachinery.compressor import Compressor
from tespy.tools.data_containers import ComponentCharacteristicMaps as dc_cm
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 isentropic


[docs] @component_registry class TurboCompressor(Compressor): r""" Class for a turbocompressor. **Mandatory Equations** - fluid: :py:meth:`tespy.components.component.Component.variable_equality_structure_matrix` - mass flow: :py:meth:`tespy.components.component.Component.variable_equality_structure_matrix` **Optional Equations** - :py:meth:`tespy.components.component.Component.dp_structure_matrix` - :py:meth:`tespy.components.component.Component.pr_structure_matrix` - :py:meth:`tespy.components.turbomachinery.base.Turbomachine.energy_balance_func` - :py:meth:`tespy.components.turbomachinery.compressor.Compressor.eta_s_func` - :py:meth:`tespy.components.turbomachinery.turbocompressor.TurboCompressor.char_map_eta_s_func` - :py:meth:`tespy.components.turbomachinery.turbocompressor.TurboCompressor.char_map_pr_func` Inlets/Outlets - in1 - out1 Optional inlets - power Image .. image:: /api/_images/Compressor.svg :alt: flowsheet of the compressor :align: center :class: only-light .. image:: /api/_images/Compressor_darkmode.svg :alt: flowsheet of the compressor :align: center :class: only-dark Parameters ---------- label : str The label of the component. design : list List containing design parameters (stated as String). offdesign : list List containing offdesign parameters (stated as String). design_path : str Path to the components design case. local_offdesign : boolean Treat this component in offdesign mode in a design calculation. local_design : boolean Treat this component in design mode in an offdesign calculation. char_warnings : boolean Ignore warnings on default characteristics usage for this component. printout : boolean Include this component in the network's results printout. P : float, dict Power, :math:`P/\text{W}` eta_s : float, dict Isentropic efficiency, :math:`\eta_s/1` pr : float, dict Outlet to inlet pressure ratio, :math:`pr/1` dp : float, dict Inlet to outlet pressure difference, :math:`dp/\text{p}_\text{unit}` Is specified in the Network's pressure unit char_map_pr : tespy.tools.characteristics.CharMap, dict Characteristic map for pressure ratio vs. nondimensional mass flow. char_map_eta_s : tespy.tools.characteristics.CharMap, dict Characteristic map for isentropic efficiency vs. nondimensional mass flow. igva : float, dict, :code:`"var"` Inlet guide vane angle, :math:`igva/^\circ`. Example ------- Create an air compressor model and calculate the power required for compression of 50 l/s of ambient air to 5 bars. Using a generic compressor map how does the efficiency change in different operation mode (e.g. 90 % of nominal volumetric flow)? >>> from tespy.components import Sink, Source, TurboCompressor >>> from tespy.connections import Connection >>> from tespy.networks import Network >>> import os >>> nw = Network(iterinfo=False) >>> nw.units.set_defaults(**{ ... "pressure": "bar", "temperature": "degC", "volumetric_flow": "l/s", ... "enthalpy": "kJ/kg" ... }) >>> si = Sink('sink') >>> so = Source('source') >>> comp = TurboCompressor('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 characteristic map is selected instead of the isentropic efficiency. For offdesign, the inlet guide vane angle should be variable in order to maintain the same pressure ratio at a different volumetric flow. >>> comp.set_attr( ... pr=5, eta_s=0.8, design=['eta_s'], ... offdesign=['char_map_pr', 'char_map_eta_s'] ... ) >>> inc.set_attr(fluid={'air': 1}, p=1, T=20, v=50) >>> nw.solve('design') >>> nw.save('tmp.json') >>> round(comp.P.val, 0) 12772.0 >>> round(comp.eta_s.val, 2) 0.8 >>> inc.set_attr(v=45) >>> comp.set_attr(igva='var') >>> nw.solve('offdesign', design_path='tmp.json') >>> round(comp.eta_s.val, 2) 0.77 >>> round(comp.igva.val, 2) 8.88 Or, we can fix the inlet guide vane angle and under the given pressure ratio the volumetric flow is a result. Note, that the problem can be very sensitive to changes of :code:`igva`. >>> comp.set_attr(igva=10) >>> inc.set_attr(v=None) >>> nw.solve('offdesign', design_path='tmp.json') >>> nw.assert_convergence() >>> round(inc.v.val, 2) 44.31 >>> os.remove('tmp.json') """ def _preprocess(self, row_idx): # skip the FutureWarning of the Compressor class return super(Compressor, self)._preprocess(row_idx)
[docs] def get_parameters(self): parameters = super().get_parameters() parameters.update({ 'igva': dc_cp( min_val=-90, max_val=90, val=0, quantity="angle", description="inlet guide vane angle", _potential_var=True ), 'char_map_eta_s': dc_cm( description="2D lookup table for efficiency over non-dimensional mass flow and speed line" ), 'char_map_eta_s_group': dc_gcp( elements=['char_map_eta_s', 'igva'], num_eq_sets=1, func=self.char_map_eta_s_func, dependents=self.char_map_dependents, description="map for isentropic efficiency over speedlines and non-dimensional mass flow" ), 'char_map_pr': dc_cm( description="2D lookup table for pressure ratio over non-dimensional mass flow and speed line" ), 'char_map_pr_group': dc_gcp( elements=['char_map_pr', 'igva'], num_eq_sets=1, func=self.char_map_pr_func, dependents=self.char_map_dependents, description="map for pressure ratio over speedlines and non-dimensional mass flow" ) }) del parameters["eta_s_char"] return parameters
[docs] def char_map_pr_func(self): r""" Calculate pressure ratio from characteristic map. Returns ------- residual : float Residual value of equations. Note ---- - X: speedline index (rotational speed is constant) - Y: nondimensional mass flow - igva: variable inlet guide vane angle for value manipulation according to :cite:`GasTurb2018`. .. math:: X = \sqrt{\frac{T_\mathrm{in,design}}{T_\mathrm{in}}}\\ Y = \frac{\dot{m}_\mathrm{in} \cdot p_\mathrm{in,design}} {\dot{m}_\mathrm{in,design} \cdot p_\mathrm{in} \cdot X}\\ \vec{Y} = f\left(X,Y\right)\cdot\left(1-\frac{igva}{100}\right)\\ \vec{Z} = f\left(X,Y\right)\cdot\left(1-\frac{igva}{100}\right)\\ 0 = \frac{p_{out} \cdot p_{in,design}} {p_\mathrm{in} \cdot p_\mathrm{out,design}}- f\left(Y,\vec{Y},\vec{Z}\right) """ i = self.inl[0] o = self.outl[0] beta = np.sqrt(i.T.design / i.calc_T()) y = (i.m.val_SI * i.p.design) / (i.m.design * i.p.val_SI * beta) yarr, zarr = self.char_map_pr.char_func.evaluate_x(beta) # value manipulation with igva yarr *= (1 - self.igva.val_SI / 100) zarr *= (1 - self.igva.val_SI / 100) pr = self.char_map_pr.char_func.evaluate_y(y, yarr, zarr) return (o.p.val_SI / i.p.val_SI) - pr * self.pr.design
[docs] def char_map_eta_s_func(self): r""" Calculate isentropic efficiency from characteristic map. Returns ------- residual : float Residual value of equation. Note ---- - X: speedline index (rotational speed is constant) - Y: nondimensional mass flow - igva: variable inlet guide vane angle for value manipulation according to :cite:`GasTurb2018`. .. math:: X = \sqrt{\frac{T_\mathrm{in,design}}{T_\mathrm{in}}}\\ Y = \frac{\dot{m}_\mathrm{in} \cdot p_\mathrm{in,design}} {\dot{m}_\mathrm{in,design} \cdot p_\mathrm{in} \cdot X}\\ \vec{Y} = f\left(X,Y\right)\cdot\left(1-\frac{igva}{100}\right)\\ \vec{Z}=f\left(X,Y\right)\cdot\left(1-\frac{igva^2}{10000}\right)\\ 0 = \frac{\eta_\mathrm{s}}{\eta_\mathrm{s,design}} - f\left(Y,\vec{Y},\vec{Z}\right) """ i = self.inl[0] o = self.outl[0] x = np.sqrt(i.T.design / i.calc_T()) y = (i.m.val_SI * i.p.design) / (i.m.design * i.p.val_SI * x) yarr, zarr = self.char_map_eta_s.char_func.evaluate_x(x) # value manipulation with igva yarr *= (1 - self.igva.val_SI / 100) zarr *= (1 - self.igva.val_SI ** 2 / 10000) eta = self.char_map_eta_s.char_func.evaluate_y(y, yarr, zarr) 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 ) - i.h.val_SI) / (o.h.val_SI - i.h.val_SI) - eta * self.eta_s.design )
[docs] def char_map_dependents(self): return [ self.inl[0].m, self.inl[0].p, self.inl[0].h, self.outl[0].p, self.outl[0].h, self.igva ]
[docs] def check_parameter_bounds(self): r"""Check parameter value limits.""" _no_limit_violations = super().check_parameter_bounds() for data in [self.char_map_pr, self.char_map_eta_s]: if data.is_set: x = np.sqrt(self.inl[0].T.design / self.inl[0].T.val_SI) y = ( (self.inl[0].m.val_SI * self.inl[0].p.design) / (self.inl[0].m.design * self.inl[0].p.val_SI * x) ) yarr = data.char_func.get_domain_errors_x(x, self.label) yarr *= (1 - self.igva.val_SI / 100) data.char_func.get_domain_errors_y(y, yarr, self.label) return _no_limit_violations