Plot FBPIC ouptut using holoviews

A. Petrenko (Novosibirsk, 2019)

In [1]:
pwd
Out[1]:
'/home/petrenko/work/AWAKE/Run2/further_seed/FBPIC'
In [2]:
#ls -all -h diags/hdf5/
In [3]:
ls diags/hdf5/
data00000000.h5  data00900000.h5  data01800000.h5  data02700000.h5
data00100000.h5  data01000000.h5  data01900000.h5  data02800000.h5
data00200000.h5  data01100000.h5  data02000000.h5  data02900000.h5
data00300000.h5  data01200000.h5  data02100000.h5  data03000000.h5
data00400000.h5  data01300000.h5  data02200000.h5  data03100000.h5
data00500000.h5  data01400000.h5  data02300000.h5  data03200000.h5
data00600000.h5  data01500000.h5  data02400000.h5
data00700000.h5  data01600000.h5  data02500000.h5
data00800000.h5  data01700000.h5  data02600000.h5
In [4]:
#!h5dump --header diags/hdf5/data00000000.h5
In [5]:
import numpy as np
from opmd_viewer import OpenPMDTimeSeries
In [6]:
ts = OpenPMDTimeSeries('diags/hdf5/')
In [7]:
ts.avail_fields
Out[7]:
['B', 'E', 'J', 'rho', 'rho_bunch', 'rho_electrons']
In [8]:
ts.iterations
Out[8]:
array([      0,  100000,  200000,  300000,  400000,  500000,  600000,
        700000,  800000,  900000, 1000000, 1100000, 1200000, 1300000,
       1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000,
       2100000, 2200000, 2300000, 2400000, 2500000, 2600000, 2700000,
       2800000, 2900000, 3000000, 3100000, 3200000])
In [9]:
i = ts.iterations[-1]
i
Out[9]:
3200000

Fields

In [10]:
import matplotlib.pyplot as plt
In [11]:
plt.rcParams['figure.figsize'] = [15, 7]
In [12]:
rho, info_rho = ts.get_field( iteration=i, field='rho', plot=True, cmap='bwr', vmax=200, vmin=-200) # , m=1
In [13]:
import holoviews as hv

hv.extension('matplotlib')
In [14]:
import warnings
#warnings.filterwarnings(action='once')
warnings.filterwarnings('ignore')
In [15]:
%output size=350
%opts Image [colorbar=True aspect=2 show_grid=False] (cmap='bwr')
from holoviews.plotting import Plot
%opts Image [fontsize={'title':12, 'xlabel':12, 'ylabel':12, 'ticks':12, 'legend': 12}]
In [16]:
def plot_fbpic_fmap(i=None, field_name='rho', coord='', units=None, field_range=(None,None),
                    z_range=(None,None), x_range=(None,None)):

    if not i: i = ts.iterations[-1]
    
    t = ts.t[ts.iterations == i][0]
    
    A, info = ts.get_field(iteration=i, field=field_name, coord=coord, plot=False)
    A = A[::-1]
    
    zmin = info_rho.zmin*1e3 # mm
    zmax = info_rho.zmax*1e3 # mm
    xmin = info_rho.rmin*1e3 # mm
    xmax = info_rho.rmax*1e3 # mm
    
    bounds=(zmin-zmax, xmin, zmax-zmax, xmax)
    
    dim_field = hv.Dimension(field_name, unit=units, range=field_range)
    dim_z   = hv.Dimension('z', unit='mm', label=r'$z-z_0$', range=z_range)
    dim_x   = hv.Dimension('x', unit='mm', range=x_range)
    
    return hv.Image(A, bounds=bounds, kdims=[dim_z,dim_x], vdims=dim_field,
                    label=("%s%s at t = %.2f ps" % (field_name, coord, t*1e12))  )
