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
@@ -11,9 +10,9 @@ class Deaerator(Component):
11
10
  r"""
12
11
  Class for exergy analysis of deaerators.
13
12
 
14
- This class performs exergy analysis calculations for deaerators with multiple
15
- inlet streams and one outlet stream. The exergy product and fuel definitions
16
- vary based on the temperature relationships between inlet streams, outlet
13
+ This class performs exergy analysis calculations for deaerators with multiple
14
+ inlet streams and one outlet stream. The exergy product and fuel definitions
15
+ vary based on the temperature relationships between inlet streams, outlet
17
16
  stream, and ambient conditions.
18
17
 
19
18
  Parameters
@@ -116,7 +115,7 @@ class Deaerator(Component):
116
115
  ------
117
116
  ValueError
118
117
  If the required inlet and outlet streams are not properly defined.
119
- """
118
+ """
120
119
  # Ensure that the component has both inlet and outlet streams
121
120
  if len(self.inl) < 2 or len(self.outl) < 1:
122
121
  raise ValueError("Deaerator requires at least two inlets and one outlet.")
@@ -125,34 +124,34 @@ class Deaerator(Component):
125
124
  self.E_F = 0
126
125
 
127
126
  # Case 1: Outlet temperature is greater than T0
128
- if self.outl[0]['T'] > T0:
127
+ if self.outl[0]["T"] > T0:
129
128
  for _, inlet in self.inl.items():
130
- if inlet['T'] < self.outl[0]['T']: # Tin < Tout
131
- if inlet['T'] >= T0: # and Tin >= T0
132
- self.E_P += inlet['m'] * (self.outl[0]['e_PH'] - inlet['e_PH'])
129
+ if inlet["T"] < self.outl[0]["T"]: # Tin < Tout
130
+ if inlet["T"] >= T0: # and Tin >= T0
131
+ self.E_P += inlet["m"] * (self.outl[0]["e_PH"] - inlet["e_PH"])
133
132
  else: # and Tin < T0
134
- self.E_P += inlet['m'] * self.outl[0]['e_PH']
135
- self.E_F += inlet['m'] * inlet['e_PH']
133
+ self.E_P += inlet["m"] * self.outl[0]["e_PH"]
134
+ self.E_F += inlet["m"] * inlet["e_PH"]
136
135
  else: # Tin > Tout
137
- self.E_F += inlet['m'] * (inlet['e_PH'] - self.outl[0]['e_PH'])
136
+ self.E_F += inlet["m"] * (inlet["e_PH"] - self.outl[0]["e_PH"])
138
137
 
139
138
  # Case 2: Outlet temperature is equal to T0
140
- elif self.outl[0]['T'] == T0:
139
+ elif self.outl[0]["T"] == T0:
141
140
  self.E_P = np.nan
142
141
  for _, inlet in self.inl.items():
143
- self.E_F += inlet['m'] * inlet['e_PH']
142
+ self.E_F += inlet["m"] * inlet["e_PH"]
144
143
 
145
144
  # Case 3: Outlet temperature is less than T0
146
145
  else:
147
146
  for _, inlet in self.inl.items():
148
- if inlet['T'] > self.outl[0]['T']: # Tin > Tout
149
- if inlet['T'] >= T0: # and Tin >= T0
150
- self.E_P += inlet['m'] * self.outl[0]['e_PH']
151
- self.E_F += inlet['m'] * inlet['e_PH']
147
+ if inlet["T"] > self.outl[0]["T"]: # Tin > Tout
148
+ if inlet["T"] >= T0: # and Tin >= T0
149
+ self.E_P += inlet["m"] * self.outl[0]["e_PH"]
150
+ self.E_F += inlet["m"] * inlet["e_PH"]
152
151
  else: # and Tin < T0
153
- self.E_P += inlet['m'] * (self.outl[0]['e_PH'] - inlet['e_PH'])
152
+ self.E_P += inlet["m"] * (self.outl[0]["e_PH"] - inlet["e_PH"])
154
153
  else: # Tin < Tout
155
- self.E_F += inlet['m'] * (inlet['e_PH'] - self.outl[0]['e_PH'])
154
+ self.E_F += inlet["m"] * (inlet["e_PH"] - self.outl[0]["e_PH"])
156
155
 
157
156
  # Calculate exergy destruction and efficiency
158
157
  if np.isnan(self.E_P):
@@ -163,7 +162,7 @@ class Deaerator(Component):
163
162
 
164
163
  # Log the results
165
164
  logging.info(
166
- f"Deaerator exergy balance calculated: "
165
+ f"Exergy balance of Deaerator {self.name} calculated: "
167
166
  f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
168
167
  f"Efficiency={self.epsilon:.2%}"
169
168
  )
@@ -171,25 +170,25 @@ class Deaerator(Component):
171
170
  def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
172
171
  """
