Coverage for backend/pinch_service/OpenPinch/src/utils/heat_exchanger_eq.py: 5%

149 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-11-06 23:27 +0000

1import math 

2from ..lib.enums import HeatExchangerTypes as HX 

3 

4 

5def HX_Eff(Arrangement, Ntu, c, Passes=None, Rows=None, Cmin_Phase=None): 

6 if Passes == None: 

7 Passes = 1 

8 

9 Ntu = Ntu / Passes 

10 if Ntu > 0 and c >= 0: 

11 # Counter Flow - Single Pass Effectiveness 

12 if Arrangement == HX.CF.value: 

13 # test = c * math.exp(-Ntu * (1 - c)) 

14 if c != 1 and c * math.exp(-Ntu * (1 - c)) != 1: 

15 eff = (1 - math.exp(-Ntu * (1 - c))) / (1 - c * math.exp(-Ntu * (1 - c))) 

16 else: 

17 eff = Ntu / (1 + Ntu) 

18 # Parallel Flow - Single Pass Effectiveness 

19 elif Arrangement == HX.PF.value: 

20 eff = (1 - math.exp(-Ntu * (1 + c))) / (1 + c) 

21 # Cross Flow - Both Streams Unmixed Effectiveness 

22 elif Arrangement == HX.CrFUU: 

23 if Rows == None or Cmin_Phase == None: 

24 eff = CrossflowUnmixedEff1(Ntu, c) 

25 else: 

26 eff = CrossflowUnmixedEff2(Ntu, c, Rows, Cmin_Phase) 

27 # Cross Flow - Both Streams Mixed Effectiveness 

28 elif Arrangement == HX.CrFMM: 

29 eff = (1 / (1 - math.exp(-Ntu)) + c / (1 - math.exp(-Ntu * c)) - 1 / Ntu) ** -1 

30 # Cross Flow - Stream Cmax Unmixed Effectiveness 

31 elif Arrangement == HX.CrFMUmax: 

32 eff = 1 - math.exp(-1 / c * (1 - math.exp(-Ntu * c))) 

33 # Cross Flow - Stream Cmin Unmixed Effectiveness 

34 elif Arrangement == HX.CrFMUmin: 

35 eff = 1 / c * (1 - math.exp(-c * (1 - math.exp(-Ntu)))) 

36 # Shell and Tube - One Shell Pass; 2,4,6, etc., Tube Passes Effectiveness 

37 elif Arrangement == HX.ShellTube.value: 

38 d = (1 + c ** 2) ** 0.5 

39 eff = 2 / ((1 + c) + d ** 0.5 * Coth(Ntu * d / 2)) 

40 # Condensing or Evaporating of One Fluid 

41 elif Arrangement == HX.CondEvap: 

42 eff = 1 - math.exp(-Ntu) 

43 else: 

44 eff = HX_Eff(HX.CF.value, Ntu, c, 1) 

45 else: 

46 eff = 0 

47 

48 if Passes > 1: 

49 Eff_p = eff 

50 return MultiPassEff(Eff_p, c, Passes) 

51 else: 

52 return eff 

53 

54 

55def HX_NTU(Arrangement, eff, c, Passes=None): 

56 if Passes == None: 

57 Passes = 1 

58 

59 if Passes > 1: 

60 Eff_p = MultiPassNTU(eff, c, Passes) 

61 eff = Eff_p 

62 

63 if eff > 0 and eff < 1: 

64 # Counter Flow - Single Pass Effectiveness 

65 if Arrangement == HX.CF.value: 

66 if c != 1: 

67 Ntu = 1 / (1 - c) * math.log((1 - eff * c) / (1 - eff)) 

68 else: 

69 Ntu = eff / (1 - eff) 

70 # Parallel Flow - Single Pass Effectiveness 

71 elif Arrangement == HX.PF.value: 

72 Ntu = -math.log(1 - eff * (1 + c)) / (1 + c) 

73 # Cross Flow - Both Streams Unmixed NTU 

74 elif Arrangement == HX.CrFUU: 

75 Ntu = HX_NTU_Numerical(Arrangement, eff, c) 

76 # Cross Flow - Both Streams Mixed NTU 

77 elif Arrangement == HX.CrFMM: 

78 Ntu = HX_NTU_Numerical(Arrangement, eff, c) 

79 # Cross Flow - Stream Cmax Unmixed NTU 

80 elif Arrangement == HX.CrFMUmax: 

81 Ntu = -1 / c * math.log(1 + c * math.log(1 - eff)) 

82 # Cross Flow - Stream Cmin Unmixed NTU 

83 elif Arrangement == HX.CrFMUmin: 

84 Ntu = -math.log(1 + 1 / c * math.log(1 - eff * c)) 

85 # Shell and Tube - One Shell Pass; 2,4,6, etc., Tube Passes NTU 

86 elif Arrangement == HX.ShellTube.value: 

87 D1 = 1 + c - (1 + c ** 2) ** (1 / 4) 

88 D2 = 1 + c + (1 + c ** 2) ** (1 / 4) 

89 Ntu = (1 + c ** 2) ** -0.5 * math.log((2 - eff * D1) / (2 - eff * D2)) 

90 # Condensing or Evaporating of One Fluid 

91 elif Arrangement == HX.CondEvap: 

92 Ntu = -math.log(1 - eff) 

93 else: 

94 Ntu = -1 

95 else: 

96 Ntu = 0 

97 

98 return Ntu * Passes 

99 

100 

101def CalcAreaUE(Arrangement, U, C_p, T_p1, T_p2, T_u1, T_u2, Passes): 