In [17]:
plot_fbpic_fmap(field_range=(-100,+100))
Out[17]:
In [18]:
i_plots = ts.iterations[1:]
#i_plots = ts.iterations
In [19]:
items = [(i, plot_fbpic_fmap(i, 'E', 'z', units='V/m', field_range=(-1.5e9,+1.5e9))) for i in i_plots]

hv.HoloMap(items, kdims = ['Iteration'])
Out[19]:

Plot Ez on axis:

In [20]:
img = plot_fbpic_fmap(i, 'E', 'z', units='V/m', field_range=(-1.5e9,+1.5e9))
z_Ez = img.sample(x=0)
In [21]:
%opts Curve [aspect=2 show_grid=True]
In [22]:
(img + z_Ez).cols(1)
Out[22]:
In [23]:
z_Ez.data['E'].max()/1e9
Out[23]:
0.64316464387698913
In [24]:
c = 299792458 # m/sec
Ez_vals = []
t_vals  = []
for i, img in items:
    t = ts.t[ts.iterations == i][0]
    t_vals.append(t)
    z_Ez = img.sample(x=0)
    Ez = z_Ez.data['E'].max()/1e9 # GV/m
    #print(Ez)
    Ez_vals.append(Ez)
s_vals  = np.array(t_vals)*c
Ez_vals = np.array(Ez_vals) # GV/m
In [25]:
dim_s   = hv.Dimension('s', unit='m', range=(0, None))
dim_Ez  = hv.Dimension('Ez', unit='GV/m', range=(0, None))

hv.Curve((s_vals,Ez_vals), kdims=dim_s, vdims=dim_Ez)
Out[25]:
In [26]:
np.savetxt("Ez_max.txt", np.transpose([s_vals,Ez_vals]), delimiter=' ', header='s [m], Ez_max [GV/m]')
In [27]:
%opts Image [show_grid=True]

items = [(i, plot_fbpic_fmap(i, 'rho_bunch', units='a.u.', field_range=(-10,+10))) for i in i_plots]

hv.HoloMap(items, kdims = ['Iteration'])
Out[27]:
In [28]:
# Zoom:

items = [(i, plot_fbpic_fmap(i, 'rho_bunch', units='a.u.',
                             field_range=(-20,+20), z_range=(-120,-110),
                             x_range=(-0.4,+0.4)) ) for i in i_plots]

hv.HoloMap(items, kdims = ['Iteration'])
Out[28]:
In [29]:
items = [(i, plot_fbpic_fmap(i, 'rho', units='a.u.', field_range=(-200,+200))) for i in i_plots]

hv.HoloMap(items, kdims = ['Iteration'])
Out[29]:
In [30]:
%opts Image [show_grid=False]
In [31]:
items = [(i, plot_fbpic_fmap(i, 'rho_electrons', units='a.u.', field_range=(-500,+500))) for i in i_plots]

hv.HoloMap(items, kdims = ['Iteration'])
Out[31]:
In [32]:
items = [(i, plot_fbpic_fmap(i, 'E', 'x', units='V/m', field_range=(-1.5e9,+1.5e9))) for i in i_plots]

hv.HoloMap(items, kdims = ['Iteration'])
Out[32]:
In [39]:
items = [(i, plot_fbpic_fmap(i, 'E', 'y', units='V/m', field_range=(-0.2e9,+0.2e9))) for i in i_plots]

hv.HoloMap(items, kdims = ['Iteration'])
Out[39]:
In [36]:
with open("ssm.py", "r") as f:
    print(f.read())
"""
A. Petrenko. Novosibirsk, 2019.

This is an input script that runs a simulation of
seeded proton beam self-modulation using FBPIC.

All the structures implemented in FBPIC are internally documented.
Enter "print(fbpic_object.__doc__)" to have access to this documentation,
where fbpic_object is any of the objects or function of FBPIC.
"""

# -------
# Imports
# -------

