From 5dbc2214002e13066ab985cc3549f30e05cdf52c Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 27 Jul 2026 15:53:01 +0100 Subject: [PATCH 01/18] Remove in-situ of power decay length and use as input calculated from SOL module --- process/models/divertor.py | 39 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/process/models/divertor.py b/process/models/divertor.py index a9563e4383..b085c246ad 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -88,17 +88,17 @@ def run(self, output: bool = False): == DivertorHeatLoadModel.WADE ): self.divwade( - self.data.physics.rmajor, - self.data.physics.rminor, - self.data.physics.aspect, - self.data.physics.b_plasma_toroidal_on_axis, - self.data.physics.b_plasma_surface_poloidal_average, - self.data.physics.p_plasma_separatrix_mw, - self.data.divertor.f_div_flux_expansion, - self.data.physics.nd_plasma_separatrix_electron, - self.data.divertor.deg_div_field_plate, - self.data.physics.rad_fraction_sol, - self.data.physics.f_p_div_lower, + rmajor=self.data.physics.rmajor, + rminor=self.data.physics.rminor, + b_plasma_toroidal_on_axis=self.data.physics.b_plasma_toroidal_on_axis, + b_plasma_poloidal_average=self.data.physics.b_plasma_surface_poloidal_average, + p_plasma_separatrix_mw=self.data.physics.p_plasma_separatrix_mw, + len_plasma_sol_power_decay=self.data.physics.len_plasma_sol_eich13_power_decay, + f_div_flux_expansion=self.data.divertor.f_div_flux_expansion, + nd_plasma_separatrix_electron=self.data.physics.nd_plasma_separatrix_electron, + deg_div_field_plate=self.data.divertor.deg_div_field_plate, + rad_fraction_sol=self.data.physics.rad_fraction_sol, + f_p_div_lower=self.data.physics.f_p_div_lower, output=output, ) return @@ -273,10 +273,10 @@ def divwade( self, rmajor: float, rminor: float, - aspect: float, b_plasma_toroidal_on_axis: float, b_plasma_poloidal_average: float, p_plasma_separatrix_mw: float, + len_plasma_sol_power_decay: float, f_div_flux_expansion: float, nd_plasma_separatrix_electron: float, deg_div_field_plate: float, @@ -300,14 +300,14 @@ def divwade( plasma major radius (m) rminor : float plasma minor radius (m) - aspect : float - tokamak aspect ratio b_plasma_toroidal_on_axis : float toroidal field (T) b_plasma_poloidal_average : float poloidal field (T) p_plasma_separatrix_mw : float power to divertor (MW) + len_plasma_sol_power_decay : float + SOL power decay length (λ_q) [m] f_div_flux_expansion : float plasma flux expansion in divertor nd_plasma_separatrix_electron : float @@ -332,15 +332,6 @@ def divwade( Bt_omp = -b_plasma_toroidal_on_axis * rmajor / r_omp - # Eich scaling for lambda_q - lambda_eich = ( - 1.35 - * p_plasma_separatrix_mw**-0.02 - * rmajor**0.04 - * b_plasma_poloidal_average**-0.92 - * aspect**0.42 - ) - # Spreading factor spread_fact = ( 0.12 @@ -351,7 +342,7 @@ def divwade( ) # SOL width - lambda_int = lambda_eich + 1.64 * spread_fact + lambda_int = len_plasma_sol_power_decay + 1.64 * spread_fact # Flux angle on midplane alpha_mid = math.degrees(math.atan(Bp_omp / Bt_omp)) From 8d7dda1d7c5e80c6cb16491ec8c5118f750a1e85 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 27 Jul 2026 16:05:13 +0100 Subject: [PATCH 02/18] Add description of scrape-off layer spreading parameter and its impact on divertor heat loads --- .../physics-models/plasma_scrape_off_layer.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/documentation/source/physics-models/plasma_scrape_off_layer.md b/documentation/source/physics-models/plasma_scrape_off_layer.md index a85fd981f6..6a5e87e017 100644 --- a/documentation/source/physics-models/plasma_scrape_off_layer.md +++ b/documentation/source/physics-models/plasma_scrape_off_layer.md @@ -83,6 +83,20 @@ The $R^2$ value for this fit is 0.55 -------- +## Spreading Parameter + +The scrape-off layer (SOL) spreading parameter $S$ represents a Gaussian width that quantifies additional perpendicular heat spreading in the divertor leg. It works alongside the upstream heat flux decay length $\lambda_{q}$ to determine total target heat loads on the divertor. + +Unlike $\lambda_{q}$, which is governed by robust upstream parallel and perpendicular transport physics at the plasma midplane, $S$ is inherently a "local" divertor parameter. Deriving a single, absolute multi-machine formula for $S$ is incredibly difficult due to several overlapping regional variables: + +- Divertor Geometry: The path length from the X-point to the target tile heavily impacts how much the heat spreads radially. + +- Plasma Recycling Regimes: Low-recycling, high-recycling, and detached plasma conditions completely alter the cross-field diffusion rates. + +- Localized Radiation: Impurity seeding and neutral gas interactions dissipate power unevenly along the divertor leg, altering the effective Gaussian profile width. + +------------ + [^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,” From 91aaf7593627afcca10e9c566214b90ff50a88d2 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 27 Jul 2026 16:58:47 +0100 Subject: [PATCH 03/18] Add Scarabosio 2014 power spreading factor calculation and corresponding tests --- .../physics-models/plasma_scrape_off_layer.md | 19 +++++++- process/models/physics/scrape_off_layer.py | 45 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/documentation/source/physics-models/plasma_scrape_off_layer.md b/documentation/source/physics-models/plasma_scrape_off_layer.md index 6a5e87e017..8092c2f72e 100644 --- a/documentation/source/physics-models/plasma_scrape_off_layer.md +++ b/documentation/source/physics-models/plasma_scrape_off_layer.md @@ -81,7 +81,7 @@ This can be found in Table 2 and Equation 4 from Thornton et.al [^mast_2014] The $R^2$ value for this fit is 0.55 --------- +------------- ## Spreading Parameter @@ -95,6 +95,19 @@ Unlike $\lambda_{q}$, which is governed by robust upstream parallel and perpendi - Localized Radiation: Impurity seeding and neutral gas interactions dissipate power unevenly along the divertor leg, altering the effective Gaussian profile width. +----------- + +### Scarabosio 2015 | `calculate_scarabosio2015_power_spreading_factor()` + +The H-mode SOL spreading factor, $S$ is given in $\text{m}$ by[^scarabosio_2015]: + +$$ +S = (0.12(\pm0.07)\times 10^{-3}) P_{\text{sep}}^{0.21(\pm0.11)}R_0^{0.71(\pm0.5)}B_{\text{p}}(a)^{-0.82(\pm0.27)}n_{\text{sep}}^{0.71(\pm0.5)} +$$ + +- This was fitted from ASDEX Upgrade and JET outer target data +- The $R^2$ value of the regression fit was 0.65 + ------------ [^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. @@ -104,4 +117,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. + +[^scarabosio_2015]: A. Scarabosio et al., “Scaling of the divertor power spreading (S-factor) in open and closed divertor operation in JET and ASDEX Upgrade,” Journal of Nuclear Materials, vol. 463, pp. 49-54, Aug. 2015, doi: 10.1016/j.jnucmat.2014.11.076. \ No newline at end of file diff --git a/process/models/physics/scrape_off_layer.py b/process/models/physics/scrape_off_layer.py index 7b2ea9055f..9a891fb9e1 100644 --- a/process/models/physics/scrape_off_layer.py +++ b/process/models/physics/scrape_off_layer.py @@ -250,3 +250,48 @@ def calculate_upstream_sol_outboard_parallel_area( * len_plasma_sol_power_decay * (b_plasma_surface_poloidal_average / b_plasma_outboard_total) ) + + @staticmethod + def calculate_scarabosio2014_power_spreading_factor( + p_plasma_separatrix_mw: float, + b_plasma_surface_poloidal_average: float, + nd_plasma_separatrix_electron_19: float, + rmajor: float, + ) -> float: + """Calculate the Scrabosio 2014 H-mode power spreading factor (S). + + Parameters + ---------- + p_plasma_separatrix_mw : float + Power crossing the separatrix (Pₛₑₚ) [MW] + b_plasma_surface_poloidal_average : float + Poloidal magnetic field at the plasma surface (Bₚₒₗ(a)) [T] + nd_plasma_separatrix_electron_19 : float + Electron density at the separatrix (nₑ,ₛₑₚ) [10¹⁹ m⁻³] + rmajor : float + Major radius of the plasma (R₀) [m] + + Returns + ------- + float + Scrabosio 2014 H-mode power spreading factor (S) [m] + + Notes + ----- + - The R² for the fit is 0.65 + + References + ---------- + [1] A. Scarabosio et al., “Scaling of the divertor power spreading (S-factor) in + open and closed divertor operation in JET and ASDEX Upgrade,” + Journal of Nuclear Materials, vol. 463, pp. 49-54, Aug. 2015, + doi: 10.1016/j.jnucmat.2014.11.076. + + """ + return ( + 0.12e-3 + * p_plasma_separatrix_mw**0.21 + * b_plasma_surface_poloidal_average**-0.82 + * nd_plasma_separatrix_electron_19**-0.02 + * rmajor**0.71 + ) From 69631edb47b54d02dacdebf91029cb60014d8e5c Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 27 Jul 2026 17:00:24 +0100 Subject: [PATCH 04/18] Add Scrabosio 2014 power spreading length to PhysicsData class --- 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 22f742e3ad..5c74b2690d 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1670,6 +1670,10 @@ 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_scrabosio14_power_spreading: float = 0.0 + """Scrabosio 2014 H-mode power spreading length in the scrape-off layer scaling + (S) [m]""" a_plasma_outboard_sol_eich13_parallel: float = 0.0 """Plasma outboard midplane (upstream) Eich 2013 SOL parallel area (Aₗₗ,ᵤ) [m]""" From 3595d3c9f6966d1374472c5e4c08ae6ce60a4c46 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 27 Jul 2026 17:04:04 +0100 Subject: [PATCH 05/18] Add Scrabosio 2014 power spreading factor calculation to ScrapeOffLayer model and update tests --- process/data_structure/physics_variables.py | 4 ++-- process/models/physics/scrape_off_layer.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 5c74b2690d..e3a3c7c33c 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1670,9 +1670,9 @@ 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_scrabosio14_power_spreading: float = 0.0 - """Scrabosio 2014 H-mode power spreading length in the scrape-off layer scaling + """Scrabosio 2014 H-mode power spreading length in the scrape-off layer scaling (S) [m]""" a_plasma_outboard_sol_eich13_parallel: float = 0.0 diff --git a/process/models/physics/scrape_off_layer.py b/process/models/physics/scrape_off_layer.py index 9a891fb9e1..5033ee3e5c 100644 --- a/process/models/physics/scrape_off_layer.py +++ b/process/models/physics/scrape_off_layer.py @@ -53,6 +53,14 @@ def run(self): / self.data.physics.a_plasma_outboard_sol_eich13_parallel ) + self.data.physics.len_plasma_sol_scrabosio14_power_spreading = self.calculate_scarabosio2014_power_spreading_factor( + p_plasma_separatrix_mw=self.data.physics.p_plasma_separatrix_mw, + b_plasma_surface_poloidal_average=self.data.physics.b_plasma_surface_poloidal_average, + nd_plasma_separatrix_electron_19=self.data.physics.nd_plasma_separatrix_electron + / 1e19, + rmajor=self.data.physics.rmajor, + ) + def output(self) -> None: """Output plasma scrape off layer physics information.""" po.oheadr(self.outfile, "Plasma Scrape Off Layer") @@ -94,6 +102,14 @@ def output(self) -> None: self.data.physics.pflux_plasma_outboard_sol_eich13_parallel_mw, ) + po.osubhd(self.outfile, "Power Spreading Factors (S):") + po.ovarre( + self.outfile, + "Scrabosio 2014 H-mode power spreading factor (S) [m]", + "(len_plasma_sol_scrabosio14_power_spreading)", + self.data.physics.len_plasma_sol_scrabosio14_power_spreading, + ) + @staticmethod def calculate_eich2013_sol_power_decay_length( p_plasma_separatrix_mw: float, From 0c68ee0cd96a30359048323b4a23f66e9269479a Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 27 Jul 2026 17:07:18 +0100 Subject: [PATCH 06/18] Add Scrabosio 2014 power spreading factor to Divertor model and update calculations --- process/models/divertor.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/process/models/divertor.py b/process/models/divertor.py index b085c246ad..e6952af83f 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -94,8 +94,8 @@ def run(self, output: bool = False): b_plasma_poloidal_average=self.data.physics.b_plasma_surface_poloidal_average, p_plasma_separatrix_mw=self.data.physics.p_plasma_separatrix_mw, len_plasma_sol_power_decay=self.data.physics.len_plasma_sol_eich13_power_decay, + len_plasma_sol_power_spreading=self.data.physics.len_plasma_sol_scrabosio14_power_spreading, f_div_flux_expansion=self.data.divertor.f_div_flux_expansion, - nd_plasma_separatrix_electron=self.data.physics.nd_plasma_separatrix_electron, deg_div_field_plate=self.data.divertor.deg_div_field_plate, rad_fraction_sol=self.data.physics.rad_fraction_sol, f_p_div_lower=self.data.physics.f_p_div_lower, @@ -277,8 +277,8 @@ def divwade( b_plasma_poloidal_average: float, p_plasma_separatrix_mw: float, len_plasma_sol_power_decay: float, + len_plasma_sol_power_spreading: float, f_div_flux_expansion: float, - nd_plasma_separatrix_electron: float, deg_div_field_plate: float, rad_fraction_sol: float, f_p_div_lower: float, @@ -308,10 +308,10 @@ def divwade( power to divertor (MW) len_plasma_sol_power_decay : float SOL power decay length (λ_q) [m] + len_plasma_sol_power_spreading : float + SOL power spreading factor (S) [m] f_div_flux_expansion : float plasma flux expansion in divertor - nd_plasma_separatrix_electron : float - electron density at separatrix (m-3) deg_div_field_plate : float field line angle wrt divertor target plate (degrees) rad_fraction_sol : float @@ -332,17 +332,8 @@ def divwade( Bt_omp = -b_plasma_toroidal_on_axis * rmajor / r_omp - # Spreading factor - spread_fact = ( - 0.12 - * (nd_plasma_separatrix_electron / 1e19) ** -0.02 - * p_plasma_separatrix_mw**-0.21 - * rmajor**0.71 - * b_plasma_poloidal_average**-0.82 - ) - # SOL width - lambda_int = len_plasma_sol_power_decay + 1.64 * spread_fact + lambda_int = len_plasma_sol_power_decay + 1.64 * len_plasma_sol_power_spreading # Flux angle on midplane alpha_mid = math.degrees(math.atan(Bp_omp / Bt_omp)) From 8a86b80503701b42379a2aa3620065589ff23933 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 27 Jul 2026 17:23:37 +0100 Subject: [PATCH 07/18] Refactor Divertor model: update divwade method parameters and calculations for outboard toroidal field --- process/models/divertor.py | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/process/models/divertor.py b/process/models/divertor.py index e6952af83f..94245c52f6 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -89,9 +89,8 @@ def run(self, output: bool = False): ): self.divwade( rmajor=self.data.physics.rmajor, - rminor=self.data.physics.rminor, - b_plasma_toroidal_on_axis=self.data.physics.b_plasma_toroidal_on_axis, - b_plasma_poloidal_average=self.data.physics.b_plasma_surface_poloidal_average, + b_plasma_outboard_toroidal=self.data.physics.b_plasma_outboard_toroidal, + b_plasma_surface_poloidal_average=self.data.physics.b_plasma_surface_poloidal_average, p_plasma_separatrix_mw=self.data.physics.p_plasma_separatrix_mw, len_plasma_sol_power_decay=self.data.physics.len_plasma_sol_eich13_power_decay, len_plasma_sol_power_spreading=self.data.physics.len_plasma_sol_scrabosio14_power_spreading, @@ -272,9 +271,8 @@ def divtart( def divwade( self, rmajor: float, - rminor: float, - b_plasma_toroidal_on_axis: float, - b_plasma_poloidal_average: float, + b_plasma_outboard_toroidal: float, + b_plasma_surface_poloidal_average: float, p_plasma_separatrix_mw: float, len_plasma_sol_power_decay: float, len_plasma_sol_power_spreading: float, @@ -298,12 +296,10 @@ def divwade( ---------- rmajor : float plasma major radius (m) - rminor : float - plasma minor radius (m) - b_plasma_toroidal_on_axis : float - toroidal field (T) - b_plasma_poloidal_average : float - poloidal field (T) + b_plasma_outboard_toroidal : float + toroidal field at the outboard midplane (T) + b_plasma_surface_poloidal_average : float + Surface averaged poloidal field (B) [T] p_plasma_separatrix_mw : float power to divertor (MW) len_plasma_sol_power_decay : float @@ -324,19 +320,14 @@ def divwade( float divertor heat load for a tight aspect ratio machine """ - # Radius on midplane - r_omp = rmajor + rminor - - # B fields on midplane - Bp_omp = -b_plasma_poloidal_average * rmajor / r_omp - - Bt_omp = -b_plasma_toroidal_on_axis * rmajor / r_omp - - # SOL width + # SOL width at divertor plates (λ_int) [m] + # λ_int = λ_q + 1.64 * S lambda_int = len_plasma_sol_power_decay + 1.64 * len_plasma_sol_power_spreading # Flux angle on midplane - alpha_mid = math.degrees(math.atan(Bp_omp / Bt_omp)) + alpha_mid = math.degrees( + math.atan(b_plasma_surface_poloidal_average / b_plasma_outboard_toroidal) + ) # Flux angle in the divertor alpha_div = f_div_flux_expansion * alpha_mid From fda05b5eba2ce4dfeb559df31c5feca93bf9d228 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 11:06:50 +0100 Subject: [PATCH 08/18] Tidy some docs and references --- documentation/source/eng-models/divertor.md | 42 +++++++-------------- process/models/divertor.py | 7 ++++ process/models/physics/scrape_off_layer.py | 2 +- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/documentation/source/eng-models/divertor.md b/documentation/source/eng-models/divertor.md index 7eb1d26c5d..4394738f9e 100644 --- a/documentation/source/eng-models/divertor.md +++ b/documentation/source/eng-models/divertor.md @@ -169,21 +169,11 @@ The interactive graph below can be used to investigate how changing the key para !!! Note "" `i_div_heat_load == 2` -A divertor heat flux model is provided in Appendix A.II. of [^2]. This uses the Eich scaling -[^3] and S-factor [^4] to calculate the SOL width at the outboard divertor, mapped to the midplane: +A divertor heat flux model is provided in Appendix A.II. of [^wade_21]. This model originally uses the [Eich scaling](../physics-models/plasma_scrape_off_layer.md#eich-2013-model--calculate_eich2013_sol_power_decay_length) +and the [Scrabosio S-factor](../physics-models/plasma_scrape_off_layer.md#scarabosio-2015--calculate_scarabosio2015_power_spreading_factor) to calculate the SOL width at the outboard divertor, mapped to the midplane: $$ -\lambda_{int} = \lambda_{q,Eich} + 1.64S -$$ - -where - -$$ -\lambda_{q,Eich} = 1.35 \, P_{\mathrm{SOL}}^{-0.02} \, R_{o}^{0.04} \, B_{p}^{-0.92} \, \epsilon^{0.42} -$$ - -$$ -S = 0.12(n_{e,mid}/10^{19})^{-0.02} \, P_{\mathrm{SOL}}^{-0.21} \, R_{o}^{0.71} \, B_{p}^{-0.82}. +\lambda_{\text{int}} = \lambda_{\text{q,Eich}} + 1.64S $$ This is then used to calculate the wetted area in the divertor @@ -196,44 +186,40 @@ where $N_{div}$ is the number of divertors (1 or 2), $F_{exp}$ is the relevant f $\theta_{div}$ is the tilt of the separatrix relative to the target in the poloidal plane, and has the form $$ -\theta_{div} = \sin^{-1} [(1+1/\alpha_{div}^{2})\sin\beta_{div}], +\theta_{\text{div}} = \sin^{-1} [(1+1/\alpha_{\text{div}}^{2})\sin(\beta_{\text{div}})], $$ where $$ -\alpha_{div} = F_{exp}\alpha_{mid} +\alpha_{\text{div}} = F_{\text{exp}}\alpha_{\text{mid}} $$ $$ -\alpha_{mid} = \tan^{-1}\frac{B_{p,mid}}{B_{T,mid}} +\alpha_{\text{mid}} = \tan^{-1}\frac{B_{\text{p,mid}}}{B_{\text{T,mid}}} $$ -where $B_{p,mid}$ and $B_{T,mid}$ are the poloidal and toroidal fields on the outer midplane. The +where $B_{\text{p,mid}}$ and $B_{\text{T,mid}}$ are the poloidal and toroidal fields on the outer midplane. The parameter $\beta_{div}$ is the angle of incidence between the field line and the target. -The divertor heat flux in $\mathrm{MW}/\mathrm{m^{2}}$ is then +The divertor heat flux in $[\mathrm{MW}/\mathrm{m^{2}}]$ is then $$ -q_{div} = P_{\mathrm{SOL}}(1-f_{rad,div})/A_{wetted} +q_{\text{div}} = P_{\mathrm{SOL}}(1-f_{\text{rad,div}})/A_{\text{wetted}} $$ -where $f_{rad,div}$ is the SOL radiative fraction. +where $f_{\text{rad,div}}$ is the SOL radiative fraction. For the purposes of this model, the following are inputs: -- Flux expansion $F_{exp}$ (`f_div_flux_expansion`, default = 2) -- Field line angle with respect to divertor target plate (degrees) $\beta_{div}$ (`deg_div_field_plate`), also +- Flux expansion $F_{\text{exp}}$ (`f_div_flux_expansion`, default = 2) +- Field line angle with respect to divertor target plate (degrees) $\beta_{\text{div}}$ (`deg_div_field_plate`), also available as an iteration variable (170) -- SOL radiative fraction, $f_{rad,div}$ (`rad_fraction_sol`). +- SOL radiative fraction, $f_{\text{rad,div}}$ (`rad_fraction_sol`). [^1]: N.A. Uckan and ITER Physics Group, 'ITER Physics Design Guidelines: 1989', ITER Documentation Series, No. 10, IAEA/ITER/DS/10 (1990) -[^2]: M.R. Wade & J.A. Leuer, 'Cost Drivers for a Tokamak-Based Compact Pilot Plant, Fusion Science and Technology, 77:2, 119-143 (2021) - -[^3]: T. Eich et al, 'Scaling of the tokamak near the scrape-off layer H-mode power width and implications for ITER', Nucl. Fusion 53 093031 (2013) - -[^4]: A. Scarabosio et al, 'Scaling of the divertor power spreading (S-factor) in open and closed divertor operation in JET and ASDEX Upgrade, Journal of Nuclear Materials, Vol. 463, 49-54 (2015) +[^wade_21]: M. R. Wade and J. A. Leuer, “Cost Drivers for a Tokamak-Based Compact Pilot Plant,” Fusion Science and Technology, vol. 77, no. 2, pp. 119–143, Feb. 2021, doi: 10.1080/15361055.2020.1858670. [^5]: Y.-K. M. Peng, J. B. Hicksand AEA Fusion, Culham (UK), "Engineering feasibility of tight aspect ratio Tokamak (spherical torus) reactors". 1990. https://inis.iaea.org/records/ey2rf-dah04 diff --git a/process/models/divertor.py b/process/models/divertor.py index 94245c52f6..b11a0b6ce0 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -319,6 +319,13 @@ def divwade( ------- float divertor heat load for a tight aspect ratio machine + + + References + ---------- + [1] M. R. Wade and J. A. Leuer, “Cost Drivers for a Tokamak-Based Compact Pilot + Plant,” Fusion Science and Technology, vol. 77, no. 2, pp. 119-143, Feb. 2021, + doi: 10.1080/15361055.2020.1858670. """ # SOL width at divertor plates (λ_int) [m] # λ_int = λ_q + 1.64 * S diff --git a/process/models/physics/scrape_off_layer.py b/process/models/physics/scrape_off_layer.py index 5033ee3e5c..a0194e960d 100644 --- a/process/models/physics/scrape_off_layer.py +++ b/process/models/physics/scrape_off_layer.py @@ -53,7 +53,7 @@ def run(self): / self.data.physics.a_plasma_outboard_sol_eich13_parallel ) - self.data.physics.len_plasma_sol_scrabosio14_power_spreading = self.calculate_scarabosio2014_power_spreading_factor( + self.data.physics.len_plasma_sol_scrabosio14_power_spreading = self.calculate_scarabosio2014_power_spreading_factor( # noqa: E501 p_plasma_separatrix_mw=self.data.physics.p_plasma_separatrix_mw, b_plasma_surface_poloidal_average=self.data.physics.b_plasma_surface_poloidal_average, nd_plasma_separatrix_electron_19=self.data.physics.nd_plasma_separatrix_electron From 5765ee1fcfacb5cc0e3cee007f838b9a08a8d857 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 16:35:00 +0100 Subject: [PATCH 09/18] Add WadeDivertorMetrics dataclass and update Divertor model calculations for strike width --- process/core/io/plot/summary.py | 394 +++++++++++++++++++++++++++++++- process/models/divertor.py | 37 ++- 2 files changed, 428 insertions(+), 3 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index d5b3e71109..526e97f5f1 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13,7 +13,7 @@ import matplotlib.pyplot as plt import numpy as np from matplotlib import patches -from matplotlib.patches import Circle, Rectangle +from matplotlib.patches import Arc, Circle, Polygon, Rectangle from matplotlib.path import Path as mplPath from scipy.interpolate import interp1d @@ -16201,6 +16201,394 @@ def plot_pf_dimensions( axis.set_aspect("equal", adjustable="box") +def plot_divertor_geometry( + theta_div_deg, alpha_div, beta_div_deg, wetted_width=3.5, plate_angle_deg=20 +): + """ + Plots the divertor geometry using calculated angle values. + + Parameters + ---------- + theta_div_deg : float + Separatrix tilt angle in degrees (poloidal plane). + alpha_div : float + Magnetic pitch / flux expansion factor (F_exp * alpha_mid). + beta_div_deg : float + Total magnetic field grazing angle in degrees (3D). + wetted_width : float + Distance along the plate between strike points (in arbitrary units/cm). + plate_angle_deg : float + Orientation angle of the plate relative to the horizontal plane. + """ + # --- 1. Geometric Angle Calculations --- + L = 7.5 # Length of incoming flux lines + + # Orientation of vectors + plate_tangent_deg = plate_angle_deg + 180 # Along plate face (leftward) + sep_deg = plate_tangent_deg - theta_div_deg # Direction of incoming flux lines + + plate_tangent_rad = np.radians(plate_tangent_deg) + sep_rad = np.radians(sep_deg) + + # --- 2. Target Plate & Strike Point Coordinates --- + sp1 = np.array([0.0, 0.0]) # Strike point 1 (origin) + sp2 = sp1 + wetted_width * np.array([ + np.cos(plate_tangent_rad), + np.sin(plate_tangent_rad), + ]) + + # Endpoints of flux lines + fl1_end = sp1 + L * np.array([np.cos(sep_rad), np.sin(sep_rad)]) + fl2_end = sp2 + L * np.array([np.cos(sep_rad), np.sin(sep_rad)]) + + # Extend target plate beyond the strike points + plate_start = sp1 - 2.0 * np.array([ + np.cos(plate_tangent_rad), + np.sin(plate_tangent_rad), + ]) + plate_end = sp2 + 2.0 * np.array([ + np.cos(plate_tangent_rad), + np.sin(plate_tangent_rad), + ]) + + # --- 3. Figure Setup --- + fig, ax = plt.subplots(figsize=(11, 7.5)) + + # Target Plate + ax.plot( + [plate_start[0], plate_end[0]], + [plate_start[1], plate_end[1]], + color="dimgray", + linewidth=8, + solid_capstyle="round", + label="Divertor Target Plate", + ) + + # Highlight Wetted Area (Strike Zone) + ax.plot( + [sp1[0], sp2[0]], + [sp1[1], sp2[1]], + color="gold", + linewidth=10, + zorder=3, + label="Wetted Area (Strike Zone)", + ) + + # Shaded Scrape-Off Layer (SOL) Heat Flow Region + sol_region = Polygon( + [sp1, fl1_end, fl2_end, sp2], + closed=True, + color="red", + alpha=0.15, + label="Heat Flow Corridor (SOL)", + ) + ax.add_patch(sol_region) + + # --- 4. Plot Separatrix Lines --- + ax.plot( + [sp1[0], fl1_end[0]], + [sp1[1], fl1_end[1]], + color="red", + linewidth=3, + linestyle="-", + label="Separatrix Line 1", + ) + ax.plot( + [sp2[0], fl2_end[0]], + [sp2[1], fl2_end[1]], + color="darkred", + linewidth=3, + linestyle="--", + label="Flux Line 2 (SOL Edge)", + ) + + # Strike Points + ax.plot(sp1[0], sp1[1], "ko", markersize=7) + ax.plot(sp2[0], sp2[1], "ko", markersize=7) + + # --- 5. Dynamic Angle Arc for theta_div --- + arc_radius = 2.8 + arc = Arc( + sp1, + arc_radius * 2, + arc_radius * 2, + angle=0, + theta1=sep_deg, + theta2=plate_tangent_deg, + color="red", + linewidth=2.5, + ) + ax.add_patch(arc) + + # Label showing the dynamic value of theta_div + mid_angle_rad = np.radians((sep_deg + plate_tangent_deg) / 2) + label_x = sp1[0] + (arc_radius * 1.3) * np.cos(mid_angle_rad) + label_y = sp1[1] + (arc_radius * 1.3) * np.sin(mid_angle_rad) + ax.text( + label_x, + label_y, + rf"$\theta_{{div}} = {theta_div_deg:.1f}^\circ$", + color="red", + fontsize=13, + fontweight="bold", + ha="center", + va="center", + ) + + # --- 6. Wetted Area Dimension Marker --- + normal_vec = np.array([-np.sin(plate_tangent_rad), np.cos(plate_tangent_rad)]) + offset = 1.0 + dim_sp1 = sp1 - normal_vec * offset + dim_sp2 = sp2 - normal_vec * offset + + ax.annotate( + "", + xy=dim_sp1, + xytext=dim_sp2, + arrowprops=dict(arrowstyle="<->", color="darkorange", lw=2.5), + ) + + dim_mid = (dim_sp1 + dim_sp2) / 2 - normal_vec * 0.5 + ax.text( + dim_mid[0], + dim_mid[1], + f"Wetted Width = {wetted_width:.2f}", + color="darkorange", + fontsize=11, + fontweight="bold", + ha="center", + va="center", + rotation=plate_angle_deg, + ) + + # --- 7. Overlay Box displaying Calculated Code Values --- + info_text = ( + r"$\mathbf{Calculated\ Divertor\ Parameters:}$" + "\n" + "---------------------------------------\n" + rf"• $\mathbf{{\theta_{{div}}}}$ (Separatrix Tilt): {theta_div_deg:.2f}°" + "\n" + rf"• $\mathbf{{\alpha_{{div}}}}$ (Pitch / Flux Factor): {alpha_div:.2f}" + "\n" + rf"• $\mathbf{{\beta_{{div}}}}$ (3D Grazing Angle): {beta_div_deg:.2f}°" + "\n" + "---------------------------------------\n" + r"Note: $\beta_{div}$ is a 3D skimming angle" + "\n" + r"between total field $\mathbf{B}$ & target face." + ) + props = dict( + boxstyle="round,pad=0.6", facecolor="whitesmoke", alpha=0.9, edgecolor="gray" + ) + ax.text( + 0.03, + 0.95, + info_text, + transform=ax.transAxes, + fontsize=11, + verticalalignment="top", + bbox=props, + ) + + # --- 8. Formatting & Cleanup --- + ax.set_aspect("equal") + ax.set_xlim(-9, 4) + ax.set_ylim(-4, 8) + ax.axis("off") + ax.legend(loc="upper right", fontsize=11) + plt.title( + "Divertor Geometry with Calculated Field Angles", fontsize=15, fontweight="bold" + ) + plt.tight_layout() + plt.show() + + +def plot_wade_divertor_dimensions( + axis: plt.Axes, mfile: MFile, scan: int, colour_scheme: Literal[1, 2] = 1 +) -> None: + """Plot the Wade divertor dimensions on the given axis.""" + # --- 1. Parameters --- + alpha_div = mfile.get( + "alpha_div", scan=scan + ) # Magnetic pitch / flux expansion factor (F_exp * alpha_mid) + beta_div_deg = mfile.get( + "deg_div_field_plate", scan=scan + ) # Total magnetic field grazing angle (3D) + plate_angle_deg = 20 # Visual orientation of the plate in the room + theta_div_deg = np.degrees( + mfile.get("theta_div", scan=scan) + ) # Separatrix tilt angle relative to the target face + wetted_width = mfile.get( + "strike_width", scan=scan + ) # Distance along plate between the two strike points + L = 0.5 # Length of flux lines + + # --- 1. Geometric Angle Calculations --- + L = 3 * wetted_width # Length of incoming flux lines + + # Orientation of vectors + plate_tangent_deg = plate_angle_deg + 180 # Along plate face (leftward) + sep_deg = plate_tangent_deg - theta_div_deg # Direction of incoming flux lines + + plate_tangent_rad = np.radians(plate_tangent_deg) + sep_rad = np.radians(sep_deg) + + # --- 2. Target Plate & Strike Point Coordinates --- + sp1 = np.array([0.0, 0.0]) # Strike point 1 (origin) + sp2 = sp1 + wetted_width * np.array([ + np.cos(plate_tangent_rad), + np.sin(plate_tangent_rad), + ]) + + # Endpoints of flux lines + fl1_end = sp1 + L * np.array([np.cos(sep_rad), np.sin(sep_rad)]) + fl2_end = sp2 + L * np.array([np.cos(sep_rad), np.sin(sep_rad)]) + + # Extend target plate beyond the strike points + plate_start = sp1 - (2 * wetted_width) * np.array([ + np.cos(plate_tangent_rad), + np.sin(plate_tangent_rad), + ]) + plate_end = sp2 + (2 * wetted_width) * np.array([ + np.cos(plate_tangent_rad), + np.sin(plate_tangent_rad), + ]) + + # Target Plate + axis.plot( + [plate_start[0], plate_end[0]], + [plate_start[1], plate_end[1]], + color="dimgray", + linewidth=8, + solid_capstyle="round", + label="Divertor Target Plate", + ) + + # Highlight Wetted Area (Strike Zone) + axis.plot( + [sp1[0], sp2[0]], + [sp1[1], sp2[1]], + color="gold", + linewidth=10, + zorder=3, + label="Wetted Area (Strike Zone)", + ) + + # Shaded Scrape-Off Layer (SOL) Heat Flow Region + sol_region = Polygon( + [sp1, fl1_end, fl2_end, sp2], + closed=True, + color="red", + alpha=0.15, + label="Heat Flow Corridor (SOL)", + ) + axis.add_patch(sol_region) + + # --- 4. Plot Separatrix Lines --- + axis.plot( + [sp1[0], fl1_end[0]], + [sp1[1], fl1_end[1]], + color="red", + linewidth=3, + linestyle="-", + label="Separatrix Line 1", + ) + axis.plot( + [sp2[0], fl2_end[0]], + [sp2[1], fl2_end[1]], + color="darkred", + linewidth=3, + linestyle="--", + label="Flux Line 2 (SOL Edge)", + ) + + # Strike Points + axis.plot(sp1[0], sp1[1], "ko", markersize=7) + axis.plot(sp2[0], sp2[1], "ko", markersize=7) + + # --- 5. Dynamic Angle Arc for theta_div --- + arc_radius = 2 * wetted_width + arc = Arc( + sp1, + arc_radius * 2, + arc_radius * 2, + angle=0, + theta1=sep_deg, + theta2=plate_tangent_deg, + color="red", + linewidth=2.5, + ) + axis.add_patch(arc) + + # Label showing the dynamic value of theta_div + mid_angle_rad = np.radians((sep_deg + plate_tangent_deg) / 2) + label_x = sp1[0] + (arc_radius * 1.3) * np.cos(mid_angle_rad) + label_y = sp1[1] + (arc_radius * 1.3) * np.sin(mid_angle_rad) + axis.text( + label_x, + label_y, + rf"$\theta_{{div}} = {theta_div_deg:.1f}^\circ$", + color="red", + fontsize=13, + fontweight="bold", + ha="center", + va="center", + ) + + # --- 6. Wetted Area Dimension Marker --- + normal_vec = np.array([-np.sin(plate_tangent_rad), np.cos(plate_tangent_rad)]) + offset = 1.0 + dim_sp1 = sp1 - normal_vec * offset + dim_sp2 = sp2 - normal_vec * offset + + axis.annotate( + "", + xy=dim_sp1, + xytext=dim_sp2, + arrowprops=dict(arrowstyle="<->", color="darkorange", lw=2.5), + ) + + dim_mid = (dim_sp1 + dim_sp2) / 2 - normal_vec * 0.5 + axis.text( + dim_mid[0], + dim_mid[1], + f"Wetted Width = {wetted_width:.3e}", + color="darkorange", + fontsize=11, + fontweight="bold", + ha="center", + va="center", + rotation=plate_angle_deg, + ) + + # --- 7. Overlay Box displaying Calculated Code Values --- + info_text = ( + r"$\mathbf{Calculated\ Divertor\ Parameters:}$" + "\n" + "---------------------------------------\n" + rf"• $\mathbf{{\theta_{{div}}}}$ (Separatrix Tilt): {theta_div_deg:.2f}°" + "\n" + rf"• $\mathbf{{\alpha_{{div}}}}$ (Pitch / Flux Factor): {alpha_div:.2f}" + "\n" + rf"• $\mathbf{{\beta_{{div}}}}$ (3D Grazing Angle): {beta_div_deg:.2f}°" + "\n" + "---------------------------------------\n" + r"Note: $\beta_{div}$ is a 3D skimming angle" + "\n" + r"between total field $\mathbf{B}$ & target face." + ) + props = dict( + boxstyle="round,pad=0.6", facecolor="whitesmoke", alpha=0.9, edgecolor="gray" + ) + axis.text( + 0.03, + 0.95, + info_text, + transform=axis.transAxes, + fontsize=11, + verticalalignment="top", + bbox=props, + ) + + # --- 8. Formatting & Cleanup --- + axis.set_aspect("equal") + axis.legend(loc="upper right", fontsize=11) + plt.title( + "Divertor Geometry with Calculated Field Angles", fontsize=15, fontweight="bold" + ) + plt.tight_layout() + + def main_plot( m_file: MFile, scan: int, @@ -16498,6 +16886,10 @@ def _add_page(name: str | None = None): pages["freq"].subplots_adjust(hspace=0.5) + plot_wade_divertor_dimensions( + _add_page("wade_divertor").add_subplot(111), m_file, scan + ) + # Plot poloidal cross-section poloidal_cross_section( _add_page("tokamak_cross_section").add_subplot(121, aspect="equal"), diff --git a/process/models/divertor.py b/process/models/divertor.py index b11a0b6ce0..efd9ad61cc 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -1,7 +1,7 @@ """Module containing divertor routines""" import math - +from dataclasses import dataclass import numpy as np from process.core import constants @@ -12,6 +12,18 @@ from process.data_structure.physics_variables import DivertorNumberModels +@dataclass +class WadeDivertorMetrics: + """Dataclass to hold the output metrics for the Wade divertor model.""" + + lambda_int: float # SOL width at divertor plates (λ_int) [m] + alpha_mid: float # Flux angle on midplane (degrees) + alpha_div: float # Flux angle in the divertor (degrees) + theta_div: float # Tilt of the separatrix relative to the target in the poloidal plane (radians) + area_wetted: float # Wetted area (m²) + strike_width: float # Strike point width (m) + hldiv_base: float # Base divertor heat load (MW/m²) + class Divertor(Model): """Module containing divertor routines @@ -341,7 +353,8 @@ def divwade( # Tilt of the separatrix relative to the target in the poloidal plane theta_div = math.asin( - (1 + 1 / alpha_div**2) * math.sin(math.radians(deg_div_field_plate)) + (1 + 1 / math.radians(alpha_div) ** 2) + * math.sin(math.radians(deg_div_field_plate)) ) # Wetted area @@ -349,6 +362,8 @@ def divwade( 2 * np.pi * rmajor * lambda_int * f_div_flux_expansion * math.sin(theta_div) ) + strike_width = lambda_int * f_div_flux_expansion * math.sin(theta_div) + # Divertor heat load hldiv_base = p_plasma_separatrix_mw * (1 - rad_fraction_sol) / area_wetted @@ -377,12 +392,30 @@ def divwade( "(deg_div_field_plate)", deg_div_field_plate, ) + po.ovarre( + self.outfile, + "Field line angle wrt to target divertor plate (degrees)", + "(alpha_div)", + alpha_div, + ) + po.ovarre( + self.outfile, + "Field line angle wrt to target divertor plate (degrees)", + "(theta_div)", + theta_div, + ) po.ovarre( self.outfile, "Divertor heat load (MW/m²)", "(pflux_div_heat_load_mw)", self.data.divertor.pflux_div_heat_load_mw, ) + po.ovarre( + self.outfile, + "Strike point width (m)", + "(strike_width)", + strike_width, + ) return self.data.divertor.pflux_div_heat_load_mw @staticmethod From 927b708dadf51458c0901d6bd193e56673a8829f Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 16:36:25 +0100 Subject: [PATCH 10/18] Add dx_div_lower_outboard_strike variable to DivertorData for strike point width --- process/data_structure/divertor_variables.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index 29934185b4..9bc9cd7d68 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -96,6 +96,9 @@ class DivertorData: deg_div_poloidal_plasma: float = 0.0 """Divertor poloidal angle subtended by plasma (degrees)""" + + dx_div_lower_outboard_strike: float = 0.0 + """Lower divertor outboard strike point width [m]""" CREATE_DICTS_FROM_DATACLASS = DivertorData From 407fe05b54bf47d487b49934d1c70d1697f35d87 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 16:39:14 +0100 Subject: [PATCH 11/18] Add lower divertor outboard strike point width and flux expansion factor to DivertorData --- process/data_structure/divertor_variables.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index 9bc9cd7d68..f6459fa7e4 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -99,6 +99,9 @@ class DivertorData: dx_div_lower_outboard_strike: float = 0.0 """Lower divertor outboard strike point width [m]""" + + f_div_lower_flux_expansion: float = 0.0 + """Lower divertor flux expansion factor (fₓ)""" CREATE_DICTS_FROM_DATACLASS = DivertorData From 22cdf904d741428d43cdeb63a83de5db6428d216 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 16:48:32 +0100 Subject: [PATCH 12/18] Add lower divertor flux angle and update strike point width in DivertorData --- process/data_structure/divertor_variables.py | 7 +++++-- process/models/divertor.py | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index f6459fa7e4..e08493e51c 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -96,12 +96,15 @@ class DivertorData: deg_div_poloidal_plasma: float = 0.0 """Divertor poloidal angle subtended by plasma (degrees)""" - + dx_div_lower_outboard_strike: float = 0.0 """Lower divertor outboard strike point width [m]""" - + f_div_lower_flux_expansion: float = 0.0 """Lower divertor flux expansion factor (fₓ)""" + deg_b_div_lower_flux: float = 0.0 + """Lower divertor flux angle [degrees]""" + CREATE_DICTS_FROM_DATACLASS = DivertorData diff --git a/process/models/divertor.py b/process/models/divertor.py index efd9ad61cc..a082a59848 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -2,6 +2,7 @@ import math from dataclasses import dataclass + import numpy as np from process.core import constants @@ -24,6 +25,7 @@ class WadeDivertorMetrics: strike_width: float # Strike point width (m) hldiv_base: float # Base divertor heat load (MW/m²) + class Divertor(Model): """Module containing divertor routines From 8867e25a2a66387d4e444625fdbbfacd85123803 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 16:59:42 +0100 Subject: [PATCH 13/18] Add plasma outboard midplane flux angle calculation and variable to PhysicsData --- process/data_structure/divertor_variables.py | 2 +- process/data_structure/physics_variables.py | 3 +++ process/models/physics/physics.py | 7 +++++ process/models/physics/plasma_fields.py | 28 ++++++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index e08493e51c..54522ea7d6 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -104,7 +104,7 @@ class DivertorData: """Lower divertor flux expansion factor (fₓ)""" deg_b_div_lower_flux: float = 0.0 - """Lower divertor flux angle [degrees]""" + """Lower divertor flux angle [degrees]""" CREATE_DICTS_FROM_DATACLASS = DivertorData diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index e3a3c7c33c..c21af1c091 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1682,6 +1682,9 @@ class PhysicsData: """Plasma outboard midplane (upstream) Eich 2013 SOL parallel power flux (qₗₗ,ᵤ) [MW/m²]""" + deg_b_plasma_outboard_flux_midplane: float = 0.0 + """Plasma outboard midplane flux angle [deg]""" + dt_power_density_plasma: float = 0.0 sigmav_dt_average: float = 0.0 dhe3_power_density: float = 0.0 diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index cc94f885fb..178034d2d5 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -419,6 +419,13 @@ def run(self): ) ) + self.data.physics.deg_b_plasma_outboard_flux_midplane = ( + self.fields.calculate_plasma_magnetic_flux_angle( + b_plasma_toroidal=self.data.physics.b_plasma_outboard_toroidal, + b_plasma_poloidal=self.data.physics.b_plasma_surface_poloidal_average, + ) + ) + # ============================================ # ----------------------------------------------------- diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index e178ebf2e3..becf0cf09d 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -197,6 +197,26 @@ def calculate_total_magnetic_field( """ return np.sqrt(b_plasma_toroidal**2 + b_plasma_poloidal**2) + def calculate_plasma_magnetic_flux_angle( + self, b_plasma_toroidal: float, b_plasma_poloidal: float + ) -> float: + """Calculate the magnetic flux angle in the plasma based on the toroidal and + poloidal magnetic fields strengths + + Parameters + ---------- + b_plasma_toroidal : + toroidal field at point of interest [T] + b_plasma_poloidal : + poloidal field at point of interest [T] + + Returns + ------- + : + magnetic flux angle at the plasma edge (degrees) + """ + return np.degrees(np.arctan(b_plasma_poloidal / b_plasma_toroidal)) + def output(self): """Output plasma magnetic fields data.""" po.oheadr(self.outfile, "Plasma magnetic fields") @@ -266,3 +286,11 @@ def output(self): self.data.physics.b_plasma_outboard_total, "OP ", ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Outboard plasma magnetic flux angle from midplane [deg]", + "(deg_b_plasma_outboard_flux_midplane)", + self.data.physics.deg_b_plasma_outboard_flux_midplane, + "OP ", + ) From 9fd755122d034b379e15ea4d1ba6cc3fa03ab14d Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 17:02:19 +0100 Subject: [PATCH 14/18] Refactor divwade method to remove unused parameters and update flux angle calculation with midplane angle --- process/models/divertor.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/process/models/divertor.py b/process/models/divertor.py index a082a59848..eb2c29c789 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -103,8 +103,6 @@ def run(self, output: bool = False): ): self.divwade( rmajor=self.data.physics.rmajor, - b_plasma_outboard_toroidal=self.data.physics.b_plasma_outboard_toroidal, - b_plasma_surface_poloidal_average=self.data.physics.b_plasma_surface_poloidal_average, p_plasma_separatrix_mw=self.data.physics.p_plasma_separatrix_mw, len_plasma_sol_power_decay=self.data.physics.len_plasma_sol_eich13_power_decay, len_plasma_sol_power_spreading=self.data.physics.len_plasma_sol_scrabosio14_power_spreading, @@ -112,6 +110,7 @@ def run(self, output: bool = False): deg_div_field_plate=self.data.divertor.deg_div_field_plate, rad_fraction_sol=self.data.physics.rad_fraction_sol, f_p_div_lower=self.data.physics.f_p_div_lower, + deg_b_plasma_outboard_flux_midplane=self.data.physics.deg_b_plasma_outboard_flux_midplane, output=output, ) return @@ -285,8 +284,6 @@ def divtart( def divwade( self, rmajor: float, - b_plasma_outboard_toroidal: float, - b_plasma_surface_poloidal_average: float, p_plasma_separatrix_mw: float, len_plasma_sol_power_decay: float, len_plasma_sol_power_spreading: float, @@ -294,6 +291,7 @@ def divwade( deg_div_field_plate: float, rad_fraction_sol: float, f_p_div_lower: float, + deg_b_plasma_outboard_flux_midplane: float, output: bool, ) -> float: """Divertor heat load model (Wade 2020) @@ -310,10 +308,6 @@ def divwade( ---------- rmajor : float plasma major radius (m) - b_plasma_outboard_toroidal : float - toroidal field at the outboard midplane (T) - b_plasma_surface_poloidal_average : float - Surface averaged poloidal field (B) [T] p_plasma_separatrix_mw : float power to divertor (MW) len_plasma_sol_power_decay : float @@ -345,13 +339,8 @@ def divwade( # λ_int = λ_q + 1.64 * S lambda_int = len_plasma_sol_power_decay + 1.64 * len_plasma_sol_power_spreading - # Flux angle on midplane - alpha_mid = math.degrees( - math.atan(b_plasma_surface_poloidal_average / b_plasma_outboard_toroidal) - ) - # Flux angle in the divertor - alpha_div = f_div_flux_expansion * alpha_mid + alpha_div = f_div_flux_expansion * deg_b_plasma_outboard_flux_midplane # Tilt of the separatrix relative to the target in the poloidal plane theta_div = math.asin( From c95963e3a868aeea92381444ed182a9590c3b76d Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 17:09:35 +0100 Subject: [PATCH 15/18] Add lower divertor outboard wetted area variable to DivertorData --- process/data_structure/divertor_variables.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index 54522ea7d6..d6fde0e953 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -105,6 +105,9 @@ class DivertorData: deg_b_div_lower_flux: float = 0.0 """Lower divertor flux angle [degrees]""" + + a_div_lower_outboard_wetted: float = 0.0 + """Lower divertor outboard wetted area [m²]""" CREATE_DICTS_FROM_DATACLASS = DivertorData From ec55c2d3ab0daa4c2b78435b188841d4d9319944 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 17:11:53 +0100 Subject: [PATCH 16/18] Rename 'deg_div_field_plate' to 'deg_b_div_lower_outboard_grazing' across multiple files for consistency --- documentation/source/eng-models/divertor.md | 2 +- process/core/input.py | 2 +- process/core/io/obsolete_vars.py | 2 +- process/core/io/plot/summary.py | 2 +- process/core/solver/iteration_variables.py | 2 +- process/data_structure/divertor_variables.py | 2 +- process/data_structure/numerics.py | 2 +- process/models/divertor.py | 12 ++++++------ tests/regression/input_files/st_regression.IN.DAT | 2 +- tests/unit/models/test_divertor.py | 4 ++-- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/documentation/source/eng-models/divertor.md b/documentation/source/eng-models/divertor.md index 4394738f9e..b8bc20f4f6 100644 --- a/documentation/source/eng-models/divertor.md +++ b/documentation/source/eng-models/divertor.md @@ -213,7 +213,7 @@ where $f_{\text{rad,div}}$ is the SOL radiative fraction. For the purposes of this model, the following are inputs: - Flux expansion $F_{\text{exp}}$ (`f_div_flux_expansion`, default = 2) -- Field line angle with respect to divertor target plate (degrees) $\beta_{\text{div}}$ (`deg_div_field_plate`), also +- Field line angle with respect to divertor target plate (degrees) $\beta_{\text{div}}$ (`deg_b_div_lower_outboard_grazing`), also available as an iteration variable (170) - SOL radiative fraction, $f_{\text{rad,div}}$ (`rad_fraction_sol`). diff --git a/process/core/input.py b/process/core/input.py index 1d7c132b6a..f0f05a6a52 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -217,7 +217,7 @@ def bounds(self) -> tuple[NumberType | None, NumberType | None]: "bctmp": InputVariable("pulse", float, range=(1.0, 800.0)), "e_beam_kev": InputVariable("current_drive", float, range=(1.0, 1000000.0)), "dx_beam_duct": InputVariable("current_drive", float, range=(0.001, 5.0)), - "deg_div_field_plate": InputVariable("divertor", float, range=(0.0, 360.0)), + "deg_b_div_lower_outboard_grazing": InputVariable("divertor", float, range=(0.0, 360.0)), "beta_poloidal_max": InputVariable("constraints", float, range=(0.01, 2.0)), "betai": InputVariable("divertor", float, range=(0.0, 1.5707)), "betao": InputVariable("divertor", float, range=(0.0, 1.5707)), diff --git a/process/core/io/obsolete_vars.py b/process/core/io/obsolete_vars.py index 2b34765c7e..451f27217c 100644 --- a/process/core/io/obsolete_vars.py +++ b/process/core/io/obsolete_vars.py @@ -302,7 +302,7 @@ "wave_mode": "i_ecrh_wave_mode", "pheatfix": "p_hcd_secondary_extra_heat_mw", "ftar": "f_p_div_lower", - "beta_div": "deg_div_field_plate", + "beta_div": "deg_b_div_lower_outboard_grazing", "divclfr": "f_vol_div_coolant", "divdens": "den_div_structure", "divplt": "dx_div_plate", diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 526e97f5f1..bd6946ee6c 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -16407,7 +16407,7 @@ def plot_wade_divertor_dimensions( "alpha_div", scan=scan ) # Magnetic pitch / flux expansion factor (F_exp * alpha_mid) beta_div_deg = mfile.get( - "deg_div_field_plate", scan=scan + "deg_b_div_lower_outboard_grazing", scan=scan ) # Total magnetic field grazing angle (3D) plate_angle_deg = 20 # Visual orientation of the plate in the room theta_div_deg = np.degrees( diff --git a/process/core/solver/iteration_variables.py b/process/core/solver/iteration_variables.py index c40f932ec1..e89092316b 100644 --- a/process/core/solver/iteration_variables.py +++ b/process/core/solver/iteration_variables.py @@ -231,7 +231,7 @@ class IterationVariable: ), 162: IterationVariable("r_cp_top", "build", 0.0010, 10.0), 169: IterationVariable("te0_ecrh_achievable", "stellarator", 1.0, 1.0e3), - 170: IterationVariable("deg_div_field_plate", "divertor", 0.49, 5.01), + 170: IterationVariable("deg_b_div_lower_outboard_grazing", "divertor", 0.49, 5.01), 171: IterationVariable("casths_fraction", "tfcoil", 0.01, 0.99), 172: IterationVariable("dx_tf_side_case_min", "tfcoil", 0.001, 1.0), 173: IterationVariable("f_plasma_fuel_tritium", "physics", 0.000, 1.000), diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index d6fde0e953..7042e89255 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -24,7 +24,7 @@ class DivertorData: anginc: float = 0.262 """angle of incidence of field line on plate (rad)""" - deg_div_field_plate: float = 1.0 + deg_b_div_lower_outboard_grazing: float = 1.0 """field line angle wrt divertor target plate (degrees)""" betai: float = 1.0 diff --git a/process/data_structure/numerics.py b/process/data_structure/numerics.py index 57505f1c39..a56ad53a80 100644 --- a/process/data_structure/numerics.py +++ b/process/data_structure/numerics.py @@ -566,7 +566,7 @@ class NumericsData: * (167) NOT USED * (168) NOT USED * (169) te0_ecrh_achievable: Max. achievable electron temperature at ignition point - * (170) deg_div_field_plate : field line angle wrt divertor target plate (degrees) + * (170) deg_b_div_lower_outboard_grazing : field line angle wrt divertor target plate (degrees) * (171) casths_fraction : TF side case thickness as fraction of toridal case thickness * (172) dx_tf_side_case_min : TF side case thickness [m] * (173) f_plasma_fuel_deuterium : Deuterium fraction in fuel diff --git a/process/models/divertor.py b/process/models/divertor.py index eb2c29c789..43227faea1 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -107,7 +107,7 @@ def run(self, output: bool = False): len_plasma_sol_power_decay=self.data.physics.len_plasma_sol_eich13_power_decay, len_plasma_sol_power_spreading=self.data.physics.len_plasma_sol_scrabosio14_power_spreading, f_div_flux_expansion=self.data.divertor.f_div_flux_expansion, - deg_div_field_plate=self.data.divertor.deg_div_field_plate, + deg_b_div_lower_outboard_grazing=self.data.divertor.deg_b_div_lower_outboard_grazing, rad_fraction_sol=self.data.physics.rad_fraction_sol, f_p_div_lower=self.data.physics.f_p_div_lower, deg_b_plasma_outboard_flux_midplane=self.data.physics.deg_b_plasma_outboard_flux_midplane, @@ -288,7 +288,7 @@ def divwade( len_plasma_sol_power_decay: float, len_plasma_sol_power_spreading: float, f_div_flux_expansion: float, - deg_div_field_plate: float, + deg_b_div_lower_outboard_grazing: float, rad_fraction_sol: float, f_p_div_lower: float, deg_b_plasma_outboard_flux_midplane: float, @@ -316,7 +316,7 @@ def divwade( SOL power spreading factor (S) [m] f_div_flux_expansion : float plasma flux expansion in divertor - deg_div_field_plate : float + deg_b_div_lower_outboard_grazing : float field line angle wrt divertor target plate (degrees) rad_fraction_sol : float SOL radiation fraction @@ -345,7 +345,7 @@ def divwade( # Tilt of the separatrix relative to the target in the poloidal plane theta_div = math.asin( (1 + 1 / math.radians(alpha_div) ** 2) - * math.sin(math.radians(deg_div_field_plate)) + * math.sin(math.radians(deg_b_div_lower_outboard_grazing)) ) # Wetted area @@ -380,8 +380,8 @@ def divwade( po.ovarre( self.outfile, "Field line angle wrt to target divertor plate (degrees)", - "(deg_div_field_plate)", - deg_div_field_plate, + "(deg_b_div_lower_outboard_grazing)", + deg_b_div_lower_outboard_grazing, ) po.ovarre( self.outfile, diff --git a/tests/regression/input_files/st_regression.IN.DAT b/tests/regression/input_files/st_regression.IN.DAT index 78f4028107..ec8b3a4b59 100644 --- a/tests/regression/input_files/st_regression.IN.DAT +++ b/tests/regression/input_files/st_regression.IN.DAT @@ -748,7 +748,7 @@ p_plasma_separatrix_rmajor_max_mw = 40.0 * DESCRIPTION: Angle of incidence of field line on plate (rad) (default = 0.262) * JUSTIFICATION: Not yet set -*deg_div_field_plate = +*deg_b_div_lower_outboard_grazing = * DESCRIPTION: Field line angle wrt divertor target plate (degrees) (default = 1.0) * JUSTIFICATION: Not yet set diff --git a/tests/unit/models/test_divertor.py b/tests/unit/models/test_divertor.py index 803df7cfc7..c2caaf17ba 100644 --- a/tests/unit/models/test_divertor.py +++ b/tests/unit/models/test_divertor.py @@ -84,7 +84,7 @@ def test_divwade(monkeypatch, divertor): p_plasma_separatrix_mw = 1.0e2 f_div_flux_expansion = 2 nd_plasma_separatrix_electron = 1.0e19 - deg_div_field_plate = 5.0 + deg_b_div_lower_outboard_grazing = 5.0 rad_fraction_sol = 8.0e-1 f_p_div_lower = 1.0 @@ -99,7 +99,7 @@ def test_divwade(monkeypatch, divertor): p_plasma_separatrix_mw, f_div_flux_expansion, nd_plasma_separatrix_electron, - deg_div_field_plate, + deg_b_div_lower_outboard_grazing, rad_fraction_sol, f_p_div_lower, False, From 8739954a54302ee7349e1dd03380674ed01c43d7 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 17:13:28 +0100 Subject: [PATCH 17/18] Add lower divertor outboard plate-separatrix poloidal angle variable to DivertorData --- process/data_structure/divertor_variables.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index 7042e89255..ac46b11298 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -108,6 +108,9 @@ class DivertorData: a_div_lower_outboard_wetted: float = 0.0 """Lower divertor outboard wetted area [m²]""" + + deg_div_lower_outboard_plate_separatrix_poloidal: float = 0.0 + """Lower divertor outboard plate-separatrix poloidal angle [degrees]""" CREATE_DICTS_FROM_DATACLASS = DivertorData From 25accf8bcbd9e6313b3095e0d073e15b1292dcaa Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 10 Aug 2026 17:49:01 +0100 Subject: [PATCH 18/18] Refactor divertor-related variables and methods for clarity and consistency --- process/core/input.py | 4 +- process/core/io/obsolete_vars.py | 1 + process/core/io/plot/summary.py | 6 +- process/data_structure/divertor_variables.py | 6 +- process/models/divertor.py | 152 ++++++++++++------- process/models/physics/plasma_fields.py | 3 +- 6 files changed, 108 insertions(+), 64 deletions(-) diff --git a/process/core/input.py b/process/core/input.py index f0f05a6a52..e16e77af8d 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -217,7 +217,9 @@ def bounds(self) -> tuple[NumberType | None, NumberType | None]: "bctmp": InputVariable("pulse", float, range=(1.0, 800.0)), "e_beam_kev": InputVariable("current_drive", float, range=(1.0, 1000000.0)), "dx_beam_duct": InputVariable("current_drive", float, range=(0.001, 5.0)), - "deg_b_div_lower_outboard_grazing": InputVariable("divertor", float, range=(0.0, 360.0)), + "deg_b_div_lower_outboard_grazing": InputVariable( + "divertor", float, range=(0.0, 360.0) + ), "beta_poloidal_max": InputVariable("constraints", float, range=(0.01, 2.0)), "betai": InputVariable("divertor", float, range=(0.0, 1.5707)), "betao": InputVariable("divertor", float, range=(0.0, 1.5707)), diff --git a/process/core/io/obsolete_vars.py b/process/core/io/obsolete_vars.py index 451f27217c..9c66c4b327 100644 --- a/process/core/io/obsolete_vars.py +++ b/process/core/io/obsolete_vars.py @@ -473,6 +473,7 @@ "i_cs_stress": None, "f_nd_alpha_electron": "f_nd_alpha_thermal_electron", "cost_model": "i_cost_model", + "deg_div_field_plate": "deg_b_div_lower_outboard_grazing", } OBS_VARS_HELP = { diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index bd6946ee6c..c234a64064 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -16404,17 +16404,17 @@ def plot_wade_divertor_dimensions( """Plot the Wade divertor dimensions on the given axis.""" # --- 1. Parameters --- alpha_div = mfile.get( - "alpha_div", scan=scan + "deg_b_div_lower_flux", scan=scan ) # Magnetic pitch / flux expansion factor (F_exp * alpha_mid) beta_div_deg = mfile.get( "deg_b_div_lower_outboard_grazing", scan=scan ) # Total magnetic field grazing angle (3D) plate_angle_deg = 20 # Visual orientation of the plate in the room theta_div_deg = np.degrees( - mfile.get("theta_div", scan=scan) + mfile.get("deg_div_lower_outboard_plate_separatrix_poloidal", scan=scan) ) # Separatrix tilt angle relative to the target face wetted_width = mfile.get( - "strike_width", scan=scan + "dx_div_lower_outboard_strike", scan=scan ) # Distance along plate between the two strike points L = 0.5 # Length of flux lines diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index ac46b11298..e3af28ff81 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -25,7 +25,7 @@ class DivertorData: """angle of incidence of field line on plate (rad)""" deg_b_div_lower_outboard_grazing: float = 1.0 - """field line angle wrt divertor target plate (degrees)""" + """3D grazing field line angle with respect to divertor target plate [degrees]""" betai: float = 1.0 """poloidal plane angle between divertor plate and leg, inboard (rad)""" @@ -105,10 +105,10 @@ class DivertorData: deg_b_div_lower_flux: float = 0.0 """Lower divertor flux angle [degrees]""" - + a_div_lower_outboard_wetted: float = 0.0 """Lower divertor outboard wetted area [m²]""" - + deg_div_lower_outboard_plate_separatrix_poloidal: float = 0.0 """Lower divertor outboard plate-separatrix poloidal angle [degrees]""" diff --git a/process/models/divertor.py b/process/models/divertor.py index 43227faea1..4afd1ef6ce 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -17,13 +17,20 @@ class WadeDivertorMetrics: """Dataclass to hold the output metrics for the Wade divertor model.""" - lambda_int: float # SOL width at divertor plates (λ_int) [m] - alpha_mid: float # Flux angle on midplane (degrees) - alpha_div: float # Flux angle in the divertor (degrees) - theta_div: float # Tilt of the separatrix relative to the target in the poloidal plane (radians) - area_wetted: float # Wetted area (m²) - strike_width: float # Strike point width (m) - hldiv_base: float # Base divertor heat load (MW/m²) + pflux_div_heat_load_mw: float + """Divertor heat load [MW/m²]""" + dx_div_lower_outboard_strike: float + """Lower outboard divertor strike point width [m]""" + a_div_lower_outboard_wetted: float + """Lower divertor outboard wetted area [m²]""" + f_div_lower_flux_expansion: float + """Lower divertor flux expansion factor (fₓ)""" + deg_b_div_lower_flux: float + """Lower divertor flux angle [degrees]""" + deg_div_lower_outboard_plate_separatrix_poloidal: float + """Lower divertor outboard plate-separatrix poloidal angle [degrees]""" + deg_b_div_lower_outboard_grazing: float + """Lower divertor outboard grazing angle [degrees]""" class Divertor(Model): @@ -101,7 +108,7 @@ def run(self, output: bool = False): DivertorHeatLoadModel(self.data.divertor.i_div_heat_load) == DivertorHeatLoadModel.WADE ): - self.divwade( + wade_divertor = self.divwade( rmajor=self.data.physics.rmajor, p_plasma_separatrix_mw=self.data.physics.p_plasma_separatrix_mw, len_plasma_sol_power_decay=self.data.physics.len_plasma_sol_eich13_power_decay, @@ -111,8 +118,31 @@ def run(self, output: bool = False): rad_fraction_sol=self.data.physics.rad_fraction_sol, f_p_div_lower=self.data.physics.f_p_div_lower, deg_b_plasma_outboard_flux_midplane=self.data.physics.deg_b_plasma_outboard_flux_midplane, - output=output, ) + + self.data.divertor.pflux_div_heat_load_mw = ( + wade_divertor.pflux_div_heat_load_mw + ) + self.data.divertor.dx_div_lower_outboard_strike = ( + wade_divertor.dx_div_lower_outboard_strike + ) + self.data.divertor.a_div_lower_outboard_wetted = ( + wade_divertor.a_div_lower_outboard_wetted + ) + self.data.divertor.f_div_lower_flux_expansion = ( + wade_divertor.f_div_lower_flux_expansion + ) + self.data.divertor.deg_b_div_lower_flux = wade_divertor.deg_b_div_lower_flux + self.data.divertor.deg_div_lower_outboard_plate_separatrix_poloidal = ( + wade_divertor.deg_div_lower_outboard_plate_separatrix_poloidal + ) + self.data.divertor.deg_b_div_lower_outboard_grazing = ( + wade_divertor.deg_b_div_lower_outboard_grazing + ) + + if output: + self.output_wade_divertor_model() + return @property @@ -292,8 +322,7 @@ def divwade( rad_fraction_sol: float, f_p_div_lower: float, deg_b_plasma_outboard_flux_midplane: float, - output: bool, - ) -> float: + ) -> WadeDivertorMetrics: """Divertor heat load model (Wade 2020) This subroutine calculates the divertor heat flux for any machine, @@ -325,8 +354,7 @@ def divwade( Returns ------- - float - divertor heat load for a tight aspect ratio machine + WadeDivertorMetrics References @@ -363,51 +391,63 @@ def divwade( if self.data.divertor.n_divertors == 2: hldiv_lower = f_p_div_lower * hldiv_base hldiv_upper = (1.0 - f_p_div_lower) * hldiv_base - self.data.divertor.pflux_div_heat_load_mw = max(hldiv_lower, hldiv_upper) + pflux_div_heat_load_mw = max(hldiv_lower, hldiv_upper) else: - self.data.divertor.pflux_div_heat_load_mw = hldiv_base + pflux_div_heat_load_mw = hldiv_base + + return WadeDivertorMetrics( + pflux_div_heat_load_mw=pflux_div_heat_load_mw, + dx_div_lower_outboard_strike=strike_width, + a_div_lower_outboard_wetted=area_wetted, + f_div_lower_flux_expansion=f_div_flux_expansion, + deg_b_div_lower_flux=deg_b_div_lower_outboard_grazing, + deg_div_lower_outboard_plate_separatrix_poloidal=theta_div, + deg_b_div_lower_outboard_grazing=deg_b_div_lower_outboard_grazing, + ) - if output: - po.osubhd(self.outfile, "Divertor Heat Load") - po.ocmmnt(self.outfile, "Assume an expanded divertor with a gaseous target") - po.oblnkl(self.outfile) - po.ovarre( - self.outfile, - "Flux expansion", - "(f_div_flux_expansion)", - f_div_flux_expansion, - ) - po.ovarre( - self.outfile, - "Field line angle wrt to target divertor plate (degrees)", - "(deg_b_div_lower_outboard_grazing)", - deg_b_div_lower_outboard_grazing, - ) - po.ovarre( - self.outfile, - "Field line angle wrt to target divertor plate (degrees)", - "(alpha_div)", - alpha_div, - ) - po.ovarre( - self.outfile, - "Field line angle wrt to target divertor plate (degrees)", - "(theta_div)", - theta_div, - ) - po.ovarre( - self.outfile, - "Divertor heat load (MW/m²)", - "(pflux_div_heat_load_mw)", - self.data.divertor.pflux_div_heat_load_mw, - ) - po.ovarre( - self.outfile, - "Strike point width (m)", - "(strike_width)", - strike_width, - ) - return self.data.divertor.pflux_div_heat_load_mw + def output_wade_divertor_model(self): + + po.oheadr(self.outfile, "Wade Divertor Heat Load Model") + po.ocmmnt(self.outfile, "Assume an expanded divertor with a gaseous target") + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Flux expansion factor (fₓ)", + "(f_div_flux_expansion)", + self.data.divertor.f_div_lower_flux_expansion, + ) + po.ovarre( + self.outfile, + "Field line angle wrt to target divertor plate [degrees]", + "(deg_b_div_lower_outboard_grazing)", + self.data.divertor.deg_b_div_lower_outboard_grazing, + ) + po.ovarre( + self.outfile, + "Lower divertor flux angle [degrees]", + "(deg_b_div_lower_flux)", + self.data.divertor.deg_b_div_lower_flux, + ) + po.ovarre( + self.outfile, + "Lower divertor outboard plate-separatrix poloidal angle [degrees]", + "(deg_div_lower_outboard_plate_separatrix_poloidal)", + self.data.divertor.deg_div_lower_outboard_plate_separatrix_poloidal, + ) + + po.ovarre( + self.outfile, + "Strike point width (m)", + "(dx_div_lower_outboard_strike)", + self.data.divertor.dx_div_lower_outboard_strike, + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Divertor heat load [MW/m²]", + "(pflux_div_heat_load_mw)", + self.data.divertor.pflux_div_heat_load_mw, + ) @staticmethod def incident_radiation_power( diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index becf0cf09d..6444aecf9b 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -197,8 +197,9 @@ def calculate_total_magnetic_field( """ return np.sqrt(b_plasma_toroidal**2 + b_plasma_poloidal**2) + @staticmethod def calculate_plasma_magnetic_flux_angle( - self, b_plasma_toroidal: float, b_plasma_poloidal: float + b_plasma_toroidal: float, b_plasma_poloidal: float ) -> float: """Calculate the magnetic flux angle in the plasma based on the toroidal and poloidal magnetic fields strengths