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.
- exerpy/__init__.py +12 -0
- exerpy/analyses.py +1711 -0
- exerpy/components/__init__.py +16 -0
- exerpy/components/combustion/__init__.py +0 -0
- exerpy/components/combustion/base.py +248 -0
- exerpy/components/component.py +126 -0
- exerpy/components/heat_exchanger/__init__.py +0 -0
- exerpy/components/heat_exchanger/base.py +449 -0
- exerpy/components/heat_exchanger/condenser.py +323 -0
- exerpy/components/heat_exchanger/simple.py +358 -0
- exerpy/components/heat_exchanger/steam_generator.py +264 -0
- exerpy/components/helpers/__init__.py +0 -0
- exerpy/components/helpers/cycle_closer.py +104 -0
- exerpy/components/nodes/__init__.py +0 -0
- exerpy/components/nodes/deaerator.py +318 -0
- exerpy/components/nodes/drum.py +164 -0
- exerpy/components/nodes/flash_tank.py +89 -0
- exerpy/components/nodes/mixer.py +332 -0
- exerpy/components/piping/__init__.py +0 -0
- exerpy/components/piping/valve.py +394 -0
- exerpy/components/power_machines/__init__.py +0 -0
- exerpy/components/power_machines/generator.py +168 -0
- exerpy/components/power_machines/motor.py +173 -0
- exerpy/components/turbomachinery/__init__.py +0 -0
- exerpy/components/turbomachinery/compressor.py +318 -0
- exerpy/components/turbomachinery/pump.py +310 -0
- exerpy/components/turbomachinery/turbine.py +351 -0
- exerpy/data/Ahrendts.json +90 -0
- exerpy/functions.py +637 -0
- exerpy/parser/__init__.py +0 -0
- exerpy/parser/from_aspen/__init__.py +0 -0
- exerpy/parser/from_aspen/aspen_config.py +61 -0
- exerpy/parser/from_aspen/aspen_parser.py +721 -0
- exerpy/parser/from_ebsilon/__init__.py +38 -0
- exerpy/parser/from_ebsilon/check_ebs_path.py +74 -0
- exerpy/parser/from_ebsilon/ebsilon_config.py +1055 -0
- exerpy/parser/from_ebsilon/ebsilon_functions.py +181 -0
- exerpy/parser/from_ebsilon/ebsilon_parser.py +660 -0
- exerpy/parser/from_ebsilon/utils.py +79 -0
- exerpy/parser/from_tespy/tespy_config.py +23 -0
- exerpy-0.0.1.dist-info/METADATA +158 -0
- exerpy-0.0.1.dist-info/RECORD +44 -0
- exerpy-0.0.1.dist-info/WHEEL +4 -0
- 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)
|