{ "cells": [ { "cell_type": "markdown", "id": "23b5a3c2", "metadata": {}, "source": [ "(tutorial_debugging_label)=\n", "\n", "# Debug your models efficiently\n", "\n", "This tutorial gives insights on how you can debug tespy models. The user\n", "interface of the current implementation might still need some refinement, so\n", "you are invited to raise issues in the github repository. We will change it\n", "based on the feedback. The outputs shown here are based on the following\n", "version of tespy:" ] }, { "cell_type": "code", "execution_count": null, "id": "383d7922", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:09:58.268552Z", "iopub.status.busy": "2025-10-15T15:09:58.268278Z", "iopub.status.idle": "2025-10-15T15:10:00.084509Z", "shell.execute_reply": "2025-10-15T15:10:00.083910Z" } }, "outputs": [], "source": [ "from tespy import __version__\n", "__version__" ] }, { "cell_type": "markdown", "id": "3d7a262d", "metadata": {}, "source": [ "## Simple model debugging\n", "\n", "This tutorial will show a couple of things\n", "\n", "1. How to extract the variables of the problem\n", "\n", " - before presolving step\n", " - after presolving step and identify the presolved variables\n", "\n", "2. How to extract the applied equations of the problem\n", "\n", " - before presolving step\n", " - after presolving step and identify the presolved equations\n", "\n", "3. How to read and fix the errors that are raised during presolving\n", "4. How to debug a model in case of linear dependency by inspecting the\n", " error message, incidence matrix and Jacobian\n", "5. How to interpret/deal with a couple of warnings/errors that might pop up\n", " during postprocessing\n", "\n", "### Model overview\n", "\n", "The model we implement is a very simple heat pump model, just as implemented\n", "in the introductory {ref}`basics_heat_pump_label` tutorial.\n", "\n", "```{image} /_static/images/basics/heat_pump.svg\n", ":align: center\n", ":class: only-light\n", "```\n", "\n", "```{image} /_static/images/basics/heat_pump_darkmode.svg\n", ":align: center\n", ":class: only-dark\n", "```\n", "\n", "### Model code" ] }, { "cell_type": "code", "execution_count": null, "id": "3432e30e", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.096266Z", "iopub.status.busy": "2025-10-15T15:10:00.096076Z", "iopub.status.idle": "2025-10-15T15:10:00.099972Z", "shell.execute_reply": "2025-10-15T15:10:00.099152Z" } }, "outputs": [], "source": [ "from tespy.components import CycleCloser, SimpleHeatExchanger, Compressor, Valve, Motor, PowerSource\n", "from tespy.connections import Connection, PowerConnection\n", "from tespy.networks import Network" ] }, { "cell_type": "code", "execution_count": null, "id": "48eaf8ab", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.102483Z", "iopub.status.busy": "2025-10-15T15:10:00.102268Z", "iopub.status.idle": "2025-10-15T15:10:00.128136Z", "shell.execute_reply": "2025-10-15T15:10:00.127454Z" } }, "outputs": [], "source": [ "nw = Network()\n", "nw.units.set_defaults(\n", " temperature=\"°C\",\n", " pressure=\"bar\",\n", " power=\"kW\",\n", " heat=\"kW\",\n", " enthalpy=\"kJ/kg\"\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "0e187bfb", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.130602Z", "iopub.status.busy": "2025-10-15T15:10:00.130386Z", "iopub.status.idle": "2025-10-15T15:10:00.148406Z", "shell.execute_reply": "2025-10-15T15:10:00.147783Z" } }, "outputs": [], "source": [ "grid = PowerSource(\"grid\")\n", "motor = Motor(\"motor\")\n", "\n", "cc = CycleCloser(\"cycle closer\")\n", "valve = Valve(\"valve\")\n", "evaporator = SimpleHeatExchanger(\"evaporator\")\n", "compressor = Compressor(\"compressor\")\n", "condenser = SimpleHeatExchanger(\"condenser\")\n", "\n", "c1 = Connection(cc, \"out1\", evaporator, \"in1\", label=\"c1\")\n", "c2 = Connection(evaporator, \"out1\", compressor, \"in1\", label=\"c2\")\n", "c3 = Connection(compressor, \"out1\", condenser, \"in1\", label=\"c3\")\n", "c4 = Connection(condenser, \"out1\", valve, \"in1\", label=\"c4\")\n", "c0 = Connection(valve, \"out1\", cc, \"in1\", label=\"c0\")\n", "\n", "\n", "nw.add_conns(c1, c2, c3, c4, c0)\n", "\n", "e1 = PowerConnection(grid, \"power\", motor, \"power_in\", label=\"e1\")\n", "e2 = PowerConnection(motor, \"power_out\", compressor, \"power\", label=\"e2\")\n", "\n", "nw.add_conns(e1, e2)" ] }, { "cell_type": "markdown", "id": "3e470d54", "metadata": {}, "source": [ "### Debug the model\n", "\n", "#### Variable and equation identification\n", "\n", "With nothing specified and trying to solve we will get an information, that\n", "the network is lacking fluid information." ] }, { "cell_type": "code", "execution_count": null, "id": "2ecbbeaf", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.150951Z", "iopub.status.busy": "2025-10-15T15:10:00.150766Z", "iopub.status.idle": "2025-10-15T15:10:00.518539Z", "shell.execute_reply": "2025-10-15T15:10:00.517773Z" } }, "outputs": [], "source": [ "nw.solve(\"design\", init_only=True)" ] }, { "cell_type": "markdown", "id": "1689ff92", "metadata": {}, "source": [ "Then let's specify the fluid and try to preprocess the network. With\n", "{code}`solve_determination` we can check, how many parameters are specified and\n", "how many are missing. When running with {code}`init_only` no error is raised,\n", "so we can use that starting point in an interactive python environment to get\n", "started with debugging." ] }, { "cell_type": "code", "execution_count": null, "id": "5eb03fb8", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.521154Z", "iopub.status.busy": "2025-10-15T15:10:00.520972Z", "iopub.status.idle": "2025-10-15T15:10:00.583100Z", "shell.execute_reply": "2025-10-15T15:10:00.582091Z" } }, "outputs": [], "source": [ "c1.set_attr(fluid={\"R290\": 1})\n", "nw.solve(\"design\", init_only=True)\n", "# this would be executed as next step for actual solve call\n", "nw.solve_determination()" ] }, { "cell_type": "markdown", "id": "bf318544", "metadata": {}, "source": [ "Now we can check the following information:\n", "\n", "- Which are the original variables of our model\n", "- Which of those variables have already been determined by the presolving\n", "- Which ones are the actual variables, that the model has to solve and which\n", " original variables of the model these variables represent\n", "\n", "\n", "The original variables:" ] }, { "cell_type": "code", "execution_count": null, "id": "0642a28a", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.586682Z", "iopub.status.busy": "2025-10-15T15:10:00.586402Z", "iopub.status.idle": "2025-10-15T15:10:00.592662Z", "shell.execute_reply": "2025-10-15T15:10:00.591897Z" } }, "outputs": [], "source": [ "nw.get_variables_before_presolve()" ] }, { "cell_type": "markdown", "id": "db416854", "metadata": {}, "source": [ "The variables solved already by the presolving step:" ] }, { "cell_type": "code", "execution_count": null, "id": "9166d12e", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.595118Z", "iopub.status.busy": "2025-10-15T15:10:00.594730Z", "iopub.status.idle": "2025-10-15T15:10:00.599359Z", "shell.execute_reply": "2025-10-15T15:10:00.598808Z" } }, "outputs": [], "source": [ "nw.get_presolved_variables()" ] }, { "cell_type": "markdown", "id": "6be9bbe2", "metadata": {}, "source": [ "The actual variables of the problem as a dictionary:\n", "\n", "- keys: tuple with variable number as first element, variable type as second\n", " element (mass flow, pressure, enthalpy, fluid, ...)\n", "- values: list of the original variables this variable represents. The list\n", " again contains tuples with\n", "\n", " - the label of the component/connection from which the variable originated\n", " - the type of variable as second element" ] }, { "cell_type": "code", "execution_count": null, "id": "95ff482c", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.601545Z", "iopub.status.busy": "2025-10-15T15:10:00.601315Z", "iopub.status.idle": "2025-10-15T15:10:00.606179Z", "shell.execute_reply": "2025-10-15T15:10:00.605743Z" } }, "outputs": [], "source": [ "nw.get_variables()" ] }, { "cell_type": "markdown", "id": "60c0aafc", "metadata": {}, "source": [ "We can also check which equations of the model have been presolved in order to\n", "retrieve the dependencies between the variables. E.g. the mass flow variable\n", "just before represents all the mass flows in this model. We can see, that the\n", "{code}`mass_flow_constraints` have been solved for all components. The\n", "equations indicated here can be inspected in the tables of the documentation on\n", "the {ref}`components ` and \n", "{ref}`connections `." ] }, { "cell_type": "code", "execution_count": null, "id": "8d553ccb", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.608673Z", "iopub.status.busy": "2025-10-15T15:10:00.608415Z", "iopub.status.idle": "2025-10-15T15:10:00.612890Z", "shell.execute_reply": "2025-10-15T15:10:00.612341Z" } }, "outputs": [], "source": [ "nw.get_presolved_equations()" ] }, { "cell_type": "markdown", "id": "87b9b64d", "metadata": {}, "source": [ "There is not yet an easy way to identify which variable was presolved by which.\n", "Next to the presolved equations we can also inspect, which equations are \n", "present in the actual model that needs to be solved iteratively." ] }, { "cell_type": "code", "execution_count": null, "id": "2e3a3b1c", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.615003Z", "iopub.status.busy": "2025-10-15T15:10:00.614812Z", "iopub.status.idle": "2025-10-15T15:10:00.618456Z", "shell.execute_reply": "2025-10-15T15:10:00.617944Z" } }, "outputs": [], "source": [ "nw.get_equations()" ] }, { "cell_type": "markdown", "id": "1502bd64", "metadata": {}, "source": [ "With the equations we can also extract the variables these depend on." ] }, { "cell_type": "code", "execution_count": null, "id": "89300629", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.620438Z", "iopub.status.busy": "2025-10-15T15:10:00.620248Z", "iopub.status.idle": "2025-10-15T15:10:00.623984Z", "shell.execute_reply": "2025-10-15T15:10:00.623464Z" } }, "outputs": [], "source": [ "nw.get_equations_with_dependents()" ] }, { "cell_type": "markdown", "id": "9dc219c1", "metadata": {}, "source": [ "#### Impose parameters and check again\n", "\n", "Now let's impose a couple of boundary conditions:\n", "\n", "- No pressure drop in heat exchanges\n", "- Compressor efficiency\n", "- Motor efficiency" ] }, { "cell_type": "code", "execution_count": null, "id": "5e64fded", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.626203Z", "iopub.status.busy": "2025-10-15T15:10:00.626015Z", "iopub.status.idle": "2025-10-15T15:10:00.636099Z", "shell.execute_reply": "2025-10-15T15:10:00.635226Z" } }, "outputs": [], "source": [ "condenser.set_attr(dp=0)\n", "evaporator.set_attr(dp=0)\n", "compressor.set_attr(eta_s=0.8)\n", "motor.set_attr(eta=0.97)\n", "nw.solve(\"design\", init_only=True)" ] }, { "cell_type": "markdown", "id": "2dededf3", "metadata": {}, "source": [ "Again, we can inspect, which variables have been presolved now. It does not\n", "change, because we did not impose any boundary conditions, where any of the\n", "variables can be directly determined from." ] }, { "cell_type": "code", "execution_count": null, "id": "933a2974", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.638348Z", "iopub.status.busy": "2025-10-15T15:10:00.638161Z", "iopub.status.idle": "2025-10-15T15:10:00.642587Z", "shell.execute_reply": "2025-10-15T15:10:00.641907Z" } }, "outputs": [], "source": [ "nw.get_presolved_variables()" ] }, { "cell_type": "markdown", "id": "41526825", "metadata": {}, "source": [ "But if we check the actual variables of the system, we see that the number has\n", "been reduced. The two energy flows are now mapped to a single variable, and\n", "the pressure values before and after the heat exchangers have been also mapped\n", "to a single variable respectively." ] }, { "cell_type": "code", "execution_count": null, "id": "46bab30a", "metadata": {}, "outputs": [], "source": [ "nw.get_variables()" ] }, { "cell_type": "markdown", "id": "d398b276", "metadata": {}, "source": [ "The reason for that can be seen in the presolved equations, where now we have\n", "three additional entries." ] }, { "cell_type": "code", "execution_count": null, "id": "cb404695", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.644649Z", "iopub.status.busy": "2025-10-15T15:10:00.644464Z", "iopub.status.idle": "2025-10-15T15:10:00.648892Z", "shell.execute_reply": "2025-10-15T15:10:00.648366Z" } }, "outputs": [], "source": [ "nw.get_presolved_equations()" ] }, { "cell_type": "markdown", "id": "fb27b378", "metadata": {}, "source": [ "And we also get one more equation in our model equations, that needs to be \n", "solved numerically: the compressor efficiency." ] }, { "cell_type": "code", "execution_count": null, "id": "2959c07e", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.658114Z", "iopub.status.busy": "2025-10-15T15:10:00.657914Z", "iopub.status.idle": "2025-10-15T15:10:00.662560Z", "shell.execute_reply": "2025-10-15T15:10:00.661862Z" } }, "outputs": [], "source": [ "nw.get_equations_with_dependents()" ] }, { "cell_type": "markdown", "id": "e4f768a9", "metadata": {}, "source": [ "Let's add more boundary conditions, because we are still missing a couple:\n", "\n", "- evaporation temperature level and superheating" ] }, { "cell_type": "code", "execution_count": null, "id": "702ad1ad", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.665012Z", "iopub.status.busy": "2025-10-15T15:10:00.664824Z", "iopub.status.idle": "2025-10-15T15:10:00.668112Z", "shell.execute_reply": "2025-10-15T15:10:00.667362Z" } }, "outputs": [], "source": [ "c2.set_attr(T_dew=10, td_dew=10)\n", "nw.solve(\"design\", init_only=True)" ] }, { "cell_type": "markdown", "id": "3ee39792", "metadata": {}, "source": [ "Now we can see that the number of variables has been reduced by two. The reason\n", "for this is, that the presolver was able to identify pressure and enthalpy\n", "at the compressor inlet with the given boundary conditions." ] }, { "cell_type": "code", "execution_count": null, "id": "8b0c8b6d", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.681587Z", "iopub.status.busy": "2025-10-15T15:10:00.681357Z", "iopub.status.idle": "2025-10-15T15:10:00.686395Z", "shell.execute_reply": "2025-10-15T15:10:00.685783Z" } }, "outputs": [], "source": [ "nw.get_variables()" ] }, { "cell_type": "markdown", "id": "2c6b346d", "metadata": {}, "source": [ "Since these two variables have now been presolved, the equation of the \n", "compressor has less dependents, as it is not necessary to solve for the\n", "respective variables anymore. " ] }, { "cell_type": "code", "execution_count": null, "id": "ad3ea89a", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.688550Z", "iopub.status.busy": "2025-10-15T15:10:00.688326Z", "iopub.status.idle": "2025-10-15T15:10:00.693599Z", "shell.execute_reply": "2025-10-15T15:10:00.692777Z" } }, "outputs": [], "source": [ "nw.get_equations_with_dependents()" ] }, { "cell_type": "markdown", "id": "b5325d4b", "metadata": {}, "source": [ "We are still missing 3 equations as we have 5 variables in the problem and only\n", "2 equations at the moment, so let's add the missing specifications:\n", "\n", "- electrical power input\n", "- condensing temperature level and subcooling" ] }, { "cell_type": "code", "execution_count": null, "id": "c7ac404e", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.695825Z", "iopub.status.busy": "2025-10-15T15:10:00.695579Z", "iopub.status.idle": "2025-10-15T15:10:00.698803Z", "shell.execute_reply": "2025-10-15T15:10:00.698099Z" } }, "outputs": [], "source": [ "c4.set_attr(T_bubble=60, td_bubble=0)\n", "e1.set_attr(E=100) # 100 kW" ] }, { "cell_type": "code", "execution_count": null, "id": "5374e33b", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.700983Z", "iopub.status.busy": "2025-10-15T15:10:00.700763Z", "iopub.status.idle": "2025-10-15T15:10:00.748487Z", "shell.execute_reply": "2025-10-15T15:10:00.747750Z" } }, "outputs": [], "source": [ "nw.solve(\"design\")" ] }, { "cell_type": "markdown", "id": "0c7fa7f8", "metadata": {}, "source": [ "(tutorial_debugging_error_presolve_label)=\n", "\n", "#### Handle errors during presolving\n", "\n", "Some errors can occur during presolving, for example:\n", "\n", "You specify a linear change of specific variable while specifying both values\n", "simultaneously. In this case, the error message directly tells you which\n", "variables are linear dependent and that you specified more than a single value\n", "in that set (points to the labels of the connections/components)." ] }, { "cell_type": "code", "execution_count": null, "id": "988578f8", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.750601Z", "iopub.status.busy": "2025-10-15T15:10:00.750414Z", "iopub.status.idle": "2025-10-15T15:10:00.832725Z", "shell.execute_reply": "2025-10-15T15:10:00.831950Z" } }, "outputs": [], "source": [ "e2.set_attr(E=97)\n", "nw.solve(\"design\")" ] }, { "cell_type": "markdown", "id": "5bf55736", "metadata": {}, "source": [ "We can see the same problem if we were to specify compressor pressure ratio:\n", "The compressor inlet pressure is determined from the compressor inlet state,\n", "the condenser outlet pressure is determined from the condenser outlet state and\n", "the condenser pressure drop is specified. By that also the compressor outlet\n", "pressure is known and you cannot specify the outlet pressure." ] }, { "cell_type": "code", "execution_count": null, "id": "5f1575a1", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.835342Z", "iopub.status.busy": "2025-10-15T15:10:00.835162Z", "iopub.status.idle": "2025-10-15T15:10:00.915658Z", "shell.execute_reply": "2025-10-15T15:10:00.915133Z" } }, "outputs": [], "source": [ "e2.set_attr(E=None)\n", "compressor.set_attr(pr=4)\n", "nw.solve(\"design\", init_only=True)" ] }, { "cell_type": "markdown", "id": "27a26f1e", "metadata": {}, "source": [ "You can also think of creating a circular dependency. For example, if you\n", "specify a relationship of mass flow in front and behind the cycle closer (or\n", "if you were to remove the cycle closer). Then the mass flow would form a\n", "circular dependency. As output of the error message you get the variables which\n", "are part of the circular dependency and the equations responsible for that.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "0519739d", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:00.918317Z", "iopub.status.busy": "2025-10-15T15:10:00.918117Z", "iopub.status.idle": "2025-10-15T15:10:01.037781Z", "shell.execute_reply": "2025-10-15T15:10:01.036560Z" } }, "outputs": [], "source": [ "compressor.set_attr(pr=None)\n", "\n", "\n", "from tespy.connections import Ref\n", "\n", "c1.set_attr(m=Ref(c0, 1, 0))\n", "nw.solve(\"design\", init_only=True)" ] }, { "cell_type": "markdown", "id": "dac35244", "metadata": {}, "source": [ "A last error that might occur is specification of properties that determine the\n", "same variable, e.g. the c2 pressure as the evaporation pressure is already\n", "determined from the saturation temperature and superheating." ] }, { "cell_type": "code", "execution_count": null, "id": "74014b58", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:01.041001Z", "iopub.status.busy": "2025-10-15T15:10:01.040798Z", "iopub.status.idle": "2025-10-15T15:10:01.221577Z", "shell.execute_reply": "2025-10-15T15:10:01.220849Z" } }, "outputs": [], "source": [ "c1.set_attr(m=None)\n", "c2.set_attr(p=10) # p has already been set!\n", "nw.solve(\"design\")" ] }, { "cell_type": "code", "execution_count": null, "id": "ddc5a851", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:01.224708Z", "iopub.status.busy": "2025-10-15T15:10:01.224505Z", "iopub.status.idle": "2025-10-15T15:10:01.227617Z", "shell.execute_reply": "2025-10-15T15:10:01.226979Z" } }, "outputs": [], "source": [ "c2.set_attr(p=None)" ] }, { "cell_type": "markdown", "id": "dde3b9bb", "metadata": {}, "source": [ "(tutorial_debugging_linear_dependency_label)=\n", "\n", "#### Inspect reasons for linear dependency\n", "\n", "You can also inspect the network after crashing due to a linear dependency in\n", "the Jacobian. For this, we will construct a case where, we get this exact\n", "issue:\n", "\n", "- We fix heat output of condenser (having fixed motor electrical power already)\n", "- no specification of evaporator delta p" ] }, { "cell_type": "code", "execution_count": null, "id": "70cb46df", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:01.230138Z", "iopub.status.busy": "2025-10-15T15:10:01.229966Z", "iopub.status.idle": "2025-10-15T15:10:01.239632Z", "shell.execute_reply": "2025-10-15T15:10:01.239185Z" } }, "outputs": [], "source": [ "condenser.set_attr(Q=-350)\n", "evaporator.set_attr(dp=None)\n", "nw.solve(\"design\")" ] }, { "cell_type": "markdown", "id": "3f6f235d", "metadata": {}, "source": [ "The solver now tells us:\n", "\n", "- The problem is likely a problem in the setup\n", "- The reason is that a variable is not associated with any equation\n", "\n", "We can then retrieve, which unknowns the variable represents to understand,\n", "the issue behind it." ] }, { "cell_type": "code", "execution_count": null, "id": "5e338977", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:01.241916Z", "iopub.status.busy": "2025-10-15T15:10:01.241745Z", "iopub.status.idle": "2025-10-15T15:10:01.246008Z", "shell.execute_reply": "2025-10-15T15:10:01.245174Z" } }, "outputs": [], "source": [ "nw.get_variables()" ] }, { "cell_type": "markdown", "id": "5dcf42f5", "metadata": {}, "source": [ "And we can identify, that there is no equation, that depends on that variable:" ] }, { "cell_type": "code", "execution_count": null, "id": "6b2d69d9", "metadata": {}, "outputs": [], "source": [ "nw.get_equations_with_dependents()" ] }, { "cell_type": "markdown", "id": "e9f34762", "metadata": {}, "source": [ "If the problem is not a setup based problem but a numerical one due to\n", "partial derivatives in the Jacobian becoming zero where they should not, it is\n", "also possible to inspect the issue. For this we will change the model setup.\n", "Just a normal HeatExchanger is sufficient for that:" ] }, { "cell_type": "code", "execution_count": null, "id": "ffd2adbb", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:01.248078Z", "iopub.status.busy": "2025-10-15T15:10:01.247898Z", "iopub.status.idle": "2025-10-15T15:10:01.250984Z", "shell.execute_reply": "2025-10-15T15:10:01.250207Z" } }, "outputs": [], "source": [ "from tespy.components import Source, Sink, HeatExchanger\n", "\n", "\n", "nw = Network()\n", "nw.units.set_defaults(\n", " temperature=\"°C\",\n", " pressure=\"bar\"\n", ")\n", "\n", "so1 = Source(\"source 1\")\n", "so2 = Source(\"source 2\")\n", "\n", "si1 = Sink(\"sink 1\")\n", "si2 = Sink(\"sink 2\")\n", "\n", "heatex = HeatExchanger(\"heatexchanger\")\n", "\n", "c1 = Connection(so1, \"out1\", heatex, \"in1\", label=\"c1\")\n", "c2 = Connection(heatex, \"out1\", si1, \"in1\", label=\"c2\")\n", "d1 = Connection(so2, \"out1\", heatex, \"in2\", label=\"d1\")\n", "d2 = Connection(heatex, \"out2\", si2, \"in1\", label=\"d2\")\n", "\n", "nw.add_conns(c1, c2, d1, d2)" ] }, { "cell_type": "markdown", "id": "7b40f287", "metadata": {}, "source": [ "Now we could make a specification that is impossible but hard to catch as being\n", "a setup problem: We set a minimum terminal temperature difference of 25 K but\n", "at the same time fix the temperature at hot side outlet and cold side inlet\n", "(leading to a temperature difference of 20 K)." ] }, { "cell_type": "code", "execution_count": null, "id": "8495eb3e", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:01.291385Z", "iopub.status.busy": "2025-10-15T15:10:01.291145Z", "iopub.status.idle": "2025-10-15T15:10:01.295280Z", "shell.execute_reply": "2025-10-15T15:10:01.294406Z" } }, "outputs": [], "source": [ "c1.set_attr(fluid={\"air\": 1}, T=200, p=1, m=5)\n", "c2.set_attr(T=110)\n", "d1.set_attr(fluid={\"water\": 1}, T=90, p=1)\n", "heatex.set_attr(dp1=0, dp2=0, ttd_min=25)" ] }, { "cell_type": "code", "execution_count": null, "id": "2376e858", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:01.297538Z", "iopub.status.busy": "2025-10-15T15:10:01.297300Z", "iopub.status.idle": "2025-10-15T15:10:01.332513Z", "shell.execute_reply": "2025-10-15T15:10:01.331885Z" } }, "outputs": [], "source": [ "nw.solve(\"design\")" ] }, { "cell_type": "markdown", "id": "0ac31e7f", "metadata": {}, "source": [ "We can see:\n", "\n", "- The solver tells us potentially numerical issue (not always correct!).\n", "- The heat exchanger ttd_min function should depend on variable (0, \"h\")\n", " representing \"h\" of connection \"d2\".\n", "- There is a zero in the Jacobian of that function towards that number while\n", " there should be a non-zero entry.\n", "- An extra piece of information: all entries in the Jacobian of the equation\n", " became zero.\n", "\n", "Next we can also extract the incidence matrix and the Jacobian to identify\n", "issue if we want. For that we have a look at the equations and their dependents\n", "first." ] }, { "cell_type": "code", "execution_count": null, "id": "c6d242e6", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:01.335111Z", "iopub.status.busy": "2025-10-15T15:10:01.334873Z", "iopub.status.idle": "2025-10-15T15:10:01.339337Z", "shell.execute_reply": "2025-10-15T15:10:01.338776Z" } }, "outputs": [], "source": [ "nw.get_equations_with_dependents()" ] }, { "cell_type": "markdown", "id": "121ee873", "metadata": {}, "source": [ "Then we can use the incidence_matrix and the Jacobian. Here we see for the\n", "problematic equation number (1) and variable number (0):\n", "\n", "- The incidence matrix indicates which entries (rows = equations, columns =\n", " variables) of the Jacobian are supposed to be zero or non-zero.\n", "- The actual values of the Jacobian.\n", "- And, that a non-zero is expected for (1, 0) but there is a zero instead." ] }, { "cell_type": "code", "execution_count": null, "id": "9861665b", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:01.341378Z", "iopub.status.busy": "2025-10-15T15:10:01.341211Z", "iopub.status.idle": "2025-10-15T15:10:01.344940Z", "shell.execute_reply": "2025-10-15T15:10:01.344539Z" } }, "outputs": [], "source": [ "nw._incidence_matrix_dense" ] }, { "cell_type": "code", "execution_count": null, "id": "d4319715", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:10:01.347151Z", "iopub.status.busy": "2025-10-15T15:10:01.346978Z", "iopub.status.idle": "2025-10-15T15:10:01.350536Z", "shell.execute_reply": "2025-10-15T15:10:01.350146Z" } }, "outputs": [], "source": [ "nw.jacobian" ] } ], "metadata": { "kernelspec": { "display_name": "tespy-user-meeting-2025", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.6" } }, "nbformat": 4, "nbformat_minor": 5 }