exerpy 0.0.2__py3-none-any.whl → 0.0.3__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 (39) hide show
  1. exerpy/__init__.py +2 -4
  2. exerpy/analyses.py +597 -297
  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 +186 -119
  7. exerpy/components/heat_exchanger/condenser.py +96 -60
  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 +181 -63
  22. exerpy/components/turbomachinery/pump.py +182 -63
  23. exerpy/components/turbomachinery/turbine.py +182 -74
  24. exerpy/functions.py +388 -263
  25. exerpy/parser/from_aspen/aspen_config.py +57 -48
  26. exerpy/parser/from_aspen/aspen_parser.py +373 -280
  27. exerpy/parser/from_ebsilon/__init__.py +2 -2
  28. exerpy/parser/from_ebsilon/check_ebs_path.py +15 -19
  29. exerpy/parser/from_ebsilon/ebsilon_config.py +328 -226
  30. exerpy/parser/from_ebsilon/ebsilon_functions.py +205 -38
  31. exerpy/parser/from_ebsilon/ebsilon_parser.py +392 -255
  32. exerpy/parser/from_ebsilon/utils.py +16 -11
  33. exerpy/parser/from_tespy/tespy_config.py +32 -1
  34. exerpy/parser/from_tespy/tespy_parser.py +151 -0
  35. {exerpy-0.0.2.dist-info → exerpy-0.0.3.dist-info}/METADATA +43 -2
  36. exerpy-0.0.3.dist-info/RECORD +48 -0
  37. exerpy-0.0.2.dist-info/RECORD +0 -44
  38. {exerpy-0.0.2.dist-info → exerpy-0.0.3.dist-info}/WHEEL +0 -0
  39. {exerpy-0.0.2.dist-info → exerpy-0.0.3.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
@@ -88,7 +87,7 @@ class Compressor(Component):
88
87
  r"""Initialize compressor component with given parameters."""
89
88
  super().__init__(**kwargs)
90
89
  self.P = None
91
- self.Z_costs = kwargs.get('Z_costs', 0.0) # Investment cost rate in currency/h
90
+ self.Z_costs = kwargs.get("Z_costs", 0.0) # Investment cost rate in currency/h
92
91
 
93
92
  def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
94
93
  r"""
@@ -107,13 +106,19 @@ class Compressor(Component):
107
106
  Flag indicating whether physical exergy is split into thermal and mechanical components.
108
107
 
109
108
  """
110
- # Get power flow if not already available
111
- if self.P is None:
112
- self.P = self.outl[0]['m'] * (self.outl[0]['h'] - self.inl[0]['h'])
113
-
109
+ # Get power flow
110
+ if (
111
+ 1 in self.inl
112
+ and self.inl[1] is not None
113
+ and self.inl[1].get("kind") == "power"
114
+ and "energy_flow" in self.inl[1]
115
+ ):
116
+ self.P = self.inl[1]["energy_flow"]
117
+ else:
118
+ self.P = self.outl[0]["m"] * (self.outl[0]["h"] - self.inl[0]["h"])
114
119
 
115
120
  # First, check for the invalid case: outlet temperature smaller than inlet temperature.
116
- if self.inl[0]['T'] > self.outl[0]['T']:
121
+ if self.inl[0]["T"] > self.outl[0]["T"]:
117
122
  logging.warning(
118
123
  f"Exergy balance of compressor '{self.name}' where outlet temperature ({self.outl[0]['T']}) "
119
124
  f"is smaller than inlet temperature ({self.inl[0]['T']}) is not implemented."
@@ -122,32 +127,36 @@ class Compressor(Component):
122
127
  self.E_F = np.nan
123
128
 
124
129
  # Case 1: Both temperatures above ambient
125
- elif round(self.inl[0]['T'], 5) >= T0 and round(self.outl[0]['T'], 5) > T0:
126
- self.E_P = self.outl[0]['m'] * (self.outl[0]['e_PH'] - self.inl[0]['e_PH'])
130
+ elif round(self.inl[0]["T"], 5) >= T0 and round(self.outl[0]["T"], 5) > T0:
131
+ self.E_P = self.outl[0]["m"] * (self.outl[0]["e_PH"] - self.inl[0]["e_PH"])
127
132
  self.E_F = abs(self.P)
128
133
 
129
134
  # Case 2: Inlet below, outlet above ambient
130
- elif round(self.inl[0]['T'], 5) < T0 and round(self.outl[0]['T'], 5) > T0:
135
+ elif round(self.inl[0]["T"], 5) < T0 and round(self.outl[0]["T"], 5) > T0:
131
136
  if split_physical_exergy:
132
- self.E_P = (self.outl[0]['m'] * self.outl[0]['e_T'] +
133
- self.outl[0]['m'] * (self.outl[0]['e_M'] - self.inl[0]['e_M']))
134
- self.E_F = abs(self.P) + self.inl[0]['m'] * self.inl[0]['e_T']
137
+ self.E_P = self.outl[0]["m"] * self.outl[0]["e_T"] + self.outl[0]["m"] * (
138
+ self.outl[0]["e_M"] - self.inl[0]["e_M"]
139
+ )
140
+ self.E_F = abs(self.P) + self.inl[0]["m"] * self.inl[0]["e_T"]
135
141
  else:
136
- logging.warning("While dealing with compressor below ambient, "
137
- "physical exergy should be split into thermal and mechanical components!")
138
- self.E_P = self.outl[0]['m'] * (self.outl[0]['e_PH'] - self.inl[0]['e_PH'])
142
+ logging.warning(
143
+ "While dealing with compressor below ambient, "
144
+ "physical exergy should be split into thermal and mechanical components!"
145
+ )
146
+ self.E_P = self.outl[0]["m"] * (self.outl[0]["e_PH"] - self.inl[0]["e_PH"])
139
147
  self.E_F = abs(self.P)
140
148
 
141
149
  # Case 3: Both temperatures below ambient
142
- elif round(self.inl[0]['T'], 5) < T0 and round(self.outl[0]['T'], 5) <= T0:
150
+ elif round(self.inl[0]["T"], 5) < T0 and round(self.outl[0]["T"], 5) <= T0:
143
151
  if split_physical_exergy:
144
- self.E_P = self.outl[0]['m'] * (self.outl[0]['e_M'] - self.inl[0]['e_M'])
145
- self.E_F = abs(self.P) + self.inl[0]['m'] * (self.inl[0]['e_T'] -
146
- self.outl[0]['e_T'])
152
+ self.E_P = self.outl[0]["m"] * (self.outl[0]["e_M"] - self.inl[0]["e_M"])
153
+ self.E_F = abs(self.P) + self.inl[0]["m"] * (self.inl[0]["e_T"] - self.outl[0]["e_T"])
147
154
  else:
148
- logging.warning("While dealing with compressor below ambient, "
149
- "physical exergy should be split into thermal and mechanical components!")
150
- self.E_P = self.outl[0]['m'] * (self.outl[0]['e_PH'] - self.inl[0]['e_PH'])
155
+ logging.warning(
156
+ "While dealing with compressor below ambient, "
157
+ "physical exergy should be split into thermal and mechanical components!"
158
+ )
159
+ self.E_P = self.outl[0]["m"] * (self.outl[0]["e_PH"] - self.inl[0]["e_PH"])
151
160
  self.E_F = abs(self.P)
152
161
 
153
162
  # Invalid case: outlet temperature smaller than inlet
@@ -165,37 +174,36 @@ class Compressor(Component):
165
174
 
166
175
  # Log the results
167
176
  logging.info(
168
- f"Compressor '{self.name}' exergy balance calculated: "
177
+ f"Exergy balance of Compressor {self.name} calculated: "
169
178
  f"E_P={self.E_P:.2f} W, E_F={self.E_F:.2f} W, E_D={self.E_D:.2f} W, "
170
179
  f"Efficiency={self.epsilon:.2%}"
171
180
  )
172
181
 
173
-
174
182
  def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
175
183
  """
176
184
  Auxiliary equations for the compressor.
177
-
185
+
178
186
  This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
179
187
  the following auxiliary cost relations:
180
-
188
+
181
189
  (1) Chemical exergy cost equation (if enabled):
182
190
  1/E_CH_in * C_CH_in - 1/E_CH_out * C_CH_out = 0
183
191
  - F-principle: specific chemical exergy costs equalized between inlet/outlet
184
-
192
+
185
193
  (2) Thermal/Mechanical exergy cost equations (based on temperature conditions):
186
-
194
+
187
195
  Case 1 (T_in > T0, T_out > T0):
188
196
  1/dET * C_T_out - 1/dET * C_T_in - 1/dEM * C_M_out + 1/dEM * C_M_in = 0
189
197
  - P-principle: relates inlet/outlet thermal and mechanical exergy costs
190
-
198
+
191
199
  Case 2 (T_in ≤ T0, T_out > T0):
192
200
  1/E_T_out * C_T_out - 1/dEM * C_M_out + 1/dEM * C_M_in = 0
193
201
  - P-principle: relates outlet thermal and inlet/outlet mechanical exergy costs
194
-
202
+
195
203
  Case 3 (T_in ≤ T0, T_out ≤ T0):
196
204
  1/E_T_out * C_T_out - 1/E_T_in * C_T_in = 0
197
205
  - F-principle: specific thermal exergy costs equalized between inlet/outlet
198
-
206
+
199
207
  Parameters
200
208
  ----------
201
209
  A : numpy.ndarray
@@ -210,7 +218,7 @@ class Compressor(Component):
210
218
  Data structure for storing equation labels.
211
219
  chemical_exergy_enabled : bool
212
220
  Flag indicating whether chemical exergy auxiliary equations should be added.
213
-
221
+
214
222
  Returns
215
223
  -------
216
224
  A : numpy.ndarray
@@ -225,9 +233,17 @@ class Compressor(Component):
225
233
  # --- Chemical equality equation (row added only if enabled) ---
226
234
  if chemical_exergy_enabled:
227
235
  # Set the chemical cost equality:
228
- A[counter, self.inl[0]["CostVar_index"]["CH"]] = (1 / self.inl[0]["E_CH"]) if self.inl[0]["e_CH"] != 0 else 1
229
- A[counter, self.outl[0]["CostVar_index"]["CH"]] = (-1 / self.outl[0]["E_CH"]) if self.outl[0]["e_CH"] != 0 else 1
230
- equations[counter] = f"aux_equality_chem_{self.outl[0]['name']}"
236
+ A[counter, self.inl[0]["CostVar_index"]["CH"]] = (
237
+ (1 / self.inl[0]["E_CH"]) if self.inl[0]["e_CH"] != 0 else 1
238
+ )
239
+ A[counter, self.outl[0]["CostVar_index"]["CH"]] = (
240
+ (-1 / self.outl[0]["E_CH"]) if self.outl[0]["e_CH"] != 0 else 1
241
+ )
242
+ equations[counter] = {
243
+ "kind": "aux_equality",
244
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
245
+ "property": "c_CH",
246
+ }
231
247
  chem_row = 1
232
248
  else:
233
249
  chem_row = 0
@@ -236,7 +252,7 @@ class Compressor(Component):
236
252
  # Compute differences in thermal and mechanical exergy:
237
253
  dET = self.outl[0]["E_T"] - self.inl[0]["E_T"]
238
254
  dEM = self.outl[0]["E_M"] - self.inl[0]["E_M"]
239
-
255
+
240
256
  # The row for the thermal/mechanical equation:
241
257
  row_index = counter + chem_row
242
258
  if self.inl[0]["T"] > T0 and self.outl[0]["T"] > T0:
@@ -245,50 +261,152 @@ class Compressor(Component):
245
261
  A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1 / dET
246
262
  A[row_index, self.inl[0]["CostVar_index"]["M"]] = 1 / dEM
247
263
  A[row_index, self.outl[0]["CostVar_index"]["M"]] = -1 / dEM
248
- equations[row_index] = f"aux_p_rule_{self.name}"
264
+ equations[row_index] = {
265
+ "kind": "aux_p_rule",
266
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
267
+ "property": "c_T, c_M",
268
+ }
249
269
  else:
250
270
  logging.warning("Case where thermal or mechanical exergy difference is zero is not implemented.")
251
271
  elif self.inl[0]["T"] <= T0 and self.outl[0]["T"] > T0:
252
272
  A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
253
273
  A[row_index, self.inl[0]["CostVar_index"]["M"]] = 1 / dEM
254
274
  A[row_index, self.outl[0]["CostVar_index"]["M"]] = -1 / dEM
255
- equations[row_index] = f"aux_p_rule_{self.name}"
275
+ equations[row_index] = {
276
+ "kind": "aux_p_rule",
277
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
278
+ "property": "c_T, c_M",
279
+ }
256
280
  else:
257
281
  A[row_index, self.inl[0]["CostVar_index"]["T"]] = -1 / self.inl[0]["E_T"]
258
282
  A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
259
- equations[row_index] = f"aux_f_rule_{self.name}"
260
-
283
+ equations[row_index] = {
284
+ "kind": "aux_f_rule",
285
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
286
+ "property": "c_T",
287
+ }
288
+
261
289
  # Set the right-hand side entry for the thermal/mechanical row to zero.
262
290
  b[row_index] = 0
263
291
 
264
292
  # Update the counter accordingly.
265
- if chemical_exergy_enabled:
266
- new_counter = counter + 2
267
- else:
268
- new_counter = counter + 1
293
+ new_counter = counter + 2 if chemical_exergy_enabled else counter + 1
269
294
 
270
295
  return A, b, new_counter, equations
271
296
 
272
- def exergoeconomic_balance(self, T0):
273
- """
274
- Perform exergoeconomic balance calculations for the compressor.
275
-
276
- This method calculates various exergoeconomic parameters including:
277
- - Cost rates of product (C_P) and fuel (C_F)
278
- - Specific cost of product (c_P) and fuel (c_F)
279
- - Cost rate of exergy destruction (C_D)
280
- - Relative cost difference (r)
281
- - Exergoeconomic factor (f)
282
-
297
+ def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
298
+ r"""
299
+ Perform exergoeconomic cost balance for the compressor.
300
+
301
+ The general exergoeconomic balance equation is:
302
+
303
+ .. math::
304
+ \dot{C}^{\mathrm{T}}_{\mathrm{in}}
305
+ + \dot{C}^{\mathrm{M}}_{\mathrm{in}}
306
+ - \dot{C}^{\mathrm{T}}_{\mathrm{out}}
307
+ - \dot{C}^{\mathrm{M}}_{\mathrm{out}}
308
+ + \dot{Z}
309
+ = 0
310
+
311
+ In case the chemical exergy of the streams is known:
312
+
313
+ .. math::
314
+ \dot{C}^{\mathrm{CH}}_{\mathrm{in}} =
315
+ \dot{C}^{\mathrm{CH}}_{\mathrm{out}}
316
+
317
+ This method computes cost rates for product and fuel, and derives
318
+ exergoeconomic indicators. The compressor consumes power (fuel) to increase
319
+ the exergy of the working fluid (product).
320
+
321
+ **Case 1: Both inlet and outlet above ambient temperature**
322
+
323
+ Both inlet and outlet satisfy :math:`T \geq T_0`:
324
+
325
+ .. math::
326
+ \dot{C}_{\mathrm{P}}
327
+ = \dot{C}^{\mathrm{PH}}_{\mathrm{out}}
328
+ - \dot{C}^{\mathrm{PH}}_{\mathrm{in}}
329
+
330
+ .. math::
331
+ \dot{C}_{\mathrm{F}}
332
+ = \dot{C}^{\mathrm{TOT}}_{\mathrm{power,in}}
333
+
334
+ **Case 2: Inlet at or below and outlet above ambient temperature**
335
+
336
+ Inlet satisfies :math:`T \leq T_0` and outlet :math:`T > T_0`:
337
+
338
+ .. math::
339
+ \dot{C}_{\mathrm{P}}
340
+ = \dot{C}^{\mathrm{T}}_{\mathrm{out}}
341
+ + \bigl(\dot{C}^{\mathrm{M}}_{\mathrm{out}}
342
+ - \dot{C}^{\mathrm{M}}_{\mathrm{in}}\bigr)
343
+
344
+ .. math::
345
+ \dot{C}_{\mathrm{F}}
346
+ = \dot{C}^{\mathrm{TOT}}_{\mathrm{power,in}}
347
+ + \dot{C}^{\mathrm{T}}_{\mathrm{in}}
348
+
349
+ **Case 3: Both inlet and outlet at or below ambient temperature**
350
+
351
+ Both inlet and outlet satisfy :math:`T \leq T_0`:
352
+
353
+ .. math::
354
+ \dot{C}_{\mathrm{P}}
355
+ = \dot{C}^{\mathrm{M}}_{\mathrm{out}}
356
+ - \dot{C}^{\mathrm{M}}_{\mathrm{in}}
357
+
358
+ .. math::
359
+ \dot{C}_{\mathrm{F}}
360
+ = \dot{C}^{\mathrm{TOT}}_{\mathrm{power,in}}
361
+ + \bigl(\dot{C}^{\mathrm{T}}_{\mathrm{in}}
362
+ - \dot{C}^{\mathrm{T}}_{\mathrm{out}}\bigr)
363
+
364
+ **Calculated exergoeconomic indicators:**
365
+
366
+ .. math::
367
+ c_{\mathrm{F}} = \frac{\dot{C}_{\mathrm{F}}}{\dot{E}_{\mathrm{F}}}
368
+
369
+ .. math::
370
+ c_{\mathrm{P}} = \frac{\dot{C}_{\mathrm{P}}}{\dot{E}_{\mathrm{P}}}
371
+
372
+ .. math::
373
+ \dot{C}_{\mathrm{D}} = c_{\mathrm{F}} \cdot \dot{E}_{\mathrm{D}}
374
+
375
+ .. math::
376
+ r = \frac{c_{\mathrm{P}} - c_{\mathrm{F}}}{c_{\mathrm{F}}}
377
+
378
+ .. math::
379
+ f = \frac{\dot{Z}}{\dot{Z} + \dot{C}_{\mathrm{D}}}
380
+
283
381
  Parameters
284
382
  ----------
285
383
  T0 : float
286
- Ambient temperature
287
-
288
- Notes
289
- -----
290
- The exergoeconomic balance considers thermal (T), chemical (CH),
291
- and mechanical (M) exergy components for the inlet and outlet streams.
384
+ Ambient temperature (K).
385
+ chemical_exergy_enabled : bool, optional
386
+ If True, chemical exergy is considered in the calculations.
387
+ Default is False.
388
+
389
+ Attributes Set
390
+ --------------
391
+ C_P : float
392
+ Cost rate of product (currency/time).
393
+ C_F : float
394
+ Cost rate of fuel (currency/time).
395
+ c_P : float
396
+ Specific cost of product (currency/energy).
397
+ c_F : float
398
+ Specific cost of fuel (currency/energy).
399
+ C_D : float
400
+ Cost rate of exergy destruction (currency/time).
401
+ r : float
402
+ Relative cost difference (dimensionless).
403
+ f : float
404
+ Exergoeconomic factor (dimensionless).
405
+
406
+ Raises
407
+ ------
408
+ ValueError
409
+ If no inlet power stream is found.
292
410
  """
293
411
  # Retrieve the cost of power from the inlet stream of kind "power"
294
412
  power_cost = None