From e4116245ccda8bcd5ede9390e9b710ee18444a7d Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 11:51:03 +0100 Subject: [PATCH 01/20] Add DetailedPhysics class for enhanced plasma processing models --- process/main.py | 5 ++++- process/physics.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/process/main.py b/process/main.py index 2b0c1028bd..4b859c8f59 100644 --- a/process/main.py +++ b/process/main.py @@ -94,7 +94,7 @@ ) from process.log import logging_model_handler, show_errors from process.pfcoil import PFCoil -from process.physics import Physics +from process.physics import DetailedPhysics, Physics from process.plasma_geometry import PlasmaGeom from process.plasma_profiles import PlasmaProfile from process.power import Power @@ -678,6 +678,9 @@ def __init__(self): self.physics = Physics( plasma_profile=self.plasma_profile, current_drive=self.current_drive ) + self.physics_detailed = DetailedPhysics( + plasma_profile=self.plasma_profile, current_drive=self.current_drive + ) self.neoclassics = Neoclassics() self.stellarator = Stellarator( availability=self.availability, diff --git a/process/physics.py b/process/physics.py index 3227317fa1..cd8c7550c7 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9057,3 +9057,13 @@ def reinke_tsep(b_plasma_toroidal_on_axis, flh, qstar, rmajor, eps, fgw, kappa, * (eps**0.15 * (1.0 + kappa**2.0) ** 0.34) * (lhat**0.29 * kappa_0 ** (-0.29) * 0.285) ) + + +class DetailedPhysics: + """Class to hold detailed physics models for plasma processing.""" + + def __init__(self, plasma_profile, current_drive): + self.outfile = constants.NOUT + self.mfile = constants.MFILE + self.plasma_profile = plasma_profile + self.current_drive = current_drive From 05538c3b47b847305aa0fb7805bc39b460be9287 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 11:55:32 +0100 Subject: [PATCH 02/20] :sparkle: Add Debye length calculations to DetailedPhysics class --- process/physics.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/process/physics.py b/process/physics.py index cd8c7550c7..f3b98d9ff9 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9067,3 +9067,36 @@ def __init__(self, plasma_profile, current_drive): self.mfile = constants.MFILE self.plasma_profile = plasma_profile self.current_drive = current_drive + + def calculate_debye_length( + self, + temp_plasma_species_kev: float, + nd_plasma_species: float, + ) -> float: + """ + Calculate the Debye length for a plasma. + + :param temp_plasma_species_kev: Species temperature in keV. + :type temp_plasma_species_kev: float + :param nd_plasma_species: Species number density (/m^3). + :type nd_plasma_species: float + + :returns: Debye length in meters. + :rtype: float + """ + return ( + (constants.EPSILON0 * temp_plasma_species_kev) + / (nd_plasma_species * constants.ELECTRON_CHARGE**2) + ) ** 0.5 + + def calculate_debye_length_profile( + self, + temp_plasma_species_profile_kev: np.ndarray, + nd_plasma_species_profile: np.ndarray, + ) -> np.ndarray: + """ + Calculate the Debye length profile for a plasma. + """ + return self.calculate_debye_length( + temp_plasma_species_profile_kev, nd_plasma_species_profile + ) From 2a094580807d573d5e2dd33a5a7b0ea87f2950ee Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 11:57:14 +0100 Subject: [PATCH 03/20] :sparkle: Add electron Debye length profile variable to physics module --- process/data_structure/physics_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index ef365893bc..b71d9e458a 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1320,6 +1320,9 @@ n_charge_plasma_effective_mass_weighted_vol_avg: float = None """Plasma mass-weighted volume averaged plasma effective charge""" +len_plasma_debye_electron_profile: list[float] = None +"""Profile of electron Debye length in plasma (m)""" + def init_physics_module(): """Initialise the physics module""" @@ -1638,6 +1641,7 @@ def init_physics_variables(): global n_charge_plasma_effective_vol_avg global n_charge_plasma_effective_profile global n_charge_plasma_effective_mass_weighted_vol_avg + global len_plasma_debye_electron_profile m_beam_amu = 0.0 m_fuel_amu = 0.0 @@ -1898,3 +1902,4 @@ def init_physics_variables(): n_charge_plasma_effective_vol_avg = 0.0 n_charge_plasma_effective_profile = [] n_charge_plasma_effective_mass_weighted_vol_avg = 0.0 + len_plasma_debye_electron_profile = [] From 2e23b5ee56d55220d5053c89099069b6ee86af3f Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 13:04:25 +0100 Subject: [PATCH 04/20] Add volume averaged electron Debye length variable to physics module --- process/data_structure/physics_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index b71d9e458a..27d071b835 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1323,6 +1323,9 @@ len_plasma_debye_electron_profile: list[float] = None """Profile of electron Debye length in plasma (m)""" +len_plasma_debye_electron_vol_avg: float = None +"""Volume averaged electron Debye length in plasma (m)""" + def init_physics_module(): """Initialise the physics module""" @@ -1642,6 +1645,7 @@ def init_physics_variables(): global n_charge_plasma_effective_profile global n_charge_plasma_effective_mass_weighted_vol_avg global len_plasma_debye_electron_profile + global len_plasma_debye_electron_vol_avg m_beam_amu = 0.0 m_fuel_amu = 0.0 @@ -1903,3 +1907,4 @@ def init_physics_variables(): n_charge_plasma_effective_profile = [] n_charge_plasma_effective_mass_weighted_vol_avg = 0.0 len_plasma_debye_electron_profile = [] + len_plasma_debye_electron_vol_avg = 0.0 From 04220f81d8c21ef0a581e24f01e444870cb1a225 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 13:41:18 +0100 Subject: [PATCH 05/20] Add detailed Debye length calculations and output to physics module --- process/caller.py | 1 + process/io/plot_proc.py | 24 ++++++++++++++++++++++++ process/output.py | 1 + process/physics.py | 41 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/process/caller.py b/process/caller.py index 0a93243a15..b73675f71c 100644 --- a/process/caller.py +++ b/process/caller.py @@ -253,6 +253,7 @@ def _call_models_once(self, xc: np.ndarray) -> None: self.models.build.run() self.models.physics.physics() + self.models.physics_detailed.run() # Toroidal field coil model diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index cfef09d06a..a62037d442 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -12542,6 +12542,30 @@ def plot_ebw_ecrh_coupling_graph(axis, mfile_data, scan): axis.minorticks_on() +def plot_debye_length_profile(axis, mfile_data, scan): + """Plot the Debye length profile on the given axis.""" + len_plasma_debye_electron_profile = [ + mfile_data.data[f"len_plasma_debye_electron_profile{i}"].get_scan(scan) + for i in range(500) + ] + + axis.plot( + np.linspace(0, 1, len(len_plasma_debye_electron_profile)), + len_plasma_debye_electron_profile, + color="blue", + linestyle="-", + label=r"$\lambda_{Debye,e}$", + ) + + axis.set_ylabel("Debye Length [m]") + axis.set_xlabel("$\\rho \\ [r/a]$") + axis.set_yscale("log") + axis.grid(True, which="both", linestyle="--", alpha=0.5) + axis.set_xlim([0, 1.025]) + axis.minorticks_on() + axis.legend() + + def main_plot( fig0, fig1, diff --git a/process/output.py b/process/output.py index 5cd8422f73..765871265b 100644 --- a/process/output.py +++ b/process/output.py @@ -43,6 +43,7 @@ def write(models, _outfile): # Writing the output from physics.f90 into OUT.DAT + MFILE.DAT models.physics.calculate_effective_charge_ionisation_profiles() models.physics.outplas() + models.physics_detailed.output_detailed_physics() # TODO what is this? Not in caller.f90? models.current_drive.output_current_drive() diff --git a/process/physics.py b/process/physics.py index f3b98d9ff9..45f813a1aa 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9068,6 +9068,23 @@ def __init__(self, plasma_profile, current_drive): self.plasma_profile = plasma_profile self.current_drive = current_drive + def run(self): + # --------------------------- + # Debye length calculation + # --------------------------- + + physics_variables.len_plasma_debye_electron_vol_avg = self.calculate_debye_length_profile( + temp_plasma_species_profile_kev=physics_variables.temp_plasma_electron_vol_avg_kev, + nd_plasma_species_profile=physics_variables.nd_plasma_electrons_vol_avg, + ) + + physics_variables.len_plasma_debye_electron_profile = ( + self.calculate_debye_length_profile( + temp_plasma_species_profile_kev=self.plasma_profile.teprofile.profile_y, + nd_plasma_species_profile=self.plasma_profile.neprofile.profile_y, + ) + ) + def calculate_debye_length( self, temp_plasma_species_kev: float, @@ -9085,7 +9102,7 @@ def calculate_debye_length( :rtype: float """ return ( - (constants.EPSILON0 * temp_plasma_species_kev) + (constants.EPSILON0 * temp_plasma_species_kev * constants.KILOELECTRON_VOLT) / (nd_plasma_species * constants.ELECTRON_CHARGE**2) ) ** 0.5 @@ -9100,3 +9117,25 @@ def calculate_debye_length_profile( return self.calculate_debye_length( temp_plasma_species_profile_kev, nd_plasma_species_profile ) + + def output_detailed_physics(self): + """Outputs detailed physics variables to file.""" + + po.oheadr(self.outfile, "Detailed Plasma") + + po.osubhd(self.outfile, "Debye lengths:") + + po.ovarrf( + self.outfile, + "Plasma volume averaged electron Debye length (m)", + "(len_plasma_debye_electron_vol_avg)", + physics_variables.len_plasma_debye_electron_vol_avg, + "OP ", + ) + for i in range(len(physics_variables.len_plasma_debye_electron_profile)): + po.ovarre( + self.mfile, + f"Plasma electron Debye length at point {i}", + f"len_plasma_debye_electron_profile{i}", + physics_variables.len_plasma_debye_electron_profile[i], + ) From 1e1e5b620e108e2d338d00af1b14ef0325a0bf5f Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 13:44:41 +0100 Subject: [PATCH 06/20] :sparkle: Add Lorentz factor and relativistic particle speed calculations to DetailedPhysics class --- process/physics.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/process/physics.py b/process/physics.py index 45f813a1aa..0e0fd691f4 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9118,6 +9118,34 @@ def calculate_debye_length_profile( temp_plasma_species_profile_kev, nd_plasma_species_profile ) + def calculate_lorentz_factor(self, velocity: float) -> float: + """ + Calculate the Lorentz factor for a given velocity. + :param velocity: Velocity in m/s. + :type velocity: float + :returns: Lorentz factor (dimensionless). + :rtype: float + """ + return 1 / (1 - (velocity / constants.SPEED_LIGHT) ** 2) ** 0.5 + + def calculate_relativistic_particle_speed( + self, e_kinetic: float, mass: float + ) -> float: + """ + Calculate the speed of a particle given its kinetic energy and mass using relativistic mechanics. + :param e_kinetic: Kinetic energy in Joules. + :type e_kinetic: float + :param mass: Mass of the particle in kg. + :type mass: float + :returns: Speed of the particle in m/s. + :rtype: float + """ + return ( + constants.SPEED_OF_LIGHT + * (1 - (1 / ((e_kinetic / (mass * constants.SPEED_OF_LIGHT**2)) + 1) ** 2)) + ** 0.5 + ) + def output_detailed_physics(self): """Outputs detailed physics variables to file.""" From dafdc2069547687c0e4b663c85df49b268edab5e Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 13:46:21 +0100 Subject: [PATCH 07/20] :sparkle: Add electron thermal velocity profile variable to physics module --- process/data_structure/physics_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 27d071b835..686f7adec9 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1326,6 +1326,9 @@ len_plasma_debye_electron_vol_avg: float = None """Volume averaged electron Debye length in plasma (m)""" +vel_plasma_electron_profile: list[float] = None +"""Profile of electron thermal velocity in plasma (m/s)""" + def init_physics_module(): """Initialise the physics module""" @@ -1646,6 +1649,7 @@ def init_physics_variables(): global n_charge_plasma_effective_mass_weighted_vol_avg global len_plasma_debye_electron_profile global len_plasma_debye_electron_vol_avg + global vel_plasma_electron_profile m_beam_amu = 0.0 m_fuel_amu = 0.0 @@ -1908,3 +1912,4 @@ def init_physics_variables(): n_charge_plasma_effective_mass_weighted_vol_avg = 0.0 len_plasma_debye_electron_profile = [] len_plasma_debye_electron_vol_avg = 0.0 + vel_plasma_electron_profile = [] From baf610b8975687067714e5115105e8fac9f614cb Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 13:56:32 +0100 Subject: [PATCH 08/20] :sparkle: Add electron thermal velocity profile calculation and plotting functionality --- process/constants.py | 2 +- process/io/plot_proc.py | 23 +++++++++++++++++++++++ process/physics.py | 26 ++++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/process/constants.py b/process/constants.py index 0f853ad282..a0d74193fe 100644 --- a/process/constants.py +++ b/process/constants.py @@ -189,7 +189,7 @@ https://physics.nist.gov/cgi-bin/Compositions/stand_alone.pl?ele=W """ -SPEED_LIGHT = 299792458 +SPEED_LIGHT = 299792458.0 """Speed of light in vacuum (c) [m/s] Reference: National Institute of Standards and Technology (NIST) https://physics.nist.gov/cgi-bin/cuu/Value?c|search_for=light diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index a62037d442..0e7c379ddb 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -12566,6 +12566,29 @@ def plot_debye_length_profile(axis, mfile_data, scan): axis.legend() +def plot_velocity_profile(axis, mfile_data, scan): + """Plot the electron thermal velocity profile on the given axis.""" + vel_plasma_electron_profile = [ + mfile_data.data[f"vel_plasma_electron_profile{i}"].get_scan(scan) + for i in range(500) + ] + + axis.plot( + np.linspace(0, 1, len(vel_plasma_electron_profile)), + vel_plasma_electron_profile, + color="blue", + linestyle="-", + label=r"$v_{e}$", + ) + + axis.set_ylabel("Velocity [m/s]") + axis.set_xlabel("$\\rho \\ [r/a]$") + axis.grid(True, which="both", linestyle="--", alpha=0.5) + axis.set_xlim([0, 1.025]) + axis.minorticks_on() + axis.legend() + + def main_plot( fig0, fig1, diff --git a/process/physics.py b/process/physics.py index 0e0fd691f4..2698dd4936 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9085,6 +9085,18 @@ def run(self): ) ) + # ============================ + # Particle relativistic speeds + # ============================ + + physics_variables.vel_plasma_electron_profile = ( + self.calculate_relativistic_particle_speed( + e_kinetic=self.plasma_profile.teprofile.profile_y + * constants.KILOELECTRON_VOLT, + mass=constants.ELECTRON_MASS, + ) + ) + def calculate_debye_length( self, temp_plasma_species_kev: float, @@ -9141,8 +9153,8 @@ def calculate_relativistic_particle_speed( :rtype: float """ return ( - constants.SPEED_OF_LIGHT - * (1 - (1 / ((e_kinetic / (mass * constants.SPEED_OF_LIGHT**2)) + 1) ** 2)) + constants.SPEED_LIGHT + * (1 - (1 / ((e_kinetic / (mass * constants.SPEED_LIGHT**2)) + 1) ** 2)) ** 0.5 ) @@ -9167,3 +9179,13 @@ def output_detailed_physics(self): f"len_plasma_debye_electron_profile{i}", physics_variables.len_plasma_debye_electron_profile[i], ) + + po.osubhd(self.outfile, "Velocities:") + + for i in range(len(physics_variables.vel_plasma_electron_profile)): + po.ovarre( + self.mfile, + f"Plasma electron thermal velocity at point {i}", + f"vel_plasma_electron_profile{i}", + physics_variables.vel_plasma_electron_profile[i], + ) From 8a7ba0d60703ee768c3be36ec031be2172f5a776 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 16:08:49 +0100 Subject: [PATCH 09/20] Add Planck's constant and new physics calculations to DetailedPhysics class --- process/constants.py | 3 +++ process/physics.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/process/constants.py b/process/constants.py index a0d74193fe..7954eff803 100644 --- a/process/constants.py +++ b/process/constants.py @@ -195,6 +195,9 @@ https://physics.nist.gov/cgi-bin/cuu/Value?c|search_for=light """ +PLANCK_CONSTANT = 6.62607015e-34 +"""Planck's constant [J.s]""" + D_T_ENERGY = ( (DEUTERON_MASS + TRITON_MASS) - (ALPHA_MASS + NEUTRON_MASS) ) * SPEED_LIGHT**2 diff --git a/process/physics.py b/process/physics.py index 2698dd4936..78164a1cc6 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9158,6 +9158,44 @@ def calculate_relativistic_particle_speed( ** 0.5 ) + def calculate_coulomb_log_from_impact( + self, impact_param_max: float, impact_param_min: float + ) -> float: + """ + Calculate the Coulomb logarithm from maximum and minimum impact parameters. + :param impact_param_max: Maximum impact parameter in meters. + :type impact_param_max: float + :param impact_param_min: Minimum impact parameter in meters. + :type impact_param_min: float + :returns: Coulomb logarithm (dimensionless). + :rtype: float + """ + return np.log(impact_param_max / impact_param_min) + + def calculate_classical_distance_of_closest_approach( + self, + charge1: float, + charge2: float, + e_kinetic: float, + ) -> float: + """ """ + + return (charge1 * charge2 * constants.ELECTRON_CHARGE**2) / ( + 4 * np.pi * constants.EPSILON0 * e_kinetic + ) + + def calculate_debroglie_wavelength(self, mass: float, velocity: float) -> float: + """ + Calculate the de Broglie wavelength of a particle. + :param mass: Mass of the particle in kg. + :type mass: float + :param velocity: Velocity of the particle in m/s. + :type velocity: float + :returns: de Broglie wavelength in meters. + :rtype: float + """ + return constants.PLANCK_CONSTANT / (mass * velocity) + def output_detailed_physics(self): """Outputs detailed physics variables to file.""" From dcebf670b8eed06af7a4123a88c9fe8518ed3fbc Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 16:17:46 +0100 Subject: [PATCH 10/20] :sparkle: Add electron-electron Coulomb logarithm profile variable to physics module --- process/data_structure/physics_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 686f7adec9..e16bde26ba 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1329,6 +1329,9 @@ vel_plasma_electron_profile: list[float] = None """Profile of electron thermal velocity in plasma (m/s)""" +plasma_coulomb_log_electron_electron_profile: list[float] = None +"""Profile of electron-electron Coulomb logarithm in plasma""" + def init_physics_module(): """Initialise the physics module""" @@ -1650,6 +1653,7 @@ def init_physics_variables(): global len_plasma_debye_electron_profile global len_plasma_debye_electron_vol_avg global vel_plasma_electron_profile + global plasma_coulomb_log_electron_electron_profile m_beam_amu = 0.0 m_fuel_amu = 0.0 @@ -1913,3 +1917,4 @@ def init_physics_variables(): len_plasma_debye_electron_profile = [] len_plasma_debye_electron_vol_avg = 0.0 vel_plasma_electron_profile = [] + plasma_coulomb_log_electron_electron_profile = [] From 05528bd81f0e731c7d32a8a646c8c0ce30a7f700 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 16:40:12 +0100 Subject: [PATCH 11/20] :sparkle: Add plasma frequency calculation to DetailedPhysics class --- process/physics.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/process/physics.py b/process/physics.py index 78164a1cc6..5beaed9059 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9195,6 +9195,28 @@ def calculate_debroglie_wavelength(self, mass: float, velocity: float) -> float: :rtype: float """ return constants.PLANCK_CONSTANT / (mass * velocity) + + def calculate_plasma_frequency( + nd_particle: float, m_particle: float, z_particle: float + ) -> float: + """ + Calculate the plasma frequency for a particle species. + :param nd_particle: Number density of the particle species (/m^3). + :type nd_particle: float + :param m_particle: Mass of the particle species (kg). + :type m_particle: float + :param Z_particle: Charge state of the particle species (dimensionless). + :type Z_particle: float + :returns: Plasma frequency in Hz. + :rtype: float + """ + return ( + ( + (nd_particle * z_particle**2 * constants.ELECTRON_CHARGE**2) + / (m_particle * constants.EPSILON0) + ) + ** 0.5 + ) / (2 * np.pi) def output_detailed_physics(self): """Outputs detailed physics variables to file.""" From b4544d36c1ffd105e3e55b626669afe2acf9eccb Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 16:43:04 +0100 Subject: [PATCH 12/20] :sparkle: Add electron plasma frequency profile variable to physics module --- process/data_structure/physics_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index e16bde26ba..88e6e8a70f 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1332,6 +1332,9 @@ plasma_coulomb_log_electron_electron_profile: list[float] = None """Profile of electron-electron Coulomb logarithm in plasma""" +freq_plasma_electron_profile: list[float] = None +"""Electron plasma frequency profile (Hz)""" + def init_physics_module(): """Initialise the physics module""" @@ -1654,6 +1657,7 @@ def init_physics_variables(): global len_plasma_debye_electron_vol_avg global vel_plasma_electron_profile global plasma_coulomb_log_electron_electron_profile + global freq_plasma_electron_profile m_beam_amu = 0.0 m_fuel_amu = 0.0 @@ -1918,3 +1922,4 @@ def init_physics_variables(): len_plasma_debye_electron_vol_avg = 0.0 vel_plasma_electron_profile = [] plasma_coulomb_log_electron_electron_profile = [] + freq_plasma_electron_profile = [] From 6ec77c24e7800c059d15abc264216064829a87fb Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 16:49:27 +0100 Subject: [PATCH 13/20] :sparkle: Add electron thermal frequency profile calculation to DetailedPhysics class and plotting functionality --- process/io/plot_proc.py | 28 +++++++++++++++++++++++++--- process/physics.py | 26 ++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 0e7c379ddb..d51d4cd889 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -12546,7 +12546,7 @@ def plot_debye_length_profile(axis, mfile_data, scan): """Plot the Debye length profile on the given axis.""" len_plasma_debye_electron_profile = [ mfile_data.data[f"len_plasma_debye_electron_profile{i}"].get_scan(scan) - for i in range(500) + for i in range(int(mfile_data.data["n_plasma_profile_elements"].get_scan(scan))) ] axis.plot( @@ -12559,7 +12559,6 @@ def plot_debye_length_profile(axis, mfile_data, scan): axis.set_ylabel("Debye Length [m]") axis.set_xlabel("$\\rho \\ [r/a]$") - axis.set_yscale("log") axis.grid(True, which="both", linestyle="--", alpha=0.5) axis.set_xlim([0, 1.025]) axis.minorticks_on() @@ -12570,7 +12569,7 @@ def plot_velocity_profile(axis, mfile_data, scan): """Plot the electron thermal velocity profile on the given axis.""" vel_plasma_electron_profile = [ mfile_data.data[f"vel_plasma_electron_profile{i}"].get_scan(scan) - for i in range(500) + for i in range(int(mfile_data.data["n_plasma_profile_elements"].get_scan(scan))) ] axis.plot( @@ -12589,6 +12588,29 @@ def plot_velocity_profile(axis, mfile_data, scan): axis.legend() +def plot_frequency_profile(axis, mfile_data, scan): + """Plot the electron thermal frequency profile on the given axis.""" + freq_plasma_electron_profile = [ + mfile_data.data[f"freq_plasma_electron_profile{i}"].get_scan(scan) + for i in range(int(mfile_data.data["n_plasma_profile_elements"].get_scan(scan))) + ] + + axis.plot( + np.linspace(0, 1, len(freq_plasma_electron_profile)), + np.array(freq_plasma_electron_profile) / 1e9, + color="blue", + linestyle="-", + label=r"$\omega_{p,e}$", + ) + + axis.set_ylabel("Frequency [GHz]") + axis.set_xlabel("$\\rho \\ [r/a]$") + axis.grid(True, which="both", linestyle="--", alpha=0.5) + axis.set_xlim([0, 1.025]) + axis.minorticks_on() + axis.legend() + + def main_plot( fig0, fig1, diff --git a/process/physics.py b/process/physics.py index 5beaed9059..0027689dfc 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9097,6 +9097,18 @@ def run(self): ) ) + # ============================ + # Plasma frequencies + # ============================ + + physics_variables.freq_plasma_electron_profile = ( + self.calculate_plasma_frequency( + nd_particle=self.plasma_profile.neprofile.profile_y, + m_particle=constants.ELECTRON_MASS, + z_particle=1.0, + ) + ) + def calculate_debye_length( self, temp_plasma_species_kev: float, @@ -9195,9 +9207,9 @@ def calculate_debroglie_wavelength(self, mass: float, velocity: float) -> float: :rtype: float """ return constants.PLANCK_CONSTANT / (mass * velocity) - + def calculate_plasma_frequency( - nd_particle: float, m_particle: float, z_particle: float + self, nd_particle: float, m_particle: float, z_particle: float ) -> float: """ Calculate the plasma frequency for a particle species. @@ -9249,3 +9261,13 @@ def output_detailed_physics(self): f"vel_plasma_electron_profile{i}", physics_variables.vel_plasma_electron_profile[i], ) + + po.osubhd(self.outfile, "Frequencies:") + + for i in range(len(physics_variables.freq_plasma_electron_profile)): + po.ovarre( + self.mfile, + f"Plasma electron frequency at point {i}", + f"freq_plasma_electron_profile{i}", + physics_variables.freq_plasma_electron_profile[i], + ) From 3f8297f4d2c74d917036a4ab4ed836f96f84f31a Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 16:55:20 +0100 Subject: [PATCH 14/20] :sparkle: Add Larmor frequency calculation method to DetailedPhysics class --- process/physics.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/process/physics.py b/process/physics.py index 0027689dfc..e035be46c5 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9230,6 +9230,24 @@ def calculate_plasma_frequency( ** 0.5 ) / (2 * np.pi) + def calculate_larmor_frequency( + self, b_field: float, m_particle: float, z_particle: float + ) -> float: + """ + Calculate the Larmor frequency for a particle species. + :param b_field: Magnetic field strength (T). + :type b_field: float + :param m_particle: Mass of the particle species (kg). + :type m_particle: float + :param Z_particle: Charge state of the particle species (dimensionless). + :type Z_particle: float + :returns: Larmor frequency in Hz. + :rtype: float + """ + return (z_particle * constants.ELECTRON_CHARGE * b_field) / ( + 2 * np.pi * m_particle + ) + def output_detailed_physics(self): """Outputs detailed physics variables to file.""" From e0cd32dbc750513de29798b34e69ca5a1343d6e9 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 16:56:44 +0100 Subject: [PATCH 15/20] Add electron Larmor frequency profile variable for toroidal magnetic field --- process/data_structure/physics_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 88e6e8a70f..a95ed17c0d 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -1335,6 +1335,9 @@ freq_plasma_electron_profile: list[float] = None """Electron plasma frequency profile (Hz)""" +freq_plasma_larmor_toroidal_electron_profile: list[float] = None +"""Profile of electron Larmor frequency in plasma due to toroidal magnetic field (Hz)""" + def init_physics_module(): """Initialise the physics module""" @@ -1658,6 +1661,7 @@ def init_physics_variables(): global vel_plasma_electron_profile global plasma_coulomb_log_electron_electron_profile global freq_plasma_electron_profile + global freq_plasma_larmor_toroidal_electron_profile m_beam_amu = 0.0 m_fuel_amu = 0.0 @@ -1923,3 +1927,4 @@ def init_physics_variables(): vel_plasma_electron_profile = [] plasma_coulomb_log_electron_electron_profile = [] freq_plasma_electron_profile = [] + freq_plasma_larmor_toroidal_electron_profile = [] From 5a828674472e93c6aa2417a0b6ffec7a810ee776 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 22 Oct 2025 18:44:22 +0100 Subject: [PATCH 16/20] :sparkle: Add Larmor frequency calculation for electron profile in DetailedPhysics class --- process/io/plot_proc.py | 15 +++++++-------- process/physics.py | 12 ++++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index d51d4cd889..b29a6a5b4b 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -12595,18 +12595,17 @@ def plot_frequency_profile(axis, mfile_data, scan): for i in range(int(mfile_data.data["n_plasma_profile_elements"].get_scan(scan))) ] - axis.plot( - np.linspace(0, 1, len(freq_plasma_electron_profile)), - np.array(freq_plasma_electron_profile) / 1e9, - color="blue", - linestyle="-", - label=r"$\omega_{p,e}$", - ) + x = np.linspace(0, 1, len(freq_plasma_electron_profile)) + y = np.array(freq_plasma_electron_profile) / 1e9 + # original curve + axis.plot(x, y, color="blue", linestyle="-", label=r"$\omega_{p,e}$") + # mirrored across the y-axis (drawn at negative rho) + axis.plot(-x, y, color="blue", linestyle="-", label="_nolegend_") + axis.set_xlim(-1.025, 1.025) axis.set_ylabel("Frequency [GHz]") axis.set_xlabel("$\\rho \\ [r/a]$") axis.grid(True, which="both", linestyle="--", alpha=0.5) - axis.set_xlim([0, 1.025]) axis.minorticks_on() axis.legend() diff --git a/process/physics.py b/process/physics.py index e035be46c5..bedc20b3e6 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9108,6 +9108,18 @@ def run(self): z_particle=1.0, ) ) + + # ============================ + # Larmor frequencies + # ============================ + + physics_variables.freq_plasma_electron_larmor_profile = ( + self.calculate_larmor_frequency( + b_field=self.plasma_profile.bprofile.profile_y, + m_particle=constants.ELECTRON_MASS, + z_particle=1.0, + ) + ) def calculate_debye_length( self, From 9b00dbde9ba5a6a7f4ef49875752399f16d5ef52 Mon Sep 17 00:00:00 2001 From: chris-ashe Date: Wed, 22 Oct 2025 19:17:18 +0100 Subject: [PATCH 17/20] :sparkle: Add Larmor frequency profile for toroidal magnetic field in DetailedPhysics class --- process/io/plot_proc.py | 15 +++++++++++++++ process/physics.py | 17 +++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index b29a6a5b4b..6411bb79bf 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -12594,7 +12594,22 @@ def plot_frequency_profile(axis, mfile_data, scan): mfile_data.data[f"freq_plasma_electron_profile{i}"].get_scan(scan) for i in range(int(mfile_data.data["n_plasma_profile_elements"].get_scan(scan))) ] + freq_plasma_larmor_toroidal_electron_profile = [ + mfile_data.data[f"freq_plasma_larmor_toroidal_electron_profile{i}"].get_scan( + scan + ) + for i in range( + 2 * int(mfile_data.data["n_plasma_profile_elements"].get_scan(scan)) + ) + ] + axis.plot( + np.linspace(-1, 1, len(freq_plasma_larmor_toroidal_electron_profile)), + np.array(freq_plasma_larmor_toroidal_electron_profile) / 1e9, + color="red", + linestyle="-", + label=r"$f_{Larmor,toroidal,e}$", + ) x = np.linspace(0, 1, len(freq_plasma_electron_profile)) y = np.array(freq_plasma_electron_profile) / 1e9 # original curve diff --git a/process/physics.py b/process/physics.py index bedc20b3e6..f914613537 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9108,14 +9108,14 @@ def run(self): z_particle=1.0, ) ) - + # ============================ # Larmor frequencies # ============================ - - physics_variables.freq_plasma_electron_larmor_profile = ( + + physics_variables.freq_plasma_larmor_toroidal_electron_profile = ( self.calculate_larmor_frequency( - b_field=self.plasma_profile.bprofile.profile_y, + b_field=physics_variables.b_plasma_toroidal_profile, m_particle=constants.ELECTRON_MASS, z_particle=1.0, ) @@ -9301,3 +9301,12 @@ def output_detailed_physics(self): f"freq_plasma_electron_profile{i}", physics_variables.freq_plasma_electron_profile[i], ) + for i in range( + len(physics_variables.freq_plasma_larmor_toroidal_electron_profile) + ): + po.ovarre( + self.mfile, + f"Plasma electron Larmor frequency at point {i}", + f"freq_plasma_larmor_toroidal_electron_profile{i}", + physics_variables.freq_plasma_larmor_toroidal_electron_profile[i], + ) From 931d2f9f1a5ef962f13cd5cb0ea7b032568e8dae Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 23 Oct 2025 10:23:45 +0100 Subject: [PATCH 18/20] :sparkle: Add calculation and plotting for plasma Coulomb logarithms in DetailedPhysics class --- process/io/plot_proc.py | 24 ++++++++++++++++++++++++ process/physics.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 6411bb79bf..d1d827dcf1 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -12625,6 +12625,30 @@ def plot_frequency_profile(axis, mfile_data, scan): axis.legend() +def plot_plasma_coloumb_logarithms(axis, mfile_data, scan): + """Plot the plasma coloumb logarithms on the given axis.""" + plasma_coulomb_log_electron_electron_profile = [ + mfile_data.data[f"plasma_coulomb_log_electron_electron_profile{i}"].get_scan( + scan + ) + for i in range(int(mfile_data.data["n_plasma_profile_elements"].get_scan(scan))) + ] + + axis.plot( + np.linspace(0, 1, len(plasma_coulomb_log_electron_electron_profile)), + plasma_coulomb_log_electron_electron_profile, + color="blue", + linestyle="-", + label=r"$ln \Lambda_{e-e}$", + ) + + axis.set_ylabel("Coulomb Logarithm") + axis.set_xlabel("$\\rho \\ [r/a]$") + axis.grid(True, which="both", linestyle="--", alpha=0.5) + axis.minorticks_on() + axis.legend() + + def main_plot( fig0, fig1, diff --git a/process/physics.py b/process/physics.py index f914613537..4c485517c1 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9121,6 +9121,29 @@ def run(self): ) ) + # ============================ + # Coulomb logarithm + # ============================ + + physics_variables.plasma_coulomb_log_electron_electron_profile = np.array([ + self.calculate_coulomb_log_from_impact( + impact_param_max=physics_variables.len_plasma_debye_electron_profile[i], + impact_param_min=max( + self.calculate_classical_distance_of_closest_approach( + charge1=1, + charge2=1, + e_kinetic=self.plasma_profile.teprofile.profile_y[i] + * constants.KILOELECTRON_VOLT, + ), + self.calculate_debroglie_wavelength( + mass=constants.ELECTRON_MASS, + velocity=physics_variables.vel_plasma_electron_profile[i], + ), + ), + ) + for i in range(len(physics_variables.len_plasma_debye_electron_profile)) + ]) + def calculate_debye_length( self, temp_plasma_species_kev: float, @@ -9310,3 +9333,15 @@ def output_detailed_physics(self): f"freq_plasma_larmor_toroidal_electron_profile{i}", physics_variables.freq_plasma_larmor_toroidal_electron_profile[i], ) + + po.osubhd(self.outfile, "Coulomb Logarithms:") + + for i in range( + len(physics_variables.plasma_coulomb_log_electron_electron_profile) + ): + po.ovarre( + self.mfile, + f"Electron-electron Coulomb log at point {i}", + f"plasma_coulomb_log_electron_electron_profile{i}", + physics_variables.plasma_coulomb_log_electron_electron_profile[i], + ) From c3ccd3bff61c780d363585f52d9b14870890c3c7 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 8 Dec 2025 13:28:44 +0000 Subject: [PATCH 19/20] Post rebase changes --- process/caller.py | 1 - process/io/plot_proc.py | 78 +++++++++++++++++++++++++---------------- process/output.py | 4 +++ process/physics.py | 2 +- 4 files changed, 52 insertions(+), 33 deletions(-) diff --git a/process/caller.py b/process/caller.py index b73675f71c..0a93243a15 100644 --- a/process/caller.py +++ b/process/caller.py @@ -253,7 +253,6 @@ def _call_models_once(self, xc: np.ndarray) -> None: self.models.build.run() self.models.physics.physics() - self.models.physics_detailed.run() # Toroidal field coil model diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index d1d827dcf1..f4f9e3499e 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -12549,15 +12549,21 @@ def plot_debye_length_profile(axis, mfile_data, scan): for i in range(int(mfile_data.data["n_plasma_profile_elements"].get_scan(scan))) ] + # Convert to micrometres (1e-6 m) + len_plasma_debye_electron_profile_um = [ + length * 1e6 for length in len_plasma_debye_electron_profile + ] + axis.plot( - np.linspace(0, 1, len(len_plasma_debye_electron_profile)), - len_plasma_debye_electron_profile, + np.linspace(0, 1, len(len_plasma_debye_electron_profile_um)), + len_plasma_debye_electron_profile_um, color="blue", linestyle="-", label=r"$\lambda_{Debye,e}$", ) - axis.set_ylabel("Debye Length [m]") + axis.set_ylabel(r"Debye Length [$\mu$m]") + axis.set_xlabel("$\\rho \\ [r/a]$") axis.grid(True, which="both", linestyle="--", alpha=0.5) axis.set_xlim([0, 1.025]) @@ -12678,6 +12684,7 @@ def main_plot( fig25, fig26, fig27, + fig28, m_file_data, scan, imp="../data/lz_non_corona_14_elements/", @@ -12827,9 +12834,14 @@ def main_plot( plot_density_limit_comparison(fig13.add_subplot(221), m_file_data, scan) plot_confinement_time_comparison(fig13.add_subplot(224), m_file_data, scan) + plot_debye_length_profile(fig14.add_subplot(232), m_file_data, scan) + plot_velocity_profile(fig14.add_subplot(233), m_file_data, scan) + plot_frequency_profile(fig14.add_subplot(212), m_file_data, scan) + plot_plasma_coloumb_logarithms(fig14.add_subplot(231), m_file_data, scan) + # Plot poloidal cross-section poloidal_cross_section( - fig14.add_subplot(121, aspect="equal"), + fig15.add_subplot(121, aspect="equal"), m_file_data, scan, demo_ranges, @@ -12838,7 +12850,7 @@ def main_plot( # Plot toroidal cross-section toroidal_cross_section( - fig14.add_subplot(122, aspect="equal"), + fig15.add_subplot(122, aspect="equal"), m_file_data, scan, demo_ranges, @@ -12846,19 +12858,19 @@ def main_plot( ) # Plot color key - ax17 = fig14.add_subplot(222) + ax17 = fig15.add_subplot(222) ax17.set_position([0.5, 0.5, 0.5, 0.5]) color_key(ax17, m_file_data, scan, colour_scheme) - ax18 = fig15.add_subplot(211) + ax18 = fig16.add_subplot(211) ax18.set_position([0.1, 0.33, 0.8, 0.6]) plot_radial_build(ax18, m_file_data, colour_scheme) # Make each axes smaller vertically to leave room for the legend - ax185 = fig16.add_subplot(211) + ax185 = fig17.add_subplot(211) ax185.set_position([0.1, 0.61, 0.8, 0.32]) - ax18b = fig16.add_subplot(212) + ax18b = fig17.add_subplot(212) ax18b.set_position([0.1, 0.13, 0.8, 0.32]) plot_upper_vertical_build(ax185, m_file_data, colour_scheme) plot_lower_vertical_build(ax18b, m_file_data, colour_scheme) @@ -12866,53 +12878,53 @@ def main_plot( # Can only plot WP and turn structure if superconducting coil at the moment if m_file_data.data["i_tf_sup"].get_scan(scan) == 1: # TF coil with WP - ax19 = fig17.add_subplot(221, aspect="equal") + ax19 = fig18.add_subplot(221, aspect="equal") ax19.set_position([ 0.025, 0.45, 0.5, 0.5, ]) # Half height, a bit wider, top left - plot_superconducting_tf_wp(ax19, m_file_data, scan, fig17) + plot_superconducting_tf_wp(ax19, m_file_data, scan, fig18) # TF coil turn structure - ax20 = fig18.add_subplot(325, aspect="equal") + ax20 = fig19.add_subplot(325, aspect="equal") ax20.set_position([0.025, 0.5, 0.4, 0.4]) - plot_tf_cable_in_conduit_turn(ax20, fig18, m_file_data, scan) - plot_205 = fig18.add_subplot(223, aspect="equal") + plot_tf_cable_in_conduit_turn(ax20, fig19, m_file_data, scan) + plot_205 = fig19.add_subplot(223, aspect="equal") plot_205.set_position([0.075, 0.1, 0.3, 0.3]) - plot_cable_in_conduit_cable(plot_205, fig18, m_file_data, scan) + plot_cable_in_conduit_cable(plot_205, fig19, m_file_data, scan) else: - ax19 = fig17.add_subplot(211, aspect="equal") + ax19 = fig18.add_subplot(211, aspect="equal") ax19.set_position([0.06, 0.55, 0.675, 0.4]) - plot_resistive_tf_wp(ax19, m_file_data, scan, fig17) + plot_resistive_tf_wp(ax19, m_file_data, scan, fig18) plot_tf_coil_structure( - fig19.add_subplot(111, aspect="equal"), m_file_data, scan, colour_scheme + fig20.add_subplot(111, aspect="equal"), m_file_data, scan, colour_scheme ) - plot_plasma_outboard_toroidal_ripple_map(fig20, m_file_data, scan) + plot_plasma_outboard_toroidal_ripple_map(fig21, m_file_data, scan) - axes = fig21.subplots(nrows=3, ncols=1, sharex=True).flatten() + axes = fig22.subplots(nrows=3, ncols=1, sharex=True).flatten() plot_tf_stress(axes) - plot_current_profiles_over_time(fig22.add_subplot(111), m_file_data, scan) + plot_current_profiles_over_time(fig23.add_subplot(111), m_file_data, scan) plot_cs_coil_structure( - fig23.add_subplot(121, aspect="equal"), fig23, m_file_data, scan + fig24.add_subplot(121, aspect="equal"), fig24, m_file_data, scan ) plot_cs_turn_structure( - fig23.add_subplot(224, aspect="equal"), fig23, m_file_data, scan + fig24.add_subplot(224, aspect="equal"), fig24, m_file_data, scan ) plot_first_wall_top_down_cross_section( - fig24.add_subplot(221, aspect="equal"), m_file_data, scan + fig25.add_subplot(221, aspect="equal"), m_file_data, scan ) - plot_first_wall_poloidal_cross_section(fig24.add_subplot(122), m_file_data, scan) - plot_fw_90_deg_pipe_bend(fig24.add_subplot(337), m_file_data, scan) + plot_first_wall_poloidal_cross_section(fig25.add_subplot(122), m_file_data, scan) + plot_fw_90_deg_pipe_bend(fig25.add_subplot(337), m_file_data, scan) - plot_blkt_pipe_bends(fig25, m_file_data, scan) - ax_blanket = fig25.add_subplot(122, aspect="equal") + plot_blkt_pipe_bends(fig26, m_file_data, scan) + ax_blanket = fig26.add_subplot(122, aspect="equal") plot_blanket(ax_blanket, m_file_data, scan, colour_scheme) plot_firstwall(ax_blanket, m_file_data, scan, colour_scheme) ax_blanket.set_xlabel("Radial position [m]") @@ -12955,13 +12967,13 @@ def main_plot( ) plot_main_power_flow( - fig26.add_subplot(111, aspect="equal"), m_file_data, scan, fig26 + fig27.add_subplot(111, aspect="equal"), m_file_data, scan, fig27 ) - ax24 = fig27.add_subplot(111) + ax24 = fig28.add_subplot(111) # set_position([left, bottom, width, height]) -> height ~ 0.66 => ~2/3 of page height ax24.set_position([0.08, 0.35, 0.84, 0.57]) - plot_system_power_profiles_over_time(ax24, m_file_data, scan, fig27) + plot_system_power_profiles_over_time(ax24, m_file_data, scan, fig28) def main(args=None): @@ -13282,6 +13294,7 @@ def main(args=None): page25 = plt.figure(figsize=(12, 9), dpi=80) page26 = plt.figure(figsize=(12, 9), dpi=80) page27 = plt.figure(figsize=(12, 9), dpi=80) + page28 = plt.figure(figsize=(12, 9), dpi=80) # run main_plot main_plot( @@ -13313,6 +13326,7 @@ def main(args=None): page25, page26, page27, + page28, m_file, scan=scan, demo_ranges=demo_ranges, @@ -13349,6 +13363,7 @@ def main(args=None): pdf.savefig(page25) pdf.savefig(page26) pdf.savefig(page27) + pdf.savefig(page28) # show fig if option used if args.show: @@ -13382,6 +13397,7 @@ def main(args=None): plt.close(page25) plt.close(page26) plt.close(page27) + plt.close(page28) if __name__ == "__main__": diff --git a/process/output.py b/process/output.py index 765871265b..1fe44b8281 100644 --- a/process/output.py +++ b/process/output.py @@ -43,6 +43,10 @@ def write(models, _outfile): # Writing the output from physics.f90 into OUT.DAT + MFILE.DAT models.physics.calculate_effective_charge_ionisation_profiles() models.physics.outplas() + + # Detailed physics, currently only done at final point as values are not used + # by any other functions + models.physics_detailed.run() models.physics_detailed.output_detailed_physics() # TODO what is this? Not in caller.f90? diff --git a/process/physics.py b/process/physics.py index 4c485517c1..2d4a63a9e3 100644 --- a/process/physics.py +++ b/process/physics.py @@ -9241,7 +9241,7 @@ def calculate_debroglie_wavelength(self, mass: float, velocity: float) -> float: :returns: de Broglie wavelength in meters. :rtype: float """ - return constants.PLANCK_CONSTANT / (mass * velocity) + return (constants.PLANCK_CONSTANT / (2 * np.pi)) / (mass * velocity) def calculate_plasma_frequency( self, nd_particle: float, m_particle: float, z_particle: float From b09637cd4a25e71b5886dc8ed30c12d8be47a5ea Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 8 Dec 2025 15:17:58 +0000 Subject: [PATCH 20/20] :sparkle: Add documentation for Detailed Plasma Physics and link in mkdocs --- .../physics-models/detailed_physics.md | 85 +++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 86 insertions(+) create mode 100644 documentation/physics-models/detailed_physics.md diff --git a/documentation/physics-models/detailed_physics.md b/documentation/physics-models/detailed_physics.md new file mode 100644 index 0000000000..dd01a72ffd --- /dev/null +++ b/documentation/physics-models/detailed_physics.md @@ -0,0 +1,85 @@ +# Detailed Plasma Physics + +It can sometimes be useful to calculate rough values for key plasma paramters that are normally used in higher fidelity codes. The `DetailedPhysics()` class stores functions that are called and the end of the run to show rough values for key plasma behavior parameters. The calculation is done at the end as no other methods currently depend on these values. + +## Detailed Plasma Physics | `DetailedPhysics()` + + +------------------ + +### Debye length | `calculate_debye_length()` + +Calculates the Debye lenght given by: + +$$ +\lambda_{D} = \sqrt{\frac{\epsilon_0 k_B T_e}{n e^2}} +$$ + +------------------- + +### Relativistic particle speed | `calculate_relativistic_particle_speed()` + +$$ +v = c \times \sqrt{\left(1- \frac{1}{\left(1+\frac{E}{mc^2}\right)^2}\right)} +$$ + +------------------ + +### Coulomb Logarithm | `calculate_coulomb_log_from_impact()` + +Calculates the Coulomb logarithm assuming a straight line Landau-Spitzer method + +$$ +\ln \Lambda = \ln{\left(\frac{b_{\text{max}}}{b_{\text{min}}}\right)} +$$ + +The maximum impact parameter is given by the Debye length calculated by [`calculate_debye_length()`](#debye-length--calculate_debye_length) +$$ +b_{\text{max}} = \lambda_{\text{Debye}} +$$ + +The minimum impact paramter is the largest of either the classical distance of closest approach or the Debye length. + +$$ +\begin{split}b_{\text{min}} ≡ +\left\{ + \begin{array}{ll} + λ_{\text{de Broglie}} & \mbox{if } λ_{\text{de Broglie}} ≥ ρ_⟂ \\ + ρ_⟂ & \mbox{if } ρ_⟂ ≥ λ_{\text{de Broglie}} + \end{array} +\right.\end{split} +$$ + +$ρ_⟂$ is the classical distance of closest approach calculated by [`calculate_classical_distance_of_closest_approach()`](#classical-distance-of-closest-approach----calculate_classical_distance_of_closest_approach) + +------------------ + +### Classical distance of closest approach | `calculate_classical_distance_of_closest_approach()` + +$$ +\frac{Z_1Z_2e^2}{4\pi \epsilon_0 E_{\text{kinetic}}} +$$ + +--------------------- + +### DeBroglie Wavelength | `calculate_debroglie_wavelength()` + +$$ +\lambda_{\text{DeBroglie}} = \frac{h}{2\pi m v} +$$ + +---------------------- + +### Plasma Frequency | `calculate_plasma_frequency()` + +$$ +\omega_p = \sqrt{\frac{n_ie^2}{\epsilon_0 m_i}} +$$ + +--------------------- + +### Larmor Frequency | `calculate_larmor_frequency()` + +$$ +f_{\text{Larmor}} = \frac{Z_ieB}{2\pi m_i} +$$ \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 22dcfea2dc..ba7cb96aa7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -70,6 +70,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 + - Detailed Plasma Physics: physics-models/detailed_physics.md - Pulsed Plant Operation: physics-models/pulsed-plant.md - Engineering Models: - Machine Build: eng-models/machine-build.md