Workflow integration using model classes¶
ModelTemplate is a base class
for building reusable, self-contained TESPy models. Subclassing it gives you a
consistent interface for parameter access, solving, sensitivity sweeps,
optimization and diagram plotting - without having to rewrite that plumbing for
every model.
Note
This is a new feature. ModelTemplate is actively developed and will
receive additional capabilities and refinements in future releases. If you have
ideas, encounter unexpected behavior, or have built something useful on top of
ModelTemplate that others could benefit from, we warmly invite you to open an
issue or start a discussion in the
TESPy issue tracker. Contributions and
pull requests are equally welcome. See the
contribution guide
for details.
Background¶
Subclassing ModelTemplate¶
Every concrete model overrides two methods:
_parameter_lookup- returns adictmapping human-readable parameter names to their location in the network._create_network- assembles the TESPy network and produces a stable initial solution. Always callsuper()._create_network()first.
For use with the optimization API, also implement solve_model to delegate
to solve_model_design or solve_model_offdesign. Alternatively, you can wrap
solve model around your custom implementation logic for solving a model
including arbitrary pre- or postprocessings.
Parameter lookup¶
_parameter_lookup maps each name to one of four entry forms:
Entry |
Meaning |
|---|---|
|
Read and write a connection attribute |
|
Read and write a component attribute |
|
Read-only derived quantity; callable takes no arguments |
|
Write-only or read/write custom quantity |
Solving¶
solve_model_design(**kwargs)- sets parameters and runs a design-mode solve, recovering from corruption via the last stable solution stored inself._stable_solution.solve_model_offdesign(**kwargs)- off-design mode; requiresself._design_pathto be set.solve_model(**kwargs)- not implemented in the base class; implement it in the subclass to make the model callable fromOptimizationProblem.
Reading and writing parameters¶
get_parameter(name) and set_parameters(**kwargs) use the names defined in
_parameter_lookup. Multiple parameters targeting the same network object are
batched into a single set_attr call.
Sensitivity analysis¶
sensitivity_analysis(param_dict, result_param_list) sweeps a table of input
combinations and returns a DataFrame. All lists in param_dict must have the
same length - each row is one simulation. The evaluation order is minimized
automatically with a greedy nearest-neighbor heuristic to reduce step sizes
between consecutive solves.
Optimization¶
optimize(algorithm, termination, variables, ...) wraps OptimizationProblem
and a pymoo algorithm into a single call, returning a DataFrame of all
evaluated individuals. For a detailed walkthrough of the optimization API see
Thermal Power Plant Efficiency Optimization.
Plotting¶
Three diagram methods are available on every instance:
plot_Ts_diagram_matplotlib(connection_label)- T-s diagram for a cycleplot_logph_diagram_matplotlib(connection_label)- log(p,h) diagram for a cycleplot_QT_diagram_matplotlib(component_label)- Q-T diagram for a heat exchanger
The connection label specified for the cycle diagrams can be any connection within the respective (sub)cycles.
All three accept an optional ax to draw into an existing matplotlib axes and
save_dir to write the figure to a file.
Examples¶
ORC example¶
This example builds an Organic Rankine Cycle with an internal recuperator. The
_parameter_lookup uses the ["Connections", ...] and ["Components", ...]
forms. solve_model delegates directly to solve_model_design.
from tespy.components import (
CycleCloser, Generator, Motor, MovingBoundaryHeatExchanger,
PowerBus, PowerSink, Pump, Sink, Source, Turbine,
)
from tespy.connections import Connection, PowerConnection
from tespy.models import ModelTemplate
class ORCModel(ModelTemplate):
def _parameter_lookup(self) -> dict:
return {
"evaporator_pinch": ["Components", "evaporator", "td_pinch"],
"condenser_pinch": ["Components", "condenser", "td_pinch"],
"turbine__efficiency": ["Components", "turbine", "eta_s"],
"net_power": ["Connections", "e5", "E"],
"T_source": ["Connections", "a1", "T"],
"T_outflow": ["Connections", "a3", "T"],
"m_source": ["Connections", "a1", "m"]
}
def solve_model(self, **kwargs):
self.solve_model_design(**kwargs)
def _create_network(self) -> None:
super()._create_network()
self.nw.units.set_defaults(
temperature="degC",
pressure="bar",
pressure_difference="bar",
power="kW",
heat="kW"
)
turbine = Turbine("turbine")
recuperator = MovingBoundaryHeatExchanger("recuperator")
condenser = MovingBoundaryHeatExchanger("condenser")
pump = Pump("pump")
preheater = MovingBoundaryHeatExchanger("preheater")
evaporator = MovingBoundaryHeatExchanger("evaporator")
cc = CycleCloser("cc")
heat_source = Source("heat source")
heat_outflow = Sink("heat outflow")
air_source = Source("air source")
air_sink = Sink("air sink")
a1 = Connection(heat_source, "out1", evaporator, "in1", label="a1")
a2 = Connection(evaporator, "out1", preheater, "in1", label="a2")
a3 = Connection(preheater, "out1", heat_outflow, "in1", label="a3")
b1 = Connection(cc, "out1", turbine, "in1", label="b1")
b2 = Connection(turbine, "out1", recuperator, "in1", label="b2")
b3 = Connection(recuperator, "out1", condenser, "in1", label="b3")
b4 = Connection(condenser, "out1", pump, "in1", label="b4")
b5 = Connection(pump, "out1", recuperator, "in2", label="b5")
b6 = Connection(recuperator, "out2", preheater, "in2", label="b6")
b7 = Connection(preheater, "out2", evaporator, "in2", label="b7")
b8 = Connection(evaporator, "out2", cc, "in1", label="b8")
c1 = Connection(air_source, "out1", condenser, "in2", label="c1")
c2 = Connection(condenser, "out2", air_sink, "in1", label="c2")
self.nw.add_conns(a1, a2, a3, b1, b2, b3, b4, b5, b6, b7, b8, c1, c2)
generator = Generator("generator")
motor = Motor("motor")
power_bus = PowerBus("bus", num_in=1, num_out=2)
grid = PowerSink("grid")
e1 = PowerConnection(turbine, "power", generator, "power_in", label="e1")
e2 = PowerConnection(generator, "power_out", power_bus, "power_in1", label="e2")
e3 = PowerConnection(power_bus, "power_out1", motor, "power_in", label="e3")
e4 = PowerConnection(motor, "power_out", pump, "power", label="e4")
e5 = PowerConnection(power_bus, "power_out2", grid, "power", label="e5")
self.nw.add_conns(e1, e2, e3, e4, e5)
generator.set_attr(eta=0.98)
motor.set_attr(eta=0.98)
a1.set_attr(fluid={"air": 1}, T=200, p=1, m=10)
a2.set_attr(T=155)
b1.set_attr(fluid={"Isopentane": 1}, x=1, T=150)
b3.set_attr(td_dew=10, T_dew=30)
b4.set_attr(td_bubble=5)
b7.set_attr(td_bubble=5)
c1.set_attr(fluid={"air": 1}, T=10, p=1)
c2.set_attr(T=20)
recuperator.set_attr(dp1=0, dp2=0)
condenser.set_attr(dp1=0, dp2=0)
preheater.set_attr(dp1=0, dp2=0)
evaporator.set_attr(dp1=0, dp2=0)
turbine.set_attr(eta_s=0.8)
pump.set_attr(eta_s=0.7)
self.nw.solve("design")
b3.set_attr(T_dew=None)
condenser.set_attr(td_pinch=5)
a2.set_attr(T=None)
evaporator.set_attr(td_pinch=10)
self.nw.solve("design")
self._stable_solution = "stable_solution.json"
self.nw.save(self._stable_solution)
Create an instance¶
Instantiating the model triggers _create_network and runs the initial solve.
model = ORCModel()
iter | residual | progress | massflow | pressure | enthalpy | fluid | component
-------+------------+------------+------------+------------+------------+------------+------------
1 | 3.19e+06 | 0 % | 7.81e+01 | 0.00e+00 | 3.13e+05 | 0.00e+00 | 2.81e+05
2 | 5.21e+04 | 14 % | 1.24e-09 | 0.00e+00 | 2.06e+04 | 0.00e+00 | 3.98e+04
3 | 1.14e-03 | 99 % | 1.01e-14 | 0.00e+00 | 1.21e-08 | 0.00e+00 | 1.03e-03
4 | 5.29e-10 | 100 % | 1.01e-14 | 0.00e+00 | 4.43e-11 | 0.00e+00 | 2.42e-11
5 | 4.12e-10 | 100 % | 1.01e-14 | 0.00e+00 | 4.73e-11 | 0.00e+00 | 3.13e-11
Total iterations: 5, Calculation time: 0.01 s, Iterations per second: 724.38
iter | residual | progress | massflow | pressure | enthalpy | fluid | component
-------+------------+------------+------------+------------+------------+------------+------------
1 | 1.86e+05 | 8 % | 2.03e+00 | 2.03e+04 | 2.26e+04 | 0.00e+00 | 2.62e+05
2 | 2.67e+03 | 28 % | 3.86e-02 | 1.41e+03 | 1.23e+03 | 0.00e+00 | 3.29e+02
3 | 1.61e+01 | 53 % | 1.38e-05 | 8.73e+00 | 7.54e+00 | 0.00e+00 | 1.67e+00
4 | 5.80e-04 | 100 % | 2.86e-09 | 3.20e-04 | 2.77e-04 | 0.00e+00 | 7.08e-05
5 | 1.52e-09 | 100 % | 2.14e-13 | 2.81e-11 | 1.52e-09 | 0.00e+00 | 5.84e-09
Total iterations: 5, Calculation time: 0.03 s, Iterations per second: 166.04
Resolve with updated parameters¶
Use solve_model_design to re-run the model with new input values.
model.solve_model(
**{"T_source": 225}
)
iter | residual | progress | massflow | pressure | enthalpy | fluid | component
-------+------------+------------+------------+------------+------------+------------+------------
1 | 3.20e+05 | 5 % | 4.77e+01 | 8.03e-12 | 3.36e+04 | 0.00e+00 | 4.24e+05
2 | 1.47e-02 | 87 % | 3.24e-04 | 4.99e-13 | 4.44e-01 | 0.00e+00 | 1.08e+00
3 | 4.13e-09 | 100 % | 3.97e-12 | 1.82e-10 | 5.24e-09 | 0.00e+00 | 1.22e-08
4 | 5.30e-09 | 100 % | 4.83e-14 | 8.53e-12 | 6.43e-10 | 0.00e+00 | 6.29e-10
Total iterations: 4, Calculation time: 0.02 s, Iterations per second: 181.58
Read results¶
Parameters are read back by name via get_parameter. Diagrams can be produced
directly from the model instance.
model.get_parameter("T_outflow")
72.41164019435348
fig, ax = model.plot_Ts_diagram_matplotlib("b1")
Heat pump with two refrigerant cycles¶
This example has a low-temperature and a high-temperature refrigerant cycle
coupled through an internal heat exchanger. It showcases all four
_parameter_lookup entry forms:
["Components", ...]and["Connections", ...]for standard network attributes,{"get": callable}for a read-only derived quantity - here the computed COP,{"get": ..., "set": ...}for a read/write custom quantity - here a COP target that reads back the current target value and, when written, activates aUserDefinedEquationthat enforces it. PassingNonedeactivates the constraint and restores the degree of freedom,{"set": callable}for a write-only parameter with arbitrary setter logic - here used to assign a pressure drop to all heat exchanger working-fluid sides in one call.
See the API docs of
ModelTemplate for more
information.
from tespy.components import Compressor
from tespy.components import CycleCloser
from tespy.components import Motor
from tespy.components import PowerBus
from tespy.components import PowerSource
from tespy.components import SectionedHeatExchanger
from tespy.components import SimpleHeatExchanger
from tespy.components import Sink
from tespy.components import Source
from tespy.components import Valve
from tespy.connections import Connection
from tespy.connections import PowerConnection
from tespy.models import ModelTemplate
from tespy.tools import UserDefinedEquation
class HeatPumpModel(ModelTemplate):
def _parameter_lookup(self):
return {
"ihx pinch": ["Components", "internal heat exchanger", "td_pinch"],
"heat": ["Components", "condenser high", "Q"],
"power": ["Connections", "e1", "E"],
"higher cycle compressor efficiency": ["Components", "compressor high", "eta_s"],
"lower cycle mass flow": ["Connections", "a1", "m"],
"lower cycle condensation temperature": ["Connections", "a3", "T_bubble"],
"cop": {"get": self.calc_cop, "set": self.set_cop},
"heat_exchangers_all_dp": {"set": self.set_all_hx_dp},
}
def calc_cop(self):
return (
abs(self.get_parameter("heat"))
/ self.get_parameter("power")
)
def set_cop(self, value):
def cop_ude(ude):
e1, b2, b3 = ude.conns
return (
ude.params["cop"] * e1.E.val_SI
- b2.m.val_SI * (b2.h.val_SI - b3.h.val_SI)
)
def cop_ude_dependents(ude):
e1, b2, b3 = ude.conns
return [e1.E, b2.m, b2.h, b3.h]
if value is None:
ude = self.nw.user_defined_eq.get("cop_ude")
if ude is not None:
ude.is_set = False
else:
ude = self.nw.user_defined_eq.get("cop_ude")
if ude is None:
ude = UserDefinedEquation(
"cop_ude",
cop_ude,
cop_ude_dependents,
conns=self.nw.get_conn(["e1", "b2", "b3"]),
params={"cop": value}
)
self.nw.add_ude(ude)
ude.is_set = True
ude.params["cop"] = value
def set_all_hx_dp(self, value):
self.nw.get_comp("internal heat exchanger").set_attr(dp1=value, dp2=value)
self.nw.get_comp("evaporator low").set_attr(dp2=value)
self.nw.get_comp("condenser high").set_attr(dp=value)
def _create_network(self):
super()._create_network()
self.nw.units.set_defaults(
temperature="degC",
pressure="bar",
pressure_difference="bar",
power="kW",
heat="kW"
)
cc_low = CycleCloser("cc low")
compressor_low = Compressor("compressor low")
condenser_low = SectionedHeatExchanger("internal heat exchanger")
valve_low = Valve("valve low")
evaporator_low = SectionedHeatExchanger("evaporator low")
he_in = Source("source")
he_out = Sink("sink")
cc_high = CycleCloser("cc high")
compressor_high = Compressor("compressor high")
condenser_high = SimpleHeatExchanger("condenser high")
valve_high = Valve("valve high")
a1 = Connection(cc_low, "out1", compressor_low, "in1", label="a1")
a2 = Connection(compressor_low, "out1", condenser_low, "in1", label="a2")
a3 = Connection(condenser_low, "out1", valve_low, "in1", label="a3")
a4 = Connection(valve_low, "out1", evaporator_low, "in2", label="a4")
a5 = Connection(evaporator_low, "out2", cc_low, "in1", label="a5")
b1 = Connection(cc_high, "out1", compressor_high, "in1", label="b1")
b2 = Connection(compressor_high, "out1", condenser_high, "in1", label="b2")
b3 = Connection(condenser_high, "out1", valve_high, "in1", label="b3")
b4 = Connection(valve_high, "out1", condenser_low, "in2", label="b4")
b5 = Connection(condenser_low, "out2", cc_high, "in1", label="b5")
c1 = Connection(he_in, "out1", evaporator_low, "in1", label="c1")
c2 = Connection(evaporator_low, "out1", he_out, "in1", label="c2")
self.nw.add_conns(a1, a2, a3, a4, a5, b1, b2, b3, b4, b5, c1, c2)
grid = PowerSource("grid")
distribution = PowerBus("distribution", num_in=1, num_out=2)
motor1 = Motor("motor1")
motor2 = Motor("motor2")
e1 = PowerConnection(grid, "power", distribution, "power_in1", label="e1")
e2 = PowerConnection(distribution, "power_out1", motor1, "power_in", label="e2")
e3 = PowerConnection(motor1, "power_out", compressor_low, "power", label="e3")
e4 = PowerConnection(distribution, "power_out2", motor2, "power_in", label="e4")
e5 = PowerConnection(motor2, "power_out", compressor_high, "power", label="e5")
self.nw.add_conns(e1, e2, e3, e4, e5)
a1.set_attr(fluid={"R600a": 1}, T_dew=0, td_dew=10, m=1)
a3.set_attr(T_bubble=55, td_bubble=5)
b1.set_attr(fluid={"R600a": 1}, td_dew=10)
b3.set_attr(T_bubble=100, td_bubble=2)
condenser_high.set_attr(dp=0)
condenser_low.set_attr(dp1=0, dp2=0, td_pinch=5)
evaporator_low.set_attr(dp1=0, dp2=0)
c1.set_attr(p=1, T=20, fluid={"air": 1})
c2.set_attr(T=10)
compressor_low.set_attr(eta_s=0.8)
compressor_high.set_attr(eta_s=0.8)
motor1.set_attr(eta=0.98)
motor2.set_attr(eta=0.98)
self.nw.solve("design")
self.nw.print_results()
self.nw.save(self._stable_solution)
def solve_model(self, **kwargs):
self.solve_model_design(**kwargs)
Create an instance¶
Instantiating HeatPumpModel builds the network, runs the initial design-point
solve, and stores a stable solution for automatic recovery. After this the
model is ready for all operations without any further setup.
model = HeatPumpModel()
FutureWarning: Calling Network.save() without a file path returns a JSON string, which is deprecated and will be removed in a future release. Use Network.save(as_dict=True) to get a dict that can be passed directly as design_path or init_path.
iter | residual | progress | massflow | pressure | enthalpy | fluid | component
-------+------------+------------+------------+------------+------------+------------+------------
1 | 9.63e+05 | 0 % | 2.36e+01 | 1.73e+06 | 2.68e+05 | 0.00e+00 | 1.33e+06
2 | 1.02e+06 | 0 % | 2.94e+00 | 3.49e+05 | 6.93e+04 | 0.00e+00 | 1.68e+06
3 | 1.40e+05 | 9 % | 3.97e-01 | 4.52e+04 | 4.40e+03 | 0.00e+00 | 1.44e+05
4 | 1.96e+03 | 30 % | 9.85e-03 | 1.37e+03 | 1.29e+02 | 0.00e+00 | 2.92e+03
5 | 1.84e+00 | 63 % | 7.11e-06 | 1.16e+00 | 1.08e-01 | 0.00e+00 | 2.02e+00
6 | 1.10e-06 | 100 % | 4.22e-12 | 8.26e-07 | 6.39e-08 | 0.00e+00 | 1.14e-06
7 | 1.71e-09 | 100 % | 1.04e-15 | 8.73e-10 | 7.14e-10 | 0.00e+00 | 1.07e-09
Total iterations: 7, Calculation time: 0.08 s, Iterations per second: 91.18
##### RESULTS (CycleCloser) #####
+---------+------------------+-------------------+
| | mass_deviation | fluid_deviation |
|---------+------------------+-------------------|
| cc high | 0.00e+00 | 0.00e+00 |
| cc low | 0.00e+00 | 0.00e+00 |
+---------+------------------+-------------------+
##### RESULTS (Compressor) #####
+-----------------+----------+----------+-----------+----------+
| | P | pr | dp | eta_s |
|-----------------+----------+----------+-----------+----------|
| compressor high | 1.09e+02 | 3.29e+00 | -1.38e+01 | 8.00e-01 |
| compressor low | 7.89e+01 | 4.92e+00 | -6.16e+00 | 8.00e-01 |
+-----------------+----------+----------+-----------+----------+
##### RESULTS (SimpleHeatExchanger) #####
+----------------+-----------+----------+----------+----------+
| | Q | pr | dp | zeta |
|----------------+-----------+----------+----------+----------|
| condenser high | -4.37e+02 | 1.00e+00 | 0.00e+00 | 0.00e+00 |
+----------------+-----------+----------+----------+----------+
##### RESULTS (SectionedHeatExchanger) #####
+-------------------------+-----------+----------+----------+----------+----------+-----------+----------+----------+----------+----------+----------+----------+-----------+------------+-----------+----------+------------+
| | Q | kA | td_log | ttd_u | ttd_l | ttd_min | pr1 | pr2 | dp1 | dp2 | zeta1 | zeta2 | eff_hot | eff_cold | eff_max | UA | td_pinch |
|-------------------------+-----------+----------+----------+----------+----------+-----------+----------+----------+----------+----------+----------+----------+-----------+------------+-----------+----------+------------|
| evaporator low | -2.49e+02 | 2.49e+04 | 1.00e+01 | 1.00e+01 | 1.00e+01 | 1.00e+01 | 1.00e+00 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 5.00e-01 | 9.37e-01 | 9.37e-01 | 1.76e+04 | 1.00e+01 |
| internal heat exchanger | -3.28e+02 | 4.35e+04 | 7.53e+00 | 1.08e+01 | 5.00e+00 | 5.00e+00 | 1.00e+00 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 9.62e-01 | 8.90e-01 | 9.62e-01 | 3.42e+04 | 5.00e+00 |
+-------------------------+-----------+----------+----------+----------+----------+-----------+----------+----------+----------+----------+----------+----------+-----------+------------+-----------+----------+------------+
##### RESULTS (Motor) #####
+--------+----------+---------------+
| | eta | delta_power |
|--------+----------+---------------|
| motor1 | 9.80e-01 | 1.61e+00 |
| motor2 | 9.80e-01 | 2.23e+00 |
+--------+----------+---------------+
##### RESULTS (Valve) #####
+------------+----------+----------+----------+----------+
| | pr | dp | zeta | Kv |
|------------+----------+----------+----------+----------|
| valve high | 3.04e-01 | 1.38e+01 | 2.78e+07 | 2.75e+00 |
| valve low | 2.03e-01 | 6.16e+00 | 1.81e+07 | 2.02e+00 |
+------------+----------+----------+----------+----------+
##### RESULTS (Connection) #####
+----+-----------+-----------+-----------+------------+------------+---------+
| | m | p | h | T | x | phase |
|----+-----------+-----------+-----------+------------+------------+---------|
| a1 | 1.000e+00 | 1.570e+00 | 5.707e+05 | 1.000e+01 | 1.000e+00 | g |
| a2 | 1.000e+00 | 7.730e+00 | 6.496e+05 | 6.581e+01 | 1.000e+00 | g |
| a3 | 1.000e+00 | 7.730e+00 | 3.221e+05 | 5.000e+01 | 0.000e+00 | l |
| a4 | 1.000e+00 | 1.570e+00 | 3.221e+05 | -2.274e-13 | 3.445e-01 | tp |
| a5 | 1.000e+00 | 1.570e+00 | 5.707e+05 | 1.000e+01 | 1.000e+00 | g |
| b1 | 1.874e+00 | 6.044e+00 | 6.341e+05 | 5.500e+01 | 1.000e+00 | g |
| b2 | 1.874e+00 | 1.987e+01 | 6.923e+05 | 1.054e+02 | 1.000e+00 | g |
| b3 | 1.874e+00 | 1.987e+01 | 4.593e+05 | 9.800e+01 | 0.000e+00 | l |
| b4 | 1.874e+00 | 6.044e+00 | 4.593e+05 | 4.500e+01 | 4.921e-01 | tp |
| b5 | 1.874e+00 | 6.044e+00 | 6.341e+05 | 5.500e+01 | 1.000e+00 | g |
| c1 | 2.471e+01 | 1.000e+00 | 4.194e+05 | 2.000e+01 | -1.000e+00 | g |
| c2 | 2.471e+01 | 1.000e+00 | 4.093e+05 | 1.000e+01 | -1.000e+00 | g |
+----+-----------+-----------+-----------+------------+------------+---------+
##### RESULTS (PowerConnection) #####
+----+-----------+
| | E |
|----+-----------|
| e1 | 1.919e+02 |
| e2 | 8.049e+01 |
| e3 | 7.888e+01 |
| e4 | 1.114e+02 |
| e5 | 1.092e+02 |
+----+-----------+
Plotting¶
ModelTemplate provides built-in T-s, log(p,h) and Q-T diagram methods.
Pass the label of the connection at the start of the cycle, or the component
label for Q-T diagrams.
model.plot_logph_diagram_matplotlib("a1", save_dir=".")
model.plot_Ts_diagram_matplotlib("b1", save_dir=".")
model.plot_QT_diagram_matplotlib("internal heat exchanger", save_dir=".")
model.plot_QT_diagram_matplotlib("evaporator low", save_dir=".")
(<Figure size 1000x600 with 1 Axes>, <Axes: >)
Sensitivity analysis¶
sensitivity_analysis sweeps a table of input combinations and returns the
results as a DataFrame. The evaluation order is automatically minimised to
reduce step sizes between consecutive solves.
model.nw.iterinfo = False
param_dict = {
"ihx pinch": [3, 3, 20, 5, 0.2, 10],
"heat": [-100, -200, -100, -800, -100, -200],
"higher cycle compressor efficiency": [0.7, 0.7, 0.4, 0.9, 0.9, 0.7]
}
model.set_parameters(**{"lower cycle mass flow": None})
result = model.sensitivity_analysis(
param_dict=param_dict,
result_param_list=["cop", "lower cycle condensation temperature"]
)
result.style
| ihx pinch | heat | higher cycle compressor efficiency | cop | lower cycle condensation temperature | |
|---|---|---|---|---|---|
| 0 | 3.000000 | -100.000000 | 0.700000 | 2.219955 | 55.000000 |
| 1 | 3.000000 | -200.000000 | 0.700000 | 2.219955 | 55.000000 |
| 2 | 20.000000 | -100.000000 | 0.400000 | 1.574370 | 55.000000 |
| 3 | 5.000000 | -800.000000 | 0.900000 | 2.365062 | 55.000000 |
| 4 | 0.200000 | -100.000000 | 0.900000 | 2.475170 | 55.000000 |
| 5 | 10.000000 | -200.000000 | 0.700000 | 2.073391 | 55.000000 |
Setting a COP target¶
The "cop" entry uses the combined {"get": ..., "set": ...} form. Writing it
activates a UserDefinedEquation that constrains the solution to the requested
COP. For that the lower cycle condensation temperature is set to None as it
is now to be calculated by the imposed COP value.
model.solve_model_design(
**{"cop": 2.25, "lower cycle condensation temperature": None}
)
model.get_parameter("lower cycle condensation temperature")
36.770431380246805
Bulk pressure-drop assignment¶
"heat_exchangers_all_dp" uses the write-only {"set": callable} form. The
callable receives the requested value and distributes it to every working-fluid
side of every heat exchanger: dp1 and dp2 on the internal heat exchanger
(both sides carry refrigerant), dp2 on the evaporator (the air side dp1 is
left untouched), and dp on the high-side condenser.
model.set_parameters(**{"heat_exchangers_all_dp": 0.05})
model.solve_model_design()
model.get_parameter("lower cycle condensation temperature")
38.46665624138626
Optimization¶
model.optimize wraps OptimizationProblem and a pymoo algorithm into a
single call. It returns a DataFrame of all evaluated individuals so results
can be inspected directly.
For another walkthrough of the optimization API including constraints see Thermal Power Plant Efficiency Optimization.
from pymoo.algorithms.soo.nonconvex.pso import PSO
model.set_parameters(**{"heat": -1000, "ihx pinch": 7.5, "cop": None})
variables = {
"lower cycle condensation temperature": {"min": 35, "max": 75},
}
result_param_list = ["ihx pinch"] # just to show an example
algorithm = PSO(pop_size=10)
result = model.optimize(
algorithm=algorithm,
termination=("n_gen", 5),
variables=variables,
objective=["cop"],
minimize_flags=[False],
kpi=result_param_list
)
result.tail(10).style
| lower cycle condensation temperature | cop | ihx pinch | |
|---|---|---|---|
| 40 | 62.104316 | 2.303964 | 7.500000 |
| 41 | 58.765915 | 2.304766 | 7.500000 |
| 42 | 64.190151 | 2.300256 | 7.500000 |
| 43 | 60.301360 | 2.305174 | 7.500000 |
| 44 | 58.948999 | 2.304884 | 7.500000 |
| 45 | 60.323793 | 2.305170 | 7.500000 |
| 46 | 59.280271 | 2.305049 | 7.500000 |
| 47 | 57.553818 | 2.303524 | 7.500000 |
| 48 | 62.413098 | 2.303572 | 7.500000 |
| 49 | 61.469969 | 2.304600 | 7.500000 |
result.loc[result["cop"].idxmax()]
lower cycle condensation temperature 59.841641
cop 2.305190
ihx pinch 7.500000
Name: 23, dtype: float64