diff --git a/process/models/physics/confinement_time.py b/process/models/physics/confinement_time.py index 78d6eca7ad..6e697c71c7 100644 --- a/process/models/physics/confinement_time.py +++ b/process/models/physics/confinement_time.py @@ -1,6 +1,7 @@ """Confinement time model calculations and definitions.""" import logging +from dataclasses import dataclass import numpy as np from scipy.optimize import root_scalar @@ -20,6 +21,24 @@ logger = logging.getLogger(__name__) +@dataclass(slots=True, frozen=True) +class ConfinementTimeData: + """Dataclass to hold confinement time model calculation parameters.""" + + pden_electron_transport_loss_mw: float + """Electron transport loss power density in MW.""" + pden_ion_transport_loss_mw: float + """Ion transport loss power density in MW.""" + t_electron_energy_confinement: float + """Electron energy confinement time in seconds.""" + t_ion_energy_confinement: float + """Ion energy confinement time in seconds.""" + t_plasma_energy_confinement: float + """Plasma energy confinement time in seconds.""" + p_plasma_loss_mw: float + """Total plasma loss power in MW.""" + + class PlasmaConfinementTime(Model): """Class to calculate plasma confinement time using various empirical scaling laws""" @@ -60,7 +79,7 @@ def calculate_confinement_time( zeff: float, eden_plasma_electrons_thermal_vol_avg: float, eden_plasma_ions_thermal_vol_avg: float, - ) -> tuple[float, float, float, float, float, float, float]: + ) -> ConfinementTimeData: """Calculate the confinement times and the transport power loss terms. Parameters @@ -118,14 +137,8 @@ def calculate_confinement_time( Returns ------- - type - Tuple containing: - - pden_electron_transport_loss_mw (float): Electron transport power (MW/m³) - - pden_ion_transport_loss_mw (float): Ion transport power (MW/m³) - - t_electron_energy_confinement (float): Electron energy confinement time (s) - - t_ion_energy_confinement (float): Ion energy confinement time (s) - - t_energy_confinement (float): Global energy confinement time (s) - - p_plasma_loss_mw (float): Heating power (MW) assumed in calculation + ConfinementTimeData + Dataclass containing confinement time model calculation parameters Raises ------ @@ -982,13 +995,13 @@ def calculate_confinement_time( self.data.physics.e_plasma_beta / 1e6 ) / p_plasma_loss_mw - return ( - pden_electron_transport_loss_mw, - pden_ion_transport_loss_mw, - t_electron_energy_confinement, - t_ion_energy_confinement, - t_energy_confinement, - p_plasma_loss_mw, + return ConfinementTimeData( + pden_electron_transport_loss_mw=pden_electron_transport_loss_mw, + pden_ion_transport_loss_mw=pden_ion_transport_loss_mw, + t_electron_energy_confinement=t_electron_energy_confinement, + t_ion_energy_confinement=t_ion_energy_confinement, + t_plasma_energy_confinement=t_energy_confinement, + p_plasma_loss_mw=p_plasma_loss_mw, ) @staticmethod @@ -1054,14 +1067,7 @@ def fhz(hfact: float) -> float: balance. """ - ( - ptrez, - ptriz, - _, - _, - _, - _, - ) = self.calculate_confinement_time( + confinement_time_data = self.calculate_confinement_time( m_fuel_amu=self.data.physics.m_fuel_amu, p_alpha_total_mw=self.data.physics.p_alpha_total_mw, aspect=self.data.physics.aspect, @@ -1091,8 +1097,8 @@ def fhz(hfact: float) -> float: # At power balance, fhz is zero. fhz_value = ( - ptrez - + ptriz + confinement_time_data.pden_electron_transport_loss_mw + + confinement_time_data.pden_ion_transport_loss_mw - self.data.physics.f_p_alpha_plasma_deposited * self.data.physics.pden_alpha_total_mw - self.data.physics.pden_non_alpha_charged_mw @@ -1347,14 +1353,8 @@ def output_confinement_comparison(self, istell: int): ): if i_confinement_time == 25: continue - ( - _, - _, - taueez, - _, - _, - _, - ) = self.calculate_confinement_time( + + confinement_time_data = self.calculate_confinement_time( m_fuel_amu=self.data.physics.m_fuel_amu, p_alpha_total_mw=self.data.physics.p_alpha_total_mw, aspect=self.data.physics.aspect, @@ -1397,7 +1397,7 @@ def output_confinement_comparison(self, istell: int): po.ocmmnt( self.outfile, f"{'':>2}{scaling_name:<38}" - f"{taueez:<28.3f}{self.data.physics.hfac[i_confinement_time - 1]:.3f}", + f"{confinement_time_data.t_plasma_energy_confinement:<28.3f}{self.data.physics.hfac[i_confinement_time - 1]:.3f}", ) po.oblnkl(self.outfile) diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 17e9a0bec7..3987ad7a08 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -843,14 +843,7 @@ def run(self): # Calculate transport losses and energy confinement time using the # chosen scaling law - ( - self.data.physics.pden_electron_transport_loss_mw, - self.data.physics.pden_ion_transport_loss_mw, - self.data.physics.t_electron_energy_confinement, - self.data.physics.t_energy_confinement, - self.data.physics.t_ion_energy_confinement, - self.data.physics.p_plasma_loss_mw, - ) = self.confinement.calculate_confinement_time( + confinement_time_data = self.confinement.calculate_confinement_time( m_fuel_amu=self.data.physics.m_fuel_amu, p_alpha_total_mw=self.data.physics.p_alpha_total_mw, aspect=self.data.physics.aspect, @@ -877,6 +870,22 @@ def run(self): eden_plasma_electrons_thermal_vol_avg=self.data.physics.eden_plasma_electrons_thermal_vol_avg, eden_plasma_ions_thermal_vol_avg=self.data.physics.eden_plasma_ions_thermal_vol_avg, ) + self.data.physics.pden_electron_transport_loss_mw = ( + confinement_time_data.pden_electron_transport_loss_mw + ) + self.data.physics.pden_ion_transport_loss_mw = ( + confinement_time_data.pden_ion_transport_loss_mw + ) + self.data.physics.t_electron_energy_confinement = ( + confinement_time_data.t_electron_energy_confinement + ) + self.data.physics.t_energy_confinement = ( + confinement_time_data.t_plasma_energy_confinement + ) + self.data.physics.t_ion_energy_confinement = ( + confinement_time_data.t_ion_energy_confinement + ) + self.data.physics.p_plasma_loss_mw = confinement_time_data.p_plasma_loss_mw # Total transport power from scaling law (MW) self.data.physics.p_electron_transport_loss_mw = ( diff --git a/process/models/stellarator/stellarator.py b/process/models/stellarator/stellarator.py index 0a0b17b309..a2f6129e6e 100644 --- a/process/models/stellarator/stellarator.py +++ b/process/models/stellarator/stellarator.py @@ -2251,14 +2251,7 @@ def st_phys(self, output): + self.data.physics.e_plasma_ions_thermal ) - ( - self.data.physics.pden_electron_transport_loss_mw, - self.data.physics.pden_ion_transport_loss_mw, - self.data.physics.t_electron_energy_confinement, - self.data.physics.t_ion_energy_confinement, - self.data.physics.t_energy_confinement, - self.data.physics.p_plasma_loss_mw, - ) = self.physics.confinement.calculate_confinement_time( + confinement_time_data = self.physics.confinement.calculate_confinement_time( self.data.physics.m_fuel_amu, self.data.physics.p_alpha_total_mw, self.data.physics.aspect, @@ -2286,6 +2279,23 @@ def st_phys(self, output): eden_plasma_ions_thermal_vol_avg=self.data.physics.eden_plasma_ions_thermal_vol_avg, ) + self.data.physics.pden_electron_transport_loss_mw = ( + confinement_time_data.pden_electron_transport_loss_mw + ) + self.data.physics.pden_ion_transport_loss_mw = ( + confinement_time_data.pden_ion_transport_loss_mw + ) + self.data.physics.t_electron_energy_confinement = ( + confinement_time_data.t_electron_energy_confinement + ) + self.data.physics.t_energy_confinement = ( + confinement_time_data.t_plasma_energy_confinement + ) + self.data.physics.t_ion_energy_confinement = ( + confinement_time_data.t_ion_energy_confinement + ) + self.data.physics.p_plasma_loss_mw = confinement_time_data.p_plasma_loss_mw + self.data.physics.ntau, self.data.physics.nTtau = ( self.physics.confinement.calculate_double_and_triple_product( nd_plasma_electrons_vol_avg=self.data.physics.nd_plasma_electrons_vol_avg, diff --git a/tests/unit/models/physics/test_physics.py b/tests/unit/models/physics/test_physics.py index 9d9351e200..40635ef441 100644 --- a/tests/unit/models/physics/test_physics.py +++ b/tests/unit/models/physics/test_physics.py @@ -3119,14 +3119,7 @@ def test_calculate_confinement_time(confinementtimeparam, monkeypatch, physics): physics.data.physics, field, getattr(confinementtimeparam, field) ) - ( - pden_electron_transport_loss_mw, - pden_ion_transport_loss_mw, - t_electron_energy_confinement, - t_ion_energy_confinement, - t_energy_confinement, - p_plasma_loss_mw, - ) = physics.confinement.calculate_confinement_time( + confinement_time_data = physics.confinement.calculate_confinement_time( i_confinement_time=confinementtimeparam.i_confinement_time, i_plasma_ignited=confinementtimeparam.i_plasma_ignited, m_fuel_amu=confinementtimeparam.m_fuel_amu, @@ -3158,27 +3151,27 @@ def test_calculate_confinement_time(confinementtimeparam, monkeypatch, physics): confinementtimeparam.expected_kappa_ipb ) - assert p_plasma_loss_mw == pytest.approx( + assert confinement_time_data.p_plasma_loss_mw == pytest.approx( confinementtimeparam.expected_p_plasma_loss_mw ) - assert pden_electron_transport_loss_mw == pytest.approx( + assert confinement_time_data.pden_electron_transport_loss_mw == pytest.approx( confinementtimeparam.expected_pden_electron_transport_loss_mw ) - assert pden_ion_transport_loss_mw == pytest.approx( + assert confinement_time_data.pden_ion_transport_loss_mw == pytest.approx( confinementtimeparam.expected_pden_ion_transport_loss_mw ) - assert t_electron_energy_confinement == pytest.approx( + assert confinement_time_data.t_electron_energy_confinement == pytest.approx( confinementtimeparam.expected_tauee ) - assert t_energy_confinement == pytest.approx( + assert confinement_time_data.t_plasma_energy_confinement == pytest.approx( confinementtimeparam.expected_t_energy_confinement ) - assert t_ion_energy_confinement == pytest.approx( + assert confinement_time_data.t_ion_energy_confinement == pytest.approx( confinementtimeparam.expected_t_ion_energy_confinement )