Application Center - Maplesoft

App Preview:

Pinch analysis for heat integration

You can switch back to the summary page by clicking here.

Learn about Maple
Download Application


 

Pinch Analysis Calculations for Heat Integration Using Maple

Copyright 1993, 2000

L R Partin Enterprises

All Rights Reserved

http://users.chartertn.net/lpartin

Pinch analysis is a well established synthesis and analysis tool for the exchange of heat within a network of heat exchangers. Some of its capabilities are:

  • setting pre-design targets for utility consumption
  • setting a pre-design target for the installed cost of the heat exchanger network
  • designing the heat exchanger network
  • optimizing the trade-off between energy costs and capital cost
  • optimizing the selection of utility sources

It is therefore very useful in the synthesis and design of heat exchanger networks. There are numerous journal articles on the topic. Several of the articles describe cost savings from applying the technology.

The initial work was in E. C. Hohmann's PhD, "Optimum Networks for Heat Exchange," U niv. of S. Calif. (1971). Bodo Linnhoff and coworkers have done extensive research on the method. A few of the references include:

Linnhoff, B. and D. R. Vredeveld, "Pinch Technology Has Come of Age," Chem. Eng. Prog. , July (1984) 33-40

Linnhoff, B. and E. Hindmarsh, "The Pinch Design Method for Heat Exchanger Networks," Chem. Engng. Sci. , 38, No. 5, 745-763 (1983)

Tjoe, T. N. and B. Linnhoff, "Using Pinch Technology for Process Retrofit," Chem. Eng. , April 28, 1986


@

Maple Procedures for Pinch Analysis

The pinch analysis method is programmed in several Maple procedures.