173
172
  Auxiliary equations for the deaerator.
174
-
173
+
175
174
  This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
176
175
  the following auxiliary cost relations:
177
-
176
+
178
177
  (1) Mixing equation for chemical exergy costs (if enabled):
179
178
 
180
179
  - The outlet's specific chemical exergy cost is calculated as a mass-weighted average of the inlet streams' specific chemical exergy costs
181
-
180
+
182
181
  - This enforces proper chemical exergy cost distribution through the deaerator
183
-
182
+
184
183
  (2) Mixing equation for mechanical exergy costs:
185
-
184
+
186
185
  - The outlet's specific mechanical exergy cost is calculated as a mass-weighted average of the inlet streams' specific mechanical exergy costs
187
-
186
+
188
187
  - This ensures mechanical exergy costs are properly conserved in the mixing process
189
-
188
+
190
189
  Both equations implement the proportionality rule for mixing processes where
191
190
  the outlet's specific costs should reflect the contribution of each inlet stream.
192
-
191
+
193
192
  Parameters
194
193
  ----------
195
194
  A : numpy.ndarray
@@ -204,7 +203,7 @@ class Deaerator(Component):
204
203
  Dictionary for storing equation labels.
205
204
  chemical_exergy_enabled : bool
206
205
  Flag indicating whether chemical exergy auxiliary equations should be added.
207
-
206
+
208
207
  Returns
209
208
  -------
210
209
  A : numpy.ndarray
@@ -230,13 +229,16 @@ class Deaerator(Component):
230
229
  # Outlet chemical exergy is zero: assign fallback for all inlets.
231
230
  for inlet in self.inl.values():
232
231
  A[counter, inlet["CostVar_index"]["CH"]] = 1
233
- equations[counter] = f"aux_mixing_chem_{self.outl[0]['name']}"
232
+ equations[counter] = {
233
+ "kind": "aux_mixing",
234
+ "objects": [self.name, self.inl[0]["name"], self.inl[1]["name"], self.outl[0]["name"]],
235
+ "property": "c_CH",
236
+ }
234
237
  chem_row = 1 # One row added for chemical equation.
235
238
  else:
236
239
  chem_row = 0 # No row added.
237
240
 
238
241
  # --- Mechanical cost auxiliary equation ---
239
- mech_row = 0 # This row will always be added.
240
242
  if self.outl[0]["e_M"] != 0:
241
243
  A[counter + chem_row, self.outl[0]["CostVar_index"]["M"]] = -1 / self.outl[0]["E_M"]
242
244
  # Iterate over inlet streams for mechanical mixing.
@@ -248,7 +250,11 @@ class Deaerator(Component):
248
250
  else:
249
251
  for inlet in self.inl.values():
250
252
  A[counter + chem_row, inlet["CostVar_index"]["M"]] = 1
251
- equations[counter + chem_row] = f"aux_mixing_mech_{self.outl[0]['name']}"
253
+ equations[counter + chem_row] = {
254
+ "kind": "aux_mixing",
255
+ "objects": [self.name, self.inl[0]["name"], self.inl[1]["name"], self.outl[0]["name"]],
256
+ "property": "c_M",
257
+ }
252
258
 
253
259
  # Set the right-hand side entries to zero for the added rows.
254
260
  if chemical_exergy_enabled:
@@ -261,26 +267,141 @@ class Deaerator(Component):
261
267
 
262
268
  return A, b, counter, equations
263
269
 
264
- def exergoeconomic_balance(self, T0):
265
- """
266
- Perform exergoeconomic balance calculations for the deaerator.
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
-
270
+ def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
271
+ r"""
272
+ Perform exergoeconomic cost balance for the deaerator (mixing component).
273
+
274
+ The deaerator is a mixing component where multiple streams combine. The general
275
+ exergoeconomic balance equation is:
276
+
277
+ .. math::
278
+ \sum_{\mathrm{in}} \left(\dot{C}^{\mathrm{T}}_{\mathrm{in}}
279
+ + \dot{C}^{\mathrm{M}}_{\mathrm{in}}
280
+ + \dot{C}^{\mathrm{CH}}_{\mathrm{in}}\right)
281
+ - \sum_{\mathrm{out}} \left(\dot{C}^{\mathrm{T}}_{\mathrm{out}}
282
+ + \dot{C}^{\mathrm{M}}_{\mathrm{out}}
283
+ + \dot{C}^{\mathrm{CH}}_{\mathrm{out}}\right)
284
+ + \dot{Z}
285
+ = 0
286
+
287
+ The product is defined as the outlet stream. The fuel consists of all inlet
288
+ streams, with specific treatment depending on temperature levels. The cost
289
+ balance is closed using:
290
+
291
+ .. math::
292
+ \dot{C}_{\mathrm{P}} = \dot{C}_{\mathrm{F}} + \dot{Z}
293
+
294
+ **Case 1: Outlet above ambient temperature**
295
+
296
+ When :math:`T_{\mathrm{out}} > T_0`:
297
+
298
+ For cold inlets (:math:`T_{\mathrm{in}} < T_{\mathrm{out}}`):
299
+
300
+ .. math::
301
+ \dot{C}_{\mathrm{F,cold}} = \dot{C}^{\mathrm{M}}_{\mathrm{in}}
302
+ + \dot{C}^{\mathrm{CH}}_{\mathrm{in}}
303
+
304
+ For hot inlets (:math:`T_{\mathrm{in}} \geq T_{\mathrm{out}}`):
305
+
306
+ .. math::
307
+ \dot{C}_{\mathrm{F,hot}} = -\dot{m}_{\mathrm{in}} \cdot c^{\mathrm{T}}_{\mathrm{in}}
308
+ \cdot e^{\mathrm{T}}_{\mathrm{in}}
309
+ + \left(\dot{C}^{\mathrm{T}}_{\mathrm{in}}
310
+ + \dot{C}^{\mathrm{M}}_{\mathrm{in}}
311
+ + \dot{C}^{\mathrm{CH}}_{\mathrm{in}}\right)
312
+
313
+ Total fuel cost:
314
+
315
+ .. math::
316
+ \dot{C}_{\mathrm{F}} = \sum \dot{C}_{\mathrm{F,cold}}
317
+ + \sum \dot{C}_{\mathrm{F,hot}}
318
+ - \dot{C}^{\mathrm{M}}_{\mathrm{out}}
319
+ - \dot{C}^{\mathrm{CH}}_{\mathrm{out}}
320
+
321
+ **Case 2: Outlet at ambient temperature (dissipative)**
322
+
323
+ When :math:`|T_{\mathrm{out}} - T_0| < 10^{-6}`:
324
+
325
+ .. math::
326
+ \dot{C}_{\mathrm{F}} = \sum_{\mathrm{in}} \dot{C}^{\mathrm{TOT}}_{\mathrm{in}}
327
+
328
+ **Case 3: Outlet below ambient temperature**
329
+
330
+ When :math:`T_{\mathrm{out}} < T_0`:
331
+
332
+ For hot inlets (:math:`T_{\mathrm{in}} > T_{\mathrm{out}}`):
333
+
334
+ .. math::
335
+ \dot{C}_{\mathrm{F,hot}} = \dot{C}^{\mathrm{M}}_{\mathrm{in}}
336
+ + \dot{C}^{\mathrm{CH}}_{\mathrm{in}}
337
+
338
+ For cold inlets (:math:`T_{\mathrm{in}} \leq T_{\mathrm{out}}`):
339
+
340
+ .. math::
341
+ \dot{C}_{\mathrm{F,cold}} = -\dot{m}_{\mathrm{in}} \cdot c^{\mathrm{T}}_{\mathrm{in}}
342
+ \cdot e^{\mathrm{T}}_{\mathrm{in}}
343
+ + \left(\dot{C}^{\mathrm{T}}_{\mathrm{in}}
344
+ + \dot{C}^{\mathrm{M}}_{\mathrm{in}}
345
+ + \dot{C}^{\mathrm{CH}}_{\mathrm{in}}\right)
346
+
347
+ Total fuel cost:
348
+
349
+ .. math::
350
+ \dot{C}_{\mathrm{F}} = \sum \dot{C}_{\mathrm{F,hot}}
351
+ + \sum \dot{C}_{\mathrm{F,cold}}
352
+ - \dot{C}^{\mathrm{M}}_{\mathrm{out}}
353
+ - \dot{C}^{\mathrm{CH}}_{\mathrm{out}}
354
+
355
+ **Calculated exergoeconomic indicators:**
356
+
357
+ .. math::
358
+ c_{\mathrm{F}} = \frac{\dot{C}_{\mathrm{F}}}{\dot{E}_{\mathrm{F}}}
359
+
360
+ .. math::
361
+ c_{\mathrm{P}} = \frac{\dot{C}_{\mathrm{P}}}{\dot{E}_{\mathrm{P}}}
362
+
363
+ .. math::
364
+ \dot{C}_{\mathrm{D}} = c_{\mathrm{F}} \cdot \dot{E}_{\mathrm{D}}
365
+
366
+ .. math::
367
+ r = \frac{c_{\mathrm{P}} - c_{\mathrm{F}}}{c_{\mathrm{F}}}
368
+
369
+ .. math::
370
+ f = \frac{\dot{Z}}{\dot{Z} + \dot{C}_{\mathrm{D}}}
371
+
275
372
  Parameters
276
373
  ----------
277
374
  T0 : float
278
- Ambient temperature
279
-
375
+ Ambient temperature (K).
376
+ chemical_exergy_enabled : bool, optional
377
+ If True, chemical exergy is considered in the calculations.
378
+ Default is False.
379
+
380
+ Attributes Set
381
+ --------------
382
+ C_P : float
383
+ Cost rate of product (currency/time).
384
+ C_F : float
385
+ Cost rate of fuel (currency/time).
386
+ c_P : float
387
+ Specific cost of product (currency/energy).
388
+ c_F : float
389
+ Specific cost of fuel (currency/energy).
390
+ C_D : float
391
+ Cost rate of exergy destruction (currency/time).
392
+ r : float
393
+ Relative cost difference (dimensionless).
394
+ f : float
395
+ Exergoeconomic factor (dimensionless).
396
+
280
397
  Notes
281
398
  -----
282
- The exergoeconomic balance considers thermal (T), chemical (CH),
283
- and mechanical (M) exergy components for the inlet and outlet streams.
399
+ The deaerator treats thermal, mechanical, and chemical exergy components
400
+ differently depending on whether inlets are "hot" or "cold" relative to
401
+ the outlet temperature. The distinction ensures proper cost allocation
402
+ for streams that provide heating versus those being heated.
403
+
404
+ Future development may include merging profits from dissipative components.
284
405
  """
285
406
  self.C_P = 0
286
407
  self.C_F = 0
@@ -291,9 +412,8 @@ class Deaerator(Component):
291
412
  self.C_F += i["C_M"] + i["C_CH"]
292
413
  else:
293
414
  # hot inlets
294
- self.C_F += - i["M"] * i["C_T"] * i["e_T"] + (
295
- i["C_T"] + i["C_M"] + i["C_CH"])
296
- self.C_F += (-self.outl[0]["C_M"] - self.outl[0]["C_CH"])
415
+ self.C_F += -i["M"] * i["C_T"] * i["e_T"] + (i["C_T"] + i["C_M"] + i["C_CH"])
416
+ self.C_F += -self.outl[0]["C_M"] - self.outl[0]["C_CH"]
297
417
  elif self.outl[0]["T"] - 1e-6 < T0 and self.outl[0]["T"] + 1e-6 > T0:
298
418
  # dissipative
299
419
  for i in self.inl:
@@ -305,15 +425,13 @@ class Deaerator(Component):
305
425
  self.C_F += i["C_M"] + i["C_CH"]
306
426
  else:
307
427
  # cold inlets
308
- self.C_F += - i["M"] * i["C_T"] * i["e_T"] + (
309
- i["C_T"] + i["C_M"] + i["C_CH"])
310
- self.C_F += (-self.outl[0]["C_M"] - self.outl[0]["C_CH"])
311
- self.C_P = self.C_F + self.Z_costs # +1/num_serving_comps * C_diff
428
+ self.C_F += -i["M"] * i["C_T"] * i["e_T"] + (i["C_T"] + i["C_M"] + i["C_CH"])
429
+ self.C_F += -self.outl[0]["C_M"] - self.outl[0]["C_CH"]
430
+ self.C_P = self.C_F + self.Z_costs # +1/num_serving_comps * C_diff
312
431
  # ToDo: add case that merge profits from dissipative component(s)
313
432
 
314
-
315
433
  self.c_F = self.C_F / self.E_F
316
434
  self.c_P = self.C_P / self.E_P
317
435
  self.C_D = self.c_F * self.E_D
318
436
  self.r = (self.c_P - self.c_F) / self.c_F
319
- self.f = self.Z_costs / (self.Z_costs + self.C_D)
437
+ self.f = self.Z_costs / (self.Z_costs + self.C_D)
@@ -1,9 +1,6 @@
1
1
  import logging
2
2
 
3
- import numpy as np
4
-
5
- from exerpy.components.component import Component
6
- from exerpy.components.component import component_registry
3
+ from exerpy.components.component import Component, component_registry
7
4
 
8
5
 
9
6
  @component_registry
@@ -30,14 +27,8 @@ class Drum(Component):
30
27
  \dot{E}_\mathrm{P} = \sum \dot{E}_{\mathrm{out,}j}^\mathrm{PH}\\
31
28
  \dot{E}_\mathrm{F} = \sum \dot{E}_{\mathrm{in,}i}^\mathrm{PH}
32
29
  """
33
- self.E_P = (
34
- self.outl[0]['e_PH'] * self.outl[0]['m']
35
- + self.outl[1]['e_PH'] * self.outl[1]['m']
36
- )
37
- self.E_F = (
38
- self.inl[0]['e_PH'] * self.inl[0]['m']
39
- + self.inl[1]['e_PH'] * self.inl[1]['m']
40
- )
30
+ self.E_P = self.outl[0]["e_PH"] * self.outl[0]["m"] + self.outl[1]["e_PH"] * self.outl[1]["m"]
31
+ self.E_F = self.inl[0]["e_PH"] * self.inl[0]["m"] + self.inl[1]["e_PH"] * self.inl[1]["m"]
41
32
 
42
33
  # Calculate exergy destruction and efficiency
43
34
  self.E_D = self.E_F - self.E_P
@@ -45,7 +36,7 @@ class Drum(Component):
45
36
 
46
37
  # Log the results
47
38
  logging.info(
48
- f"Drum exergy balance calculated: "
39
+ f"Exergy balance of Drum {self.name} calculated: "
49
40
  f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
50
41
  f"Efficiency={self.epsilon:.2%}"
51
42
  )
@@ -102,18 +93,26 @@ class Drum(Component):
102
93
  A[counter, self.outl[0]["CostVar_index"]["CH"]] = -1 / self.outl[0]["E_CH"]
103
94
  else:
104
95
  A[counter, self.outl[0]["CostVar_index"]["CH"]] = -1
105
- equations[counter] = f"aux_drum_chem1_{self.outl[0]['name']}"
106
-
96
+ equations[counter] = {
97
+ "kind": "aux_equality",
98
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
99
+ "property": "c_CH",
100
+ }
101
+
107
102
  # Equation 2: Balance between inlet 0 and outlet 1 for chemical exergy
108
103
  if self.inl[0]["e_CH"] != 0:
109
- A[counter+1, self.inl[0]["CostVar_index"]["CH"]] = 1 / self.inl[0]["E_CH"]
104
+ A[counter + 1, self.inl[0]["CostVar_index"]["CH"]] = 1 / self.inl[0]["E_CH"]
110
105
  else:
111
- A[counter+1, self.inl[0]["CostVar_index"]["CH"]] = 1
106
+ A[counter + 1, self.inl[0]["CostVar_index"]["CH"]] = 1
112
107
  if self.outl[1]["e_CH"] != 0:
113
- A[counter+1, self.outl[1]["CostVar_index"]["CH"]] = -1 / self.outl[1]["E_CH"]
108
+ A[counter + 1, self.outl[1]["CostVar_index"]["CH"]] = -1 / self.outl[1]["E_CH"]
114
109
  else:
115
- A[counter+1, self.outl[1]["CostVar_index"]["CH"]] = -1
116
- equations[counter+1] = f"aux_drum_chem2_{self.outl[1]['name']}"
110
+ A[counter + 1, self.outl[1]["CostVar_index"]["CH"]] = -1
111
+ equations[counter + 1] = {
112
+ "kind": "aux_equality",
113
+ "objects": [self.name, self.inl[1]["name"], self.outl[1]["name"]],
114
+ "property": "c_CH",
115
+ }
117
116
  chem_rows = 2
118
117
  else:
119
118
  chem_rows = 0
@@ -121,41 +120,53 @@ class Drum(Component):
121
120
  # --- Thermal cost auxiliary equation ---
122
121
  # For thermal exergy, we balance the two outlets.
123
122
  if (self.outl[0]["e_T"] != 0) and (self.outl[1]["e_T"] != 0):
124
- A[counter+chem_rows, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
125
- A[counter+chem_rows, self.outl[1]["CostVar_index"]["T"]] = -1 / self.outl[1]["E_T"]
123
+ A[counter + chem_rows, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
124
+ A[counter + chem_rows, self.outl[1]["CostVar_index"]["T"]] = -1 / self.outl[1]["E_T"]
126
125
  elif self.outl[0]["e_T"] == 0 and self.outl[1]["e_T"] != 0:
127
- A[counter+chem_rows, self.outl[0]["CostVar_index"]["T"]] = 1
126
+ A[counter + chem_rows, self.outl[0]["CostVar_index"]["T"]] = 1
128
127
  elif self.outl[0]["e_T"] != 0 and self.outl[1]["e_T"] == 0:
129
- A[counter+chem_rows, self.outl[1]["CostVar_index"]["T"]] = -1
128
+ A[counter + chem_rows, self.outl[1]["CostVar_index"]["T"]] = -1
130
129
  else:
131
- A[counter+chem_rows, self.outl[0]["CostVar_index"]["T"]] = 1
132
- A[counter+chem_rows, self.outl[1]["CostVar_index"]["T"]] = -1
133
- equations[counter+chem_rows] = f"aux_drum_therm_{self.outl[0]['name']}_{self.outl[1]['name']}"
130
+ A[counter + chem_rows, self.outl[0]["CostVar_index"]["T"]] = 1
131
+ A[counter + chem_rows, self.outl[1]["CostVar_index"]["T"]] = -1
132
+ equations[counter + chem_rows] = {
133
+ "kind": "aux_p_rule",
134
+ "objects": [self.name, self.outl[0]["name"], self.outl[1]["name"]],
135
+ "property": "c_T",
136
+ }
134
137
 
135
138
  # --- Mechanical cost auxiliary equation ---
136
139
  if self.outl[0]["e_M"] != 0:
137
- A[counter+chem_rows+1, self.outl[0]["CostVar_index"]["M"]] = 1 / self.outl[0]["E_M"]
140
+ A[counter + chem_rows + 1, self.outl[0]["CostVar_index"]["M"]] = 1 / self.outl[0]["E_M"]
138
141
  else:
139
- A[counter+chem_rows+1, self.outl[0]["CostVar_index"]["M"]] = 1
142
+ A[counter + chem_rows + 1, self.outl[0]["CostVar_index"]["M"]] = 1
140
143
  if self.outl[1]["e_M"] != 0:
141
- A[counter+chem_rows+1, self.outl[1]["CostVar_index"]["M"]] = -1 / self.outl[1]["E_M"]
144
+ A[counter + chem_rows + 1, self.outl[1]["CostVar_index"]["M"]] = -1 / self.outl[1]["E_M"]
142
145
  else:
143
- A[counter+chem_rows+1, self.outl[1]["CostVar_index"]["M"]] = -1
144
- equations[counter+chem_rows+1] = f"aux_drum_mech_{self.outl[0]['name']}_{self.outl[1]['name']}"
146
+ A[counter + chem_rows + 1, self.outl[1]["CostVar_index"]["M"]] = -1
147
+ equations[counter + chem_rows + 1] = {
148
+ "kind": "aux_p_rule",
149
+ "objects": [self.name, self.outl[0]["name"], self.outl[1]["name"]],
150
+ "property": "c_M",
151
+ }
145
152
 
146
153
  # --- Thermal-Mechanical coupling equation for outlet 0 ---
147
154
  # This enforces that the thermal and mechanical cost components at outlet 0 are consistent.
148
155
  if (self.outl[0]["e_T"] != 0) and (self.outl[0]["e_M"] != 0):
149
- A[counter+chem_rows+2, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
150
- A[counter+chem_rows+2, self.outl[0]["CostVar_index"]["M"]] = -1 / self.outl[0]["E_M"]
156
+ A[counter + chem_rows + 2, self.outl[0]["CostVar_index"]["T"]] = 1 / self.outl[0]["E_T"]
157
+ A[counter + chem_rows + 2, self.outl[0]["CostVar_index"]["M"]] = -1 / self.outl[0]["E_M"]
151
158
  elif (self.outl[0]["e_T"] == 0) and (self.outl[0]["e_M"] == 0):
152
- A[counter+chem_rows+2, self.outl[0]["CostVar_index"]["T"]] = 1
153
- A[counter+chem_rows+2, self.outl[0]["CostVar_index"]["M"]] = -1
159
+ A[counter + chem_rows + 2, self.outl[0]["CostVar_index"]["T"]] = 1
160
+ A[counter + chem_rows + 2, self.outl[0]["CostVar_index"]["M"]] = -1
154
161
  elif self.outl[0]["e_T"] == 0:
155
- A[counter+chem_rows+2, self.outl[0]["CostVar_index"]["T"]] = 1
162
+ A[counter + chem_rows + 2, self.outl[0]["CostVar_index"]["T"]] = 1
156
163
  else:
157
- A[counter+chem_rows+2, self.outl[0]["CostVar_index"]["M"]] = -1
158
- equations[counter+chem_rows+2] = f"aux_drum_therm_mech_{self.outl[0]['name']}"
164
+ A[counter + chem_rows + 2, self.outl[0]["CostVar_index"]["M"]] = -1
165
+ equations[counter + chem_rows + 2] = {
166
+ "kind": "aux_equality",
167
+ "objects": [self.name, self.outl[0]["name"]],
168
+ "property": "c_T, c_M",
169
+ }
159
170
 
160
171
  # Set the right-hand side entries to zero for all added rows.
161
172
  for i in range(chem_rows + 3):