5 minutes into TESPy#

General information#

TESPy is an open-source framework for the simulation of component based thermodynamic conversion processes. With the software you can use predefined components such as a pump, compressor, heat exchanger, turbine or valve (and many more) to build thermodynamic systems. The system is set up by connecting the components in a generic way and then specifying respective process and component parameters.

The software then performs a steady state simulation of your system by creating and solving a system of equations in the so-called equation oriented (EO) approach (see [6] for more information). The system represents the individual topology and component and process specifications provided by you. TESPy accomplishes this by solving for

  • mass flow,

  • pressure and

  • enthalpy

of every connection between two components[1]. After solving a model, missing component and process parameters - for example: efficiencies, temperatures, pressure losses - are determined based on this information. The EO approach lets the modeler choose, which parameters are inputs and which parameters are results: For instance, a compressor efficiency can be an input and the system variables are solved to meet that constraint, or it can be a result of other inputs.

Mini example#

TESPy consists of three main building blocks:

  • class Network as container of the simulation

  • class Component (children of them: Compressor, Valve, …), in which thermodynamic conversion processes take place

  • class Connection, which define the topology of the Network by connecting the individual components

In this example, we will create two simple networks to show the principle of TESPy. For further tutorials and examples we recommend looking into the online documentation.

Modeling a Compressor#

First, we are modeling a compressor, which compresses fully saturated steam to a higher pressure level. The Fig. 1 shows the abstract representation of the component. The table below summarizes the process parameters we are going to apply in our example.

../_images/Compressor.svg

Fig. 1 Compressor model.#

parameter description

model location

model parameter

value

unit

saturated gas state

in

x

100

%

temperature

T

10

°C

mass flow

m

0.1

kg/s

efficiency

compressor

eta_s

80

%

pressure ratio

pr

3

-

from tespy.networks import Network
from tespy.components import Source, Sink, Compressor
from tespy.connections import Connection


nwk = Network(p_unit="bar", T_unit="C")

so = Source("source")
cp = Compressor("compressor")
si = Sink("sink")

c1 = Connection(so, "out1", cp, "in1", label="1")
c2 = Connection(cp, "out1", si, "in1", label="2")

nwk.add_conns(c1, c2)

To make a simulation it is now necessary to specify relevant component and process parameters. We start with the values as provided in Table 1.

c1.set_attr(fluid={"R290": 1}, T=10, x=1, m=0.1)

cp.set_attr(eta_s=0.8, pr=3)

nwk.solve("design")
 iter  | residual   | progress   | massflow   | pressure   | enthalpy   | fluid      | component  
-------+------------+------------+------------+------------+------------+------------+------------
 1     | 1.86e+06   | 0 %        | 0.00e+00   | 1.81e+06   | 3.88e+05   | 0.00e+00   | 0.00e+00   
 2     | 5.91e+05   | 2 %        | 0.00e+00   | 2.33e-10   | 7.39e+05   | 0.00e+00   | 0.00e+00   
 3     | 2.33e-10   | 100 %      | 0.00e+00   | 2.33e-10   | 6.94e-12   | 0.00e+00   | 0.00e+00   
 4     | 2.33e-10   | 100 %      | 0.00e+00   | 2.33e-10   | 6.94e-12   | 0.00e+00   | 0.00e+00   
Total iterations: 4, Calculation time: 0.00 s, Iterations per second: 919.00

We can have a look at the results. An overview is provided by the print_results method of the Network.

nwk.print_results()
##### RESULTS (Compressor) #####
+------------+----------+----------+----------+--------+
|            |        P |    eta_s |       pr |   igva |
|------------+----------+----------+----------+--------|
| compressor | 6.37e+03 | 8.00e-01 | 3.00e+00 |    nan |
+------------+----------+----------+----------+--------+
##### RESULTS (Connection) #####
+----+-----------+-----------+-----------+-----------+
|    |         m |         p |         h |         T |
|----+-----------+-----------+-----------+-----------|
|  1 | 1.000e-01 | 6.366e+00 | 5.857e+05 | 1.000e+01 |
|  2 | 1.000e-01 | 1.910e+01 | 6.494e+05 | 6.476e+01 |
+----+-----------+-----------+-----------+-----------+

Since TESPy is working with an equation oriented solver, we can now change things up. For example, instead of providing the efficiency of the compressor, we could provide an outlet temperature. Given that temperature, the efficiency of the compressor will be a result of the calculation.

Note

With the equation oriented structure the user is not constraint in the inputs. As long as the network is well determined, the solver be able to find a result. One downside of the equation oriented approach is that the a initial guess for all variables is required. Bad starting values often lead to the solver being unable to find a solution. For more information please have a look at the TESPy documentation. Here in detail information and best practices are provided for this topic.

cp.set_attr(eta_s=None)  # unset the isentropic efficiency
c2.set_attr(T=70)
nwk.set_attr(iterinfo=False)
nwk.solve("design")
nwk.print_results()
##### RESULTS (Compressor) #####
+------------+----------+----------+----------+--------+
|            |        P |    eta_s |       pr |   igva |
|------------+----------+----------+----------+--------|
| compressor | 7.64e+03 | 6.67e-01 | 3.00e+00 |    nan |
+------------+----------+----------+----------+--------+
##### RESULTS (Connection) #####
+----+-----------+-----------+-----------+-----------+
|    |         m |         p |         h |         T |
|----+-----------+-----------+-----------+-----------|
|  1 | 1.000e-01 | 6.366e+00 | 5.857e+05 | 1.000e+01 |
|  2 | 1.000e-01 | 1.910e+01 | 6.621e+05 | 7.000e+01 |
+----+-----------+-----------+-----------+-----------+

