Пример #1
0
import math as m


mpl.rcParams['font.size'] = 20
mpl.rcParams['legend.fontsize'] = 20
mpl.rcParams['figure.facecolor'] = 'white'

# ________________________________________________
# Functions



# ________________________________________________
# Open results

res = happi.Open("./tst1d_cir_plane_wave_rela",verbose=False)

# ________________________________________________
# Parameters

#a0 = res_Boris.namelist.LaserGaussian3D[0].a0
a0 = 5.
step = 10  
 
# ____________________________________________
# Fields

if False:

  Ey = res_Boris.Field(0, "Ey", timesteps=1300, average = {"z":[5.]}).get()
 
Пример #2
0
import happi
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import erf as erf

for path in ["beam_relaxation7", "beam_relaxation8", "beam_relaxation9"]:

    sim = happi.Open(path)
    mass_ion = np.double(sim.namelist.Species["ion1"].mass)
    charge_ion = np.double(sim.namelist.Species["ion1"].charge)
    density_ion = np.double(sim.namelist.Species["ion1"].charge_density)
    temperature_ion = np.double(sim.namelist.Species["ion1"].temperature)
    velocity_electron = np.double(
        sim.namelist.Species["electron1"].mean_velocity)[0]
    temperature_electron = np.double(
        sim.namelist.Species["electron1"].temperature)
    coulomb_log = np.double(sim.namelist.Collisions[0].coulomb_log)
    dt = np.double(sim.namelist.Main.timestep) / (2 * np.pi)

    re = 2.8179403267e-15  # meters
    wavelength = 1e-6  # meters
    c = 3e8
    coeff = (2. * np.pi / wavelength)**2 * re * c

    times = sim.ParticleBinning(diagNumber=0).getAvailableTimesteps()

    e_vx_mean = np.zeros(len(times))
    e_vperp2 = np.zeros(len(times))
    i_vx_mean = np.zeros(len(times))
    Ti = np.zeros(len(times))
Пример #3
0
### mpl.rcParams['text.usetex'] = True
mpl.rcParams.update({
    'font.family'      :'serif',
    'font.serif'       :'Times',
    'font.size'        :16,
    'xtick.major.size' :10,
    'ytick.major.size' :10,
    'xtick.minor.size' :5,
    'ytick.minor.size' :5,
})


# LOADING SIMULATION & IMPORTANT VARIABLES FROM NAMELIST
# ------------------------------------------------------

S  = happi.Open(simulation_to_analyse ,verbose=False)

t0  = 2.*pi
Lv  = S.namelist.Lv
Lp  = S.namelist.Lp
dt  = S.namelist.Main.timestep
Zat = S.namelist.Species[0].atomic_number

print('- vector potential    aL = '+str(S.namelist.aL))
print('- ref. ang. frequency w0 = '+str(S.namelist.Main.reference_angular_frequency_SI))


# SOLVE THE RATE EQUATION NUMERICALLY & PLOT THE RESULTS
# ------------------------------------------------------

from solve_rate_eqs import solve_rate_eqs_
Пример #4
0
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams['text.usetex'] = True
plt.matplotlib.rcParams.update({
       'font.family':'serif',
       'font.serif':'Times',
        'font.size':20
})
mpl.rcParams['xtick.major.size'] = 10
mpl.rcParams['ytick.major.size'] = 10
mpl.rcParams['xtick.minor.size'] = 5
mpl.rcParams['ytick.minor.size'] = 5


S  = happi.Open('/Users/mica/RESULTS/SMILEI/thermalPlasmaPzDrift/')
T  = S.namelist.Te
mu = 1./T
print mu
v0 = S.namelist.v0
g0 = 1./m.sqrt(1.-v0**2)

# read p-distribution fct
fp  = np.array(S.ParticleBinning(1).getData())[0]
p   = np.array(S.ParticleBinning(1).get()['pz'])
print 'int over all pz:', (p[1]-p[0])*np.sum(fp)


