From c4c3e5350af1c69999c91d0f38e1681ccdee3e47 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 13 Apr 2026 13:27:30 +0100 Subject: [PATCH 01/17] Add option for density pedestal setting in physics variables --- process/data_structure/physics_variables.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 6e12a4941..0aecb7d92 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -611,6 +611,12 @@ class DivertorNumberModels(IntEnum): nd_plasma_separatrix_electron: float = None """electron density at separatrix [m-3] (`i_plasma_pedestal==1)""" +i_nd_plasma_pedestal_separatrix: int = None +"""switch for pedestal and separatrix density calculation: +- =0 User input pedestal and separatrix density +- =1 Calculate pedestal and separatrix density as fraction of Greenwald limit (see `f_nd_plasma_pedestal_greenwald` and `f_nd_plasma_separatrix_greenwald`) +""" + alpha_crit: float = None """critical ballooning parameter value""" @@ -1630,6 +1636,7 @@ def init_physics_variables(): ffwal, \ f_nd_plasma_pedestal_greenwald, \ f_nd_plasma_separatrix_greenwald, \ + i_nd_plasma_pedestal_separatrix, \ f_plasma_fuel_helium3, \ figmer, \ fkzohm, \ @@ -1961,6 +1968,7 @@ def init_physics_variables(): ffwal = 0.92 f_nd_plasma_pedestal_greenwald = 0.85 f_nd_plasma_separatrix_greenwald = 0.50 + i_nd_plasma_pedestal_separatrix = 0 f_plasma_fuel_helium3 = 0.0 figmer = 0.0 fkzohm = 1.0 From 2836a48cda13e7d757a51c8071e85906f5973814 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 13 Apr 2026 13:42:23 +0100 Subject: [PATCH 02/17] Update density profile text box to include average density from mfile --- process/core/io/plot/summary.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index ac1e0951d..f7d76d7bc 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -4048,7 +4048,8 @@ def plot_n_profiles(prof, demo_ranges: bool, mfile: MFile, scan: int): # Add text box with density profile parameters textstr_density = "\n".join(( - rf"$\langle n_{{\text{{e}}}} \rangle$: {nd_plasma_electrons_vol_avg:.3e} m$^{{-3}}$", + rf"$\langle n_{{\text{{e}}}} \rangle$: {nd_plasma_electrons_vol_avg:.3e} m$^{{-3}}$" + rf"$\hspace{{4}} \overline{{n_{{e}}}}$: {mfile.get('nd_plasma_electron_line', scan=scan):.3e} m$^{{-3}}$", ( rf"$n_{{\text{{e,0}}}}$: {ne0:.3e} m$^{{-3}}$" rf"$\hspace{{4}} \alpha_{{\text{{n}}}}$: {alphan:.3f}" From fda3c51a50c7e746155ad16f03b0818b8b10cb2d Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 13 Apr 2026 13:45:30 +0100 Subject: [PATCH 03/17] Add line averaged electron temperature variable to physics variables --- process/data_structure/physics_variables.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 0aecb7d92..fdf517bcd 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1223,6 +1223,8 @@ class DivertorNumberModels(IntEnum): temp_plasma_electron_density_weighted_kev: float = None """density weighted average electron temperature (keV)""" +temp_plasma_electron_line_average_kev: float = None +"""line averaged electron temperature (keV)""" temp_plasma_ion_vol_avg_kev: float = None """volume averaged ion temperature (keV). N.B. calculated from temp_plasma_electron_vol_avg_kev if `f_temp_plasma_ion_electron > 0.0`""" @@ -1803,6 +1805,7 @@ def init_physics_variables(): temp_plasma_electron_vol_avg_kev, \ temp_plasma_electron_on_axis_kev, \ temp_plasma_electron_density_weighted_kev, \ + temp_plasma_electron_line_average_kev, \ temp_plasma_ion_vol_avg_kev, \ temp_plasma_ion_on_axis_kev, \ temp_plasma_ion_density_weighted_kev, \ @@ -2137,6 +2140,7 @@ def init_physics_variables(): temp_plasma_electron_vol_avg_kev = 12.9 temp_plasma_electron_on_axis_kev = 0.0 temp_plasma_electron_density_weighted_kev = 0.0 + temp_plasma_electron_line_average_kev = 0.0 temp_plasma_ion_vol_avg_kev = 12.9 temp_plasma_ion_on_axis_kev = 0.0 temp_plasma_ion_density_weighted_kev = 0.0 From 0cb8f06a7e9e5c0bfa06a05d910805393f44d6b2 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 13 Apr 2026 14:12:36 +0100 Subject: [PATCH 04/17] Add line-averaged electron temperature calculation and update physics variables --- process/core/io/plot/summary.py | 17 +++++++++-------- process/models/physics/physics.py | 6 ++++++ process/models/physics/plasma_profiles.py | 14 +++++++++++++- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index f7d76d7bc..b31a1c517 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -4294,33 +4294,34 @@ def plot_t_profiles(prof, demo_ranges: bool, mfile: MFile, scan: int): # Add text box with temperature profile parameters textstr_temperature = "\n".join(( ( - rf"$\langle T_{{\text{{e}}}} \rangle_\text{{V}}$: {mfile.get('temp_plasma_electron_vol_avg_kev', scan=scan):.3f} keV" - rf"$\hspace{{3}} \langle T_{{\text{{e}}}} \rangle_\text{{n}}$: {mfile.get('temp_plasma_electron_density_weighted_kev', scan=scan):.3f} keV" + rf"$\langle T_{{\text{{e}}}} \rangle_\text{{V}}$: {mfile.get('temp_plasma_electron_vol_avg_kev', scan=scan):.3f} keV" + rf"$\hspace{{2}} \langle T_{{\text{{e}}}} \rangle_\text{{n}}$: {mfile.get('temp_plasma_electron_density_weighted_kev', scan=scan):.3f} keV" + rf"$\hspace{{2}} \overline{{T_{{e}}}}$: {mfile.get('temp_plasma_electron_line_average_kev', scan=scan):.3f} keV" ), ( - rf"$T_{{\text{{e,0}}}}$: {te0:.3f} keV" - rf"$\hspace{{4}} \alpha_{{\text{{T}}}}$: {alphat:.3f}" + rf"$T_{{\text{{e,0}}}}$: {te0:.3f} keV" + rf"$\hspace{{2}} \alpha_{{\text{{T}}}}$: {alphat:.3f}" ), ( rf"$T_{{\text{{e,ped}}}}$: {temp_plasma_pedestal_kev:.3f} keV" - r"$ \hspace{4} \frac{\langle T_i \rangle}{\langle T_e \rangle}$: " + r"$ \hspace{3} \frac{\langle T_i \rangle}{\langle T_e \rangle}$: " f"{f_temp_plasma_ion_electron:.3f}" ), ( rf"$\rho_{{\text{{ped,T}}}}$: {radius_plasma_pedestal_temp_norm:.3f}" - r"$ \hspace{6} \frac{T_{e,0}}{\langle T_e \rangle}$: " + r"$ \hspace{5} \frac{T_{e,0}}{\langle T_e \rangle}$: " f"{te0 / te:.3f}" ), ( rf"$T_{{\text{{e,sep}}}}$: {temp_plasma_separatrix_kev:.3f} keV" - r"$ \hspace{4} \frac{{{\langle T_e \rangle_n}}}{{{\langle T_e \rangle_V}}}$: " + r"$ \hspace{3} \frac{{{\langle T_e \rangle_n}}}{{{\langle T_e \rangle_V}}}$: " f"{mfile.get('f_temp_plasma_electron_density_vol_avg', scan=scan):.3f}" ), )) props_temperature = {"boxstyle": "round", "facecolor": "wheat", "alpha": 0.5} prof.text( - 0.0, + -0.1, -0.125, textstr_temperature, transform=prof.transAxes, diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 34ce980ec..29058e378 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -2438,6 +2438,12 @@ def output_temperature_density_profile_info(self) -> None: physics_variables.temp_plasma_electron_on_axis_kev, "OP ", ) + po.ovarrf( + self.outfile, + "Line averaged electron temperature (keV)", + "(temp_plasma_electron_line_average_kev)", + physics_variables.temp_plasma_electron_line_average_kev, + ) po.ovarrf( self.outfile, "Volume averaged density weighted electron temperature (⟨Tₑ⟩ₙ) (keV)", diff --git a/process/models/physics/plasma_profiles.py b/process/models/physics/plasma_profiles.py index 1956ed38a..7be937935 100644 --- a/process/models/physics/plasma_profiles.py +++ b/process/models/physics/plasma_profiles.py @@ -143,6 +143,14 @@ def parabolic_paramterisation(self): / sp.special.gamma(physics_variables.alphan + 1.5) ) + physics_variables.temp_plasma_electron_line_average_kev = ( + physics_variables.temp_plasma_electron_vol_avg_kev + * (1.0 + physics_variables.alphat) + * (sp.special.gamma(0.5) / 2.0) + * sp.special.gamma(physics_variables.alphat + 1.0) + / sp.special.gamma(physics_variables.alphat + 1.5) + ) + # Density-weighted temperatures physics_variables.temp_plasma_electron_density_weighted_kev = ( @@ -224,11 +232,15 @@ def pedestal_parameterisation(self): / physics_variables.temp_plasma_electron_vol_avg_kev ) - # Line-averaged electron density + # Line-averaged electron density and temperature # = integral(n(rho).drho) physics_variables.nd_plasma_electron_line = self.neprofile.profile_integ + physics_variables.temp_plasma_electron_line_average_kev = ( + self.teprofile.profile_integ + ) + # Scrape-off density / volume averaged density # (Input value is used if i_plasma_pedestal = 0) From c3c0140c083b60cb0d78285f1ca8475c51626303 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 13 Apr 2026 14:38:40 +0100 Subject: [PATCH 05/17] Add density pedestal setting options and refactor related calculations --- process/core/io/plot/summary.py | 8 ++++--- process/models/physics/physics.py | 20 ++--------------- process/models/physics/profiles.py | 35 ++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index b31a1c517..741d1e156 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -4048,8 +4048,10 @@ def plot_n_profiles(prof, demo_ranges: bool, mfile: MFile, scan: int): # Add text box with density profile parameters textstr_density = "\n".join(( - rf"$\langle n_{{\text{{e}}}} \rangle$: {nd_plasma_electrons_vol_avg:.3e} m$^{{-3}}$" - rf"$\hspace{{4}} \overline{{n_{{e}}}}$: {mfile.get('nd_plasma_electron_line', scan=scan):.3e} m$^{{-3}}$", + ( + rf"$\langle n_{{\text{{e}}}} \rangle$: {nd_plasma_electrons_vol_avg:.3e} m$^{{-3}}$" + rf"$\hspace{{4}} \overline{{n_{{e}}}}$: {mfile.get('nd_plasma_electron_line', scan=scan):.3e} m$^{{-3}}$" + ), ( rf"$n_{{\text{{e,0}}}}$: {ne0:.3e} m$^{{-3}}$" rf"$\hspace{{4}} \alpha_{{\text{{n}}}}$: {alphan:.3f}" @@ -4296,7 +4298,7 @@ def plot_t_profiles(prof, demo_ranges: bool, mfile: MFile, scan: int): ( rf"$\langle T_{{\text{{e}}}} \rangle_\text{{V}}$: {mfile.get('temp_plasma_electron_vol_avg_kev', scan=scan):.3f} keV" rf"$\hspace{{2}} \langle T_{{\text{{e}}}} \rangle_\text{{n}}$: {mfile.get('temp_plasma_electron_density_weighted_kev', scan=scan):.3f} keV" - rf"$\hspace{{2}} \overline{{T_{{e}}}}$: {mfile.get('temp_plasma_electron_line_average_kev', scan=scan):.3f} keV" + rf"$\hspace{{2}} \overline{{T_{{e}}}}$: {mfile.get('temp_plasma_electron_line_average_kev', scan=scan):.3f} keV" ), ( rf"$T_{{\text{{e,0}}}}$: {te0:.3f} keV" diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 29058e378..abc4924eb 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -358,24 +358,8 @@ def run(self): if ( PlasmaProfileShapeType(physics_variables.i_plasma_pedestal) == PlasmaProfileShapeType.PEDESTAL_PROFILE - ) and (physics_variables.f_nd_plasma_pedestal_greenwald >= 0e0): - physics_variables.nd_plasma_pedestal_electron = ( - physics_variables.f_nd_plasma_pedestal_greenwald - * 1.0e14 - * physics_variables.plasma_current - / (np.pi * physics_variables.rminor * physics_variables.rminor) - ) - - if ( - PlasmaProfileShapeType(physics_variables.i_plasma_pedestal) - == PlasmaProfileShapeType.PEDESTAL_PROFILE - ) and (physics_variables.f_nd_plasma_separatrix_greenwald >= 0e0): - physics_variables.nd_plasma_separatrix_electron = ( - physics_variables.f_nd_plasma_separatrix_greenwald - * 1.0e14 - * physics_variables.plasma_current - / (np.pi * physics_variables.rminor * physics_variables.rminor) - ) + ): + self.plasma_profile.neprofile.set_pedestal_and_separatrix_values() self.plasma_profile.run() diff --git a/process/models/physics/profiles.py b/process/models/physics/profiles.py index 634451109..5341ebf74 100644 --- a/process/models/physics/profiles.py +++ b/process/models/physics/profiles.py @@ -13,6 +13,7 @@ import scipy as sp from process.data_structure import physics_variables +from process.models.physics.density_limit import PlasmaDensityLimit logger = logging.getLogger(__name__) @@ -105,6 +106,13 @@ def integrate_profile_y(self): ) +class DensityProfilePedestalType(IntEnum): + """Enum for i_nd_plasma_pedestal_separatrix types""" + + USER_INPUT = 0 + GREENWALD_FRACTION = 1 + + class NeProfile(Profile): """Electron density profile class. Contains a function to calculate the electron density profile and store the data. @@ -250,6 +258,33 @@ def ncore( ncore = 1.0e-6 return ncore + def set_pedestal_and_separatrix_values(self): + """Sets the pedestal and separatrix density values based on the user input or greenwald fraction method.""" + + if ( + DensityProfilePedestalType(physics_variables.i_nd_plasma_pedestal_separatrix) + == DensityProfilePedestalType.USER_INPUT + ): + pass + elif ( + DensityProfilePedestalType(physics_variables.i_nd_plasma_pedestal_separatrix) + == DensityProfilePedestalType.GREENWALD_FRACTION + ): + physics_variables.nd_plasma_pedestal_electron = ( + physics_variables.f_nd_plasma_pedestal_greenwald + * 1.0e14 + * PlasmaDensityLimit.calculate_greenwald_density_limit( + c_plasma=physics_variables.c_plasma, rminor=physics_variables.rminor + ) + ) + physics_variables.nd_plasma_separatrix_electron = ( + physics_variables.f_nd_plasma_separatrix_greenwald + * 1.0e14 + * PlasmaDensityLimit.calculate_greenwald_density_limit( + c_plasma=physics_variables.c_plasma, rminor=physics_variables.rminor + ) + ) + def set_physics_variables(self): """Calculates and sets physics variables required for the profile.""" if ( From 8115249a053f3728bcf093bccf36866827f60366 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 13 Apr 2026 14:42:35 +0100 Subject: [PATCH 06/17] Refactor line averaged electron temperature variable naming for consistency --- process/core/io/plot/summary.py | 2 +- process/data_structure/physics_variables.py | 6 +++--- process/models/physics/physics.py | 4 ++-- process/models/physics/plasma_profiles.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 741d1e156..8c84f85e4 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -4298,7 +4298,7 @@ def plot_t_profiles(prof, demo_ranges: bool, mfile: MFile, scan: int): ( rf"$\langle T_{{\text{{e}}}} \rangle_\text{{V}}$: {mfile.get('temp_plasma_electron_vol_avg_kev', scan=scan):.3f} keV" rf"$\hspace{{2}} \langle T_{{\text{{e}}}} \rangle_\text{{n}}$: {mfile.get('temp_plasma_electron_density_weighted_kev', scan=scan):.3f} keV" - rf"$\hspace{{2}} \overline{{T_{{e}}}}$: {mfile.get('temp_plasma_electron_line_average_kev', scan=scan):.3f} keV" + rf"$\hspace{{2}} \overline{{T_{{e}}}}$: {mfile.get('temp_plasma_electron_line_avg_kev', scan=scan):.3f} keV" ), ( rf"$T_{{\text{{e,0}}}}$: {te0:.3f} keV" diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index fdf517bcd..bd85d45c2 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1223,7 +1223,7 @@ class DivertorNumberModels(IntEnum): temp_plasma_electron_density_weighted_kev: float = None """density weighted average electron temperature (keV)""" -temp_plasma_electron_line_average_kev: float = None +temp_plasma_electron_line_avg_kev: float = None """line averaged electron temperature (keV)""" temp_plasma_ion_vol_avg_kev: float = None @@ -1805,7 +1805,7 @@ def init_physics_variables(): temp_plasma_electron_vol_avg_kev, \ temp_plasma_electron_on_axis_kev, \ temp_plasma_electron_density_weighted_kev, \ - temp_plasma_electron_line_average_kev, \ + temp_plasma_electron_line_avg_kev, \ temp_plasma_ion_vol_avg_kev, \ temp_plasma_ion_on_axis_kev, \ temp_plasma_ion_density_weighted_kev, \ @@ -2140,7 +2140,7 @@ def init_physics_variables(): temp_plasma_electron_vol_avg_kev = 12.9 temp_plasma_electron_on_axis_kev = 0.0 temp_plasma_electron_density_weighted_kev = 0.0 - temp_plasma_electron_line_average_kev = 0.0 + temp_plasma_electron_line_avg_kev = 0.0 temp_plasma_ion_vol_avg_kev = 12.9 temp_plasma_ion_on_axis_kev = 0.0 temp_plasma_ion_density_weighted_kev = 0.0 diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index abc4924eb..197836f2a 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -2425,8 +2425,8 @@ def output_temperature_density_profile_info(self) -> None: po.ovarrf( self.outfile, "Line averaged electron temperature (keV)", - "(temp_plasma_electron_line_average_kev)", - physics_variables.temp_plasma_electron_line_average_kev, + "(temp_plasma_electron_line_avg_kev)", + physics_variables.temp_plasma_electron_line_avg_kev, ) po.ovarrf( self.outfile, diff --git a/process/models/physics/plasma_profiles.py b/process/models/physics/plasma_profiles.py index 7be937935..f890fe1e8 100644 --- a/process/models/physics/plasma_profiles.py +++ b/process/models/physics/plasma_profiles.py @@ -143,7 +143,7 @@ def parabolic_paramterisation(self): / sp.special.gamma(physics_variables.alphan + 1.5) ) - physics_variables.temp_plasma_electron_line_average_kev = ( + physics_variables.temp_plasma_electron_line_avg_kev = ( physics_variables.temp_plasma_electron_vol_avg_kev * (1.0 + physics_variables.alphat) * (sp.special.gamma(0.5) / 2.0) @@ -237,7 +237,7 @@ def pedestal_parameterisation(self): physics_variables.nd_plasma_electron_line = self.neprofile.profile_integ - physics_variables.temp_plasma_electron_line_average_kev = ( + physics_variables.temp_plasma_electron_line_avg_kev = ( self.teprofile.profile_integ ) From 7d0a62190f5248de6f94976c51ebfeb433f57c26 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 13 Apr 2026 15:00:12 +0100 Subject: [PATCH 07/17] Add density pedestal electron input variable and update related calculations --- process/core/input.py | 3 +++ process/core/solver/iteration_variables.py | 4 ++-- process/data_structure/physics_variables.py | 2 +- process/models/physics/profiles.py | 8 ++++---- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/process/core/input.py b/process/core/input.py index 565a13087..bee3d4d9d 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -692,6 +692,9 @@ def __post_init__(self): "nd_plasma_separatrix_electron": InputVariable( data_structure.physics_variables, float, range=(0.0, 1e21) ), + "i_nd_plasma_pedestal_electron": InputVariable( + data_structure.physics_variables, int, choices=[0, 1] + ), "nflutfmax": InputVariable("constraints", float, range=(0.0, 1e24)), "oacdcp": InputVariable( data_structure.tfcoil_variables, float, range=(10000.0, 1000000000.0) diff --git a/process/core/solver/iteration_variables.py b/process/core/solver/iteration_variables.py index 17392b439..db9d70ad4 100644 --- a/process/core/solver/iteration_variables.py +++ b/process/core/solver/iteration_variables.py @@ -235,10 +235,10 @@ class IterationVariable: 1.0e20, ), 145: IterationVariable( - "f_nd_plasma_pedestal_greenwald", data_structure.physics_variables, 0.1, 0.9 + "f_nd_plasma_pedestal_greenwald", data_structure.physics_variables, 0.1, 1.5 ), 152: IterationVariable( - "f_nd_plasma_separatrix_greenwald", data_structure.physics_variables, 0.001, 0.5 + "f_nd_plasma_separatrix_greenwald", data_structure.physics_variables, 0.001, 0.9 ), 155: IterationVariable("pfusife", "ife", 5.0e2, 3.0e3), 156: IterationVariable("rrin", "ife", 1.0, 1.0e1), diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index bd85d45c2..d145c88cc 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1971,7 +1971,7 @@ def init_physics_variables(): ffwal = 0.92 f_nd_plasma_pedestal_greenwald = 0.85 f_nd_plasma_separatrix_greenwald = 0.50 - i_nd_plasma_pedestal_separatrix = 0 + i_nd_plasma_pedestal_separatrix = 1 f_plasma_fuel_helium3 = 0.0 figmer = 0.0 fkzohm = 1.0 diff --git a/process/models/physics/profiles.py b/process/models/physics/profiles.py index 5341ebf74..c6fce818c 100644 --- a/process/models/physics/profiles.py +++ b/process/models/physics/profiles.py @@ -272,16 +272,16 @@ def set_pedestal_and_separatrix_values(self): ): physics_variables.nd_plasma_pedestal_electron = ( physics_variables.f_nd_plasma_pedestal_greenwald - * 1.0e14 * PlasmaDensityLimit.calculate_greenwald_density_limit( - c_plasma=physics_variables.c_plasma, rminor=physics_variables.rminor + c_plasma=physics_variables.plasma_current, + rminor=physics_variables.rminor, ) ) physics_variables.nd_plasma_separatrix_electron = ( physics_variables.f_nd_plasma_separatrix_greenwald - * 1.0e14 * PlasmaDensityLimit.calculate_greenwald_density_limit( - c_plasma=physics_variables.c_plasma, rminor=physics_variables.rminor + c_plasma=physics_variables.plasma_current, + rminor=physics_variables.rminor, ) ) From db349f1e7f3bb6ee5441eb6d91e070241c79de1a Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 13 Apr 2026 15:17:51 +0100 Subject: [PATCH 08/17] Add density pedestal and separatrix setting options with updated input validation --- .../profiles/plasma_density_profile.md | 28 +++++++++++++++++++ .../profiles/plasma_profiles.md | 18 ------------ process/core/init.py | 9 +++--- process/core/input.py | 4 +-- process/data_structure/physics_variables.py | 8 ++---- process/models/physics/physics.py | 4 +++ 6 files changed, 41 insertions(+), 30 deletions(-) diff --git a/documentation/source/physics-models/profiles/plasma_density_profile.md b/documentation/source/physics-models/profiles/plasma_density_profile.md index df5525e05..ed6a397b2 100644 --- a/documentation/source/physics-models/profiles/plasma_density_profile.md +++ b/documentation/source/physics-models/profiles/plasma_density_profile.md @@ -148,4 +148,32 @@ $$\begin{aligned} 5. Profile is then integrated with `integrate_profile_y()` using Simpsons integration from the profile abstract base class +----------------------- + +### Setting pedestal and separatrix values | `set_pedestal_and_separatrix_values()` + +The switch `i_nd_plasma_pedestal_separatrix` controls how the values of the density pedestal and separatrix are set. + +#### User input + +If `i_nd_plasma_pedestal_separatrix == 0` then the values of `nd_plasma_pedestal_electron` and `nd_plasma_separatrix_electron` are taken directly from the input file + +#### Fraction of Greenwald Limit + +If `i_nd_plasma_pedestal_separatrix == 1`, the values of $n_{\text{ped}}$ and $n_{\text{sep}}$ are set as fractions of the [Greenwald](https://wiki.fusion.ciemat.es/wiki/Greenwald_limit) limit such as: + +$$ +n_{\text{ped}} = \overbrace{f_{\text{GW,ped}}}^{\texttt{f_nd_plasma_pedestal_greenwald}} \times \frac{I_p [\text{A}]}{\pi a^2 [\text{m}^2]} \times 10^{14} +$$ + +$$ +n_{\text{sep}} = \overbrace{f_{\text{GW,sep}}}^{\texttt{f_nd_plasma_separatrix_greenwald}} \times \frac{I_p [\text{A}]}{\pi a^2 [\text{m}^2]} \times 10^{14} +$$ + + +$\texttt{f_nd_plasma_pedestal_greenwald}$ and $\texttt{f_nd_plasma_separatrix_greenwald}$ can be set as iteration variables respectively by using `ixc = 45` +and `ixc = 152` respectively + +------ + [^1]: Jean, J. (2011). *HELIOS: A Zero-Dimensional Tool for Next Step and Reactor Studies*. Fusion Science and Technology, 59(2), 308–349. diff --git a/documentation/source/physics-models/profiles/plasma_profiles.md b/documentation/source/physics-models/profiles/plasma_profiles.md index c6514b5d5..1befc3407 100644 --- a/documentation/source/physics-models/profiles/plasma_profiles.md +++ b/documentation/source/physics-models/profiles/plasma_profiles.md @@ -636,24 +636,6 @@ The same function is run from the `i_plasma_pedestal == 0 ` profile case, found -------- -### Setting pedestal values as fractions of the Greenwald limit - -By default, the values of $n_{\text{ped}}$ and $n_{\text{sep}}$ are set as fractions of the [Greenwald](https://wiki.fusion.ciemat.es/wiki/Greenwald_limit) limit such as: - -$$ -n_{\text{ped}} = \overbrace{f_{\text{GW,ped}}}^{\texttt{f_nd_plasma_pedestal_greenwald}} \times \frac{I_p [\text{A}]}{\pi a^2 [\text{m}^2]} \times 10^{14} -$$ - -$$ -n_{\text{sep}} = \overbrace{f_{\text{GW,sep}}}^{\texttt{f_nd_plasma_separatrix_greenwald}} \times \frac{I_p [\text{A}]}{\pi a^2 [\text{m}^2]} \times 10^{14} -$$ - -To set the values of $n_{\text{ped}}$ and $n_{\text{sep}}$ directly, the user can input the value of $\texttt{f_nd_plasma_pedestal_greenwald}$ or $\texttt{f_nd_plasma_separatrix_greenwald}$ to be less than 0.0 (i.e negative) to prevent the Greenwald fraction value being set. - -$\texttt{f_nd_plasma_pedestal_greenwald}$ and $\texttt{f_nd_plasma_separatrix_greenwald}$ can be set as iteration variables respectively by using `ixc = 45` -and `ixc = 152` respectively - ------- ### Pedestal Density Upper limit diff --git a/process/core/init.py b/process/core/init.py index 5d12d87f9..018102c76 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -30,6 +30,7 @@ init_superconducting_tf_coil_variables, ) from process.data_structure.tfcoil_variables import init_tfcoil_variables +from process.models.physics.profiles import DensityProfilePedestalType from process.models.stellarator.initialization import st_init from process.models.superconductors import ( SuperconductorMaterial, @@ -445,10 +446,10 @@ def check_process(inputs, data): # noqa: ARG001 # Density checks # Case where pedestal density is set manually if ( - data_structure.physics_variables.f_nd_plasma_pedestal_greenwald < 0 - or not ( - data_structure.numerics.ixc[: data_structure.numerics.nvar] == 145 - ).any() + DensityProfilePedestalType( + data_structure.physics_variables.i_nd_plasma_pedestal_separatrix + ) + == DensityProfilePedestalType.USER_INPUT ): # Issue #589 Pedestal density is set manually using nd_plasma_pedestal_electron but it is less than nd_plasma_separatrix_electron. if ( diff --git a/process/core/input.py b/process/core/input.py index bee3d4d9d..91d595678 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -176,10 +176,10 @@ def __post_init__(self): ), "ffwal": InputVariable(data_structure.physics_variables, float, range=(0.0, 10.0)), "f_nd_plasma_pedestal_greenwald": InputVariable( - data_structure.physics_variables, float, range=(-1.0, 5.0) + data_structure.physics_variables, float, range=(0.1, 1.5) ), "f_nd_plasma_separatrix_greenwald": InputVariable( - data_structure.physics_variables, float, range=(-1.0, 5.0) + data_structure.physics_variables, float, range=(0.01, 0.9) ), "f_plasma_fuel_helium3": InputVariable( data_structure.physics_variables, float, range=(-1.0, 5.0) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index d145c88cc..425ed14fb 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -432,16 +432,12 @@ class DivertorNumberModels(IntEnum): f_nd_plasma_pedestal_greenwald: float = None -"""fraction of Greenwald density to set as pedestal-top density. If `<0`, pedestal-top -density set manually using nd_plasma_pedestal_electron (`i_plasma_pedestal==1`). -(`iteration variable 145`) +"""fraction of Greenwald density to set as pedestal-top density. """ f_nd_plasma_separatrix_greenwald: float = None -"""fraction of Greenwald density to set as separatrix density. If `<0`, separatrix -density set manually using nd_plasma_separatrix_electron (`i_plasma_pedestal==1`). -(`iteration variable 152`) +"""fraction of Greenwald density to set as separatrix density. """ diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 197836f2a..60ff76b14 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -22,6 +22,10 @@ numerics, physics_variables, ) +from process.models.physics.profiles import ( + DensityProfilePedestalType, + PlasmaProfileShapeType, +) from process.models.physics import impurity_radiation from process.models.physics.plasma_geometry import PlasmaGeom from process.models.physics.profiles import PlasmaProfileShapeType From 797487d371549b78c50d506cc775089382138546 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 21 May 2026 13:35:00 +0100 Subject: [PATCH 09/17] Enhance density pedestal and separatrix settings with detailed descriptions and calculations for Greenwald fractions --- process/models/physics/physics.py | 105 +++++++++++++++++------------ process/models/physics/profiles.py | 37 ++++++++-- 2 files changed, 96 insertions(+), 46 deletions(-) diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 60ff76b14..f8697d501 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -22,13 +22,12 @@ numerics, physics_variables, ) +from process.models.physics import impurity_radiation +from process.models.physics.plasma_geometry import PlasmaGeom from process.models.physics.profiles import ( DensityProfilePedestalType, PlasmaProfileShapeType, ) -from process.models.physics import impurity_radiation -from process.models.physics.plasma_geometry import PlasmaGeom -from process.models.physics.profiles import PlasmaProfileShapeType if TYPE_CHECKING: from process.models.physics.bootstrap_current import PlasmaBootstrapCurrent @@ -2480,68 +2479,90 @@ def output_temperature_density_profile_info(self) -> None: ) po.oblnkl(self.outfile) if ( - PlasmaProfileShapeType(physics_variables.i_plasma_pedestal) + physics_variables.i_plasma_pedestal == PlasmaProfileShapeType.PEDESTAL_PROFILE ): + po.ovarin( + self.outfile, + "Pedestal and separatrix density model selected", + "(i_nd_plasma_pedestal_separatrix)", + physics_variables.i_nd_plasma_pedestal_separatrix, + ) + po.ocmmnt( + self.outfile, + f"Pedestal and separatrix values set by: " + f"{DensityProfilePedestalType(physics_variables.i_nd_plasma_pedestal_separatrix).description}", + ) + po.oblnkl(self.outfile) + po.ovarrf( self.outfile, "Density pedestal r/a location (ρₙ,pedestal)", "(radius_plasma_pedestal_density_norm)", physics_variables.radius_plasma_pedestal_density_norm, ) - if physics_variables.f_nd_plasma_pedestal_greenwald >= 0e0: - po.ovarre( + if ( + physics_variables.i_nd_plasma_pedestal_separatrix + == DensityProfilePedestalType.USER_INPUT + ): + po.ovarin( self.outfile, "Electron density pedestal height (nₑ_pedestal) (/m³)", "(nd_plasma_pedestal_electron)", physics_variables.nd_plasma_pedestal_electron, + ) + po.ovarin( + self.outfile, + "Electron separatrix density (nₑ,ₛₑₚ) (/m³)", + "(nd_plasma_separatrix_electron)", + physics_variables.nd_plasma_separatrix_electron, + ) + po.ovarre( + self.outfile, + "Pedestal Greenwald fraction", + "(f_nd_plasma_pedestal_greenwald)", + physics_variables.f_nd_plasma_pedestal_greenwald, "OP ", ) - else: + po.ovarre( + self.outfile, + "Separatrix Greenwald fraction", + "(f_nd_plasma_separatrix_greenwald)", + physics_variables.f_nd_plasma_separatrix_greenwald, + "OP ", + ) + + elif ( + physics_variables.i_nd_plasma_pedestal_separatrix + == DensityProfilePedestalType.GREENWALD_FRACTION + ): po.ovarre( self.outfile, "Electron density pedestal height (nₑ_pedestal) (/m³)", "(nd_plasma_pedestal_electron)", physics_variables.nd_plasma_pedestal_electron, + "OP ", + ) + po.ovarre( + self.outfile, + "Electron separatrix density (nₑ,ₛₑₚ) (/m³)", + "(nd_plasma_separatrix_electron)", + physics_variables.nd_plasma_separatrix_electron, + "OP ", ) - # must be assigned to their exisiting values# - fgwped_out = ( - physics_variables.nd_plasma_pedestal_electron - / physics_variables.nd_plasma_electron_max_array[6] - ) - fgwsep_out = ( - physics_variables.nd_plasma_separatrix_electron - / physics_variables.nd_plasma_electron_max_array[6] - ) - if physics_variables.f_nd_plasma_pedestal_greenwald >= 0e0: - physics_variables.f_nd_plasma_pedestal_greenwald = ( - physics_variables.nd_plasma_pedestal_electron - / physics_variables.nd_plasma_electron_max_array[6] + po.ovarin( + self.outfile, + "Pedestal Greenwald fraction", + "(f_nd_plasma_pedestal_greenwald)", + physics_variables.f_nd_plasma_pedestal_greenwald, ) - if physics_variables.f_nd_plasma_separatrix_greenwald >= 0e0: - physics_variables.f_nd_plasma_separatrix_greenwald = ( - physics_variables.nd_plasma_separatrix_electron - / physics_variables.nd_plasma_electron_max_array[6] + po.ovarin( + self.outfile, + "Separatrix Greenwald fraction", + "(f_nd_plasma_separatrix_greenwald)", + physics_variables.f_nd_plasma_separatrix_greenwald, ) - po.ovarre( - self.outfile, - "Pedestal Greenwald fraction", - "(fgwped_out)", - fgwped_out, - ) - po.ovarre( - self.outfile, - "Electron density at separatrix (nₑ,ₛₑₚ) (/m³)", - "(nd_plasma_separatrix_electron)", - physics_variables.nd_plasma_separatrix_electron, - ) - po.ovarre( - self.outfile, - "Separatrix Greenwald fraction", - "(fgwsep_out)", - fgwsep_out, - ) po.oblnkl(self.outfile) po.ovarre( diff --git a/process/models/physics/profiles.py b/process/models/physics/profiles.py index c6fce818c..c39ebf472 100644 --- a/process/models/physics/profiles.py +++ b/process/models/physics/profiles.py @@ -109,8 +109,20 @@ def integrate_profile_y(self): class DensityProfilePedestalType(IntEnum): """Enum for i_nd_plasma_pedestal_separatrix types""" - USER_INPUT = 0 - GREENWALD_FRACTION = 1 + USER_INPUT = (0, "User input direct values") + GREENWALD_FRACTION = (1, "Fractions of the Greenwald limit") + + def __new__(cls, value: int, description: str): + """Create a new PlasmaProfileShapeType instance.""" + obj = int.__new__(cls, value) + obj._value_ = value + obj._description_ = description + return obj + + @DynamicClassAttribute + def description(self): + """Get the description of the plasma profile shape.""" + return self._description_ class NeProfile(Profile): @@ -260,12 +272,29 @@ def ncore( def set_pedestal_and_separatrix_values(self): """Sets the pedestal and separatrix density values based on the user input or greenwald fraction method.""" - if ( DensityProfilePedestalType(physics_variables.i_nd_plasma_pedestal_separatrix) == DensityProfilePedestalType.USER_INPUT ): - pass + physics_variables.f_nd_plasma_pedestal_greenwald = ( + physics_variables.nd_plasma_pedestal_electron + / ( + PlasmaDensityLimit.calculate_greenwald_density_limit( + c_plasma=physics_variables.plasma_current, + rminor=physics_variables.rminor, + ) + ) + ) + + physics_variables.f_nd_plasma_separatrix_greenwald = ( + physics_variables.nd_plasma_separatrix_electron + / ( + PlasmaDensityLimit.calculate_greenwald_density_limit( + c_plasma=physics_variables.plasma_current, + rminor=physics_variables.rminor, + ) + ) + ) elif ( DensityProfilePedestalType(physics_variables.i_nd_plasma_pedestal_separatrix) == DensityProfilePedestalType.GREENWALD_FRACTION From 6da2aa1bae8a2c4608a9e2674fad0d09a5566cca Mon Sep 17 00:00:00 2001 From: Christopher Ashe <91618944+chris-ashe@users.noreply.github.com> Date: Thu, 21 May 2026 13:48:43 +0100 Subject: [PATCH 10/17] Update process/core/input.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- process/core/input.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process/core/input.py b/process/core/input.py index 91d595678..69fc99e95 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -692,7 +692,7 @@ def __post_init__(self): "nd_plasma_separatrix_electron": InputVariable( data_structure.physics_variables, float, range=(0.0, 1e21) ), - "i_nd_plasma_pedestal_electron": InputVariable( + "i_nd_plasma_pedestal_separatrix": InputVariable( data_structure.physics_variables, int, choices=[0, 1] ), "nflutfmax": InputVariable("constraints", float, range=(0.0, 1e24)), From ae3402db9a49f2b8210174e2b081a00d56f3b15d Mon Sep 17 00:00:00 2001 From: Christopher Ashe <91618944+chris-ashe@users.noreply.github.com> Date: Thu, 21 May 2026 13:49:16 +0100 Subject: [PATCH 11/17] Update documentation/source/physics-models/profiles/plasma_density_profile.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../source/physics-models/profiles/plasma_density_profile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/physics-models/profiles/plasma_density_profile.md b/documentation/source/physics-models/profiles/plasma_density_profile.md index ed6a397b2..1fa6be944 100644 --- a/documentation/source/physics-models/profiles/plasma_density_profile.md +++ b/documentation/source/physics-models/profiles/plasma_density_profile.md @@ -171,7 +171,7 @@ n_{\text{sep}} = \overbrace{f_{\text{GW,sep}}}^{\texttt{f_nd_plasma_separatrix_g $$ -$\texttt{f_nd_plasma_pedestal_greenwald}$ and $\texttt{f_nd_plasma_separatrix_greenwald}$ can be set as iteration variables respectively by using `ixc = 45` +$\texttt{f_nd_plasma_pedestal_greenwald}$ and $\texttt{f_nd_plasma_separatrix_greenwald}$ can be set as iteration variables respectively by using `ixc = 145` and `ixc = 152` respectively ------ From cc8c236e6968bb2ead747ab38104f27b6648abbc Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 21 May 2026 13:56:05 +0100 Subject: [PATCH 12/17] Add expected_temp_plasma_electron_line_avg_kev to PlasmaProfilesParam and update tests --- tests/unit/models/physics/test_plasma_profiles.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unit/models/physics/test_plasma_profiles.py b/tests/unit/models/physics/test_plasma_profiles.py index 562792b27..31dcad54f 100644 --- a/tests/unit/models/physics/test_plasma_profiles.py +++ b/tests/unit/models/physics/test_plasma_profiles.py @@ -216,6 +216,8 @@ class PlasmaProfilesParam(NamedTuple): expected_nd_electron_line: float = 0.0 + expected_temp_plasma_electron_line_avg_kev: float = 0.0 + expected_ti: float = 0.0 @@ -265,6 +267,7 @@ class PlasmaProfilesParam(NamedTuple): expected_ne0=1.0585658890823703e20, expected_ti0=27.370104119511087, expected_nd_electron_line=8.8687354645836431e19, + expected_temp_plasma_electron_line_avg_kev=17.582796426026842, expected_ti=13.07, ), PlasmaProfilesParam( @@ -310,6 +313,7 @@ class PlasmaProfilesParam(NamedTuple): expected_ne0=1.0585658890823703e20, expected_ti0=27.370104119511087, expected_nd_electron_line=8.8687354645836431e19, + expected_temp_plasma_electron_line_avg_kev=17.582796426026842, expected_ti=13.07, ), ], @@ -520,6 +524,10 @@ def test_plasma_profiles(plasmaprofilesparam, monkeypatch, plasmaprofile): plasmaprofilesparam.expected_nd_electron_line ) + assert physics_variables.temp_plasma_electron_line_avg_kev == pytest.approx( + plasmaprofilesparam.expected_temp_plasma_electron_line_avg_kev + ) + assert physics_variables.temp_plasma_ion_vol_avg_kev == pytest.approx( plasmaprofilesparam.expected_ti ) From b8c0ae60dbaaf42ae76822f7f13098d4e9e9fed9 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 21 May 2026 14:11:08 +0100 Subject: [PATCH 13/17] Refactor Greenwald density variables and update documentation for clarity --- .../profiles/plasma_density_profile.md | 3 +-- process/core/io/plot/summary.py | 14 +++++++++----- process/data_structure/physics_variables.py | 6 +++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/documentation/source/physics-models/profiles/plasma_density_profile.md b/documentation/source/physics-models/profiles/plasma_density_profile.md index 1fa6be944..c80182768 100644 --- a/documentation/source/physics-models/profiles/plasma_density_profile.md +++ b/documentation/source/physics-models/profiles/plasma_density_profile.md @@ -171,9 +171,8 @@ n_{\text{sep}} = \overbrace{f_{\text{GW,sep}}}^{\texttt{f_nd_plasma_separatrix_g $$ -$\texttt{f_nd_plasma_pedestal_greenwald}$ and $\texttt{f_nd_plasma_separatrix_greenwald}$ can be set as iteration variables respectively by using `ixc = 145` +`f_nd_plasma_pedestal_greenwald` and `f_nd_plasma_separatrix_greenwald` can be set as iteration variables respectively by using `ixc = 145` and `ixc = 152` respectively ------- [^1]: Jean, J. (2011). *HELIOS: A Zero-Dimensional Tool for Next Step and Reactor Studies*. Fusion Science and Technology, 59(2), 308–349. diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 8c84f85e4..aabcff08b 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -3857,8 +3857,12 @@ def plot_n_profiles(prof, demo_ranges: bool, mfile: MFile, scan: int): nd_ions_total = mfile.get("nd_plasma_ions_total_vol_avg", scan=scan) nd_fuel_ions = mfile.get("nd_plasma_fuel_ions_vol_avg", scan=scan) alphan = mfile.get("alphan", scan=scan) - fgwped_out = mfile.get("fgwped_out", scan=scan) - fgwsep_out = mfile.get("fgwsep_out", scan=scan) + f_nd_plasma_pedestal_greenwald = mfile.get( + "f_nd_plasma_pedestal_greenwald", scan=scan + ) + f_nd_plasma_separatrix_greenwald = mfile.get( + "f_nd_plasma_separatrix_greenwald", scan=scan + ) nd_plasma_electrons_vol_avg = mfile.get("nd_plasma_electrons_vol_avg", scan=scan) # find impurity densities imp_frac = np.array([ @@ -4062,7 +4066,7 @@ def plot_n_profiles(prof, demo_ranges: bool, mfile: MFile, scan: int): f"{nd_fuel_ions / nd_plasma_electrons_vol_avg:.3f}" ), ( - rf"$f_{{\text{{GW e,ped}}}}$: {fgwped_out:.3f}" + rf"$f_{{\text{{GW e,ped}}}}$: {f_nd_plasma_pedestal_greenwald:.3f}" r"$ \hspace{7} \frac{n_{e,0}}{\langle n_e \rangle}$: " f"{ne0 / nd_plasma_electrons_vol_avg:.3f}" ), @@ -4072,12 +4076,12 @@ def plot_n_profiles(prof, demo_ranges: bool, mfile: MFile, scan: int): f"{mfile.get('nd_plasma_electron_line', scan=scan) / mfile.get('nd_plasma_electron_max_array(7)', scan=scan):.3f}" ), rf"$n_{{\text{{e,sep}}}}$: {nd_plasma_separatrix_electron:.3e} m$^{{-3}}$", - rf"$f_{{\text{{GW e,sep}}}}$: {fgwsep_out:.3f}", + rf"$f_{{\text{{GW e,sep}}}}$: {f_nd_plasma_separatrix_greenwald:.3f}", )) props_density = {"boxstyle": "round", "facecolor": "wheat", "alpha": 0.5} ax_main.text( - 0.0, + -0.05, -0.175, textstr_density, transform=ax_impurity.transAxes, diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 425ed14fb..47ccfa756 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -432,12 +432,12 @@ class DivertorNumberModels(IntEnum): f_nd_plasma_pedestal_greenwald: float = None -"""fraction of Greenwald density to set as pedestal-top density. +"""Greenwald fraction of the pedestal density """ f_nd_plasma_separatrix_greenwald: float = None -"""fraction of Greenwald density to set as separatrix density. +"""Greenwald fraction of the separatrix density """ @@ -1220,7 +1220,7 @@ class DivertorNumberModels(IntEnum): """density weighted average electron temperature (keV)""" temp_plasma_electron_line_avg_kev: float = None -"""line averaged electron temperature (keV)""" +"""Line averaged electron temperature (keV)""" temp_plasma_ion_vol_avg_kev: float = None """volume averaged ion temperature (keV). N.B. calculated from temp_plasma_electron_vol_avg_kev if `f_temp_plasma_ion_electron > 0.0`""" From 539051612155147c3e436c2615600adabdd8e5e6 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 21 May 2026 14:19:14 +0100 Subject: [PATCH 14/17] Rename 'dnla_gw' to 'f_nd_plasma_greenwald' and update related calculations and documentation --- process/core/io/mfile/comparison.py | 2 +- process/core/io/plot/summary.py | 2 +- process/core/io/variable_metadata.py | 2 +- process/data_structure/physics_variables.py | 5 +++++ process/models/physics/density_limit.py | 10 +++++++++- process/models/physics/physics.py | 5 ++--- 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/process/core/io/mfile/comparison.py b/process/core/io/mfile/comparison.py index c55e95d80..9ed37ecae 100644 --- a/process/core/io/mfile/comparison.py +++ b/process/core/io/mfile/comparison.py @@ -117,7 +117,7 @@ "temp_plasma_electron_on_axis_kev", "nd_plasma_electrons_vol_avg", "nd_plasma_electron_on_axis", - "dnla_gw", + "f_nd_plasma_greenwald", "temp_plasma_separatrix_kev", "nd_plasma_separatrix_electron", "temp_plasma_pedestal_kev", diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index aabcff08b..ff26e4bd0 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -3163,7 +3163,7 @@ def plot_main_plasma_information( f"$\\mathbf{{Density \\ limit:}}$\n" f"({DensityLimitModel(int(mfile.get('i_density_limit', scan=scan))).full_name})\n" f"$n_{{\\text{{e,limit}}}}: {mfile.get('nd_plasma_electrons_max', scan=scan):.3e} \\ m^{{-3}}$\n" - f"$f_{{\\text{{GW}}}}$: {mfile.get('dnla_gw', scan=scan):.4f}" + f"$f_{{\\text{{GW}}}}$: {mfile.get('f_nd_plasma_greenwald', scan=scan):.4f}" ) axis.text( diff --git a/process/core/io/variable_metadata.py b/process/core/io/variable_metadata.py index b0bf93117..aed863610 100644 --- a/process/core/io/variable_metadata.py +++ b/process/core/io/variable_metadata.py @@ -187,7 +187,7 @@ class VariableMetadata: description="Average electron density", units="m^{-3}", ), - "dnla_gw": VariableMetadata( + "f_nd_plasma_greenwald": VariableMetadata( latex=r"$f_{\mathrm{GW}}$", description="Greenwald fraction", units="" ), "normalised_toroidal_beta": VariableMetadata( diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 47ccfa756..4e3f14638 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -430,6 +430,9 @@ class DivertorNumberModels(IntEnum): load calculation (`i_pflux_fw_neutron=1`) """ +f_nd_plasma_greenwald: float = None +"""Greenwald fraction of the line averaged electron density. The classic Greenwald +limit value""" f_nd_plasma_pedestal_greenwald: float = None """Greenwald fraction of the pedestal density @@ -1632,6 +1635,7 @@ def init_physics_variables(): f_plasma_fuel_deuterium, \ f_p_div_lower, \ ffwal, \ + f_nd_plasma_greenwald, \ f_nd_plasma_pedestal_greenwald, \ f_nd_plasma_separatrix_greenwald, \ i_nd_plasma_pedestal_separatrix, \ @@ -1965,6 +1969,7 @@ def init_physics_variables(): f_plasma_fuel_deuterium = 0.5 f_p_div_lower = 1.0 ffwal = 0.92 + f_nd_plasma_greenwald = 1.0 f_nd_plasma_pedestal_greenwald = 0.85 f_nd_plasma_separatrix_greenwald = 0.50 i_nd_plasma_pedestal_separatrix = 1 diff --git a/process/models/physics/density_limit.py b/process/models/physics/density_limit.py index 8ef3c4677..6b416e291 100644 --- a/process/models/physics/density_limit.py +++ b/process/models/physics/density_limit.py @@ -90,7 +90,9 @@ def run(self): zeff=physics_variables.n_charge_plasma_effective_vol_avg, ) - # Calculate beta_norm_max based on i_beta_norm_max + # Convert the chosen density limit model to the actual density limit value and + # store in physics variables. This is the value that will be used for all + # comparisons to the plasma density in the rest of the code. try: model = DensityLimitModel(int(physics_variables.i_density_limit)) physics_variables.nd_plasma_electrons_max = self.get_density_limit_value( @@ -102,6 +104,12 @@ def run(self): i_density_limit=physics_variables.i_density_limit, ) from None + # Assign the Greenwald fraction for the rest of the code + physics_variables.f_nd_plasma_greenwald = ( + physics_variables.nd_plasma_electron_line + / physics_variables.nd_plasma_electron_max_array[6] + ) + @staticmethod def get_density_limit_value(model: DensityLimitModel) -> float: """ diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index f8697d501..0d6b24fce 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -2589,9 +2589,8 @@ def output_temperature_density_profile_info(self) -> None: po.ovarre( self.outfile, "Greenwald fraction (f_GW)", - "(dnla_gw)", - physics_variables.nd_plasma_electron_line - / physics_variables.nd_plasma_electron_max_array[6], + "(f_nd_plasma_greenwald)", + physics_variables.f_nd_plasma_greenwald, "OP ", ) po.oblnkl(self.outfile) From af0f2cb5a24211c104647cf1667fa6115468c5ca Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 21 May 2026 14:52:33 +0100 Subject: [PATCH 15/17] Copilot requested changes --- process/core/init.py | 31 +++++++++++++++++++++--------- process/models/physics/profiles.py | 11 ++++++----- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/process/core/init.py b/process/core/init.py index 018102c76..d1bf847fa 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -450,17 +450,30 @@ def check_process(inputs, data): # noqa: ARG001 data_structure.physics_variables.i_nd_plasma_pedestal_separatrix ) == DensityProfilePedestalType.USER_INPUT + and data_structure.physics_variables.nd_plasma_pedestal_electron + < data_structure.physics_variables.nd_plasma_separatrix_electron ): # Issue #589 Pedestal density is set manually using nd_plasma_pedestal_electron but it is less than nd_plasma_separatrix_electron. - if ( - data_structure.physics_variables.nd_plasma_pedestal_electron - < data_structure.physics_variables.nd_plasma_separatrix_electron - ): - raise ProcessValidationError( - "Density pedestal is lower than separatrix density", - nd_plasma_pedestal_electron=data_structure.physics_variables.nd_plasma_pedestal_electron, - nd_plasma_separatrix_electron=data_structure.physics_variables.nd_plasma_separatrix_electron, - ) + raise ProcessValidationError( + "Density pedestal is lower than separatrix density", + nd_plasma_pedestal_electron=data_structure.physics_variables.nd_plasma_pedestal_electron, + nd_plasma_separatrix_electron=data_structure.physics_variables.nd_plasma_separatrix_electron, + ) + + if ( + DensityProfilePedestalType( + data_structure.physics_variables.i_nd_plasma_pedestal_separatrix + ) + == DensityProfilePedestalType.GREENWALD_FRACTION + and data_structure.physics_variables.f_nd_plasma_pedestal_greenwald + < data_structure.physics_variables.f_nd_plasma_separatrix_greenwald + ): + # Issue #589 Pedestal density is set manually using nd_plasma_pedestal_electron but it is less than nd_plasma_separatrix_electron. + raise ProcessValidationError( + "Density pedestal is lower than separatrix density", + f_nd_plasma_pedestal_greenwald=data_structure.physics_variables.f_nd_plasma_pedestal_greenwald, + f_nd_plasma_separatrix_greenwald=data_structure.physics_variables.f_nd_plasma_separatrix_greenwald, + ) # Issue #589 Pedestal density is set manually using nd_plasma_pedestal_electron, # but pedestal width = 0. diff --git a/process/models/physics/profiles.py b/process/models/physics/profiles.py index c39ebf472..248f823ca 100644 --- a/process/models/physics/profiles.py +++ b/process/models/physics/profiles.py @@ -272,10 +272,11 @@ def ncore( def set_pedestal_and_separatrix_values(self): """Sets the pedestal and separatrix density values based on the user input or greenwald fraction method.""" - if ( - DensityProfilePedestalType(physics_variables.i_nd_plasma_pedestal_separatrix) - == DensityProfilePedestalType.USER_INPUT - ): + i_nd_plasma_pedestal_separatrix = DensityProfilePedestalType( + physics_variables.i_nd_plasma_pedestal_separatrix + ) + + if i_nd_plasma_pedestal_separatrix == DensityProfilePedestalType.USER_INPUT: physics_variables.f_nd_plasma_pedestal_greenwald = ( physics_variables.nd_plasma_pedestal_electron / ( @@ -296,7 +297,7 @@ def set_pedestal_and_separatrix_values(self): ) ) elif ( - DensityProfilePedestalType(physics_variables.i_nd_plasma_pedestal_separatrix) + i_nd_plasma_pedestal_separatrix == DensityProfilePedestalType.GREENWALD_FRACTION ): physics_variables.nd_plasma_pedestal_electron = ( From a3ff8c07a0c1785a43512a77dc2cfd919c3a9226 Mon Sep 17 00:00:00 2001 From: Christopher Ashe <91618944+chris-ashe@users.noreply.github.com> Date: Thu, 21 May 2026 14:53:12 +0100 Subject: [PATCH 16/17] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- process/core/input.py | 2 +- process/models/physics/profiles.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/process/core/input.py b/process/core/input.py index 69fc99e95..2ffa9b73b 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -179,7 +179,7 @@ def __post_init__(self): data_structure.physics_variables, float, range=(0.1, 1.5) ), "f_nd_plasma_separatrix_greenwald": InputVariable( - data_structure.physics_variables, float, range=(0.01, 0.9) + data_structure.physics_variables, float, range=(0.001, 0.9) ), "f_plasma_fuel_helium3": InputVariable( data_structure.physics_variables, float, range=(-1.0, 5.0) diff --git a/process/models/physics/profiles.py b/process/models/physics/profiles.py index 248f823ca..8a57cf50c 100644 --- a/process/models/physics/profiles.py +++ b/process/models/physics/profiles.py @@ -113,7 +113,12 @@ class DensityProfilePedestalType(IntEnum): GREENWALD_FRACTION = (1, "Fractions of the Greenwald limit") def __new__(cls, value: int, description: str): - """Create a new PlasmaProfileShapeType instance.""" + """Create a new DensityProfilePedestalType instance. + + Parameters: + value: Integer value for the enum member. + description: Human-readable description for the enum member. + """ obj = int.__new__(cls, value) obj._value_ = value obj._description_ = description From 737b19f0bccc9f05c6cc36c00ae27e253dc59bb3 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 21 May 2026 15:22:45 +0100 Subject: [PATCH 17/17] Requested changes --- process/core/init.py | 75 ++++++++++++++---------------- process/models/physics/profiles.py | 3 +- 2 files changed, 37 insertions(+), 41 deletions(-) diff --git a/process/core/init.py b/process/core/init.py index d1bf847fa..a7ed435b9 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -444,59 +444,54 @@ def check_process(inputs, data): # noqa: ARG001 ) # Density checks - # Case where pedestal density is set manually + # Issue #589: Pedestal density is lower than separatrix density + pedestal_type = DensityProfilePedestalType( + data_structure.physics_variables.i_nd_plasma_pedestal_separatrix + ) if ( - DensityProfilePedestalType( - data_structure.physics_variables.i_nd_plasma_pedestal_separatrix - ) - == DensityProfilePedestalType.USER_INPUT + pedestal_type == DensityProfilePedestalType.USER_INPUT and data_structure.physics_variables.nd_plasma_pedestal_electron < data_structure.physics_variables.nd_plasma_separatrix_electron + ) or ( + pedestal_type == DensityProfilePedestalType.GREENWALD_FRACTION + and data_structure.physics_variables.f_nd_plasma_pedestal_greenwald + < data_structure.physics_variables.f_nd_plasma_separatrix_greenwald ): - # Issue #589 Pedestal density is set manually using nd_plasma_pedestal_electron but it is less than nd_plasma_separatrix_electron. raise ProcessValidationError( "Density pedestal is lower than separatrix density", - nd_plasma_pedestal_electron=data_structure.physics_variables.nd_plasma_pedestal_electron, - nd_plasma_separatrix_electron=data_structure.physics_variables.nd_plasma_separatrix_electron, + **( + { + "nd_plasma_pedestal_electron": data_structure.physics_variables.nd_plasma_pedestal_electron, + "nd_plasma_separatrix_electron": data_structure.physics_variables.nd_plasma_separatrix_electron, + } + if pedestal_type == DensityProfilePedestalType.USER_INPUT + else { + "f_nd_plasma_pedestal_greenwald": data_structure.physics_variables.f_nd_plasma_pedestal_greenwald, + "f_nd_plasma_separatrix_greenwald": data_structure.physics_variables.f_nd_plasma_separatrix_greenwald, + } + ), ) if ( - DensityProfilePedestalType( - data_structure.physics_variables.i_nd_plasma_pedestal_separatrix + abs( + data_structure.physics_variables.radius_plasma_pedestal_density_norm + - 1.0 ) - == DensityProfilePedestalType.GREENWALD_FRACTION - and data_structure.physics_variables.f_nd_plasma_pedestal_greenwald - < data_structure.physics_variables.f_nd_plasma_separatrix_greenwald + <= 1e-7 + and ( + data_structure.physics_variables.nd_plasma_pedestal_electron + - data_structure.physics_variables.nd_plasma_separatrix_electron + ) + >= 1e-7 ): - # Issue #589 Pedestal density is set manually using nd_plasma_pedestal_electron but it is less than nd_plasma_separatrix_electron. - raise ProcessValidationError( - "Density pedestal is lower than separatrix density", - f_nd_plasma_pedestal_greenwald=data_structure.physics_variables.f_nd_plasma_pedestal_greenwald, - f_nd_plasma_separatrix_greenwald=data_structure.physics_variables.f_nd_plasma_separatrix_greenwald, + warn( + "Density pedestal is at plasma edge " + f"({data_structure.physics_variables.radius_plasma_pedestal_density_norm = }), but nd_plasma_pedestal_electron " + f"({data_structure.physics_variables.nd_plasma_pedestal_electron}) differs from " + f"nd_plasma_separatrix_electron ({data_structure.physics_variables.nd_plasma_separatrix_electron})", + stacklevel=2, ) - # Issue #589 Pedestal density is set manually using nd_plasma_pedestal_electron, - # but pedestal width = 0. - if ( - abs( - data_structure.physics_variables.radius_plasma_pedestal_density_norm - - 1.0 - ) - <= 1e-7 - and ( - data_structure.physics_variables.nd_plasma_pedestal_electron - - data_structure.physics_variables.nd_plasma_separatrix_electron - ) - >= 1e-7 - ): - warn( - "Density pedestal is at plasma edge " - f"({data_structure.physics_variables.radius_plasma_pedestal_density_norm = }), but nd_plasma_pedestal_electron " - f"({data_structure.physics_variables.nd_plasma_pedestal_electron}) differs from " - f"nd_plasma_separatrix_electron ({data_structure.physics_variables.nd_plasma_separatrix_electron})", - stacklevel=2, - ) - # Issue #862 : Variable nd_plasma_electron_on_axis/nd_plasma_pedestal_electron ratio without constraint eq 81 (nd_plasma_electron_on_axis>nd_plasma_pedestal_electron) # -> Potential hollowed density profile if ( diff --git a/process/models/physics/profiles.py b/process/models/physics/profiles.py index 8a57cf50c..1f93e300f 100644 --- a/process/models/physics/profiles.py +++ b/process/models/physics/profiles.py @@ -115,7 +115,8 @@ class DensityProfilePedestalType(IntEnum): def __new__(cls, value: int, description: str): """Create a new DensityProfilePedestalType instance. - Parameters: + Parameters + ---------- value: Integer value for the enum member. description: Human-readable description for the enum member. """