Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions documentation/source/physics-models/plasma_scrape_off_layer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Scrape off Layer | `ScrapeOffLayer`

## Power Decay Lengths

In tokamak scrape-off layer (SOL) physics, $\lambda_q$ is the radial heat flux e-folding length, representing the narrow width over which exhaust power drops exponentially. It measures how tightly thermal energy exiting the core plasma is compressed onto open magnetic field lines.

-----------

### Eich 2013 Model | `calculate_eich2013_sol_power_decay_length()`

The power decay length in metres is given by[^eich_2013]:

$$
\lambda_q = 1.35 \times 10^{-3} P_{\text{sep}}^{-0.02}R_0^{0.04}B_{\text{p}}(a)^{-0.92}\epsilon^{0.42}
$$

Here $P_{\text{sep}}$ is the plasma separatrix power in $\text{MW}$, $R_0$ is the plasma major radius in $\text{m}$, $B_{\text{p}}(a)$ is the plasma poloidal field at the outboard mid-plane measured in $\text{T}$ and $\epsilon$ is the plasma inverse aspect ratio.

This can be found in Table 3 from Eich et.al [^eich_2013]

The $R^2$ value for this fit is 0.88

----------

### MAST 2014 I | `calculate_mast2014_sol_power_decay_length_1()`

The power decay length in metres is given by[^mast_2014]:

$$
\lambda_q = 1.84(\pm0.48) \times 10^{-3} P_{\text{sep}}^{0.18(\pm0.07)}B_{\text{p}}(a)^{-0.68(\pm0.14)}
$$

Here $P_{\text{sep}}$ is the plasma separatrix power in $\text{MW}$, and $B_{\text{p}}(a)$ is the plasma poloidal field at the outboard mid-plane measured in $\text{T}$.

This can be found in Table 2 and Equation 3 from Thornton et.al [^mast_2014]

The $R^2$ value for this fit is 0.56

----------

### MAST 2014 II | `calculate_mast2014_sol_power_decay_length_2()`

The power decay length in metres is given by[^mast_2014]:

$$
\lambda_q = 4.57(\pm0.54) \times 10^{-3} P_{\text{sep}}^{0.22(\pm0.08)}I_{\text{p}}^{-0.64(\pm0.15)}
$$

Here $P_{\text{sep}}$ is the plasma separatrix power in $\text{MW}$, and $I_{\text{p}}$ is the total plasma current measured in $\text{MA}$.

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

--------

