FLEKS Python Visualization Toolkit: Test Particle Data

flekspy is a Python package for processing FLEKS data. This notebook focuses on handling test particle data.

FLEKS data format

  • Field: *.out format or AMREX built-in format, whose directory name is assumed to end with “_amrex”

  • PIC particle: AMREX built-in format

  • Test particle: binary data format

Importing the package

import flekspy

Downloading demo data

Example test particle data can be downloaded as follows:

from flekspy.util import download_testfile

url = "https://raw.githubusercontent.com/henry2004y/batsrus_data/master/test_particles_PBEG.tar.gz"
download_testfile(url, "data")

Plotting Test Particle Trajectories

Loading particle data

from flekspy import FLEKSTP

tp = FLEKSTP("data/test_particles_PBEG", iSpecies=1)

Obtaining particle IDs

Test particle IDs consists of a CPU index and a particle index attached to the CPU.

tp.getIDs()
[(0, 3), (0, 4)]

Reading particle trajectory

tp[0].collect()
shape: (5, 22)
timexyzvxvyvzbxbybzexeyezdbxdxdbxdydbxdzdbydxdbydydbydzdbzdxdbzdydbzdz
f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32
0.00.000128-0.0007950.00.0000420.0000670.000016224199.65625281.6527411182.642578-0.4473063.0646878.8907970.00.00.02.1949e60.00.0-107828.218750.00.0
0.2865430.000134-0.0007860.0000020.0000420.0000680.000014224414.28125207.77128611134.431641-1.9919423.3053348.97459-225897.34375123607.8671880.01.993212e6110127.5781250.0-52048.246094458591.00.0
0.5730860.000146-0.0007660.0000020.0000420.0000690.000012224547.4375171.82315111111.666016-1.0189982.3463798.844476-159384.8125358307.843750.02.0199e625620.7539060.068019.8984381.3220e60.0
0.8648570.000159-0.0007450.0000020.0000420.0000710.000011224356.7812525.65421711075.1494141.3787133.300569.281602385832.53125685539.18750.02.0586e6-450143.343750.0668866.25693783.8750.0
1.1570840.000171-0.0007240.0000010.0000430.0000720.000009224224.421875-133.65737910893.3281250.6838552.136019.826899822001.01.3780e60.01.7280e6-886848.81250.01.0217e6-653294.81250.0

Getting initial location

x = tp.read_initial_condition(tp.getIDs()[0])
print("time, X, Y, Z, Vx, Vy, Vz")
print(
    f"{x[0]:.2e}, {x[1]:.2e}, {x[2]:.2e}, {x[3]:.2e}, {x[4]:.2e}, {x[5]:.2e}, {x[6]:.2e}"
)
time, X, Y, Z, Vx, Vy, Vz
0.00e+00, 1.28e-04, -7.95e-04, 0.00e+00, 4.21e-05, 6.65e-05, 1.55e-05

Interpolate particles trajectory at selected times

from flekspy.tp import interpolate_at_times

interpolate_at_times(tp[0], [1.0])
shape: (1, 22)
timexyzvxvyvzbxbybzexeyezdbxdxdbxdydbxdzdbydxdbydydbydzdbzdxdbzdydbzdz
f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32f32
1.00.000165-0.0007360.0000010.0000420.0000710.00001224295.5625-48.02141210991.0634771.0573672.7619999.533781587544.06251.0058e60.01.90571e6-652103.18750.0832021.687570810.6250.0

Plotting trajectory

import matplotlib.pyplot as plt
tp.plot_trajectory(tp.getIDs()[0])
plt.show()
_images/ef2a163ca8b76d256d14a5c11bb97372c987bf1428649eb17f7f52ee535ffd59.png

Reading and visualizing all particle’s location at a given snapshot

ids, pData = tp.read_particles_at_time(0.0, doSave=False)
tp.plot_location(pData)
plt.show()
_images/86640e60a79cd3bc89633757aa38727fcdbb79bd292c3148c037b46e374bac5e.png

Selecting particles starting in a region

from flekspy.tp import Indices

def f_select(tp, pid):
    pData = tp.read_initial_condition(pid)
    inRegion = pData[Indices.X] > 0 and pData[Indices.Y] > 0
    return inRegion


pSelected = tp.select_particles(f_select)

Saving trajectories to CSV

tp.save_trajectory_to_csv(tp.getIDs()[0])

Calculating drifts

With the $\mathbf{E}, \mathbf{B}$, and $\nabla \mathbf{B}$ saved along the trajectory, we can compute various drifts:

  • Convection drift velocity: tp.get_ExB_drift(pid)

  • Curvature drift velocity: tp.get_curvature_drift(pid)

  • Gradient drift velocity: tp.get_gradient_drift(pid)

  • Polarization drift velocity: tp.get_polarization_drift(pid)

The first adiabatic assumption can be checked by comparing the ratio between the curvature length and the gyroradius $r_c / r_L$ via get_adiabaticity_parameter. If $r_c / r_L \gg 1$, then the gyromotion can be savely ignored.

The Betatron acceleration $\mu \partial B/\partial t$ can be indirectly computed with the knowledge of total derivative $\mathrm{d}B/\mathrm{d}t$ and $\mathbf{v}\nabla B$ along the trajectory:

pid = tp.getIDs()[0]
pt = tp[pid]
mu = tp.get_first_adiabatic_invariant(pt)
pt = tp.get_betatron_acceleration(pt, mu)

To analyze a specific drift:

tp.analyze_drift(pid, "gradient")
_images/f1e27a1a3bc4b6e72b2174ae132f63a2b632c11a9df4e5583e874418b5256515.png

By combining all these together, we come up with time series of various terms:

tp.analyze_drifts(pid)
_images/c777b0d75289ea955162696ddc6cb9b276a8e1d51d8ca18f730a5a62bc64ee61.png

We can the track the energy change from drift and EM field variations:

from flekspy.tp import plot_integrated_energy

df = tp.integrate_drift_accelerations(tp.getIDs()[0])
plot_integrated_energy(df)
_images/f2056a05d4adb0eccffd23827d05b19959ae4c28a2a58eb3f26137c28d93d1f7.png

Alternatively, the energy change can be decomposed into three terms: the parallel acceleration, Betatron acceleration, and the Fermi acceleration:

df = tp.get_energy_change_guiding_center(tp.getIDs()[0])

df
shape: (5, 5)
timedW_paralleldW_betatrondW_fermidW_total
f32f32f32f32f32
0.0-1.2793e-157.9045e-141.1878e-187.7767e-14
0.286543-6.5532e-86.6485e-141.0648e-18-6.5532e-8
0.573086-2.4509e-8-1.0753e-141.0303e-18-2.4509e-8
0.8648577.8494e-8-6.7355e-149.9597e-197.8494e-8
1.1570844.9847e-8-5.8379e-148.8976e-194.9847e-8