{ "cells": [ { "cell_type": "markdown", "id": "fe85b323", "metadata": {}, "source": [ "(integration_model_class_template_label)=\n", "\n", "# Workflow integration using model classes\n", "\n", "{py:class}`ModelTemplate ` is a base class\n", "for building reusable, self-contained TESPy models. Subclassing it gives you a\n", "consistent interface for parameter access, solving, sensitivity sweeps,\n", "optimization and diagram plotting - without having to rewrite that plumbing for\n", "every model.\n", "\n", "```{note}\n", "**This is a new feature.** `ModelTemplate` is actively developed and will\n", "receive additional capabilities and refinements in future releases. If you have\n", "ideas, encounter unexpected behavior, or have built something useful on top of\n", "`ModelTemplate` that others could benefit from, we warmly invite you to open an\n", "issue or start a discussion in the\n", "[TESPy issue tracker](https://github.com/oemof/tespy/issues). Contributions and\n", "pull requests are equally welcome. See the\n", "[contribution guide](https://github.com/oemof/tespy/blob/dev/CONTRIBUTING.md)\n", "for details.\n", "```" ] }, { "cell_type": "markdown", "id": "3081345f", "metadata": {}, "source": [ "## Background\n", "\n", "### Subclassing ModelTemplate\n", "\n", "Every concrete model overrides two methods:\n", "\n", "1. **`_parameter_lookup`** - returns a `dict` mapping human-readable parameter\n", " names\n", " to their location in the network.\n", "2. **`_create_network`** - assembles the TESPy network and produces a stable\n", " initial solution. Always call `super()._create_network()` first.\n", "\n", "For use with the optimization API, also implement **`solve_model`** to delegate\n", "to `solve_model_design` or `solve_model_offdesign`. Alternatively, you can wrap\n", "solve model around your custom implementation logic for solving a model\n", "including arbitrary pre- or postprocessings.\n", "\n", "### Parameter lookup\n", "\n", "`_parameter_lookup` maps each name to one of four entry forms:\n", "\n", "| Entry | Meaning |\n", "|---|---|\n", "| `[\"Connections\", \"label\", \"attr\"]` | Read and write a connection attribute |\n", "| `[\"Components\", \"label\", \"attr\"]` | Read and write a component attribute |\n", "| `{\"get\": callable}` | Read-only derived quantity; callable takes no arguments |\n", "| `{\"set\": callable}` or `{\"get\": ..., \"set\": ...}` | Write-only or read/write custom quantity |\n", "\n", "### Solving\n", "\n", "- **`solve_model_design(**kwargs)`** - sets parameters and runs a design-mode\n", " solve, recovering from corruption via the last stable solution stored in\n", " `self._stable_solution`.\n", "- **`solve_model_offdesign(**kwargs)`** - off-design mode; requires\n", " `self._design_path` to be set.\n", "- **`solve_model(**kwargs)`** - not implemented in the base class; implement it\n", " in the subclass to make the model callable from `OptimizationProblem`.\n", "\n", "### Reading and writing parameters\n", "\n", "`get_parameter(name)` and `set_parameters(**kwargs)` use the names defined in\n", "`_parameter_lookup`. Multiple parameters targeting the same network object are\n", "batched into a single `set_attr` call.\n", "\n", "### Sensitivity analysis\n", "\n", "`sensitivity_analysis(param_dict, result_param_list)` sweeps a table of input\n", "combinations and returns a `DataFrame`. All lists in `param_dict` must have the\n", "same length - each row is one simulation. The evaluation order is minimized\n", "automatically with a greedy nearest-neighbor heuristic to reduce step sizes\n", "between consecutive solves.\n", "\n", "### Optimization\n", "\n", "`optimize(algorithm, termination, variables, ...)` wraps `OptimizationProblem`\n", "and a pymoo algorithm into a single call, returning a `DataFrame` of all\n", "evaluated individuals. For a detailed walkthrough of the optimization API see\n", "{ref}`tutorial_optimization_label`.\n", "\n", "### Plotting\n", "\n", "Three diagram methods are available on every instance:\n", "\n", "- **`plot_Ts_diagram_matplotlib(connection_label)`** - T-s diagram for a cycle\n", "- **`plot_logph_diagram_matplotlib(connection_label)`** - log(p,h) diagram for\n", " a cycle\n", "- **`plot_QT_diagram_matplotlib(component_label)`** - Q-T diagram for a heat\n", " exchanger\n", "\n", "The connection label specified for the cycle diagrams can be any connection\n", "within the respective (sub)cycles.\n", "\n", "All three accept an optional `ax` to draw into an existing matplotlib axes and\n", "`save_dir` to write the figure to a file.\n" ] }, { "cell_type": "markdown", "id": "5b72dbda", "metadata": {}, "source": [ "## Examples\n", "\n", "### ORC example\n", "\n", "This example builds an Organic Rankine Cycle with an internal recuperator. The\n", "`_parameter_lookup` uses the `[\"Connections\", ...]` and `[\"Components\", ...]`\n", "forms. `solve_model` delegates directly to `solve_model_design`." ] }, { "cell_type": "code", "execution_count": null, "id": "409f7de1", "metadata": {}, "outputs": [], "source": [ "from tespy.components import (\n", " CycleCloser, Generator, Motor, MovingBoundaryHeatExchanger,\n", " PowerBus, PowerSink, Pump, Sink, Source, Turbine,\n", ")\n", "from tespy.connections import Connection, PowerConnection\n", "from tespy.models import ModelTemplate" ] }, { "cell_type": "code", "execution_count": null, "id": "4f9138b2", "metadata": {}, "outputs": [], "source": [ "class ORCModel(ModelTemplate):\n", "\n", " def _parameter_lookup(self) -> dict:\n", " return {\n", " \"evaporator_pinch\": [\"Components\", \"evaporator\", \"td_pinch\"],\n", " \"condenser_pinch\": [\"Components\", \"condenser\", \"td_pinch\"],\n", " \"turbine__efficiency\": [\"Components\", \"turbine\", \"eta_s\"],\n", " \"net_power\": [\"Connections\", \"e5\", \"E\"],\n", " \"T_source\": [\"Connections\", \"a1\", \"T\"],\n", " \"T_outflow\": [\"Connections\", \"a3\", \"T\"],\n", " \"m_source\": [\"Connections\", \"a1\", \"m\"]\n", " }\n", "\n", " def solve_model(self, **kwargs):\n", " self.solve_model_design(**kwargs)\n", "\n", " def _create_network(self) -> None:\n", " super()._create_network()\n", " self.nw.units.set_defaults(\n", " temperature=\"degC\",\n", " pressure=\"bar\",\n", " pressure_difference=\"bar\",\n", " power=\"kW\",\n", " heat=\"kW\"\n", " )\n", "\n", " turbine = Turbine(\"turbine\")\n", " recuperator = MovingBoundaryHeatExchanger(\"recuperator\")\n", " condenser = MovingBoundaryHeatExchanger(\"condenser\")\n", " pump = Pump(\"pump\")\n", " preheater = MovingBoundaryHeatExchanger(\"preheater\")\n", " evaporator = MovingBoundaryHeatExchanger(\"evaporator\")\n", " cc = CycleCloser(\"cc\")\n", "\n", " heat_source = Source(\"heat source\")\n", " heat_outflow = Sink(\"heat outflow\")\n", "\n", " air_source = Source(\"air source\")\n", " air_sink = Sink(\"air sink\")\n", "\n", " a1 = Connection(heat_source, \"out1\", evaporator, \"in1\", label=\"a1\")\n", " a2 = Connection(evaporator, \"out1\", preheater, \"in1\", label=\"a2\")\n", " a3 = Connection(preheater, \"out1\", heat_outflow, \"in1\", label=\"a3\")\n", "\n", " b1 = Connection(cc, \"out1\", turbine, \"in1\", label=\"b1\")\n", " b2 = Connection(turbine, \"out1\", recuperator, \"in1\", label=\"b2\")\n", " b3 = Connection(recuperator, \"out1\", condenser, \"in1\", label=\"b3\")\n", " b4 = Connection(condenser, \"out1\", pump, \"in1\", label=\"b4\")\n", " b5 = Connection(pump, \"out1\", recuperator, \"in2\", label=\"b5\")\n", " b6 = Connection(recuperator, \"out2\", preheater, \"in2\", label=\"b6\")\n", " b7 = Connection(preheater, \"out2\", evaporator, \"in2\", label=\"b7\")\n", " b8 = Connection(evaporator, \"out2\", cc, \"in1\", label=\"b8\")\n", "\n", " c1 = Connection(air_source, \"out1\", condenser, \"in2\", label=\"c1\")\n", " c2 = Connection(condenser, \"out2\", air_sink, \"in1\", label=\"c2\")\n", "\n", " self.nw.add_conns(a1, a2, a3, b1, b2, b3, b4, b5, b6, b7, b8, c1, c2)\n", "\n", " generator = Generator(\"generator\")\n", " motor = Motor(\"motor\")\n", " power_bus = PowerBus(\"bus\", num_in=1, num_out=2)\n", " grid = PowerSink(\"grid\")\n", "\n", " e1 = PowerConnection(turbine, \"power\", generator, \"power_in\", label=\"e1\")\n", " e2 = PowerConnection(generator, \"power_out\", power_bus, \"power_in1\", label=\"e2\")\n", " e3 = PowerConnection(power_bus, \"power_out1\", motor, \"power_in\", label=\"e3\")\n", " e4 = PowerConnection(motor, \"power_out\", pump, \"power\", label=\"e4\")\n", " e5 = PowerConnection(power_bus, \"power_out2\", grid, \"power\", label=\"e5\")\n", "\n", " self.nw.add_conns(e1, e2, e3, e4, e5)\n", "\n", " generator.set_attr(eta=0.98)\n", " motor.set_attr(eta=0.98)\n", "\n", " a1.set_attr(fluid={\"air\": 1}, T=200, p=1, m=10)\n", " a2.set_attr(T=155)\n", "\n", " b1.set_attr(fluid={\"Isopentane\": 1}, x=1, T=150)\n", " b3.set_attr(td_dew=10, T_dew=30)\n", " b4.set_attr(td_bubble=5)\n", " b7.set_attr(td_bubble=5)\n", "\n", " c1.set_attr(fluid={\"air\": 1}, T=10, p=1)\n", " c2.set_attr(T=20)\n", "\n", " recuperator.set_attr(dp1=0, dp2=0)\n", " condenser.set_attr(dp1=0, dp2=0)\n", " preheater.set_attr(dp1=0, dp2=0)\n", " evaporator.set_attr(dp1=0, dp2=0)\n", "\n", " turbine.set_attr(eta_s=0.8)\n", " pump.set_attr(eta_s=0.7)\n", "\n", " self.nw.solve(\"design\")\n", "\n", " b3.set_attr(T_dew=None)\n", " condenser.set_attr(td_pinch=5)\n", "\n", " a2.set_attr(T=None)\n", " evaporator.set_attr(td_pinch=10)\n", "\n", " self.nw.solve(\"design\")\n", " self._stable_solution = \"stable_solution.json\"\n", " self.nw.save(self._stable_solution)" ] }, { "cell_type": "markdown", "id": "3f2f5bf1", "metadata": {}, "source": [ "#### Create an instance\n", "\n", "Instantiating the model triggers `_create_network` and runs the initial solve." ] }, { "cell_type": "code", "execution_count": null, "id": "37c8af7c", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:12:01.506263Z", "iopub.status.busy": "2025-10-15T15:12:01.505892Z", "iopub.status.idle": "2025-10-15T15:12:02.006773Z", "shell.execute_reply": "2025-10-15T15:12:02.005856Z" } }, "outputs": [], "source": [ "model = ORCModel()" ] }, { "cell_type": "markdown", "id": "1e1c9aba", "metadata": {}, "source": [ "#### Resolve with updated parameters\n", "\n", "Use `solve_model_design` to re-run the model with new input values." ] }, { "cell_type": "code", "execution_count": null, "id": "613205a2", "metadata": { "execution": { "iopub.execute_input": "2025-10-15T15:12:02.011177Z", "iopub.status.busy": "2025-10-15T15:12:02.010703Z", "iopub.status.idle": "2025-10-15T15:12:02.178082Z", "shell.execute_reply": "2025-10-15T15:12:02.177550Z" } }, "outputs": [], "source": [ "model.solve_model(\n", " **{\"T_source\": 225}\n", ")" ] }, { "cell_type": "markdown", "id": "7cfdf7fb", "metadata": {}, "source": [ "#### Read results\n", "\n", "Parameters are read back by name via `get_parameter`. Diagrams can be produced\n", "directly from the model instance." ] }, { "cell_type": "code", "execution_count": null, "id": "e2c74726", "metadata": {}, "outputs": [], "source": [ "model.get_parameter(\"T_outflow\")" ] }, { "cell_type": "code", "execution_count": null, "id": "36aa0917", "metadata": {}, "outputs": [], "source": [ "fig, ax = model.plot_Ts_diagram_matplotlib(\"b1\")" ] }, { "cell_type": "markdown", "id": "1e73c686", "metadata": {}, "source": [ "### Heat pump with two refrigerant cycles\n", "\n", "This example has a low-temperature and a high-temperature refrigerant cycle\n", "coupled through an internal heat exchanger. It showcases all four\n", "`_parameter_lookup` entry forms:\n", "\n", "- `[\"Components\", ...]` and `[\"Connections\", ...]` for standard network\n", " attributes,\n", "- `{\"get\": callable}` for a read-only derived quantity - here the computed COP,\n", "- `{\"get\": ..., \"set\": ...}` for a read/write custom quantity - here a COP\n", " target that reads back the current target value and, when written, activates a\n", " `UserDefinedEquation` that enforces it. Passing `None` deactivates the\n", " constraint and restores the degree of freedom,\n", "- `{\"set\": callable}` for a write-only parameter with arbitrary setter logic -\n", " here used to assign a pressure drop to all heat exchanger working-fluid sides\n", " in one call.\n", "\n", "See the API docs of\n", "{py:class}`ModelTemplate ` for more\n", "information." ] }, { "cell_type": "code", "execution_count": null, "id": "37742ede", "metadata": {}, "outputs": [], "source": [ "from tespy.components import Compressor\n", "from tespy.components import CycleCloser\n", "from tespy.components import Motor\n", "from tespy.components import PowerBus\n", "from tespy.components import PowerSource\n", "from tespy.components import SectionedHeatExchanger\n", "from tespy.components import SimpleHeatExchanger\n", "from tespy.components import Sink\n", "from tespy.components import Source\n", "from tespy.components import Valve\n", "from tespy.connections import Connection\n", "from tespy.connections import PowerConnection\n", "from tespy.models import ModelTemplate\n", "from tespy.tools import UserDefinedEquation" ] }, { "cell_type": "code", "execution_count": null, "id": "1dd39c8a", "metadata": {}, "outputs": [], "source": [ "class HeatPumpModel(ModelTemplate):\n", "\n", " def _parameter_lookup(self):\n", " return {\n", " \"ihx pinch\": [\"Components\", \"internal heat exchanger\", \"td_pinch\"],\n", " \"heat\": [\"Components\", \"condenser high\", \"Q\"],\n", " \"power\": [\"Connections\", \"e1\", \"E\"],\n", " \"higher cycle compressor efficiency\": [\"Components\", \"compressor high\", \"eta_s\"],\n", " \"lower cycle mass flow\": [\"Connections\", \"a1\", \"m\"],\n", " \"lower cycle condensation temperature\": [\"Connections\", \"a3\", \"T_bubble\"],\n", " \"cop\": {\"get\": self.calc_cop, \"set\": self.set_cop},\n", " \"heat_exchangers_all_dp\": {\"set\": self.set_all_hx_dp},\n", " }\n", "\n", " def calc_cop(self):\n", " return (\n", " abs(self.get_parameter(\"heat\"))\n", " / self.get_parameter(\"power\")\n", " )\n", "\n", " def set_cop(self, value):\n", " def cop_ude(ude):\n", " e1, b2, b3 = ude.conns\n", " return (\n", " ude.params[\"cop\"] * e1.E.val_SI\n", " - b2.m.val_SI * (b2.h.val_SI - b3.h.val_SI)\n", " )\n", "\n", " def cop_ude_dependents(ude):\n", " e1, b2, b3 = ude.conns\n", " return [e1.E, b2.m, b2.h, b3.h]\n", "\n", " if value is None:\n", " ude = self.nw.user_defined_eq.get(\"cop_ude\")\n", " if ude is not None:\n", " ude.is_set = False\n", " else:\n", " ude = self.nw.user_defined_eq.get(\"cop_ude\")\n", " if ude is None:\n", " ude = UserDefinedEquation(\n", " \"cop_ude\",\n", " cop_ude,\n", " cop_ude_dependents,\n", " conns=self.nw.get_conn([\"e1\", \"b2\", \"b3\"]),\n", " params={\"cop\": value}\n", " )\n", " self.nw.add_ude(ude)\n", " ude.is_set = True\n", " ude.params[\"cop\"] = value\n", "\n", " def set_all_hx_dp(self, value):\n", " self.nw.get_comp(\"internal heat exchanger\").set_attr(dp1=value, dp2=value)\n", " self.nw.get_comp(\"evaporator low\").set_attr(dp2=value)\n", " self.nw.get_comp(\"condenser high\").set_attr(dp=value)\n", "\n", " def _create_network(self):\n", " super()._create_network()\n", "\n", " self.nw.units.set_defaults(\n", " temperature=\"degC\",\n", " pressure=\"bar\",\n", " pressure_difference=\"bar\",\n", " power=\"kW\",\n", " heat=\"kW\"\n", " )\n", "\n", " cc_low = CycleCloser(\"cc low\")\n", " compressor_low = Compressor(\"compressor low\")\n", " condenser_low = SectionedHeatExchanger(\"internal heat exchanger\")\n", " valve_low = Valve(\"valve low\")\n", " evaporator_low = SectionedHeatExchanger(\"evaporator low\")\n", " he_in = Source(\"source\")\n", " he_out = Sink(\"sink\")\n", "\n", " cc_high = CycleCloser(\"cc high\")\n", " compressor_high = Compressor(\"compressor high\")\n", " condenser_high = SimpleHeatExchanger(\"condenser high\")\n", " valve_high = Valve(\"valve high\")\n", "\n", " a1 = Connection(cc_low, \"out1\", compressor_low, \"in1\", label=\"a1\")\n", " a2 = Connection(compressor_low, \"out1\", condenser_low, \"in1\", label=\"a2\")\n", " a3 = Connection(condenser_low, \"out1\", valve_low, \"in1\", label=\"a3\")\n", " a4 = Connection(valve_low, \"out1\", evaporator_low, \"in2\", label=\"a4\")\n", " a5 = Connection(evaporator_low, \"out2\", cc_low, \"in1\", label=\"a5\")\n", "\n", " b1 = Connection(cc_high, \"out1\", compressor_high, \"in1\", label=\"b1\")\n", " b2 = Connection(compressor_high, \"out1\", condenser_high, \"in1\", label=\"b2\")\n", " b3 = Connection(condenser_high, \"out1\", valve_high, \"in1\", label=\"b3\")\n", " b4 = Connection(valve_high, \"out1\", condenser_low, \"in2\", label=\"b4\")\n", " b5 = Connection(condenser_low, \"out2\", cc_high, \"in1\", label=\"b5\")\n", "\n", " c1 = Connection(he_in, \"out1\", evaporator_low, \"in1\", label=\"c1\")\n", " c2 = Connection(evaporator_low, \"out1\", he_out, \"in1\", label=\"c2\")\n", "\n", " self.nw.add_conns(a1, a2, a3, a4, a5, b1, b2, b3, b4, b5, c1, c2)\n", "\n", " grid = PowerSource(\"grid\")\n", " distribution = PowerBus(\"distribution\", num_in=1, num_out=2)\n", " motor1 = Motor(\"motor1\")\n", " motor2 = Motor(\"motor2\")\n", "\n", " e1 = PowerConnection(grid, \"power\", distribution, \"power_in1\", label=\"e1\")\n", " e2 = PowerConnection(distribution, \"power_out1\", motor1, \"power_in\", label=\"e2\")\n", " e3 = PowerConnection(motor1, \"power_out\", compressor_low, \"power\", label=\"e3\")\n", "\n", " e4 = PowerConnection(distribution, \"power_out2\", motor2, \"power_in\", label=\"e4\")\n", " e5 = PowerConnection(motor2, \"power_out\", compressor_high, \"power\", label=\"e5\")\n", "\n", " self.nw.add_conns(e1, e2, e3, e4, e5)\n", "\n", " a1.set_attr(fluid={\"R600a\": 1}, T_dew=0, td_dew=10, m=1)\n", " a3.set_attr(T_bubble=55, td_bubble=5)\n", " b1.set_attr(fluid={\"R600a\": 1}, td_dew=10)\n", " b3.set_attr(T_bubble=100, td_bubble=2)\n", "\n", " condenser_high.set_attr(dp=0)\n", " condenser_low.set_attr(dp1=0, dp2=0, td_pinch=5)\n", " evaporator_low.set_attr(dp1=0, dp2=0)\n", " c1.set_attr(p=1, T=20, fluid={\"air\": 1})\n", " c2.set_attr(T=10)\n", "\n", " compressor_low.set_attr(eta_s=0.8)\n", " compressor_high.set_attr(eta_s=0.8)\n", " motor1.set_attr(eta=0.98)\n", " motor2.set_attr(eta=0.98)\n", "\n", " self.nw.solve(\"design\")\n", " self.nw.print_results()\n", " self.nw.save(self._stable_solution)\n", "\n", " def solve_model(self, **kwargs):\n", " self.solve_model_design(**kwargs)" ] }, { "cell_type": "markdown", "id": "58ee19a8", "metadata": {}, "source": [ "#### Create an instance\n", "\n", "Instantiating `HeatPumpModel` builds the network, runs the initial design-point\n", "solve, and stores a stable solution for automatic recovery. After this the\n", "model is ready for all operations without any further setup." ] }, { "cell_type": "code", "execution_count": null, "id": "25cf25ba", "metadata": {}, "outputs": [], "source": [ "model = HeatPumpModel()" ] }, { "cell_type": "markdown", "id": "05fe9dc4", "metadata": {}, "source": [ "#### Plotting\n", "\n", "`ModelTemplate` provides built-in T-s, log(p,h) and Q-T diagram methods.\n", "Pass the label of the connection at the start of the cycle, or the component\n", "label for Q-T diagrams." ] }, { "cell_type": "code", "execution_count": null, "id": "48038ba6", "metadata": {}, "outputs": [], "source": [ "model.plot_logph_diagram_matplotlib(\"a1\", save_dir=\".\")\n", "model.plot_Ts_diagram_matplotlib(\"b1\", save_dir=\".\")\n", "model.plot_QT_diagram_matplotlib(\"internal heat exchanger\", save_dir=\".\")\n", "model.plot_QT_diagram_matplotlib(\"evaporator low\", save_dir=\".\")" ] }, { "cell_type": "markdown", "id": "2c7adc7b", "metadata": {}, "source": [ "#### Sensitivity analysis\n", "\n", "`sensitivity_analysis` sweeps a table of input combinations and returns the\n", "results as a `DataFrame`. The evaluation order is automatically minimised to\n", "reduce step sizes between consecutive solves." ] }, { "cell_type": "code", "execution_count": null, "id": "58151e2f", "metadata": {}, "outputs": [], "source": [ "model.nw.iterinfo = False\n", "param_dict = {\n", " \"ihx pinch\": [3, 3, 20, 5, 0.2, 10],\n", " \"heat\": [-100, -200, -100, -800, -100, -200],\n", " \"higher cycle compressor efficiency\": [0.7, 0.7, 0.4, 0.9, 0.9, 0.7]\n", "}\n", "model.set_parameters(**{\"lower cycle mass flow\": None})\n", "result = model.sensitivity_analysis(\n", " param_dict=param_dict,\n", " result_param_list=[\"cop\", \"lower cycle condensation temperature\"]\n", ")\n", "result.style" ] }, { "cell_type": "markdown", "id": "ea78cd6c", "metadata": {}, "source": [ "#### Setting a COP target\n", "\n", "The `\"cop\"` entry uses the combined `{\"get\": ..., \"set\": ...}` form. Writing it\n", "activates a `UserDefinedEquation` that constrains the solution to the requested\n", "COP. For that the lower cycle condensation temperature is set to `None` as it\n", "is now to be calculated by the imposed COP value." ] }, { "cell_type": "code", "execution_count": null, "id": "c3208d39", "metadata": {}, "outputs": [], "source": [ "model.solve_model_design(\n", " **{\"cop\": 2.25, \"lower cycle condensation temperature\": None}\n", ")\n", "model.get_parameter(\"lower cycle condensation temperature\")" ] }, { "cell_type": "markdown", "id": "9a425d97", "metadata": {}, "source": [ "#### Bulk pressure-drop assignment\n", "\n", "`\"heat_exchangers_all_dp\"` uses the write-only `{\"set\": callable}` form. The\n", "callable receives the requested value and distributes it to every working-fluid\n", "side of every heat exchanger: `dp1` and `dp2` on the internal heat exchanger\n", "(both sides carry refrigerant), `dp2` on the evaporator (the air side `dp1` is\n", "left untouched), and `dp` on the high-side condenser." ] }, { "cell_type": "code", "execution_count": null, "id": "c9e81dcb", "metadata": {}, "outputs": [], "source": [ "model.set_parameters(**{\"heat_exchangers_all_dp\": 0.05})\n", "model.solve_model_design()\n", "model.get_parameter(\"lower cycle condensation temperature\")" ] }, { "cell_type": "markdown", "id": "41c2cc8a", "metadata": {}, "source": [ "#### Optimization\n", "\n", "`model.optimize` wraps `OptimizationProblem` and a pymoo algorithm into a\n", "single call. It returns a `DataFrame` of all evaluated individuals so results\n", "can be inspected directly.\n", "\n", "For another walkthrough of the optimization API including constraints see\n", "{ref}`tutorial_optimization_label`." ] }, { "cell_type": "code", "execution_count": null, "id": "b8a1b019", "metadata": {}, "outputs": [], "source": [ "from pymoo.algorithms.soo.nonconvex.pso import PSO\n", "\n", "model.set_parameters(**{\"heat\": -1000, \"ihx pinch\": 7.5, \"cop\": None})\n", "\n", "variables = {\n", " \"lower cycle condensation temperature\": {\"min\": 35, \"max\": 75},\n", "}\n", "result_param_list = [\"ihx pinch\"] # just to show an example\n", "\n", "algorithm = PSO(pop_size=10)\n", "\n", "result = model.optimize(\n", " algorithm=algorithm,\n", " termination=(\"n_gen\", 5),\n", " variables=variables,\n", " objective=[\"cop\"],\n", " minimize_flags=[False],\n", " kpi=result_param_list\n", ")\n", "result.tail(10).style" ] }, { "cell_type": "code", "execution_count": null, "id": "43cd9503", "metadata": {}, "outputs": [], "source": [ "result.loc[result[\"cop\"].idxmax()]" ] }, { "cell_type": "code", "execution_count": null, "id": "03a09bd8", "metadata": {}, "outputs": [], "source": [] } ], "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 }