Here is a test of the account for the space-charge fields of a relativistic electron beam. We compare
with CHIMERA, where two modes of space-charge modeling are avaliable
In [1]:
%matplotlib inline
import numpy as np
from scipy.constants import e
import matplotlib.pyplot as plt
import sys
import ocelot as oclt
from chimera.moduls.species import Specie
from chimera.moduls.solvers import Solver
from chimera.moduls.chimera_main import ChimeraRun
from chimera.moduls.diagnostics import Diagnostics
from chimera.moduls import fimera as chimera
As a simplest test, we consider a 20 $\mu$m drift of a 30 pC electron beam with the gaussian density profiles $\sigma_z=\sigma_r=3$ $\mu$m, with the longitudinal momentum $p_z=50\, m_e c $. Initially beam has no energy spread and zero divergence.
In [2]:
SimLgth = 20.
pz0 = 50.
Chrg = 30e-12
Size = 3.
nmax = Chrg/e/((Size*1.e-4)**3*(2*np.pi)**1.5)/(1.1e21)
In [3]:
Np = int(1e6)
BeamCharge = Chrg
parts0 = np.zeros((6,Np))
parts0[0] = Size*1e-6*np.random.randn(Np)
parts0[2] = Size*1e-6*np.random.randn(Np)
parts0[4] = Size*1e-6*np.random.randn(Np)
p_array_init = oclt.ParticleArray(n=Np)
p_array_init.rparticles[:] = parts0 #.T.flatten()
p_array_init.E = (1+pz0**2)**.5*0.511e-3
p_array_init.s = 0.0
p_array_init.q_array = (BeamCharge/Np)*np.ones(Np)
D1 = oclt.Drift(l = SimLgth*1e-6 )
D2 = oclt.Drift(l=0)
cell = (D1,D2)
sc1 = oclt.SpaceCharge()
sc1.nmesh_xyz = [63, 63, 63]
sc1.low_order_kick = False
sc1.step = 1
method = oclt.MethodTM()
lat = oclt.MagneticLattice(cell,method=method)
print("length of the cell: ", lat.totalLen, "m")
navi = oclt.Navigator(lat)
navi.add_physics_proc(sc1,lat.sequence[0],lat.sequence[-1])
p_array = oclt.deepcopy(p_array_init)
dz = 1e-6
LL = p_array.s
SS = []
Sx = []
Sy = []
Sz = []
Ex = []
Ey = []
while LL<lat.totalLen:
oclt.tracking_step(lat, p_array, dz,navi)
proc_list = navi.get_proc_list()
for p in proc_list:
p.z0 = navi.z0
p.apply(p_array, dz)
LL += dz
sys.stdout.write('\r'+str(LL)+'/'+str(lat.totalLen))
sys.stdout.flush()
In [4]:
xmin, xmax = -7.*Size,7.*Size
lrg = 30*Size
Nx = 300
Nr = 300
dx = (xmax-xmin)/Nx
dr = lrg/Nr
dt = 1.0
solver_in = {
'Grid':(xmin, xmax,lrg,dx,dr),'TimeStep':dt,'MaxAzimuthMode':1,
'Features':('StaticKick',),'Xchunked':(4,6)
}
beam_in = {
'Grid':(xmin, xmax,lrg,dx,dr),'TimeStep':dt,'Charge':-1.,'Mass':1.,
'Density':nmax, 'FixedCell':(4,8,8),'MomentaMeans':(pz0,0.,0.),
'Xchunked':(4,6)
}
solver = Solver(solver_in)
beam = Specie(beam_in)
MovingFrame = {'TimeStep':dt,'Steps':1,'Features':('NoSorting','Staged')}
chimera_in = {
'Solvers':(solver,),'Particles':(beam,),'MovingFrames':(MovingFrame,)
}
fu = lambda x,y,z: np.exp(-0.5*(x**2+y**2+z**2)/Size**2)
beam.add_particles(*beam.gen_parts(Domain=(-3.5*Size,3.5*Size, 0.0, 3.5*Size),ProfileFunc=fu))
Chimera = ChimeraRun(chimera_in)
Diags = Diagnostics(Chimera,(),out_folder=None)
for i in range(int(SimLgth/dt)+1):
Chimera.make_step(i)
sys.stdout.write('\r'+str(i)+'/'+str(int(SimLgth/dt)))
sys.stdout.flush()
In [5]:
xmin, xmax = -7.*Size,7.*Size
lrg = 30*Size
Nx = 300
Nr = 300
dx = (xmax-xmin)/Nx
dr = lrg/Nr
dt = 0.06
solver_in1 = {
'Grid':(xmin, xmax,lrg,dx,dr),'TimeStep':dt,'MaxAzimuthMode':1,
'Features':('SpaceCharge',),'Xchunked':(4,6)
}
beam_in1 = {
'Grid':(xmin, xmax,lrg,dx,dr),'TimeStep':dt,'Charge':-1.,'Mass':1.,
'Density':nmax, 'FixedCell':(4,8,8),'MomentaMeans':(pz0,0.,0.),
'Features':(),'Xchunked':(4,6)
}
solver1 = Solver(solver_in1)
beam1 = Specie(beam_in1)
MovingFrame = {'TimeStep':dt,'Steps':1,'Features':('NoSorting','Staged')}
chimera_in1 = {
'Solvers':(solver1,),'Particles':(beam1,),'MovingFrames':(MovingFrame,)
}
fu = lambda x,y,z: np.exp(-0.5*(x**2+y**2+z**2)/Size**2)
beam1.add_particles(*beam1.gen_parts(Domain=(-3.5*Size,3.5*Size, 0.0, 3.5*Size),ProfileFunc=fu))
Chimera1 = ChimeraRun(chimera_in1)
Diags1 = Diagnostics(Chimera1,(),out_folder=None)
for i in range(int(SimLgth/dt)+1):
Chimera1.make_step(i)
sys.stdout.write('\r'+str(i)+'/'+str(int(SimLgth/dt)))
sys.stdout.flush()
In [6]:
fig,((ax1,ax2,ax3),(ax4,ax5,ax6)) = plt.subplots(2,3,figsize=(16,12),dpi=200)
dpmax = (beam.Data['momenta'][0].max()-pz0)*1e3
range1 = [[SimLgth-10,SimLgth+10],[-2*dpmax,+2*dpmax]]
range2=[[-15,15],[-2,2]]
weight2pC = beam.Args['weight2pC']
dns_max = 1e-3
ax1.hist2d(beam1.Data['coords'][0],(beam1.Data['momenta'][0]-pz0)*1e3,weights=-beam1.Data['weights'],
bins=120,range=range1,vmax=dns_max,cmap=plt.cm.spectral);
ax4.hist2d(beam1.Data['coords'][2],beam1.Data['momenta'][2]*1e4,weights=-beam1.Data['weights'],
bins=120,range=range2,vmax=dns_max,cmap=plt.cm.spectral);
ax2.hist2d(beam.Data['coords'][0],(beam.Data['momenta'][0]-pz0)*1e3,weights=-beam.Data['weights'],
bins=120,range=range1,vmax=dns_max,cmap=plt.cm.spectral);
ax5.hist2d(beam.Data['coords'][2],beam.Data['momenta'][2]*1e4,weights=-beam.Data['weights'],
bins=120,range=range2,vmax=5e-4,cmap=plt.cm.spectral);
ax3.hist2d((-p_array.tau()+p_array.s)*1e6,(p_array.p())*p_array.E*1e6/0.511,weights=p_array.q_array,
bins=120,range=range1,vmax=dns_max*weight2pC*1e-12,cmap=plt.cm.spectral);
ax6.hist2d(p_array.x()*1e6,p_array.px()*p_array.E*1e7/0.511,weights=p_array.q_array,
bins=120,range=range2,vmax=dns_max*weight2pC*1e-12,cmap=plt.cm.spectral);
ax1.set_title('CHIMERA full PIC',fontsize=18);
ax2.set_title('CHIMERA kicker',fontsize=18)
ax3.set_title('OCELOT',fontsize=18)
for ax in (ax4,ax5,ax6): ax.set_xlabel('coordinate ($\mu$m)',fontsize=18)
ax1.set_ylabel('$(p_\parallel-p_{z0})\cdot 10^3$',fontsize=18)
ax4.set_ylabel('$p_\perp\cdot 10^4$',fontsize=18);
In [7]:
fig,((ax1,ax2),(ax3,ax4)) = plt.subplots(2,2,figsize=(16,10),dpi=200)
extent = (solver1.Args['Xgrid'].min(),solver1.Args['Xgrid'].max(),
-solver1.Args['Rgrid'].max(),solver1.Args['Rgrid'].max())
ee = Diags1.fld_out( {'Features':('Return',)} )[0]
nn = Diags1.dns_out( {'Features':{'Return':0,'MaxMode':1}} )[0]
ex = np.real(np.hstack((ee[:,::-1,0,0] + ee[:,::-1,1,0],ee[:,1:,0,0] -ee[:,1:,1,0])))
ey = np.real(np.hstack((ee[:,::-1,0,1] - ee[:,::-1,1,1],ee[:,1:,0,1] +ee[:,1:,1,1])))
bz = np.real(np.hstack((ee[:,::-1,0,5] + ee[:,::-1,1,5],ee[:,1:,0,5] -ee[:,1:,1,5])))
ne = np.real(np.hstack((nn[:,::-1,0] + nn[:,::-1,1],nn[:,1:,0] -nn[:,1:,1])))
pl1 = ax1.imshow(ex.T ,aspect='auto', extent=extent,origin='lower',cmap=plt.cm.seismic)
pl2 = ax2.imshow((ey+bz).T ,aspect='auto', extent=extent,origin='lower',cmap=plt.cm.seismic)
extent = (solver.Args['Xgrid'].min(),solver.Args['Xgrid'].max(),
-solver.Args['Rgrid'].max(),solver.Args['Rgrid'].max())
ee = Diags.fld_out( {'Features':('Return',)} )[0]
nn = Diags.dns_out( {'Features':{'Return':0,'MaxMode':1}} )[0]
ex = np.real(np.hstack((ee[:,::-1,0,0] + ee[:,::-1,1,0],ee[:,1:,0,0] -ee[:,1:,1,0])))
ey = np.real(np.hstack((ee[:,::-1,0,1] - ee[:,::-1,1,1],ee[:,1:,0,1] +ee[:,1:,1,1])))
bz = np.real(np.hstack((ee[:,::-1,0,5] + ee[:,::-1,1,5],ee[:,1:,0,5] -ee[:,1:,1,5])))
ne = np.real(np.hstack((nn[:,::-1,0] + nn[:,::-1,1],nn[:,1:,0] -nn[:,1:,1])))
pl3 = ax3.imshow(ex.T ,aspect='auto', extent=extent,origin='lower',cmap=plt.cm.seismic)
pl4 = ax4.imshow((ey+bz).T ,aspect='auto', extent=extent,origin='lower',cmap=plt.cm.seismic)
#fig.colorbar(pl1,ax=ax1)
#fig.colorbar(pl2,ax=ax2)
#fig.colorbar(pl3,ax=ax3)
#fig.colorbar(pl4,ax=ax4)
ax1.set_title('$E_x$',fontsize=18);
ax2.set_title('$E_y$',fontsize=18)
for ax in (ax3,ax4,): ax.set_xlabel('x-coordinate ($\mu$m)',fontsize=18)
ax1.set_ylabel('CHIMERA full PIC \n y-coordinate ($\mu$m)',fontsize=18)
ax3.set_ylabel('CHIMERA kicker \n y-coordinate ($\mu$m)',fontsize=18)
Out[7]: