I am learning particle-in-cell (PIC) python code. PIC currently represents one of the most important plasma simulation tools. It is particularly suited to the study of kinetic or non-Maxwellian effects.
Given the following dispersion relation
I found the range of wave numbers k for which the oscillation frequency is imaginary to be -|\frac{w}{v_0}| < k < |\frac{w}{v_0}|
What I am trying to understand is how to find the minimum grid length L_{min} as a function of \frac{v_0}{w}. L_{min} indicates the needed minimum grid length to support such unstable modes.
I think we should be able to study the plasma behaviour for both L < L_{min} and L > L_{min}. I was told I should adjust the number of simulation particles to grid points to improve the statistics. Besides, the number of particles per cell (i.e. npart/ngrid) should be fixed and should be much greater than 1, in order to reduce numerical noise. The runtime needed (here in units of ω_p^−1) to observe the instability can be estimated from the maximum growth rate.
Here's the full python 3 code I am working with. Please note I have little experience with coding so I might ask lots of follow up questions. Thank you.
#! /usr/bin/python
#
# Python script for computing and plotting single charged particle
# trajectories in prescribed electric and magnetic fields.
# Roughly equivalent to boris.m matlab program
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import Slider, Button, RadioButtons
from mpl_toolkits.mplot3d import Axes3D
import os
import os.path
import sys
from sys import exit
from time import sleep
# ===================================
#
# Function to integrate particle trajectory
# in given E, B fields
#
# ===================================
def integrate(E0, B0, vz0):
global dt, v0, x0, xp, yp, zp, qom, larmor, nsteps
wc=qom*B0 # cyclotron frequency
larmor=vperp/wc
print ("Cyclotron frequency =",wc)
print ("Perpendicular velocity v_p=",vperp)
print ("Larmor radius=",larmor)
norm = 1. # choose whether to normalise plot axes dimensions to Larmor radius
trun=5*2*np.pi/wc # total runtime
dt=.1/wc # timestep - adjust to current B-field
nsteps=int(trun/dt) # timesteps
E=np.array([0.,E0,0.]) # initial E-field
B=np.array([0.,0.,B0]) # initial B-field
u=np.array([0.,0.,0.]) # intermediate velocity
h=np.array([0.,0.,0.]) # normalized B-field
xp[0]=x0[0]
yp[0]=x0[1]
zp[0]=x0[2]
v0[2]=vz0 # z-component
v=v0+.5*dt*qom*(E+np.cross(v0,B)) # shift initial velocity back 1/2 step
x=x0
for itime in range(1,nsteps):
x=x+dt*v
xp[itime]=x[0] /norm
yp[itime]=x[1] /norm
zp[itime]=x[2] /norm
tp[itime]=itime*dt
#
# Boris mover: solves dv/dt = q/m*(E + vxB) to 2nd order accuracy in dt
#
qomdt2 = dt*qom/2
h = qomdt2*B
s=2*h/(1+np.dot(h,h))
u = v + qomdt2*E
up=u+np.cross(u+np.cross(u,h),s)
v=up+qomdt2*E
# vxp[itime] = v[0]
# ===================================
# Make 2D plots of particle orbit
#
# ===================================
def plot_track2D():
global xp,yp,nsteps,ax1
fig = plt.figure(figsize=(8,8)) # initialize plot
xmin=np.min(xp)
xmax=np.max(xp)
ymin=np.min(yp)
ymax=np.max(yp)
fig.add_subplot(221) # 1st subplot in 2x2 arrangement
plt.cla()
plt.grid(True, which='both')
plt.xlim( (xmin, xmax) )
plt.ylim( (ymin, ymax) )
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.plot(xp[0:nsteps],yp[0:nsteps],c='b')
fig.add_subplot(222) # 2nd subplot
# fig.add_subplot(223) # 2nd subplot
# fig.add_subplot(224) # 2nd subplot
plt.draw()
plt.savefig('./particle_orbit.png') # Save plot to file
# ===================================
#
# Make 3D plot of particle orbit
#
# ===================================
def plot_track3D():
global xp,yp,zp,nsteps,ax1
xmin=np.min(xp)
xmax=np.max(xp)
ymin=np.min(yp)
ymax=np.max(yp)
zmin=np.min(zp)
zmax=np.max(zp)
ax1.cla()
plt.ion()
plt.grid(True, which='both')
ax1.set_xlim( (xmin, xmax) )
ax1.set_ylim( (ymin, ymax) )
ax1.set_zlim( (zmin, zmax) )
ax1.set_xlabel('$x $ [m]')
ax1.set_ylabel('$y $ [m]')
ax1.set_zlabel('$z $ [m]')
#ax1.set_aspect(1.)
ax1.scatter(xp,yp,zp,c=tp,marker='o') # tracks coloured by elapsed time since start
plt.draw()
# =============================================
#
# Main program
#
# =============================================
print ("Charged particle orbit solver")
plotboxsize = 8.
animated = True
x0=np.array([0.,0.,0.]) # initial coords
vz0=0.
v0=np.array([-1e2,0.,vz0]) # initial velocity
vperp = np.sqrt(v0[0]**2+v0[2]**2)
E0=0.
B0=.1
e=1.602176e-19 # electron charge
m=9.109e-31 # electron mass
qom=e/m # charge/mass ratio
wc=qom*B0 # cyclotron frequency
larmor=vperp/wc
print (wc,vperp,larmor)
trun=5*2*np.pi/wc # total runtime
dt=.1/wc # timestep - adjust to current B-field
nsteps=int(trun/dt) # timesteps
B1=np.array([0.,0.,0.1]) # gradient B perturbation
#wc=qom*np.linalg.norm(B) # cyclotron frequency
#nsteps=2
tp = np.zeros(nsteps) # variables to store particle tracks
xp = np.zeros(nsteps)
yp = np.zeros(nsteps)
zp = np.zeros(nsteps)
vxp = np.zeros(nsteps)
vyp = np.zeros(nsteps)
vzp = np.zeros(nsteps)
# Compute orbit
integrate(E0, B0, vz0)
# 2D orbit plotter
plot_track2D()
exit(0) # Quit script before 3D plot - comment out to continue!
# Start 3D interactive mode with sliders for B, E and v0
plt.ion() # Turn on interactive plot display
fig = plt.figure(figsize=(8,8))
# Get instance of Axis3D
ax1 = fig.add_subplot(111, projection='3d')
# Get current rotation angle
print (ax1.azim)
# Set initial view to x-y plane
ax1.view_init(elev=90,azim=0)
ax1.set_xlabel('$x $[microns]')
ax1.set_ylabel('$y $[microns]')
ax1.set_zlabel('$z $[microns]')
plot_track3D()
#filename = 'a0_45/parts_p0000.%0*d'%(6, ts)
#plot_from_file(filename):
axcolor = 'lightgoldenrodyellow'
axe0 = fig.add_axes([0.1, 0.95, 0.3, 0.03])#, facecolor=axcolor) # box position, color & size
axb0 = fig.add_axes([0.5, 0.95, 0.3, 0.03])#, facecolor=axcolor)
axv0 = fig.add_axes([0.1, 0.9, 0.3, 0.03])#, facecolor=axcolor)
sefield = Slider(axe0, 'Ey [V/m]', -5.0,5.0, valinit=E0)
sbfield = Slider(axb0, 'Bz [T]', -1.0, 1.0, valinit=B0)
svz = Slider(axv0, 'vz [m/s]', 0.0, 1.0, valinit=0.)
def update(val):
E0 = sefield.val
B0 = sbfield.val
vz0 = svz.val
integrate(E0,B0,vz0)
plot_track3D()
plt.draw()
sefield.on_changed(update)
sbfield.on_changed(update)
svz.on_changed(update)
resetax = fig.add_axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
global ax1
sefield.reset()
sbfield.reset()
svz.reset()
ax1.cla()
ax1.set_xlabel('$x $[microns]')
ax1.set_ylabel('$y $[microns]')
ax1.set_xlim( (0., 10.) )
# ax1.set_ylim( (-sigma, sigma) )
ax1.grid(True, which='both')
plt.draw()
button.on_clicked(reset)
#plt.show()
plt.show(block=False)
$$x=3$$
From algorithmic stability concerns there are only upper bounds on the two variables grid length and time step, dx and dt taken together. Put very informally, in one time step a particle must not travel a longer distance than from a grid cell to one of its direct neighbours. There is no lower limit on grid spacing until you come into conflict with the resolution of your numerical data type or run out of computing memory, or the time to execute one simulation run takes far too long.
I am having some troubles getting the desired results when using Multiplot with a specified size and margins. I have attached a picture shows the issue when plotting my data. I am interested in merging plots with the same scale. For that I specified xsize= 0.40, ysize= 0.90, and xinit= 0.10 as in the code below. When the data files were plotted, the y-axis of the right plot was a bit lower than that of the left plot. If I was not looping, everything seems to work as expected. I even tried to move xsize= 0.40, ysize= 0.90, and xinit= 0.10 inside the for loop but no luck.
set term png
xsize= 0.40
ysize= 0.90
xinit= 0.10
do for [i=1:files] {
set output sprintf("Picture(x=-%d).png",i)
set multiplot layout 1,2
# Reset keys
unset rmargin
unset lmargin
# Set individual keys
set size xsize, ysize # Plot size in relation with canvas
set lmargin at screen xinit # x inital possition 0.10
set rmargin at screen xinit + xsize # x final possition 0.50
set xlabel "x[m]" offset 2,0 # Displace xlabel to the center of canvas
set ylabel "y[m]"
plot filename(i) using 4:3 with lines notitle
# Reset keys
unset rmargin # Clears the past x position
unset lmargin
unset label # Clears the past label
unset ytics # Removes the y axis tics
unset ylabel # Removes the y axis label
set size xsize, ysize # Plot size in relation with canvas
set lmargin at screen xinit+xsize # x inital possition 0.50
set rmargin at screen 1 - xinit # x final possition 0.90
plot filename(i) using 6:3 with lines notitle
unset multiplot
}
I wonder what could be the issue in my code. Thanks for the help.
For your use case you should use the spacing and margins options for multiplot:
set multiplot layout 1,2 margins 0.1,0.1,0.05,0.05 spacing 0,0
I basically want to draw 2d color surface (or contour plot)
of rosenbrock function f(x,y) = (a-x)^2 + b * (y-x*x) ^2
and append some points (x,y) on this image.
Sample file with points looks as follows:
#x #y
15.00000 12.00000
8.00000 9.00000
The thing is, both graphs do not share the same coordinate system on output image:
coordinate systems do not overlap on each other
gnuplot code:
#!/usr/bin/env gnuplot
reset
set terminal png size 700,700
enhanced set output 'output.png'
set tmargin screen 1
set bmargin screen 0
set border 0 back
set size square
xr=20
yr=20
set xrange [-xr:xr]
set yrange [-yr:yr]
unset key #disablegraph name
unset colorbox
set surface
set multiplot
set view map
set cntrparam levels 10# contour tenderness
set style data pm3d
set pm3d
set contour
a=1 #rosenbrock parameter
b=1 #rosenbrock parameter
#set isosamples 50
splot (a-x) * (a-x) + b * (y-x*x) * (y-x*x) # 2d rosenbrock
unset view
unset pm3d
plot 'data.dat' pt 5, 'data.dat' using 1:2:($0+1) with labels offset 1 notitle
mixing 2d and surface plots with multiplot is usually a mess. I guess you probably don't need multiplot in this simple case. Maybe something like this is enough:
set size square
xr=20
yr=20
set xrange [-xr:xr]
set yrange [-yr:yr]
unset key
unset colorbox
set surface
set pm3d map
set contour
set cntrparam levels 10# contour tenderness
rosenbrock(x,y,a,b)= (a-x) * (a-x) + b * (y-x*x) * (y-x*x)
splot rosenbrock(x,y,1,1) w pm3d, 'data.dat' u 1:2:0 w p pt 5, 'data.dat' using 1:2:(1):($0+1) with labels offset 1,1 notitle
I try to make a figure using multiplot, but some axes are weird (see figure). I use epslatex to generate a standalone tex file. It looks like a "box" limits the axis, but I cannot figure out what the corresponding option is.
The code used to generate the figure is below.
reset
set term epslatex color standalone header \
"\\usepackage[utf8]{inputenc}\n\\usepackage[T1]{fontenc}\n\\usepackage{cmbright}\n"
set output 'correlations.tex'
set pm3d at b
set pm3d map
unset surface
set pm3d corners2color c4
eps = 1.e-6
f(x)=(log(abs(x)/eps+1))*sgn(x)
set palette functions 2*(gray), 1-2*abs((gray)-.5), 2-2*(gray)
zmax = 1
set cbrange [f(-zmax):f(zmax)]
SX=0.8; SY=0.8
set bmargin 0; set tmargin 0; set lmargin 0; set rmargin 0
X0=0.1; Y0=0.1
DX=0.46; DY=0.64
set size X0+SX+DX,Y0+SY+DY
set origin 0,0
#
# Multiplot
#
set multiplot
#
# bottom left
#
set origin X0,Y0
unset colorbox
unset key
set size square SX,SY
xmax = 5
xshift = 0
set xrange [-xmax-xshift:xmax-xshift]
set yrange [-xmax:xmax]
set xlabel '$x_\parallel$'
set ylabel '$x_\perp$'
set xtics -5,5,5
set ytics -5,5,5
datafile = '../Résultats/correl_pm_smooth2f1.0cutoff5.0xMax5.0nX61.dat'
splot datafile u 1:2:(f($3)) w l t '',\
datafile u 1:(-$2):(f($3)) w l t ''
#
# top left
#
set origin X0,Y0+DY
set xlabel ''
set ylabel ''
set xtics ("" -5, "" 0., "" 5)
datafile = '../Résultats/correl_pm_smooth2f0.0cutoff5.0xMax5.0nX61.dat'
splot datafile u 1:2:(f($3)) w l t '',\
datafile u 1:(-$2):(f($3)) w l t ''
#
# top right
#
set origin X0+DX,Y0+DY
set xtics ("" -5, "" 0., "" 5)
set ytics ("" -5, "" 0., "" 5)
datafile = '../Résultats/correl_pm_smooth2f0.5cutoff5.0xMax5.0nX61.dat'
splot datafile u 1:2:(f($3)) w l t '',\
datafile u 1:(-$2):(f($3)) w l t ''
set origin X0+DX,Y0
set colorbox
set xtics -5,5,5
datafile = '../Résultats/correl_pm_smooth2f5.0cutoff5.0xMax5.0nX61.dat'
splot datafile u 1:2:(f($3)) w l t '',\
datafile u 1:(-$2):(f($3)) w l t ''
unset multiplot
I found the answer. With the epslatex terminal, the size and position of the various items refer to the size of the canvas and should thus be contained in the 1x1 box.
Changing the size of the terminal is done via "set term epslatex size 5,5…".
We have a large spectrogram that needs to be pixel perfect (1 row = 100ms of data, 1 column = 1 frequency bin of a fft). I use the below code to calculate the size of the plot:
set terminal unknown
sedcmd="<(sed -n '1p;" .rowstart. "," .rowend. "p' " .filename. ".csv)"
plot sedcmd nonuniform matrix using 2:1:3 notitle with image
xspan = GPVAL_DATA_X_MAX - GPVAL_DATA_X_MIN
yspan = GPVAL_DATA_Y_MAX - GPVAL_DATA_Y_MIN
set terminal png size (rowend-rowstart),yspan
sedcmd="<(sed -n '1p;" .rowstart. "," .rowend. "p' " .filename. ".csv)"
plot sedcmd nonuniform matrix using 2:1:3 notitle with image
rowstart and rowend are variables passed in to gnuplot which represent the frequency bins. This works fine with one exception, it doesn't account for the space needed for the legend and labels. How can I either calculate, or set the pixels so that:
[ylabels][ PLOT ][LEGEND]
[ xlabels ]
PLOT will be the exact size I specify (ie: 1000x1000)
EDIT: final calculation code for the plotsize and margins:
# margins and plotsize
rowstart = 2457 # rowstart/end represent the fft bins
rowend = 5734 # plot is actually rotated 90deg (rows are cols)
cols = 6970 # number of ms in plot (plotted rows)
plotwidth = (rowend - rowstart) +1
plotheight = cols
lm = 1200.00
rm = 600
tbmargin = 200.00
width = plotwidth + (lm + rm)
height = plotheight + (tbmargin * 2)
set lmargin at screen lm / width
set rmargin at screen 1 - (rm / width)
set tmargin at screen tbmargin / height
set bmargin at screen 1 - (tbmargin / height)
show margin
If you know the exact size of the plot you want it helps a lot. If you want a 1000x1000 plot, you can start with:
s = 1250 # size of plot
set terminal pngcairo size 1250,1250
set output 'spectrogram.png'
# difference between l/r and t/b margins = (0.9-0.1)*1250 = 1000 px
set lmargin at screen 0.1
set rmargin at screen 0.9
set bmargin at screen 0.1
set tmargin at screen 0.9
plot ...
I find that doing some basic algebra and using the *margin commands is very helpful when fiddling with pixel-perfect plot shapes (examples here and here).
There may be a 1 px difference or so--I am not sure if the axes are drawn from 0 to 1 px or -1 to 0 as far as the plot edges are concerned.