import numpy as np
from scipy.constants import c, e, m_e, m_p
from scipy.constants import physical_constants
# Import the relevant structures from fbpic
from fbpic.main import Simulation
from fbpic.lpa_utils.laser import add_laser
from fbpic.lpa_utils.bunch import add_particle_bunch_gaussian
from fbpic.openpmd_diag import FieldDiagnostic, \
    ParticleDiagnostic, ParticleChargeDensityDiagnostic, \
    set_periodic_checkpoint, restart_from_checkpoint

import shutil

# ----------
# Parameters
# ----------

# Whether to use the GPU
use_cuda = True

dz = 4.0e-6 # m -- cell size in z
dr = 2*dz   # m -- cell size in r

# The simulation box
zmax = 0    # Right end of the simulation box (meters)
zmin = -120.0e-3  # Left end of the simulation box (meters)
rmax = 2.0e-3    # Length of the box along r (meters)

Nz = int((zmax-zmin)/dz) # Number of gridpoints along z

Nr = int(rmax/dr)        # Number of gridpoints along r

Nm = 3          # Number of modes used
#Nm = 2          #
#Nm = 1

# The particles
p_zmin = 0.0     # Position of the beginning of the plasma (meters)
p_zmax = 20.0    # Position of the end of the plasma (meters)
p_rmin = 0.0     # Minimal radial position of the plasma (meters)
p_rmax = 1.0e-3  # Maximal radial position of the plasma (meters)

# The simulation timestep
dt = dz/c # Timestep (seconds)
N_step = int((p_zmax-p_zmin)/dz) # Number of timesteps to simulate
#N_step = int(0.1/dz) # Number of timesteps to simulate

# Order of the stencil for z derivatives in the Maxwell solver.
# Use -1 for infinite order, i.e. for exact dispersion relation in
# all direction (adviced for single-GPU/single-CPU simulation).
# Use a positive number (and multiple of 2) for a finite-order stencil
# (required for multi-GPU/multi-CPU with MPI). A large `n_order` leads
# to more overhead in MPI communications, but also to a more accurate
# dispersion relation for electromagnetic waves. (Typically,
# `n_order = 32` is a good trade-off.)
# See https://arxiv.org/abs/1611.05712 for more information.
#n_order = -1
n_order=32

n_Rb = 7.0e20    # plasma density
p_nz = 1         # Number of particles per cell along z
p_nr = 2         # Number of particles per cell along r
p_nt = 4         # Number of particles per cell along theta

r_e = physical_constants['classical electron radius'][0] # m

kp = np.sqrt(4*np.pi*n_Rb*r_e) # 1/m -- plasma wavevector
wavelength = 2*np.pi/kp # m -- plasma wavelength

print("Plasma wavelength = %.2f mm" % (wavelength*1e3) )

# The proton bunch:
p_sigma_r =  0.8/kp # m
p_sigma_z =  300/kp  # m
p_head_z0 =  zmin - 9/kp  # m -- position of the bunch center
p_gamma   =  426.0
p_emitt_n =  2.20e-6 # m
microbunch_Np = 3.0e11 # Number of protons per bunch

# The moving window
v_window = c       # Speed of the window
#v_window = 0      # Speed of the window

# The diagnostics and the checkpoints/restarts
diag_period = int(N_step/50) # Period of the diagnostics in number of timesteps
save_checkpoints = True # Whether to write checkpoint files
checkpoint_period = int(N_step/3) # Period for writing the checkpoints
use_restart = False      # Whether to restart from a previous checkpoint
track_electrons = False  # Whether to track and write particle ids

# The density profile

n_step_start  = 5000/kp # m
n_step_length = 1000/kp # m
n_step = 0.03 # relative n-step
n_ramp_length = 2e-2 # m -- density ramp at the entrance

