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.
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 +157 -114
  5. exerpy/components/component.py +8 -8
  6. exerpy/components/heat_exchanger/base.py +593 -256
  7. exerpy/components/heat_exchanger/condenser.py +353 -166
  8. exerpy/components/heat_exchanger/simple.py +575 -225
  9. exerpy/components/heat_exchanger/steam_generator.py +153 -123
  10. exerpy/components/helpers/cycle_closer.py +61 -34
  11. exerpy/components/helpers/power_bus.py +117 -0
  12. exerpy/components/nodes/deaerator.py +221 -102
  13. exerpy/components/nodes/drum.py +50 -39
  14. exerpy/components/nodes/flash_tank.py +218 -43
  15. exerpy/components/nodes/mixer.py +296 -115
  16. exerpy/components/nodes/splitter.py +173 -0
  17. exerpy/components/nodes/storage.py +130 -0
  18. exerpy/components/piping/valve.py +351 -139
  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 +329 -227
  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.1.dist-info → exerpy-0.0.3.dist-info}/METADATA +45 -4
  36. exerpy-0.0.3.dist-info/RECORD +48 -0
  37. exerpy-0.0.1.dist-info/RECORD +0 -44
  38. {exerpy-0.0.1.dist-info → exerpy-0.0.3.dist-info}/WHEEL +0 -0
  39. {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