exerpy 0.0.2__py3-none-any.whl → 0.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. exerpy/__init__.py +2 -4
  2. exerpy/analyses.py +849 -304
  3. exerpy/components/__init__.py +3 -0
  4. exerpy/components/combustion/base.py +53 -35
  5. exerpy/components/component.py +8 -8
  6. exerpy/components/heat_exchanger/base.py +188 -121
  7. exerpy/components/heat_exchanger/condenser.py +98 -62
  8. exerpy/components/heat_exchanger/simple.py +237 -137
  9. exerpy/components/heat_exchanger/steam_generator.py +46 -41
  10. exerpy/components/helpers/cycle_closer.py +61 -34
  11. exerpy/components/helpers/power_bus.py +117 -0
  12. exerpy/components/nodes/deaerator.py +176 -58
  13. exerpy/components/nodes/drum.py +50 -39
  14. exerpy/components/nodes/flash_tank.py +218 -43
  15. exerpy/components/nodes/mixer.py +249 -69
  16. exerpy/components/nodes/splitter.py +173 -0
  17. exerpy/components/nodes/storage.py +130 -0
  18. exerpy/components/piping/valve.py +311 -115
  19. exerpy/components/power_machines/generator.py +105 -38
  20. exerpy/components/power_machines/motor.py +111 -39
  21. exerpy/components/turbomachinery/compressor.py +214 -68
  22. exerpy/components/turbomachinery/pump.py +215 -68
  23. exerpy/components/turbomachinery/turbine.py +182 -74
  24. exerpy/cost_estimation/__init__.py +65 -0
  25. exerpy/cost_estimation/turton.py +1260 -0
  26. exerpy/data/cost_correlations/cepci_index.json +135 -0
  27. exerpy/data/cost_correlations/component_mapping.json +450 -0
  28. exerpy/data/cost_correlations/material_factors.json +428 -0
  29. exerpy/data/cost_correlations/pressure_factors.json +206 -0
  30. exerpy/data/cost_correlations/turton2008.json +726 -0
  31. exerpy/data/cost_correlations/turton2008_design_analysis_synthesis_components_tables.pdf +0 -0
  32. exerpy/data/cost_correlations/turton2008_design_analysis_synthesis_components_theory.pdf +0 -0
  33. exerpy/functions.py +389 -264
  34. exerpy/parser/from_aspen/aspen_config.py +57 -48
  35. exerpy/parser/from_aspen/aspen_parser.py +373 -280
  36. exerpy/parser/from_ebsilon/__init__.py +2 -2
  37. exerpy/parser/from_ebsilon/check_ebs_path.py +15 -19
  38. exerpy/parser/from_ebsilon/ebsilon_config.py +328 -226
  39. exerpy/parser/from_ebsilon/ebsilon_functions.py +205 -38
  40. exerpy/parser/from_ebsilon/ebsilon_parser.py +392 -255
  41. exerpy/parser/from_ebsilon/utils.py +16 -11
  42. exerpy/parser/from_tespy/tespy_config.py +33 -1
  43. exerpy/parser/from_tespy/tespy_parser.py +151 -0
  44. {exerpy-0.0.2.dist-info → exerpy-0.0.4.dist-info}/METADATA +43 -2
  45. exerpy-0.0.4.dist-info/RECORD +57 -0
  46. exerpy-0.0.2.dist-info/RECORD +0 -44
  47. {exerpy-0.0.2.dist-info → exerpy-0.0.4.dist-info}/WHEEL +0 -0
  48. {exerpy-0.0.2.dist-info → exerpy-0.0.4.dist-info}/licenses/LICENSE +0 -0
@@ -2,8 +2,7 @@ import logging
2
2
 
3
3
  import numpy as np
4
4
 
5
- from exerpy.components.component import Component
6
- from exerpy.components.component import component_registry
5
+ from exerpy.components.component import Component, component_registry
7
6
 
8
7
 
9
8
  @component_registry
