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:
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 ] ] );
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:
Input the Hot Stream data array as follows:
> HotStreamTable:= array([ [ 4, 2, 425, 350, .7 ],
> [ 5, 4, 375, 300, .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();
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);
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)`]);
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)`]);
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);
dTmin Optimization Plot
> plots[pointplot](NPCvalues,title=`NPC vs. dTmin; `||ans,connect=true,labels=[`dTmin(K)`,`NPC($M)`]);
Capital Cost Plot
> plots[pointplot](CapitalTargetvalues,title=`Target Capital Cost ($M) vs. dTmin`,connect=true,labels=[`dTmin(K)`,`NPC($M)`]);
Utility Cost Plot
> plots[pointplot](UtilityNPCvalues,title=`Target Utility Costs ($M) vs. dTmin`,connect=true,labels=[`dTmin(K)`,`NPC($M)`]);