Coverage for backend/pinch_service/OpenPinch/src/classes/target.py: 80%

220 statements  

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

1from __future__ import annotations 

2from typing import TYPE_CHECKING 

3import pandas as pd 

4from ..lib.schema import * 

5from ..lib.enums import * 

6from ..lib.config import * 

7from .value import Value 

8from .stream_collection import StreamCollection 

9from .problem_table import ProblemTable 

10 

11if TYPE_CHECKING: 

12 from .zone import Zone 

13 

14class Target(): 

15 """Class representing energy targets.""" 

16 

17 def __init__(self, name: str = "untitled", identifier: str = TargetType.DI.value, parent_zone: Zone = None, config: Optional[Configuration] = None): 

18 

19 # === Metadata === 

20 self._config = config or Configuration() 

21 self._parent_zone = parent_zone 

22 self._identifier = identifier 

23 self._name = name 

24 self._active = True 

25 

26 self._graphs = {} 

27 

28 # === Streams & Utilities === 

29 self._hot_utilities: StreamCollection = StreamCollection() 

30 self._cold_utilities: StreamCollection = StreamCollection() 

31 

32 # === System Data === 

33 self._problem_table = ProblemTable({col.value: pd.Series(dtype='float64') for col in ProblemTableLabel}) 

34 self._problem_table_real = ProblemTable({col.value: pd.Series(dtype='float64') for col in ProblemTableLabel}) 

35 

36 # === Thermal Targeting === 

37 self._hot_pinch = 0.0 

38 self._cold_pinch = 0.0 

39 self._heat_recovery_target = 0.0 

40 self._heat_recovery_limit = 0.0 

41 self._hot_utility_target = 0.0 

42 self._cold_utility_target = 0.0 

43 self._utility_heat_recovery_target = 0.0 

44 self._degree_of_int = 100.0 

45 self._num_units = 0 

46 

47 # === Exergy Targeting === 

48 self._exergy_sinks = 0.0 

49 self._exergy_sources = 0.0 

50 self._exergy_des_min = 0.0 

51 self._exergy_req_min = 0.0 

52 self._w_target = 0.0 

53 self._w_eff_target = 0.0 

54 self._ETE = 0.0 

55 

56 # === Cost Targeting === 

57 self._area = 0.0 

58 self._capital_cost = 0.0 

59 self._total_cost = 0.0 

60 self._utility_cost = 0.0 

61 

62 # === Properties === 

63 

64 @property 

65 def name(self): return self._name 

66 @name.setter 

67 def name(self, value): self._name = value 67 ↛ exitline 67 didn't return from function 'name' because

68 

69 @property 

70 def identifier(self): return self._identifier 70 ↛ exitline 70 didn't return from function 'identifier' because the return on line 70 wasn't executed

71 @identifier.setter 

72 def identifier(self, value): self._identifier = value 72 ↛ exitline 72 didn't return from function 'identifier' because

73 

74 @property 

75 def config(self): return self._config 

76 @config.setter 

77 def config(self, value): self._config = value 77 ↛ exitline 77 didn't return from function 'config' because

78 

79 @property 

80 def parent_zone(self): return self._parent_zone 80 ↛ exitline 80 didn't return from function 'parent_zone' because the return on line 80 wasn't executed

81 @parent_zone.setter 

82 def parent_zone(self, value): self._parent_zone = value 82 ↛ exitline 82 didn't return from function 'parent_zone' because

83 

84 @property 

85 def active(self) -> bool: 

86 """Whether the stream is active in analysis.""" 

87 if isinstance(self._active, Value): 

88 return self._active.value 

89 else: 

90 return self._active 

91 @active.setter 

92 def active(self, value: bool): 

93 self._active = Value(value) 

94 

95 @property 

96 def pt(self): return self._problem_table 96 ↛ exitline 96 didn't return from function 'pt' because the return on line 96 wasn't executed

97 @pt.setter 

98 def pt(self, data): self._problem_table = data 

99 

100 @property 

101 def pt_real(self): return self._problem_table_real 101 ↛ exitline 101 didn't return from function 'pt_real' because the return on line 101 wasn't executed

102 @pt_real.setter 

103 def pt_real(self, data): self._problem_table_real = data 

104 

105 @property 

106 def hot_utilities(self): return self._hot_utilities 

107 @hot_utilities.setter 

108 def hot_utilities(self, data): self._hot_utilities = data 

109 

110 @property 

111 def cold_utilities(self): return self._cold_utilities 

112 @cold_utilities.setter 

113 def cold_utilities(self, data): self._cold_utilities = data 

114 

115 @property 

116 def graphs(self): return self._graphs 

117 @graphs.setter 

118 def graphs(self, data): self._graphs = data 

119 

120 @property 

121 def utility_streams(self): 

122 return self._hot_utilities + self._cold_utilities 

123 

124 

125 @property 

126 def area(self): return self._area 126 ↛ exitline 126 didn't return from function 'area' because the return on line 126 wasn't executed

