exerpy 0.0.1__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.
- exerpy/__init__.py +2 -4
- exerpy/analyses.py +597 -297
- exerpy/components/__init__.py +3 -0
- exerpy/components/combustion/base.py +157 -114
- exerpy/components/component.py +8 -8
- exerpy/components/heat_exchanger/base.py +593 -256
- exerpy/components/heat_exchanger/condenser.py +353 -166
- exerpy/components/heat_exchanger/simple.py +575 -225
- exerpy/components/heat_exchanger/steam_generator.py +153 -123
- exerpy/components/helpers/cycle_closer.py +61 -34
- exerpy/components/helpers/power_bus.py +117 -0
- exerpy/components/nodes/deaerator.py +221 -102
- exerpy/components/nodes/drum.py +50 -39
- exerpy/components/nodes/flash_tank.py +218 -43
- exerpy/components/nodes/mixer.py +296 -115
- exerpy/components/nodes/splitter.py +173 -0
- exerpy/components/nodes/storage.py +130 -0
- exerpy/components/piping/valve.py +351 -139
- exerpy/components/power_machines/generator.py +105 -38
- exerpy/components/power_machines/motor.py +111 -39
- exerpy/components/turbomachinery/compressor.py +181 -63
- exerpy/components/turbomachinery/pump.py +182 -63
- exerpy/components/turbomachinery/turbine.py +182 -74
- exerpy/functions.py +388 -263
- exerpy/parser/from_aspen/aspen_config.py +57 -48
- exerpy/parser/from_aspen/aspen_parser.py +373 -280
- exerpy/parser/from_ebsilon/__init__.py +2 -2
- exerpy/parser/from_ebsilon/check_ebs_path.py +15 -19
- exerpy/parser/from_ebsilon/ebsilon_config.py +329 -227
- exerpy/parser/from_ebsilon/ebsilon_functions.py +205 -38
- exerpy/parser/from_ebsilon/ebsilon_parser.py +392 -255
- exerpy/parser/from_ebsilon/utils.py +16 -11
- exerpy/parser/from_tespy/tespy_config.py +32 -1
- exerpy/parser/from_tespy/tespy_parser.py +151 -0
- {exerpy-0.0.1.dist-info → exerpy-0.0.3.dist-info}/METADATA +45 -4
- exerpy-0.0.3.dist-info/RECORD +48 -0
- exerpy-0.0.1.dist-info/RECORD +0 -44
- {exerpy-0.0.1.dist-info → exerpy-0.0.3.dist-info}/WHEEL +0 -0
- {exerpy-0.0.1.dist-info → exerpy-0.0.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from exerpy.components.component import Component, component_registry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@component_registry
|
|
9
|
+
class Splitter(Component):
|
|
10
|
+
r"""
|
|
11
|
+
Class for exergy analysis of splitters.
|
|
12
|
+
|
|
13
|
+
This class performs exergy analysis calculations for splitters with one
|
|
14
|
+
inlet stream and multiple outlet stream. For this component, it is not
|
|
15
|
+
reasonable to define exergy fuel and product in the same way as for other components,
|
|
16
|
+
since the splitter does not convert energy from one form to another.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
**kwargs : dict
|
|
21
|
+
Arbitrary keyword arguments passed to parent class.
|
|
22
|
+
|
|
23
|
+
Attributes
|
|
24
|
+
----------
|
|
25
|
+
inl : dict
|
|
26
|
+
Dictionary containing inlet streams data with temperature, mass flows,
|
|
27
|
+
and specific exergies.
|
|
28
|
+
outl : dict
|
|
29
|
+
Dictionary containing outlet stream data with temperature, mass flows,
|
|
30
|
+
and specific exergies.
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, **kwargs):
|
|
35
|
+
r"""Initialize splitter component with given parameters."""
|
|
36
|
+
super().__init__(**kwargs)
|
|
37
|
+
|
|
38
|
+
def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
|
|
39
|
+
r"""
|
|
40
|
+
Calculate the exergy balance of the splitter.
|
|
41
|
+
|
|
42
|
+
Performs exergy balance calculations.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
T0 : float
|
|
47
|
+
Ambient temperature in :math:`\mathrm{K}`.
|
|
48
|
+
p0 : float
|
|
49
|
+
Ambient pressure in :math:`\mathrm{Pa}`.
|
|
50
|
+
split_physical_exergy : bool
|
|
51
|
+
Flag indicating whether physical exergy is split into thermal and mechanical components.
|
|
52
|
+
|
|
53
|
+
Raises
|
|
54
|
+
------
|
|
55
|
+
ValueError
|
|
56
|
+
If the required inlet and outlet streams are not properly defined.
|
|
57
|
+
"""
|
|
58
|
+
# Ensure that the component has at least two inlets and one outlet.
|
|
59
|
+
if len(self.inl) < 1 or len(self.outl) < 2:
|
|
60
|
+
raise ValueError("Splitter requires at least one inlet and two outlets.")
|
|
61
|
+
outlet_list = list(self.outl.values())
|
|
62
|
+
inlet_list = list(self.inl.values())
|
|
63
|
+
E_in = sum(inlet.get("m", 0) * inlet.get("e_PH") for inlet in inlet_list)
|
|
64
|
+
E_out = sum(outlet.get("m", 0) * outlet.get("e_PH") for outlet in outlet_list)
|
|
65
|
+
self.E_P = np.nan
|
|
66
|
+
self.E_F = np.nan
|
|
67
|
+
self.E_D = E_in - E_out
|
|
68
|
+
self.epsilon = np.nan
|
|
69
|
+
|
|
70
|
+
# Log the results.
|
|
71
|
+
logging.info(
|
|
72
|
+
f"Exergy balance of Splitter {self.name} calculated: "
|
|
73
|
+
f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
|
|
74
|
+
f"Efficiency={self.epsilon:.2%}"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
|
|
78
|
+
"""
|
|
79
|
+
Auxiliary equations for the splitter.
|
|
80
|
+
|
|
81
|
+
This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
|
|
82
|
+
equality of specific exergy costs between the single inlet stream and each outlet stream.
|
|
83
|
+
Thermal and mechanical costs are always equated; chemical costs are equated only if enabled.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
A : numpy.ndarray
|
|
88
|
+
The current cost matrix.
|
|
89
|
+
b : numpy.ndarray
|
|
90
|
+
The current right-hand-side vector.
|
|
91
|
+
counter : int
|
|
92
|
+
The current row index in the matrix.
|
|
93
|
+
T0 : float
|
|
94
|
+
Ambient temperature (not used).
|
|
95
|
+
equations : list or dict
|
|
96
|
+
Data structure for storing equation labels.
|
|
97
|
+
chemical_exergy_enabled : bool
|
|
98
|
+
Flag indicating whether chemical exergy auxiliary equations should be added.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
A : numpy.ndarray
|
|
103
|
+
The updated cost matrix.
|
|
104
|
+
b : numpy.ndarray
|
|
105
|
+
The updated right-hand-side vector.
|
|
106
|
+
counter : int
|
|
107
|
+
The updated row index after adding equations.
|
|
108
|
+
equations : list or dict
|
|
109
|
+
Updated structure with equation labels.
|
|
110
|
+
"""
|
|
111
|
+
inlet = self.inl[0]
|
|
112
|
+
|
|
113
|
+
# Thermal cost equality for each outlet
|
|
114
|
+
for outlet in self.outl.values():
|
|
115
|
+
A[counter, inlet["CostVar_index"]["T"]] = (1 / inlet["e_T"]) if inlet["e_T"] != 0 else 1
|
|
116
|
+
A[counter, outlet["CostVar_index"]["T"]] = (-1 / outlet["e_T"]) if outlet["e_T"] != 0 else -1
|
|
117
|
+
equations[counter] = {
|
|
118
|
+
"kind": "aux_equality",
|
|
119
|
+
"objects": [self.name, inlet["name"], outlet["name"]],
|
|
120
|
+
"property": "c_T",
|
|
121
|
+
}
|
|
122
|
+
b[counter] = 0
|
|
123
|
+
counter += 1
|
|
124
|
+
|
|
125
|
+
# Mechanical cost equality for each outlet
|
|
126
|
+
for outlet in self.outl.values():
|
|
127
|
+
A[counter, inlet["CostVar_index"]["M"]] = (1 / inlet["e_M"]) if inlet["e_M"] != 0 else 1
|
|
128
|
+
A[counter, outlet["CostVar_index"]["M"]] = (-1 / outlet["e_M"]) if outlet["e_M"] != 0 else -1
|
|
129
|
+
equations[counter] = {
|
|
130
|
+
"kind": "aux_equality",
|
|
131
|
+
"objects": [self.name, inlet["name"], outlet["name"]],
|
|
132
|
+
"property": "c_M",
|
|
133
|
+
}
|
|
134
|
+
b[counter] = 0
|
|
135
|
+
counter += 1
|
|
136
|
+
|
|
137
|
+
# Chemical cost equality for each outlet (if enabled)
|
|
138
|
+
if chemical_exergy_enabled:
|
|
139
|
+
for outlet in self.outl.values():
|
|
140
|
+
A[counter, inlet["CostVar_index"]["CH"]] = (1 / inlet["e_CH"]) if inlet["e_CH"] != 0 else 1
|
|
141
|
+
A[counter, outlet["CostVar_index"]["CH"]] = (-1 / outlet["e_CH"]) if outlet["e_CH"] != 0 else -1
|
|
142
|
+
equations[counter] = {
|
|
143
|
+
"kind": "aux_equality",
|
|
144
|
+
"objects": [self.name, inlet["name"], outlet["name"]],
|
|
145
|
+
"property": "c_CH",
|
|
146
|
+
}
|
|
147
|
+
b[counter] = 0
|
|
148
|
+
counter += 1
|
|
149
|
+
|
|
150
|
+
return A, b, counter, equations
|
|
151
|
+
|
|
152
|
+
def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
|
|
153
|
+
"""
|
|
154
|
+
The exergoeconomic balance for the Splitter component is not neglected,
|
|
155
|
+
as it does not perform any conversion of energy forms.
|
|
156
|
+
Instead, it is assumed that the specific costs of the inlet and outlet streams are equal.
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
T0 : float
|
|
162
|
+
Ambient temperature
|
|
163
|
+
chemical_exergy_enabled : bool, optional
|
|
164
|
+
If True, chemical exergy is considered in the calculations.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
self.C_P = np.nan
|
|
168
|
+
self.C_F = np.nan
|
|
169
|
+
self.c_F = np.nan
|
|
170
|
+
self.c_P = np.nan
|
|
171
|
+
self.C_D = np.nan
|
|
172
|
+
self.r = np.nan
|
|
173
|
+
self.f = np.nan
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from exerpy.components.component import Component, component_registry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@component_registry
|
|
9
|
+
class Storage(Component):
|
|
10
|
+
r"""
|
|
11
|
+
Class for exergy and exergoeconomic analysis of a storage.
|
|
12
|
+
|
|
13
|
+
This class performs exergy and exergoeconomic analysis calculations for storage components,
|
|
14
|
+
accounting for one inlet and one outlet stream.
|
|
15
|
+
|
|
16
|
+
Attributes
|
|
17
|
+
----------
|
|
18
|
+
E_F : float
|
|
19
|
+
Exergy fuel of the component :math:`\dot{E}_\mathrm{F}` in :math:`\mathrm{W}`.
|
|
20
|
+
E_P : float
|
|
21
|
+
Exergy product of the component :math:`\dot{E}_\mathrm{P}` in :math:`\mathrm{W}`.
|
|
22
|
+
E_D : float
|
|
23
|
+
Exergy destruction of the component :math:`\dot{E}_\mathrm{D}` in :math:`\mathrm{W}`.
|
|
24
|
+
epsilon : float
|
|
25
|
+
Exergetic efficiency of the component :math:`\varepsilon` in :math:`-`.
|
|
26
|
+
inl : dict
|
|
27
|
+
Dictionary containing inlet stream data with mass flows and specific exergies.
|
|
28
|
+
outl : dict
|
|
29
|
+
Dictionary containing outlet stream data with mass flows and specific exergies.
|
|
30
|
+
Z_costs : float
|
|
31
|
+
Investment cost rate of the component in currency/h.
|
|
32
|
+
C_P : float
|
|
33
|
+
Cost of product stream :math:`\dot{C}_P` in currency/h.
|
|
34
|
+
C_F : float
|
|
35
|
+
Cost of fuel stream :math:`\dot{C}_F` in currency/h.
|
|
36
|
+
C_D : float
|
|
37
|
+
Cost of exergy destruction :math:`\dot{C}_D` in currency/h.
|
|
38
|
+
c_P : float
|
|
39
|
+
Specific cost of product stream (currency per unit exergy).
|
|
40
|
+
c_F : float
|
|
41
|
+
Specific cost of fuel stream (currency per unit exergy).
|
|
42
|
+
r : float
|
|
43
|
+
Relative cost difference, :math:`(c_P - c_F)/c_F`.
|
|
44
|
+
f : float
|
|
45
|
+
Exergoeconomic factor, :math:`\dot{Z}/(\dot{Z} + \dot{C}_D)`.
|
|
46
|
+
Ex_C_col : dict
|
|
47
|
+
Custom cost coefficients collection passed via `kwargs`.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, **kwargs):
|
|
51
|
+
r"""
|
|
52
|
+
Initialize the storage component.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
**kwargs : dict
|
|
57
|
+
Arbitrary keyword arguments. Recognized keys:
|
|
58
|
+
- Ex_C_col (dict): custom cost coefficients, default {}
|
|
59
|
+
- Z_costs (float): investment cost rate in currency/h, default 0.0
|
|
60
|
+
"""
|
|
61
|
+
self.dissipative = False
|
|
62
|
+
super().__init__(**kwargs)
|
|
63
|
+
|
|
64
|
+
def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
|
|
65
|
+
r"""
|
|
66
|
+
Compute the exergy balance of the storage.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
T0 : float
|
|
71
|
+
Ambient temperature in Kelvin.
|
|
72
|
+
p0 : float
|
|
73
|
+
Ambient pressure in Pascal.
|
|
74
|
+
split_physical_exergy : bool
|
|
75
|
+
Flag indicating whether physical exergy is split into thermal and mechanical components.
|
|
76
|
+
|
|
77
|
+
Notes
|
|
78
|
+
-----
|
|
79
|
+
The exergy analysis considers the cases where the storage is either charged or discharged.
|
|
80
|
+
|
|
81
|
+
Case 1 (Charging):
|
|
82
|
+
|
|
83
|
+
.. math::
|
|
84
|
+
|
|
85
|
+
\dot{E}_\mathrm{F} = \dot{E}_\mathrm{in}^\mathrm{PH} - \dot{E}_\mathrm{out}^\mathrm{PH}
|
|
86
|
+
|
|
87
|
+
.. math::
|
|
88
|
+
\dot{E}_\mathrm{P} = (\dot{m}_\mathrm{in} - \dot{m}_\mathrm{out}) \cdot e_\mathrm{out}^\mathrm{PH}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
Case 2 (Discharging):
|
|
92
|
+
|
|
93
|
+
.. math::
|
|
94
|
+
|
|
95
|
+
\dot{E}_\mathrm{F} = (\dot{m}_\mathrm{out} - \dot{m}_\mathrm{in}) \cdot e_\mathrm{out}^\mathrm{PH}
|
|
96
|
+
|
|
97
|
+
.. math::
|
|
98
|
+
|
|
99
|
+
\dot{E}_\mathrm{P} = \dot{E}_\mathrm{out}^\mathrm{PH} - \dot{E}_\mathrm{in}^\mathrm{PH}
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
if self.outl[0]["m"] < self.inl[0]["m"]:
|
|
104
|
+
logging.info(f"Storage '{self.name}' is charged.")
|
|
105
|
+
self.E_F = self.inl[0]["m"] * self.inl[0]["e_PH"] - self.outl[0]["m"] * self.outl[0]["e_PH"]
|
|
106
|
+
self.E_P = (self.inl[0]["m"] - self.outl[0]["m"]) * self.outl[0][
|
|
107
|
+
"e_PH"
|
|
108
|
+
] # assuming that exergy is stored at the same temperature as the outlet
|
|
109
|
+
self.E_D = self.E_F - self.E_P
|
|
110
|
+
elif self.outl[0]["m"] > self.inl[0]["m"]:
|
|
111
|
+
logging.info(f"Storage '{self.name}' is discharged.")
|
|
112
|
+
self.E_F = (self.outl[0]["m"] - self.inl[0]["m"]) * self.outl[0][
|
|
113
|
+
"e_PH"
|
|
114
|
+
] # assuming that exergy is stored at the same temperature as the outlet
|
|
115
|
+
self.E_P = self.outl[0]["m"] * self.outl[0]["e_PH"] - self.inl[0]["m"] * self.inl[0]["e_PH"]
|
|
116
|
+
self.E_D = self.E_F - self.E_P
|
|
117
|
+
|
|
118
|
+
self.epsilon = self.E_P / self.E_F if self.E_F != 0 else np.nan
|
|
119
|
+
|
|
120
|
+
# Log the results.
|
|
121
|
+
logging.info(
|
|
122
|
+
f"Exergy balance of Storage {self.name} calculated: "
|
|
123
|
+
f"E_F = {self.E_F:.2f} W, E_P = {self.E_P:.2f} W, E_D = {self.E_D:.2f} W, "
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
|
|
127
|
+
r"""
|
|
128
|
+
This class has not been implemented yet!
|
|
129
|
+
"""
|
|
130
|
+
pass
|