exerpy 0.0.1__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 (44) hide show
  1. exerpy/__init__.py +12 -0
  2. exerpy/analyses.py +1711 -0
  3. exerpy/components/__init__.py +16 -0
  4. exerpy/components/combustion/__init__.py +0 -0
  5. exerpy/components/combustion/base.py +248 -0
  6. exerpy/components/component.py +126 -0
  7. exerpy/components/heat_exchanger/__init__.py +0 -0
  8. exerpy/components/heat_exchanger/base.py +449 -0
  9. exerpy/components/heat_exchanger/condenser.py +323 -0
  10. exerpy/components/heat_exchanger/simple.py +358 -0
  11. exerpy/components/heat_exchanger/steam_generator.py +264 -0
  12. exerpy/components/helpers/__init__.py +0 -0
  13. exerpy/components/helpers/cycle_closer.py +104 -0
  14. exerpy/components/nodes/__init__.py +0 -0
  15. exerpy/components/nodes/deaerator.py +318 -0
  16. exerpy/components/nodes/drum.py +164 -0
  17. exerpy/components/nodes/flash_tank.py +89 -0
  18. exerpy/components/nodes/mixer.py +332 -0
  19. exerpy/components/piping/__init__.py +0 -0
  20. exerpy/components/piping/valve.py +394 -0
  21. exerpy/components/power_machines/__init__.py +0 -0
  22. exerpy/components/power_machines/generator.py +168 -0
  23. exerpy/components/power_machines/motor.py +173 -0
  24. exerpy/components/turbomachinery/__init__.py +0 -0
  25. exerpy/components/turbomachinery/compressor.py +318 -0
  26. exerpy/components/turbomachinery/pump.py +310 -0
  27. exerpy/components/turbomachinery/turbine.py +351 -0
  28. exerpy/data/Ahrendts.json +90 -0
  29. exerpy/functions.py +637 -0
  30. exerpy/parser/__init__.py +0 -0
  31. exerpy/parser/from_aspen/__init__.py +0 -0
  32. exerpy/parser/from_aspen/aspen_config.py +61 -0
  33. exerpy/parser/from_aspen/aspen_parser.py +721 -0
  34. exerpy/parser/from_ebsilon/__init__.py +38 -0
  35. exerpy/parser/from_ebsilon/check_ebs_path.py +74 -0
  36. exerpy/parser/from_ebsilon/ebsilon_config.py +1055 -0
  37. exerpy/parser/from_ebsilon/ebsilon_functions.py +181 -0
  38. exerpy/parser/from_ebsilon/ebsilon_parser.py +660 -0
  39. exerpy/parser/from_ebsilon/utils.py +79 -0
  40. exerpy/parser/from_tespy/tespy_config.py +23 -0
  41. exerpy-0.0.1.dist-info/METADATA +158 -0
  42. exerpy-0.0.1.dist-info/RECORD +44 -0
  43. exerpy-0.0.1.dist-info/WHEEL +4 -0
  44. exerpy-0.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,173 @@
1
+ import logging
2
+
3
+ from exerpy.components.component import Component
4
+ from exerpy.components.component import component_registry
5
+
6
+
7
+ @component_registry
8
+ class Motor(Component):
9
+ r"""
10
+ Class for exergy analysis of motors.
11
+
12
+ This class performs exergy analysis calculations for motors, converting electrical
13
+ energy into mechanical energy. The exergy product is defined as the mechanical
14
+ power output, while the exergy fuel is the electrical power input.
15
+
16
+ Parameters
17
+ ----------
18
+ **kwargs : dict
19
+ Arbitrary keyword arguments passed to parent class.
20
+
21
+ Attributes
22
+ ----------
23
+ E_F : float
24
+ Exergy fuel of the component :math:`\dot{E}_\mathrm{F}` in :math:`\mathrm{W}`.
25
+ E_P : float
26
+ Exergy product of the component :math:`\dot{E}_\mathrm{P}` in :math:`\mathrm{W}`.
27
+ E_D : float
28
+ Exergy destruction of the component :math:`\dot{E}_\mathrm{D}` in :math:`\mathrm{W}`.
29
+ epsilon : float
30
+ Exergetic efficiency of the component :math:`\varepsilon` in :math:`-`.
31
+ inl : dict
32
+ Dictionary containing inlet stream data with energy flow.
33
+ outl : dict
34
+ Dictionary containing outlet stream data with energy flow.
35
+
36
+ Notes
37
+ -----
38
+ The exergy analysis for a motor is straightforward as both electrical and mechanical
39
+ energy are pure exergy. The equations are:
40
+
41
+ .. math::
42
+
43
+ \dot{E}_\mathrm{P} & = \dot{W}_\mathrm{mech}
44
+
45
+ \dot{E}_\mathrm{F} & = \dot{W}_\mathrm{el}
46
+
47
+ \dot{E}_\mathrm{D} & = \dot{E}_\mathrm{F} - \dot{E}_\mathrm{P}
48
+
49
+ where:
50
+ - :math:`\dot{W}_\mathrm{mech}`: Mechanical power output
51
+ - :math:`\dot{W}_\mathrm{el}`: Electrical power input
52
+ """
53
+
54
+ def __init__(self, **kwargs):
55
+ r"""Initialize motor component with given parameters."""
56
+ super().__init__(**kwargs)
57
+
58
+ def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
59
+ r"""
60
+ Calculate the exergy balance of the motor.
61
+
62
+ Calculates the exergy product (mechanical power output), exergy fuel
63
+ (electrical power input), and the resulting exergy destruction and efficiency.
64
+
65
+ Parameters
66
+ ----------
67
+ T0 : float
68
+ Ambient temperature in :math:`\mathrm{K}`.
69
+ p0 : float
70
+ Ambient pressure in :math:`\mathrm{Pa}`.
71
+ split_physical_exergy : bool
72
+ Flag indicating whether physical exergy is split into thermal and mechanical components.
73
+
74
+ """
75
+
76
+ if self.outl[0]['energy_flow'] > self.inl[0]['energy_flow']:
77
+ pruduct = self.inl[0]['energy_flow']
78
+ fuel = self.outl[0]['energy_flow']
79
+ else:
80
+ pruduct = self.outl[0]['energy_flow']
81
+ fuel = self.inl[0]['energy_flow']
82
+
83
+ # Exergy product is the mechanical power output
84
+ self.E_P = pruduct
85
+
86
+ # Exergy fuel is the electrical power input
87
+ self.E_F = fuel
88
+
89
+ # Calculate exergy destruction
90
+ self.E_D = self.E_F - self.E_P
91
+
92
+ # Calculate exergy efficiency
93
+ self.epsilon = self.calc_epsilon()
94
+
95
+ # Log the results
96
+ logging.info(
97
+ f"Motor exergy balance calculated: "
98
+ f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
99
+ f"Efficiency={self.epsilon:.2%}"
100
+ )
101
+
102
+
103
+ def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
104
+ """
105
+ Auxiliary equations for the motor.
106
+
107
+ This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
108
+ the auxiliary cost relations for the motor. Since the motor converts mechanical
109
+ or thermal energy to electrical energy, the auxiliary equations typically enforce:
110
+
111
+ - No additional auxiliary equations are needed for motors as electrical energy
112
+ is pure exergy and the cost balance equations are sufficient.
113
+
114
+ Parameters
115
+ ----------
116
+ A : numpy.ndarray
117
+ The current cost matrix.
118
+ b : numpy.ndarray
119
+ The current right-hand-side vector.
120
+ counter : int
121
+ The current row index in the matrix.
122
+ T0 : float
123
+ Ambient temperature.
124
+ equations : dict
125
+ Dictionary for storing equation labels.
126
+ chemical_exergy_enabled : bool
127
+ Flag indicating whether chemical exergy auxiliary equations should be added.
128
+
129
+ Returns
130
+ -------
131
+ A : numpy.ndarray
132
+ The updated cost matrix.
133
+ b : numpy.ndarray
134
+ The updated right-hand-side vector.
135
+ counter : int
136
+ The updated row index.
137
+ equations : dict
138
+ Updated dictionary with equation labels.
139
+ """
140
+ return [A, b, counter, equations]
141
+
142
+ def exergoeconomic_balance(self, T0):
143
+ """
144
+ Perform exergoeconomic balance calculations for the motor.
145
+
146
+ This method calculates various exergoeconomic parameters including:
147
+ - Cost rates of product (C_P) and fuel (C_F)
148
+ - Specific cost of product (c_P) and fuel (c_F)
149
+ - Cost rate of exergy destruction (C_D)
150
+ - Relative cost difference (r)
151
+ - Exergoeconomic factor (f)
152
+
153
+ Parameters
154
+ ----------
155
+ T0 : float
156
+ Ambient temperature
157
+
158
+ Notes
159
+ -----
160
+ The exergoeconomic balance considers thermal (T), chemical (CH),
161
+ and mechanical (M) exergy components for the inlet and outlet streams.
162
+ """
163
+ self.C_P = self.outl[0].get("C_TOT", 0)
164
+ self.C_F = self.inl[0].get("C_TOT", 0)
165
+
166
+ if self.E_P == 0 or self.E_F == 0:
167
+ raise ValueError(f"E_P or E_F is zero; cannot compute specific costs for component: {self.name}.")
168
+
169
+ self.c_P = self.C_P / self.E_P
170
+ self.c_F = self.C_F / self.E_F
171
+ self.C_D = self.c_F * self.E_D # Ensure that self.E_D is computed beforehand.
172
+ self.r = (self.C_P - self.C_F) / self.C_F
173
+ self.f = self.Z_costs / (self.Z_costs + self.C_D) if (self.Z_costs + self.C_D) != 0 else 0
File without changes
@@ -0,0 +1,318 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+
5
+ from exerpy.components.component import Component
6
+ from exerpy.components.component import component_registry
7
+
8
+
9
+ @component_registry
10
+ class Compressor(Component):
11
+ r"""
12
+ Class for exergy and exergoeconomic analysis of compressors.
13
+
14
+ This class performs exergy and exergoeconomic analysis calculations for compressors,
15
+ considering thermal, mechanical, and physical exergy flows. The exergy product and fuel
16
+ are calculated based on temperature relationships between inlet, outlet, and ambient conditions.
17
+
18
+ Parameters
19
+ ----------
20
+ **kwargs : dict
21
+ Arbitrary keyword arguments passed to parent class.
22
+ Optional parameter 'Z_costs' (float): Investment cost rate of the component in currency/h.
23
+
24
+ Attributes
25
+ ----------
26
+ E_F : float
27
+ Exergy fuel of the component :math:`\dot{E}_\mathrm{F}` in :math:`\mathrm{W}`.
28
+ E_P : float
29
+ Exergy product of the component :math:`\dot{E}_\mathrm{P}` in :math:`\mathrm{W}`.
30
+ E_D : float
31
+ Exergy destruction of the component :math:`\dot{E}_\mathrm{D}` in :math:`\mathrm{W}`.
32
+ epsilon : float
33
+ Exergetic efficiency of the component :math:`\varepsilon` in :math:`-`.
34
+ P : float
35
+ Power input to the compressor in :math:`\mathrm{W}`.
36
+ inl : dict
37
+ Dictionary containing inlet stream data with temperature, mass flows,
38
+ enthalpies, and specific exergies.
39
+ outl : dict
40
+ Dictionary containing outlet stream data with temperature, mass flows,
41
+ enthalpies, and specific exergies.
42
+ Z_costs : float
43
+ Investment cost rate of the component in currency/h.
44
+
45
+ Notes
46
+ -----
47
+ The exergy analysis considers three cases based on temperature relationships:
48
+
49
+ Case 1 - **Both temperatures above ambient** (:math:`T_\mathrm{in}, T_\mathrm{out} > T_0`):
50
+
51
+ .. math::
52
+
53
+ \dot{E}_\mathrm{P} &= \dot{m} \cdot (e_\mathrm{out}^\mathrm{PH} -
54
+ e_\mathrm{in}^\mathrm{PH})\\
55
+ \dot{E}_\mathrm{F} &= |\dot{W}|
56
+
57
+ Case 2 - **Inlet below, outlet above ambient** (:math:`T_\mathrm{in} < T_0 < T_\mathrm{out}`):
58
+
59
+ .. math::
60
+
61
+ \dot{E}_\mathrm{P} &= \dot{m} \cdot e_\mathrm{out}^\mathrm{T} +
62
+ \dot{m} \cdot (e_\mathrm{out}^\mathrm{M} - e_\mathrm{in}^\mathrm{M})\\
63
+ \dot{E}_\mathrm{F} &= |\dot{W}| + \dot{m} \cdot e_\mathrm{in}^\mathrm{T}
64
+
65
+ Case 3 - **Both temperatures below ambient** (:math:`T_\mathrm{in}, T_\mathrm{out} \leq T_0`):
66
+
67
+ .. math::
68
+
69
+ \dot{E}_\mathrm{P} &= \dot{m} \cdot (e_\mathrm{out}^\mathrm{M} -
70
+ e_\mathrm{in}^\mathrm{M})\\
71
+ \dot{E}_\mathrm{F} &= |\dot{W}| + \dot{m} \cdot (e_\mathrm{in}^\mathrm{T}
72
+ - e_\mathrm{out}^\mathrm{T})
73
+
74
+ For all valid cases, the exergy destruction is:
75
+
76
+ .. math::
77
+
78
+ \dot{E}_\mathrm{D} = \dot{E}_\mathrm{F} - \dot{E}_\mathrm{P}
79
+
80
+ where:
81
+ - :math:`\dot{W}`: Power input
82
+ - :math:`e^\mathrm{T}`: Thermal exergy
83
+ - :math:`e^\mathrm{M}`: Mechanical exergy
84
+ - :math:`e^\mathrm{PH}`: Physical exergy
85
+ """
86
+
87
+ def __init__(self, **kwargs):
88
+ r"""Initialize compressor component with given parameters."""
89
+ super().__init__(**kwargs)
90
+ self.P = None
91
+ self.Z_costs = kwargs.get('Z_costs', 0.0) # Investment cost rate in currency/h
92
+
93
+ def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
94
+ r"""
95
+ Calculate the exergy balance of the compressor.
96
+
97
+ Performs exergy balance calculations considering the temperature relationships
98
+ between inlet stream, outlet stream, and ambient conditions.
99
+
100
+ Parameters
101
+ ----------
102
+ T0 : float
103
+ Ambient temperature in :math:`\mathrm{K}`.
104
+ p0 : float
105
+ Ambient pressure in :math:`\mathrm{Pa}`.
106
+ split_physical_exergy : bool
107
+ Flag indicating whether physical exergy is split into thermal and mechanical components.
108
+
109
+ """
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
+
114
+
115
+ # First, check for the invalid case: outlet temperature smaller than inlet temperature.
116
+ if self.inl[0]['T'] > self.outl[0]['T']:
117
+ logging.warning(
118
+ f"Exergy balance of compressor '{self.name}' where outlet temperature ({self.outl[0]['T']}) "
119
+ f"is smaller than inlet temperature ({self.inl[0]['T']}) is not implemented."
120
+ )
121
+ self.E_P = np.nan
122
+ self.E_F = np.nan
123
+
124
+ # 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'])
127
+ self.E_F = abs(self.P)
128
+
129
+ # Case 2: Inlet below, outlet above ambient
130
+ elif round(self.inl[0]['T'], 5) < T0 and round(self.outl[0]['T'], 5) > T0:
131
+ 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']
135
+ 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'])
139
+ self.E_F = abs(self.P)
140
+
141
+ # Case 3: Both temperatures below ambient
142
+ elif round(self.inl[0]['T'], 5) < T0 and round(self.outl[0]['T'], 5) <= T0:
143
+ 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'])
147
+ 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'])
151
+ self.E_F = abs(self.P)
152
+
153
+ # Invalid case: outlet temperature smaller than inlet
154
+ else:
155
+ logging.warning(
156
+ f"Exergy balance of compressor '{self.name}' where outlet temperature is smaller "
157
+ "than inlet temperature is not implemented."
158
+ )
159
+ self.E_P = np.nan
160
+ self.E_F = np.nan
161
+
162
+ # Calculate exergy destruction and efficiency
163
+ self.E_D = self.E_F - self.E_P
164
+ self.epsilon = self.calc_epsilon()
165
+
166
+ # Log the results
167
+ logging.info(
168
+ f"Compressor '{self.name}' exergy balance calculated: "
169
+ f"E_P={self.E_P:.2f} W, E_F={self.E_F:.2f} W, E_D={self.E_D:.2f} W, "
170
+ f"Efficiency={self.epsilon:.2%}"
171
+ )
172
+
173
+
174
+ def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
175
+ """
176
+ Auxiliary equations for the compressor.
177
+
178
+ This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
179
+ the following auxiliary cost relations:
180
+
181
+ (1) Chemical exergy cost equation (if enabled):
182
+ 1/E_CH_in * C_CH_in - 1/E_CH_out * C_CH_out = 0
183
+ - F-principle: specific chemical exergy costs equalized between inlet/outlet
184
+
185
+ (2) Thermal/Mechanical exergy cost equations (based on temperature conditions):
186
+
187
+ Case 1 (T_in > T0, T_out > T0):
188
+ 1/dET * C_T_out - 1/dET * C_T_in - 1/dEM * C_M_out + 1/dEM * C_M_in = 0
189
+ - P-principle: relates inlet/outlet thermal and mechanical exergy costs
190
+
191
+ Case 2 (T_in ≤ T0, T_out > T0):
192
+ 1/E_T_out * C_T_out - 1/dEM * C_M_out + 1/dEM * C_M_in = 0
193
+ - P-principle: relates outlet thermal and inlet/outlet mechanical exergy costs
194
+
195
+ Case 3 (T_in ≤ T0, T_out ≤ T0):
196
+ 1/E_T_out * C_T_out - 1/E_T_in * C_T_in = 0
197
+ - F-principle: specific thermal exergy costs equalized between inlet/outlet
198
+
199
+ Parameters
200
+ ----------
201
+ A : numpy.ndarray
202
+ The current cost matrix.
203
+ b : numpy.ndarray
204
+ The current right-hand-side vector.
205
+ counter : int
206
+ The current row index in the matrix.
207
+ T0 : float
208
+ Ambient temperature.
209
+ equations : list or dict
210
+ Data structure for storing equation labels.
211
+ chemical_exergy_enabled : bool
212
+ Flag indicating whether chemical exergy auxiliary equations should be added.
213
+
214
+ Returns
215
+ -------
216
+ A : numpy.ndarray
217
+ The updated cost matrix.
218
+ b : numpy.ndarray
219
+ The updated right-hand-side vector.
220
+ counter : int
221
+ The updated row index (increased by 2 if chemical exergy is enabled, or by 1 otherwise).
222
+ equations : list or dict
223
+ Updated structure with equation labels.
224
+ """
225
+ # --- Chemical equality equation (row added only if enabled) ---
226
+ if chemical_exergy_enabled:
227
+ # 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']}"
231
+ chem_row = 1
232
+ else:
233
+ chem_row = 0
234
+
235
+ # --- Thermal/Mechanical cost equation ---
236
+ # Compute differences in thermal and mechanical exergy:
237
+ dET = self.outl[0]["E_T"] - self.inl[0]["E_T"]
238
+ dEM = self.outl[0]["E_M"] - self.inl[0]["E_M"]
239
+
240
+ # The row for the thermal/mechanical equation:
241
+ row_index = counter + chem_row
242
+ if self.inl[0]["T"] > T0 and self.outl[0]["T"] > T0:
243
+ if dET != 0 and dEM != 0:
244
+ A[row_index, self.inl[0]["CostVar_index"]["T"]] = -1 / dET
245
+ A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1 / dET
246
+ A[row_index, self.inl[0]["CostVar_index"]["M"]] = 1 / dEM
247
+ A[row_index, self.outl[0]["CostVar_index"]["M"]] = -1 / dEM
248
+ equations[row_index] = f"aux_p_rule_{self.name}"
249
+ else:
250
+ logging.warning("Case where thermal or mechanical exergy difference is zero is not implemented.")
251
+ elif self.inl[0]["T"] <= T0 and self.outl[0]["T"] > T0:
252
+ A[row_index, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
253
+ A[row_index, self.inl[0]["CostVar_index"]["M"]] = 1 / dEM
254
+ A[row_index, self.outl[0]["CostVar_index"]["M"]] = -1 / dEM
255
+ equations[row_index] = f"aux_p_rule_{self.name}"
256
+ else:
257
+ A[row_index, self.inl[0]["CostVar_index"]["T"]] = -1 / self.inl[0]["E_T"]
258
+ 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
+
261
+ # Set the right-hand side entry for the thermal/mechanical row to zero.
262
+ b[row_index] = 0
263
+
264
+ # Update the counter accordingly.
265
+ if chemical_exergy_enabled:
266
+ new_counter = counter + 2
267
+ else:
268
+ new_counter = counter + 1
269
+
270
+ return A, b, new_counter, equations
271
+
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
+
283
+ Parameters
284
+ ----------
285
+ 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.
292
+ """
293
+ # Retrieve the cost of power from the inlet stream of kind "power"
294
+ power_cost = None
295
+ for stream in self.inl.values():
296
+ if stream.get("kind") == "power":
297
+ power_cost = stream.get("C_TOT")
298
+ break
299
+ if power_cost is None:
300
+ logging.error("No inlet power stream found to determine power cost (C_TOT).")
301
+ raise ValueError("No inlet power stream found for exergoeconomic_balance.")
302
+
303
+ # Compute product and fuel costs depending on inlet/outlet temperatures relative to T0.
304
+ if self.inl[0]["T"] >= T0 and self.outl[0]["T"] >= T0:
305
+ self.C_P = self.outl[0]["C_PH"] - self.inl[0]["C_PH"]
306
+ self.C_F = power_cost
307
+ elif self.inl[0]["T"] <= T0 and self.outl[0]["T"] > T0:
308
+ self.C_P = self.outl[0]["C_T"] + (self.outl[0]["C_M"] - self.inl[0]["C_M"])
309
+ self.C_F = power_cost + self.inl[0]["C_T"]
310
+ elif self.inl[0]["T"] <= T0 and self.outl[0]["T"] <= T0:
311
+ self.C_P = self.outl[0]["C_M"] - self.inl[0]["C_M"]
312
+ self.C_F = power_cost + (self.inl[0]["C_T"] - self.outl[0]["C_T"])
313
+
314
+ self.c_F = self.C_F / self.E_F
315
+ self.c_P = self.C_P / self.E_P
316
+ self.C_D = self.c_F * self.E_D
317
+ self.r = (self.C_P - self.C_F) / self.C_F
318
+ self.f = self.Z_costs / (self.Z_costs + self.C_D)