{ "cells": [ { "cell_type": "markdown", "id": "label-cell", "metadata": {}, "source": [ "(tutorial_heat_exchanger_label)=\n", "\n", "# Overview of Heat Exchanger Models\n", "\n", "This tutorial shows an overview of the different heat exchanger models\n", "available in TESPy. The table below indicates the different types and how they\n", "compare in general. Further down we have created a problem which models the\n", "heat exchanger with a variety of the mentioned types and we show the\n", "differences in the results.\n", "\n", "## Overview table\n", "\n", "| type | | speed | accuracy* | internal pinch |\n", "|---|---|---|---|---|\n", "| SimpleHeatExchanger | 0D | fastest | lowest | no |\n", "| HeatExchanger | 0D | very fast | lower | no |\n", "| ParallelFlowHeatExchanger | 0D | very fast | lower | no |\n", "| Desuperheater | 0D | very fast | lower | no |\n", "| Condenser | 0D | very fast | low | (no) |\n", "| MovingBoundaryHeatExchanger | 1D | mid | high | yes |\n", "| SectionedHeatExchanger | 1D | slow | highest | yes |\n", "\n", "```{note}\n", "- The accuracy depends on the context. In many contexts, the standard 0D\n", " heat exchanger components can be just as accurate as the 1D models.\n", "- The calculation speed also depends on context for the `SectionedHeatExchanger`:\n", "\n", " 1. The number of specified sections influences the speed.\n", " 2. If you impose `td_pinch` or `UA` to your model it will be significantly\n", " slower compared to other models, especially with a high number of sections.\n", " 3. If you do not specify `td_pinch` or `UA` the sectioning is only applied\n", " once in the postprocessing; this takes more time than other heat exchanger\n", " types but is still relatively fast.\n", "```" ] }, { "cell_type": "code", "execution_count": null, "id": "imports", "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import matplotlib.patches as mpatches\n", "from matplotlib import pyplot as plt\n", "from tespy.components import (\n", " Condenser,\n", " HeatExchanger,\n", " MovingBoundaryHeatExchanger,\n", " SectionedHeatExchanger,\n", " Sink,\n", " Source,\n", ")\n", "from tespy.connections import Connection\n", "from tespy.networks import Network\n", "from tespy.tools.fluid_properties import h_mix_pQ\n", "\n", "annotation_color = \"black\"\n", "results = []\n", "\n", "\n", "def make_network(heatex):\n", "\n", " nw = Network()\n", " nw.units.set_defaults(\n", " temperature=\"°C\",\n", " pressure=\"bar\",\n", " pressure_difference=\"bar\",\n", " heat=\"MW\",\n", " heat_transfer_coefficient=\"kW/K\"\n", " )\n", " so1 = Source(\"source 1\")\n", " so2 = Source(\"source 2\")\n", " si1 = Sink(\"sink 1\")\n", " si2 = Sink(\"sink 2\")\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", " nw.add_conns(c1, c2, d1, d2)\n", " c1.set_attr(fluid={\"R290\": 1}, td_dew=50, T_dew=60, m=5)\n", " c2.set_attr(td_bubble=5)\n", " d1.set_attr(fluid={\"water\": 1}, p=1, T=45)\n", " d2.set_attr(T=55)\n", " heatex.set_attr(dp1=0, dp2=0)\n", " return nw, c1, c2, d1, d2" ] }, { "cell_type": "markdown", "id": "model-comparison-intro", "metadata": {}, "source": [ "## Model comparisons\n", "\n", "For the model comparison we have selected a typical problem: the condensation\n", "of a working fluid in a heat pump to heat up water. The boundary conditions\n", "are listed in the table below.\n", "\n", "| label | Specification | value | unit |\n", "|---|---|---|---|\n", "| c1 | fluid | R290 | - |\n", "| | mass flow | 5 | kg/s |\n", "| | dew line temperature | 60 | °C |\n", "| | superheating | 50 | °C |\n", "| c2 | subcooling | 5 | °C |\n", "| d1 | fluid | water | - |\n", "| | temperature | 45 | °C |\n", "| | pressure | 1 | bar |\n", "| d2 | temperature | 55 | °C |\n", "| heatexchanger | pressure drops | 0 | bar |\n", "\n", "```{attention}\n", "Keep in mind: under different boundary conditions (e.g. no phase change)\n", "results of this comparison may vary a lot. There might be conditions where the\n", "0D components yield very similar results, or where the deviation is even higher.\n", "```\n", "\n", "For the calculation of the results the following equations apply:\n", "\n", "```{math}\n", "\\Delta \\theta_\\text{log} = \\frac{\\Delta T_{i} - \\Delta T_{i+1}}{\\ln \\frac{\\Delta T_{i}}{\\Delta T_{i+1}}}\\\\\n", "\n", "kA=\\frac{\\dot Q}{\\Delta \\theta_\\text{log}}\\\\\n", "\n", "UA_{i}=\\frac{\\dot Q_{i}}{\\Delta \\theta_{\\text{log,}i}}\\\\\n", "\n", "UA=\\sum UA_{i}\n", "\n", "```\n", "\n", "For the calculation of {math}`kA` the terminal temperature differences `ttd_u` and\n", "`ttd_l` are considered as {math}`\\Delta T_0` and {math}`\\Delta T_1`. For the calculation\n", "of {math}`UA`, the internal temperature differences {math}`\\Delta T_{i}` in each section\n", "of the heat exchanger are employed." ] }, { "cell_type": "markdown", "id": "heatexchanger-intro", "metadata": {}, "source": [ "### HeatExchanger" ] }, { "cell_type": "code", "execution_count": null, "id": "heatexchanger-setup", "metadata": {}, "outputs": [], "source": [ "hx_0d = HeatExchanger(\"heatexchanger\")\n", "nw, c1, c2, d1, d2 = make_network(hx_0d)\n", "nw.solve(\"design\")\n", "\n", "results.append({\n", " \"model\": \"HeatExchanger\",\n", " \"minimum pinch (K)\": f\"n/a ({hx_0d.ttd_min.val:.1f})\",\n", " \"kA (kW/K)\": f\"{hx_0d.kA.val:.1f}\",\n", " \"UA (kW/K)\": \"n/a\",\n", "})\n", "\n", "heat = [0, abs(hx_0d.Q.val)]\n", "T_hot = [c2.T.val_SI, c1.T.val_SI]\n", "T_cold = [d1.T.val_SI, d2.T.val_SI]\n", "\n", "fig, ax = plt.subplots(1, figsize=(10, 6))\n", "\n", "ax.plot(heat, T_hot, \"o-\", color=\"red\")\n", "ax.plot(heat, T_cold, \"o-\", color=\"blue\")\n", "\n", "offset = heat[-1] / 15\n", "bar_len = heat[-1] / 15\n", "\n", "bracket = mpatches.FancyArrowPatch(\n", " (heat[-1] * (1 + 1 / 15), T_cold[-1]), (heat[-1] * (1 + 1 / 15), T_hot[-1]),\n", " connectionstyle=\"bar,angle=0,fraction=0\", arrowstyle=\"-\", linewidth=2, color=annotation_color,\n", ")\n", "ax.add_patch(bracket)\n", "ax.plot([heat[-1] + offset - bar_len/2, heat[-1] + offset + bar_len/2], [T_hot[-1], T_hot[-1]], color=annotation_color, lw=2)\n", "ax.plot([heat[-1] + offset - bar_len/2, heat[-1] + offset + bar_len/2], [T_cold[-1], T_cold[-1]], color=annotation_color, lw=2)\n", "ax.text(heat[-1] * 1.1, (T_hot[-1] + T_cold[-1]) / 2, r\"$\\Delta T = \\text{ttd\\_u}$\", va=\"center\", color=annotation_color)\n", "\n", "bracket = mpatches.FancyArrowPatch(\n", " (0 - offset, T_cold[0]), (0 - offset, T_hot[0]),\n", " connectionstyle=\"bar,angle=0,fraction=0\", arrowstyle=\"-\", linewidth=2, color=annotation_color,\n", ")\n", "ax.add_patch(bracket)\n", "ax.text(heat[0] - offset * 5, (T_hot[0] + T_cold[0]) / 2, r\"$\\Delta T = \\text{ttd\\_l}$\", va=\"center\", color=annotation_color)\n", "ax.plot([heat[0] - offset - bar_len/2, heat[0] - offset + bar_len/2], [T_hot[0], T_hot[0]], color=annotation_color, lw=2)\n", "ax.plot([heat[0] - offset - bar_len/2, heat[0] - offset + bar_len/2], [T_cold[0], T_cold[0]], color=annotation_color, lw=2)\n", "\n", "ax.set_xbound([-5.5 * offset, heat[-1] + 4 * offset])\n", "ax.set_ylabel(\"temperature in K\")\n", "ax.set_xlabel(\"heat transferred in MW\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "condenser-intro", "metadata": {}, "source": [ "### Condenser\n", "\n", "In the condenser the upper terminal temperature difference is assigned to the\n", "temperature difference between the dew line temperature of the condensing\n", "fluid and the outlet temperature of the cold fluid." ] }, { "cell_type": "code", "execution_count": null, "id": "condenser-setup", "metadata": {}, "outputs": [], "source": [ "hx_cond = Condenser(\"heatexchanger\")\n", "nw, c1, c2, d1, d2 = make_network(hx_cond)\n", "hx_cond.set_attr(subcooling=True)\n", "nw.solve(\"design\")\n", "\n", "results.append({\n", " \"model\": \"Condenser\",\n", " \"minimum pinch (K)\": f\"n/a ({hx_cond.ttd_min.val:.1f})\",\n", " \"kA (kW/K)\": f\"{hx_cond.kA.val:.1f}\",\n", " \"UA (kW/K)\": \"n/a\",\n", "})\n", "\n", "T_cond = c2.T.val_SI - c2.calc_td_dew()\n", "heat_to_cond = c1.m.val_SI * (h_mix_pQ(c2.p.val_SI, 1, c2.fluid_data) - c2.h.val_SI) / 1e6\n", "heat = [0, heat_to_cond, abs(hx_cond.Q.val)]\n", "T_hot = [c2.T.val_SI, T_cond, c1.T.val_SI]\n", "T_cold = [d1.T.val_SI, d2.T.val_SI]\n", "\n", "fig, ax = plt.subplots(1, figsize=(10, 6))\n", "ax.plot(heat, T_hot, \"o-\", color=\"red\")\n", "ax.plot([heat[0], heat[-1]], [T_cold[0], T_cold[-1]], \"o-\", color=\"blue\")\n", "ax.plot([heat[0], heat[-1]], [T_cond, T_cond], \"--\", color=annotation_color)\n", "ax.text(heat[0], T_cond + 2.5, r\"$T_\\text{dew}$\", va=\"center\", color=annotation_color)\n", "\n", "offset = heat[-1] / 15\n", "bar_len = heat[-1] / 15\n", "\n", "bracket = mpatches.FancyArrowPatch(\n", " (heat[-1] * (1 + 1 / 15), T_cold[-1]), (heat[-1] * (1 + 1 / 15), T_cond),\n", " connectionstyle=\"bar,angle=0,fraction=0\", arrowstyle=\"-\", linewidth=2, color=annotation_color,\n", ")\n", "ax.add_patch(bracket)\n", "ax.plot([heat[-1] + offset - bar_len/2, heat[-1] + offset + bar_len/2], [T_cond, T_cond], color=annotation_color, lw=2)\n", "ax.plot([heat[-1] + offset - bar_len/2, heat[-1] + offset + bar_len/2], [T_cold[-1], T_cold[-1]], color=annotation_color, lw=2)\n", "ax.text(heat[-1] * 1.1, (T_cond + T_cold[-1]) / 2, r\"$\\Delta T = \\text{ttd\\_u}$\", va=\"center\", color=annotation_color)\n", "\n", "bracket = mpatches.FancyArrowPatch(\n", " (0 - offset, T_cold[0]), (0 - offset, T_hot[0]),\n", " connectionstyle=\"bar,angle=0,fraction=0\", arrowstyle=\"-\", linewidth=2, color=annotation_color,\n", ")\n", "ax.add_patch(bracket)\n", "ax.text(heat[0] - offset * 5, (T_hot[0] + T_cold[0]) / 2, r\"$\\Delta T = \\text{ttd\\_l}$\", va=\"center\", color=annotation_color)\n", "ax.plot([heat[0] - offset - bar_len/2, heat[0] - offset + bar_len/2], [T_hot[0], T_hot[0]], color=annotation_color, lw=2)\n", "ax.plot([heat[0] - offset - bar_len/2, heat[0] - offset + bar_len/2], [T_cold[0], T_cold[0]], color=annotation_color, lw=2)\n", "\n", "ax.set_xbound([-5.5 * offset, heat[-1] + 4 * offset])\n", "ax.set_ylabel(\"temperature in K\")\n", "ax.set_xlabel(\"heat transferred in MW\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "moving-boundary-intro", "metadata": {}, "source": [ "### MovingBoundaryHeatExchanger\n", "\n", "The moving boundary model sections the heat exchange into three different\n", "sections at the phase change points." ] }, { "cell_type": "code", "execution_count": null, "id": "moving-boundary-setup", "metadata": {}, "outputs": [], "source": [ "hx_mb = MovingBoundaryHeatExchanger(\"heatexchanger\")\n", "nw, c1, c2, d1, d2 = make_network(hx_mb)\n", "nw.solve(\"design\")\n", "\n", "heat, T_hot, T_cold, _, _ = hx_mb.calc_sections()\n", "heat /= 1e6\n", "\n", "results.append({\n", " \"model\": \"MovingBoundaryHeatExchanger\",\n", " \"minimum pinch (K)\": f\"{hx_mb.td_pinch.val:.2f}\",\n", " \"kA (kW/K)\": f\"{hx_mb.kA.val:.1f}\",\n", " \"UA (kW/K)\": f\"{hx_mb.UA.val:.1f}\",\n", "})\n", "\n", "fig, ax = plt.subplots(1, figsize=(10, 6))\n", "ax.plot((heat, heat), (list(T_hot), list(T_cold)), color=annotation_color, linestyle=\"--\")\n", "ax.text(heat[2] * 1.05, (T_hot[2] + T_cold[2]) / 2, r\"$\\Delta T_\\text{i}$\", va=\"center\", color=annotation_color)\n", "ax.plot(heat, T_hot, \"o-\", color=\"red\")\n", "ax.plot(heat, T_cold, \"o-\", color=\"blue\")\n", "\n", "offset = heat[-1] / 15\n", "bar_len = heat[-1] / 15\n", "\n", "bracket = mpatches.FancyArrowPatch(\n", " (heat[-1] * (1 + 1 / 15), T_cold[-1]), (heat[-1] * (1 + 1 / 15), T_hot[-1]),\n", " connectionstyle=\"bar,angle=0,fraction=0\", arrowstyle=\"-\", linewidth=2, color=annotation_color,\n", ")\n", "ax.add_patch(bracket)\n", "ax.plot([heat[-1] + offset - bar_len/2, heat[-1] + offset + bar_len/2], [T_hot[-1], T_hot[-1]], color=annotation_color, lw=2)\n", "ax.plot([heat[-1] + offset - bar_len/2, heat[-1] + offset + bar_len/2], [T_cold[-1], T_cold[-1]], color=annotation_color, lw=2)\n", "ax.text(heat[-1] * 1.1, (T_hot[-1] + T_cold[-1]) / 2, r\"$\\Delta T = \\text{ttd\\_u}$\", va=\"center\", color=annotation_color)\n", "\n", "bracket = mpatches.FancyArrowPatch(\n", " (0 - offset, T_cold[0]), (0 - offset, T_hot[0]),\n", " connectionstyle=\"bar,angle=0,fraction=0\", arrowstyle=\"-\", linewidth=2, color=annotation_color,\n", ")\n", "ax.add_patch(bracket)\n", "ax.text(heat[0] - offset * 5, (T_hot[0] + T_cold[0]) / 2, r\"$\\Delta T = \\text{ttd\\_l}$\", va=\"center\", color=annotation_color)\n", "ax.plot([heat[0] - offset - bar_len/2, heat[0] - offset + bar_len/2], [T_hot[0], T_hot[0]], color=annotation_color, lw=2)\n", "ax.plot([heat[0] - offset - bar_len/2, heat[0] - offset + bar_len/2], [T_cold[0], T_cold[0]], color=annotation_color, lw=2)\n", "\n", "ax.set_xbound([-5.5 * offset, heat[-1] + 4 * offset])\n", "ax.set_ylabel(\"temperature in K\")\n", "ax.set_xlabel(\"heat transferred in MW\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "sectioned-intro", "metadata": {}, "source": [ "### SectionedHeatExchanger\n", "\n", "The sectioned model sections the heat exchange into 50 sections by default and\n", "extra sections are inserted at the moving boundaries (note the smaller\n", "sections in between)." ] }, { "cell_type": "code", "execution_count": null, "id": "sectioned-setup", "metadata": {}, "outputs": [], "source": [ "hx_sectioned = SectionedHeatExchanger(\"heatexchanger\")\n", "nw_sectioned, c1, c2, d1, d2 = make_network(hx_sectioned)\n", "nw_sectioned.solve(\"design\")\n", "\n", "heat_50secs, T_hot_50secs, T_cold_50secs, _, _ = hx_sectioned.calc_sections()\n", "heat_50secs /= 1e6\n", "\n", "results.append({\n", " \"model\": \"SectionedHeatExchanger (50 sections)\",\n", " \"minimum pinch (K)\": f\"{hx_sectioned.td_pinch.val:.2f}\",\n", " \"kA (kW/K)\": f\"{hx_sectioned.kA.val:.1f}\",\n", " \"UA (kW/K)\": f\"{hx_sectioned.UA.val:.1f}\",\n", "})\n", "\n", "fig, ax = plt.subplots(1, figsize=(10, 6))\n", "ax.plot((heat_50secs, heat_50secs), (list(T_hot_50secs), list(T_cold_50secs)), color=annotation_color, linestyle=\"--\")\n", "ax.plot(heat_50secs, T_hot_50secs, \"o-\", color=\"red\")\n", "ax.plot(heat_50secs, T_cold_50secs, \"o-\", color=\"blue\")\n", "\n", "offset = heat_50secs[-1] / 15\n", "bar_len = heat_50secs[-1] / 15\n", "\n", "bracket = mpatches.FancyArrowPatch(\n", " (heat_50secs[-1] * (1 + 1 / 15), T_cold_50secs[-1]), (heat_50secs[-1] * (1 + 1 / 15), T_hot_50secs[-1]),\n", " connectionstyle=\"bar,angle=0,fraction=0\", arrowstyle=\"-\", linewidth=2, color=annotation_color,\n", ")\n", "ax.add_patch(bracket)\n", "ax.plot([heat_50secs[-1] + offset - bar_len/2, heat_50secs[-1] + offset + bar_len/2], [T_hot_50secs[-1], T_hot_50secs[-1]], color=annotation_color, lw=2)\n", "ax.plot([heat_50secs[-1] + offset - bar_len/2, heat_50secs[-1] + offset + bar_len/2], [T_cold_50secs[-1], T_cold_50secs[-1]], color=annotation_color, lw=2)\n", "ax.text(heat_50secs[-1] * 1.1, (T_hot_50secs[-1] + T_cold_50secs[-1]) / 2, r\"$\\Delta T = \\text{ttd\\_u}$\", va=\"center\", color=annotation_color)\n", "\n", "bracket = mpatches.FancyArrowPatch(\n", " (0 - offset, T_cold_50secs[0]), (0 - offset, T_hot_50secs[0]),\n", " connectionstyle=\"bar,angle=0,fraction=0\", arrowstyle=\"-\", linewidth=2, color=annotation_color,\n", ")\n", "ax.add_patch(bracket)\n", "ax.text(heat_50secs[0] - offset * 5, (T_hot_50secs[0] + T_cold_50secs[0]) / 2, r\"$\\Delta T = \\text{ttd\\_l}$\", va=\"center\", color=annotation_color)\n", "ax.plot([heat_50secs[0] - offset - bar_len/2, heat_50secs[0] - offset + bar_len/2], [T_hot_50secs[0], T_hot_50secs[0]], color=annotation_color, lw=2)\n", "ax.plot([heat_50secs[0] - offset - bar_len/2, heat_50secs[0] - offset + bar_len/2], [T_cold_50secs[0], T_cold_50secs[0]], color=annotation_color, lw=2)\n", "\n", "ax.set_xbound([-5.5 * offset, heat_50secs[-1] + 4 * offset])\n", "ax.set_ylabel(\"temperature in K\")\n", "ax.set_xlabel(\"heat transferred in MW\")\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "sectioned-6secs", "metadata": {}, "outputs": [], "source": [ "hx_sectioned.set_attr(num_sections=6)\n", "nw_sectioned.solve(\"design\")\n", "heat_6secs, T_hot_6secs, T_cold_6secs, _, _ = hx_sectioned.calc_sections()\n", "heat_6secs /= 1e6\n", "\n", "results.append({\n", " \"model\": \"SectionedHeatExchanger (6 sections)\",\n", " \"minimum pinch (K)\": f\"{hx_sectioned.td_pinch.val:.2f}\",\n", " \"kA (kW/K)\": f\"{hx_sectioned.kA.val:.1f}\",\n", " \"UA (kW/K)\": f\"{hx_sectioned.UA.val:.1f}\",\n", "})" ] }, { "cell_type": "code", "execution_count": null, "id": "results-table", "metadata": {}, "outputs": [], "source": [ "pd.DataFrame(results).set_index(\"model\").style" ] }, { "cell_type": "markdown", "id": "moving-sectioned-intro", "metadata": {}, "source": [ "## MovingBoundary and Sectioned models\n", "\n", "Comparing these two models, we see almost identical results in the cases\n", "shown above. However, this is not necessarily the case. There are situations\n", "where these models may yield slightly different results. This is specifically\n", "the case when there is a curvature in the isobars (e.g. supercritical\n", "conditions near the critical point) or when there is a pressure drop in a pure\n", "liquid phase nearing the two-phase region.\n", "\n", "First, we have a look at different comparisons of the two models, where the\n", "performance is very similar.\n", "\n", "### Comparing different number of sections\n", "\n", "The following graph shows the comparison of a `MovingBoundaryHeatExchanger` and\n", "a `SectionedHeatExchanger`. There is a very small difference in the\n", "desuperheating section." ] }, { "cell_type": "code", "execution_count": null, "id": "sections-compare-1", "metadata": {}, "outputs": [], "source": [ "hx_sectioned.set_attr(num_sections=1)\n", "nw_sectioned.solve(\"design\")\n", "\n", "heat_1sec, T_hot_1sec, T_cold_1sec, _, _ = hx_sectioned.calc_sections()\n", "heat_1sec /= 1e6\n", "\n", "fig, ax = plt.subplots(1, figsize=(10, 6))\n", "ax.plot((heat_1sec, heat_1sec), (list(T_hot_1sec), list(T_cold_1sec)), color=annotation_color, linestyle=\"-\", linewidth=0.5)\n", "ax.plot(heat_50secs, T_hot_50secs, \"o-\", color=annotation_color, markersize=2, linewidth=0.5, label=\"SectionedHeatExchanger (50 sections)\")\n", "ax.plot(heat_1sec, T_hot_1sec, \"o-\", color=\"red\", label=\"0D heat exchanger\")\n", "ax.plot(heat_1sec, T_cold_1sec, \"o-\", color=\"blue\")\n", "ax.set_ylabel(\"temperature in K\")\n", "ax.set_xlabel(\"heat transferred in MW\")\n", "ax.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "sections-compare-6-intro", "metadata": {}, "source": [ "And two sectioned models with different numbers of sections, where we can see\n", "that already with a few sections we can yield quite good results." ] }, { "cell_type": "code", "execution_count": null, "id": "sections-compare-6", "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(1, figsize=(10, 6))\n", "ax.plot((heat_6secs, heat_6secs), (list(T_hot_6secs), list(T_cold_6secs)), color=annotation_color, linestyle=\"-\", linewidth=0.5)\n", "ax.plot(heat_50secs, T_hot_50secs, \"o-\", color=annotation_color, markersize=2, linewidth=0.5, label=\"SectionedHeatExchanger (50 sections)\")\n", "ax.plot(heat_6secs, T_hot_6secs, \"o-\", color=\"red\", label=\"SectionedHeatExchanger (6 sections)\")\n", "ax.plot(heat_6secs, T_cold_6secs, \"o-\", color=\"blue\")\n", "ax.set_ylabel(\"temperature in K\")\n", "ax.set_xlabel(\"heat transferred in MW\")\n", "ax.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "pressure-drop-intro", "metadata": {}, "source": [ "### Considering pressure drop\n", "\n", "Since both types of models can consider pressure drop (in the same way), they\n", "match quite well again." ] }, { "cell_type": "code", "execution_count": null, "id": "pressure-drop", "metadata": {}, "outputs": [], "source": [ "hx_sectioned = SectionedHeatExchanger(\"heatexchanger\")\n", "nw_sectioned, c1, c2, d1, d2 = make_network(hx_sectioned)\n", "hx_sectioned.set_attr(dp1=2, dp2=0.5)\n", "nw_sectioned.solve(\"design\")\n", "heat_sect, T_hot_sect, T_cold_sect, _, _ = hx_sectioned.calc_sections()\n", "heat_sect /= 1e6\n", "\n", "hx_mb = MovingBoundaryHeatExchanger(\"heatexchanger\")\n", "nw_mb, c1, c2, d1, d2 = make_network(hx_mb)\n", "hx_mb.set_attr(dp1=2, dp2=0.5)\n", "nw_mb.solve(\"design\")\n", "heat_mb, T_hot_mb, T_cold_mb, _, _ = hx_mb.calc_sections()\n", "heat_mb /= 1e6\n", "\n", "fig, ax = plt.subplots(1, figsize=(10, 6))\n", "ax.plot((heat_mb, heat_mb), (list(T_hot_mb), list(T_cold_mb)), color=annotation_color, linestyle=\"-\", linewidth=0.5)\n", "ax.plot(heat_sect, T_hot_sect, \"o-\", color=annotation_color, markersize=2, linewidth=0.5, label=\"SectionedHeatExchanger (50 sections)\")\n", "ax.plot(heat_mb, T_hot_mb, \"o-\", color=\"red\", label=\"MovingBoundaryHeatExchanger\")\n", "ax.plot(heat_mb, T_cold_mb, \"o-\", color=\"blue\")\n", "ax.set_ylabel(\"temperature in K\")\n", "ax.set_xlabel(\"heat transferred in MW\")\n", "ax.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "pressure-drop-preheating-intro", "metadata": {}, "source": [ "Comparing the two again in a different situation: we preheat the working fluid\n", "with a relatively high pressure drop towards saturation. The difference shows\n", "on the secondary side (blue line)." ] }, { "cell_type": "code", "execution_count": null, "id": "pressure-drop-preheating", "metadata": {}, "outputs": [], "source": [ "def make_network_preheating(heatex):\n", "\n", " nw = Network()\n", " nw.units.set_defaults(\n", " temperature=\"°C\",\n", " pressure=\"bar\",\n", " pressure_difference=\"bar\",\n", " heat=\"MW\",\n", " heat_transfer_coefficient=\"kW/K\"\n", " )\n", " so1 = Source(\"source 1\")\n", " so2 = Source(\"source 2\")\n", " si1 = Sink(\"sink 1\")\n", " si2 = Sink(\"sink 2\")\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", " nw.add_conns(c1, c2, d1, d2)\n", " c1.set_attr(fluid={\"water\": 1}, p=1, T=80)\n", " c2.set_attr(T=40)\n", " d1.set_attr(fluid={\"R290\": 1}, T=10, m=5)\n", " d2.set_attr(T_bubble=78, td_bubble=1)\n", " heatex.set_attr(dp1=0, dp2=4)\n", " return nw, c1, c2, d1, d2\n", "\n", "\n", "hx_sectioned = SectionedHeatExchanger(\"heatexchanger\")\n", "nw_sectioned, c1, c2, d1, d2 = make_network_preheating(hx_sectioned)\n", "nw_sectioned.solve(\"design\")\n", "heat_sect, T_hot_sect, T_cold_sect, _, _ = hx_sectioned.calc_sections()\n", "heat_sect /= 1e6\n", "\n", "hx_mb = MovingBoundaryHeatExchanger(\"heatexchanger\")\n", "nw_mb, c1, c2, d1, d2 = make_network_preheating(hx_mb)\n", "nw_mb.solve(\"design\")\n", "heat_mb, T_hot_mb, T_cold_mb, _, _ = hx_mb.calc_sections()\n", "heat_mb /= 1e6\n", "\n", "fig, ax = plt.subplots(1, figsize=(10, 6))\n", "ax.plot((heat_mb, heat_mb), (list(T_hot_mb), list(T_cold_mb)), color=annotation_color, linestyle=\"-\", linewidth=0.5)\n", "ax.plot(heat_sect, T_cold_sect, \"o-\", color=annotation_color, markersize=2, linewidth=0.5, label=\"SectionedHeatExchanger (50 sections)\")\n", "ax.plot(heat_mb, T_hot_mb, \"o-\", color=\"red\")\n", "ax.plot(heat_mb, T_cold_mb, \"o-\", color=\"blue\", label=\"MovingBoundaryHeatExchanger\")\n", "ax.set_ylabel(\"temperature in K\")\n", "ax.set_xlabel(\"heat transferred in MW\")\n", "ax.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "near-critical-intro", "metadata": {}, "source": [ "### Curvature of isobars\n", "\n", "Very similar to the previous comparison, in the supercritical region the\n", "sectioning is quite important." ] }, { "cell_type": "code", "execution_count": null, "id": "near-critical", "metadata": {}, "outputs": [], "source": [ "def make_network_supercritical(heatex):\n", "\n", " nw = Network()\n", " nw.units.set_defaults(\n", " temperature=\"°C\",\n", " pressure=\"bar\",\n", " pressure_difference=\"bar\",\n", " heat=\"MW\",\n", " heat_transfer_coefficient=\"kW/K\"\n", " )\n", " so1 = Source(\"source 1\")\n", " so2 = Source(\"source 2\")\n", " si1 = Sink(\"sink 1\")\n", " si2 = Sink(\"sink 2\")\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", " nw.add_conns(c1, c2, d1, d2)\n", " c1.set_attr(fluid={\"R290\": 1}, T=150, p=45, m=5)\n", " c2.set_attr(T=90)\n", " d1.set_attr(fluid={\"water\": 1}, p=2, T=70)\n", " d2.set_attr(T=110)\n", " heatex.set_attr(dp1=0, dp2=0)\n", " return nw, c1, c2, d1, d2\n", "\n", "\n", "hx_sectioned = SectionedHeatExchanger(\"heatexchanger\")\n", "nw_sectioned, c1, c2, d1, d2 = make_network_supercritical(hx_sectioned)\n", "nw_sectioned.solve(\"design\")\n", "heat_sect, T_hot_sect, T_cold_sect, _, _ = hx_sectioned.calc_sections()\n", "heat_sect /= 1e6\n", "\n", "hx_mb = MovingBoundaryHeatExchanger(\"heatexchanger\")\n", "nw_mb, c1, c2, d1, d2 = make_network_supercritical(hx_mb)\n", "nw_mb.solve(\"design\")\n", "heat_mb, T_hot_mb, T_cold_mb, _, _ = hx_mb.calc_sections()\n", "heat_mb /= 1e6\n", "heatex = SectionedHeatExchanger(\"heatexchanger\")\n", "\n", "fig, ax = plt.subplots(1, figsize=(10, 6))\n", "ax.plot((heat_mb, heat_mb), (list(T_hot_mb), list(T_cold_mb)), color=annotation_color, linestyle=\"-\", linewidth=0.5)\n", "ax.plot(heat_sect, T_hot_sect, \"o-\", color=annotation_color, markersize=2, linewidth=0.5, label=\"SectionedHeatExchanger (50 sections)\")\n", "ax.plot(heat_mb, T_hot_mb, \"o-\", color=\"red\", label=\"MovingBoundaryHeatExchanger\")\n", "ax.plot(heat_mb, T_cold_mb, \"o-\", color=\"blue\")\n", "ax.set_ylabel(\"temperature in K\")\n", "ax.set_xlabel(\"heat transferred in MW\")\n", "ax.legend()\n", "plt.show()" ] } ], "metadata": { "kernelspec": { "display_name": "tespy (3.14.0)", "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.14.0" } }, "nbformat": 4, "nbformat_minor": 5 }