> # Program a fn to retrieve composite CPcold given a shifted T
CPcold:=proc(Temp)
local j, CP:
global ColdStreams, ShiftedColdStreamTable:
CP:=0: # initialize result
for j from 1 to ColdStreams do
if ShiftedColdStreamTable[j,3] < Temp and
ShiftedColdStreamTable[j,4] > Temp then
CP:=CP+ShiftedColdStreamTable[j,2] fi
od:
CP:
end:
# Program a fn to retrieve composite CPhot given a shifted T
CPhot:=proc(Temp)
local j, CP:
global HotStreams, ShiftedHotStreamTable:
CP:=0: # initialize result
for j from 1 to HotStreams do
if ShiftedHotStreamTable[j,3] > Temp then
if ShiftedHotStreamTable[j,4] < Temp then
CP:=CP+ShiftedHotStreamTable[j,2]:
fi
fi
od:
CP:
end:
# Program a function for performing the Problem Data Table
ProblemDataTable:=proc(dTmin)
local i, j, k, CTempSet, HTempSet, TempSet, Qlist, items, minQ, Plist:
global ColdStreamTable, HotStreamTable, AbovePinch, BelowPinch,
CCdata, CCtitle, ColdC, ColdStreams, GCCdata, GCCtitle, HCdata, HotC,
HotStreams, MaxColdT, MaxHotT, MinColdT, MinHotT, PinchT, PinchTo, QC,
QCo, QH, QHo, ShiftedColdStreamTable, ShiftedColdTemperatures,
ShiftedHotStreamTable, ShiftedHotTemperatures, ShiftedTemperatures,
Tlist, Umin, Uminap, Uminbp, dTo:
# Get the number of cold streams entered
ColdStreams:=op(2,op(1,[op(2,eval(ColdStreamTable))])):
# Get the number of hot streams entered
HotStreams:=op(2,op(1,[op(2,eval(HotStreamTable))])):
# Shift the cold and hot stream temperatures
ShiftedColdStreamTable:=array(1..ColdStreams,1..5):
for i from 1 to ColdStreams
do ShiftedColdStreamTable[i,1]:=ColdStreamTable[i,1]:
ShiftedColdStreamTable[i,2]:=ColdStreamTable[i,2]:
ShiftedColdStreamTable[i,3]:=ColdStreamTable[i,3]+dTmin/2:
ShiftedColdStreamTable[i,4]:=ColdStreamTable[i,4]+dTmin/2:
ShiftedColdStreamTable[i,5]:=ColdStreamTable[i,5]:
od:
ShiftedHotStreamTable:=array(1..HotStreams,1..5):
for i from 1 to HotStreams
do ShiftedHotStreamTable[i,1]:=HotStreamTable[i,1]:
ShiftedHotStreamTable[i,2]:=HotStreamTable[i,2]:
ShiftedHotStreamTable[i,3]:=HotStreamTable[i,3]-dTmin/2:
ShiftedHotStreamTable[i,4]:=HotStreamTable[i,4]-dTmin/2:
ShiftedHotStreamTable[i,5]:=HotStreamTable[i,5]:
od:
# Extract the temperatures from the shifted tables, columns 2 and 3
ShiftedColdTemperatures:=[
op(convert(linalg[col](ShiftedColdStreamTable,3),list)),
op(convert(linalg[col](ShiftedColdStreamTable,4),list)) ]:
ShiftedHotTemperatures:=[
op(convert(linalg[col](ShiftedHotStreamTable,3),list)),
op(convert(linalg[col](ShiftedHotStreamTable,4),list)) ]:
# Find the min & max of the curves,
MinColdT:=min(op(ShiftedColdTemperatures)):
MaxColdT:=max(op(ShiftedColdTemperatures)):
MinHotT:=min(op(ShiftedHotTemperatures)):
MaxHotT:=max(op(ShiftedHotTemperatures)):
# Combine the temperatures,
ShiftedTemperatures:=[op(ShiftedColdTemperatures),
op(ShiftedHotTemperatures) ]:
# Delete duplicate entries by using the set style of data:
CTempSet:=convert(ShiftedColdTemperatures,set):
HTempSet:=convert(ShiftedHotTemperatures,set):
TempSet:=convert(ShiftedTemperatures,set):
ShiftedColdTemperatures:=convert(CTempSet,list):
ShiftedHotTemperatures:=convert(HTempSet,list):
ShiftedTemperatures:=convert(TempSet,list):
# Sort the shifted temperatures
ShiftedColdTemperatures:=sort(ShiftedColdTemperatures):
ShiftedHotTemperatures:=sort(ShiftedHotTemperatures):
ShiftedTemperatures:=sort(ShiftedTemperatures):
Tlist:=ShiftedTemperatures:
# Perform the energy flow balances
items:=nops(Tlist):
Qlist:=array(1..items):
Qlist[items]:=0:
Plist:=array(1..2*items):
for j from items-1 by -1 to 1 do # apply energy cascade formula
Qlist[j]:=Qlist[j+1]+(CPhot(Tlist[j]+.001)-CPcold(Tlist[j]+.001))
*(Tlist[j+1]-Tlist[j]):
od:
minQ:=min(op(convert(Qlist,list))): # find minimum cascade energy
# subtract the minimum from each value
for j from 1 to items do Qlist[j]:=Qlist[j]-minQ od:
# prepare data for plotting the Grand Composite Curve
for j from 1 to items do
Plist[j*2-1]:=Qlist[j]:
Plist[j*2]:=Tlist[j]:
if abs(Qlist[j])<.00001 then PinchT:=Tlist[j] fi
od:
QH:=Qlist[items]:
QC:=Qlist[1]:
# pass the results as a list
GCCdata:=convert(Plist,list):
# calculate the Cold Composite data
items:=nops(ShiftedColdTemperatures):
Plist:=array(1..2*items):
ColdC:=array(1..items):
ColdC[1]:=QC:
for j from 2 to items do
ColdC[j]:=ColdC[j-1]+CPcold(ShiftedColdTemperatures[j]-.001)
*(ShiftedColdTemperatures[j]-ShiftedColdTemperatures[j-1]):
od:
for j from 1 to items do
Plist[j*2-1]:=ColdC[j]:
Plist[j*2]:=ShiftedColdTemperatures[j]:
od:
ColdC:=convert(ColdC,list):
CCdata:=convert(Plist,list):
CCdata:=[seq([CCdata[2*k-1],CCdata[2*k]],k=1..nops(CCdata)/2)]:
# calculate the Hot Composite data
items:=nops(ShiftedHotTemperatures):
Plist:=array(1..2*items):
HotC:=array(1..items):
HotC[1]:=0:
for j from 2 to items do
HotC[j]:=HotC[j-1]+CPhot(ShiftedHotTemperatures[j]-.001)
*(ShiftedHotTemperatures[j]-ShiftedHotTemperatures[j-1]):
od:
for j from 1 to items do
Plist[j*2-1]:=HotC[j]:
Plist[j*2]:=ShiftedHotTemperatures[j]:
od:
HotC:=convert(HotC,list):
HCdata:=convert(Plist,list):
HCdata:=[seq([HCdata[2*k-1],HCdata[2*k]],k=1..nops(HCdata)/2)]:
# calculate the streams below the pinch
BelowPinch:=table():
for j from 1 to ColdStreams do
if PinchT > ShiftedColdStreamTable[j,3] then
BelowPinch[j]:=ShiftedColdStreamTable[j,1] fi
od:
for j from 1 to HotStreams do
if PinchT > ShiftedHotStreamTable[j,4] then
BelowPinch[ColdStreams+j]:=ShiftedHotStreamTable[j,1] fi
od:
BelowPinch:=convert(BelowPinch,set): # delete multiple entries
BelowPinch:=convert(BelowPinch,list): # convert to a list
BelowPinch:=sort(BelowPinch): # sort the list
Uminbp:=nops(BelowPinch): # Minimum HX units below pinch
# calculate the streams above the pinch
AbovePinch:=table():
for j from 1 to ColdStreams do
if PinchT < ShiftedColdStreamTable[j,4] then
AbovePinch[j]:=ShiftedColdStreamTable[j,1] fi
od:
for j from 1 to HotStreams do
if PinchT < ShiftedHotStreamTable[j,3] then
AbovePinch[ColdStreams+j]:=ShiftedHotStreamTable[j,1] fi
od:
AbovePinch:=convert(AbovePinch,set): # delete multiple entries
AbovePinch:=convert(AbovePinch,list): # convert to a list
AbovePinch:=sort(AbovePinch): # sort the list
Uminap:=nops(AbovePinch): # Minimum HX units above pinch
Umin:=Uminbp+Uminap:
QHo:=convert(evalf(QH,6),string):
QCo:=convert(evalf(QC,6),string):
PinchTo:=convert(evalf(PinchT,5),string):
dTo:=convert(evalf(dTmin,5),string):
CCtitle:=`Shifted Composite Curves @dTmin=`||dTo||`K;`||` QH=`||QHo:
CCtitle:=``||CCtitle||` QC=`||QCo||` and Pinch=`||PinchTo||`K;`:
GCCtitle:=`Grand Composite Curve @dTmin=`||dTo||`K;`||` QH=`||QHo:
GCCtitle:=``||GCCtitle||` QC=`||QCo||` and Pinch=`||PinchTo||`K;`:
# pass significant resutls
`QH = `||QHo||` and QC = `||QCo||`; Pinch T = `||PinchTo||`(shifted)`||`; Umin = `||Umin:
end:

