Guiding Center Integration
Guiding center integration in Boozer coordinates is performed using equations of motion obtained from the Littlejohn Lagrangian:
where \(2\pi \psi\) is the toroidal flux, \(2\pi \chi\) is the poloidal flux, \(q\) is the charge, \(m\) is the mass, \(\rho_{||} = q v_{||}/(m B)\) and the covariant form of the magnetic field is:
See R. White, Theory of Tokamak Plasmas, Sec. 3.2.
The trajectory information is saved as \((s,\theta,\zeta,v_{||})\), where \(s = \psi_0\) is the toroidal flux normalized to its value on the boundary, \(2\pi\psi_0\).
Unperturbed Guiding Center Integration
The primary routine for unperturbed guiding center integration is trace_particles_boozer. The mode can be specified as mode='gc_vac', mode='gc_noK', or mode='gc'. Be default, the mode is determined from the BoozerMagneticField object.
Vacuum Mode (gc_vac)
In the case of mode='gc_vac' we solve the guiding center equations under the vacuum assumption, i.e \(G =\) const. and \(I = 0\):
where \(q\) is the charge, \(m\) is the mass, and \(v_\perp^2 = 2\mu B\).
General Mode (gc)
In the case of mode='gc' we solve the general guiding center equations for an MHD equilibrium:
where primes indicate differentiation wrt \(\psi\). In the case mode='gc_noK', the above equations are used with \(K=0\).
Perturbed Guiding Center Integration
The primary routine for perturbed guiding center integration is trace_particles_boozer_perturbed.
Vacuum Mode (gc_vac)
In the case of mode='gc_vac' we solve the guiding center equations under the vacuum assumption, i.e. \(G =\) const. and \(I = 0\):
where \(q\) is the charge, \(m\) is the mass, and \(v_\perp^2 = 2\mu B\).
General Mode (gc)
In the case of mode='gc' we solve the general guiding center equations for an MHD equilibrium:
Usage Examples
Unperturbed Tracing
from simsopt.field.boozermagneticfield import (
BoozerRadialInterpolant,
InterpolatedBoozerField,
)
from simsopt.field.tracing import trace_particles_boozer, MaxToroidalFluxStoppingCriterion
from simsopt.field.tracing_helpers import initialize_position_profile, initialize_velocity_uniform
from simsopt.util.constants import (
ALPHA_PARTICLE_MASS,
ALPHA_PARTICLE_CHARGE,
FUSION_ALPHA_PARTICLE_ENERGY,
)
import numpy as np
# Setup magnetic field from VMEC output
boozmn_filename = "boozmn_equilibrium.nc"
bri = BoozerRadialInterpolant(boozmn_filename, order=3, no_K=True)
field = InterpolatedBoozerField(
bri,
degree=3,
ns_interp=48,
ntheta_interp=48,
nzeta_interp=48,
)
# Initialize particle positions and velocities
nParticles = 1000
points = initialize_position_profile(field, nParticles, lambda s: 1-s, comm=None)
Ekin = FUSION_ALPHA_PARTICLE_ENERGY
vpar0 = np.sqrt(2 * Ekin / ALPHA_PARTICLE_MASS)
vpar_init = initialize_velocity_uniform(vpar0, nParticles, comm=None)
# Trace particles
res_tys, res_hits = trace_particles_boozer(
field=field,
stz_inits=points,
parallel_speeds=vpar_init,
tmax=1e-3,
mass=ALPHA_PARTICLE_MASS,
charge=ALPHA_PARTICLE_CHARGE,
comm=None,
Ekin=Ekin,
stopping_criteria=[MaxToroidalFluxStoppingCriterion(1.0)],
forget_exact_path=True,
abstol=1e-8,
reltol=1e-8
)
Perturbed Tracing
from simsopt.field.boozermagneticfield import (
BoozerRadialInterpolant,
InterpolatedBoozerField,
ShearAlfvenHarmonic,
)
from simsopt.field.tracing import trace_particles_boozer_perturbed, MaxToroidalFluxStoppingCriterion
from simsopt.field.tracing_helpers import initialize_position_profile, initialize_velocity_uniform
from simsopt.util.constants import (
ALPHA_PARTICLE_MASS,
ALPHA_PARTICLE_CHARGE,
FUSION_ALPHA_PARTICLE_ENERGY,
)
import numpy as np
# Setup equilibrium field
boozmn_filename = "boozmn_equilibrium.nc"
bri = BoozerRadialInterpolant(boozmn_filename, order=3, no_K=True)
field = InterpolatedBoozerField(
bri,
degree=3,
ns_interp=48,
ntheta_interp=48,
nzeta_interp=48,
)
# Create shear Alfvén wave perturbation
Phihat = -1.50119e3
saw = ShearAlfvenHarmonic(
Phihat, m=1, n=1, omega=136041, phase=0, B0=field
)
# Initialize particles
nParticles = 1000
points = initialize_position_profile(field, nParticles, lambda s: 1-s, comm=None)
Ekin = FUSION_ALPHA_PARTICLE_ENERGY
vpar0 = np.sqrt(2 * Ekin / ALPHA_PARTICLE_MASS)
vpar_init = initialize_velocity_uniform(vpar0, nParticles, comm=None)
# Calculate magnetic moment
field.set_points(points)
mu_init = (vpar0**2 - vpar_init**2)/(2*field.modB()[:,0])
# Trace particles with perturbation
res_tys, res_hits = trace_particles_boozer_perturbed(
perturbed_field=saw,
stz_inits=points,
parallel_speeds=vpar_init,
mus=mu_init,
tmax=1e-3,
mass=ALPHA_PARTICLE_MASS,
charge=ALPHA_PARTICLE_CHARGE,
comm=None,
stopping_criteria=[MaxToroidalFluxStoppingCriterion(1.0)],
forget_exact_path=True,
abstol=1e-8,
reltol=1e-8
)