Fluid properties¶
The default fluid property engine CoolProp. All available fluids can be found on their homepage. Also see [8]. Since version 0.7 of TESPy it is possible to use other engines. TESPy comes with two additional predefined engines, i.e.
For each fluid you can specify, which library should be used, and you can easily implement your own engine, for example, if your fluid is not available through the predefined engines, or you need a different implementation of the fluid properties of a specific fluid. Further below on this page we have added an example implementation of the KKH polynomial formulation to illustrate, how you can integrate your own engine into TESPy.
CoolProp¶
CoolProp is the default fluid property back end of TESPy. It provides different back ends for different applications and allows calling mixture functions.
Available back ends¶
CoolProp provides multiple back ends for fluid property calculation. The back ends vary in calculation speed and calculation accuracy. It is possible to choose from the following back ends:
HEOS
: Helmhotz Equation Of State with highest accuracy and lowest calculation speed. This is the default back end!REFPROP
: Highest accuracy and highest convergence stability. This back end is not free, a separate REFPROP license is required.BICUBIC
: Tabular back end with high accuracy and very high calculation speed.TTSE
: Tabular back end with lowest accuracy and very high calculation speed.INCOMP
: Back end for incompressible fluids.IF97
: Back end for the IAPWS-IF97 of water, very accurate and much higher calculation speed thanHEOS
. Due to a bug in the CoolProp back end this option is instable, for more information see the CoolProp issue #1918.
For more information on the back ends please visit the CoolProp online documentation.
Pure and pseudo-pure fluids¶
CoolProp covers the most important fluids such as water, air as a pseudo-pure fluid as well as its components, several fuels and refrigerants etc.. Look for the aliases in the list of fluids. All fluids provided in this list cover liquid and gaseous state and the two-phase region.
Incompressible fluids¶
If you are looking for heat transfer fluids, the list of incompressible fluids might be interesting for you. In contrast to the pure fluids, the properties cover liquid state only.
Fluid mixtures¶
TESPy provides support for three types of mixtures:
ideal: Mixtures for gases only.
ideal-cond: Mixture for gases with condensation calculation for water share.
incompressible: Mixtures for incompressible fluids.
Furthermore, CoolProp provides a back end for predefined mixtures, which is rather instable using HEOS. Using the CoolProp mixture back-end is not tested, reach out if you would like to support us in adopting the TESPy implementation. In general, to use the mixture feature of CoolProp we recommend using the REFPROP back end instead of HEOS.
Using other engines¶
To use any of the other fluid property engines, you can do the following, e.g. to use the iapws back end:
>>> from tespy.components import Sink
>>> from tespy.components import Source
>>> from tespy.components import Turbine
>>> from tespy.connections import Connection
>>> from tespy.networks import Network
>>> from tespy.tools.fluid_properties.wrappers import IAPWSWrapper
>>> nwk = Network(iterinfo=False)
>>> so = Source("Source")
>>> tu = Turbine("Turbine")
>>> si = Sink("Sink")
>>> c1 = Connection(so, "out1", tu, "in1", label="1")
>>> c2 = Connection(tu, "out1", si, "in1", label="2")
>>> nwk.add_conns(c1, c2)
>>> tu.set_attr(eta_s=0.9)
>>> c1.set_attr(
... v=1, p=1e5, T=500,
... fluid={"IF97::H2O": 1}, fluid_engines={"H2O": IAPWSWrapper}
... )
>>> c2.set_attr(p=1e4)
>>> nwk.solve("design")
>>> float(round(c2.x.val, 3))
0.99
>>> tu.set_attr(eta_s=None)
>>> c2.set_attr(x=1)
>>> nwk.solve("design")
>>> float(round(tu.eta_s.val, 3))
0.841
Implementing a custom engine¶
The fluid property calls to different engines have to be masqueraded with
respective wrappers. The implementation of the wrappers for CoolProp,
iapws and pyromat can be found in the
fluid_properties.wrappers
module, and serve as example implementations for your own wrappers:
The wrapper for your own engine (or an engine from a different library) has to
inherit from the
FluidPropertyWrapper
class. Below we will use the polynomial formulation for gaseous water from
[9] as an example. First we import the necessary dependencies.
>>> import numpy as np
>>> from tespy.tools.fluid_properties.wrappers import FluidPropertyWrapper
>>> from tespy.tools.global_vars import gas_constants
Then we set up a new class and implement the methods to calculate enthalpy and entropy from (pressure and) temperature. The structure and names of the functions have to match the pattern from the FluidPropertyWrapper, in this case h_pT. On top of that, we add a backwards function T_ph and a function to analytically calculate the heat capacity _cp_pT, the derivative of the enthalpy to the temperature. Lastly, to make the calculation of isentropic efficiencies possible, we can add the equation for change in enthalpy on isentropic change of pressure for an ideal gas.
# coefficients H+ S+ a b c d M
>>> COEF = {
... "H2O": [-253.871, -11.750, 34.376, 7.841, -0.423, 0.0, 18.0152],
... }
>>> class KKHWrapper(FluidPropertyWrapper):
...
... def __init__(self, fluid, back_end=None, reference_temperature=298.15) -> None:
... super().__init__(fluid, back_end)
...
... if self.fluid not in COEF:
... msg = "Fluid not available in KKH database"
... raise KeyError(msg)
...
... self.coefficients = COEF[fluid]
... self.h_ref = self._h_pT(None, reference_temperature)
... self._molar_mass = self.coefficients[-1] * 1e-3
... self._T_min = 100
... self._T_max = 2000
... self._p_min = 1000
... self._p_max = 10000000
...
... def cp_pT(self, p, T):
... y = T * 1e-3
... return 1e3 * (
... self.coefficients[2]
... + self.coefficients[3] * y
... + self.coefficients[4] / (y ** 2)
... + self.coefficients[5] * y ** 2
... ) / self.coefficients[6]
...
... def h_pT(self, p, T):
... return self._h_pT(p, T) - self.h_ref
...
... def _h_pT(self, p, T):
... y = T * 1e-3
... return 1e6 * (
... self.coefficients[0]
... + self.coefficients[2] * y
... + self.coefficients[3] / 2 * y ** 2
... - self.coefficients[4] / y
... + self.coefficients[5] / 3 * y ** 3
... ) / self.coefficients[6]
...
... def T_ph(self, p, h):
... return newton(self.h_pT, self.cp_pT, h, p)
...
... def isentropic(self, p_1, h_1, p_2):
... T_1 = self.T_ph(p_1, h_1)
... cp = self.cp_pT(p_1, T_1)
... kappa = cp / (cp - gas_constants["uni"] / self._molar_mass)
... T_2 = T_1 * (p_2 / p_1) ** ((kappa - 1) / kappa)
... return self.h_pT(p_2, T_2)
We can add a newton for the backwards function:
>>> def newton(func, deriv, h, p):
... # default valaues
... T = 300
... valmin = 70
... valmax = 3000
... max_iter = 10
... tol_rel = 1e-6
...
... # start newton loop
... expr = True
... i = 0
... while expr:
... # calculate function residual and new value
... res = h - func(p, T)
... T += res / deriv(p, T)
...
... # check for value ranges
... if T < valmin:
... T = valmin
... if T > valmax:
... T = valmax
... i += 1
...
... if i > max_iter:
... break
...
... expr = abs(res / h) >= tol_rel
...
... return T
And then we can test a call to the interface and check our results, also compare them to CoolProp.
>>> kkh_water = KKHWrapper("H2O", reference_temperature=298.15) # same as in CoolProp
>>> h = kkh_water.h_pT(1e5, 400)
>>> T = kkh_water.T_ph(1e5, h)
>>> round(T, 1)
400.0
>>> round(h)
189769
>>> from tespy.tools.fluid_properties import CoolPropWrapper
>>> coolprop_water = CoolPropWrapper("H2O")
>>> h_cp = coolprop_water.h_pT(1e5, 400)
>>> T_cp = coolprop_water.T_ph(1e5, h_cp)
>>> round(T_cp, 1)
400.0
>>> round(h)
189769
To use this wrapper in a simple TESPy model, we can then proceed as we have in the previous section:
>>> from tespy.components import Sink
>>> from tespy.components import Source
>>> from tespy.components import Turbine
>>> from tespy.connections import Connection
>>> from tespy.networks import Network
>>> nwk = Network(T_unit="C", p_unit="MPa", iterinfo=False)
>>> so = Source("Source")
>>> tu = Turbine("Turbine")
>>> si = Sink("Sink")
>>> c1 = Connection(so, "out1", tu, "in1", label="1")
>>> c2 = Connection(tu, "out1", si, "in1", label="2")
>>> nwk.add_conns(c1, c2)
>>> c1.set_attr(
... m=1, p=10, T=600,
... fluid={"H2O": 1}, fluid_engines={"H2O": KKHWrapper}
... )
>>> c2.set_attr(p=1, T=400)
>>> nwk.solve("design")
>>> tu.set_attr(eta_s=0.9)
>>> c2.set_attr(T=None)
>>> nwk.solve("design")
>>> round(c2.T.val, 1)
306.3
Mixture routines in TESPy¶
Different types of mixture routines are implemented in TESPy. You can select, which routine should be applied in each separated subnetwork of your system by specifying a mixing rule. ideal-cond is the default mixing rule. The following mixing rules are available at the moment:
ideal-cond: gaseous fluids with flash calculations for water.
ideal: gaseous fluids without flash calculations.
incompressible: mass based mixtures of individual incompressible fluids.
The mixtures are calculated by using the pure fluid properties from the selected
fluid property engines and combining them through corresponding equations. The
equations are documented in the
fluid_properties.mixtures
module.
Note
Similarly to the custom fluid property engine, you can implement your own mixture routines. If you are interested in doing so, you can get in contact via the user meeting or the GitHub discussion forum.
Creating Fluid Property Diagrams¶
CoolProp has an inbuilt feature for creating fluid property diagrams.
Unfortunately, the handling is not very easy at the moment. We recommend using
fluprodia (Fluid Property Diagram) instead. You can create and customize
different types of diagrams for all pure and pseudo-pure fluids available in
CoolProp. In order to plot your process data into a diagram, you can use the
get_plotting_data
method of each component. The method returns a
dictionary, that can be passed as **kwargs
to the
calc_individual_isoline
method of a fluprodia
FluidPropertyDiagram
object. The fluprodia documentation provides
examples of how to plot a process into different diagrams, too. For more
information on fluprodia have a look at the
online documentation. You can
install the package with pip.
pip install fluprodia
Note
The plotting data a returned from the get_plotting_data
as a
nested dictionary. The first level key contains the connection id of the
state change (change state from incoming connection to outgoing
connection). The table below shows the state change and the respective id.
component |
state from |
state to |
id |
---|---|---|---|
components with one inlet and one outlet only |
|
|
|
class HeatExchanger and subclasses |
|
|
|
|
|
|
|
class ORCEvaporator |
|
|
|
|
|
|
|
|
|
|
|
class Merge |
|
|
|
|
|
|
|
… |
… |
… |
|
class Drum |
|
|
|
All other components do not return any information as either there is no change in state or the state change is accompanied by a change in fluid composition.