# Provide a fn to return the cold composite stream T given H.
ColdTemp:=proc(Hval)
local j, slopeval, b, tries, Tresult:
global ColdC, ShiftedColdTemperatures, dTmin:
tries:=nops(ShiftedColdTemperatures)-1:
Tresult:=0:
for j from 1 to tries do
if Hval >= ColdC[j] and
Hval <= ColdC[j+1] then
slopeval:=(ShiftedColdTemperatures[j+1]-ShiftedColdTemperatures[j])
/(ColdC[j+1]-ColdC[j]):
b:=ShiftedColdTemperatures[j+1]-slopeval*ColdC[j+1]:
Tresult:=slopeval*Hval+b-dTmin/2: # apply linear fit/use actual T
fi
od:
Tresult:
end:

# Provide a fn to return the hot composite stream T given H.
HotTemp:=proc(Hval)
local j, slopeval, b, tries, Tresult:
global HotC, ShiftedHotTemperatures, dTmin:
tries:=nops(ShiftedHotTemperatures)-1:
Tresult:=0:
for j from 1 to tries do
if Hval >= HotC[j] and
Hval <= HotC[j+1] then
slopeval:=(ShiftedHotTemperatures[j+1]-ShiftedHotTemperatures[j])
/(HotC[j+1]-HotC[j]):
b:=ShiftedHotTemperatures[j+1]-slopeval*HotC[j+1]:
Tresult:=slopeval*Hval+b+dTmin/2: # apply linear fit/use actual T
fi
od:
Tresult:
end:

# calculate area target for the network
# note: ProblemDataTable must be executed prior to area.
AreaTarget:=proc(TColdUtility,hColdUtility,THotUtility,hHotUtility)
local i, j, number, T1,T2,T3,T4, qh, dTlm, items,
numera, denomer:
global ColdC, ColdStreamTable, ColdStreams, HotC, HotStreamTable,
HotStreams, Hintervals, area:
# calculations are performed for all intervals of enthalpy
# finding all intervals,
Hintervals:=[op(ColdC),op(HotC)]: # using data from composite curves
Hintervals:=convert(Hintervals,set): # delete multiple entries
Hintervals:=sort(convert(Hintervals,list)): # sort the values
number:=nops(Hintervals): # number of enthalpy values
# loop through intervals of enthalpy for area target
area:=0:
for i from 2 to number do
# get interval temperatures, cold streams
# (use cold utility T if cold streams are not available)
T1:=ColdTemp(Hintervals[i-1]):
T2:=ColdTemp(Hintervals[i]):
# test for cold utility used for the interval
if T1=0 then T1:=TColdUtility:
T2:=TColdUtility: fi:
# get interval temperatures, hot streams
# (use hot utility T if hot streams are not available)
T3:=HotTemp(Hintervals[i-1]):
T4:=HotTemp(Hintervals[i]):
# test for hot utility used for the interval
if T4=0 then T3:=THotUtility:
T4:=THotUtility: fi:
# calculate interval delta T log mean
numera:=T3-T1:
denomer:=T4-T2:
if numera = denomer then
dTlm:=numera
else
dTlm:=((T3-T1)-(T4-T2))/ln(numera/denomer):
fi:
# cycle through cold streams
qh:=0:
if Hintervals[i-1]<ColdC[1] then # cold utility domain
qh:=evalf((Hintervals[i]-Hintervals[i-1])/hColdUtility):
else for j from 1 to ColdStreams do # cycle through cold streams
if (T1+T2)/2 > ColdStreamTable[j,3] and
(T1+T2)/2 < ColdStreamTable[j,4] then
qh:=evalf(qh)+evalf(ColdStreamTable[j,2]*(T2-T1)
/ColdStreamTable[j,5]) fi:
od
fi:
# cycle through hot streams
items:=nops(HotC):
if Hintervals[i-1]>HotC[items] then # hot utility domain
qh:=evalf(qh)+
evalf((Hintervals[i]-Hintervals[i-1])/hHotUtility):
else for j from 1 to HotStreams do # cycle through hot streams
if (T3+T4)/2 < HotStreamTable[j,3] and
(T3+T4)/2 > HotStreamTable[j,4] then
qh:=evalf(qh)+evalf(HotStreamTable[j,2]*(T4-T3)
/HotStreamTable[j,5]) fi:
od
fi:
# add to the area summation
area:=evalf(area)+evalf(1/dTlm*qh):
od:
# return the area target
area:
end:

#
# a function for providing the dT between composite curves versus Tcold
# note: AreaTarget must be executed prior to this routine
dTcurve:=proc(THotUtility)
local i, number, delete, Tcold, Thot, dT, dTinfo:
global Hintervals, count, dTdata:
# cycle through all enthalpy entries
number:=nops(Hintervals):
dTinfo:=array(1..2*number):
delete:=0:
for i from 1 to number do
Tcold:=ColdTemp(Hintervals[i]):
Thot:=HotTemp(Hintervals[i]):
if Thot = 0 then Thot:=THotUtility fi:
dT:=Thot-Tcold:
if Tcold > 0 then
dTinfo[2*i-1]:=evalf(Tcold):
dTinfo[2*i]:=evalf(dT):
else dTinfo[2*i-1]:=0:
dTinfo[2*i]:=0:
delete:=delete+1:
fi:
od:
count:=0:
# extract only the values with entries
dTdata:=array(1..2*(number-delete)):
for i from 1 to number do
if dTinfo[2*i-1]>0 then
count:=count+1:
dTdata[2*count-1]:=dTinfo[2*i-1]:
dTdata[2*count]:=dTinfo[2*i]:
fi:
od:
dTdata:=convert(dTdata,list):
end:

# function for calculating installed heat exchanger cost
# (assume that each heat exchanger is if equal size and type.
# assume that the minimum number of exchangers to meet the
# energy target are used.)
# exchanger cost function: ln(cost) = a + b*ln(area) + c*ln(area)^2
# CostAdjust, adjusts from cost function year to purchase year
# LangFactor, factors equipment cost to installed plant cost
CapitalCost:=proc(a,b,c,CostAdjust,LangFactor)
global Umin, CostperUnit, ExchangerCapital:
CostperUnit:=exp(a+b*ln(area/Umin)+c*ln(area/Umin)^2)*CostAdjust:
# return the total heat exchanger installed cost
ExchangerCapital:=evalf(CostperUnit*Umin*LangFactor):
evalf(ExchangerCapital):
end:

# function to sum the net present cost of the exchanger capital and
# utilities
NetPresentCost:=proc(ColdUtilityNPCfactor,HotUtilityNPCfactor,
CapitalNPCfactor)
global ExchangerCapital, QC, QH, NPC:
NPC:=evalf(ColdUtilityNPCfactor*QC+HotUtilityNPCfactor*QH
+CapitalNPCfactor*ExchangerCapital):
evalf(NPC):
end:

OptimizeData:=proc(dTstart,Range,Increment)
local i:
global CapitalNPCfactor, ColdUtilityNPCfactor, CostAdjust,
HotUtilityNPCfactor, LangFactor, QC, QH, TColdUtility,
THotUtility, capA, capB, capC, hColdUtility, hHotUtility,
AreaTargetvalues, CapitalTargetvalues, NPCopt, NPCvalues,
QCvalues, QHvalues, UtilityNPCvalues, dTmin, dTminopt, opt:
# prepare output arrays
QCvalues:=array(1..2*Range):
QHvalues:=array(1..2*Range):
AreaTargetvalues:=array(1..2*Range):
CapitalTargetvalues:=array(1..2*Range):
NPCvalues:=array(1..2*Range):
UtilityNPCvalues:=array(1..2*Range):
# cycle through the desired dTmin range
for i from 1 to Range do
dTmin:=dTstart+i*Increment-Increment:
ProblemDataTable(dTmin):
QCvalues[2*i-1]:=dTmin:
QCvalues[2*i]:=QC:
QHvalues[2*i-1]:=dTmin:
QHvalues[2*i]:=QH:
AreaTargetvalues[2*i-1]:=dTmin:
AreaTargetvalues[2*i]:=AreaTarget(TColdUtility,hColdUtility,
THotUtility,hHotUtility):
CapitalTargetvalues[2*i-1]:=dTmin:
CapitalTargetvalues[2*i]:=CapitalCost(capA,capB,capC,
CostAdjust,LangFactor)/1000000.:
NPCvalues[2*i-1]:=dTmin:
NPCvalues[2*i]:=evalf(NetPresentCost(ColdUtilityNPCfactor,
HotUtilityNPCfactor,CapitalNPCfactor)/1000000.,5):
UtilityNPCvalues[2*i-1]:=dTmin:
UtilityNPCvalues[2*i]:=NPCvalues[2*i]-CapitalTargetvalues[2*i]
*CapitalNPCfactor:
od:
QCvalues:=convert(QCvalues,list):
QHvalues:=convert(QHvalues,list):
AreaTargetvalues:=convert(AreaTargetvalues,list):
CapitalTargetvalues:=convert(CapitalTargetvalues,list):
NPCvalues:=convert(NPCvalues,list):
UtilityNPCvalues:=convert(UtilityNPCvalues,list):
# find the optimum value
opt:=1:
for i from 2 to Range do
if NPCvalues[2*i]<NPCvalues[2*opt] then
opt:=i fi:
od:
dTminopt:=convert(NPCvalues[2*opt-1],string):
NPCopt:=convert(NPCvalues[2*opt],string):
`Optimum at dTmin = `||dTminopt||` K with NPC = `||NPCopt||` ($M)`:
end:

#
# a function to print a stream grid in ascii format
# note: ColdStreamTable and HotStreamTable must be
# defined first.
# note: multiple stream segments are not handled.
StreamGrid:=proc()
local i, ID, Hval, CP, line, T1, T2:
global ColdStreamTable, HotStreamTable, ColdStreams, HotStreams:
# Get the number of cold streams entered
ColdStreams:=op(2,op(1,[op(2,eval(ColdStreamTable))])):
# Get the number of hot streams entered
HotStreams:=op(2,op(1,[op(2,eval(HotStreamTable))])):
printf("Stream Grid\n"):
printf("\n"):
for i from 1 to HotStreams do
printf("\n"):
T1:=convert(HotStreamTable[i,3],string):
T2:=convert(HotStreamTable[i,4],string):
printf(" "||T1||" "||T2||"\n"):
ID:=HotStreamTable[i,1]:
ID:=convert(ID,string):
CP:=HotStreamTable[i,2]:
Hval:=CP*(HotStreamTable[i,3]-HotStreamTable[i,4]):
CP:=convert(CP,string):
Hval:=convert(evalf(Hval,4),string):
line:="("||ID||")"||"--------------------------------------> CP="||CP||",H="||Hval||"\n":
printf(line):
od:
printf("\n"):
printf("\n"):
for i from 1 to ColdStreams do
printf("\n"):
T1:=convert(ColdStreamTable[i,4],string):
T2:=convert(ColdStreamTable[i,3],string):
printf(" "||T1||" "||T2||"\n"):
ID:=ColdStreamTable[i,1]:
ID:=convert(ID,string):
CP:=ColdStreamTable[i,2]:
Hval:=CP*(ColdStreamTable[i,4]-ColdStreamTable[i,3]):
CP:=convert(CP,string):
Hval:=convert(evalf(Hval,4),string):
line:="<--------------------------------------("||ID||") CP="\
||CP||",H="||Hval||"\n":
printf(line):
od:
end:

Stream Data (Cold and Hot Streams)

The heat exchanger network has the task of heating and/or cooling a collection of process streams. For example, a hot water stream may require cooling before it is sent to a waste treatment facility. The engineer must first define all of the stream requirements. The heating/cooling requirements are met by interchanging the heat between streams or by using utilities.

Cold Streams

The cold streams are the streams that must be heated to a higher temperature. Each cold stream is assigned a stream ID as a sequential integer starting at 1. Four more values are required for each cold stream:

CP (kw/K), stream heat capacity * stream flowrate

Tsupply (K), stream supply (initial) temperature

Ttarget (K), stream target (required) temperature which is higher than Tsupply

h (kw/sqm-K), estimated heat transfer coefficient for the stream within a heat exchanger

Input the Cold Stream data array as follows:

Stream ID, CP (kw/K), Tsupply (K), Ttarget (K), h (kw/sqm-K)

> ColdStreamTable:=array([ [ 1, 1, 325, 375, .4 ],

> [ 2, 3, 325, 425, .6 ],
[ 3, 2, 375, 450, .5 ] ] );

ColdStreamTable := matrix([[1, 1, 325, 375, .4], [2...

Hot Streams

The hot streams are the streams that must be cooled to a lower temperature. Each hot stream is assigned a stream ID as a sequential integer following the assignment of cold stream numbers. Four more values are required for each hot stream:

CP (kw/K), stream heat capacity * stream flowrate

Tsupply (K), stream supply (initial) temperature

Ttarget (K), stream target (required) temperature which is higher than Tsupply

h (kw/sqm-K), estimated heat transfer coefficient for the stream within a heat exchanger

Input the Hot Stream data array as follows:

Stream ID, CP (kw/K), Tsupply (K), Ttarget (K), h (kw/sqm-K)

> HotStreamTable:= array([ [ 4, 2, 425, 350, .7 ],

> [ 5, 4, 375, 300, .5 ] ] );

HotStreamTable := matrix([[4, 2, 425, 350, .7], [5,...

Stream Grid

The stream grid gives a visual representation of the heating and cooling requirements. It contains the information from the stream tables along with the calculation of the heat requirement for each stream as H (kw) = CP * (Ttarget-Tsupply). The stream grid also provides a good tool for designing the heat exchanger network.

Stream Grid:

> StreamGrid();

Stream Grid

 425                                   350

(4)--------------------------------------> CP=2,H=150.

 375                                   300

(5)--------------------------------------> CP=4,H=300.

 375                                   325

<--------------------------------------(1) CP=1,H=50.

 425                                   325

<--------------------------------------(2) CP=3,H=300.

 450                                   375

<--------------------------------------(3) CP=2,H=150.

Problem Definition Table

Pinch analysis defines the minimum temperature approach that occurs within the network as the Delta T min. Its value sets the heating and cooling targets for the network.

The minimum utility duties are calculated via a problem definition table. The hot stream temperatures are shifted by -1/2 dTmin and the cold stream temperatures are shifted by 1/2 dTmin. The hot streams are combined into the hot composite curve and the cold streams are combined into the cold composite curve. The problem data table then calculates the pinch temperature where dTmin is achieved along with the utility targets.

dTmin (K), delta T minimum of pinch analysis

QH (kw), minimum (target) hot utility requirement

QC (kw), minimum (target) cold utility requirement

Pinch T (K), temperature location where dTmin occurs

Umin, minimum number of heat exchangers to meet the design target

> dTmin:=10; # 10 K is a common starting point for the design
ProblemDataTable(dTmin);

dTmin := 10

`QH = 240. and QC = 190.; Pinch T = 370.(shifted); ...

Composite Curves

The results from the problem definition table are commonly plotted as composite curves in shifted temperatures as shown below. The hot composite curve is in red and the cold composite curve is in blue on the plot. The two curves touch at the pinch temperature. QC is the horizontal distance between the starting points of the composite curves and QH is the horizontal distance between the end points of the two composite curves. QC and QH are provided by cold and hot utilities. Heat is interchanged between streams where the curves overlap.

Composite Curves:

> p1:=plots[pointplot](HCdata,connect=true,color=red):
p2:=plots[pointplot](CCdata,connect=true,color=blue):
plots[display](p1,p2,title=CCtitle,labels=[`H(kw)`,`Tshifted(K)`]);

[Maple Plot]

Grand Composite Curve

The Grand Composite Curve is a plot of the difference in enthalpies between cold and hot composite curves. It is commonly used in determining the temperature levels required for the hot and cold utilities. It is plotted as the shifted temperatures versus the enthalpy difference.

Grand Composite Curve:

> plots[pointplot](GCCdata,title=GCCtitle,connect=true,
labels=[`H(kw)`,`Tshifted(K)`]);

[Maple Plot]

Economic Trade-off Between Operating Costs and Capital Cost

The optimum selection of the dTmin value is made by optimizing the trade-off between operating costs and the installation cost of the network. The installation of larger heat exchangers can reduce operating costs for utilities but at the expense of a higher capital cost for the network. The trade-off is best made by minimizing the net present cost of capital and operating costs.

Utility Data

The calculations need information about the available utilities.

Cold utility usage net present cost factor ($NPC/kw)

> ColdUtilityNPCfactor:=70:

Cold utility T (K)

> TColdUtility:=290:

Cold utility heat transfer coefficient (kw/sqm-K)

> hColdUtility:=.4:

Hot utility usage net present cost factor ($NPC/kw)

> HotUtilityNPCfactor:=500:

Hot utility T (K)

> THotUtility:=500:

Hot utility heat transfer coefficient (kw/sqm-K)

> hHotUtility:=2.:

Capital Cost Data

The heat exchanger cost formula ($) is: ln(cost) = capA + capB * ln(area,sqm)

+ capC*ln(area,sqm)^2

> capA:=7.5:
capB:=0.24:
capC:=0.06:

The heat exchanger capital costs are adjusted to the purchase year by a factor.

> CostAdjust:=1.2:

The sum of heat exchanger costs are scaled to the installed network cost by a Lang factor.

> LangFactor:=5.:

The capital NPC factor is needed to convert the capital cost to net present cost terms. ($NPC/$capital)

> CapitalNPCfactor:=.90:

Perform the Optimization

The optimization is performed by calculating the net present cost total for utilities and capital over a range of dTmin values.

> dTstart:=5:
Range:=10:
Increment:=2:
ans:=OptimizeData(dTstart,Range,Increment);

ans := `Optimum at dTmin = 15 K with NPC = .32790 (...

dTmin Optimization Plot

> plots[pointplot](NPCvalues,title=`NPC vs. dTmin; `||ans,connect=true,labels=[`dTmin(K)`,`NPC($M)`]);

[Maple Plot]

Capital Cost Plot

> plots[pointplot](CapitalTargetvalues,title=`Target Capital Cost ($M) vs. dTmin`,connect=true,labels=[`dTmin(K)`,`NPC($M)`]);

[Maple Plot]

Utility Cost Plot

> plots[pointplot](UtilityNPCvalues,title=`Target Utility Costs ($M) vs. dTmin`,connect=true,labels=[`dTmin(K)`,`NPC($M)`]);

[Maple Plot]