For example, we can make an invalid parameter specification by setting the mass flow at the inlet and at the outlet of the compressor. This overdetermines the system of equations and will result in an error when trying to solve.

c2.set_attr(T=None, m=0.1)
nwk.solve("design")
---------------------------------------------------------------------------
TESPyNetworkError                         Traceback (most recent call last)
Cell In[5], line 2
      1 c2.set_attr(T=None, m=0.1)
----> 2 nwk.solve("design")

File /opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/tespy/networks/network.py:1962, in Network.solve(self, mode, init_path, design_path, max_iter, min_iter, init_only, init_previous, use_cuda, print_results, prepare_fast_lane)
   1954 msg = (
   1955     "Network information:\n"
   1956     f" - Number of components: {len(self.comps)}\n"
   1957     f" - Number of connections: {len(self.conns)}\n"
   1958     f" - Number of busses: {len(self.busses)}"
   1959 )
   1960 logger.debug(msg)
-> 1962 self.initialise()
   1964 if init_only:
   1965     self._reset_topology_reduction_specifications()

File /opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/tespy/networks/network.py:877, in Network.initialise(self)
    875     self.create_fluid_wrapper_branches()
    876 self.propagate_fluid_wrappers()
--> 877 self.presolve_massflow_topology()
    878 self.presolve_fluid_topology()
    880 self.init_set_properties()

File /opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/tespy/networks/network.py:1015, in Network.presolve_massflow_topology(self)
   1008 elif num_massflow_specs > 1:
   1009     msg = (
   1010         "You cannot specify two or more values for mass flow in "
   1011         "the same linear branch (starting at "
   1012         f"{branch['components'][0].label} and ending at "
   1013         f"{branch['components'][-1].label})."
   1014     )
-> 1015     raise hlp.TESPyNetworkError(msg)
   1017 else:
   1018     main_conn = branch["connections"][0]

TESPyNetworkError: You cannot specify two or more values for mass flow in the same linear branch (starting at source and ending at sink).

Modeling a Heat Exchanger#

In the second example we are going to model a heat exchanger as shown in Fig. 2, specifically an evaporator using heat from ambient air to evaporate the working fluid R290 (Propane). The parameter we want to provide are listed in the table below.

../_images/HeatExchanger.svg

Fig. 2 Evaporator model.#

Similar to the compressor example we work with a Network instance, this time two fluids are required, i.e. air and R290. We create the HeatExchanger component and connect and parametrize it according to the flowsheet and the data listed in the table.

from tespy.networks import Network
from tespy.components import Source, Sink, HeatExchanger
from tespy.connections import Connection
from CoolProp.CoolProp import PropsSI as PSI


nwk = Network(p_unit="bar", T_unit="C", iterinfo=False)

so_wf = Source("working fluid source")
si_wf = Sink("working fluid sink")
so_air = Source("air source")
si_air = Sink("air sink")
eva = HeatExchanger("evaporator")

c1 = Connection(so_air, "out1", eva, "in1", label="1")
c2 = Connection(eva, "out1", si_air, "in1", label="2")
c3 = Connection(so_wf, "out1", eva, "in2", label="3")
c4 = Connection(eva, "out2", si_wf, "in1", label="4")

nwk.add_conns(c1, c2, c3, c4)

c1.set_attr(fluid={"Air": 1}, T=7, p=1, m=1)
c2.set_attr(T=4)
c3.set_attr(fluid={"R290": 1}, T=0, x=0.25)
# specification of a pressure guess value for convergence improvement
c4.set_attr(x=1, p0=5)

eva.set_attr(pr1=1, pr2=1)

nwk.solve("design")
nwk.print_results()
##### RESULTS (HeatExchanger) #####
+------------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
|            |         Q |       kA |   td_log |    ttd_u |    ttd_l |      pr1 |      pr2 |    zeta1 |    zeta2 |
|------------+-----------+----------+----------+----------+----------+----------+----------+----------+----------|
| evaporator | -3.02e+03 | 5.63e+02 | 5.36e+00 | 7.00e+00 | 4.00e+00 | 1.00e+00 | 1.00e+00 | 0.00e+00 | 0.00e+00 |
+------------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
##### RESULTS (Connection) #####
+----+-----------+-----------+-----------+-----------+
|    |         m |         p |         h |         T |
|----+-----------+-----------+-----------+-----------|
|  1 | 1.000e+00 | 1.000e+00 | 4.063e+05 | 7.000e+00 |
|  2 | 1.000e+00 | 1.000e+00 | 4.033e+05 | 4.000e+00 |
|  3 | 1.073e-02 | 4.745e+00 | 2.937e+05 | 2.451e-10 |
|  4 | 1.073e-02 | 4.745e+00 | 5.749e+05 | 2.451e-10 |
+----+-----------+-----------+-----------+-----------+

After running the simulation we can see the heat transferred from the air to the working fluid, both mass flows, or the temperature differences between the hot side (air) and the cold side (R290). Instead of providing a fixed temperature value for the evaporation temperature level, we can provide a temperature difference to the air temperature level. When the air temperature changes, adjusts the evaporation pressure/temperature of the working fluid automatically.

eva.set_attr(ttd_l=5)
c3.set_attr(T=None)
nwk.solve("design")
c3.T.val
-0.9999999999998295
T_evaporation = []
for T_air in [-5, 0, 5, 10]:
    c1.set_attr(T=T_air)
    c2.set_attr(T=T_air - 3)
    nwk.solve("design")
    T_evaporation += [round(c3.T.val, 1)]

T_evaporation
[-13.0, -8.0, -3.0, 2.0]

Learn more#

TESPy relies on CoolProp to provide fluid property data for a large range of different fluids [7]. The online documentation of TESPy provides a large variety of examples and tutorials to learn to use the software as well as extensive background information and code documentation: