import logging
import numpy as np
from exerpy.components.component import Component, component_registry
[docs]
@component_registry
class Deaerator(Component):
r"""
Class for exergy analysis of deaerators.
This class performs exergy analysis calculations for deaerators with multiple
inlet streams and one outlet stream. The exergy product and fuel definitions
vary based on the temperature relationships between inlet streams, outlet
stream, and ambient conditions.
Parameters
----------
**kwargs : dict
Arbitrary keyword arguments passed to parent class.
Attributes
----------
E_F : float
Exergy fuel of the component :math:`\dot{E}_\mathrm{F}` in :math:`\mathrm{W}`.
E_P : float
Exergy product of the component :math:`\dot{E}_\mathrm{P}` in :math:`\mathrm{W}`.
E_D : float
Exergy destruction of the component :math:`\dot{E}_\mathrm{D}` in :math:`\mathrm{W}`.
epsilon : float
Exergetic efficiency of the component :math:`\varepsilon` in :math:`-`.
inl : dict
Dictionary containing inlet streams data with temperature, mass flows,
and specific exergies.
outl : dict
Dictionary containing outlet stream data with temperature, mass flows,
and specific exergies.
Notes
-----
The exergy analysis accounts for physical exergy only. The equations for exergy
product and fuel are defined based on temperature relationships:
.. math::
\displaystyle
\dot E_{P} =
\begin{cases}
\displaystyle
\sum_{i}\dot m_{i}\,\bigl(e_{\mathrm{out}}^{\mathrm{PH}}
-e_{\mathrm{in},i}^{\mathrm{PH}}\bigr),
\quad \text{if }T_{\mathrm{in},i}<T_{\mathrm{out}}\text{ and }T_{\mathrm{in},i}\ge T_{0},\\[8pt]
\displaystyle
\sum_{i}\dot m_{i}\,e_{\mathrm{out}}^{\mathrm{PH}},
\quad \text{if }T_{\mathrm{in},i}<T_{\mathrm{out}}\text{ and }T_{\mathrm{in},i}<T_{0},\\[8pt]
\displaystyle
\text{not defined (nan)},
\quad \text{if }T_{\mathrm{out}}=T_{0},\\[8pt]
\displaystyle
\sum_{i}\dot m_{i}\,e_{\mathrm{out}}^{\mathrm{PH}},
\quad \text{if }T_{\mathrm{in},i}>T_{\mathrm{out}}\text{ and }T_{\mathrm{in},i}\ge T_{0},\\[8pt]
\displaystyle
\sum_{i}\dot m_{i}\,\bigl(e_{\mathrm{out}}^{\mathrm{PH}}
-e_{\mathrm{in},i}^{\mathrm{PH}}\bigr),
\quad \text{if }T_{\mathrm{in},i}>T_{\mathrm{out}}\text{ and }T_{\mathrm{in},i}<T_{0}.
\end{cases}
.. math::
\displaystyle
\dot E_{F} =
\begin{cases}
\displaystyle
\sum_{i}\dot m_{i}\,\bigl(e_{\mathrm{in},i}^{\mathrm{PH}}
-e_{\mathrm{out}}^{\mathrm{PH}}\bigr),
\quad \text{if }T_{\mathrm{out}}>T_{0}\text{ and }T_{\mathrm{in},i}>T_{\mathrm{out}},\\[8pt]
\displaystyle
\sum_{i}\dot m_{i}\,e_{\mathrm{in},i}^{\mathrm{PH}},
\quad \text{if }T_{\mathrm{out}}>T_{0}\text{ and }T_{\mathrm{in},i}<T_{\mathrm{out}}
\text{ and }T_{\mathrm{in},i}<T_{0},\\[8pt]
\displaystyle
\sum_{i}\dot m_{i}\,e_{\mathrm{in},i}^{\mathrm{PH}},
\quad \text{if }T_{\mathrm{out}}=T_{0},\\[8pt]
\displaystyle
\sum_{i}\dot m_{i}\,e_{\mathrm{in},i}^{\mathrm{PH}},
\quad \text{if }T_{\mathrm{out}}<T_{0}\text{ and }T_{\mathrm{in},i}>T_{\mathrm{out}},\\[8pt]
\displaystyle
\sum_{i}\dot m_{i}\,\bigl(e_{\mathrm{in},i}^{\mathrm{PH}}
-e_{\mathrm{out}}^{\mathrm{PH}}\bigr),
\quad \text{if }T_{\mathrm{out}}<T_{0}\text{ and }T_{\mathrm{in},i}<T_{\mathrm{out}}.
\end{cases}
"""
def __init__(self, **kwargs):
r"""Initialize deaerator component with given parameters."""
super().__init__(**kwargs)
[docs]
def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
r"""
Calculate the exergy balance of the deaerator.
Performs exergy balance calculations considering the temperature relationships
between inlet streams, outlet stream, and ambient conditions.
Parameters
----------
T0 : float
Ambient temperature in :math:`\mathrm{K}`.
p0 : float
Ambient pressure in :math:`\mathrm{Pa}`.
split_physical_exergy : bool
Flag indicating whether physical exergy is split into thermal and mechanical components.
Raises
------
ValueError
If the required inlet and outlet streams are not properly defined.
"""
# Ensure that the component has both inlet and outlet streams
if len(self.inl) < 2 or len(self.outl) < 1:
raise ValueError("Deaerator requires at least two inlets and one outlet.")
self.E_P = 0
self.E_F = 0
# Case 1: Outlet temperature is greater than T0
if self.outl[0]["T"] > T0:
for _, inlet in self.inl.items():
if inlet["T"] < self.outl[0]["T"]: # Tin < Tout
if inlet["T"] >= T0: # and Tin >= T0
self.E_P += inlet["m"] * (self.outl[0]["e_PH"] - inlet["e_PH"])
else: # and Tin < T0
self.E_P += inlet["m"] * self.outl[0]["e_PH"]
self.E_F += inlet["m"] * inlet["e_PH"]
else: # Tin > Tout
self.E_F += inlet["m"] * (inlet["e_PH"] - self.outl[0]["e_PH"])
# Case 2: Outlet temperature is equal to T0
elif self.outl[0]["T"] == T0:
self.E_P = np.nan
for _, inlet in self.inl.items():
self.E_F += inlet["m"] * inlet["e_PH"]
# Case 3: Outlet temperature is less than T0
else:
for _, inlet in self.inl.items():
if inlet["T"] > self.outl[0]["T"]: # Tin > Tout
if inlet["T"] >= T0: # and Tin >= T0
self.E_P += inlet["m"] * self.outl[0]["e_PH"]
self.E_F += inlet["m"] * inlet["e_PH"]
else: # and Tin < T0
self.E_P += inlet["m"] * (self.outl[0]["e_PH"] - inlet["e_PH"])
else: # Tin < Tout
self.E_F += inlet["m"] * (inlet["e_PH"] - self.outl[0]["e_PH"])
# Calculate exergy destruction and efficiency
if np.isnan(self.E_P):
self.E_D = self.E_F
else:
self.E_D = self.E_F - self.E_P
self.epsilon = self.calc_epsilon()
# Log the results
logging.info(
f"Exergy balance of Deaerator {self.name} calculated: "
f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
f"Efficiency={self.epsilon:.2%}"
)
[docs]
def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
"""
Auxiliary equations for the deaerator.
This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
the following auxiliary cost relations:
(1) Mixing equation for chemical exergy costs (if enabled):
- The outlet's specific chemical exergy cost is calculated as a mass-weighted average of the inlet streams' specific chemical exergy costs
- This enforces proper chemical exergy cost distribution through the deaerator
(2) Mixing equation for mechanical exergy costs:
- The outlet's specific mechanical exergy cost is calculated as a mass-weighted average of the inlet streams' specific mechanical exergy costs
- This ensures mechanical exergy costs are properly conserved in the mixing process
Both equations implement the proportionality rule for mixing processes where
the outlet's specific costs should reflect the contribution of each inlet stream.
Parameters
----------
A : numpy.ndarray
The current cost matrix.
b : numpy.ndarray
The current right-hand-side vector.
counter : int
The current row index in the matrix.
T0 : float
Ambient temperature (provided for consistency; not used in this function).
equations : dict
Dictionary for storing equation labels.
chemical_exergy_enabled : bool
Flag indicating whether chemical exergy auxiliary equations should be added.
Returns
-------
A : numpy.ndarray
The updated cost matrix.
b : numpy.ndarray
The updated right-hand-side vector.
counter : int
The updated row index (increased by 2 if chemical exergy is enabled, or by 1 otherwise).
equations : dict
Updated dictionary with equation labels.
"""
# --- Chemical cost auxiliary equation (conditionally added) ---
if chemical_exergy_enabled:
if self.outl[0]["e_CH"] != 0:
A[counter, self.outl[0]["CostVar_index"]["CH"]] = -1 / self.outl[0]["E_CH"]
# Iterate over inlet streams for chemical mixing.
for inlet in self.inl.values():
if inlet["e_CH"] != 0:
A[counter, inlet["CostVar_index"]["CH"]] = inlet["m"] / (self.outl[0]["m"] * inlet["E_CH"])
else:
A[counter, inlet["CostVar_index"]["CH"]] = 1
else:
# Outlet chemical exergy is zero: enforce cost conservation sum(C_CH_inlets) - C_CH_outlet = 0
for inlet in self.inl.values():
A[counter, inlet["CostVar_index"]["CH"]] = 1
A[counter, self.outl[0]["CostVar_index"]["CH"]] = -1
equations[counter] = {
"kind": "aux_mixing",
"objects": [self.name, self.inl[0]["name"], self.inl[1]["name"], self.outl[0]["name"]],
"property": "c_CH",
}
chem_row = 1 # One row added for chemical equation.
else:
chem_row = 0 # No row added.
# --- Mechanical cost auxiliary equation ---
if self.outl[0]["e_M"] != 0:
A[counter + chem_row, self.outl[0]["CostVar_index"]["M"]] = -1 / self.outl[0]["E_M"]
# Iterate over inlet streams for mechanical mixing.
for inlet in self.inl.values():
if inlet["e_M"] != 0:
A[counter + chem_row, inlet["CostVar_index"]["M"]] = inlet["m"] / (self.outl[0]["m"] * inlet["E_M"])
else:
A[counter + chem_row, inlet["CostVar_index"]["M"]] = 1
else:
# When outlet e_M = 0, enforce cost conservation: sum(C_M_inlets) - C_M_outlet = 0
for inlet in self.inl.values():
A[counter + chem_row, inlet["CostVar_index"]["M"]] = 1
A[counter + chem_row, self.outl[0]["CostVar_index"]["M"]] = -1
equations[counter + chem_row] = {
"kind": "aux_mixing",
"objects": [self.name, self.inl[0]["name"], self.inl[1]["name"], self.outl[0]["name"]],
"property": "c_M",
}
# Set the right-hand side entries to zero for the added rows.
if chemical_exergy_enabled:
b[counter] = 0
b[counter + 1] = 0
counter += 2 # Two rows were added.
else:
b[counter] = 0
counter += 1 # Only one row was added.
return A, b, counter, equations
[docs]
def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
r"""
Perform exergoeconomic cost balance for the deaerator (mixing component).
The deaerator is a mixing component where multiple streams combine. The general
exergoeconomic balance equation is:
.. math::
\sum_{\mathrm{in}} \left(\dot{C}^{\mathrm{T}}_{\mathrm{in}}
+ \dot{C}^{\mathrm{M}}_{\mathrm{in}}
+ \dot{C}^{\mathrm{CH}}_{\mathrm{in}}\right)
- \sum_{\mathrm{out}} \left(\dot{C}^{\mathrm{T}}_{\mathrm{out}}
+ \dot{C}^{\mathrm{M}}_{\mathrm{out}}
+ \dot{C}^{\mathrm{CH}}_{\mathrm{out}}\right)
+ \dot{Z}
= 0
The product is defined as the outlet stream. The fuel consists of all inlet
streams, with specific treatment depending on temperature levels. The cost
balance is closed using:
.. math::
\dot{C}_{\mathrm{P}} = \dot{C}_{\mathrm{F}} + \dot{Z}
**Case 1: Outlet above ambient temperature**
When :math:`T_{\mathrm{out}} > T_0`:
For cold inlets (:math:`T_{\mathrm{in}} < T_{\mathrm{out}}`):
.. math::
\dot{C}_{\mathrm{F,cold}} = \dot{C}^{\mathrm{M}}_{\mathrm{in}}
+ \dot{C}^{\mathrm{CH}}_{\mathrm{in}}
For hot inlets (:math:`T_{\mathrm{in}} \geq T_{\mathrm{out}}`):
.. math::
\dot{C}_{\mathrm{F,hot}} = -\dot{m}_{\mathrm{in}} \cdot c^{\mathrm{T}}_{\mathrm{in}}
\cdot e^{\mathrm{T}}_{\mathrm{in}}
+ \left(\dot{C}^{\mathrm{T}}_{\mathrm{in}}
+ \dot{C}^{\mathrm{M}}_{\mathrm{in}}
+ \dot{C}^{\mathrm{CH}}_{\mathrm{in}}\right)
Total fuel cost:
.. math::
\dot{C}_{\mathrm{F}} = \sum \dot{C}_{\mathrm{F,cold}}
+ \sum \dot{C}_{\mathrm{F,hot}}
- \dot{C}^{\mathrm{M}}_{\mathrm{out}}
- \dot{C}^{\mathrm{CH}}_{\mathrm{out}}
**Case 2: Outlet at ambient temperature (dissipative)**
When :math:`|T_{\mathrm{out}} - T_0| < 10^{-6}`:
.. math::
\dot{C}_{\mathrm{F}} = \sum_{\mathrm{in}} \dot{C}^{\mathrm{TOT}}_{\mathrm{in}}
**Case 3: Outlet below ambient temperature**
When :math:`T_{\mathrm{out}} < T_0`:
For hot inlets (:math:`T_{\mathrm{in}} > T_{\mathrm{out}}`):
.. math::
\dot{C}_{\mathrm{F,hot}} = \dot{C}^{\mathrm{M}}_{\mathrm{in}}
+ \dot{C}^{\mathrm{CH}}_{\mathrm{in}}
For cold inlets (:math:`T_{\mathrm{in}} \leq T_{\mathrm{out}}`):
.. math::
\dot{C}_{\mathrm{F,cold}} = -\dot{m}_{\mathrm{in}} \cdot c^{\mathrm{T}}_{\mathrm{in}}
\cdot e^{\mathrm{T}}_{\mathrm{in}}
+ \left(\dot{C}^{\mathrm{T}}_{\mathrm{in}}
+ \dot{C}^{\mathrm{M}}_{\mathrm{in}}
+ \dot{C}^{\mathrm{CH}}_{\mathrm{in}}\right)
Total fuel cost:
.. math::
\dot{C}_{\mathrm{F}} = \sum \dot{C}_{\mathrm{F,hot}}
+ \sum \dot{C}_{\mathrm{F,cold}}
- \dot{C}^{\mathrm{M}}_{\mathrm{out}}
- \dot{C}^{\mathrm{CH}}_{\mathrm{out}}
**Calculated exergoeconomic indicators:**
.. math::
c_{\mathrm{F}} = \frac{\dot{C}_{\mathrm{F}}}{\dot{E}_{\mathrm{F}}}
.. math::
c_{\mathrm{P}} = \frac{\dot{C}_{\mathrm{P}}}{\dot{E}_{\mathrm{P}}}
.. math::
\dot{C}_{\mathrm{D}} = c_{\mathrm{F}} \cdot \dot{E}_{\mathrm{D}}
.. math::
r = \frac{c_{\mathrm{P}} - c_{\mathrm{F}}}{c_{\mathrm{F}}}
.. math::
f = \frac{\dot{Z}}{\dot{Z} + \dot{C}_{\mathrm{D}}}
Parameters
----------
T0 : float
Ambient temperature (K).
chemical_exergy_enabled : bool, optional
If True, chemical exergy is considered in the calculations.
Default is False.
Attributes Set
--------------
C_P : float
Cost rate of product (currency/time).
C_F : float
Cost rate of fuel (currency/time).
c_P : float
Specific cost of product (currency/energy).
c_F : float
Specific cost of fuel (currency/energy).
C_D : float
Cost rate of exergy destruction (currency/time).
r : float
Relative cost difference (dimensionless).
f : float
Exergoeconomic factor (dimensionless).
Notes
-----
The deaerator treats thermal, mechanical, and chemical exergy components
differently depending on whether inlets are "hot" or "cold" relative to
the outlet temperature. The distinction ensures proper cost allocation
for streams that provide heating versus those being heated.
Future development may include merging profits from dissipative components.
"""
self.C_P = 0
self.C_F = 0
if self.outl[0]["T"] > T0:
for i in self.inl.values():
if i["T"] < self.outl[0]["T"]:
# cold inlets
self.C_F += i["C_M"]
if chemical_exergy_enabled:
self.C_F += i["C_CH"]
else:
# hot inlets
self.C_F += -i["m"] * i["c_T"] * i["e_T"] + (i["C_T"] + i["C_M"])
if chemical_exergy_enabled:
self.C_F += i["C_CH"]
self.C_F += -self.outl[0]["C_M"]
if chemical_exergy_enabled:
self.C_F += -self.outl[0]["C_CH"]
elif self.outl[0]["T"] - 1e-6 < T0 and self.outl[0]["T"] + 1e-6 > T0:
# dissipative
for i in self.inl.values():
self.C_F += i["C_TOT"]
else:
for i in self.inl.values():
if i["T"] > self.outl[0]["T"]:
# hot inlets
self.C_F += i["C_M"]
if chemical_exergy_enabled:
self.C_F += i["C_CH"]
else:
# cold inlets
self.C_F += -i["m"] * i["c_T"] * i["e_T"] + (i["C_T"] + i["C_M"])
if chemical_exergy_enabled:
self.C_F += i["C_CH"]
self.C_F += -self.outl[0]["C_M"]
if chemical_exergy_enabled:
self.C_F += -self.outl[0]["C_CH"]
self.C_P = self.C_F + self.Z_costs + getattr(self, "Z_diss", 0)
self.c_F = self.C_F / self.E_F
self.c_P = self.C_P / self.E_P
self.C_D = self.c_F * self.E_D
self.r = (self.c_P - self.c_F) / self.c_F
self.f = self.Z_costs / (self.Z_costs + self.C_D)