# compute theoretical distribution fct
fth  = np.zeros(p.shape)
fth = (g0*np.sqrt(1.+p**2)+T) * np.exp( -g0*mu* (np.sqrt(1.+p**2) - v0*p) )
Пример #5
0
def BF3(A, passes):
    kernel = np.array([[[1, 2, 1], [2, 4, 2], [1, 2, 1]],
                       [[2, 4, 2], [4, 8, 4], [2, 4, 2]],
                       [[1, 2, 1], [2, 4, 2], [1, 2, 1]]]) / 64
    for i in np.arange(passes):
        A = np.concatenate((A[[0], :, :], A, A[[-1], :, :]), axis=0)
        A = np.concatenate((A[:, [0], :], A, A[:, [-1], :]), axis=1)
        A = np.concatenate((A[:, :, [0]], A, A[:, :, [-1]]), axis=2)
        A = convolve(A, kernel, 'valid')
    return A


ts = 43200
c = 2.9979e8
k0 = 2 * np.pi / 1e-6
S = happi.Open('.', k0 * c)
B_smilei = 107.1E6  # Gauss
B_flash = np.sqrt(4 * np.pi)

x_smilei = np.linspace(0, 1, 1025) * S.namelist.Lsim[0] / k0
y_smilei = np.linspace(-1, 1, 161) * S.namelist.Lsim[1] / k0
z_smilei = np.linspace(-1, 1, 161) * S.namelist.Lsim[1] / k0
print('Getting Data')
X, Y, Z = np.meshgrid((x_smilei[:-1] + x_smilei[1:]) / 2,
                      (y_smilei[:-1] + y_smilei[1:]) / 2,
                      (z_smilei[:-1] + z_smilei[1:]) / 2,
                      indexing='ij')
