Source code for exerpy.components.solar_thermal.solartower

import numpy as np

from exerpy.components.component import Component
from exerpy.components.component import component_registry
from exerpy.logger import logger

from . import T_SUN


[docs] @component_registry class SolarTower(Component): r""" Class for exergy analysis of Solar Tower. The component has a working-fluid inlet and outlet plus a heat connection that carries the solar heat input. The fuel is the supplied solar heat exergy and the product is the physical exergy gained by the working fluid. All thermal losses are accounted as exergy destruction (:math:`\dot{E}_\mathrm{D}`), not as a separate exergy loss. 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 Inlet connections: the working-fluid stream and the heat connection that carries the solar heat input (the latter has no mass flow). outl : dict Outlet connection: the working-fluid stream. """ def __init__(self, **kwargs): r""" Initializes the SolarTower component. """ super().__init__(**kwargs) self.F = None
[docs] def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None: r""" Method to calculate the exergy balance of the Solar Tower component. Parameters ---------- T0 : float Ambient temperature in :math:`\mathrm{K}`. p0 : float Ambient pressure in :math:`\mathrm{Pa}`. split_physical_exergy : bool Flag to indicate whether to split physical exergy into thermal and mechanical parts. Returns ------- None The method updates the attributes E_F, E_P, E_D, and epsilon of the component instance. Raises ------ ValueError If there is no outlet, more than one outlet, or no valid solar heat input. Notes ----- Without splitting physical exergy: - Exergy fuel: the supplied solar heat exergy, :math:`\dot{E}_\mathrm{F} = |\dot{E}_\mathrm{heat}|`. - Exergy product: the physical-exergy gain of the working fluid, :math:`\dot{E}_\mathrm{P} = \dot{m}\,(e^\mathrm{PH}_\mathrm{out} - e^\mathrm{PH}_\mathrm{in})`. With split physical exergy (the receiver's useful output is thermal exergy, as in the SimpleHeatExchanger heat-release product): - Exergy product: the thermal-exergy gain, :math:`\dot{E}_\mathrm{P} = \dot{m}\,(e^\mathrm{T}_\mathrm{out} - e^\mathrm{T}_\mathrm{in})`. - Exergy fuel: solar heat plus the fluid's mechanical-exergy decrease, :math:`\dot{E}_\mathrm{F} = |\dot{E}_\mathrm{heat}| + \dot{m}\,(e^\mathrm{M}_\mathrm{in} - e^\mathrm{M}_\mathrm{out})`. In both cases :math:`\dot{E}_\mathrm{D} = \dot{E}_\mathrm{F} - \dot{E}_\mathrm{P}` equals the true destruction and :math:`\varepsilon = \dot{E}_\mathrm{P} / \dot{E}_\mathrm{F}`. """ # Validate the number of inlets and outlets if not hasattr(self, "inl") or not hasattr(self, "outl") or len(self.outl) != 1: msg = "SolarTower requires at least one inlet and exactly one outlet." logger.error(msg) raise ValueError(msg) if len(self.inl) < 1: msg = "SolarTower requires at least one inlet stream." logger.error(msg) raise ValueError(msg) # Extract inlet and outlet streams inlet = self.inl[0] outlet = self.outl[0] # Solar heat input: the heat-kind inlet connection (selected by kind, not a fixed # slot, so the result does not depend on component-evaluation order). If it already # carries an exergy value (written by an upstream heliostat field) use it directly; # otherwise convert its raw heat flow with the Petela/Spanner factor. heat_inlets = [c for c in self.inl.values() if c is not None and c.get("kind") == "heat"] if not heat_inlets: msg = f"SolarTower {self.name} has no solar heat input connection." logger.error(msg) raise ValueError(msg) heat_in = heat_inlets[0] if heat_in.get("E") is not None: self.F = heat_in["E"] elif heat_in.get("energy_flow") is not None: self.F = heat_in["energy_flow"] * (1 - (4 / 3) * (T0 / T_SUN)) else: msg = f"SolarTower {self.name} has no valid solar heat input." logger.error(msg) raise ValueError(msg) # The receiver's useful output is the THERMAL exergy delivered to the working fluid # (as in the SimpleHeatExchanger heat-release product). With split physical exergy the # product is the thermal-exergy gain; the fluid's mechanical-exergy decrease (pressure # drop) is booked into the fuel, so E_D = E_F - E_P stays the true destruction. if split_physical_exergy: self.E_P = outlet["m"] * (outlet["e_T"] - inlet["e_T"]) self.E_F = abs(self.F) + outlet["m"] * (inlet["e_M"] - outlet["e_M"]) else: self.E_P = outlet["m"] * (outlet["e_PH"] - inlet["e_PH"]) self.E_F = abs(self.F) # Calculate exergetic efficiency self.epsilon = self.calc_epsilon() # Calculate exergy destruction if not np.isnan(self.E_P): self.E_D = self.E_F - self.E_P else: self.E_D = self.E_F # Log the results logger.info( f"Solar Tower exergy balance 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): r"""Exergoeconomic auxiliary equations are not yet implemented for this component.""" raise NotImplementedError("Exergoeconomic analysis is not yet implemented for the SolarTower component.")
[docs] def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False): r"""Exergoeconomic balance is not yet implemented for this component.""" raise NotImplementedError("Exergoeconomic analysis is not yet implemented for the SolarTower component.")