@@ -100,12 +99,19 @@ class Pump(Component):
100
99
  Flag indicating whether physical exergy is split into thermal and mechanical components.
101
100
 
102
101
  """
103
- # Get power flow if not already available
104
- if self.P is None:
105
- self.P = self.outl[0]['m'] * (self.outl[0]['h'] - self.inl[0]['h'])
102
+ # Get power flow
103
+ if (
104
+ 1 in self.inl
105
+ and self.inl[1] is not None
106
+ and self.inl[1].get("kind") == "power"
107
+ and "energy_flow" in self.inl[1]
108
+ ):
109
+ self.P = self.inl[1]["energy_flow"]
110
+ else:
111
+ self.P = self.outl[0]["m"] * (self.outl[0]["h"] - self.inl[0]["h"])
106
112
 
107
113
  # First, check for the invalid case: outlet temperature smaller than inlet temperature.
108
- if self.inl[0]['T'] > self.outl[0]['T']:
114
+ if self.inl[0]["T"] > self.outl[0]["T"]:
109
115
  logging.warning(
110
116
  f"Exergy balance of pump '{self.name}' where outlet temperature ({self.outl[0]['T']}) "
111
117
  f"is smaller than inlet temperature ({self.inl[0]['T']}) is not implemented."
@@ -114,39 +120,43 @@ class Pump(Component):
114
120
  self.E_F = np.nan
115
121
 
116
122
  # Case 1: Both temperatures above ambient
117
- elif round(self.inl[0]['T'], 5) >= T0 and round(self.outl[0]['T'], 5) > T0:
118
- self.E_P = self.outl[0]['m'] * (self.outl[0]['e_PH'] - self.inl[0]['e_PH'])
123
+ elif round(self.inl[0]["T"], 5) >= T0 and round(self.outl[0]["T"], 5) > T0:
124
+ self.E_P = self.outl[0]["m"] * (self.outl[0]["e_PH"] - self.inl[0]["e_PH"])
119
125
  self.E_F = abs(self.P)
120
126
 
121
127
  # Case 2: Inlet below, outlet above ambient
122
- elif round(self.inl[0]['T'], 5) < T0 and round(self.outl[0]['T'], 5) > T0:
128
+ elif round(self.inl[0]["T"], 5) < T0 and round(self.outl[0]["T"], 5) > T0:
123
129
  if split_physical_exergy:
124
- self.E_P = (self.outl[0]['m'] * self.outl[0]['e_T'] +
125
- self.outl[0]['m'] * (self.outl[0]['e_M'] - self.inl[0]['e_M']))
126
- self.E_F = abs(self.P) + self.inl[0]['m'] * self.inl[0]['e_T']
130
+ self.E_P = self.outl[0]["m"] * self.outl[0]["e_T"] + self.outl[0]["m"] * (
131
+ self.outl[0]["e_M"] - self.inl[0]["e_M"]
132
+ )
133
+ self.E_F = abs(self.P) + self.inl[0]["m"] * self.inl[0]["e_T"]
127
134
  else:
128
- logging.warning("While dealing with pump below ambient, "
129
- "physical exergy should be split into thermal and mechanical components!")
130
- self.E_P = self.outl[0]['m'] * (self.outl[0]['e_PH'] - self.inl[0]['e_PH'])
135
+ logging.warning(
136
+ "While dealing with pump below ambient, "
137
+ "physical exergy should be split into thermal and mechanical components!"
138
+ )
139
+ self.E_P = self.outl[0]["m"] * (self.outl[0]["e_PH"] - self.inl[0]["e_PH"])
131
140
  self.E_F = abs(self.P)
132
141
 
133
142
  # Case 3: Both temperatures below ambient
134
- elif round(self.inl[0]['T'], 5) < T0 and round(self.outl[0]['T'], 5) <= T0:
143
+ elif round(self.inl[0]["T"], 5) < T0 and round(self.outl[0]["T"], 5) <= T0:
135
144
  if split_physical_exergy:
136
- self.E_P = self.outl[0]['m'] * (self.outl[0]['e_M'] - self.inl[0]['e_M'])
137
- self.E_F = abs(self.P) + self.inl[0]['m'] * (self.inl[0]['e_T'] -
138
- self.outl[0]['e_T'])
145
+ self.E_P = self.outl[0]["m"] * (self.outl[0]["e_M"] - self.inl[0]["e_M"])
146
+ self.E_F = abs(self.P) + self.inl[0]["m"] * (self.inl[0]["e_T"] - self.outl[0]["e_T"])
139
147
  else:
140
- logging.warning("While dealing with pump below ambient, "
141
- "physical exergy should be split into thermal and mechanical components!")
142
- self.E_P = self.outl[0]['m'] * (self.outl[0]['e_PH'] - self.inl[0]['e_PH'])
148
+ logging.warning(
149
+ "While dealing with pump below ambient, "
150
+ "physical exergy should be split into thermal and mechanical components!"
151
+ )
152
+ self.E_P = self.outl[0]["m"] * (self.outl[0]["e_PH"] - self.inl[0]["e_PH"])
143
153
  self.E_F = abs(self.P)
144
154
 
145
155
  # Invalid case: outlet temperature smaller than inlet
146
156
  else:
147
157
  logging.warning(
148
- 'Exergy balance of a pump where outlet temperature is smaller '
149
- 'than inlet temperature is not implemented.'
158
+ "Exergy balance of a pump where outlet temperature is smaller "
159
+ "than inlet temperature is not implemented."
150
160
  )
151
161
  self.E_P = np.nan
152
162
  self.E_F = np.nan
@@ -157,37 +167,36 @@ class Pump(Component):
157
167
 
158
168
  # Log the results
159
169
  logging.info(
160
- f"Pump exergy balance calculated: "
170
+ f"Exergy balance of Pump {self.name} calculated: "
161
171
  f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
162
172
  f"Efficiency={self.epsilon:.2%}"
163
173
  )
164
174
 
165
-
166
175
  def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
167
176
  """
168
177
  Auxiliary equations for the pump.
169
-
178
+
170
179
  This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
171
180
  the following auxiliary cost relations:
172
-
181
+
173
182
  (1) Chemical exergy cost equation (if enabled):
174
183
  1/E_CH_in * C_CH_in - 1/E_CH_out * C_CH_out = 0
175
184
  - F-principle: specific chemical exergy costs equalized between inlet/outlet
176
-
185
+
177
186
  (2) Thermal/Mechanical exergy cost equations (based on temperature conditions):
178
-
187
+
179
188
  Case 1 (T_in > T0, T_out > T0):
180
189
  1/dET * C_T_out - 1/dET * C_T_in - 1/dEM * C_M_out + 1/dEM * C_M_in = 0
181
190
  - P-principle: relates inlet/outlet thermal and mechanical exergy costs
182
-
191
+
183
192
  Case 2 (T_in ≤ T0, T_out > T0):
184
193
  1/E_T_out * C_T_out - 1/dEM * C_M_out + 1/dEM * C_M_in = 0
185
194
  - P-principle: relates outlet thermal and inlet/outlet mechanical exergy costs
186
-
195
+
187
196
  Case 3 (T_in ≤ T0, T_out ≤ T0):
188
197
  1/E_T_out * C_T_out - 1/E_T_in * C_T_in = 0
189
198
  - F-principle: specific thermal exergy costs equalized between inlet/outlet
190
-
199
+
191
200
  Parameters
192
201
  ----------
193
202
  A : numpy.ndarray
@@ -202,7 +211,7 @@ class Pump(Component):
202
211
  Data structure for storing equation labels.
203
212
  chemical_exergy_enabled : bool
204
213
  Flag indicating whether chemical exergy auxiliary equations should be added.
205
-
214
+
206
215
  Returns
207
216
  -------
208
217
  A : numpy.ndarray