[^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,”
Plasma Physics and Controlled Fusion, vol. 56, no. 5, p. 055008, Apr. 2014, doi: 10.1088/0741-3335/56/5/055008.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ nav:
- Confinement time: physics-models/plasma_confinement.md
- L-H transition: physics-models/plasma_h_mode.md
- Plasma Core Power Balance: physics-models/plasma_power_balance.md
- Plasma Scrape-off Layer: physics-models/plasma_scrape_off_layer.md
- Detailed Plasma Physics: physics-models/detailed_physics.md
- Pulsed Plant Operation: physics-models/pulsed-plant.md
- Engineering Models:
Expand Down
83 changes: 83 additions & 0 deletions process/core/io/plot/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -8914,6 +8914,85 @@ def plot_bootstrap_comparison(axis: plt.Axes, mfile: MFile, scan: int):
axis.set_facecolor("#f0f0f0")


def plot_sol_power_decay_length_comparison(axis: plt.Axes, mfile: MFile, scan: int):
"""Function to plot a scatter box plot of SOL power decay lengths (λ_q).

Parameters
----------
axis :
axis object to plot to
mfile :
MFILE data object
scan :
scan number to use
"""
len_plasma_sol_eich13_power_decay_mm = (
mfile.get("len_plasma_sol_eich13_power_decay", scan=scan) * 1e3
)
len_plasma_sol_mast14_power_decay_1_mm = (
mfile.get("len_plasma_sol_mast14_power_decay_1", scan=scan) * 1e3
)
len_plasma_sol_mast14_power_decay_2_mm = (
mfile.get("len_plasma_sol_mast14_power_decay_2", 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,
}
# Create the violin plot
axis.violinplot(data.values(), showextrema=False)

# Create the box plot
axis.boxplot(
data.values(), showfliers=True, showmeans=True, meanline=True, widths=0.3
)

# Scatter plot for each data point
colors = plt.cm.plasma(np.linspace(0, 1, len(data.values())))
for index, (key, value) in enumerate(data.items()):
axis.scatter(1, value, color=colors[index], label=key, alpha=1.0)
axis.legend(loc="upper left", bbox_to_anchor=(1, 1))

# Calculate average, standard deviation, and median
data_values = list(data.values())
avg_decay_length = np.mean(data_values)
std_decay_length = np.std(data_values)
median_decay_length = np.median(data_values)

# Plot average, standard deviation, and median as text
axis.text(
1.02,
0.2,
f"Average: {avg_decay_length:.4f}",
transform=axis.transAxes,
fontsize=9,
)
axis.text(
1.02,
0.15,
f"Standard Dev: {std_decay_length:.4f}",
transform=axis.transAxes,
fontsize=9,
)
axis.text(
1.02,
0.1,
f"Median: {median_decay_length:.4f}",
transform=axis.transAxes,
fontsize=9,
)

axis.set_title("SOL Power Decay Length ($\\lambda_q$) Comparison")
axis.set_ylabel("Power Decay Length [mm]")
axis.set_xlim([0.5, 1.5])
axis.set_xticks([])
axis.set_xticklabels([])
axis.set_facecolor("#f0f0f0")


def plot_h_threshold_comparison(axis: plt.Axes, mfile: MFile, scan: int, u_seed=None):
"""Function to plot a scatter box plot of L-H threshold power comparisons.

Expand Down Expand Up @@ -16090,6 +16169,10 @@ def _add_page(name: str | None = None):
pages["plasma_compare_2"].add_subplot(224), m_file, scan
)

plot_sol_power_decay_length_comparison(
_add_page("plasma_compare_3").add_subplot(221), m_file, scan
)

plot_debye_length_profile(
_add_page("microscopic_quantities").add_subplot(232), m_file, scan
)
Expand Down
9 changes: 9 additions & 0 deletions process/data_structure/physics_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -1651,6 +1651,15 @@ class PhysicsData:
res_plasma_fuel_spitzer_vol_avg: float = 0.0
"""Volume averaged plasma Spitzer resistivity due to fuel ions (ohm m)"""

len_plasma_sol_eich13_power_decay: float = 0.0
"""Eich 2013 power decay length in the scrape-off layer scaling (λ_q) [m]"""

len_plasma_sol_mast14_power_decay_1: float = 0.0
"""MAST 2014 power decay length in the scrape-off layer scaling 1 (λ_q) [m]"""

len_plasma_sol_mast14_power_decay_2: float = 0.0
"""MAST 2014 power decay length in the scrape-off layer scaling 2 (λ_q) [m]"""

dt_power_density_plasma: float = 0.0
sigmav_dt_average: float = 0.0
dhe3_power_density: float = 0.0
Expand Down
4 changes: 4 additions & 0 deletions process/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
from process.models.physics.plasma_geometry import PlasmaGeom
from process.models.physics.plasma_profiles import PlasmaProfile
from process.models.physics.profiles import NeProfile, TeProfile
from process.models.physics.scrape_off_layer import ScrapeOffLayer
from process.models.power import Power
from process.models.pulse import Pulse
from process.models.shield import Shield
Expand Down Expand Up @@ -679,6 +680,7 @@ def __init__(self, data: DataStructure):
self.plasma_current = PlasmaCurrent()
self.plasma_fields = PlasmaFields()
self.plasma_dia_current = PlasmaDiamagneticCurrent()
self.scrape_off_layer = ScrapeOffLayer()
self.physics = Physics(
plasma_profile=self.plasma_profile,
current_drive=self.current_drive,
Expand All @@ -693,6 +695,7 @@ def __init__(self, data: DataStructure):
plasma_fields=self.plasma_fields,
plasma_dia_current=self.plasma_dia_current,
plasma_geometry=self.plasma_geom,
scrape_off_layer=self.scrape_off_layer,
)
self.physics_detailed = DetailedPhysics(
plasma_profile=self.plasma_profile,
Expand Down Expand Up @@ -782,6 +785,7 @@ def models(self) -> tuple[Model, ...]:
self.plasma_bootstrap_current,
self.plasma_exhaust,
self.plasma_current,
self.scrape_off_layer,
self.neoclassics,
self.plasma_inductance,
self.ne_profile,
Expand Down
12 changes: 12 additions & 0 deletions process/models/physics/physics.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from process.models.physics.plasma_fields import PlasmaFields
from process.models.physics.plasma_geometry import PlasmaGeom
from process.models.physics.plasma_profiles import PlasmaProfile
from process.models.physics.scrape_off_layer import ScrapeOffLayer

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -195,6 +196,7 @@ def __init__(
plasma_fields: PlasmaFields,
plasma_dia_current: PlasmaDiamagneticCurrent,
plasma_geometry: PlasmaGeom,
scrape_off_layer: ScrapeOffLayer,
):
self.outfile = constants.NOUT
self.mfile = constants.MFILE
Expand All @@ -211,6 +213,7 @@ def __init__(
self.fields = plasma_fields
self.dia_current = plasma_dia_current
self.geometry = plasma_geometry
self.scrape_off_layer = scrape_off_layer

def output(self) -> None:
"""Output plasma physics information."""
Expand Down Expand Up @@ -813,6 +816,13 @@ def run(self):
)
)

# ===============================

# Calculate scrape-off layer physics

self.scrape_off_layer.run()

# ===============================
self.data.physics.pflux_plasma_surface_neutron_avg_mw = (
self.data.physics.p_neutron_total_mw / self.data.physics.a_plasma_surface
)
Expand Down Expand Up @@ -2334,6 +2344,8 @@ def outplas(self):

self.exhaust.output()

self.scrape_off_layer.output()

if self.data.stellarator.istell == 0:
self.plasma_transition.output()

Expand Down
Loading
Loading