diff --git a/documentation/source/physics-models/plasma_scrape_off_layer.md b/documentation/source/physics-models/plasma_scrape_off_layer.md index a85fd981f6..502ead1183 100644 --- a/documentation/source/physics-models/plasma_scrape_off_layer.md +++ b/documentation/source/physics-models/plasma_scrape_off_layer.md @@ -83,6 +83,35 @@ The $R^2$ value for this fit is 0.55 -------- +### Eich 2011 JET Model | `calculate_eich2011_jet_sol_power_decay_length()` + +The power decay length in metres is given by[^eich_2011]: + +$$ +\lambda_q = 0.7(\pm0.26) \times 10^{-3} B_{\text{T,0}}^{-0.84(\pm0.26)} q_{\text{cyl}}^{1.23(\pm0.26)} P_{\text{sep}}^{0.14(\pm0.14)} +$$ + +Here $B_{\text{T,0}}$ is the on axis toroidal magnetic field, $q_{\text{cyl}}$ is the cylindrical safety factor and $P_{\text{sep}}$ is the plasma separatrix power in $\text{MW}$. + +This can be found in Table 2 from Eich et.al [^eich_2011] + + +---------- + +### Eich 2011 JET+ASDEX Model | `calculate_eich2011_jet_asdex_sol_power_decay_length()` + +The power decay length in metres is given by[^eich_2011]: + +$$ +\lambda_q = 0.73(\pm0.38) \times 10^{-3} B_{\text{T,0}}^{-0.78(\pm0.25)} q_{\text{cyl}}^{1.2(\pm0.27)} P_{\text{sep}}^{0.1(\pm0.11)}R_0^{0.02(\pm0.2)} +$$ + +Here $B_{\text{T,0}}$ is the on axis toroidal magnetic field, $q_{\text{cyl}}$ is the cylindrical safety factor, $P_{\text{sep}}$ is the plasma separatrix power in $\text{MW}$ and $R_0$ is the plasma major radius. + +This can be found in Table 2 from Eich et.al [^eich_2011] + +------------------ + [^eich_2013]: T. Eich et al., “Scaling of the tokamak near the scrape-off layer H-mode power width and implications for ITER,” Nuclear Fusion, vol. 53, no. 9 p. 093031, Aug. 2013, doi: 10.1088/0029-5515/53/9/093031. [^mast_2014]: A. J. Thornton and A. Kirk, “Scaling of the scrape-off layer width during inter-ELM H modes on MAST as measured by infrared thermography,” @@ -90,4 +119,6 @@ Plasma Physics and Controlled Fusion, vol. 56, no. 5, p. 055008, Apr. 2014, doi: [^stangeby_boundary]: P. C. Stangeby, “The Plasma Boundary of Magnetic Fusion Devices,” Jan. 2000, doi: 10.1201/9780367801489. -[^henderson_step]: S. S. Henderson et al., “An overview of the STEP divertor design and the simple models driving the plasma exhaust scenario,” Nuclear Fusion, vol. 65, no. 1, pp. 016033–016033, Nov. 2024, doi: 10.1088/1741-4326/ad93e7. \ No newline at end of file +[^henderson_step]: S. S. Henderson et al., “An overview of the STEP divertor design and the simple models driving the plasma exhaust scenario,” Nuclear Fusion, vol. 65, no. 1, pp. 016033–016033, Nov. 2024, doi: 10.1088/1741-4326/ad93e7. + +[^eich_2011]: T. Eich, B. Sieglin, A. Scarabosio, W. Fundamenski, Robert James Goldston, and A. Herrmann, “Inter-ELM Power Decay Length for JET and ASDEX Upgrade: Measurement and Comparison with Heuristic Drift-Based Model,” Physical Review Letters, vol. 107, no. 21, Nov. 2011, doi: https://doi.org/10.1103/PhysRevLett.107.215001. \ No newline at end of file diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 5c53ac2933..f51a34fe97 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -23,7 +23,10 @@ from process.data_structure.impurity_radiation_variables import N_IMPURITIES from process.data_structure.numerics import FiguresOfMerit, PROCESSRunMode from process.data_structure.pfcoil_variables import NFIXMX -from process.data_structure.physics_variables import ConfinementTimeModel +from process.data_structure.physics_variables import ( + ConfinementTimeModel, + OutbordSOLPowerDecayLengthModel, +) from process.data_structure.superconducting_tf_coil_variables import TFWPIntegerTurnType from process.models.build import Build from process.models.engineering.materials import ( @@ -9029,12 +9032,19 @@ def plot_sol_power_decay_length_comparison(axis: plt.Axes, mfile: MFile, scan: i len_plasma_sol_mast14_power_decay_2_mm = ( mfile.get("len_plasma_sol_mast14_power_decay_2", scan=scan) * 1e3 ) - + len_plasma_sol_eich11_jet_power_decay_mm = ( + mfile.get("len_plasma_sol_eich11_jet_power_decay", scan=scan) * 1e3 + ) + len_plasma_sol_eich11_jet_asdex_power_decay_mm = ( + mfile.get("len_plasma_sol_eich11_jet_asdex_power_decay", scan=scan) * 1e3 + ) # Data for the box plot data = { - "Eich 2013": len_plasma_sol_eich13_power_decay_mm, - "MAST 2014 (1)": len_plasma_sol_mast14_power_decay_1_mm, - "MAST 2014 (2)": len_plasma_sol_mast14_power_decay_2_mm, + f"{OutbordSOLPowerDecayLengthModel.EICH_2013.description}": len_plasma_sol_eich13_power_decay_mm, + f"{OutbordSOLPowerDecayLengthModel.MAST_2014_1.description}": len_plasma_sol_mast14_power_decay_1_mm, + f"{OutbordSOLPowerDecayLengthModel.MAST_2014_2.description}": len_plasma_sol_mast14_power_decay_2_mm, + f"{OutbordSOLPowerDecayLengthModel.EICH_2011_JET.description}": len_plasma_sol_eich11_jet_power_decay_mm, + f"{OutbordSOLPowerDecayLengthModel.EICH_2011_JET_ASDEX.description}": len_plasma_sol_eich11_jet_asdex_power_decay_mm, } # Create the violin plot axis.violinplot(data.values(), showextrema=False) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 046015ac4a..6a4261a50f 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -14,6 +14,8 @@ class OutbordSOLPowerDecayLengthModel(IntEnum): EICH_2013 = (1, "Eich 2013") MAST_2014_1 = (2, "MAST 2014-1") MAST_2014_2 = (3, "MAST 2014-2") + EICH_2011_JET = (4, "Eich 2011 JET") + EICH_2011_JET_ASDEX = (5, "Eich 2011 JET + ASDEX Upgrade") def __new__(cls, value: int, description: str): """Create a new instance of OutbordSOLPowerDecayLengthModel. @@ -1708,6 +1710,12 @@ class PhysicsData: len_plasma_sol_mast14_power_decay_2: float = 0.0 """MAST 2014 power decay length in the scrape-off layer scaling 2 (λ_q) [m]""" + len_plasma_sol_eich11_jet_power_decay: float = 0.0 + """Eich 2011 JET power decay length in the scrape-off layer scaling (λ_q) [m]""" + + len_plasma_sol_eich11_jet_asdex_power_decay: float = 0.0 + """Eich 2011 JET + ASDEX power decay length in the scrape-off layer scaling (λ_q) [m]""" + a_plasma_outboard_sol_eich13_parallel: float = 0.0 """Plasma outboard midplane (upstream) Eich 2013 SOL parallel area (Aₗₗ,ᵤ) [m]""" diff --git a/process/models/physics/scrape_off_layer.py b/process/models/physics/scrape_off_layer.py index 37fc3d06a6..71a283e24f 100644 --- a/process/models/physics/scrape_off_layer.py +++ b/process/models/physics/scrape_off_layer.py @@ -41,6 +41,23 @@ def run(self): ) ) + self.data.physics.len_plasma_sol_eich11_jet_power_decay = ( + self.calculate_eich2011_jet_sol_power_decay_length( + b_plasma_toroidal_on_axis=self.data.physics.b_plasma_toroidal_on_axis, + q_cyl=self.data.physics.qstar, + p_plasma_separatrix_mw=self.data.physics.p_plasma_separatrix_mw, + ) + ) + + self.data.physics.len_plasma_sol_eich11_jet_asdex_power_decay = ( + self.calculate_eich2011_jet_asdex_sol_power_decay_length( + b_plasma_toroidal_on_axis=self.data.physics.b_plasma_toroidal_on_axis, + q_cyl=self.data.physics.qstar, + p_plasma_separatrix_mw=self.data.physics.p_plasma_separatrix_mw, + rmajor=self.data.physics.rmajor, + ) + ) + # Set to user input if OutbordSOLPowerDecayLengthModel = 1/USER_INUT if ( @@ -70,6 +87,24 @@ def run(self): self.data.physics.len_sol_outboard_power_decay = ( self.data.physics.len_plasma_sol_mast14_power_decay_2 ) + elif ( + OutbordSOLPowerDecayLengthModel( + self.data.physics.i_len_sol_outboard_power_decay + ) + == OutbordSOLPowerDecayLengthModel.EICH_2011_JET + ): + self.data.physics.len_sol_outboard_power_decay = ( + self.data.physics.len_plasma_sol_eich11_jet_power_decay + ) + elif ( + OutbordSOLPowerDecayLengthModel( + self.data.physics.i_len_sol_outboard_power_decay + ) + == OutbordSOLPowerDecayLengthModel.EICH_2011_JET_ASDEX + ): + self.data.physics.len_sol_outboard_power_decay = ( + self.data.physics.len_plasma_sol_eich11_jet_asdex_power_decay + ) self.data.physics.a_plasma_outboard_sol_parallel = self.calculate_upstream_sol_outboard_parallel_area( # noqa: E501 rmajor=self.data.physics.rmajor, @@ -136,6 +171,18 @@ def output(self) -> None: "(len_plasma_sol_mast14_power_decay_2)", self.data.physics.len_plasma_sol_mast14_power_decay_2, ) + po.ovarre( + self.outfile, + "Eich 2011 JET SOL power decay length (λ_q) [m]", + "(len_plasma_sol_eich11_jet_power_decay)", + self.data.physics.len_plasma_sol_eich11_jet_power_decay, + ) + po.ovarre( + self.outfile, + "Eich 2011 JET + ASDEX Upgrade SOL power decay length (λ_q) [m]", + "(len_plasma_sol_eich11_jet_asdex_power_decay)", + self.data.physics.len_plasma_sol_eich11_jet_asdex_power_decay, + ) po.oblnkl(self.outfile) po.ocmmnt(self.outfile, "----------------------------") @@ -283,6 +330,94 @@ def calculate_mast2014_sol_power_decay_length_2( """ return 4.57e-3 * p_plasma_separatrix_mw**0.22 * cur_plasma_ma**-0.64 + @staticmethod + def calculate_eich2011_jet_sol_power_decay_length( + b_plasma_toroidal_on_axis: float, + q_cyl: float, + p_plasma_separatrix_mw: float, + ) -> float: + """Calculate the Eich 2011 JET SOL power decay length (λ_q). + + Parameters + ---------- + b_plasma_toroidal_on_axis : float + Toroidal magnetic field at the plasma axis (Bᴛ(R₀)) [T] + q_cyl : float + Cylindrical safety factor (q_cyl) [-] + p_plasma_separatrix_mw : float + Power crossing the separatrix (Pₛₑₚ) [MW] + + Returns + ------- + float + Eich 2011 JET SOL power decay length (λ_q) [m] + + Notes + ----- + - The fit values can be found in Table 2 of [1]. + + References + ---------- + [1] T. Eich, B. Sieglin, A. Scarabosio, W. Fundamenski, Robert James Goldston, + and A. Herrmann, “Inter-ELM Power Decay Length for JET and ASDEX Upgrade: + Measurement and Comparison with Heuristic Drift-Based Model,” + Physical Review Letters, vol. 107, no. 21, Nov. 2011, + doi: https://doi.org/10.1103/PhysRevLett.107.215001. + + """ + return ( + 0.7e-3 + * b_plasma_toroidal_on_axis**-0.84 + * q_cyl**1.23 + * p_plasma_separatrix_mw**0.14 + ) + + @staticmethod + def calculate_eich2011_jet_asdex_sol_power_decay_length( + b_plasma_toroidal_on_axis: float, + q_cyl: float, + p_plasma_separatrix_mw: float, + rmajor: float, + ) -> float: + """Calculate the Eich 2011 JET + ASDEX Upgrade SOL power decay length (λ_q). + + Parameters + ---------- + b_plasma_toroidal_on_axis : float + Toroidal magnetic field at the plasma axis (Bᴛ(R₀)) [T] + q_cyl : float + Cylindrical safety factor (q_cyl) [-] + p_plasma_separatrix_mw : float + Power crossing the separatrix (Pₛₑₚ) [MW] + rmajor : float + Major radius of the plasma (R₀) [m] + + Returns + ------- + float + Eich 2011 JET + ASDEX Upgrade SOL power decay length (λ_q) [m] + + Notes + ----- + - The fit values can be found in Table 2 of [1]. + + References + ---------- + [1] T. Eich, B. Sieglin, A. Scarabosio, W. Fundamenski, Robert James Goldston, + and A. Herrmann, “Inter-ELM Power Decay Length for JET and ASDEX Upgrade: + Measurement and Comparison with Heuristic Drift-Based Model,” + Physical Review Letters, vol. 107, no. 21, Nov. 2011, + doi: https://doi.org/10.1103/PhysRevLett.107.215001. + + """ + return ( + 0.73e-3 + * b_plasma_toroidal_on_axis**-0.78 + * q_cyl**1.2 + * p_plasma_separatrix_mw**0.1 + * rmajor**0.02 + ) + @staticmethod def calculate_upstream_sol_outboard_parallel_area( rmajor: float, diff --git a/tests/unit/models/physics/test_scrape_off_layer.py b/tests/unit/models/physics/test_scrape_off_layer.py index bbcd78a553..e9015d55c8 100644 --- a/tests/unit/models/physics/test_scrape_off_layer.py +++ b/tests/unit/models/physics/test_scrape_off_layer.py @@ -39,6 +39,83 @@ def test_calculate_eich2013_sol_power_decay_length_exact(): assert pytest.approx(result) == 0.0015345296622855315 +@pytest.mark.parametrize( + ("b_plasma_toroidal_on_axis", "q_cyl", "p_plasma_separatrix_mw"), + [ + (3.0, 2.0, 100.0), + (2.0, 1.0, 10.0), + (5.0, 4.0, 500.0), + (1.0, 3.0, 100.0), + ], +) +def test_calculate_eich2011_jet_sol_power_decay_length( + b_plasma_toroidal_on_axis, q_cyl, p_plasma_separatrix_mw +): + """Test Eich 2011 JET SOL power decay length with various parameters.""" + result = ScrapeOffLayer.calculate_eich2011_jet_sol_power_decay_length( + b_plasma_toroidal_on_axis=b_plasma_toroidal_on_axis, + q_cyl=q_cyl, + p_plasma_separatrix_mw=p_plasma_separatrix_mw, + ) + assert isinstance(result, float) + assert result > 0 + + +def test_calculate_eich2011_jet_sol_power_decay_length_exact(): + """Test Eich 2011 JET SOL power decay length with exact value check.""" + result = ScrapeOffLayer.calculate_eich2011_jet_sol_power_decay_length( + b_plasma_toroidal_on_axis=1.0, + q_cyl=1.0, + p_plasma_separatrix_mw=100.0, + ) + assert isinstance(result, float) + assert pytest.approx(result) == 0.001333822502574273 + + +@pytest.mark.parametrize( + ( + "b_plasma_toroidal_on_axis", + "q_cyl", + "p_plasma_separatrix_mw", + "rmajor", + ), + [ + (3.0, 2.0, 100.0, 3.0), + (2.0, 1.0, 10.0, 3.0), + (5.0, 4.0, 500.0, 3.0), + (3.0, 2.0, 100.0, 10.0), + ], +) +def test_calculate_eich2011_jet_asdex_sol_power_decay_length( + b_plasma_toroidal_on_axis, q_cyl, p_plasma_separatrix_mw, rmajor +): + """Test Eich 2011 JET + ASDEX Upgrade SOL power decay length with + various parameters. + """ + result = ScrapeOffLayer.calculate_eich2011_jet_asdex_sol_power_decay_length( + b_plasma_toroidal_on_axis=b_plasma_toroidal_on_axis, + q_cyl=q_cyl, + p_plasma_separatrix_mw=p_plasma_separatrix_mw, + rmajor=rmajor, + ) + assert isinstance(result, float) + assert result > 0 + + +def test_calculate_eich2011_jet_asdex_sol_power_decay_length_exact(): + """Test Eich 2011 JET + ASDEX Upgrade SOL power decay length with exact value + check. + """ + result = ScrapeOffLayer.calculate_eich2011_jet_asdex_sol_power_decay_length( + b_plasma_toroidal_on_axis=1.0, + q_cyl=1.0, + p_plasma_separatrix_mw=100.0, + rmajor=1.0, + ) + assert isinstance(result, float) + assert pytest.approx(result) == 0.0011569720304966129 + + @pytest.mark.parametrize( ("p_plasma_separatrix_mw", "b_plasma_surface_poloidal_average"), [