@@ -217,9 +226,17 @@ class Pump(Component):
217
226
  # --- Chemical equality equation (row added only if enabled) ---
218
227
  if chemical_exergy_enabled:
219
228
  # Set the chemical cost equality:
220
- A[counter, self.inl[0]["CostVar_index"]["CH"]] = (1 / self.inl[0]["E_CH"]) if self.inl[0]["e_CH"] != 0 else 1
221
- A[counter, self.outl[0]["CostVar_index"]["CH"]] = (-1 / self.outl[0]["E_CH"]) if self.outl[0]["e_CH"] != 0 else 1
222
- equations[counter] = f"aux_equality_chem_{self.outl[0]['name']}"
229
+ A[counter, self.inl[0]["CostVar_index"]["CH"]] = (
230
+ (1 / self.inl[0]["E_CH"]) if self.inl[0]["e_CH"] != 0 else 1
231
+ )
232
+ A[counter, self.outl[0]["CostVar_index"]["CH"]] = (
233
+ (-1 / self.outl[0]["E_CH"]) if self.outl[0]["e_CH"] != 0 else 1
234
+ )
235
+ equations[counter] = {
236
+ "kind": "aux_equality",
237
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
238
+ "property": "c_CH",
239
+ }
223
240
  chem_row = 1
224
241
  else:
225
242
  chem_row = 0
@@ -228,7 +245,7 @@ class Pump(Component):
228
245
  # Compute differences in thermal and mechanical exergy:
229
246
  dET = self.outl[0]["E_T"] - self.inl[0]["E_T"]
230
247
  dEM = self.outl[0]["E_M"] - self.inl[0]["E_M"]
231
-
248
+
232
249
  # The row for the thermal/mechanical equation:
233
250
  row_index = counter + chem_row
234
251
  if self.inl[0]["T"] > T0 and self.outl[0]["T"] > T0:
@@ -237,50 +254,180 @@ class Pump(Component):
237
254
  A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1 / dET
238
255
  A[row_index, self.inl[0]["CostVar_index"]["M"]] = 1 / dEM
239
256
  A[row_index, self.outl[0]["CostVar_index"]["M"]] = -1 / dEM
240
- equations[row_index] = f"aux_p_rule_{self.name}"
257
+ equations[row_index] = {
258
+ "kind": "aux_p_rule",
259
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
260
+ "property": "c_T, c_M",
261
+ }
241
262
  else:
242
263
  logging.warning("Case where thermal or mechanical exergy difference is zero is not implemented.")
243
264
  elif self.inl[0]["T"] <= T0 and self.outl[0]["T"] > T0:
244
- A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
245
- A[row_index, self.inl[0]["CostVar_index"]["M"]] = 1 / dEM
246
- A[row_index, self.outl[0]["CostVar_index"]["M"]] = -1 / dEM
247
- equations[row_index] = f"aux_p_rule_{self.name}"
265
+ # Case 2: Inlet at/below ambient, outlet above ambient
266
+ # Handle potential zero values for robustness
267
+ if self.outl[0]["e_T"] != 0 and dEM != 0:
268
+ A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
269
+ A[row_index, self.inl[0]["CostVar_index"]["M"]] = 1 / dEM
270
+ A[row_index, self.outl[0]["CostVar_index"]["M"]] = -1 / dEM
271
+ equations[row_index] = {
272
+ "kind": "aux_p_rule",
273
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
274
+ "property": "c_T, c_M",
275
+ }
276
+ else:
277
+ logging.warning(
278
+ f"Pump '{self.name}' Case 2: outlet thermal exergy or mechanical exergy "
279
+ "difference is zero, auxiliary equation may be degenerate."
280
+ )
281
+ # Fallback: set identity equation for thermal cost
282
+ A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1
283
+ equations[row_index] = {
284
+ "kind": "aux_p_rule",
285
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
286
+ "property": "c_T",
287
+ }
248
288
  else:
