From f36fb45045167accfe4bc521cb4022a36b870412 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 24 Mar 2026 16:04:48 +0000 Subject: [PATCH 01/14] Add PlasmaFields class for poloidal field calculations --- process/main.py | 3 + process/models/physics/physics.py | 5 +- process/models/physics/plasma_fields.py | 84 +++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 process/models/physics/plasma_fields.py diff --git a/process/main.py b/process/main.py index c37e24dad..f3f2cdcc4 100644 --- a/process/main.py +++ b/process/main.py @@ -106,6 +106,7 @@ PlasmaInductance, ) from process.models.physics.plasma_current import PlasmaCurrent +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.power import Power @@ -715,6 +716,7 @@ def __init__(self): self.plasma_confinement = PlasmaConfinementTime() self.plasma_transition = PlasmaConfinementTransition() self.plasma_current = PlasmaCurrent() + self.plasma_fields = PlasmaFields() self.physics = Physics( plasma_profile=self.plasma_profile, current_drive=self.current_drive, @@ -726,6 +728,7 @@ def __init__(self): plasma_confinement=self.plasma_confinement, plasma_transition=self.plasma_transition, plasma_current=self.plasma_current, + plasma_fields=self.plasma_fields, ) self.physics_detailed = DetailedPhysics( plasma_profile=self.plasma_profile, diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index d313c02ec..e08f6fbea 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -31,6 +31,7 @@ from process.models.physics.exhaust import PlasmaExhaust from process.models.physics.l_h_transition import PlasmaConfinementTransition from process.models.physics.plasma_current import PlasmaCurrent +from process.models.physics.plasma_fields import PlasmaFields logger = logging.getLogger(__name__) @@ -213,6 +214,7 @@ def __init__( plasma_confinement: PlasmaConfinementTime, plasma_transition: PlasmaConfinementTransition, plasma_current: PlasmaCurrent, + plasma_fields: PlasmaFields, ): self.outfile = constants.NOUT self.mfile = constants.MFILE @@ -226,6 +228,7 @@ def __init__( self.confinement = plasma_confinement self.plasma_transition = plasma_transition self.current = plasma_current + self.fields = plasma_fields def physics(self): """Routine to calculate tokamak plasma physics information @@ -303,7 +306,7 @@ def physics(self): # Calculate the poloidal field generated by the plasma current physics_variables.b_plasma_poloidal_average = ( - self.current.calculate_poloidal_field( + self.fields.calculate_poloidal_field( i_plasma_current=physics_variables.i_plasma_current, ip=physics_variables.plasma_current, q95=physics_variables.q95, diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py new file mode 100644 index 000000000..3b99ea1a3 --- /dev/null +++ b/process/models/physics/plasma_fields.py @@ -0,0 +1,84 @@ +import logging + +import numpy as np + +from process.core import constants +from process.data_structure import ( + physics_variables, +) +from process.models.physics.plasma_current import PlasmaCurrent + +logger = logging.getLogger(__name__) + + +class PlasmaFields: + def __init__(self): + self.outfile = constants.NOUT + self.mfile = constants.MFILE + self.current = PlasmaCurrent() + + def calculate_poloidal_field( + self, + i_plasma_current: int, + ip: float, + q95: float, + aspect: float, + eps: float, + b_plasma_toroidal_on_axis: float, + kappa: float, + delta: float, + perim: float, + ) -> float: + """Function to calculate poloidal field from the plasma current + + This function calculates the poloidal field from the plasma current in Tesla, + using a simple calculation using Ampere's law for conventional + tokamaks, or for TARTs, a scaling from Peng, Galambos and + Shipe (1992). + + Parameters + ---------- + i_plasma_current : + current scaling model to use + ip : + plasma current (A) + q95 : + 95% flux surface safety factor + aspect : + plasma aspect ratio + eps : + inverse aspect ratio + b_plasma_toroidal_on_axis : + toroidal field on axis (T) + kappa : + plasma elongation + delta : + plasma triangularity + perim : + plasma perimeter (m) + + Returns + ------- + : + poloidal field in Tesla + + + References + ---------- + - J D Galambos, STAR Code : Spherical Tokamak Analysis and Reactor Code, + unpublished internal Oak Ridge document + - Peng, Y. K. M., Galambos, J. D., & Shipe, P. C. (1992). + 'Small Tokamaks for Fusion Technology Testing'. Fusion Technology, 21(3P2A), + 1729-1738. https://doi.org/10.13182/FST92-A29971 + + """ + # Use Ampere's law using the plasma poloidal cross-section + if i_plasma_current != 2: + return constants.RMU0 * ip / perim + # Use the relation from Peng, Galambos and Shipe (1992) [STAR code] otherwise + ff1, ff2, _, _ = self.current._plascar_bpol(aspect, eps, kappa, delta) + + # Transform q95 to qbar + qbar = q95 * 1.3e0 * (1.0e0 - physics_variables.eps) ** 0.6e0 + + return b_plasma_toroidal_on_axis * (ff1 + ff2) / (2.0 * np.pi * qbar) From 30824c21a83313fb44cf9b3da27c179a86a163df Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 24 Mar 2026 16:06:43 +0000 Subject: [PATCH 02/14] Rename calculate_poloidal_field to calculate_surface_averaged_poloidal_field for clarity and consistency --- .../source/physics-models/plasma_current/plasma_current.md | 2 +- process/models/physics/physics.py | 2 +- process/models/physics/plasma_fields.py | 4 +++- tests/unit/test_physics.py | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/documentation/source/physics-models/plasma_current/plasma_current.md b/documentation/source/physics-models/plasma_current/plasma_current.md index 9a635a43e..f672aa245 100644 --- a/documentation/source/physics-models/plasma_current/plasma_current.md +++ b/documentation/source/physics-models/plasma_current/plasma_current.md @@ -523,7 +523,7 @@ $$ -------------- -## Plasma Current Poloidal Field | `calculate_poloidal_field()` +## Plasma Current Poloidal Field | `calculate_surface_averaged_poloidal_field()` For calculating the poloidal magnetic field created due to the presence of the plasma current, [Ampere's law](https://en.wikipedia.org/wiki/Amp%C3%A8re%27s_circuital_law) can be used. In this case the plasma edge average poloidal field is simply returned as: diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index e08f6fbea..6f836ba10 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -306,7 +306,7 @@ def physics(self): # Calculate the poloidal field generated by the plasma current physics_variables.b_plasma_poloidal_average = ( - self.fields.calculate_poloidal_field( + self.fields.calculate_surface_averaged_poloidal_field( i_plasma_current=physics_variables.i_plasma_current, ip=physics_variables.plasma_current, q95=physics_variables.q95, diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index 3b99ea1a3..b15cc6c8b 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -17,7 +17,7 @@ def __init__(self): self.mfile = constants.MFILE self.current = PlasmaCurrent() - def calculate_poloidal_field( + def calculate_surface_averaged_poloidal_field( self, i_plasma_current: int, ip: float, @@ -82,3 +82,5 @@ def calculate_poloidal_field( qbar = q95 * 1.3e0 * (1.0e0 - physics_variables.eps) ** 0.6e0 return b_plasma_toroidal_on_axis * (ff1 + ff2) / (2.0 * np.pi * qbar) + + \ No newline at end of file diff --git a/tests/unit/test_physics.py b/tests/unit/test_physics.py index 9e4b7157a..9be9ffd8f 100644 --- a/tests/unit/test_physics.py +++ b/tests/unit/test_physics.py @@ -1323,7 +1323,7 @@ def test_calculate_plasma_current_peng(arguments, expected, physics): ), ) def test_calculate_poloidal_field(arguments, expected, physics): - assert physics.current.calculate_poloidal_field(**arguments) == pytest.approx( + assert physics.current.calculate_surface_averaged_poloidal_field(**arguments) == pytest.approx( expected ) @@ -3700,7 +3700,7 @@ def test_calculate_polidal_field( physics, ): """Parametrized test for calculate_poloidal_field.""" - result = physics.current.calculate_poloidal_field( + result = physics.current.calculate_surface_averaged_poloidal_field( i_plasma_current, c_plasma, q95, From 0e4c5a9b57fb610f8c4215938aedd197b2673a1c Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 24 Mar 2026 16:11:59 +0000 Subject: [PATCH 03/14] Add methods to calculate toroidal fields at plasma inboard and outboard midplanes --- process/models/physics/plasma_fields.py | 50 ++++++++++++++++++++++++- tests/unit/test_physics.py | 6 +-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index b15cc6c8b..d0e65c5ca 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -83,4 +83,52 @@ def calculate_surface_averaged_poloidal_field( return b_plasma_toroidal_on_axis * (ff1 + ff2) / (2.0 * np.pi * qbar) - \ No newline at end of file + @staticmethod + def calculate_plasma_inboard_toroidal_field( + b_plasma_toroidal_on_axis: float, + rmajor: float, + rminor: float, + ) -> float: + """Calculate the toroidal field at the plasma inboard midplane (Bᴛ(R₀-a)) + + Parameters + ---------- + b_plasma_toroidal_on_axis : + toroidal field on axis (T) + rmajor : + plasma major radius (m) + rminor : + plasma minor radius (m) + + Returns + ------- + : + toroidal field at the plasma inboard midplane (T) + """ + + return rmajor * b_plasma_toroidal_on_axis / (rmajor - rminor) + + @staticmethod + def calculate_plasma_outboard_toroidal_field( + b_plasma_toroidal_on_axis: float, + rmajor: float, + rminor: float, + ) -> float: + """Calculate the toroidal field at the plasma outboard midplane (Bᴛ(R₀+a)) + + Parameters + ---------- + b_plasma_toroidal_on_axis : + toroidal field on axis (T) + rmajor : + plasma major radius (m) + rminor : + plasma minor radius (m) + + Returns + ------- + : + toroidal field at the plasma outboard midplane (T) + """ + + return rmajor * b_plasma_toroidal_on_axis / (rmajor + rminor) diff --git a/tests/unit/test_physics.py b/tests/unit/test_physics.py index 9be9ffd8f..9625c387d 100644 --- a/tests/unit/test_physics.py +++ b/tests/unit/test_physics.py @@ -1323,9 +1323,9 @@ def test_calculate_plasma_current_peng(arguments, expected, physics): ), ) def test_calculate_poloidal_field(arguments, expected, physics): - assert physics.current.calculate_surface_averaged_poloidal_field(**arguments) == pytest.approx( - expected - ) + assert physics.current.calculate_surface_averaged_poloidal_field( + **arguments + ) == pytest.approx(expected) def test_calculate_beta_limit(): From e8c186bf0ad53fc2e8b76e479a2e906c7a591a19 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 25 Mar 2026 08:49:50 +0000 Subject: [PATCH 04/14] Add methods to calculate toroidal fields in PlasmaFields class --- process/models/physics/physics.py | 37 +++++++++++++------------ process/models/physics/plasma_fields.py | 34 +++++++++++++++++++++++ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 6f836ba10..beb4414e9 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -386,32 +386,33 @@ def physics(self): + physics_variables.b_plasma_poloidal_average**2 ) - # Calculate the toroidal field across the plasma - # Calculate the toroidal field profile across the plasma (1/R dependence) - # Double element size to include both sides of the plasma - rho = np.linspace( - physics_variables.rmajor - physics_variables.rminor, - physics_variables.rmajor + physics_variables.rminor, - 2 * physics_variables.n_plasma_profile_elements, - ) - # Calculate the inboard and outboard toroidal field physics_variables.b_plasma_inboard_toroidal = ( - physics_variables.rmajor - * physics_variables.b_plasma_toroidal_on_axis - / (physics_variables.rmajor - physics_variables.rminor) + self.fields.calculate_plasma_inboard_toroidal_field( + b_plasma_toroidal_on_axis=physics_variables.b_plasma_toroidal_on_axis, + rmajor=physics_variables.rmajor, + rminor=physics_variables.rminor, + ) ) physics_variables.b_plasma_outboard_toroidal = ( - physics_variables.rmajor - * physics_variables.b_plasma_toroidal_on_axis - / (physics_variables.rmajor + physics_variables.rminor) + self.fields.calculate_plasma_outboard_toroidal_field( + b_plasma_toroidal_on_axis=physics_variables.b_plasma_toroidal_on_axis, + rmajor=physics_variables.rmajor, + rminor=physics_variables.rminor, + ) ) - # Avoid division by zero at the magnetic axis - rho = np.where(rho == 0, 1e-10, rho) + # Calculate the toroidal field across the plasma + # Calculate the toroidal field profile across the plasma (1/R dependence) + # Double element size to include both sides of the plasma physics_variables.b_plasma_toroidal_profile = ( - physics_variables.rmajor * physics_variables.b_plasma_toroidal_on_axis / rho + self.fields.calculate_toroidal_field_profile( + b_plasma_toroidal_on_axis=physics_variables.b_plasma_toroidal_on_axis, + rmajor=physics_variables.rmajor, + rminor=physics_variables.rminor, + n_plasma_profile_elements=physics_variables.n_plasma_profile_elements, + ) ) # ============================================ diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index d0e65c5ca..d3c12cc15 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -132,3 +132,37 @@ def calculate_plasma_outboard_toroidal_field( """ return rmajor * b_plasma_toroidal_on_axis / (rmajor + rminor) + + @staticmethod + def calculate_toroidal_field_profile( + b_plasma_toroidal_on_axis: float, + rmajor: float, + rminor: float, + n_plasma_profile_elements: int, + ) -> np.ndarray: + """Calculate the toroidal field profile across the plasma midplane + + Parameters + ---------- + b_plasma_toroidal_on_axis : + toroidal field on axis (T) + rmajor : + plasma major radius (m) + rminor : + plasma minor radius (m) + n_plasma_profile_elements : + Number of elements to use in the plasma profile calculation + """ + + # Calculate the toroidal field across the plasma + # Calculate the toroidal field profile across the plasma (1/R dependence) + # Double element size to include both sides of the plasma + rho = np.linspace( + rmajor - rminor, + rmajor + rminor, + 2 * n_plasma_profile_elements, + ) + + # Avoid division by zero at the magnetic axis + rho = np.where(rho == 0, 1e-10, rho) + return rmajor * b_plasma_toroidal_on_axis / rho From a46f62b61955e095d4edc0b3ec9530506b09ce90 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 25 Mar 2026 09:05:25 +0000 Subject: [PATCH 05/14] Rename poloidal field variable to surface averaged and update related calculations --- .../physics-models/plasma_beta/plasma_beta.md | 2 +- process/core/io/plot_proc.py | 8 ++--- process/data_structure/physics_variables.py | 8 ++--- process/models/divertor.py | 2 +- process/models/physics/physics.py | 34 +++++++++---------- process/models/physics/plasma_fields.py | 9 ++--- process/models/stellarator/stellarator.py | 8 ++--- 7 files changed, 35 insertions(+), 36 deletions(-) diff --git a/documentation/source/physics-models/plasma_beta/plasma_beta.md b/documentation/source/physics-models/plasma_beta/plasma_beta.md index 286b260a5..c93f6acbf 100644 --- a/documentation/source/physics-models/plasma_beta/plasma_beta.md +++ b/documentation/source/physics-models/plasma_beta/plasma_beta.md @@ -117,7 +117,7 @@ $$ $$ -\overbrace{\langle \beta_p \rangle_{\text{V}}}^{\texttt{beta_poloidal_thermal_vol_avg}} = \frac{2\mu_0 \overbrace{\langle p_{\text{thermal}} \rangle}^{\texttt{pres_plasma_thermal_vol_avg}}}{\underbrace{\langle B_{\text{P,average}} \rangle^2}_{\texttt{b_plasma_poloidal_average}}} +\overbrace{\langle \beta_p \rangle_{\text{V}}}^{\texttt{beta_poloidal_thermal_vol_avg}} = \frac{2\mu_0 \overbrace{\langle p_{\text{thermal}} \rangle}^{\texttt{pres_plasma_thermal_vol_avg}}}{\underbrace{\langle B_{\text{P,average}} \rangle^2}_{\texttt{b_plasma_surface_poloidal_average}}} $$ ### Volume averaged thermal beta diff --git a/process/core/io/plot_proc.py b/process/core/io/plot_proc.py index 79bc79024..881982184 100644 --- a/process/core/io/plot_proc.py +++ b/process/core/io/plot_proc.py @@ -3116,14 +3116,14 @@ def plot_main_plasma_information( f"$\\mathbf{{Magnetic\\ fields:}}$\n\n" f"Toroidal field at $R_0$, $B_{{T}}$: {mfile.get('b_plasma_toroidal_on_axis', scan=scan):.4f} T \n" f" Ripple at outboard , $\\delta$: {mfile.get('ripple_b_tf_plasma_edge', scan=scan):.2f}% \n" - f"Average poloidal field, $B_{{p}}$: {mfile.get('b_plasma_poloidal_average', scan=scan):.4f} T \n" + f"Surface average poloidal field, $\\langle B_{{p}}(a) \\rangle$: {mfile.get('b_plasma_surface_poloidal_average', scan=scan):.4f} T\n" f"Total field, $B_{{tot}}$: {mfile.get('b_plasma_total', scan=scan):.4f} T \n" f"Vertical field, $B_{{vert}}$: {mfile.get('b_plasma_vertical_required', scan=scan):.4f} T" ) axis.text( - 0.55, - 0.13, + 0.5325, + 0.14, textstr_fields, fontsize=9, verticalalignment="top", @@ -3139,7 +3139,7 @@ def plot_main_plasma_information( # Add magnetic field label axis.text( 0.75, - 0.1, + 0.12, "$B$", fontsize=23, verticalalignment="top", diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index fccd7a829..3f9fbffc2 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -229,8 +229,8 @@ """leading coefficient for NB beta fraction""" -b_plasma_poloidal_average: float = None -"""Plasma average poloidal field (T)""" +b_plasma_surface_poloidal_average: float = None +"""Plasma surface average poloidal field (T)""" b_plasma_toroidal_on_axis: float = None @@ -1441,7 +1441,7 @@ def init_physics_variables(): e_plasma_beta_thermal, \ beta_norm_toroidal, \ betbm0, \ - b_plasma_poloidal_average, \ + b_plasma_surface_poloidal_average, \ b_plasma_toroidal_on_axis, \ b_plasma_toroidal_inboard, \ b_plasma_toroidal_outboard, \ @@ -1730,7 +1730,7 @@ def init_physics_variables(): e_plasma_beta_thermal = 0.0 beta_norm_toroidal = 0.0 betbm0 = 1.5 - b_plasma_poloidal_average = 0.0 + b_plasma_surface_poloidal_average = 0.0 b_plasma_toroidal_on_axis = 5.68 b_plasma_toroidal_inboard = 0.0 b_plasma_toroidal_outboard = 0.0 diff --git a/process/models/divertor.py b/process/models/divertor.py index cf0a76d98..7b883aa2d 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -74,7 +74,7 @@ def run(self, output: bool): pv.rminor, pv.aspect, pv.b_plasma_toroidal_on_axis, - pv.b_plasma_poloidal_average, + pv.b_plasma_surface_poloidal_average, pv.p_plasma_separatrix_mw, dv.f_div_flux_expansion, pv.nd_plasma_separatrix_electron, diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index beb4414e9..9fae7b6ec 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -305,7 +305,7 @@ def physics(self): ) # Calculate the poloidal field generated by the plasma current - physics_variables.b_plasma_poloidal_average = ( + physics_variables.b_plasma_surface_poloidal_average = ( self.fields.calculate_surface_averaged_poloidal_field( i_plasma_current=physics_variables.i_plasma_current, ip=physics_variables.plasma_current, @@ -383,7 +383,7 @@ def physics(self): # Calculate total magnetic field [T] physics_variables.b_plasma_total = np.sqrt( physics_variables.b_plasma_toroidal_on_axis**2 - + physics_variables.b_plasma_poloidal_average**2 + + physics_variables.b_plasma_surface_poloidal_average**2 ) # Calculate the inboard and outboard toroidal field @@ -618,7 +618,7 @@ def physics(self): ) = reactions.beam_fusion( physics_variables.beamfus0, physics_variables.betbm0, - physics_variables.b_plasma_poloidal_average, + physics_variables.b_plasma_surface_poloidal_average, physics_variables.b_plasma_toroidal_on_axis, current_drive_variables.c_beam_total, physics_variables.nd_plasma_electrons_vol_avg, @@ -694,7 +694,7 @@ def physics(self): ) physics_variables.beta_fast_alpha = self.beta.fast_alpha_beta( - b_plasma_poloidal_average=physics_variables.b_plasma_poloidal_average, + b_plasma_poloidal_average=physics_variables.b_plasma_surface_poloidal_average, b_plasma_toroidal_on_axis=physics_variables.b_plasma_toroidal_on_axis, nd_plasma_electrons_vol_avg=physics_variables.nd_plasma_electrons_vol_avg, nd_plasma_fuel_ions_vol_avg=physics_variables.nd_plasma_fuel_ions_vol_avg, @@ -2042,15 +2042,15 @@ def outplas(self): ) po.ovarrf( self.outfile, - "Average poloidal field (T)", - "(b_plasma_poloidal_average)", - physics_variables.b_plasma_poloidal_average, + "Plasma surface averaged poloidal field (T)", + "(b_plasma_surface_poloidal_average)", + physics_variables.b_plasma_surface_poloidal_average, "OP ", ) po.ovarrf( self.outfile, - "Total field (sqrt(b_plasma_poloidal_average^2 + b_plasma_toroidal_on_axis^2)) (T)", + "Total field (sqrt(b_plasma_surface_poloidal_average^2 + b_plasma_toroidal_on_axis^2)) (T)", "(b_plasma_total)", physics_variables.b_plasma_total, "OP ", @@ -3521,7 +3521,7 @@ def run(self): # Calculate physics_variables.beta poloidal [-] physics_variables.beta_poloidal_vol_avg = self.calculate_poloidal_beta( b_plasma_total=physics_variables.b_plasma_total, - b_plasma_poloidal_average=physics_variables.b_plasma_poloidal_average, + b_plasma_poloidal_average=physics_variables.b_plasma_surface_poloidal_average, beta=physics_variables.beta_total_vol_avg, ) @@ -3539,7 +3539,7 @@ def run(self): physics_variables.beta_thermal_vol_avg * ( physics_variables.b_plasma_total - / physics_variables.b_plasma_poloidal_average + / physics_variables.b_plasma_surface_poloidal_average ) ** 2 ) @@ -3589,7 +3589,7 @@ def run(self): physics_variables.beta_norm_total * ( physics_variables.b_plasma_total - / physics_variables.b_plasma_poloidal_average + / physics_variables.b_plasma_surface_poloidal_average ) ** 2 ) @@ -4353,13 +4353,11 @@ def run(self): self.calculate_internal_inductance_menard(kappa=physics_variables.kappa) ) - physics_variables.ind_plasma_internal_norm_iter_3 = ( - self.calculate_normalised_internal_inductance_iter_3( - b_plasma_poloidal_vol_avg=physics_variables.b_plasma_poloidal_average, - c_plasma=physics_variables.plasma_current, - vol_plasma=physics_variables.vol_plasma, - rmajor=physics_variables.rmajor, - ) + physics_variables.ind_plasma_internal_norm_iter_3 = self.calculate_normalised_internal_inductance_iter_3( + b_plasma_poloidal_vol_avg=physics_variables.b_plasma_surface_poloidal_average, + c_plasma=physics_variables.plasma_current, + vol_plasma=physics_variables.vol_plasma, + rmajor=physics_variables.rmajor, ) # Calculate ind_plasma_internal_norm based on i_ind_plasma_internal_norm diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index d3c12cc15..c622ceb4a 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -29,9 +29,9 @@ def calculate_surface_averaged_poloidal_field( delta: float, perim: float, ) -> float: - """Function to calculate poloidal field from the plasma current + """Function to calculate surface-averaged poloidal field (⟨Bₚ(a)⟩) from the plasma current - This function calculates the poloidal field from the plasma current in Tesla, + This function calculates the surface-averaged poloidal field from the plasma current in Tesla, using a simple calculation using Ampere's law for conventional tokamaks, or for TARTs, a scaling from Peng, Galambos and Shipe (1992). @@ -60,7 +60,7 @@ def calculate_surface_averaged_poloidal_field( Returns ------- : - poloidal field in Tesla + surface-averaged poloidal field in Tesla ⟨Bₚ(a)⟩ References @@ -72,7 +72,8 @@ def calculate_surface_averaged_poloidal_field( 1729-1738. https://doi.org/10.13182/FST92-A29971 """ - # Use Ampere's law using the plasma poloidal cross-section + # Use Ampere's law using the plasma poloidal cross-section this simply returns + # ⟨Bₚ(a)⟩ if i_plasma_current != 2: return constants.RMU0 * ip / perim # Use the relation from Peng, Galambos and Shipe (1992) [STAR code] otherwise diff --git a/process/models/stellarator/stellarator.py b/process/models/stellarator/stellarator.py index c7e904f16..07d72be63 100644 --- a/process/models/stellarator/stellarator.py +++ b/process/models/stellarator/stellarator.py @@ -1947,7 +1947,7 @@ def st_phys(self, output): # Total field physics_variables.b_plasma_total = np.sqrt( physics_variables.b_plasma_toroidal_on_axis**2 - + physics_variables.b_plasma_poloidal_average**2 + + physics_variables.b_plasma_surface_poloidal_average**2 ) # Check if physics_variables.beta (iteration variable 5) is an iteration variable @@ -1999,7 +1999,7 @@ def st_phys(self, output): ) # Calculate poloidal field using rotation transform - physics_variables.b_plasma_poloidal_average = ( + physics_variables.b_plasma_surface_poloidal_average = ( physics_variables.rminor * physics_variables.b_plasma_toroidal_on_axis / physics_variables.rmajor @@ -2043,7 +2043,7 @@ def st_phys(self, output): ) = reactions.beam_fusion( physics_variables.beamfus0, physics_variables.betbm0, - physics_variables.b_plasma_poloidal_average, + physics_variables.b_plasma_surface_poloidal_average, physics_variables.b_plasma_toroidal_on_axis, current_drive_variables.c_beam_total, physics_variables.nd_plasma_electrons_vol_avg, @@ -2107,7 +2107,7 @@ def st_phys(self, output): ) physics_variables.beta_fast_alpha = self.beta.fast_alpha_beta( - physics_variables.b_plasma_poloidal_average, + physics_variables.b_plasma_surface_poloidal_average, physics_variables.b_plasma_toroidal_on_axis, physics_variables.nd_plasma_electrons_vol_avg, physics_variables.nd_plasma_fuel_ions_vol_avg, From ffb8e5e737a31d8a9ac1db278d58609ad7062858 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 25 Mar 2026 09:11:20 +0000 Subject: [PATCH 06/14] Add PlasmaFields class to unit tests for physics and stellarator --- tests/unit/test_physics.py | 6 ++++-- tests/unit/test_stellarator.py | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_physics.py b/tests/unit/test_physics.py index 9625c387d..5e5111e1d 100644 --- a/tests/unit/test_physics.py +++ b/tests/unit/test_physics.py @@ -39,6 +39,7 @@ ) from process.models.physics.plasma_current import PlasmaCurrent from process.models.physics.plasma_profiles import PlasmaProfile +from process.models.physics.plasma_fields import PlasmaFields @pytest.fixture @@ -66,6 +67,7 @@ def physics(): PlasmaConfinementTime(), PlasmaConfinementTransition(), PlasmaCurrent(), + PlasmaFields(), ) @@ -1323,7 +1325,7 @@ def test_calculate_plasma_current_peng(arguments, expected, physics): ), ) def test_calculate_poloidal_field(arguments, expected, physics): - assert physics.current.calculate_surface_averaged_poloidal_field( + assert physics.fields.calculate_surface_averaged_poloidal_field( **arguments ) == pytest.approx(expected) @@ -3700,7 +3702,7 @@ def test_calculate_polidal_field( physics, ): """Parametrized test for calculate_poloidal_field.""" - result = physics.current.calculate_surface_averaged_poloidal_field( + result = physics.fields.calculate_surface_averaged_poloidal_field( i_plasma_current, c_plasma, q95, diff --git a/tests/unit/test_stellarator.py b/tests/unit/test_stellarator.py index 2c5dfe595..c5aa1194b 100644 --- a/tests/unit/test_stellarator.py +++ b/tests/unit/test_stellarator.py @@ -41,6 +41,7 @@ ) from process.models.physics.plasma_current import PlasmaCurrent from process.models.physics.plasma_profiles import PlasmaProfile +from process.models.physics.plasma_fields import PlasmaFields from process.models.power import Power from process.models.stellarator.build import st_build from process.models.stellarator.coils.coils import bmax_from_awp, intersect @@ -98,6 +99,7 @@ def stellarator(): PlasmaConfinementTime(), PlasmaConfinementTransition(), PlasmaCurrent(), + PlasmaFields(), ), Neoclassics(), plasma_beta=PlasmaBeta(), From 7ba4a6ea569c30da36a219e2740b9e09097dcc4a Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 26 Mar 2026 11:32:16 +0000 Subject: [PATCH 07/14] Add Numba JIT compilation to toroidal field calculation methods in PlasmaFields class --- process/models/physics/plasma_fields.py | 4 ++++ tests/unit/test_physics.py | 2 +- tests/unit/test_stellarator.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index c622ceb4a..0e055b0f1 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -1,5 +1,6 @@ import logging +import numba as nb import numpy as np from process.core import constants @@ -85,6 +86,7 @@ def calculate_surface_averaged_poloidal_field( return b_plasma_toroidal_on_axis * (ff1 + ff2) / (2.0 * np.pi * qbar) @staticmethod + @nb.jit(cache=True) def calculate_plasma_inboard_toroidal_field( b_plasma_toroidal_on_axis: float, rmajor: float, @@ -110,6 +112,7 @@ def calculate_plasma_inboard_toroidal_field( return rmajor * b_plasma_toroidal_on_axis / (rmajor - rminor) @staticmethod + @nb.jit(cache=True) def calculate_plasma_outboard_toroidal_field( b_plasma_toroidal_on_axis: float, rmajor: float, @@ -135,6 +138,7 @@ def calculate_plasma_outboard_toroidal_field( return rmajor * b_plasma_toroidal_on_axis / (rmajor + rminor) @staticmethod + @nb.jit(cache=True) def calculate_toroidal_field_profile( b_plasma_toroidal_on_axis: float, rmajor: float, diff --git a/tests/unit/test_physics.py b/tests/unit/test_physics.py index 5e5111e1d..ca4820fd9 100644 --- a/tests/unit/test_physics.py +++ b/tests/unit/test_physics.py @@ -38,8 +38,8 @@ rether, ) from process.models.physics.plasma_current import PlasmaCurrent -from process.models.physics.plasma_profiles import PlasmaProfile from process.models.physics.plasma_fields import PlasmaFields +from process.models.physics.plasma_profiles import PlasmaProfile @pytest.fixture diff --git a/tests/unit/test_stellarator.py b/tests/unit/test_stellarator.py index c5aa1194b..1cda3d8e5 100644 --- a/tests/unit/test_stellarator.py +++ b/tests/unit/test_stellarator.py @@ -40,8 +40,8 @@ PlasmaInductance, ) from process.models.physics.plasma_current import PlasmaCurrent -from process.models.physics.plasma_profiles import PlasmaProfile from process.models.physics.plasma_fields import PlasmaFields +from process.models.physics.plasma_profiles import PlasmaProfile from process.models.power import Power from process.models.stellarator.build import st_build from process.models.stellarator.coils.coils import bmax_from_awp, intersect From 3112c55b695a77b36066a258d9d3143df67097b3 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 26 Mar 2026 11:34:59 +0000 Subject: [PATCH 08/14] Add documentation for Magnetic Fields in PlasmaFields class --- .../source/physics-models/plasma_magnetic_fields.md | 1 + mkdocs.yml | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 documentation/source/physics-models/plasma_magnetic_fields.md diff --git a/documentation/source/physics-models/plasma_magnetic_fields.md b/documentation/source/physics-models/plasma_magnetic_fields.md new file mode 100644 index 000000000..0d01be5cf --- /dev/null +++ b/documentation/source/physics-models/plasma_magnetic_fields.md @@ -0,0 +1 @@ +# Magnetic Fields | `PlasmaFields()` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 6ccdc0a65..735c8778c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -48,6 +48,7 @@ nav: - Overview: physics-models/fusion_reactions/plasma_reactions.md - Beam reactions: physics-models/fusion_reactions/beam_reactions.md - Bosch-Hale Methods: physics-models/fusion_reactions/plasma_bosch_hale.md + - Magnetic Fields: physics-models/plasma_magnetic_fields.md - Beta Limit: - Overview: physics-models/plasma_beta/plasma_beta.md - Fast Alpha: physics-models/plasma_beta/plasma_alpha_beta_contribution.md @@ -217,9 +218,9 @@ plugins: - literate-nav - git-revision-date-localized: enable_creation_date: true - - mkdocs-jupyter: - execute: true - ignore: [scripts/**/*.py] +# - mkdocs-jupyter: +# execute: true +# ignore: [scripts/**/*.py] - mkdocstrings: handlers: python: From 41ac69d65b01ddafb964593367d4b9c2b1965d81 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 26 Mar 2026 14:03:40 +0000 Subject: [PATCH 09/14] Enhance documentation for Magnetic Fields in PlasmaFields class, adding detailed explanations for toroidal and poloidal fields, including calculation methods and equations. --- .../physics-models/plasma_magnetic_fields.md | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/documentation/source/physics-models/plasma_magnetic_fields.md b/documentation/source/physics-models/plasma_magnetic_fields.md index 0d01be5cf..32e4b0fd1 100644 --- a/documentation/source/physics-models/plasma_magnetic_fields.md +++ b/documentation/source/physics-models/plasma_magnetic_fields.md @@ -1 +1,61 @@ -# Magnetic Fields | `PlasmaFields()` \ No newline at end of file +# Magnetic Fields | `PlasmaFields()` + +In a tokamak there are two main magnetic fields that we are concerned about, the toroidal magnetic field ($B_{\text{T}}$) and the poloidal magnetic field ($B_{\text{P}}$). The former created from the electric current in the toroidal field coils and the latter from the toroidal plasma current. + + + +## Toroidal Field + +In `PROCESS` the toroidal magnetic field at the plasma centre $(B_{\text{T}}(R_0))$ (`b_plasma_toroidal_on_axis`) is normally an iteration variable and is a key paramter is most plasma physics and engineering models. + +The toroidal field decreases as $\propto \frac{1}{R}$ from the edge of the inboard toroidal field coil winding pack across the plasma. + +------------------------------------ + +### Plasma Inboard Toroidal Field | `calculate_plasma_inboard_toroidal_field()` + +The toroidal field at the plasma inboard is given as: + +$$ +\overbrace{B_{\text{T}}(R_0-a)}^{\texttt{b_plasma_inboard_toroidal}} = \frac{R_0 B_{\text{T}}(R_0)}{R_0 -a} +$$ + +------------------------------------ + +### Plasma Outboard Toroidal Field | `calculate_plasma_outboard_toroidal_field()` + +The toroidal field at the plasma outboard is given as: + +$$ +\overbrace{B_{\text{T}}(R_0+a)}^{\texttt{b_plasma_outboard_toroidal}} = \frac{R_0 B_{\text{T}}(R_0)}{R_0 +a} +$$ + +------------------------------------ + +### Plasma Toroidal Field Profile | `calculate_toroidal_field_profile()` + +The full toroidal profile across the plasma can be given as: + +$$ +\overbrace{B_{\text{T}}(\rho)}^{\texttt{b_plasma_toroidal_profile}} = \frac{R_0 B_{\text{T}}(R_0)}{\rho} +$$ + +------------------------------------ + +## Poloidal Field + +The poloidal field in `PROCESS` is always an output as it is calculated directly from the plasma current. Currently only the average poloidal field on the plasma surface can be calculated. To know the poloidal field within the plasma the Grad-Sharanov equation must be solved, which is beyond the scope of most systems codes. This would provide a correct toroidal current density profile that can be used to find the poloidal field at any point inside the plasma. + +------------------------------------ + +### Plasma Surface Averaged Field | `calculate_surface_averaged_poloidal_field()` + +As the total toroidal plasma current is calculated, the Biot-Savart law can be used to find the poloidal field at the plasma surface by using the plasmas calculated poloidal perimeter: + +$$ +\overbrace{\langle B_{\text{p}} (a) \rangle}^{\texttt{b_plasma_surface_poloidal_average}} = \frac{I_{\text{p}}}{L_{\text{plasma,perimeter}}} +$$ + +As most plasmas are non ciruclar, the poloidal field thus varies with poloidal angle so only the average value can be inferred from this method. If the plasma was a perfect torus then this would be the poloidal field at any point of the plasma surface + +------------------------------------ \ No newline at end of file From 03137bb5131430689ed92ec433d2d6b9a66af793 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 26 Mar 2026 14:17:17 +0000 Subject: [PATCH 10/14] Add total magnetic field calculation method to PlasmaFields class and update usage in Physics class --- .../physics-models/plasma_magnetic_fields.md | 10 ++++++++- mkdocs.yml | 6 +++--- process/models/physics/physics.py | 6 +++--- process/models/physics/plasma_fields.py | 21 +++++++++++++++++++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/documentation/source/physics-models/plasma_magnetic_fields.md b/documentation/source/physics-models/plasma_magnetic_fields.md index 32e4b0fd1..2e81a3776 100644 --- a/documentation/source/physics-models/plasma_magnetic_fields.md +++ b/documentation/source/physics-models/plasma_magnetic_fields.md @@ -58,4 +58,12 @@ $$ As most plasmas are non ciruclar, the poloidal field thus varies with poloidal angle so only the average value can be inferred from this method. If the plasma was a perfect torus then this would be the poloidal field at any point of the plasma surface ------------------------------------- \ No newline at end of file +------------------------------------ + +## Total Field | `calculate_total_magnetic_field()` + +As the poloidal and toroidal fields are orthogonal to eachother, the total magnetic field at any point is simply: + +$$ +\overbrace{B_{\text{total}}}^{\texttt{b_plasma_total}} = \sqrt{B_{\text{T}}^2 + B_{\text{P}}^2} +$$ diff --git a/mkdocs.yml b/mkdocs.yml index 735c8778c..18d77d888 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -218,9 +218,9 @@ plugins: - literate-nav - git-revision-date-localized: enable_creation_date: true -# - mkdocs-jupyter: -# execute: true -# ignore: [scripts/**/*.py] + - mkdocs-jupyter: + execute: true + ignore: [scripts/**/*.py] - mkdocstrings: handlers: python: diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 9fae7b6ec..48fee0add 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -381,9 +381,9 @@ def physics(self): self.plasma_profile.run() # Calculate total magnetic field [T] - physics_variables.b_plasma_total = np.sqrt( - physics_variables.b_plasma_toroidal_on_axis**2 - + physics_variables.b_plasma_surface_poloidal_average**2 + physics_variables.b_plasma_total = self.fields.calculate_total_magnetic_field( + b_plasma_toroidal=physics_variables.b_plasma_toroidal_on_axis, + b_plasma_poloidal=physics_variables.b_plasma_surface_poloidal_average, ) # Calculate the inboard and outboard toroidal field diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index 0e055b0f1..7c5491c2d 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -171,3 +171,24 @@ def calculate_toroidal_field_profile( # Avoid division by zero at the magnetic axis rho = np.where(rho == 0, 1e-10, rho) return rmajor * b_plasma_toroidal_on_axis / rho + + @staticmethod + @nb.jit(cache=True) + def calculate_total_magnetic_field( + b_plasma_toroidal: float, b_plasma_poloidal: float + ) -> float: + """Calculate the total magnetic field at the plasma edge + + Parameters + ---------- + b_plasma_toroidal : + toroidal field at point of interest (T) + b_plasma_poloidal : + poloidal field at point of interest (T) + + Returns + ------- + : + total magnetic field at the plasma edge (T) + """ + return np.sqrt(b_plasma_toroidal**2 + b_plasma_poloidal**2) From 012c0e7587512e72c4c1d2df72f8053fefdf32c8 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 26 Mar 2026 14:17:56 +0000 Subject: [PATCH 11/14] Refactor plasma_current method names for consistency in PlasmaFields class --- .../source/physics-models/plasma_current/plasma_current.md | 2 +- process/models/physics/plasma_current.py | 6 +++--- process/models/physics/plasma_fields.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/documentation/source/physics-models/plasma_current/plasma_current.md b/documentation/source/physics-models/plasma_current/plasma_current.md index f672aa245..f3e87f8e7 100644 --- a/documentation/source/physics-models/plasma_current/plasma_current.md +++ b/documentation/source/physics-models/plasma_current/plasma_current.md @@ -133,7 +133,7 @@ $$ I_{\text{p}} = \frac{5a B_{\text{T}}\kappa}{2\pi^2 \bar{q}}(F_1+F_2)\left(\frac{\arcsin{E_1}}{E_1}+\frac{\arcsin{E_2}}{E_2}\right) $$ -The values of $F_1$, $F_2$, $d_1$ & $d_2$ are first calculated from the [`_plascar_bpol()`](#_plasc_bpol) function. +The values of $F_1$, $F_2$, $d_1$ & $d_2$ are first calculated from the [`plascar_bpol()`](#_plasc_bpol) function. The values of $E_1$ & $E_2$ are then calculated such as diff --git a/process/models/physics/plasma_current.py b/process/models/physics/plasma_current.py index b8abe4a65..7db2147fc 100644 --- a/process/models/physics/plasma_current.py +++ b/process/models/physics/plasma_current.py @@ -510,7 +510,7 @@ def calculate_cyclindrical_plasma_current( ) @staticmethod - def _plascar_bpol( + def plascar_bpol( aspect: float, eps: float, kappa: float, delta: float ) -> tuple[float, float, float, float]: """Calculate the poloidal field coefficients for determining the plasma current @@ -632,7 +632,7 @@ def calculate_poloidal_field( if i_plasma_current != 2: return constants.RMU0 * ip / perim # Use the relation from Peng, Galambos and Shipe (1992) [STAR code] otherwise - ff1, ff2, _, _ = self._plascar_bpol(aspect, eps, kappa, delta) + ff1, ff2, _, _ = self.plascar_bpol(aspect, eps, kappa, delta) # Transform q95 to qbar qbar = q95 * 1.3e0 * (1.0e0 - physics_variables.eps) ** 0.6e0 @@ -706,7 +706,7 @@ def calculate_plasma_current_peng( # Transform q95 to qbar qbar = q95 * 1.3e0 * (1.0e0 - physics_variables.eps) ** 0.6e0 - ff1, ff2, d1, d2 = self._plascar_bpol(aspect, eps, kappa, delta) + ff1, ff2, d1, d2 = self.plascar_bpol(aspect, eps, kappa, delta) e1 = (2.0 * kappa) / (d1 * (1.0 + delta)) e2 = (2.0 * kappa) / (d2 * (1.0 - delta)) diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index 7c5491c2d..ad37fb31e 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -78,7 +78,7 @@ def calculate_surface_averaged_poloidal_field( if i_plasma_current != 2: return constants.RMU0 * ip / perim # Use the relation from Peng, Galambos and Shipe (1992) [STAR code] otherwise - ff1, ff2, _, _ = self.current._plascar_bpol(aspect, eps, kappa, delta) + ff1, ff2, _, _ = self.current.plascar_bpol(aspect, eps, kappa, delta) # Transform q95 to qbar qbar = q95 * 1.3e0 * (1.0e0 - physics_variables.eps) ** 0.6e0 From 2105bd303a1d259970e8a1230a350f1dd564698e Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 30 Mar 2026 09:15:21 +0100 Subject: [PATCH 12/14] Fix typos in poloidal field description and total magnetic field calculation in documentation --- documentation/source/physics-models/plasma_magnetic_fields.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/source/physics-models/plasma_magnetic_fields.md b/documentation/source/physics-models/plasma_magnetic_fields.md index 2e81a3776..583a1a463 100644 --- a/documentation/source/physics-models/plasma_magnetic_fields.md +++ b/documentation/source/physics-models/plasma_magnetic_fields.md @@ -56,13 +56,13 @@ $$ \overbrace{\langle B_{\text{p}} (a) \rangle}^{\texttt{b_plasma_surface_poloidal_average}} = \frac{I_{\text{p}}}{L_{\text{plasma,perimeter}}} $$ -As most plasmas are non ciruclar, the poloidal field thus varies with poloidal angle so only the average value can be inferred from this method. If the plasma was a perfect torus then this would be the poloidal field at any point of the plasma surface +As most plasmas are non-circular, the poloidal field thus varies with poloidal angle so only the average value can be inferred from this method. If the plasma was a perfect torus then this would be the poloidal field at any point of the plasma surface ------------------------------------ ## Total Field | `calculate_total_magnetic_field()` -As the poloidal and toroidal fields are orthogonal to eachother, the total magnetic field at any point is simply: +As the poloidal and toroidal fields are orthogonal to each other, the total magnetic field at any point is simply: $$ \overbrace{B_{\text{total}}}^{\texttt{b_plasma_total}} = \sqrt{B_{\text{T}}^2 + B_{\text{P}}^2} From bf6559389acdc8f522f6413566e2ddd8c48d5744 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 31 Mar 2026 15:19:09 +0100 Subject: [PATCH 13/14] Add output method for magnetic field information in PlasmaFields class --- process/models/physics/physics.py | 54 +++---------------------- process/models/physics/plasma_fields.py | 54 +++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 49 deletions(-) diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 48fee0add..53852f031 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -2006,55 +2006,6 @@ def outplas(self): physics_variables.j_plasma_on_axis, "OP ", ) - po.ovarrf( - self.outfile, - "Vertical field at plasma (T)", - "(b_plasma_vertical_required)", - physics_variables.b_plasma_vertical_required, - "OP ", - ) - - po.ovarrf( - self.outfile, - "Vacuum toroidal field at R (T)", - "(b_plasma_toroidal_on_axis)", - physics_variables.b_plasma_toroidal_on_axis, - ) - po.ovarrf( - self.outfile, - "Toroidal field at plasma inboard (T)", - "(b_plasma_inboard_toroidal)", - physics_variables.b_plasma_inboard_toroidal, - ) - po.ovarrf( - self.outfile, - "Toroidal field at plasma outboard (T)", - "(b_plasma_outboard_toroidal)", - physics_variables.b_plasma_outboard_toroidal, - ) - - for i in range(len(physics_variables.b_plasma_toroidal_profile)): - po.ovarre( - self.mfile, - f"Toroidal field in plasma at point {i}", - f"b_plasma_toroidal_profile{i}", - physics_variables.b_plasma_toroidal_profile[i], - ) - po.ovarrf( - self.outfile, - "Plasma surface averaged poloidal field (T)", - "(b_plasma_surface_poloidal_average)", - physics_variables.b_plasma_surface_poloidal_average, - "OP ", - ) - - po.ovarrf( - self.outfile, - "Total field (sqrt(b_plasma_surface_poloidal_average^2 + b_plasma_toroidal_on_axis^2)) (T)", - "(b_plasma_total)", - physics_variables.b_plasma_total, - "OP ", - ) if stellarator_variables.istell == 0: po.ovarrf( @@ -2099,6 +2050,11 @@ def outplas(self): physics_variables.ind_plasma_internal_norm, "OP ", ) + + po.oblnkl(self.outfile) + self.fields.output_magnetic_field_info() + po.oblnkl(self.outfile) + po.ostars(self.outfile, 110) po.oblnkl(self.outfile) po.ocmmnt(self.outfile, "Plasma normalised internal inductance scalings:") po.oblnkl(self.outfile) diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index ad37fb31e..e9cc5551c 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -4,6 +4,7 @@ import numpy as np from process.core import constants +from process.core import process_output as po from process.data_structure import ( physics_variables, ) @@ -192,3 +193,56 @@ def calculate_total_magnetic_field( total magnetic field at the plasma edge (T) """ return np.sqrt(b_plasma_toroidal**2 + b_plasma_poloidal**2) + + def output_magnetic_field_info(self): + po.oheadr(self.outfile, "Plasma magnetic fields") + + po.ovarrf( + self.outfile, + "Vertical field at plasma (T)", + "(b_plasma_vertical_required)", + physics_variables.b_plasma_vertical_required, + "OP ", + ) + + po.ovarrf( + self.outfile, + "Vacuum toroidal field at R (T)", + "(b_plasma_toroidal_on_axis)", + physics_variables.b_plasma_toroidal_on_axis, + ) + po.ovarrf( + self.outfile, + "Toroidal field at plasma inboard (T)", + "(b_plasma_inboard_toroidal)", + physics_variables.b_plasma_inboard_toroidal, + ) + po.ovarrf( + self.outfile, + "Toroidal field at plasma outboard (T)", + "(b_plasma_outboard_toroidal)", + physics_variables.b_plasma_outboard_toroidal, + ) + + for i in range(len(physics_variables.b_plasma_toroidal_profile)): + po.ovarre( + self.mfile, + f"Toroidal field in plasma at point {i}", + f"b_plasma_toroidal_profile{i}", + physics_variables.b_plasma_toroidal_profile[i], + ) + po.ovarrf( + self.outfile, + "Plasma surface averaged poloidal field (T)", + "(b_plasma_surface_poloidal_average)", + physics_variables.b_plasma_surface_poloidal_average, + "OP ", + ) + + po.ovarrf( + self.outfile, + "Total field (sqrt(b_plasma_surface_poloidal_average^2 + b_plasma_toroidal_on_axis^2)) (T)", + "(b_plasma_total)", + physics_variables.b_plasma_total, + "OP ", + ) From 7ba5f9c0aa47a3a7c5c3264cd97641f8dd52e16e Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 1 Apr 2026 09:49:59 +0100 Subject: [PATCH 14/14] Refactor output method in PlasmaFields class for consistency and update usage in Physics class --- process/models/physics/physics.py | 2 +- process/models/physics/plasma_fields.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 53852f031..bf9688d04 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -2052,7 +2052,7 @@ def outplas(self): ) po.oblnkl(self.outfile) - self.fields.output_magnetic_field_info() + self.fields.output() po.oblnkl(self.outfile) po.ostars(self.outfile, 110) po.oblnkl(self.outfile) diff --git a/process/models/physics/plasma_fields.py b/process/models/physics/plasma_fields.py index e9cc5551c..84238ace4 100644 --- a/process/models/physics/plasma_fields.py +++ b/process/models/physics/plasma_fields.py @@ -5,6 +5,7 @@ from process.core import constants from process.core import process_output as po +from process.core.model import Model from process.data_structure import ( physics_variables, ) @@ -13,12 +14,15 @@ logger = logging.getLogger(__name__) -class PlasmaFields: +class PlasmaFields(Model): def __init__(self): self.outfile = constants.NOUT self.mfile = constants.MFILE self.current = PlasmaCurrent() + def run(self): + """Run the model. This model cannot yet be 'run'.""" + def calculate_surface_averaged_poloidal_field( self, i_plasma_current: int, @@ -194,7 +198,7 @@ def calculate_total_magnetic_field( """ return np.sqrt(b_plasma_toroidal**2 + b_plasma_poloidal**2) - def output_magnetic_field_info(self): + def output(self): po.oheadr(self.outfile, "Plasma magnetic fields") po.ovarrf(