102 Q = C_p * abs(T_p1 - T_p2) 

103 C_u = Q / abs(T_u1 - T_u2) 

104 if C_p < C_u: 

105 eff = Q / C_p / abs(T_p1 - T_u1) 

106 c = C_p / C_u 

107 Ntu = HX_NTU(Arrangement, eff, c, Passes) 

108 return Ntu * C_p / U 

109 else: 

110 eff = Q / C_u / abs(T_p1 - T_u1) 

111 c = C_u / C_p 

112 Ntu = HX_NTU(Arrangement, eff, c, Passes) 

113 return Ntu * C_u / U 

114 

115 

116def eNTU_slope_Numerical(Arrangement, Ntu, c, Passes): 

117 dx = 1e-6 

118 if Ntu > 0: 

119 return (HX_Eff(Arrangement, Ntu + dx, c, Passes) - HX_Eff(Arrangement, Ntu, c, Passes)) / dx 

120 

121 

122def Coth(R): 

123 return (math.exp(2 * R) + 1) / (math.exp(2 * R) - 1) 

124 

125 

126def MultiPassEff(eff, c, Passes): 

127 if c != 1: 

128 return (((1 - eff * c) / (1 - eff)) ** Passes - 1) / (((1 - eff * c) / (1 - eff)) ** Passes - c) 

129 else: 

130 return Passes * eff / (1 + eff * (Passes - 1)) 

131 

132 

133def MultiPassNTU(Eff_p, c, Passes): 

134 if c != 1: 

135 return (((1 - Eff_p * c) / (1 - Eff_p)) ** (1 / Passes) - 1) / (((1 - Eff_p * c) / (1 - Eff_p)) ** (1 / Passes) - c) 

136 else: 

137 return Eff_p / (Passes - Eff_p * (Passes - 1)) 

138 

139 

140def CrossflowUnmixedEff1(Ntu, c): 

141 Sum_Pn = 0 

142 for i in range(1, 21): 

143 Pn = 0 

144 for j in range(1, i): 

145 Pn = Pn + c ** i / math.factorial(i + 1) * (i - j + 1) / math.factorial(j) * Ntu ** (i + j) 

146 Sum_Pn = Sum_Pn + Pn 

147 return 1 - math.exp(-Ntu) - math.exp(-(1 + c) * Ntu) * Sum_Pn 

148 

149 

150def CrossflowUnmixedEff2(Ntu, c, Rows, Cmin_fluid): 

151 # ESDU 86018 

152 if Cmin_fluid == 'Air': 

153 if Rows == 1: 

154 eff = 1 / c * (1 - math.exp(-c * (1 - math.exp(-Ntu)))) 

155 elif Rows == 2: 

156 k = 1 - math.exp(-Ntu / 2) 

157 eff = 1 / c * (1 - math.exp(-2 * k * c) * (1 + c * k ** 2)) 

158 elif Rows == 3: 

159 k = 1 - math.exp(-Ntu / 3) 

160 eff = 1 / c * (1 - math.exp(-3 * k * c) * (1 + c * k ** 2 * (3 - k) + (3 * c ** 2 * k ** 4) / 2)) 

161 elif Rows == 4: 

162 k = 1 - math.exp(-Ntu / 4) 

163 eff = 1 / c * (1 - math.exp(-4 * k * c) * (1 + c * k ** 2 * (6 - 4 * k + k ** 2) + 4 * c ** 2 * k ** 4 * (2 - k) + (8 * c ** 3 * k ** 6) / 3)) 

164 elif Rows > 4: 

165 eff = CrossflowUnmixedEff1(Ntu, c) 

166 else: 

167 if Rows == 1: 

168 eff = 1 - math.exp(-1 / c * (1 - math.exp(-Ntu * c))) 

169 elif Rows == 2: 

170 k = 1 - math.exp(-Ntu * c / 2) 

171 eff = 1 - math.exp(-2 * k / c) * (1 + (k ** 2) / c) 

172 elif Rows == 3: 

173 k = 1 - math.exp(-Ntu * c / 3) 

174 eff = 1 - math.exp(-3 * k / c) * (1 + k ** 2 * (3 - k) / c + (3 * k ** 4) / (2 * c ** 2)) 

175 elif Rows == 4: 

176 k = 1 - math.exp(-Ntu * c / 4) 

177 eff = 1 - math.exp(-4 * k / c) * (1 + k ** 2 * (6 - 4 * k + k ** 2) / c + 4 * k ** 4 * (2 - k) / c ** 2 + (8 * k ** 6) / (3 * c ** 3)) 

178 elif Rows > 4: 

179 eff = CrossflowUnmixedEff1(Ntu, c) 

180 

181 return eff 

182 

183 

184def HX_NTU_Numerical(Arrangement, eff, c): 

185 NTU1 = 0.001 

186 NTU2 = 0.1 

187 eps = 1e-5 

188 f = 100.0 

189 count = 1 

190 F1 = eff - HX_Eff(Arrangement, NTU1, c) 

191 F2 = eff - HX_Eff(Arrangement, NTU2, c) 

192 while f > eps: 

193 a = (F1 - F2) / (NTU1 - NTU2) 

194 b = F1 - a * NTU1 

195 NTU3 = -b / a 

196 F3 = eff - HX_Eff(Arrangement, NTU3, c) 

197 f = abs(F3) 

198 NTU1 = NTU2 

199 F1 = F2 

200 NTU2 = NTU3 

201 F2 = F3 

202 count += 1 

203 if count > 50: 

204 raise ValueError('Solution does not converge') 

205 

206 return NTU3