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 than HEOS. 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

logph diagram of NH3 with a simple heat pump cycle

Figure: logph diagram of NH3 with a simple heat pump cycle

Ts diagram of NH3 with a simple heat pump cycle

Figure: Ts diagram of NH3 with a simple heat pump cycle

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.

State change and respective ids of dictionary

component

state from

state to

id

components with one inlet and one outlet only

in1

out1

1

class HeatExchanger and subclasses

in1

out1

1

in2

out2

2

class ORCEvaporator

in1

out1

1

in2

out2

2

in3

out3

3

class Merge

in1

out1

1

in2

out1

2

class Drum

out1

out2

1

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.