Networks¶
The network class handles preprocessing, solving and post-processing. We will walk you through all the important steps.
Setup¶
Network container¶
The TESPy network contains all data of your plant, which in terms of the calculation is represented by a nonlinear system of equations. The system variables of your TESPy network are a subset of the following:
mass flow,
pressure,
enthalpy and
the mass fractions of the fluids of every
Connectioninstance as well asenergy flow (power or heat) for
PowerConnectioninstances andcomponent variables (e.g. compressor rpm) if declared a variable.
The solver will simplify the variable space in a presolving step and then solve
for the remaining variables. In the following, you will find information on the
Network setup, solving and debugging.
Unit specifications¶
There are a couple of ways to impose boundary conditions with the respective units to your models. The unit can be specified through two different ways:
as default units per quantity (e.g. temperature, temperature difference, power, …) for all properties of the components and connections, or
as individual units of a specific parameter.
Tip
TESPy implements pint in the back-end to handle the units. If you
want to learn more on how to work with pint units, check out the
respective documentation.
The example below shows, how you can setup units for your Network.
>>> from tespy.networks import Network
>>> nw = Network()
Default units are SI units. We can check, what unit is the default unit like this:
>>> nw.units.get_default("temperature")
'kelvin'
>>> nw.units.get_default("power")
'W'
>>> nw.units.get_default("specific_volume")
'm3/kg'
We can change the default units as follows:
>>> nw.units.set_defaults(temperature="degC") # celsius
>>> nw.units.get_default("temperature")
'degC'
>>> nw.units.set_defaults(power="hp") # horse power
>>> nw.units.get_default("power")
'hp'
>>> nw.units.set_defaults(efficiency="%") # percent
>>> nw.units.set_defaults(pressure="bar", pressure_difference="bar")
The unit specification then applies to all parameters with the same quantity. For example, let’s set up a model of a compressor.
>>> from tespy.components import Compressor, Source, Sink
>>> from tespy.connections import Connection
>>> source = Source("gas inflow")
>>> compressor = Compressor("compressor")
>>> sink = Sink("gas discharge")
>>> c1 = Connection(source, "out1", compressor, "in1", label="c1")
>>> c2 = Connection(compressor, "out1", sink, "in1", label="c2")
>>> nw.add_conns(c1, c2)
Now we can parametrize our problem. It will utilize the unit specifications we created above.
>>> c1.set_attr(fluid={"air": 1}, m=1, p=1, T=25) # p in bar, T in celsius
>>> c2.set_attr(p=3)
>>> compressor.set_attr(eta_s=80) # efficiency in %
We can disable the iteration printouts by setting iterinfo=False
>>> nw.iterinfo = False
>>> nw.solve("design")
Now we can check results, e.g. the power of the compressor, which is expected to be in horse power:
>>> round(compressor.P.val, 0)
185.0
We can also retrieve the value with the respective unit, and then use pint to transform it into what ever unit we need:
>>> round(compressor.P.val_with_unit, 0)
<Quantity(185.0, 'horsepower')>
>>> round(compressor.P.val_with_unit.to("kW"), 0)
<Quantity(138.0, 'kilowatt')>
Alternatively, we can specify an individual unit using the Quantity
class of pint. For that you have to utilize the UnitRegistry of
your Network.units: ureg.
>>> ureg = nw.units.ureg
>>> c1.set_attr(m=ureg.Quantity(1, "t/h"))
>>> c1.m.val_with_unit
<Quantity(1, 'metric_ton / hour')>
Caution
If you now modify this number, it will remove the individual unit! On top,
by specifying a bare number, you will always remove the unit information.
This information is only reconnected once, the Network is
initialized in context of a simulation!
>>> c1.set_attr(m=5)
>>> c1.m.val_with_unit
5
>>> nw.solve("design")
>>> c1.m.val_with_unit
<Quantity(5, 'kilogram / second')>
To understand, what quantity is associated with a specific parameter, you can do the following:
>>> compressor.dp.quantity
'pressure_difference'
>>> c1.td_dew.quantity
'temperature_difference'
It is also possible to use your own UnitRegistry:
>>> from pint import UnitRegistry
>>> ureg = UnitRegistry()
>>> nw.units.set_ureg(ureg)
Changing the ureg will only have effect on future specifications. Existing quantities are not changed.
Attention
The quantities pressure and pressure_difference as well as
temperature and temperature_difference need to be set
individually!
Printouts and logging¶
TESPy comes with an inbuilt logger. If you want to keep track of debugging-messages, general information, warnings or errors you should enable the logger. At the beginning of your python script e.g. add the following lines:
>>> from tespy.tools import logger
>>> import logging
>>> ();logger.define_logging(
... logpath="myloggings", log_the_path=True, log_the_version=True,
... screen_level=logging.ERROR, file_level=logging.DEBUG
... );() # +doctest: ELIPSIS
(...)
The log-file will be saved to ~/.tespy/log_files/ by default. All
available options are documented in the
API.
Prior to solving the network there are options regarding the console printouts for the calculation progress. Specify, if you want to enable or disable convergence progress printouts:
>>> nw.iterinfo
False
# enable iteration information printout
>>> nw.iterinfo = True
>>> nw.iterinfo
True
# disable iteration information printout
>>> nw.iterinfo = False
Adding connections¶
As seen in the introduction, you will have to create your networks from the components and the connections between them. You can add connections directly or via subsystems using the corresponding methods:
>>> nw.add_conns()
>>> nw.add_subsystems()
Note
You do not need to add the components to the network, as they are inherited via the added connections. After having set up your network and added all required elements, you can start the calculation.
There are two types of connections, you can learn about them more in these sections.
Start calculation¶
You can start the solution process with the following line:
nw.solve(mode='design')
This starts the initialisation of your network and proceeds to its calculation. The specification of the calculation mode is mandatory, This is the list of available keywords:
modeis the calculation mode ('design'-calculation or'offdesign'-calculation).init_pathis the path to a saved network state (json file path or adictreturned bynw.save(as_dict=True)) to use for initialisation.design_pathis the path to a saved network state (json file path or adictreturned bynw.save(as_dict=True)) which holds the information of your plant’s design point.max_iteris the maximum amount of iterations performed by the solver.min_iteris the minimum amount of iterations before a solution can be accepted (given the convergence criterion is satisfied).init_onlystop after initialisation (True/False).init_previoususe starting values from previous simulation (True/False).use_cudause cuda instead of numpy for matrix inversion, speeds up simulation in some cases by outsourcing calculation to graphics card. For more information please visit the cupy documentation.
There are two calculation modes available ('design' and
'offdesign'), which are explained in the subsections below. If you
choose offdesign as calculation mode the specification of a
design_path is mandatory.
The usage of an initialisation path is always optional but highly recommended,
as the convergence of the solution process will be improved, if you provide
good starting values. If you do not specify an init_path, the
initialisation from saved results will be skipped.
init_only=True usually is used for debugging. Or, you could use this
feature to export a not solved network, if you want to do the parametrisation
in .csv-files rather than your python script.
The init_previous parameter can be used in design and offdesign
calculations and works very similar to specifying an init_path.
In contrast, starting values are taken from the previous calculation. Specifying
the init_path overwrites init_previous.
Design mode¶
The design mode is used to design your system and is always the first calculation of your plant. The offdesign calculation is always based on a design calculation! Obviously as you are designing the plant the way you want, you are flexible to choose the parameters to specify. However, you can not specify parameters that are based on a design case, as for example the isentropic efficiency characteristic function of a turbine or a pump. Specifying a value for the efficiency is of course possible.
Offdesign mode¶
The offdesign mode is used to calculate the performance of your plant, if
parameters deviate from the plant’s design point. This can be partload
operation, operation at different temperature or pressure levels etc.. Thus,
before starting an offdesign calculation you have to design your plant first.
By stating 'offdesign' as calculation mode, components and
connections will switch to the offdesign mode. This means that all
parameters provided as design parameters will be unset and all parameters
provided as offdesign parameters will be set instead. You can specify a
connection’s or component’s (off-)design parameters using the
set_attr method.
For example, for a condenser you would usually design it to a maximum terminal temperature difference, in offdesign the heat transfer coefficient is selected. The heat transfer coefficient is calculated in the preprocessing of the offdesign case based on the results from the design-case. Of course, this applies to all other parameters in the same way. Also, the pressure drop is a result of the geometry for the offdesign case, thus we swap the pressure ratios with geometry independent zeta \(\frac{\zeta}{D^4}\) values.
mycomponent.set_attr(
design=['ttd_u', 'pr1', 'pr2'], offdesign=['UA', 'zeta1_d4', 'zeta2_d4']
)
Note
Some parameters come with characteristic functions based on the design case properties. This means, that e.g. the isentropic efficiency of a turbine is calculated as function of the actual mass flow to design mass flow ratio. You can provide your own (measured) data or use the already existing data from TESPy. All standard characteristic functions are available at tespy.data module.
For connections it works in the same way, e.g. write
myconnection.set_attr(design=['h'], offdesign=['T'])
if you want to replace the enthalpy with the temperature for your offdesign. The temperature is a result of the design calculation and that value is then used for the offdesign calculation in this example.
To solve your offdesign calculation, use:
nw.solve(mode='offdesign', design_path='path/to/designpoint.json')
Component-level design references¶
In some situations a single component - or a small group of components - has
been redesigned or replaced, so its individual design point differs from the
one captured in the network-wide design_path. TESPy supports two
complementary mechanisms to handle this.
Individual design path in an offdesign simulation
You can assign a separate design_path directly to any component.
During the offdesign preprocessing TESPy will then load that component’s
design values (e.g. the heat-transfer coefficient UA of a heat
exchanger or the isentropic efficiency of a compressor) from the component’s
own path, while every other component keeps using the global
design_path. The design values of all connections adjacent to that
component are also read from the component’s individual path and stored
internally; they serve as the reference for characteristic functions that
scale with mass-flow or similar.
# Heat exchanger was replaced; new design case is in a separate file.
heatex.set_attr(design_path='path/to/new_heatex_design.json')
nw.solve(mode='offdesign', design_path='path/to/network_design.json')
Setting design_path=None on a component reverts it to the global
reference on the next solve.
Note
It is possible to use an individual design from an isolated sub-network that contains only the component of interest. Connection labels or even component labels in that file do not need to match the labels in the main network: if no label match is found TESPy tries to identify the correct entry via the port topology (inlet/outlet IDs) stored in the file. This topology information is only available in design files exported with TESPy 0.9.15 or higher. Older exports require matching labels.
Local offdesign component in a design simulation
It is also possible to keep most of the network in design mode, while one
component that has already been built should be treated in offdesign mode.
Setting local_offdesign=True together with a design_path on
that component achieves this. The component’s offdesign equations become active
for that solve, and its design values and adjacent-connection references are
loaded from its own design_path.
# Existing heat exchanger is frozen in offdesign; rest of network is sized.
heatex.set_attr(
local_offdesign=True,
design_path='path/to/existing_heatex_design.json'
)
nw.solve(mode='design')
Solving and debugging¶
Everything about the solution process - how the variable space is reduced, how starting values are generated, how the equation system is decomposed into blocks and solved, how to interact with a running solution process and how to debug non-converging models - is described in the solver section.