127 @area.setter 

128 def area(self, value): self._area = value 128 ↛ exitline 128 didn't return from function 'area' because

129 

130 @property 

131 def capital_cost(self): return self._capital_cost 131 ↛ exitline 131 didn't return from function 'capital_cost' because the return on line 131 wasn't executed

132 @capital_cost.setter 

133 def capital_cost(self, value): self._capital_cost = value 133 ↛ exitline 133 didn't return from function 'capital_cost' because

134 

135 @property 

136 def cold_pinch(self): return self._cold_pinch 

137 @cold_pinch.setter 

138 def cold_pinch(self, value): self._cold_pinch = value 

139 

140 @property 

141 def cold_utility_target(self): return self._cold_utility_target 

142 @cold_utility_target.setter 

143 def cold_utility_target(self, value): self._cold_utility_target = value 

144 

145 @property 

146 def degree_of_int(self): return self._degree_of_int 

147 @degree_of_int.setter 

148 def degree_of_int(self, value): self._degree_of_int = value 

149 

150 @property 

151 def ETE(self): return self._ETE 151 ↛ exitline 151 didn't return from function 'ETE' because the return on line 151 wasn't executed

152 @ETE.setter 

153 def ETE(self, value): self._ETE = value 153 ↛ exitline 153 didn't return from function 'ETE' because

154 

155 @property 

156 def exergy_des_min(self): return self._exergy_des_min 156 ↛ exitline 156 didn't return from function 'exergy_des_min' because the return on line 156 wasn't executed

157 @exergy_des_min.setter 

158 def exergy_des_min(self, value): self._exergy_des_min = value 158 ↛ exitline 158 didn't return from function 'exergy_des_min' because

159 

160 @property 

161 def exergy_req_min(self): return self._exergy_req_min 161 ↛ exitline 161 didn't return from function 'exergy_req_min' because the return on line 161 wasn't executed

162 @exergy_req_min.setter 

163 def exergy_req_min(self, value): self._exergy_req_min = value 163 ↛ exitline 163 didn't return from function 'exergy_req_min' because

164 

165 @property 

166 def exergy_sinks(self): return self._exergy_sinks 166 ↛ exitline 166 didn't return from function 'exergy_sinks' because the return on line 166 wasn't executed

167 @exergy_sinks.setter 

168 def exergy_sinks(self, value): self._exergy_sinks = value 168 ↛ exitline 168 didn't return from function 'exergy_sinks' because

169 

170 @property 

171 def exergy_sources(self): return self._exergy_sources 171 ↛ exitline 171 didn't return from function 'exergy_sources' because the return on line 171 wasn't executed

172 @exergy_sources.setter 

173 def exergy_sources(self, value): self._exergy_sources = value 173 ↛ exitline 173 didn't return from function 'exergy_sources' because

174 

175 @property 

176 def heat_recovery_target(self): return self._heat_recovery_target 

177 @heat_recovery_target.setter 

178 def heat_recovery_target(self, value): self._heat_recovery_target = value 

179 

180 @property 

181 def heat_recovery_limit(self): return self._heat_recovery_limit 

182 @heat_recovery_limit.setter 

183 def heat_recovery_limit(self, value): self._heat_recovery_limit = value 

184 

185 @property 

186 def utility_heat_recovery_target(self): return self._utility_heat_recovery_target 186 ↛ exitline 186 didn't return from function 'utility_heat_recovery_target' because the return on line 186 wasn't executed

187 @utility_heat_recovery_target.setter 

188 def utility_heat_recovery_target(self, value): self._utility_heat_recovery_target = value 188 ↛ exitline 188 didn't return from function 'utility_heat_recovery_target' because

189 

190 @property 

191 def hot_pinch(self): return self._hot_pinch 

192 @hot_pinch.setter 

193 def hot_pinch(self, value): self._hot_pinch = value 

194 

195 @property 

196 def hot_utility_target(self): return self._hot_utility_target 

197 @hot_utility_target.setter 

198 def hot_utility_target(self, value): self._hot_utility_target = value 

199 

200 @property 

201 def num_units(self): return self._num_units 201 ↛ exitline 201 didn't return from function 'num_units' because the return on line 201 wasn't executed

202 @num_units.setter 

203 def num_units(self, value): self._num_units = value 203 ↛ exitline 203 didn't return from function 'num_units' because

204 

205 @property 

206 def capital_cost(self): return self._capital_cost 206 ↛ exitline 206 didn't return from function 'capital_cost' because the return on line 206 wasn't executed

207 @capital_cost.setter 

208 def capital_cost(self, value): self._capital_cost = value 208 ↛ exitline 208 didn't return from function 'capital_cost' because

209 

210 @property 

211 def total_cost(self): return self._total_cost 211 ↛ exitline 211 didn't return from function 'total_cost' because the return on line 211 wasn't executed

212 @total_cost.setter 