subs = {'axis3': 80 * 2 * np.pi}
avg = {'axis2': [120 * 2 * np.pi, 150 * 2 * np.pi]}
nele = S.Probe(4,
               '-(Rho_ele_mode_0+Rho_ele_mode_1+Rho_ele_mode_2)',
Пример #6
0
    def Smilei_Screen_1d(self,path,nb,r,x=0,verbose=True):
        """
        Extract phase space from Smilei 1D Screen diagnostic.

        Parameters
        ----------
        path : str
            path to the simulation folder
        nb : int
            Screen number
        r : float
            typical radius to consider in transverse direction (in um)
        x : float, optional
            diagnostic position
        verbose : bool, optional

        Notes
        -----
        On a 1D Smilei simulation, a typical DiagScreen must be declared as follows
        ::
            DiagScreen(
                shape               = 'plane',
                point               = [xtarget[1] - 5*um],
                vector              = [1.],
                direction           = 'forward',

                deposited_quantity  = 'weight',
                species             = ['e'],
                axes                = [
                     ['px' , pmin     , pmax     , 301],
                     ['py' , -pmax/5  , pmax/5   , 301]
                ],

                every               = every
            )
        """
        if verbose: print("Extracting screen data from %s ..."%path)

        # Import Smilei simulation
        import happi
        S = happi.Open(path,verbose=False)
        nl = S.namelist

        # Define physical constants
        m_e = 9.11e-31
        epsilon_0 = 8.85e-12
        e = 1.6e-19
        c = 2.99792458e8
        epsilon_0 = 8.854187817e-12
        # Smilei's unit in SI
        Wr = nl.Main.reference_angular_frequency_SI
        Tr = 1/Wr
        Lr = c/Wr
        Pr = 0.511 # MeV/c
        # Calculate normalizations
        nc = m_e * epsilon_0 * (Wr/e)**2
        Lx = nl.Main.grid_length[0] * Lr # Use a try/except ?
        vol = Lx * np.pi * (r * 1e-6)**2
        wnorm = nc * vol # Weight normalization : Before -> in Nc/Pr/Pr, After -> in Number/Pr/Pr
        tnorm = 1e-15/Tr
        xnorm = 1e-6/Lr
        # Save diag position
        xdiag = x

        # Initialize phase space lists
        w         = []
        x,y,z     = [],[],[]
        px,py,pz  = [],[],[]
        t         = []

        # Retrieve Screen data
        times = S.Screen(nb).getTimes()
        timesteps= S.Screen(nb).getTimesteps()

        Px  = S.Screen(nb).getAxis("px") * Pr
        Py  = S.Screen(nb).getAxis("py") * Pr

        # Compensate happi correction on weights
        wnorm /= Pr**2 # Weights are now in Nb/(MeV/c)/(MeV/c) (independant of bin size)
        wnorm *= (max(Px)-min(Px))/len(Px) # Multiply by bin size : weights are now in Nb/(MeV/c)/bin
        wnorm *= (max(Py)-min(Py))/len(Py) # Weight are now in Nb/bin/bin (dependant of bin size, it counts number of particles for given conf)

        # Current data is initialized as an empty matrix
        cdata=np.array([[0.]*len(Px)]*len(Py))

        # Loop over times
        for it,et in enumerate(timesteps):
            ldata = cdata
            # Retrieve data for given time
            cdata = S.Screen(nb,timesteps=et).getData()[0]
            # Loop over px then py
            if verbose and it % (len(times)//10) == 0: print("Retrieving timestep %i/%i ..."%(et,timesteps[-1]))
            for ipx,epx in enumerate(cdata):
                for ipy,epy in enumerate(epx):
                    # Get weight difference for given configuration
                    depy = epy-ldata[ipx][ipy]
                    # If non-zero, save config
                    if depy!=0.:
                        w.append(depy * wnorm)
                        px.append(Px[ipx])
                        py.append(Py[ipy])
                        t.append(times[it] * tnorm)

        # Reconstruct missing data
        pz = [0.0] * len(w)
        x = [xdiag] * len(w)
        y = [0.0] * len(w)
        z = [0.0] * len(w)

        # Update current phase space
        if verbose: print("Done !")
        self._ps.data.update(w,x,y,z,px,py,pz,t)
Пример #7
0
    g = E / 511. + 1.
    v2 = 1. - g**-2
    I0 = (9.76 * Z + 58.8 * Z**-0.19) / 1000.
    return 2.55e-25 * Z / (1. - g**-2) * (ln(
        (g + 1.) / 2. * (E / I0)**2) + (1. - (2. * g - 1.) * ln(2.) +
                                        (g - 1.)**2 / 8.) / g**2)


plt.figure(1, figsize=(8, 3.5))

# First, slowing down for a few examples
########################################
D = []
sims = ["2", "3", "4"]
for sim in sims:
    S = happi.Open("ionization_stopping_power" + sim)

    D.append(
        S.ParticleBinning("#0/#1",
                          sum={"x": "all"},
                          units=["fs"],
                          linestyle="None",
                          marker='o',
                          markersize=4,
                          markeredgewidth=0.))
happi.multiPlot(*D, skipAnimation=True)
fig = plt.gcf()
ax = plt.gca()
ax.xaxis.labelpad = 0
ax.yaxis.labelpad = 0
ax.set_xscale("log")
Пример #8
0
import numpy as np
from matplotlib.colors import LogNorm
from mpl_toolkits.mplot3d import Axes3D
import math as m

mpl.rcParams['font.size'] = 20
mpl.rcParams['legend.fontsize'] = 20
mpl.rcParams['figure.facecolor'] = 'white'

# ________________________________________________
# Functions

# ________________________________________________
# Open results

res_Boris = happi.Open("./tst3d_cir_plane_wave_Boris")
res_Vay = happi.Open("./tst3d_cir_plane_wave_Vay")
res_HC = happi.Open("./tst3d_cir_plane_wave_HC")

# ________________________________________________
# Parameters

#a0 = res_Boris.namelist.LaserGaussian3D[0].a0
a0 = 2.

# _________________________________________
# Scalar

Ukin = res_Boris.Scalar("Ukin").get()
Ukin_Vay = res_Vay.Scalar("Ukin").get()
Ukin_HC = res_HC.Scalar("Ukin").get()
Пример #9
0
import happi

import numpy as np
import matplotlib.pyplot as plt

ln = np.log
plt.ion()

D = []
colors = ["k", "r", "g", "b", "m"]
for elm in ["C", "Al", "Zn", "Sn", "Au"]:
    S1 = happi.Open("ionization_multiple" + elm + "1")
    S2 = happi.Open("ionization_multiple" + elm + "2")

    if S1.valid and S2.valid:

        color = colors.pop()

        timestep1 = np.round(np.double(S1.namelist.Main.timestep), decimals=1)
        D += [
            S1.ParticleBinning(0,
                               sum={"ekin": [0, 1]},
                               linestyle="-",
                               color=color,
                               label=elm)
        ]

        timestep2 = int(np.double(S2.namelist.Main.timestep))
        D += [
            S2.ParticleBinning(0,
                               sum={"ekin": [0, 1]},
Пример #10
0
    def __init__(self, parent, dirName):
        super(smileiQtPlot, self).__init__()
        self.setParent(parent)
        uiFile = os.path.dirname(
            os.path.realpath(__file__)) + '/smileiQtPlot.ui'
        self.ui = uic.loadUi(uiFile, self)

        self.dirName = dirName
        self.smilei = happi.Open(self.dirName)

        self.parent = parent

        self.parent.timer.timeout.connect(self.next)
        self.parent.ui.next.released.connect(self.next)
        self.parent.ui.previous.released.connect(self.previous)
        self.parent.ui.first.released.connect(self.first)
        self.parent.ui.last.released.connect(self.last)

        self.pauseSignal.connect(self.parent.pause)

        self.setWindowFlags(Qt.Window)
        self.setWindowTitle(dirName)

        self.step = 0

        self.ui.savePoints.setIcon(self.ui.style().standardIcon(
            QStyle.SP_DialogSaveButton))
        self.ui.savePoints.released.connect(self.doSavePoints)

        self.ui.tabWidget.currentChanged.connect(self.changeTab)
        self.ui.autoScale.stateChanged.connect(self.doPlots)

        self.fig = Figure()

        self.ui.reload.setIcon(self.ui.style().standardIcon(
            QStyle.SP_BrowserReload))
        self.ui.reload.released.connect(self.reloadAll)

        self.canvas = FigureCanvas(self.fig)
        self.canvas.setFocusPolicy(Qt.StrongFocus)
        self.canvas.setFocus()
        self.canvas.mpl_connect('motion_notify_event', self.on_movement)
        self.canvas.mpl_connect('key_press_event', self.on_key_press)
        self.canvas.mpl_connect('key_release_event', self.on_key_release)
        self.canvas.mpl_connect('button_press_event', self.on_button_press)

        self.toolbar = NavigationToolbar(self.canvas, self)
        self.toolbar.setFixedHeight(30)
        self.canvas.setCursor(Qt.CrossCursor)

        self.ui.plotLayout.addWidget(self.toolbar)
        self.ui.plotLayout.addWidget(self.canvas)

        #retrieve stuff from namelist
        self.cell_length = self.smilei.namelist.Main.cell_length[0]
        self.timestep = self.smilei.namelist.Main.timestep
        self.sim_length = self.smilei.namelist.Main.grid_length

        #scalars
        scalarSteps = []
        for scalar in self.smilei.Scalar().getScalars():
            if len(scalarSteps) == 0:
                scalarSteps = self.smilei.Scalar(
                    scalar).getAvailableTimesteps()
            else:
                if not np.array_equal(
                        scalarSteps,
                        self.smilei.Scalar(scalar).getAvailableTimesteps()):
                    log.error("Problem reading scalarSteps %s" % scalar)
            my_button = QCheckBox(scalar)
            my_button.stateChanged.connect(self.checkBoxChanged)
            self.ui.layoutScalars.addWidget(my_button)

        self.ui.layoutScalars.addStretch()

        #fields
        self.fieldSteps = self.smilei.Field(0).getAvailableTimesteps()
        if not np.array_equal(scalarSteps, self.fieldSteps):
            newfieldSteps = []
            for i in range(0, min(len(scalarSteps), len(self.fieldSteps))):
                if scalarSteps[i] == self.fieldSteps[i]:
                    newfieldSteps.append(scalarSteps[i])
                else:
                    break
            self.fieldSteps = newfieldSteps
            log.warning("Problem reading fieldSteps")
        for field in self.smilei.Field(0).getFields():
            my_button = QCheckBox(field)
            my_button.stateChanged.connect(self.checkBoxChanged)
            self.ui.layoutFields.addWidget(my_button)

        self.ui.layoutFields.addStretch()

        self.ui.slider.setRange(0, len(self.fieldSteps))
        self.ui.spinStep.setSuffix("/" + str(len(self.fieldSteps)))
        self.ui.spinStep.setMaximum(len(self.fieldSteps))

        #phase spaces
        i = 0
        # self.smilei.ParticleBinning(0)._type ...
        for phase in self.smilei.namelist.DiagParticleBinning:
            if not np.array_equal(
                    self.fieldSteps,
                    self.smilei.ParticleBinning(i).getAvailableTimesteps()):
                log.warning("Problem reading phaseSteps")

            name = str(i) + ' '
            for ax in phase.axes:
                name += ax[0]
            my_button = QCheckBox(name)
            my_button.stateChanged.connect(self.checkBoxChanged)
            self.ui.layoutPhase.addWidget(my_button)
            i = i + 1
        self.ui.layoutPhase.addStretch()

        self.load_settings()

        if sys.platform == "darwin":
            self.raise_()
        self.show()
Пример #11
0
        11.91873264, 13.84057796, 16.11965772, 18.80418339, 21.9305915,
        25.51153958, 29.52473287, 33.90815976, 38.56573939, 43.38130946,
        48.23275232, 52.99823957, 57.55375982, 61.76990959, 65.52021161,
        68.70651127, 71.28856372, 73.29422978, 74.80222575, 75.91222479,
        76.72040917, 77.30714207
    ]),
}

colors = ["b", "k", "r", "g"]

fig = plt.figure(3, figsize=(6, 3.5))

for element in ["H", "Al", "Zn", "Au"]:
    print "Analyzing " + element

    S = happi.Open("ionization_equilibrium" + element)

    npoints = S.namelist.npoints
    every = S.namelist.DiagParticleBinning[0].every
    ts = int(t0 * S.namelist.Main.reference_angular_frequency_SI /
             S.namelist.Main.timestep / every)  # timestep at 1ps

    Z = []
    Zfinal = []
    T = []
    Tfinal = []
    for i in range(npoints):
        D = S.ParticleBinning("#" + str(4 * i + 2) + "/#" + str(4 * i + 3),
                              sum={"x": "all"},
                              units=["ps"],
                              marker=".")
Пример #12
0
def loadData(directory=default_directory):
    """loading data in the simulation directory and return an object pointing to the various 
    files, see smilei website"""
    S = happi.Open(directory, show = False,verbose = False, )
    return S
Пример #13
0
import happi
import numpy as np
import json


if __name__=='__main__':

    s = happi.Open('./')
    ts = 21576
    ex = s.Field(0,'Ex',timesteps=ts).getData()
    ex_x = s.Field(0,'Ex',timesteps=ts).getAxis('x')
    rho = s.Field(0,'Rho_electron',timesteps=ts).getData()
    
    rho_mat = s.ParticleBinning(0,timesteps=ts).getData()
    px = s.ParticleBinning(0).getAxis('px')
    x = s.ParticleBinning(0).getAxis('x')
    t = s.ParticleBinning(0).getTimes()


    '''
    Pbin = {'px' : px.tolist(),
            'x'  : x.tolist(),
            't'  : t.tolist(), 
            'rho': np.array(rho_mat).tolist()
            }
    '''

    np.savetxt('Ex.csv',ex)
    np.savetxt('Rho.csv',rho)
    np.savetxt('x.csv', ex_x)
import os, re, numpy as np
import happi
from scipy.signal import butter, filtfilt

b, a = butter(5, 0.2, btype='low', analog=False)

S = happi.Open("./restart*", verbose=False)

eon_spectrum = S.ParticleBinning.Diag2().get()
ekin = eon_spectrum["ekin"]
eon_spectrum = np.mean(eon_spectrum["data"], axis=0)
eon_spectrum_filt = filtfilt(b, a, eon_spectrum)
# # theory
# Te = S.namelist.Species["eon"].temperature[0]
# factor = S.namelist.Species["eon"].number_density.xplateau / S.namelist.Main.grid_length[0]
# theoretical_spectrum = factor*2./Te * (ekin/np.pi/Te)**0.5 * np.exp(-ekin/Te)
# plt.plot(ekin, eon_spectrum_filt, '.-')
# plt.plot(ekin, theoretical_spectrum, '-')
Validate("Electron spectrum", eon_spectrum_filt, 20.)

rho = S.Field.Field0.Rho_ion(timesteps=11800).getData()[0]
rho_filt = filtfilt(b, a, rho)
Validate("Final ion profile", rho_filt[::10], 0.15)
Пример #15
0
dv0 = {
    "conductivity1": [0.00008, 0.00002, 0],
    "conductivity2": [-0.0001, -0.00015, -0.0002],
    "conductivity3": [-0.0005, -0.0012]
}

style = {"conductivity1": 'b', "conductivity2": 'g', "conductivity3": 'r'}

velocity = []
temperature = []
density = []

for path in ["conductivity1", "conductivity2", "conductivity3"]:

    S = happi.Open(path)

    ncases = 0
    for d in S.namelist.DiagParticleBinning:
        if d.deposited_quantity == "weight_charge_vx":
            ncases += 1
    if ncases == 0: continue

    print("simulation " + path + " has %d cases" % ncases)
    coulomb_log = np.double(S.namelist.Collisions[0].coulomb_log)
    dt = np.double(S.namelist.Main.timestep) / (2 * np.pi)

    times = np.double(S.ParticleBinning(0).getAvailableTimesteps())

    vx_mean = np.zeros((ncases, len(times)))
Пример #16
0
	[7.742651,0.027303], [9.111645,0.024132], [10.722692,0.021300], [12.618592,0.018779],
	[14.849710,0.016545], [17.475316,0.014570], [20.565161,0.012829], [24.201327,0.011297],
	[28.480411,0.009954], [33.516088,0.008778], [39.442133,0.007751], [46.415973,0.006855],
	[54.622872,0.006075], [64.280848,0.005398], [75.646470,0.004811], [89.021670,0.004303],
	[104.761764,0.003865], [123.284896,0.003488], [145.083138,0.003164], [170.735570,0.002888],
	[200.923659,0.002653], [236.449362,0.002454], [278.256434,0.002288], [327.455496,0.002149],
	[385.353540,0.002035], [453.488650,0.001944], [533.670861,0.001871], [628.030244,0.001815],
	[739.073494,0.001774], [869.750518,0.001746], [1023.532800,0.001730], [1204.505627,0.001723],
	[1417.476611,0.001725], [1668.103410,0.001735], [1963.044021,0.001751],
	[2310.133656,0.001772], [2718.592885,0.001798], [3199.272585,0.001827],
	[3764.942199,0.001860], [4430.628958,0.001895], [5214.017089,0.001932],
	[6135.917601,0.001971], [7220.821137,0.002012], [8497.548578,0.002053],
	[10000.016685,0.002096]])

 
S1=happi.Open("ionization_rate1")
S2=happi.Open("ionization_rate2")
S3=happi.Open("ionization_rate3")

# get electron velocity
ve = np.double(S1.namelist.Species["electron1"].mean_velocity[0])
Ee = (1./np.sqrt(1.-ve**2)-1.)*511. # energy
cse = np.interp(np.log(Ee), np.log(cs[:,0]), cs[:,1]) # interpolate cross section

# get density
ne = np.double(S1.namelist.Species["electron1"].charge_density)

# get ion charge
q0 = np.double(S1.namelist.Species["ion1"].charge)

# Plot simulation result
import os, re, numpy as np
import happi

S = happi.Open(["./restart*"], verbose=False)

for name, profile in S.namelist.profiles.items():
    A = S.Field.Field0("Rho_" + name)
    data = A.get()
    values = data["data"][0]
    #	z0 = np.pi
    #	y,x = np.meshgrid( A.get()["x"], A.get()["y"] )
    #	v = x[:,:]
    #	for i in range(x.shape[0]):
    #		for j in range(x.shape[1]):
    #			v[i,j] = profile(x[i,j],y[i,j], z0)
    Validate("Profile " + name, values[::10, ::10, ::10], 0.01)

#fig=plt.figure(1)
#for name, profile in S.namelist.profiles.items():
#	fig.clf()
#	ax1=fig.add_subplot(3,1,1)
#	print "Rho_"+name
#	A=S.Field.Field0("Rho_"+name, average={"z":3.})
#	z0 = A.getData()[0]
#	plt.colorbar( ax1.imshow(z0) )
#	y,x = np.meshgrid( A.get()["x"], A.get()["y"] )
#	z = x[:,:]
#	for i in range(x.shape[0]):
#		for j in range(x.shape[1]):
#			z[i,j] = profile(x[i,j],y[i,j], 3.)
#	ax2=fig.add_subplot(3,1,2)
Пример #18
0
    def Smilei_TrackParticles(self,path,species,wscale=1.,verbose=True):
        """
        Extract phase space from a TrackParticles Smilei diagnostic.

        Parameters
        ----------
        path : str
            path to the simulation folder
        species : str
            name of the specie in the Smilei namelist
        verbose : bool, optional
            verbosity
        """
        if verbose: print("Extracting %s phase space from %s TrackParticles ..."%(self._ps.particle["name"],species))
        # Open simulation
        import happi
        S = happi.Open(path,verbose=False)
        nl = S.namelist

        # Define physical constants
        m_e = 9.11e-31
        epsilon_0 = 8.85e-12
        e = 1.6e-19
        c = 2.99792458e8
        epsilon_0 = 8.854187817e-12

        # Smilei's unit in SI
        Wr = nl.Main.reference_angular_frequency_SI
        Tr = 1/Wr
        Lr = c/Wr
        Pr = 0.511 # MeV/c

        # Calculate normalizations
        geom = nl.Main.geometry
        nc = m_e * epsilon_0 * (Wr/e)**2
        if geom == "1Dcartesian":
            wnorm = nc * Lr * np.pi * wscale**2
        elif geom == "2Dcartesian":
            wnorm = nc * Lr**2 * wscale
        elif geom == "AMCylindrical":
            raise NotImplementedError("TODO ...")
        elif geom == "3Dcartesian":
            wnorm = nc * Lr**3
        else:
            raise NameError("Unknown geometry profile.")
        tnorm = Tr/1e-15    # in fs
        xnorm = Lr/1e-6     # in um
        pnorm = Pr          # in MeV/c

        # Initialize ps list
        w         = []
        x,y,z     = [],[],[]
        px,py,pz  = [],[],[]
        t         = []

        # Get timesteps
        timesteps = S.TrackParticles(species=species,sort=False).getTimesteps()
        dt = nl.Main.timestep

        # Loop over timesteps
        for ts in timesteps:
            if verbose:print("Timestep %i/%i ..."%(ts,timesteps[-1]))
            # Get data from current timestep
            data = S.TrackParticles(species=species,timesteps=ts,sort=False).get()[ts]
            # Get macro-particle's id. id == 0 means the macro-particle have already been exported
            id = data["Id"]
            # If no positive id, continue to the next iteration
            if len(id[id>0]) == 0: continue
            # Append phase space data of current timestep
            w += list(data["w"][id>0] * wnorm)
            x += list(data["x"][id>0] * xnorm)
            if geom == "1Dcartesian":
                y += [0.] * len(id>0)
                z += [0.] * len(id>0)
            elif geom == "2Dcartesian":
                y += list(data["y"][id>0] * xnorm)
                z += [0.] * len(id>0)
            elif geom == "AMCylindrical":
                raise NotImplementedError("TODO ...")
            elif geom == "3Dcartesian":
                y += list(data["y"][id>0] * xnorm)
                z += list(data["z"][id>0] * xnorm)
            px += list(data["px"][id>0] * pnorm)
            py += list(data["py"][id>0] * pnorm)
            pz += list(data["pz"][id>0] * pnorm)
            t += [ts*dt * tnorm] * len(id[id>0])

        if verbose: print("Done !")

        self._ps.data.update(w,x,y,z,px,py,pz,t,verbose=verbose)