def dens_func( z, r ) :
    """Returns relative density at position z and r"""
    # Allocate relative density
    n = np.ones_like(z)
    # Make density step
    n = np.where( z>n_step_start, 1 + n_step*(z - n_step_start)/n_step_length, n)
    n = np.where( z>n_step_start+n_step_length, (1+n_step), n )
    # ramp up the density at the entrance
    n = np.where( z<n_ramp_length, (1 - np.cos(2*np.pi*z/n_ramp_length/2))/2, n )
    # Supress density for z < 0:
    n = np.where( z<0, 0.0, n )
    return(n)


# ---------------------------
# Carrying out the simulation
# ---------------------------
if __name__ == '__main__':

    # clean "diags/hdf5/" folder before new simulation:
    shutil.rmtree("diags/hdf5/", ignore_errors=True)

    # Initialize the simulation object

    sim = Simulation( Nz, zmax, Nr, rmax, Nm, dt,
                      zmin=zmin, boundaries='open', initialize_ions=False,
                      n_order=n_order, use_cuda=use_cuda )
    # By default the simulation initializes an electron species (sim.ptcl[0])
    # Because we did not pass the arguments `n`, `p_nz`, `p_nr`, `p_nz`,
    # this electron species does not contain any macroparticles.
    # It is okay to just remove it from the list of species.
    sim.ptcl = []


    # Add the rubidium ions (pre-ionized up to level 1),
    # and the associated electrons (from the pre-ionized level)
    atoms_Rb = sim.add_new_species( q=e, m=85.5*m_p, n=n_Rb,
                                    dens_func=dens_func, p_nz=p_nz, p_nr=p_nr, p_nt=p_nt,
                                    p_zmin=p_zmin, p_zmax=p_zmax, p_rmax=p_rmax )

    n_e = n_Rb

    elec = sim.add_new_species(     q=-e, m=m_e, n=n_e,
                                    dens_func=dens_func, p_nz=p_nz, p_nr=p_nr, p_nt=p_nt,
                                    p_zmin=p_zmin, p_zmax=p_zmax, p_rmax=p_rmax )


    add_particle_bunch_gaussian( sim, e, m_p, sig_r=p_sigma_r, sig_z=p_sigma_z, n_emit=p_emitt_n,
                                 gamma0=p_gamma, sig_gamma=3.5e-4*p_gamma,
                                 n_physical_particles=microbunch_Np,
                                 n_macroparticles=int(20e6), zf=p_head_z0 )

    sim.ptcl[2].track( sim.comm )

    if use_restart is False:
        # Track electrons if required (species 0 correspond to the electrons)
        if track_electrons:
            elec.track( sim.comm )
    else:
        # Load the fields and particles from the latest checkpoint file
        restart_from_checkpoint( sim )

    # Configure the moving window
    sim.set_moving_window( v=v_window )

    # Add diagnostics
    sim.diags = [
                FieldDiagnostic( diag_period, sim.fld, comm=sim.comm ),
                ParticleDiagnostic( diag_period,
                    {"electrons": elec, "bunch": sim.ptcl[2]},
                    comm=sim.comm ),
                # Since rho from `FieldDiagnostic` is 0 almost everywhere
                # (neutral plasma), it is useful to see the charge density
                # of individual particles
                ParticleChargeDensityDiagnostic( diag_period, sim,
                    {"electrons": elec, "bunch": sim.ptcl[2]} )
                ]
    # Add checkpoints
    if save_checkpoints:
        set_periodic_checkpoint( sim, checkpoint_period )

    ### Run the simulation

    print('Period of diagnostics = %i' % diag_period)

    sim.step( N_step )

In [38]:
%load_ext watermark
%watermark --python --date --iversions --machine
The watermark extension is already loaded. To reload it, use:
  %reload_ext watermark
matplotlib 3.0.3
holoviews  1.12.3
numpy      1.11.3
2019-09-11 

CPython 3.6.8
IPython 7.5.0

compiler   : GCC 7.3.0
system     : Linux
release    : 3.10.0-957.12.2.el7.x86_64
machine    : x86_64
processor  : x86_64
CPU cores  : 48
interpreter: 64bit
In [ ]: