From 3b2fb187f2f151308bc8ec11e8f43c6fd3665ae3 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 23 Nov 2025 14:29:02 +0100 Subject: [PATCH 01/25] Adding first elements of pinch analysis using pina 0.1.1 --- src/tespy/tools/pinch_analysis.py | 95 +++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/tespy/tools/pinch_analysis.py diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py new file mode 100644 index 000000000..e3573bc12 --- /dev/null +++ b/src/tespy/tools/pinch_analysis.py @@ -0,0 +1,95 @@ +# using pina 0.1.1 for pinch related tasks +from pina import PinchAnalyzer, make_stream + +# Useful literature: +# Arpagaus 2019 Hochtemperatur-Wärmepumpen (p.99) +# Kemp 2006 (p.20) + + +class PinchTools(): + + def __init__(self): + self.min_dT = None + self.temp_shift = None + self.streams = [] + self.analyzer = None + + # including the general functions of pina + # adding cold streams to the used streams, colsd streams have to be heated and form the cold composite curvve (cold CC) + def add_cold_streams_manually(self, enthalpy_difference:float, T_inlet:float, T_outlet:float): + # check if the stream has a positive enthalpy difference, pina needs positive sign for hot streams, way to check user + if enthalpy_difference < 0: + print("Got positive enthaply difference, expected negative enthalpy for cold streams. stream not added") + else: + self.streams.append(make_stream(enthalpy_difference,T_inlet,T_outlet)) + + + # adding cold streams to the used streams, colsd streams have to be heated and form the cold composite curvve (hot CC) + def add_hot_streams_manually(self, enthalpy_difference:float, T_inlet:float, T_outlet:float): + # check if the stream has a negative enthalpy difference, pina needs negative sign for cold streams, way to check user + if enthalpy_difference > 0: + print("Got positive enthaply difference, expected negative enthalpy for cold streams. stream not added") + else: + self.streams.append(make_stream(enthalpy_difference,T_inlet,T_outlet)) + + + # setting the value for shifting the composit curces (CCs) + def set_minimum_temperature_difference(self, minimum_temperature_difference:float): + # minimum temperature difference of streams + self.min_dT = minimum_temperature_difference + # the hot and cold composit curve (CC) are shifted by half the minimum temperature difference to meet at the pinch point + self.temp_shift = self.min_dT / 2 + + + def _create_analyzer(self): + self.analyzer = PinchAnalyzer(self.temp_shift) + self.analyzer.add_streams(*self.streams) # add a way to add streams later + + + def get_hot_cc(self): + if self.analyzer is None: + self._create_analyzer() + [self.hot_cc_data_enthalpy, self.hot_cc_data_temperature] = self.analyzer.hot_composite_curve + + + def get_cold_cc(self): + [self.cold_cc_data_enthalpy, self.cold_cc_data_temperature] = self.analyzer.cold_composite_curve + + + def get_shifted_hot_cc(self): + [self.shifted_hot_cc_data_enthalpy, self.shifted_hot_cc_data_temperature] = self.analyzer.shifted_hot_composite_curve + + + def get_shifted_cold_cc(self): + [self.shifted_cold_cc_data_enthalpy, self.shifted_cold_cc_data_temperature] = self.analyzer.shifted_cold_composite_curve + + + def get_gcc(self): + [self.gcc_data_enthalpy, self.gcc_data_shifted_temperature] = self.analyzer.grand_composite_curve + + + # add: def plot_cc_diagram(self): + + # add: def plot_shifted_cc_diagram(self): + + # add: def plot_gcc_diagram(self): + + # include heat cascades later + + + # adding components from tespy models + + # add: def show_heat_pump_in_gcc(self): + + # get plot data of heat exchangers at heat sink + # as used in e.g. the heat exchanger example plots + + # get plot data of heat exchangers at heat source + + # show (only display) by adding the plot data to the gcc + + # add: def add_heat_exchanger_to_streams(self): + # function to read streams from given heat exchangers, later use to iterate all heat exchangers of network with possibility to exclude streams / heat exchangers + + + # adding automatization \ No newline at end of file From 859a91862f477a6a363f8eaeed27caa0fb303d18 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 30 Nov 2025 12:51:13 +0100 Subject: [PATCH 02/25] ading and correcting comments --- src/tespy/tools/pinch_analysis.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index e3573bc12..6fefe0d6f 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -14,19 +14,19 @@ def __init__(self): self.streams = [] self.analyzer = None - # including the general functions of pina + # including the general functions of pina to expand them later + # adding cold streams to the used streams, colsd streams have to be heated and form the cold composite curvve (cold CC) def add_cold_streams_manually(self, enthalpy_difference:float, T_inlet:float, T_outlet:float): - # check if the stream has a positive enthalpy difference, pina needs positive sign for hot streams, way to check user + # check if the stream has a positive enthalpy flow difference, pina needs positive sign for hot streams, way to check user if enthalpy_difference < 0: print("Got positive enthaply difference, expected negative enthalpy for cold streams. stream not added") else: self.streams.append(make_stream(enthalpy_difference,T_inlet,T_outlet)) - # adding cold streams to the used streams, colsd streams have to be heated and form the cold composite curvve (hot CC) def add_hot_streams_manually(self, enthalpy_difference:float, T_inlet:float, T_outlet:float): - # check if the stream has a negative enthalpy difference, pina needs negative sign for cold streams, way to check user + # check if the stream has a negative enthalpy flow difference, pina needs negative sign for cold streams, way to check user if enthalpy_difference > 0: print("Got positive enthaply difference, expected negative enthalpy for cold streams. stream not added") else: @@ -41,40 +41,49 @@ def set_minimum_temperature_difference(self, minimum_temperature_difference:floa self.temp_shift = self.min_dT / 2 + # create the pinch analyzer from pina def _create_analyzer(self): self.analyzer = PinchAnalyzer(self.temp_shift) self.analyzer.add_streams(*self.streams) # add a way to add streams later + # get datapoints of hot composite curve def get_hot_cc(self): if self.analyzer is None: self._create_analyzer() [self.hot_cc_data_enthalpy, self.hot_cc_data_temperature] = self.analyzer.hot_composite_curve + # get datapoints of cold composite curve def get_cold_cc(self): [self.cold_cc_data_enthalpy, self.cold_cc_data_temperature] = self.analyzer.cold_composite_curve + # get datapoints of shifted hot composite curve def get_shifted_hot_cc(self): [self.shifted_hot_cc_data_enthalpy, self.shifted_hot_cc_data_temperature] = self.analyzer.shifted_hot_composite_curve + # get datapoints of shifted cold composite curve def get_shifted_cold_cc(self): [self.shifted_cold_cc_data_enthalpy, self.shifted_cold_cc_data_temperature] = self.analyzer.shifted_cold_composite_curve + # get datapoints of grand composite curve def get_gcc(self): [self.gcc_data_enthalpy, self.gcc_data_shifted_temperature] = self.analyzer.grand_composite_curve + # add: def get_pinch_point(self): + # needed to check e.g. pinch rules for heat pump integration automatically + # add: def plot_cc_diagram(self): # add: def plot_shifted_cc_diagram(self): # add: def plot_gcc_diagram(self): - # include heat cascades later + # include heat cascades later (for more than one point in time) # adding components from tespy models @@ -90,6 +99,3 @@ def get_gcc(self): # add: def add_heat_exchanger_to_streams(self): # function to read streams from given heat exchangers, later use to iterate all heat exchangers of network with possibility to exclude streams / heat exchangers - - - # adding automatization \ No newline at end of file From 8321941b3a4c629c40480ec38a45cf9bf11dc980 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 30 Nov 2025 13:52:32 +0100 Subject: [PATCH 03/25] adding plotting functions and example for use. Example doubles as test, as there is currently only plotting to check --- src/tespy/tools/pinch_analysis.py | 155 +++++++++++++++++--- tutorial/advanced/example_pinch_analysis.py | 39 +++++ 2 files changed, 172 insertions(+), 22 deletions(-) create mode 100644 tutorial/advanced/example_pinch_analysis.py diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index 6fefe0d6f..d43c95505 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -1,49 +1,66 @@ # using pina 0.1.1 for pinch related tasks from pina import PinchAnalyzer, make_stream +import matplotlib.pyplot as plt # Useful literature: # Arpagaus 2019 Hochtemperatur-Wärmepumpen (p.99) # Kemp 2006 (p.20) +# Walden et al. 2023 (https://doi.org/10.1016/j.apenergy.2023.121933) -class PinchTools(): +class TesypPinchAnalysis(): - def __init__(self): + def __init__(self, label:str): self.min_dT = None self.temp_shift = None self.streams = [] self.analyzer = None + self.label = label + # including the general functions of pina to expand them later + + # setting the value for shifting the composit curces (CCs) + def set_minimum_temperature_difference(self, minimum_temperature_difference:float): + # minimum temperature difference of streams + self.min_dT = minimum_temperature_difference + # the hot and cold composit curve (CC) are shifted by half the minimum temperature difference to meet at the pinch point + self.temp_shift = self.min_dT / 2 + + # adding cold streams to the used streams, colsd streams have to be heated and form the cold composite curvve (cold CC) - def add_cold_streams_manually(self, enthalpy_difference:float, T_inlet:float, T_outlet:float): - # check if the stream has a positive enthalpy flow difference, pina needs positive sign for hot streams, way to check user - if enthalpy_difference < 0: + def add_cold_stream_manually(self, enthalpy_difference:float, T_inlet:float, T_outlet:float): + # check if the stream has a positive enthalpy flow difference, pina needs negative sign for hot streams, way to check user + if enthalpy_difference > 0: print("Got positive enthaply difference, expected negative enthalpy for cold streams. stream not added") else: self.streams.append(make_stream(enthalpy_difference,T_inlet,T_outlet)) + # adding cold streams to the used streams, colsd streams have to be heated and form the cold composite curvve (hot CC) - def add_hot_streams_manually(self, enthalpy_difference:float, T_inlet:float, T_outlet:float): - # check if the stream has a negative enthalpy flow difference, pina needs negative sign for cold streams, way to check user - if enthalpy_difference > 0: + def add_hot_stream_manually(self, enthalpy_difference:float, T_inlet:float, T_outlet:float): + # check if the stream has a negative enthalpy flow difference, pina needs positive sign for cold streams, way to check user + if enthalpy_difference < 0: print("Got positive enthaply difference, expected negative enthalpy for cold streams. stream not added") else: self.streams.append(make_stream(enthalpy_difference,T_inlet,T_outlet)) - # setting the value for shifting the composit curces (CCs) - def set_minimum_temperature_difference(self, minimum_temperature_difference:float): - # minimum temperature difference of streams - self.min_dT = minimum_temperature_difference - # the hot and cold composit curve (CC) are shifted by half the minimum temperature difference to meet at the pinch point - self.temp_shift = self.min_dT / 2 + # add: functions to read lists for hot and cold later + + + # add: functions to remove streams later # create the pinch analyzer from pina def _create_analyzer(self): - self.analyzer = PinchAnalyzer(self.temp_shift) + # check if necessary input is set + if self.temp_shift is not None: + self.analyzer = PinchAnalyzer(self.temp_shift) + else: + print("set minimum temperature difference first") + return self.analyzer.add_streams(*self.streams) # add a way to add streams later @@ -56,34 +73,125 @@ def get_hot_cc(self): # get datapoints of cold composite curve def get_cold_cc(self): + if self.analyzer is None: + self._create_analyzer() [self.cold_cc_data_enthalpy, self.cold_cc_data_temperature] = self.analyzer.cold_composite_curve # get datapoints of shifted hot composite curve def get_shifted_hot_cc(self): + if self.analyzer is None: + self._create_analyzer() [self.shifted_hot_cc_data_enthalpy, self.shifted_hot_cc_data_temperature] = self.analyzer.shifted_hot_composite_curve # get datapoints of shifted cold composite curve def get_shifted_cold_cc(self): + if self.analyzer is None: + self._create_analyzer() [self.shifted_cold_cc_data_enthalpy, self.shifted_cold_cc_data_temperature] = self.analyzer.shifted_cold_composite_curve # get datapoints of grand composite curve def get_gcc(self): + if self.analyzer is None: + self._create_analyzer() [self.gcc_data_enthalpy, self.gcc_data_shifted_temperature] = self.analyzer.grand_composite_curve # add: def get_pinch_point(self): # needed to check e.g. pinch rules for heat pump integration automatically - - # add: def plot_cc_diagram(self): + + + def plot_cc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_fig:bool = False): + fig, ax = plt.subplots() + # activate minor ticks + ax.minorticks_on() + # plot subplots with same axes limits + ax.plot(self.hot_cc_data_enthalpy, self.hot_cc_data_temperature, color = "red") + ax.plot(self.cold_cc_data_enthalpy, self.cold_cc_data_temperature, color = "blue") + # set x scale on lowest subplot + ax.tick_params(axis='x', which='major', labelsize=10, rotation = 0) + # set x label + ax.set_xlabel("$\dot{H}$ [kW]", loc='center', fontsize="10") + ax.xaxis.label.set_color("black") + # set y label + ax.set_ylabel("T [°C]", color = "black") + # set grid + ax.grid(visible=True, which='major', color='lightgrey', linewidth = 0.5) + ax.grid(visible=True, which='minor', color='lightgrey', linestyle='dotted', linewidth = 0.5) + # set aspect ratio of subplot + ax.set_box_aspect(1) + ax.set_title(f"Composite Curves of \"{self.label}\"",color = "black", fontsize = 10) + + if save_fig: + fig.savefig(f"Composite_Curves_{self.label}.svg") + if show_fig: + fig.show() + if return_fig: + return fig - # add: def plot_shifted_cc_diagram(self): - # add: def plot_gcc_diagram(self): - - # include heat cascades later (for more than one point in time) + def plot_shifted_cc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_fig:bool = False): + fig, ax = plt.subplots() + # activate minor ticks + ax.minorticks_on() + # plot subplots with same axes limits + ax.plot(self.shifted_hot_cc_data_enthalpy, self.shifted_hot_cc_data_temperature, color = "red") + ax.plot(self.shifted_cold_cc_data_enthalpy, self.shifted_cold_cc_data_temperature, color = "blue") + # set x scale on lowest subplot + ax.tick_params(axis='x', which='major', labelsize=10, rotation = 0) + # set x label + ax.set_xlabel("$\dot{H}$ [kW]", loc='center', fontsize="10") + ax.xaxis.label.set_color("black") + # set y label + ax.set_ylabel("shifted T* [°C]", color = "black") + # set grid + ax.grid(visible=True, which='major', color='lightgrey', linewidth = 0.5) + ax.grid(visible=True, which='minor', color='lightgrey', linestyle='dotted', linewidth = 0.5) + # set aspect ratio of subplot + ax.set_box_aspect(1) + ax.set_title(f"Shifted Composite Curves of \"{self.label}\"",color = "black", fontsize = 10) + + if save_fig: + fig.savefig(f"Shifted_Composite_Curves_{self.label}.svg") + if show_fig: + fig.show() + if return_fig: + return fig + + + def plot_gcc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_fig:bool = False): + fig, ax = plt.subplots() + # activate minor ticks + ax.minorticks_on() + # plot subplots with same axes limits + ax.plot(self.gcc_data_enthalpy, self.gcc_data_shifted_temperature, color = "black") + # set x scale on lowest subplot + ax.tick_params(axis='x', which='major', labelsize=10, rotation = 0) + # set x label + ax.set_xlabel("$\Delta\dot{H}$ [kW]", loc='center', fontsize="10") + ax.xaxis.label.set_color("black") + # set x limit to 0 + ax.set_xlim(xmin=0) + # set y label + ax.set_ylabel("shifted T* [°C]", color = "black") + # set grid + ax.grid(visible=True, which='major', color='lightgrey', linewidth = 0.5) + ax.grid(visible=True, which='minor', color='lightgrey', linestyle='dotted', linewidth = 0.5) + # set aspect ratio of subplot + ax.set_box_aspect(1) + ax.set_title(f"Grand Composite Curve of \"{self.label}\"",color = "black", fontsize = 10) + + if save_fig: + fig.savefig(f"Grand_Composite_Curve_{self.label}.svg") + if show_fig: + fig.show() + if return_fig: + return fig + + + # add: include heat cascades later (for more than one point in time / investigated interval) # adding components from tespy models @@ -97,5 +205,8 @@ def get_gcc(self): # show (only display) by adding the plot data to the gcc + # add: check heat pump integration by using the integration rules + # see e.g. Arpagaus 2019, Walden et al. 2023 + # add: def add_heat_exchanger_to_streams(self): - # function to read streams from given heat exchangers, later use to iterate all heat exchangers of network with possibility to exclude streams / heat exchangers + # function to read streams from given heat exchangers, later use to iterate all heat exchangers of network with possibility to exclude streams / heat exchangers \ No newline at end of file diff --git a/tutorial/advanced/example_pinch_analysis.py b/tutorial/advanced/example_pinch_analysis.py new file mode 100644 index 000000000..dacc0f4c4 --- /dev/null +++ b/tutorial/advanced/example_pinch_analysis.py @@ -0,0 +1,39 @@ + +from tespy.tools.pinch_analysis import TesypPinchAnalysis + + +# example Pinch Analysis of a manual workflow without tespy components +Example_Analysis = TesypPinchAnalysis("Example_Process_1") + + +# setting the minimum temperature difference for the analysis +Example_Analysis.set_minimum_temperature_difference(10) + + +# add all the streams manually +# Example: Kemp 2007 p. 20 +Example_Analysis.add_cold_stream_manually(-230, 20, 135) +Example_Analysis.add_hot_stream_manually(330, 170, 60) +Example_Analysis.add_cold_stream_manually(-240, 80, 140) +Example_Analysis.add_hot_stream_manually(180, 150, 30) +# additional latent streams as shown by Arpagaus 2019 p. 99 +Example_Analysis.add_hot_stream_manually(60,85,85) +Example_Analysis.add_cold_stream_manually(-40,130,130) + + +# with all streams added, get the results as in pina +cold_cc_data = Example_Analysis.get_cold_cc() +hot_cc_data = Example_Analysis.get_hot_cc() +shifted_cold_cc_data = Example_Analysis.get_shifted_cold_cc() +shifted_hot_cc_data = Example_Analysis.get_shifted_hot_cc() +gcc_data = Example_Analysis.get_gcc() + + +# this data can be used for other aspects like combining with tespy heat exchangers +# to plot the results use the following functions +# the composite curves +Example_Analysis.plot_cc_diagram() +# the shifted composite curves touching in the pinch point +Example_Analysis.plot_shifted_cc_diagram() +# the grand composite curve +Example_Analysis.plot_gcc_diagram() \ No newline at end of file From 7b7336fc0aea3e77104d947012cfa04b04b66735 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 30 Nov 2025 13:58:36 +0100 Subject: [PATCH 04/25] adding ISBN for one source --- src/tespy/tools/pinch_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index d43c95505..917dbb174 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -3,7 +3,7 @@ import matplotlib.pyplot as plt # Useful literature: -# Arpagaus 2019 Hochtemperatur-Wärmepumpen (p.99) +# Arpagaus 2019 Hochtemperatur-Wärmepumpen (ISBN 978-3-8007-4550-0) # Kemp 2006 (p.20) # Walden et al. 2023 (https://doi.org/10.1016/j.apenergy.2023.121933) From c7ef8082b2a9858b1f66685410f1ac2b753b0609 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 1 Dec 2025 08:27:52 +0100 Subject: [PATCH 05/25] Add pina dependency --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index a0f680f8b..8e265d3e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ dependencies = [ "matplotlib>=3.2.1", "numpy>=1.13.3", "pandas>=1.3.0", + "pina>=0.1.1", "pint", "scipy", "tabulate>=0.8.2", From 6d283c6f8a776f09727eefafdfbfc65d257e88a8 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Wed, 3 Dec 2025 16:58:42 +0100 Subject: [PATCH 06/25] starting heat pump example --- tutorial/advanced/example_pinch_analysis.py | 27 ++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tutorial/advanced/example_pinch_analysis.py b/tutorial/advanced/example_pinch_analysis.py index dacc0f4c4..92f3e8c7c 100644 --- a/tutorial/advanced/example_pinch_analysis.py +++ b/tutorial/advanced/example_pinch_analysis.py @@ -36,4 +36,29 @@ # the shifted composite curves touching in the pinch point Example_Analysis.plot_shifted_cc_diagram() # the grand composite curve -Example_Analysis.plot_gcc_diagram() \ No newline at end of file +Example_Analysis.plot_gcc_diagram() + + +# setting up the heat pump system +from tespy.networks import network +from tespy.connections import connection +from tespy.components import SimpleHeatExchanger, Valve, Compressor + +# network +nw = network("Heat Pump Network") + + +# components +condenser = SimpleHeatExchanger("Condenser") +evaporator = SimpleHeatExchanger("Evaporator") +expansion_valve = Valve("Expansion Valve") +compressor = Compressor("Compressor") + +# connections +c1 = connection(evaporator, "out1", compressor,"in1", label = "connection 1") +c2 = connection() +c3 = connection() +c4 = connection() +nw.add_conns(c1,c2,c3,c4) + +# set up parameters of heat pump \ No newline at end of file From 87b175ffe1a5d0cc52363dc36fe93ff654a7a57e Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:30:45 +0100 Subject: [PATCH 07/25] adding simple heat pump & showing heat pump in GCC with dummy temperatures --- src/tespy/tools/pinch_analysis.py | 31 ++++++++++--- tutorial/advanced/example_pinch_analysis.py | 49 ++++++++++++++------- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index 917dbb174..642441983 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -183,6 +183,8 @@ def plot_gcc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_f ax.set_box_aspect(1) ax.set_title(f"Grand Composite Curve of \"{self.label}\"",color = "black", fontsize = 10) + self.gcc_fig, self.gcc_ax = fig, ax + if save_fig: fig.savefig(f"Grand_Composite_Curve_{self.label}.svg") if show_fig: @@ -196,14 +198,33 @@ def plot_gcc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_f # adding components from tespy models - # add: def show_heat_pump_in_gcc(self): + # adding a heat pump in the GCC (by referencing evaporator and condenser) - # get plot data of heat exchangers at heat sink - # as used in e.g. the heat exchanger example plots + def show_heat_pump_in_gcc(self, evaporator, condenser): + from tespy.components import SimpleHeatExchanger + + # get the GCC + fig = self.gcc_fig + ax = self.gcc_ax + if isinstance(condenser,SimpleHeatExchanger): + # get plot data of heat exchangers at heat sink (taken from user meeting example of heat exchangers) + condenser_Q_vals = [0, abs(condenser.Q.val)] + condenser_T_vals = [50,50] # for testing only! + if isinstance(condenser,SimpleHeatExchanger): + # get plot data of heat exchangers at heat source (taken from user meeting example of heat exchangers) + evaporator_Q_vals = [0, abs(evaporator.Q.val)] + evaporator_T_vals = [10,10] # for testing only! + + # add: expand these conditions in future for other types + + + # show (only display) by adding the plot data of the heat exchangers to the GCC + ax.plot(condenser_Q_vals, condenser_T_vals, "-",color="red") # as in heat exchanger example + ax.plot(evaporator_Q_vals, evaporator_T_vals, "-",color="blue") - # get plot data of heat exchangers at heat source + # save figure + fig.savefig("GCC_with_heat_pump.svg") - # show (only display) by adding the plot data to the gcc # add: check heat pump integration by using the integration rules # see e.g. Arpagaus 2019, Walden et al. 2023 diff --git a/tutorial/advanced/example_pinch_analysis.py b/tutorial/advanced/example_pinch_analysis.py index 92f3e8c7c..36c0957f1 100644 --- a/tutorial/advanced/example_pinch_analysis.py +++ b/tutorial/advanced/example_pinch_analysis.py @@ -2,14 +2,14 @@ from tespy.tools.pinch_analysis import TesypPinchAnalysis +# integrating a tespy model of a heat pump into a given process + # example Pinch Analysis of a manual workflow without tespy components Example_Analysis = TesypPinchAnalysis("Example_Process_1") - # setting the minimum temperature difference for the analysis Example_Analysis.set_minimum_temperature_difference(10) - # add all the streams manually # Example: Kemp 2007 p. 20 Example_Analysis.add_cold_stream_manually(-230, 20, 135) @@ -20,7 +20,6 @@ Example_Analysis.add_hot_stream_manually(60,85,85) Example_Analysis.add_cold_stream_manually(-40,130,130) - # with all streams added, get the results as in pina cold_cc_data = Example_Analysis.get_cold_cc() hot_cc_data = Example_Analysis.get_hot_cc() @@ -28,7 +27,6 @@ shifted_hot_cc_data = Example_Analysis.get_shifted_hot_cc() gcc_data = Example_Analysis.get_gcc() - # this data can be used for other aspects like combining with tespy heat exchangers # to plot the results use the following functions # the composite curves @@ -40,25 +38,46 @@ # setting up the heat pump system -from tespy.networks import network -from tespy.connections import connection -from tespy.components import SimpleHeatExchanger, Valve, Compressor +from tespy.networks import Network +from tespy.connections import Connection +from tespy.components import SimpleHeatExchanger, Valve, Compressor, CycleCloser # network -nw = network("Heat Pump Network") +nw = Network() +# taken from example heat pump +nw.units.set_defaults( + temperature="degC", pressure="bar", enthalpy="kJ/kg", heat="kW", power="kW" +) # components condenser = SimpleHeatExchanger("Condenser") evaporator = SimpleHeatExchanger("Evaporator") expansion_valve = Valve("Expansion Valve") compressor = Compressor("Compressor") +cycle_closer = CycleCloser("Cycle Closer") # connections -c1 = connection(evaporator, "out1", compressor,"in1", label = "connection 1") -c2 = connection() -c3 = connection() -c4 = connection() -nw.add_conns(c1,c2,c3,c4) - -# set up parameters of heat pump \ No newline at end of file +c1 = Connection(evaporator, "out1", compressor,"in1", label = "connection 1") +c2 = Connection(compressor, "out1", condenser, "in1", label = "connection 2") +c3 = Connection(condenser, "out1", expansion_valve, "in1", label = "connection3") +c4 = Connection(expansion_valve, "out1",cycle_closer, "in1", label = "connection4") +c5 = Connection(cycle_closer, "out1", evaporator, "in1", label = "connection5") +nw.add_conns(c1,c2,c3,c4,c5) + +# set up general parameters of heat pump +compressor.set_attr(eta_s = 0.7) +condenser.set_attr(dp=0) +evaporator.set_attr(dp=0) +c1.set_attr(fluid={"R290": 1}) + +# set up parameters to show specific case +c1.set_attr(m=0.1, p=5, x=1) +c2.set_attr(p=17) +c3.set_attr(x=0) + +# solve design +nw.solve("design") + +# reference heat pump components for plotting in the GCC +Example_Analysis.show_heat_pump_in_gcc(condenser=condenser,evaporator=evaporator) \ No newline at end of file From c7623701c42794d306d89d3f2fc58f53ad6dc5d1 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:39:37 +0100 Subject: [PATCH 08/25] adding pinch point marker in GCC and getting pinch temperature --- src/tespy/tools/pinch_analysis.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index 642441983..fa683369c 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -99,7 +99,12 @@ def get_gcc(self): [self.gcc_data_enthalpy, self.gcc_data_shifted_temperature] = self.analyzer.grand_composite_curve - # add: def get_pinch_point(self): + def _get_pinch_point(self): + # pinch point is at enthalpy flow difference of 0 by definition + pinch_index_gcc = self.gcc_data_enthalpy.index(0) + self.T_pinch = self.gcc_data_shifted_temperature[pinch_index_gcc] + + # needed to check e.g. pinch rules for heat pump integration automatically @@ -162,11 +167,16 @@ def plot_shifted_cc_diagram(self, save_fig:bool = True, show_fig:bool = False, r def plot_gcc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_fig:bool = False): + # get pinch temperature + self._get_pinch_point() + # plot fig, ax = plt.subplots() # activate minor ticks ax.minorticks_on() # plot subplots with same axes limits ax.plot(self.gcc_data_enthalpy, self.gcc_data_shifted_temperature, color = "black") + # add pinch point + ax.plot(0, self.T_pinch, "o", color = "black") # set x scale on lowest subplot ax.tick_params(axis='x', which='major', labelsize=10, rotation = 0) # set x label From c91b01313475c1e155b57853df3d021858cb1ba4 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:41:13 +0100 Subject: [PATCH 09/25] added comment for todo in plotting --- src/tespy/tools/pinch_analysis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index fa683369c..e3da897dc 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -100,6 +100,7 @@ def get_gcc(self): def _get_pinch_point(self): + # add: make use of pina functions here and in other plots # pinch point is at enthalpy flow difference of 0 by definition pinch_index_gcc = self.gcc_data_enthalpy.index(0) self.T_pinch = self.gcc_data_shifted_temperature[pinch_index_gcc] From 35a9c7d00f2656be7db8b046c855b841610eaf3d Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Wed, 3 Dec 2025 21:18:05 +0100 Subject: [PATCH 10/25] adding more pina functions for analysis data and plot markers for utility/recovery sections --- src/tespy/tools/pinch_analysis.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index e3da897dc..d1b3353d4 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -62,6 +62,7 @@ def _create_analyzer(self): print("set minimum temperature difference first") return self.analyzer.add_streams(*self.streams) # add a way to add streams later + self._get_analyzer_data() # get datapoints of hot composite curve @@ -99,12 +100,12 @@ def get_gcc(self): [self.gcc_data_enthalpy, self.gcc_data_shifted_temperature] = self.analyzer.grand_composite_curve - def _get_pinch_point(self): - # add: make use of pina functions here and in other plots - # pinch point is at enthalpy flow difference of 0 by definition - pinch_index_gcc = self.gcc_data_enthalpy.index(0) - self.T_pinch = self.gcc_data_shifted_temperature[pinch_index_gcc] - + def _get_analyzer_data(self): + # get additional analysis data from pina + self.T_pinch = self.analyzer.pinch_temps[0] + self.cold_utility = self.analyzer.cold_utility_target + self.hot_utility = self.analyzer.hot_utility_target + self.heat_recovery = self.analyzer.heat_recovery_target # needed to check e.g. pinch rules for heat pump integration automatically @@ -116,6 +117,11 @@ def plot_cc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_fi # plot subplots with same axes limits ax.plot(self.hot_cc_data_enthalpy, self.hot_cc_data_temperature, color = "red") ax.plot(self.cold_cc_data_enthalpy, self.cold_cc_data_temperature, color = "blue") + # add visualization of sections + # get minimum temperature + T_min_CC = min(min(self.hot_cc_data_temperature), min(self.cold_cc_data_temperature)) + ax.plot([0,self.cold_utility,self.heat_recovery+self.cold_utility, + self.heat_recovery+self.cold_utility+self.hot_utility],[T_min_CC-5]*4, "o-", color="black") # set x scale on lowest subplot ax.tick_params(axis='x', which='major', labelsize=10, rotation = 0) # set x label @@ -145,6 +151,11 @@ def plot_shifted_cc_diagram(self, save_fig:bool = True, show_fig:bool = False, r # plot subplots with same axes limits ax.plot(self.shifted_hot_cc_data_enthalpy, self.shifted_hot_cc_data_temperature, color = "red") ax.plot(self.shifted_cold_cc_data_enthalpy, self.shifted_cold_cc_data_temperature, color = "blue") + # add visualization of sections + # get minimum temperature + T_min_shifted_CC = min(min(self.shifted_hot_cc_data_temperature), min(self.shifted_cold_cc_data_temperature)) + ax.plot([0,self.cold_utility,self.heat_recovery+self.cold_utility, + self.heat_recovery+self.cold_utility+self.hot_utility],[T_min_shifted_CC-5]*4, "o-", color="black") # set x scale on lowest subplot ax.tick_params(axis='x', which='major', labelsize=10, rotation = 0) # set x label @@ -168,8 +179,6 @@ def plot_shifted_cc_diagram(self, save_fig:bool = True, show_fig:bool = False, r def plot_gcc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_fig:bool = False): - # get pinch temperature - self._get_pinch_point() # plot fig, ax = plt.subplots() # activate minor ticks From 8de0369986d564fc64fb813e0d930e50d32f3f15 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 14 Dec 2025 12:23:50 +0100 Subject: [PATCH 11/25] lowering temperatures for example to match heat pump levels --- tutorial/advanced/example_pinch_analysis.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tutorial/advanced/example_pinch_analysis.py b/tutorial/advanced/example_pinch_analysis.py index 36c0957f1..269700e58 100644 --- a/tutorial/advanced/example_pinch_analysis.py +++ b/tutorial/advanced/example_pinch_analysis.py @@ -11,14 +11,14 @@ Example_Analysis.set_minimum_temperature_difference(10) # add all the streams manually -# Example: Kemp 2007 p. 20 -Example_Analysis.add_cold_stream_manually(-230, 20, 135) -Example_Analysis.add_hot_stream_manually(330, 170, 60) -Example_Analysis.add_cold_stream_manually(-240, 80, 140) -Example_Analysis.add_hot_stream_manually(180, 150, 30) +# Example: Kemp 2007 p. 20, reduced temperature by 50 degC to fit the heat pump example +Example_Analysis.add_cold_stream_manually(-230, 20-50, 135-50) +Example_Analysis.add_hot_stream_manually(330, 170-50, 60-50) +Example_Analysis.add_cold_stream_manually(-240, 80-50, 140-50) +Example_Analysis.add_hot_stream_manually(180, 150-50, 30-50) # additional latent streams as shown by Arpagaus 2019 p. 99 -Example_Analysis.add_hot_stream_manually(60,85,85) -Example_Analysis.add_cold_stream_manually(-40,130,130) +Example_Analysis.add_hot_stream_manually(60,35,35) +Example_Analysis.add_cold_stream_manually(-40,80,80) # with all streams added, get the results as in pina cold_cc_data = Example_Analysis.get_cold_cc() From 1041da6f3fc43c9a8ba3cb721293d355e6dd1a67 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 14 Dec 2025 12:45:16 +0100 Subject: [PATCH 12/25] finishing the heat pump example --- src/tespy/tools/pinch_analysis.py | 8 ++++--- tutorial/advanced/example_pinch_analysis.py | 23 +++++++++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index d1b3353d4..b1e77f170 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -226,14 +226,16 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): # get the GCC fig = self.gcc_fig ax = self.gcc_ax + + # get the plotting data of the heat exchangers if isinstance(condenser,SimpleHeatExchanger): # get plot data of heat exchangers at heat sink (taken from user meeting example of heat exchangers) condenser_Q_vals = [0, abs(condenser.Q.val)] - condenser_T_vals = [50,50] # for testing only! + condenser_T_vals = [condenser.outl[0].T.val,condenser.inl[0].T.val] if isinstance(condenser,SimpleHeatExchanger): # get plot data of heat exchangers at heat source (taken from user meeting example of heat exchangers) - evaporator_Q_vals = [0, abs(evaporator.Q.val)] - evaporator_T_vals = [10,10] # for testing only! + evaporator_Q_vals = [0, abs(evaporator.Q.val)] + evaporator_T_vals = [evaporator.inl[0].T.val,evaporator.outl[0].T.val] # add: expand these conditions in future for other types diff --git a/tutorial/advanced/example_pinch_analysis.py b/tutorial/advanced/example_pinch_analysis.py index 269700e58..56a58e081 100644 --- a/tutorial/advanced/example_pinch_analysis.py +++ b/tutorial/advanced/example_pinch_analysis.py @@ -53,28 +53,33 @@ # components condenser = SimpleHeatExchanger("Condenser") evaporator = SimpleHeatExchanger("Evaporator") +desuperheater = SimpleHeatExchanger("Desuperheater") expansion_valve = Valve("Expansion Valve") compressor = Compressor("Compressor") cycle_closer = CycleCloser("Cycle Closer") # connections -c1 = Connection(evaporator, "out1", compressor,"in1", label = "connection 1") -c2 = Connection(compressor, "out1", condenser, "in1", label = "connection 2") -c3 = Connection(condenser, "out1", expansion_valve, "in1", label = "connection3") -c4 = Connection(expansion_valve, "out1",cycle_closer, "in1", label = "connection4") -c5 = Connection(cycle_closer, "out1", evaporator, "in1", label = "connection5") -nw.add_conns(c1,c2,c3,c4,c5) +c1 = Connection(evaporator, "out1", compressor,"in1", label = "connection 1") +c2 = Connection(compressor, "out1", desuperheater, "in1", "connection 2") +c3 = Connection(desuperheater, "out1", condenser, "in1", label = "connection 3") +c4 = Connection(condenser, "out1", expansion_valve, "in1", label = "connection 4") +c5 = Connection(expansion_valve, "out1",cycle_closer, "in1", label = "connection 5") +c6 = Connection(cycle_closer, "out1", evaporator, "in1", label = "connection 6") +nw.add_conns(c1,c2,c3,c4,c5,c6) # set up general parameters of heat pump compressor.set_attr(eta_s = 0.7) condenser.set_attr(dp=0) +desuperheater.set_attr(dp=0) evaporator.set_attr(dp=0) c1.set_attr(fluid={"R290": 1}) -# set up parameters to show specific case +# set up parameters to show specific case, the desuperheater reduces the temperature to the dewline +# in that case only the condensation is part of the conndenser forming a horizontal line in the GCC as +# the most simple example case c1.set_attr(m=0.1, p=5, x=1) -c2.set_attr(p=17) -c3.set_attr(x=0) +c3.set_attr(p=20, x=1) +c4.set_attr(x=0) # solve design nw.solve("design") From be3a2d008ab5395637d293c4cca18add58e535be Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:00:54 +0100 Subject: [PATCH 13/25] adding values to the composit curve sections --- src/tespy/tools/pinch_analysis.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index b1e77f170..df0fd7bfc 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -122,6 +122,11 @@ def plot_cc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_fi T_min_CC = min(min(self.hot_cc_data_temperature), min(self.cold_cc_data_temperature)) ax.plot([0,self.cold_utility,self.heat_recovery+self.cold_utility, self.heat_recovery+self.cold_utility+self.hot_utility],[T_min_CC-5]*4, "o-", color="black") + # adding annotations to the different sections + ax.annotate(f'{self.cold_utility:.0f}', xy=(self.cold_utility/2, T_min_CC-4), horizontalalignment='center', fontsize=10) + ax.annotate(f'{self.heat_recovery:.0f}', xy=(self.cold_utility + self.heat_recovery/2, T_min_CC-4), horizontalalignment='center', fontsize=10) + ax.annotate(f'{self.hot_utility:.0f}', xy=(self.cold_utility + self.heat_recovery + self.hot_utility/2, T_min_CC-4), horizontalalignment='center', fontsize=10) + # set x scale on lowest subplot ax.tick_params(axis='x', which='major', labelsize=10, rotation = 0) # set x label @@ -156,6 +161,10 @@ def plot_shifted_cc_diagram(self, save_fig:bool = True, show_fig:bool = False, r T_min_shifted_CC = min(min(self.shifted_hot_cc_data_temperature), min(self.shifted_cold_cc_data_temperature)) ax.plot([0,self.cold_utility,self.heat_recovery+self.cold_utility, self.heat_recovery+self.cold_utility+self.hot_utility],[T_min_shifted_CC-5]*4, "o-", color="black") + # adding annotations to the different sections + ax.annotate(f'{self.cold_utility:.0f}', xy=(self.cold_utility/2, T_min_shifted_CC-4), horizontalalignment='center', fontsize=10) + ax.annotate(f'{self.heat_recovery:.0f}', xy=(self.cold_utility + self.heat_recovery/2, T_min_shifted_CC-4), horizontalalignment='center', fontsize=10) + ax.annotate(f'{self.hot_utility:.0f}', xy=(self.cold_utility + self.heat_recovery + self.hot_utility/2, T_min_shifted_CC-4), horizontalalignment='center', fontsize=10) # set x scale on lowest subplot ax.tick_params(axis='x', which='major', labelsize=10, rotation = 0) # set x label From b60cf15dd92204c48fe7f48395626c42a820fab0 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:07:44 +0100 Subject: [PATCH 14/25] adding missing doi of reference --- src/tespy/tools/pinch_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index df0fd7bfc..c55f3425d 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -4,7 +4,7 @@ # Useful literature: # Arpagaus 2019 Hochtemperatur-Wärmepumpen (ISBN 978-3-8007-4550-0) -# Kemp 2006 (p.20) +# Kemp 2006 (p.20) (https://doi.org/10.1016/B978-0-7506-8260-2.X5001-9) # Walden et al. 2023 (https://doi.org/10.1016/j.apenergy.2023.121933) From f72ceb36888529e36b0439b6c2f4569910f32ace Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:11:07 +0100 Subject: [PATCH 15/25] fixing one condition and adding value error for not implemented components as heat exchangers --- src/tespy/tools/pinch_analysis.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index c55f3425d..5c86cb52a 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -241,10 +241,15 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): # get plot data of heat exchangers at heat sink (taken from user meeting example of heat exchangers) condenser_Q_vals = [0, abs(condenser.Q.val)] condenser_T_vals = [condenser.outl[0].T.val,condenser.inl[0].T.val] - if isinstance(condenser,SimpleHeatExchanger): + else: + raise ValueError("The component type is not implemented as a condenser.") + + if isinstance(evaporator,SimpleHeatExchanger): # get plot data of heat exchangers at heat source (taken from user meeting example of heat exchangers) evaporator_Q_vals = [0, abs(evaporator.Q.val)] evaporator_T_vals = [evaporator.inl[0].T.val,evaporator.outl[0].T.val] + else: + raise ValueError("The component type is not implemented as an evaporator.") # add: expand these conditions in future for other types From 33f9de68f01cbc408fd86529a724b16db77f16c1 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:12:13 +0100 Subject: [PATCH 16/25] adding comments --- src/tespy/tools/pinch_analysis.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index 5c86cb52a..8c907e2cb 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -237,6 +237,7 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): ax = self.gcc_ax # get the plotting data of the heat exchangers + # condensers if isinstance(condenser,SimpleHeatExchanger): # get plot data of heat exchangers at heat sink (taken from user meeting example of heat exchangers) condenser_Q_vals = [0, abs(condenser.Q.val)] @@ -244,6 +245,7 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): else: raise ValueError("The component type is not implemented as a condenser.") + # evaporators if isinstance(evaporator,SimpleHeatExchanger): # get plot data of heat exchangers at heat source (taken from user meeting example of heat exchangers) evaporator_Q_vals = [0, abs(evaporator.Q.val)] From 1c205f260518b2fafa9ee862f167850b59e9998c Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:28:46 +0100 Subject: [PATCH 17/25] adding comments for some tasks --- src/tespy/tools/pinch_analysis.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index 8c907e2cb..61c67ab8c 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -227,8 +227,7 @@ def plot_gcc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_f # adding components from tespy models - # adding a heat pump in the GCC (by referencing evaporator and condenser) - + # adding a heat pump in the GCC (by referencing evaporator and condenser) def show_heat_pump_in_gcc(self, evaporator, condenser): from tespy.components import SimpleHeatExchanger @@ -254,6 +253,7 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): raise ValueError("The component type is not implemented as an evaporator.") # add: expand these conditions in future for other types + # missing: HeatExchanger, ParallelFlowHeatExchanger, Desuperheater, Condenser, MovingBoundaryHeatExchanger, SectionedHeatExchanger # show (only display) by adding the plot data of the heat exchangers to the GCC @@ -266,6 +266,7 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): # add: check heat pump integration by using the integration rules # see e.g. Arpagaus 2019, Walden et al. 2023 + # Temperature and heat flux # add: def add_heat_exchanger_to_streams(self): # function to read streams from given heat exchangers, later use to iterate all heat exchangers of network with possibility to exclude streams / heat exchangers \ No newline at end of file From 3ca34278725dec46c2465e21ea9545df912bae50 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sat, 27 Dec 2025 14:58:07 +0100 Subject: [PATCH 18/25] adding an example with movingboundaryHX and sectionedHX and starting plotting for both --- src/tespy/tools/pinch_analysis.py | 14 ++++- tutorial/advanced/example_pinch_analysis.py | 69 ++++++++++++++++++++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index 61c67ab8c..c44f5fd0a 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -229,7 +229,7 @@ def plot_gcc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_f # adding a heat pump in the GCC (by referencing evaporator and condenser) def show_heat_pump_in_gcc(self, evaporator, condenser): - from tespy.components import SimpleHeatExchanger + from tespy.components import SimpleHeatExchanger, MovingBoundaryHeatExchanger, SectionedHeatExchanger # get the GCC fig = self.gcc_fig @@ -241,6 +241,12 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): # get plot data of heat exchangers at heat sink (taken from user meeting example of heat exchangers) condenser_Q_vals = [0, abs(condenser.Q.val)] condenser_T_vals = [condenser.outl[0].T.val,condenser.inl[0].T.val] + # to do: include a warning, if there is at least one change between to or from a latent stream + elif isinstance(condenser, SectionedHeatExchanger) or isinstance(condenser, MovingBoundaryHeatExchanger): + # get the data from calc_sections, a and b are not needed placeholders as a reminder + a, condenser_T_steps_hot, condenser_T_steps_cold, condenser_Q_per_section, b = condenser.calc_sections() + print(condenser_T_steps_hot, condenser_Q_per_section) + raise ValueError("method not completed yet") else: raise ValueError("The component type is not implemented as a condenser.") @@ -249,6 +255,12 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): # get plot data of heat exchangers at heat source (taken from user meeting example of heat exchangers) evaporator_Q_vals = [0, abs(evaporator.Q.val)] evaporator_T_vals = [evaporator.inl[0].T.val,evaporator.outl[0].T.val] + # to do: include a warning, if there is at least one change between to or from a latent stream + elif isinstance(evaporator, SectionedHeatExchanger) or isinstance(evaporator, MovingBoundaryHeatExchanger): + # get the data from calc_sections, a and b are not needed placeholders as a reminder + a, evaporator_T_steps_hot, evaporator_T_steps_cold, evaporator_Q_per_section, b = condenser.calc_sections() + print(evaporator_T_steps_hot, evaporator_Q_per_section) + raise ValueError("method not completed yet") else: raise ValueError("The component type is not implemented as an evaporator.") diff --git a/tutorial/advanced/example_pinch_analysis.py b/tutorial/advanced/example_pinch_analysis.py index 56a58e081..8e33693b9 100644 --- a/tutorial/advanced/example_pinch_analysis.py +++ b/tutorial/advanced/example_pinch_analysis.py @@ -75,7 +75,7 @@ c1.set_attr(fluid={"R290": 1}) # set up parameters to show specific case, the desuperheater reduces the temperature to the dewline -# in that case only the condensation is part of the conndenser forming a horizontal line in the GCC as +# in that case only the condensation is part of the condenser forming a horizontal line in the GCC as # the most simple example case c1.set_attr(m=0.1, p=5, x=1) c3.set_attr(p=20, x=1) @@ -85,4 +85,69 @@ nw.solve("design") # reference heat pump components for plotting in the GCC -Example_Analysis.show_heat_pump_in_gcc(condenser=condenser,evaporator=evaporator) \ No newline at end of file +Example_Analysis.show_heat_pump_in_gcc(condenser=condenser,evaporator=evaporator) + + + +# second example using a moving boundary heat exchanger as the evaporator and a sectioned heat exchanger as the condenser +# as the heat exchangers now include the internal H,T-Data, the desuperheater is not included anymore. +# However, the connection numbers are kept to clarifiy the positions. + +# setting up the heat pump system +from tespy.components import MovingBoundaryHeatExchanger, SectionedHeatExchanger, Sink, Source + +# network +nw_2 = Network() + +# taken from example heat pump +nw_2.units.set_defaults( + temperature="degC", pressure="bar", enthalpy="kJ/kg", heat="kW", power="kW" +) + +# components +condenser_2 = SectionedHeatExchanger("Condenser_2") +evaporator_2 = MovingBoundaryHeatExchanger("Evaporator_2") +expansion_valve_2 = Valve("Expansion Valve_2") +compressor_2 = Compressor("Compressor_2") +cycle_closer_2 = CycleCloser("Cycle Closer_2") +# Sinks and Sources for the secondary media +HeatSinkIn = Source("HeatSinkIn") +HeatSinkOut = Sink("HeatSinkOut") +HeatSourceIn = Source("HeatSourceIn") +HeatSourceOut = Sink("HeatSourceOut") + +# connections +c1_2 = Connection(evaporator_2, "out2", compressor_2,"in1", label = "connection 1_2") +c2_2 = Connection(compressor_2, "out1", condenser_2, "in1", "connection 2_2") +c4_2 = Connection(condenser_2, "out1", expansion_valve_2, "in1", label = "connection 4_2") +c5_2 = Connection(expansion_valve_2, "out1",cycle_closer_2, "in1", label = "connection 5_2") +c6_2 = Connection(cycle_closer_2, "out1", evaporator_2, "in2", label = "connection 6_2") +nw_2.add_conns(c1_2,c2_2,c4_2,c5_2,c6_2) + +# add the connections for secondary media +c7_2 = Connection(HeatSinkIn, "out1", condenser_2, "in2", label = "connection 7_2") +c8_2 = Connection(condenser_2, "out2", HeatSinkOut, "in1", label = "connection 8_2") +c9_2 = Connection(HeatSourceIn, "out1", evaporator_2, "in1", label = "connection 9_2") +c10_2 = Connection(evaporator_2, "out1", HeatSourceOut, "in1", label = "connection 10_2") +nw_2.add_conns(c7_2,c8_2,c9_2,c10_2) + +# set up general parameters of heat pump +compressor_2.set_attr(eta_s = 0.7) +condenser_2.set_attr(dp1=0, dp2=0, td_pinch=2) +evaporator_2.set_attr(dp1=0, dp2=0, td_pinch=2) +c1_2.set_attr(fluid={"R290": 1}) +# media, temperatures and pressure of heat source and sink +c7_2.set_attr(fluid={"Water": 1}, T=50, p=1) +c8_2.set_attr(T=55) +c9_2.set_attr(fluid={"Water": 1}, T=10, p=1) +c10_2.set_attr(T=5) + +# set up parameters to show specific case +c1_2.set_attr(m=0.1, x=1) +c4_2.set_attr(x=0) + +# solve design +nw_2.solve("design") + +# reference heat pump components for plotting in the GCC of the same pinch analysis +Example_Analysis.show_heat_pump_in_gcc(condenser=condenser_2,evaporator=evaporator_2) \ No newline at end of file From 067f6ee7fff05b277092b52703ea77bcad5e678b Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sat, 27 Dec 2025 15:01:16 +0100 Subject: [PATCH 19/25] minor change for later use --- src/tespy/tools/pinch_analysis.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index c44f5fd0a..21c287096 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -245,7 +245,7 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): elif isinstance(condenser, SectionedHeatExchanger) or isinstance(condenser, MovingBoundaryHeatExchanger): # get the data from calc_sections, a and b are not needed placeholders as a reminder a, condenser_T_steps_hot, condenser_T_steps_cold, condenser_Q_per_section, b = condenser.calc_sections() - print(condenser_T_steps_hot, condenser_Q_per_section) + print(condenser_T_steps_hot) raise ValueError("method not completed yet") else: raise ValueError("The component type is not implemented as a condenser.") @@ -259,13 +259,13 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): elif isinstance(evaporator, SectionedHeatExchanger) or isinstance(evaporator, MovingBoundaryHeatExchanger): # get the data from calc_sections, a and b are not needed placeholders as a reminder a, evaporator_T_steps_hot, evaporator_T_steps_cold, evaporator_Q_per_section, b = condenser.calc_sections() - print(evaporator_T_steps_hot, evaporator_Q_per_section) + print(evaporator_T_steps_cold) raise ValueError("method not completed yet") else: raise ValueError("The component type is not implemented as an evaporator.") # add: expand these conditions in future for other types - # missing: HeatExchanger, ParallelFlowHeatExchanger, Desuperheater, Condenser, MovingBoundaryHeatExchanger, SectionedHeatExchanger + # missing: HeatExchanger, ParallelFlowHeatExchanger, Desuperheater, Condenser # show (only display) by adding the plot data of the heat exchangers to the GCC From 0f1c4e3608aeebd7e2944d931f35e0c0c2518e9b Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Tue, 6 Jan 2026 19:07:13 +0100 Subject: [PATCH 20/25] finishing sectioned HX and moving boundary HX in pinch, added as second analysis example --- src/tespy/tools/pinch_analysis.py | 37 ++++++++++++++------- tutorial/advanced/example_pinch_analysis.py | 20 ++++++++++- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index 21c287096..ac9667d92 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -16,6 +16,8 @@ def __init__(self, label:str): self.streams = [] self.analyzer = None self.label = label + self.gcc_fig = None + self.gcc_data_enthalpy = None # including the general functions of pina to expand them later @@ -192,6 +194,9 @@ def plot_gcc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_f fig, ax = plt.subplots() # activate minor ticks ax.minorticks_on() + # get GCC data from pina, if not done manually before + if self.gcc_data_enthalpy is None: + self.get_gcc() # plot subplots with same axes limits ax.plot(self.gcc_data_enthalpy, self.gcc_data_shifted_temperature, color = "black") # add pinch point @@ -231,9 +236,17 @@ def plot_gcc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_f def show_heat_pump_in_gcc(self, evaporator, condenser): from tespy.components import SimpleHeatExchanger, MovingBoundaryHeatExchanger, SectionedHeatExchanger - # get the GCC - fig = self.gcc_fig - ax = self.gcc_ax + # create GCC diagram, if not done before + try: + # get the GCC + fig = self.gcc_fig + ax = self.gcc_ax + except: + # create GCC without saving fig + self.plot_gcc_diagram(save_fig=False) + # get the GCC + fig = self.gcc_fig + ax = self.gcc_ax # get the plotting data of the heat exchangers # condensers @@ -243,10 +256,10 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): condenser_T_vals = [condenser.outl[0].T.val,condenser.inl[0].T.val] # to do: include a warning, if there is at least one change between to or from a latent stream elif isinstance(condenser, SectionedHeatExchanger) or isinstance(condenser, MovingBoundaryHeatExchanger): - # get the data from calc_sections, a and b are not needed placeholders as a reminder - a, condenser_T_steps_hot, condenser_T_steps_cold, condenser_Q_per_section, b = condenser.calc_sections() - print(condenser_T_steps_hot) - raise ValueError("method not completed yet") + # get the data from calc_sections + condenser_Q_sections, condenser_T_steps_hot, condenser_T_steps_cold, condenser_Q_per_section, condenser_td_log_per_section = condenser.calc_sections() + condenser_Q_vals = [Q / 1000 for Q in condenser_Q_sections] # convert to kW + condenser_T_vals = [T - 273.17 for T in condenser_T_steps_hot] # convert to degC else: raise ValueError("The component type is not implemented as a condenser.") @@ -257,10 +270,10 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): evaporator_T_vals = [evaporator.inl[0].T.val,evaporator.outl[0].T.val] # to do: include a warning, if there is at least one change between to or from a latent stream elif isinstance(evaporator, SectionedHeatExchanger) or isinstance(evaporator, MovingBoundaryHeatExchanger): - # get the data from calc_sections, a and b are not needed placeholders as a reminder - a, evaporator_T_steps_hot, evaporator_T_steps_cold, evaporator_Q_per_section, b = condenser.calc_sections() - print(evaporator_T_steps_cold) - raise ValueError("method not completed yet") + # get the data from calc_sections + evaporator_Q_sections, evaporator_T_steps_hot, evaporator_T_steps_cold, evaporator_Q_per_section, evaporator_td_log_per_section = evaporator.calc_sections() + evaporator_Q_vals = [Q / 1000 for Q in evaporator_Q_sections] # convert to kW + evaporator_T_vals = [T - 273.17 for T in evaporator_T_steps_cold] # convert to degC else: raise ValueError("The component type is not implemented as an evaporator.") @@ -273,7 +286,7 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): ax.plot(evaporator_Q_vals, evaporator_T_vals, "-",color="blue") # save figure - fig.savefig("GCC_with_heat_pump.svg") + fig.savefig(f"GCC_with_heat_pump_{self.label}.svg") # add: check heat pump integration by using the integration rules diff --git a/tutorial/advanced/example_pinch_analysis.py b/tutorial/advanced/example_pinch_analysis.py index 8e33693b9..b1d32e686 100644 --- a/tutorial/advanced/example_pinch_analysis.py +++ b/tutorial/advanced/example_pinch_analysis.py @@ -149,5 +149,23 @@ # solve design nw_2.solve("design") +# integrating a tespy model of a heat pump into a given process + +# example Pinch Analysis of a manual workflow without tespy components +Example_Analysis2 = TesypPinchAnalysis("Example_Process_2") + +# setting the minimum temperature difference for the analysis +Example_Analysis2.set_minimum_temperature_difference(10) + +# add all the streams manually +# Example: Kemp 2007 p. 20, reduced temperature by 50 degC to fit the heat pump example +Example_Analysis2.add_cold_stream_manually(-230, 20-50, 135-50) +Example_Analysis2.add_hot_stream_manually(330, 170-50, 60-50) +Example_Analysis2.add_cold_stream_manually(-240, 80-50, 140-50) +Example_Analysis2.add_hot_stream_manually(180, 150-50, 30-50) +# additional latent streams as shown by Arpagaus 2019 p. 99 +Example_Analysis2.add_hot_stream_manually(60,35,35) +Example_Analysis2.add_cold_stream_manually(-40,80,80) + # reference heat pump components for plotting in the GCC of the same pinch analysis -Example_Analysis.show_heat_pump_in_gcc(condenser=condenser_2,evaporator=evaporator_2) \ No newline at end of file +Example_Analysis2.show_heat_pump_in_gcc(condenser=condenser_2,evaporator=evaporator_2) \ No newline at end of file From c09a90ab977cd137330ab2c4538570213536a7e9 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sat, 4 Jul 2026 12:50:13 +0200 Subject: [PATCH 21/25] adding 0.10. notations of heat exchangers and minor changes to plots --- src/tespy/tools/pinch_analysis.py | 50 +++++++++++++-------- tutorial/advanced/example_pinch_analysis.py | 12 +++-- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index ac9667d92..2bb6079ca 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -122,8 +122,9 @@ def plot_cc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_fi # add visualization of sections # get minimum temperature T_min_CC = min(min(self.hot_cc_data_temperature), min(self.cold_cc_data_temperature)) + # plot line for sections below minimum temperature ax.plot([0,self.cold_utility,self.heat_recovery+self.cold_utility, - self.heat_recovery+self.cold_utility+self.hot_utility],[T_min_CC-5]*4, "o-", color="black") + self.heat_recovery+self.cold_utility+self.hot_utility],[T_min_CC-5]*4, "|-", color="black") # adding annotations to the different sections ax.annotate(f'{self.cold_utility:.0f}', xy=(self.cold_utility/2, T_min_CC-4), horizontalalignment='center', fontsize=10) ax.annotate(f'{self.heat_recovery:.0f}', xy=(self.cold_utility + self.heat_recovery/2, T_min_CC-4), horizontalalignment='center', fontsize=10) @@ -161,8 +162,9 @@ def plot_shifted_cc_diagram(self, save_fig:bool = True, show_fig:bool = False, r # add visualization of sections # get minimum temperature T_min_shifted_CC = min(min(self.shifted_hot_cc_data_temperature), min(self.shifted_cold_cc_data_temperature)) + # plot line with sections ax.plot([0,self.cold_utility,self.heat_recovery+self.cold_utility, - self.heat_recovery+self.cold_utility+self.hot_utility],[T_min_shifted_CC-5]*4, "o-", color="black") + self.heat_recovery+self.cold_utility+self.hot_utility],[T_min_shifted_CC-5]*4, "|-", color="black") # adding annotations to the different sections ax.annotate(f'{self.cold_utility:.0f}', xy=(self.cold_utility/2, T_min_shifted_CC-4), horizontalalignment='center', fontsize=10) ax.annotate(f'{self.heat_recovery:.0f}', xy=(self.cold_utility + self.heat_recovery/2, T_min_shifted_CC-4), horizontalalignment='center', fontsize=10) @@ -232,9 +234,10 @@ def plot_gcc_diagram(self, save_fig:bool = True, show_fig:bool = False, return_f # adding components from tespy models + # adding a heat pump in the GCC (by referencing evaporator and condenser) def show_heat_pump_in_gcc(self, evaporator, condenser): - from tespy.components import SimpleHeatExchanger, MovingBoundaryHeatExchanger, SectionedHeatExchanger + from tespy.components import SimpleHeatExchanger, MovingBoundaryHeatExchanger, SectionedHeatExchanger, HeatExchanger # create GCC diagram, if not done before try: @@ -248,18 +251,30 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): fig = self.gcc_fig ax = self.gcc_ax - # get the plotting data of the heat exchangers + + # TODO adjust for changes in tespy + """ + calc_sections() on HeatExchanger, SectionedHeatExchanger, + and MovingBoundaryHeatExchanger is deprecated. Section data is + now computed automatically after each solve and exposed as ComponentArrayProperties + attributes directly on the component: Q_sections, T_hot_sections, T_cold_sections, + Q_per_section, lmtd_per_section, and phases_per_section. Each attribute provides + .val in the network’s user-specified units and .val_SI in SI units. + The old return value of calc_sections() (a 5-tuple in user units) + is still returned but raises a + """ + + # get the plotting data of the heat exchangers # condensers if isinstance(condenser,SimpleHeatExchanger): # get plot data of heat exchangers at heat sink (taken from user meeting example of heat exchangers) condenser_Q_vals = [0, abs(condenser.Q.val)] condenser_T_vals = [condenser.outl[0].T.val,condenser.inl[0].T.val] - # to do: include a warning, if there is at least one change between to or from a latent stream - elif isinstance(condenser, SectionedHeatExchanger) or isinstance(condenser, MovingBoundaryHeatExchanger): - # get the data from calc_sections - condenser_Q_sections, condenser_T_steps_hot, condenser_T_steps_cold, condenser_Q_per_section, condenser_td_log_per_section = condenser.calc_sections() - condenser_Q_vals = [Q / 1000 for Q in condenser_Q_sections] # convert to kW - condenser_T_vals = [T - 273.17 for T in condenser_T_steps_hot] # convert to degC + # TODO: include a warning, if there is at least one change between to or from a latent stream + elif isinstance(condenser, SectionedHeatExchanger) or isinstance(condenser, MovingBoundaryHeatExchanger) or isinstance(condenser, HeatExchanger): + # get the data for the hot stream (refrigerant side) of condenser + condenser_T_vals = [T - 273.17 for T in condenser.T_hot_sections.val_SI] + condenser_Q_vals = [Q / 1000 for Q in condenser.Q_sections.val_SI] else: raise ValueError("The component type is not implemented as a condenser.") @@ -268,18 +283,15 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): # get plot data of heat exchangers at heat source (taken from user meeting example of heat exchangers) evaporator_Q_vals = [0, abs(evaporator.Q.val)] evaporator_T_vals = [evaporator.inl[0].T.val,evaporator.outl[0].T.val] - # to do: include a warning, if there is at least one change between to or from a latent stream - elif isinstance(evaporator, SectionedHeatExchanger) or isinstance(evaporator, MovingBoundaryHeatExchanger): - # get the data from calc_sections - evaporator_Q_sections, evaporator_T_steps_hot, evaporator_T_steps_cold, evaporator_Q_per_section, evaporator_td_log_per_section = evaporator.calc_sections() - evaporator_Q_vals = [Q / 1000 for Q in evaporator_Q_sections] # convert to kW - evaporator_T_vals = [T - 273.17 for T in evaporator_T_steps_cold] # convert to degC + # TODO: include a warning, if there is at least one change between to or from a latent stream + elif isinstance(evaporator, SectionedHeatExchanger) or isinstance(evaporator, MovingBoundaryHeatExchanger) or isinstance(evaporator, HeatExchanger): + # get the data for the cold stream (refrigerant side) of evaporator + evaporator_T_vals = [T - 273.17 for T in evaporator.T_cold_sections.val_SI] + evaporator_Q_vals = [Q / 1000 for Q in evaporator.Q_sections.val_SI] else: raise ValueError("The component type is not implemented as an evaporator.") - # add: expand these conditions in future for other types - # missing: HeatExchanger, ParallelFlowHeatExchanger, Desuperheater, Condenser - + # add: expand these conditions in future for other types: ParallelFlowHeatExchanger, Desuperheater, Condenser # show (only display) by adding the plot data of the heat exchangers to the GCC ax.plot(condenser_Q_vals, condenser_T_vals, "-",color="red") # as in heat exchanger example diff --git a/tutorial/advanced/example_pinch_analysis.py b/tutorial/advanced/example_pinch_analysis.py index b1d32e686..8998a5032 100644 --- a/tutorial/advanced/example_pinch_analysis.py +++ b/tutorial/advanced/example_pinch_analysis.py @@ -1,9 +1,13 @@ from tespy.tools.pinch_analysis import TesypPinchAnalysis +# setting up the heat pump system +from tespy.networks import Network +from tespy.connections import Connection +from tespy.components import SimpleHeatExchanger, Valve, Compressor, CycleCloser -# integrating a tespy model of a heat pump into a given process +# integrating a tespy model of a heat pump into a given process # example Pinch Analysis of a manual workflow without tespy components Example_Analysis = TesypPinchAnalysis("Example_Process_1") @@ -37,11 +41,6 @@ Example_Analysis.plot_gcc_diagram() -# setting up the heat pump system -from tespy.networks import Network -from tespy.connections import Connection -from tespy.components import SimpleHeatExchanger, Valve, Compressor, CycleCloser - # network nw = Network() @@ -88,7 +87,6 @@ Example_Analysis.show_heat_pump_in_gcc(condenser=condenser,evaporator=evaporator) - # second example using a moving boundary heat exchanger as the evaporator and a sectioned heat exchanger as the condenser # as the heat exchangers now include the internal H,T-Data, the desuperheater is not included anymore. # However, the connection numbers are kept to clarifiy the positions. From 451299f36f64fd29244def4b016a2ccffd6909ad Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sat, 4 Jul 2026 12:59:27 +0200 Subject: [PATCH 22/25] adding example to test HeatExchanger in pinch analysis --- src/tespy/tools/pinch_analysis.py | 1 + tutorial/advanced/example_pinch_analysis.py | 84 ++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index 2bb6079ca..bda00191b 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -6,6 +6,7 @@ # Arpagaus 2019 Hochtemperatur-Wärmepumpen (ISBN 978-3-8007-4550-0) # Kemp 2006 (p.20) (https://doi.org/10.1016/B978-0-7506-8260-2.X5001-9) # Walden et al. 2023 (https://doi.org/10.1016/j.apenergy.2023.121933) +# VDI VDI 4646:2026-05 class TesypPinchAnalysis(): diff --git a/tutorial/advanced/example_pinch_analysis.py b/tutorial/advanced/example_pinch_analysis.py index 8998a5032..0fd9b0beb 100644 --- a/tutorial/advanced/example_pinch_analysis.py +++ b/tutorial/advanced/example_pinch_analysis.py @@ -6,6 +6,7 @@ from tespy.components import SimpleHeatExchanger, Valve, Compressor, CycleCloser +# MARK: Example 1 # integrating a tespy model of a heat pump into a given process # example Pinch Analysis of a manual workflow without tespy components @@ -87,6 +88,7 @@ Example_Analysis.show_heat_pump_in_gcc(condenser=condenser,evaporator=evaporator) +# MARK: Example 2 # second example using a moving boundary heat exchanger as the evaporator and a sectioned heat exchanger as the condenser # as the heat exchangers now include the internal H,T-Data, the desuperheater is not included anymore. # However, the connection numbers are kept to clarifiy the positions. @@ -166,4 +168,84 @@ Example_Analysis2.add_cold_stream_manually(-40,80,80) # reference heat pump components for plotting in the GCC of the same pinch analysis -Example_Analysis2.show_heat_pump_in_gcc(condenser=condenser_2,evaporator=evaporator_2) \ No newline at end of file +Example_Analysis2.show_heat_pump_in_gcc(condenser=condenser_2,evaporator=evaporator_2) + + + +# MARK: Example 3 +# additional example to test HeatExchanger in pinch analysis +from tespy.components import HeatExchanger + +# network +nw_3 = Network() + +# taken from example heat pump +nw_3.units.set_defaults( + temperature="degC", pressure="bar", enthalpy="kJ/kg", heat="kW", power="kW" +) + +# components +condenser_3 = HeatExchanger("Condenser_3") +evaporator_3 = HeatExchanger("Evaporator_3") +expansion_valve_3 = Valve("Expansion Valve_3") +compressor_3 = Compressor("Compressor_3") +cycle_closer_3 = CycleCloser("Cycle Closer_3") +# Sinks and Sources for the secondary media +HeatSinkIn = Source("HeatSinkIn") +HeatSinkOut = Sink("HeatSinkOut") +HeatSourceIn = Source("HeatSourceIn") +HeatSourceOut = Sink("HeatSourceOut") + +# connections +c1_3 = Connection(evaporator_3, "out2", compressor_3,"in1", label = "connection 1_3") +c2_3 = Connection(compressor_3, "out1", condenser_3, "in1", "connection 2_3") +c4_3 = Connection(condenser_3, "out1", expansion_valve_3, "in1", label = "connection 4_3") +c5_3 = Connection(expansion_valve_3, "out1",cycle_closer_3, "in1", label = "connection 5_3") +c6_3 = Connection(cycle_closer_3, "out1", evaporator_3, "in2", label = "connection 6_3") +nw_3.add_conns(c1_3,c2_3,c4_3,c5_3,c6_3) + +# add the connections for secondary media +c7_3 = Connection(HeatSinkIn, "out1", condenser_3, "in2", label = "connection 7_3") +c8_3 = Connection(condenser_3, "out2", HeatSinkOut, "in1", label = "connection 8_3") +c9_3 = Connection(HeatSourceIn, "out1", evaporator_3, "in1", label = "connection 9_3") +c10_3 = Connection(evaporator_3, "out1", HeatSourceOut, "in1", label = "connection 10_3") +nw_3.add_conns(c7_3,c8_3,c9_3,c10_3) + +# set up general parameters of heat pump +compressor_3.set_attr(eta_s = 0.7) +condenser_3.set_attr(dp1=0, dp2=0, ttd_l=2) +evaporator_3.set_attr(dp1=0, dp2=0, ttd_l=2) +c1_3.set_attr(fluid={"R290": 1}) +# media, temperatures and pressure of heat source and sink +c7_3.set_attr(fluid={"Water": 1}, T=50, p=1) +c8_3.set_attr(T=55) +c9_3.set_attr(fluid={"Water": 1}, T=10, p=1) +c10_3.set_attr(T=5) + +# set up parameters to show specific case +c1_3.set_attr(m=0.1, x=1) +c4_3.set_attr(x=0) + +# solve design +nw_3.solve("design") + +# integrating a tespy model of a heat pump into a given process + +# example Pinch Analysis of a manual workflow without tespy components +Example_Analysis3 = TesypPinchAnalysis("Example_Process_3") + +# setting the minimum temperature difference for the analysis +Example_Analysis3.set_minimum_temperature_difference(10) + +# add all the streams manually +# Example: Kemp 2007 p. 20, reduced temperature by 50 degC to fit the heat pump example +Example_Analysis3.add_cold_stream_manually(-230, 20-50, 135-50) +Example_Analysis3.add_hot_stream_manually(330, 170-50, 60-50) +Example_Analysis3.add_cold_stream_manually(-240, 80-50, 140-50) +Example_Analysis3.add_hot_stream_manually(180, 150-50, 30-50) +# additional latent streams as shown by Arpagaus 2019 p. 99 +Example_Analysis3.add_hot_stream_manually(60,35,35) +Example_Analysis3.add_cold_stream_manually(-40,80,80) + +# reference heat pump components for plotting in the GCC of the same pinch analysis +Example_Analysis3.show_heat_pump_in_gcc(condenser=condenser_3,evaporator=evaporator_3) \ No newline at end of file From be6201bf974594ae2ccfd5985d18919ce936f834 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sat, 4 Jul 2026 13:00:51 +0200 Subject: [PATCH 23/25] remove comments for changes --- src/tespy/tools/pinch_analysis.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index bda00191b..7e2e2c15f 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -251,19 +251,6 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): # get the GCC fig = self.gcc_fig ax = self.gcc_ax - - - # TODO adjust for changes in tespy - """ - calc_sections() on HeatExchanger, SectionedHeatExchanger, - and MovingBoundaryHeatExchanger is deprecated. Section data is - now computed automatically after each solve and exposed as ComponentArrayProperties - attributes directly on the component: Q_sections, T_hot_sections, T_cold_sections, - Q_per_section, lmtd_per_section, and phases_per_section. Each attribute provides - .val in the network’s user-specified units and .val_SI in SI units. - The old return value of calc_sections() (a 5-tuple in user units) - is still returned but raises a - """ # get the plotting data of the heat exchangers # condensers From f54db3fefbd395e76b6ea17d8640edc6efa10dfc Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 5 Jul 2026 10:01:36 +0200 Subject: [PATCH 24/25] added missing temperature shifting for heat pump streams --- src/tespy/tools/pinch_analysis.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tespy/tools/pinch_analysis.py b/src/tespy/tools/pinch_analysis.py index 7e2e2c15f..22c726057 100644 --- a/src/tespy/tools/pinch_analysis.py +++ b/src/tespy/tools/pinch_analysis.py @@ -281,9 +281,15 @@ def show_heat_pump_in_gcc(self, evaporator, condenser): # add: expand these conditions in future for other types: ParallelFlowHeatExchanger, Desuperheater, Condenser + # shift the evaporator / condenser by the half the minimal temperature difference as cold / hot streams + # hot stream shifted downwards + condenser_shiftedT_vals = [T - self.min_dT/2 for T in condenser_T_vals] + # cold stream shifted upwards + evaporator_shiftedT_vals = [T - self.min_dT/2 for T in evaporator_T_vals] + # show (only display) by adding the plot data of the heat exchangers to the GCC - ax.plot(condenser_Q_vals, condenser_T_vals, "-",color="red") # as in heat exchanger example - ax.plot(evaporator_Q_vals, evaporator_T_vals, "-",color="blue") + ax.plot(condenser_Q_vals, condenser_shiftedT_vals, "-",color="red") # as in heat exchanger example + ax.plot(evaporator_Q_vals, evaporator_shiftedT_vals, "-",color="blue") # save figure fig.savefig(f"GCC_with_heat_pump_{self.label}.svg") From 815f4b65b9306b7f0ce4a4f0044a9d8474806580 Mon Sep 17 00:00:00 2001 From: n-arth <235067011+n-arth@users.noreply.github.com> Date: Sun, 5 Jul 2026 10:05:38 +0200 Subject: [PATCH 25/25] adjusting examples --- tutorial/advanced/example_pinch_analysis.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tutorial/advanced/example_pinch_analysis.py b/tutorial/advanced/example_pinch_analysis.py index 0fd9b0beb..dc03dac70 100644 --- a/tutorial/advanced/example_pinch_analysis.py +++ b/tutorial/advanced/example_pinch_analysis.py @@ -78,7 +78,7 @@ # in that case only the condensation is part of the condenser forming a horizontal line in the GCC as # the most simple example case c1.set_attr(m=0.1, p=5, x=1) -c3.set_attr(p=20, x=1) +c3.set_attr(p=21, x=1) c4.set_attr(x=0) # solve design @@ -137,8 +137,8 @@ evaporator_2.set_attr(dp1=0, dp2=0, td_pinch=2) c1_2.set_attr(fluid={"R290": 1}) # media, temperatures and pressure of heat source and sink -c7_2.set_attr(fluid={"Water": 1}, T=50, p=1) -c8_2.set_attr(T=55) +c7_2.set_attr(fluid={"Water": 1}, T=55, p=1) +c8_2.set_attr(T=60) c9_2.set_attr(fluid={"Water": 1}, T=10, p=1) c10_2.set_attr(T=5) @@ -217,8 +217,8 @@ evaporator_3.set_attr(dp1=0, dp2=0, ttd_l=2) c1_3.set_attr(fluid={"R290": 1}) # media, temperatures and pressure of heat source and sink -c7_3.set_attr(fluid={"Water": 1}, T=50, p=1) -c8_3.set_attr(T=55) +c7_3.set_attr(fluid={"Water": 1}, T=55, p=1) +c8_3.set_attr(T=60) c9_3.set_attr(fluid={"Water": 1}, T=10, p=1) c10_3.set_attr(T=5)