Debug your models efficiently¶
This tutorial gives insights on how you can debug tespy models. The user interface of the current implementation might still need some refinement, so you are invited to raise issues in the github repository. We will change it based on the feedback. The outputs shown here are based on the following version of tespy:
from tespy import __version__
__version__
'0.10.0.dev0'
Simple model debugging¶
This tutorial will show a couple of things
How to extract the variables of the problem
before presolving step
after presolving step and identify the presolved variables
How to extract the applied equations of the problem
before presolving step
after presolving step and identify the presolved equations
How to read and fix the errors that are raised during presolving
How to debug a model in case of linear dependency by inspecting the error message, incidence matrix and Jacobian
How to interpret/deal with a couple of warnings/errors that might pop up during postprocessing
Model overview¶
The model we implement is a very simple heat pump model, just as implemented in the introductory Heat Pump tutorial.
Model code¶
from tespy.components import CycleCloser, SimpleHeatExchanger, Compressor, Valve, Motor, PowerSource
from tespy.connections import Connection, PowerConnection
from tespy.networks import Network
nw = Network()
nw.units.set_defaults(
temperature="°C",
pressure="bar",
pressure_difference="bar",
power="kW",
heat="kW",
enthalpy="kJ/kg"
)
grid = PowerSource("grid")
motor = Motor("motor")
cc = CycleCloser("cycle closer")
valve = Valve("valve")
evaporator = SimpleHeatExchanger("evaporator")
compressor = Compressor("compressor")
condenser = SimpleHeatExchanger("condenser")
c1 = Connection(cc, "out1", evaporator, "in1", label="c1")
c2 = Connection(evaporator, "out1", compressor, "in1", label="c2")
c3 = Connection(compressor, "out1", condenser, "in1", label="c3")
c4 = Connection(condenser, "out1", valve, "in1", label="c4")
c0 = Connection(valve, "out1", cc, "in1", label="c0")
nw.add_conns(c1, c2, c3, c4, c0)
e1 = PowerConnection(grid, "power", motor, "power_in", label="e1")
e2 = PowerConnection(motor, "power_out", compressor, "power", label="e2")
nw.add_conns(e1, e2)
Debug the model¶
Variable and equation identification¶
With nothing specified and trying to solve we will get an information, that the network is lacking fluid information.
nw.solve("design", init_only=True)
---------------------------------------------------------------------------
TESPyNetworkError Traceback (most recent call last)
Cell In[5], line 1
----> 1 nw.solve("design", init_only=True)
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:2674, in Network.solve(self, mode, init_path, design_path, max_iter, min_iter, init_only, init_previous, use_cuda, print_results, robust_relax, skip_postprocess)
2667 msg = (
2668 "Network information:\n"
2669 f" - Number of components: {len(self.comps)}\n"
2670 f" - Number of connections: {len(self.conns)}\n"
2671 )
2672 logger.debug(msg)
-> 2674 self._prepare_problem()
2676 if init_only:
2677 return
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:891, in Network._prepare_problem(self)
888 self._create_fluid_wrapper_branches()
889 continue
--> 891 self._propagate_fluid_wrappers()
892 self._init_connection_result_datastructure()
894 self._prepare_solve_mode()
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:977, in Network._propagate_fluid_wrappers(self)
971 if num_potential_fluids == 0:
972 msg = (
973 "The following connections of your network are missing any "
974 "kind of fluid composition information:"
975 f"{', '.join([c.label for c in all_connections])}."
976 )
--> 977 raise hlp.TESPyNetworkError(msg)
979 for c in all_connections:
980 c.mixing_rule = mixing_rule
TESPyNetworkError: The following connections of your network are missing any kind of fluid composition information:c1, c2, c3, c4, c0.
Then let’s specify the fluid and try to preprocess the network. With
solve_determination we can check, how many parameters are specified and
how many are missing. When running with init_only no error is raised,
so we can use that starting point in an interactive python environment to get
started with debugging.
c1.set_attr(fluid={"R290": 1})
nw.solve("design", init_only=True)
# this would be executed as next step for actual solve call
nw.solve_determination()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[6], line 4
1 c1.set_attr(fluid={"R290": 1})
2 nw.solve("design", init_only=True)
3 # this would be executed as next step for actual solve call
----> 4 nw.solve_determination()
AttributeError: 'Network' object has no attribute 'solve_determination'
Now we can check the following information:
Which are the original variables of our model
Which of those variables have already been determined by the presolving
Which ones are the actual variables, that the model has to solve and which original variables of the model these variables represent
The original variables:
nw.print_variables_before_presolve()
Variables before presolving (22 total):
Object Property
-------- ----------
c0 m
c0 p
c0 h
c0 fluid
c1 m
c1 p
c1 h
c1 fluid
c2 m
c2 p
c2 h
c2 fluid
c3 m
c3 p
c3 h
c3 fluid
c4 m
c4 p
c4 h
c4 fluid
e1 E
e2 E
The variables solved already by the presolving step:
nw.print_presolved_variables()
Presolved variables (5 total):
Object Property
-------- ----------
c0 fluid
c1 fluid
c2 fluid
c3 fluid
c4 fluid
The actual variables of the problem as a dictionary:
keys: tuple with variable number as first element, variable type as second element (mass flow, pressure, enthalpy, fluid, …)
values: list of the original variables this variable represents. The list again contains tuples with
the label of the component/connection from which the variable originated
the type of variable as second element
nw.print_variables()
Variables after presolving (10 total):
# Property Represents
--- ---------- --------------------------------------
0 p c2 (p)
1 h c2 (h)
2 p c3 (p)
3 h c3 (h)
4 p c4 (p)
5 E e1 (E)
6 E e2 (E)
7 m c2 (m), c3 (m), c1 (m), c4 (m), c0 (m)
8 p c0 (p), c1 (p)
9 h c0 (h), c1 (h), c4 (h)
We can also check which equations of the model have been presolved in order to
retrieve the dependencies between the variables. E.g. the mass flow variable
just before represents all the mass flows in this model. We can see, that the
mass_flow_constraints have been solved for all components. The
equations indicated here can be inspected in the tables of the documentation on
the components and
connections.
nw.print_presolved_equations()
Presolved equations (11 total):
Object Equation
------------ ----------------------------
compressor mass_flow_constraints
compressor fluid_constraints
condenser mass_flow_constraints
condenser fluid_constraints
cycle closer pressure_equality_constraint
cycle closer enthalpy_equality_constraint
evaporator mass_flow_constraints
evaporator fluid_constraints
valve mass_flow_constraints
valve fluid_constraints
valve enthalpy_constraints
There is not yet an easy way to identify which variable was presolved by which. Next to the presolved equations we can also inspect, which equations are present in the actual model that needs to be solved iteratively.
nw.print_equations()
Equations after presolving (1 total):
Eq# Object Equation
----- ---------- ------------------------
0 compressor energy_connector_balance
With the equations we can also extract the variables these depend on.
nw.print_equations_with_dependents()
Equations with dependent variables (1 total):
Eq# Object Equation Dependent variables
----- ---------- ------------------------ ---------------------
0 compressor energy_connector_balance h1, h3, E6, m7
Impose parameters and check again¶
Now let’s impose a couple of boundary conditions:
No pressure drop in heat exchanges
Compressor efficiency
Motor efficiency
condenser.set_attr(dp=0)
evaporator.set_attr(dp=0)
compressor.set_attr(eta_s=0.8)
motor.set_attr(eta=0.97)
nw.solve("design", init_only=True)
Again, we can inspect, which variables have been presolved now. It does not change, because we did not impose any boundary conditions, where any of the variables can be directly determined from.
nw.print_presolved_variables()
Presolved variables (5 total):
Object Property
-------- ----------
c0 fluid
c1 fluid
c2 fluid
c3 fluid
c4 fluid
But if we check the actual variables of the system, we see that the number has been reduced. The two energy flows are now mapped to a single variable, and the pressure values before and after the heat exchangers have been also mapped to a single variable respectively.
nw.print_variables()
Variables after presolving (7 total):
# Property Represents
--- ---------- --------------------------------------
0 h c2 (h)
1 h c3 (h)
2 m c2 (m), c3 (m), c1 (m), c4 (m), c0 (m)
3 p c3 (p), c4 (p)
4 p c0 (p), c1 (p), c2 (p)
5 h c0 (h), c1 (h), c4 (h)
6 E e1 (E), e2 (E)
The reason for that can be seen in the presolved equations, where now we have three additional entries.
nw.print_presolved_equations()
Presolved equations (14 total):
Object Equation
------------ ----------------------------
compressor mass_flow_constraints
compressor fluid_constraints
condenser mass_flow_constraints
condenser fluid_constraints
condenser dp
cycle closer pressure_equality_constraint
cycle closer enthalpy_equality_constraint
evaporator mass_flow_constraints
evaporator fluid_constraints
evaporator dp
motor eta
valve mass_flow_constraints
valve fluid_constraints
valve enthalpy_constraints
And we also get one more equation in our model equations, that needs to be solved numerically: the compressor efficiency.
nw.print_equations_with_dependents()
Equations with dependent variables (2 total):
Eq# Object Equation Dependent variables
----- ---------- ------------------------ ---------------------
0 compressor energy_connector_balance h0, h1, m2, E6
1 compressor eta_s h0, h1, p3, p4
Or in a matrix view:
nw.print_incidence_matrix()
Incidence matrix:
h0 h1 m2 p3 p4 E6
----------------------------------- ---- ---- ---- ---- ---- ----
compressor.energy_connector_balance x x x - - x
compressor.eta_s x x - x x -
Let’s add more boundary conditions, because we are still missing a couple:
evaporation temperature level and superheating
c2.set_attr(T_dew=10, td_dew=10)
nw.solve("design", init_only=True)
Now we can see that the number of variables has been reduced by two. The reason for this is, that the presolver was able to identify pressure and enthalpy at the compressor inlet with the given boundary conditions.
nw.print_variables()
Variables after presolving (5 total):
# Property Represents
--- ---------- --------------------------------------
0 h c3 (h)
1 m c2 (m), c3 (m), c1 (m), c4 (m), c0 (m)
2 p c3 (p), c4 (p)
3 h c0 (h), c1 (h), c4 (h)
4 E e1 (E), e2 (E)
Since these two variables have now been presolved, the equation of the compressor has less dependents, as it is not necessary to solve for the respective variables anymore.
nw.print_equations_with_dependents()
Equations with dependent variables (2 total):
Eq# Object Equation Dependent variables
----- ---------- ------------------------ ---------------------
0 compressor energy_connector_balance h0, m1, E4
1 compressor eta_s h0, p2
We are still missing 3 equations as we have 5 variables in the problem and only 2 equations at the moment, so let’s add the missing specifications:
electrical power input
condensing temperature level and subcooling
c4.set_attr(T_bubble=60, td_bubble=0)
e1.set_attr(E=100) # 100 kW
nw.solve("design")
iter | residual | progress | massflow | pressure | enthalpy | fluid | component
-------+------------+------------+------------+------------+------------+------------+------------
1 | 2.12e+05 | 7 % | 6.63e-01 | 0.00e+00 | 1.25e+05 | 0.00e+00 | 0.00e+00
2 | 8.26e+04 | 12 % | 1.12e+00 | 0.00e+00 | 1.82e-10 | 0.00e+00 | 0.00e+00
3 | 4.19e-09 | 100 % | 5.79e-14 | 0.00e+00 | 6.37e-11 | 0.00e+00 | 0.00e+00
4 | 1.27e-10 | 100 % | 4.44e-16 | 0.00e+00 | 6.37e-11 | 0.00e+00 | 0.00e+00
Total iterations: 4, Calculation time: 0.00 s, Iterations per second: 1191.82
nw.print_incidence_matrix()
Incidence matrix:
h0 m1
----------------------------------- ---- ----
compressor.energy_connector_balance x x
compressor.eta_s x -
Handle errors during presolving¶
Some errors can occur during presolving, for example:
You specify a linear change of specific variable while specifying both values simultaneously. In this case, the error message directly tells you which variables are linear dependent and that you specified more than a single value in that set (points to the labels of the connections/components).
e2.set_attr(E=97)
nw.solve("design")
---------------------------------------------------------------------------
TESPyNetworkError Traceback (most recent call last)
Cell In[25], line 2
1 e2.set_attr(E=97)
----> 2 nw.solve("design")
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:2674, in Network.solve(self, mode, init_path, design_path, max_iter, min_iter, init_only, init_previous, use_cuda, print_results, robust_relax, skip_postprocess)
2667 msg = (
2668 "Network information:\n"
2669 f" - Number of components: {len(self.comps)}\n"
2670 f" - Number of connections: {len(self.conns)}\n"
2671 )
2672 logger.debug(msg)
-> 2674 self._prepare_problem()
2676 if init_only:
2677 return
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:900, in Network._prepare_problem(self)
897 self._transform_user_input_to_SI()
898 self._create_structure_matrix()
--> 900 self._presolve()
901 self._prepare_for_solver()
903 # generic fluid property initialisation
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:1547, in Network._presolve(self)
1544 for c in self.conns['object']:
1545 self._presolved_equations += c._presolve()
-> 1547 self._presolve_linear_dependents()
1549 # iteratively check presolvable fluid properties
1550 # and distribute presolved variables to all linear dependents
1551 # until the number of variables does not change anymore
1552 number_variables = sum([
1553 variable.is_var
1554 for conn in self.conns['object']
1555 for variable in conn.get_variables().values()
1556 ])
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:1656, in Network._presolve_linear_dependents(self)
1650 var_str = ", ".join(variables_properties)
1651 msg = (
1652 "You specified more than one variable within a set of "
1653 "linearly dependent variables.\n"
1654 f" Variables: {var_str}"
1655 )
-> 1656 raise hlp.TESPyNetworkError(msg)
1657 elif number_specifications == 1:
1658 reference_data = self._variable_lookup[reference]
TESPyNetworkError: You specified more than one variable within a set of linearly dependent variables.
Variables: e1 (E), e2 (E)
We can see the same problem if we were to specify compressor pressure ratio: The compressor inlet pressure is determined from the compressor inlet state, the condenser outlet pressure is determined from the condenser outlet state and the condenser pressure drop is specified. By that also the compressor outlet pressure is known and you cannot specify the outlet pressure.
e2.set_attr(E=None)
compressor.set_attr(pr=4)
nw.solve("design", init_only=True)
---------------------------------------------------------------------------
TESPyNetworkError Traceback (most recent call last)
Cell In[26], line 3
1 e2.set_attr(E=None)
2 compressor.set_attr(pr=4)
----> 3 nw.solve("design", init_only=True)
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:2674, in Network.solve(self, mode, init_path, design_path, max_iter, min_iter, init_only, init_previous, use_cuda, print_results, robust_relax, skip_postprocess)
2667 msg = (
2668 "Network information:\n"
2669 f" - Number of components: {len(self.comps)}\n"
2670 f" - Number of connections: {len(self.conns)}\n"
2671 )
2672 logger.debug(msg)
-> 2674 self._prepare_problem()
2676 if init_only:
2677 return
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:900, in Network._prepare_problem(self)
897 self._transform_user_input_to_SI()
898 self._create_structure_matrix()
--> 900 self._presolve()
901 self._prepare_for_solver()
903 # generic fluid property initialisation
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:1547, in Network._presolve(self)
1544 for c in self.conns['object']:
1545 self._presolved_equations += c._presolve()
-> 1547 self._presolve_linear_dependents()
1549 # iteratively check presolvable fluid properties
1550 # and distribute presolved variables to all linear dependents
1551 # until the number of variables does not change anymore
1552 number_variables = sum([
1553 variable.is_var
1554 for conn in self.conns['object']
1555 for variable in conn.get_variables().values()
1556 ])
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:1656, in Network._presolve_linear_dependents(self)
1650 var_str = ", ".join(variables_properties)
1651 msg = (
1652 "You specified more than one variable within a set of "
1653 "linearly dependent variables.\n"
1654 f" Variables: {var_str}"
1655 )
-> 1656 raise hlp.TESPyNetworkError(msg)
1657 elif number_specifications == 1:
1658 reference_data = self._variable_lookup[reference]
TESPyNetworkError: You specified more than one variable within a set of linearly dependent variables.
Variables: c2 (p), c3 (p), c1 (p), c0 (p), c4 (p)
You can also think of creating a circular dependency. For example, if you specify a relationship of mass flow in front and behind the cycle closer (or if you were to remove the cycle closer). Then the mass flow would form a circular dependency. As output of the error message you get the variables which are part of the circular dependency and the equations responsible for that.
compressor.set_attr(pr=None)
from tespy.connections import Ref
c1.set_attr(m=Ref(c0, 1, 0))
nw.solve("design", init_only=True)
---------------------------------------------------------------------------
TESPyNetworkError Traceback (most recent call last)
Cell In[27], line 7
3
4 from tespy.connections import Ref
5
6 c1.set_attr(m=Ref(c0, 1, 0))
----> 7 nw.solve("design", init_only=True)
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:2674, in Network.solve(self, mode, init_path, design_path, max_iter, min_iter, init_only, init_previous, use_cuda, print_results, robust_relax, skip_postprocess)
2667 msg = (
2668 "Network information:\n"
2669 f" - Number of components: {len(self.comps)}\n"
2670 f" - Number of connections: {len(self.conns)}\n"
2671 )
2672 logger.debug(msg)
-> 2674 self._prepare_problem()
2676 if init_only:
2677 return
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:898, in Network._prepare_problem(self)
895 # this method will distribute units and set SI values from given values
896 # and units
897 self._transform_user_input_to_SI()
--> 898 self._create_structure_matrix()
900 self._presolve()
901 self._prepare_for_solver()
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:1175, in Network._create_structure_matrix(self)
1172 sum_eq = self._preprocess_network_parts(self.comps["object"], sum_eq)
1173 sum_eq = self._preprocess_network_parts(self.user_defined_eq.values(), sum_eq)
-> 1175 _linear_dependencies = self._find_linear_dependent_variables(
1176 self._structure_matrix, self._rhs
1177 )
1178 _linear_dependent_variables = [
1179 var for linear_dependents in _linear_dependencies
1180 for var in linear_dependents["variables"]
1181 ]
1182 _missing_variables = [
1183 {
1184 "variables": [var],
(...) 1190 for var in set(range(num_vars)) - set(_linear_dependent_variables)
1191 ]
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:1338, in Network._find_linear_dependent_variables(self, sparse_matrix, rhs)
1334 cycle = self._find_cycles_in_graph(
1335 {k: [x[0] for x in v] for k, v in adjacency_list.items()}
1336 )
1337 if cycle is not None:
-> 1338 self._raise_error_if_cycle(cycle, edges_with_factors, eq_idx)
1340 # Find connected components and compute factors/offsets
1341 visited = set()
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:1500, in Network._raise_error_if_cycle(self, cycle, edges_with_factors, eq_idx)
1494 eq_str = ", ".join(f"{lbl}.{eq}" for lbl, eq in equations)
1495 msg = (
1496 "A circular dependency has been detected. This overdetermines the problem.\n"
1497 f" Variables: {var_str}\n"
1498 f" Equations: {eq_str}"
1499 )
-> 1500 raise hlp.TESPyNetworkError(msg)
TESPyNetworkError: A circular dependency has been detected. This overdetermines the problem.
Variables: c0 (m), c1 (m), c2 (m), c3 (m), c4 (m)
Equations: c1.m_ref, compressor.mass_flow_constraints, condenser.mass_flow_constraints, evaporator.mass_flow_constraints, valve.mass_flow_constraints
A last error that might occur is specification of properties that determine the same variable, e.g. the c2 pressure as the evaporation pressure is already determined from the saturation temperature and superheating.
c1.set_attr(m=None)
c2.set_attr(p=10) # p has already been set!
nw.solve("design")
---------------------------------------------------------------------------
TESPyNetworkError Traceback (most recent call last)
Cell In[28], line 3
1 c1.set_attr(m=None)
2 c2.set_attr(p=10) # p has already been set!
----> 3 nw.solve("design")
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:2674, in Network.solve(self, mode, init_path, design_path, max_iter, min_iter, init_only, init_previous, use_cuda, print_results, robust_relax, skip_postprocess)
2667 msg = (
2668 "Network information:\n"
2669 f" - Number of components: {len(self.comps)}\n"
2670 f" - Number of connections: {len(self.conns)}\n"
2671 )
2672 logger.debug(msg)
-> 2674 self._prepare_problem()
2676 if init_only:
2677 return
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:900, in Network._prepare_problem(self)
897 self._transform_user_input_to_SI()
898 self._create_structure_matrix()
--> 900 self._presolve()
901 self._prepare_for_solver()
903 # generic fluid property initialisation
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/networks/network.py:1545, in Network._presolve(self)
1542 # set up the actual list of equations for connections, components,
1544 for c in self.conns['object']:
-> 1545 self._presolved_equations += c._presolve()
1547 self._presolve_linear_dependents()
1549 # iteratively check presolvable fluid properties
1550 # and distribute presolved variables to all linear dependents
1551 # until the number of variables does not change anymore
File ~/checkouts/readthedocs.org/user_builds/tespy/envs/990/lib/python3.14/site-packages/tespy/connections/connection.py:1040, in Connection._presolve(self)
1033 if num_specs > 2:
1034 msg = (
1035 "You have specified more than 2 parameters for the connection "
1036 f"{self.label} with a known fluid composition: "
1037 f"{', '.join(specifications)}. This overdetermines the state "
1038 "of the fluid."
1039 )
-> 1040 raise TESPyNetworkError(msg)
1042 presolved_equations = []
1044 if self.p.is_set:
TESPyNetworkError: You have specified more than 2 parameters for the connection c2 with a known fluid composition: p, T_dew, td_dew. This overdetermines the state of the fluid.
c2.set_attr(p=None)
Inspect reasons for linear dependency¶
You can also inspect the network after crashing due to a linear dependency in the Jacobian. For this, we will construct a case where, we get this exact issue:
We fix heat output of condenser (having fixed motor electrical power already)
no specification of evaporator delta p
condenser.set_attr(Q=-350)
evaporator.set_attr(dp=None)
nw.solve("design")
Detected singularity in Jacobian matrix. This singularity is most likely caused by the parametrization of your problem and NOT a numerical issue. Double check your setup.
The following variables are not associated with any equation:
p2
iter | residual | progress | massflow | pressure | enthalpy | fluid | component
-------+------------+------------+------------+------------+------------+------------+------------
1 | 5.67e+04 | 13 % | NaN | NaN | NaN | NaN | NaN
Total iterations: 1, Calculation time: 0.00 s, Iterations per second: 1061.04
The solver now tells us:
The problem is likely a problem in the setup
The reason is that a variable is not associated with any equation
We can then retrieve, which unknowns the variable represents to understand, the issue behind it.
nw.print_variables()
Variables after presolving (3 total):
# Property Represents
--- ---------- --------------------------------------
0 h c3 (h)
1 m c2 (m), c3 (m), c1 (m), c4 (m), c0 (m)
2 p c0 (p), c1 (p)
And we can identify, that there is no equation, that depends on that variable:
nw.print_equations_with_dependents()
Equations with dependent variables (3 total):
Eq# Object Equation Dependent variables
----- ---------- ------------------------ ---------------------
0 compressor energy_connector_balance h0, m1
1 compressor eta_s h0
2 condenser Q h0, m1
nw.print_incidence_matrix()
Incidence matrix:
h0 m1
----------------------------------- ---- ----
compressor.energy_connector_balance x x
compressor.eta_s x -
condenser.Q x x
If the problem is not a setup based problem but a numerical one due to partial derivatives in the Jacobian becoming zero where they should not, it is also possible to inspect the issue. For this we will change the model setup. Just a normal HeatExchanger is sufficient for that:
from tespy.components import Source, Sink, HeatExchanger
nw = Network()
nw.units.set_defaults(
temperature="°C",
pressure="bar",
pressure_difference="bar"
)
so1 = Source("source 1")
so2 = Source("source 2")
si1 = Sink("sink 1")
si2 = Sink("sink 2")
heatex = HeatExchanger("heatexchanger")
c1 = Connection(so1, "out1", heatex, "in1", label="c1")
c2 = Connection(heatex, "out1", si1, "in1", label="c2")
d1 = Connection(so2, "out1", heatex, "in2", label="d1")
d2 = Connection(heatex, "out2", si2, "in1", label="d2")
nw.add_conns(c1, c2, d1, d2)
Now we could make a specification that is impossible but hard to catch as being a setup problem: We set a minimum terminal temperature difference of 25 K but at the same time fix the temperature at hot side outlet and cold side inlet (leading to a temperature difference of 20 K).
c1.set_attr(fluid={"air": 1}, T=200, p=1, m=5)
c2.set_attr(T=110)
d1.set_attr(fluid={"water": 1}, T=90, p=1)
heatex.set_attr(dp1=0, dp2=0, ttd_min=25)
nw.solve("design")
The solver does not seem to make any progress, aborting calculation. Residual value is 5.00e+00
Possible reasons include:
- fluid properties moving outside the valid range of the property database (consider adjusting p_range or h_range),
- an impossible constraint that can never be satisfied
- bad starting values causing the Newton solver to diverge.
Use nw.print_residuals() to identify which equations have the largest residuals.
iter | residual | progress | massflow | pressure | enthalpy | fluid | component
-------+------------+------------+------------+------------+------------+------------+------------
1 | 3.57e+06 | 0 % | 1.29e+00 | 0.00e+00 | 1.48e+05 | 0.00e+00 | 0.00e+00
2 | 1.90e+05 | 8 % | 7.76e-02 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
3 | 5.02e+00 | 58 % | 2.24e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
4 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
5 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
6 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
7 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
8 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
9 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
10 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
11 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
12 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
13 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
14 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
15 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
16 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
17 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
18 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
19 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
20 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
21 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
22 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
23 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
24 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
25 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
26 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
27 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
28 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
29 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
30 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
31 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
32 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
33 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
34 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
35 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
36 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
37 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
38 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
39 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
40 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
41 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
42 | 5.00e+00 | 58 % | 3.82e-07 | 0.00e+00 | 5.00e+00 | 0.00e+00 | 0.00e+00
Total iterations: 42, Calculation time: 0.13 s, Iterations per second: 316.78
Such an impossible specification often leads to plain non-convergence of the numerical problem. We can inspect the residual value vector, and identify the associated equation(s):
nw.print_residuals()
Residuals per equation (2 total, sorted by magnitude):
Eq# Object Equation Residual
----- ------------- -------------------------- ----------
1 heatexchanger ttd_min 5.000e+00
0 heatexchanger energy_balance_constraints -1.911e-06