213 def total_cost(self, value): self._total_cost = value 213 ↛ exitline 213 didn't return from function 'total_cost' because

214 

215 @property 

216 def utility_cost(self): return self._utility_cost 

217 @utility_cost.setter 

218 def utility_cost(self, value): self._utility_cost = value 218 ↛ exitline 218 didn't return from function 'utility_cost' because

219 

220 @property 

221 def work_target(self): return self._w_target 221 ↛ exitline 221 didn't return from function 'work_target' because the return on line 221 wasn't executed

222 @work_target.setter 

223 def work_target(self, value): self._w_target = value 223 ↛ exitline 223 didn't return from function 'work_target' because

224 

225 @property 

226 def turbine_efficiency_target(self): return self._w_eff_target 226 ↛ exitline 226 didn't return from function 'turbine_efficiency_target' because the return on line 226 wasn't executed

227 @turbine_efficiency_target.setter 

228 def turbine_efficiency_target(self, value): self._w_eff_target = value 228 ↛ exitline 228 didn't return from function 'turbine_efficiency_target' because

229 

230 @property 

231 def target_values(self): return self._target_values 231 ↛ exitline 231 didn't return from function 'target_values' because the return on line 231 wasn't executed

232 @target_values.setter 

233 def target_values(self, value_dict): 

234 self._target_values = value_dict 

235 for key, value in value_dict.items(): 

236 setattr(self, key, value) 

237 

238 

239 # === Methods === 

240 def add_graph(self, name: str, result): 

241 self._graphs[name] = result 

242 

243 

244 def calc_utility_cost(self): 

245 self._utility_cost = sum([u.ut_cost for u in self.utility_streams]) 

246 return self._utility_cost 

247 

248 

249 def serialize_json(self, isTotal=False): 

250 """ 

251 Serialize process into json for return data 

252 """ 

253 

254 degree_of_integration = None 

255 if(self.degree_of_int): 255 ↛ 258line 255 didn't jump to line 258 because the condition on line 255 was always true

256 degree_of_integration = self.degree_of_int * 100 

257 

258 data = { 

259 'name': self.name, 

260 'degree_of_integration': degree_of_integration, 

261 'Qh': self.hot_utility_target, 

262 'Qc': self.cold_utility_target, 

263 'Qr': self.heat_recovery_target, 

264 'utility_cost': self.utility_cost, 

265 'row_type': SummaryRowType.FOOTER.value if isTotal else SummaryRowType.CONTENT.value, 

266 'hot_utilities': [], 

267 'cold_utilities': [], 

268 'temp_pinch': { 

269 'cold_temp': None, 

270 'hot_temp': None 

271 } 

272 } 

273 

274 if isinstance(self.cold_pinch, float) and isinstance(self.hot_pinch, float): 274 ↛ 281line 274 didn't jump to line 281 because the condition on line 274 was always true

275 if abs(self.cold_pinch - self.hot_pinch) < ZERO: 

276 temp_pinch = {'cold_temp': self.cold_pinch} 

277 data['temp_pinch'] = temp_pinch 

278 else: 

279 temp_pinch = {'cold_temp': self.cold_pinch, 'hot_temp': self.hot_pinch} 

280 data['temp_pinch'] = temp_pinch 

281 elif isinstance(self.cold_pinch, float): 

282 temp_pinch = {'cold_temp': self.cold_pinch} 

283 data['temp_pinch'] = temp_pinch 

284 elif isinstance(self.hot_pinch, float): 

285 temp_pinch = {'hot_temp': self.hot_pinch} 

286 data['temp_pinch'] = temp_pinch 

287 

288 if self.config.TURBINE_WORK_BUTTON: 288 ↛ 289line 288 didn't jump to line 289 because the condition on line 288 was never true

289 data['work_target'] = self.work_target 

290 data['turbine_efficiency_target'] = self.turbine_efficiency_target * 100 

291 

292 if self.config.AREA_BUTTON: 292 ↛ 293line 292 didn't jump to line 293 because the condition on line 292 was never true

293 data['area'] = self.area 

294 data['num_units'] = self.num_units 

295 data['capital_cost'] = self.capital_cost 

296 data['total_cost'] = self.total_cost 

297 

298 if self.config.EXERGY_BUTTON: 298 ↛ 299line 298 didn't jump to line 299 because the condition on line 298 was never true

299 data['exergy_sources'] = self.exergy_sources 

300 data['exergy_sinks'] = self.exergy_sinks 

301 data['ETE'] = self.ETE * 100 

302 data['exergy_req_min'] = self.exergy_req_min 

303 data['exergy_des_min'] = self.exergy_des_min 

304 

305 for utility in self.hot_utilities: 

306 data['hot_utilities'].append({'name': utility.name, 'heat_flow': utility.heat_flow}) 

307 for utility in self.cold_utilities: 

308 data['cold_utilities'].append({'name': utility.name, 'heat_flow': utility.heat_flow}) 

309 

310 return data