# Pinch Analysis and Waste Heat Recovery

In this post I will present a technique to model waste heat utilization using the pinch analysis. Therefore, I will explain the pinch technology among the problem table cascade. Furthermore, we will use Python to state our problem and do the pinch analysis.

### Introduction

First, *waste heat recovery* is a process where energy is removed from one system in order to utilize it for another process. Usually, this waste heat would be rejected without being used. For example, it can be used for process heating, space heating, service water heating or air reheating (air conditioning). Therefore, this is done to reduce the fuel consumption or the demand of electrical energy.

The stream which has a heat demand can be called heat sink. Furthermore, the stream where heat needs to be removed is called a heat source. Without external supply of energy, heat can only be transported from a hot to a cold stream. Therfore, the major limiting factor is the minimal driving temperature difference during the heat transfer. Therefore, the temperature range of heat source and sink need to be considered.

In order to predict the recoverable amount of waste heat, we need a modeling method. Hence, we can use the *pinch analysis* to identify potential energy savings in processes.

### Pinch Analysis

The **pinch analysis** is a method to analyze heat flows to identify recoverable waste heat. You can find a very good overview regarding pinch analysis in [1]. Additionally, the pinch analysis can be used to analyze water streams, but I will not go into further details.

Hence, designs and system optimizations can be derived. At first, data like start and target temperatures and heat flows , need to be extracted from heat balances. Furthermore, these data need to be processed.

Here, the heat capacity flow rate can be calculated from the heat flow and the temperature difference according to

(1)

Furthermore, let us assume a global minimal temperature difference of 10 K. Hence, the shifted temperatures ( and ) can be calculated as follows:

(2)

(3)

To explain the procedure sufficiently, let us consider the following system considering of 2 reactors and 4 heat exchangers.

And here are the 4 considered heat flows.

Type | T_start in °C | T_target in °C | Q_dot in kW | C_dot in kW/K | T_s_shift in °C | T_t_shift in °C |
---|---|---|---|---|---|---|

hot | 140 | 50 | 180 | 2 | 135 | 45 |

hot | 90 | 40 | 300 | 6 | 85 | 35 |

cold | 30 | 150 | 240 | 2 | 35 | 155 |

cold | 70 | 125 | 165 | 3 | 75 | 130 |

Principally, processes that have to be cooled form hot energy streams, while processes that have to be heated up form cold energy streams. By comparing these streams, we can determine heat recovery possibilities. The graphical allocation of the energy streams is shown in the following.

Now, we can start to use the shifted temperatures to build six temperature intervals of the streams. Afterwards, the temperature difference for each temperature interval can be calculated.

For each temperature interval the heat capacity flow rate of the source is subtracted from the heat capacity flow rate of the heat sink. Hence, by multiplying the temperature difference and the heat capacity flowrate difference we can calculate the internal heat flow.

In case the sign of the heat flow is positive, there is a heat flow decit in the corresponding interval. Therefore the cold streams are larger compared to the hot streams. This means the heat source cannot supply the heat sink entirely. In case the sign is negative, there is a heat flow surplus in the corresponding interval.

Interval | Temperature range in °C | Delta T Interval in K | Delta C Interval in kW/K | Q Interval in K | Balance |
---|---|---|---|---|---|

1 | 155 - 135 | 20 | 2 | 40 | Deficit |

2 | 135 - 130 | 5 | 0 | 0 | Balance |

3 | 130 - 85 | 45 | 3 | 135 | Deficit |

4 | 85 - 75 | 10 | -3 | -30 | Surplus |

5 | 75 - 45 | 30 | -6 | -180 | Surplus |

6 | 45 - 35 | 10 | -4 | -40 | Surplus |

From these previous calculations we can build the so called **problem table cascade**. Above the hottest temperature level of 150 °C there is no heat flow. Therefore, the table starts with 0 kW on the left side (a).

Afterwards, the internal heat flow is successively subtracted from the previous values . Furthermore, a negative sign in the -column represents a heat decit in the corresponding interval. Hence, heat flow surpluses from lower temperature levels cannot be used to cover heat flow decits from upper temperature levels.

Therefore, the largest negative value of -175 kW represents the heat flow which cannot be provided using waste heat utilization measures. Through the identication of the largest negative heat flow the heat cascade can be altered into the figure on the right (b).

Hence, the initial heat flow is set to 175 kW. Furthermore, the -column stays the same. Again, the internal heat flow is subsequently subtracted from the previous values.

Also, the top value of the problem table cascade is the remaining heat sink of 175 kW. From that, we can calculate the recovered heat flow which is 230 kW:

(4)

Furthermore, we can identify the remaining heat source of 250 kW which is the bottom value of the problem table cascade. Using this methodology, we can finally write the Python script.

### Python Code Pinch Analysis

Now, let is have a look at the Python code. It consists of the following elements:

- State input data
- Identify hot and cold streams
- Calculate shifted temperatures and heat capacity flow rates
- Build hot and cold heat cascade
- State problem table cascade and solve it
- Calculate remaining and recovered heat flows
- Prepare data and plot results.

#License: MIT License (http://opensource.org/licenses/MIT)

import numpy as np

import matplotlib.pyplot as plt

#===Input: Q_dot, T_start, T_target, dT_min

IN = [[180, 140, 50, 10], [300, 90, 40, 10], [240, 30, 150, 10], [165, 70, 125, 10]]

#===Get hot and cold stream index

hotStreamIndex = []

coldStreamIndex = []

for i in range(len(IN)):

if IN[i][1] > IN[i][2]:

hotStreamIndex.append(i)

elif IN[i][1] < IN[i][2]:

coldStreamIndex.append(i)

#===Calc source and sink heat flow

Q_dot_source = 0

for i in range(len(hotStreamIndex)):

Q_dot_source = Q_dot_source + IN[hotStreamIndex[i]][0]

Q_dot_sink = 0

for i in range(len(coldStreamIndex)):

Q_dot_sink = Q_dot_sink + IN[coldStreamIndex[i]][0]

#===Shifted temperature and heat capacity flow hot stream

for i in range(len(hotStreamIndex)):

deltaT = 0.5*IN[i][3]

row = hotStreamIndex[i]

IN[row].append(IN[row][1] - deltaT)

IN[row].append(IN[row][2] - deltaT)

IN[row].append(IN[row][0]/(IN[row][5]- IN[row][4]))

#===Shifted temperature and heat capacity flow cold stream

deltaT = 0

for i in range(len(coldStreamIndex)):

deltaT = 0.5*IN[i][3]

row = coldStreamIndex[i]

IN[row].append(IN[row][1] + deltaT)

IN[row].append(IN[row][2] + deltaT)

IN[row].append(IN[row][0]/(IN[row][5]- IN[row][4]))

print IN

#===Get temperatures and intervals

temperatures = []

for i in range(len(IN)):

temperatures.append(IN[i][4])

temperatures.append(IN[i][5])

#===Get sorting index

tempInd = np.argsort(temperatures)

#===Get rid of duplicates

temperatures = set(temperatures)

temperatures = list(temperatures)

#===Sort temperatures

temperatures.sort(reverse=True)

#===Set cascade temperatures

cascade = []

cascadeSink = []

cascadeSource = []

for i in range(len(temperatures)-1):

cascade.append([])

cascadeSink.append([])

cascadeSource.append([])

cascade[i].append(temperatures[i])

cascadeSink[i].append(temperatures[i])

cascadeSource[i].append(temperatures[i])

cascade[i].append(temperatures[i+1])

cascadeSink[i].append(temperatures[i+1])

cascadeSource[i].append(temperatures[i+1])

#===

for i in range(len(cascade)):

cascade[i].append(cascade[i][0] - cascade[i][1])

cascadeSink[i].append(cascade[i][0] - cascade[i][1])

cascadeSource[i].append(cascade[i][0] - cascade[i][1])

C_source = 0

C_sink = 0

for j in range(len(IN)):

if (cascade[i][0] <= IN[j][4]) and (cascade[i][1] >= IN[j][5]) and (IN[j][6] < 0):

C_source = C_source + IN[j][6]

#break

for j in range(len(IN)):

if (cascade[i][0] > IN[j][4]) and (cascade[i][1] < IN[j][5]) and (IN[j][6] > 0):

C_sink =C_sink + IN[j][6]

#break

cascade[i].append(C_source + C_sink)

cascade[i].append(cascade[i][3] * cascade[i][2])

cascadeSink[i].append(C_sink)

cascadeSink[i].append(cascadeSink[i][3] * cascadeSink[i][2])

cascadeSource[i].append(C_source)

cascadeSource[i].append(cascadeSource[i][3] * cascadeSource[i][2])

heatFlow = 0

ptCascade = []

ptCascade.append(heatFlow)

for i in range(len(cascade)):

heatFlow = heatFlow - cascade[i][4]

ptCascade.append(heatFlow)

#===Calc recovered and remaining heat flow

Q_dot_rem = min([n for n in ptCascade if n<0])

Q_dot_rec = Q_dot_sink - abs(Q_dot_rem)

heatFlow = abs(Q_dot_rem)

ptCascade = []

ptCascade.append(heatFlow)

for i in range(len(cascade)):

heatFlow = heatFlow - cascade[i][4]

ptCascade.append(heatFlow)

#===Prepare plot for source

heatFlowSource = []

tempSource = []

heatFlowSource.append(0.)

for i in reversed(cascadeSource):

if i[4] < 0:

heatFlowSource.append(heatFlowSource[-1] + i[4]*-1.)

tempSource.append(i[0] + deltaT)

tempSource.append(i[1] + deltaT)

#===Get rid of duplicates

tempSource = set(tempSource)

tempSource = list(tempSource)

#===Sort temperatures

tempSource.sort(reverse=False)

#===Prepare plot for sink

heatFlowSink = []

tempSink = []

heatFlowSink.append(ptCascade[-1]) #last element of cascade, start of sink

for i in reversed(cascadeSink):

if i[4] > 0:

heatFlowSink.append(heatFlowSink[-1] + i[4])

tempSink.append(i[0] - deltaT)

tempSink.append(i[1] - deltaT)

#===Get rid of duplicates

tempSink = set(tempSink)

tempSink = list(tempSink)

#===Sort temperatures

tempSink.sort(reverse=False)

#===Prepare plots

fig1 = plt.figure()

#===Plot cycles sink

ax1.plot(heatFlowSink, tempSink, 'b', linewidth=3)

ax1.plot(heatFlowSource, tempSource, 'r', linewidth=3)

plt.grid(True)

plt.xlabel('Heat flow in kW')

plt.ylabel('Temperature in C')

plt.show()

plt.savefig('pinch_result.png')

### Composite curve

The results of our calculations are the hot and cold composite curves. They are separated by the minimal driving temperature difference of 10 K. Furthermore, in the area were they lie on top of each other heat can be recovered. Also, we can identify the remaining heat source and heat sink, after heat is recovered.

### Conclusion

In process engineering waste heat recovery measures can be used to reduce the fuel consumption or the demand of electrical energy.

A sophisticated method to identify the recoverable waste heat is the pinch analysis. Therefore, heat streams are analyzed and a so called heat cascade is build. Afterwards, a problem cascade algorithm is used to solve the emerging problem. As a result, we receive the so called hot and cold composite curve. Thus, the recovered heat, remaining heat source and heat sink can be identified.

In the future, this article will be extended by a method called grand composite curve and by heat exchanger networks.

In case you wanna read more about pinch analysis applied to refrigeration systems, you can download an article in the downloads.

### Sources

[1] Linnhoff, Introduction to Pinch Technology, 1998, link### What do you think?

I’d like to hear what you think about this post.

Let me know by leaving a comment below and don’t forget to subscribe to this blog!

After going through the whole article, facts, and figures even I recognize that waste heat recovery measures can be used to reduce fuel consumption. Thanks for sharing such a wonderful piece of content and Great work with charts and figures.

To resolve “NameError: name ‘ax1’ is not defined”,

ax1 = plt.subplot(111)