Example: Loss Fraction
#!/usr/bin/env python3
import time
import matplotlib.pyplot as plt
from neat.fields import StellnaQS
from neat.tracing import ChargedParticleEnsemble, ParticleEnsembleOrbit
"""
Calculate the loss fraction of a distribution of particles
in a quasisymmetric stellarator
"""
# Initialize an ensemble of alpha particles at a radius = r_initial
# Calculate loss fraction at a radius = r_max
# Test OpenMP parallelization with an array of threads = nthreads_array
# The total number of particles is ntheta * nphi * (nlambda_passing+nlambda_trapped) * 2 (particles with v_parallel = +1, -1)
r_initial = 0.06 # meters
r_max = 0.1 # meters
B0 = 5 # Tesla, magnetic field on-axis
energy = 3.52e6 # electron-volt
charge = 2 # times charge of proton
mass = 4 # times mass of proton
ntheta = 12 # resolution in theta
nphi = 4 # resolution in phi
nlambda_trapped = 18 # number of pitch angles for trapped particles
nlambda_passing = 3 # number of pitch angles for passing particles
nsamples = 10000 # resolution in time
tfinal = 1e-3 # seconds
nthreads_array = [4]
stellarator_index = 4
g_field = StellnaQS.from_paper(stellarator_index, B0=B0, nphi=131)
g_particle = ChargedParticleEnsemble(
r_initial=r_initial,
r_max=r_max,
energy=energy,
charge=charge,
mass=mass,
ntheta=ntheta,
nphi=nphi,
nlambda_trapped=nlambda_trapped,
nlambda_passing=nlambda_passing,
)
print("Starting particle tracer with B20 constant")
constant_b20 = True
threads_vs_time = []
for nthreads in nthreads_array:
start_time = time.time()
g_orbits = ParticleEnsembleOrbit(
g_particle,
g_field,
nsamples=nsamples,
tfinal=tfinal,
nthreads=nthreads,
constant_b20=constant_b20,
)
total_time = time.time() - start_time
print(
f" Running with {nthreads} threads and {g_orbits.nparticles} particles took {total_time}s"
)
threads_vs_time.append([nthreads, total_time])
g_orbits.loss_fraction(r_max=r_max, jacobian_weight=True)
plt.semilogx(
g_orbits.time,
g_orbits.loss_fraction_array,
label="With jacobian weights and B20 constant",
)
print(
f"Final loss fraction with jacobian weights and B20 constant = {g_orbits.loss_fraction_array[-1]*100}%"
)
# g_orbits.plot_loss_fraction()
g_orbits.loss_fraction(r_max=r_max, jacobian_weight=False)
plt.semilogx(
g_orbits.time,
g_orbits.loss_fraction_array,
label="Without jacobian weights and B20 constant",
)
print(
f"Final loss fraction without jacobian weights and B20 constant = {g_orbits.loss_fraction_array[-1]*100}%"
)
# g_orbits.plot_loss_fraction()
# print("Starting particle tracer with B20 not constant")
# constant_b20 = False
# threads_vs_time = []
# for nthreads in nthreads_array:
# start_time = time.time()
# g_orbits = ParticleEnsembleOrbit(
# g_particle,
# g_field,
# nsamples=nsamples,
# tfinal=tfinal,
# nthreads=nthreads,
# constant_b20=constant_b20,
# )
# total_time = time.time() - start_time
# print(
# f" Running with {nthreads} threads and {g_orbits.nparticles} particles took {total_time}s"
# )
# threads_vs_time.append([nthreads, total_time])
# g_orbits.loss_fraction(r_max=r_max, jacobian_weight=True)
# plt.semilogx(
# g_orbits.time,
# g_orbits.loss_fraction_array,
# label="With jacobian weights and B20 not constant",
# )
# print(
# f"Final loss fraction with jacobian weights and B20 not constant = {g_orbits.loss_fraction_array[-1]*100}%"
# )
# # g_orbits.plot_loss_fraction()
# g_orbits.loss_fraction(r_max=r_max, jacobian_weight=False)
# plt.semilogx(
# g_orbits.time,
# g_orbits.loss_fraction_array,
# label="Without jacobian weights and B20 not constant",
# )
# print(
# f"Final loss fraction without jacobian weights and B20 not constant = {g_orbits.loss_fraction_array[-1]*100}%"
# )
# # g_orbits.plot_loss_fraction()
plt.xlabel("Time (s)")
plt.ylabel("Loss Fraction")
plt.tight_layout()
plt.legend()
plt.show()
Figure 1: Loss fraction calculated with the example in examples/calculate_loss_fraction_qs.py