249
- A[row_index, self.inl[0]["CostVar_index"]["T"]] = -1 / self.inl[0]["E_T"]
250
- A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
251
- equations[row_index] = f"aux_f_rule_{self.name}"
252
-
289
+ # Case 3: Both temperatures at or below ambient - apply F-rule for thermal exergy
290
+ # Handle zero thermal exergy cases to avoid division by zero
291
+ if self.inl[0]["e_T"] != 0 and self.outl[0]["e_T"] != 0:
292
+ A[row_index, self.inl[0]["CostVar_index"]["T"]] = -1 / self.inl[0]["E_T"]
293
+ A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
294
+ elif self.inl[0]["e_T"] == 0 and self.outl[0]["e_T"] != 0:
295
+ # Inlet thermal exergy is zero, constrain C_T_in = 0
296
+ A[row_index, self.inl[0]["CostVar_index"]["T"]] = 1
297
+ elif self.inl[0]["e_T"] != 0 and self.outl[0]["e_T"] == 0:
298
+ # Outlet thermal exergy is zero, constrain C_T_out = 0
299
+ A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1
300
+ else:
301
+ # Both thermal exergies are zero, set identity equation
302
+ A[row_index, self.inl[0]["CostVar_index"]["T"]] = 1
303
+ A[row_index, self.outl[0]["CostVar_index"]["T"]] = -1
304
+ equations[row_index] = {
305
+ "kind": "aux_f_rule",
306
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
307
+ "property": "c_T",
308
+ }
309
+
253
310
  # Set the right-hand side entry for the thermal/mechanical row to zero.
254
311
  b[row_index] = 0
255
312
 
256
313
  # Update the counter accordingly.
257
- if chemical_exergy_enabled:
258
- new_counter = counter + 2
259
- else:
260
- new_counter = counter + 1
314
+ new_counter = counter + 2 if chemical_exergy_enabled else counter + 1
261
315
 
262
316
  return A, b, new_counter, equations
263
317
 
264
- def exergoeconomic_balance(self, T0):
265
- """
266
- Perform exergoeconomic balance calculations for the pump.
267
-
268
- This method calculates various exergoeconomic parameters including:
269
- - Cost rates of product (C_P) and fuel (C_F)
270
- - Specific cost of product (c_P) and fuel (c_F)
271
- - Cost rate of exergy destruction (C_D)
272
- - Relative cost difference (r)
273
- - Exergoeconomic factor (f)
274
-
318
+ def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
319
+ r"""
320
+ Perform exergoeconomic cost balance for the pump.
321
+
322
+ The general exergoeconomic balance equation is:
323
+
324
+ .. math::
325
+ \dot{C}^{\mathrm{T}}_{\mathrm{in}}
326
+ + \dot{C}^{\mathrm{M}}_{\mathrm{in}}
327
+ - \dot{C}^{\mathrm{T}}_{\mathrm{out}}
328
+ - \dot{C}^{\mathrm{M}}_{\mathrm{out}}
329
+ + \dot{Z}
330
+ = 0
331
+
332
+ In case the chemical exergy of the streams is known:
333
+
334
+ .. math::
335
+ \dot{C}^{\mathrm{CH}}_{\mathrm{in}} =
336
+ \dot{C}^{\mathrm{CH}}_{\mathrm{out}}
337
+
338
+ This method computes cost rates for product and fuel, and derives
339
+ exergoeconomic indicators. The pump consumes power (fuel) to increase
340
+ the exergy of the working fluid (product).
341
+
342
+ **Case 1: Both inlet and outlet above ambient temperature**
343
+
344
+ Both inlet and outlet satisfy :math:`T \geq T_0`:
345
+
346
+ .. math::
347
+ \dot{C}_{\mathrm{P}}
348
+ = \dot{C}^{\mathrm{PH}}_{\mathrm{out}}
349
+ - \dot{C}^{\mathrm{PH}}_{\mathrm{in}}
350
+
351
+ .. math::
352
+ \dot{C}_{\mathrm{F}}
353
+ = \dot{C}^{\mathrm{TOT}}_{\mathrm{power,in}}
354
+
355
+ **Case 2: Inlet at or below and outlet above ambient temperature**
356
+
357
+ Inlet satisfies :math:`T \leq T_0` and outlet :math:`T > T_0`:
358
+
359
+ .. math::
360
+ \dot{C}_{\mathrm{P}}
361
+ = \dot{C}^{\mathrm{T}}_{\mathrm{out}}
362
+ + \bigl(\dot{C}^{\mathrm{M}}_{\mathrm{out}}
363
+ - \dot{C}^{\mathrm{M}}_{\mathrm{in}}\bigr)
364
+
365
+ .. math::
366
+ \dot{C}_{\mathrm{F}}
367
+ = \dot{C}^{\mathrm{TOT}}_{\mathrm{power,in}}
368
+ + \dot{C}^{\mathrm{T}}_{\mathrm{in}}
369
+
370
+ **Case 3: Both inlet and outlet at or below ambient temperature**
371
+
372
+ Both inlet and outlet satisfy :math:`T \leq T_0`:
373
+
374
+ .. math::
375
+ \dot{C}_{\mathrm{P}}
376
+ = \dot{C}^{\mathrm{M}}_{\mathrm{out}}
377
+ - \dot{C}^{\mathrm{M}}_{\mathrm{in}}
378
+
379
+ .. math::
380
+ \dot{C}_{\mathrm{F}}
381
+ = \dot{C}^{\mathrm{TOT}}_{\mathrm{power,in}}
382
+ + \bigl(\dot{C}^{\mathrm{T}}_{\mathrm{in}}
383
+ - \dot{C}^{\mathrm{T}}_{\mathrm{out}}\bigr)
384
+
385
+ **Calculated exergoeconomic indicators:**
386
+
387
+ .. math::
388
+ c_{\mathrm{F}} = \frac{\dot{C}_{\mathrm{F}}}{\dot{E}_{\mathrm{F}}}
389
+
390
+ .. math::
391
+ c_{\mathrm{P}} = \frac{\dot{C}_{\mathrm{P}}}{\dot{E}_{\mathrm{P}}}
392
+
393
+ .. math::
394
+ \dot{C}_{\mathrm{D}} = c_{\mathrm{F}} \cdot \dot{E}_{\mathrm{D}}
395
+
396
+ .. math::
397
+ r = \frac{c_{\mathrm{P}} - c_{\mathrm{F}}}{c_{\mathrm{F}}}
398
+
399
+ .. math::
400
+ f = \frac{\dot{Z}}{\dot{Z} + \dot{C}_{\mathrm{D}}}
401
+
275
402
  Parameters
276
403
  ----------
277
404
  T0 : float
278
- Ambient temperature
279
-
280
- Notes
281
- -----
282
- The exergoeconomic balance considers thermal (T), chemical (CH),
283
- and mechanical (M) exergy components for the inlet and outlet streams.
405
+ Ambient temperature (K).
406
+ chemical_exergy_enabled : bool, optional
407
+ If True, chemical exergy is considered in the calculations.
408
+ Default is False.
409
+
410
+ Attributes Set
411
+ --------------
412
+ C_P : float
413
+ Cost rate of product (currency/time).
414
+ C_F : float
415
+ Cost rate of fuel (currency/time).
416
+ c_P : float
417
+ Specific cost of product (currency/energy).
418
+ c_F : float
419
+ Specific cost of fuel (currency/energy).
420
+ C_D : float
421
+ Cost rate of exergy destruction (currency/time).
422
+ r : float
423
+ Relative cost difference (dimensionless).
424
+ f : float
425
+ Exergoeconomic factor (dimensionless).
426
+
427
+ Raises
428
+ ------
429
+ ValueError
430
+ If no inlet power stream is found.
284
431
  """
285
432
  # Retrieve the cost of power from the inlet stream of kind "power"
286
433
  power_cost = None