Simulated Quad Scan¶
This will show a simple 'simulation' of a quadrupole scan of a beam to a screen.
This assumes that the system consists of a thick quadrupole maget followed by an uncoupled linear transport map that the user provides. Here we will just use a 2.2 m long drift.
In [1]:
Copied!
%load_ext autoreload
%autoreload 2
%load_ext autoreload
%autoreload 2
In [2]:
Copied!
import numpy as np
import matplotlib.pyplot as plt
from pyemittance.emittance_calc import EmitCalc
from pyemittance.load_json_configs import load_configs
import numpy as np
import matplotlib.pyplot as plt
from pyemittance.emittance_calc import EmitCalc
from pyemittance.load_json_configs import load_configs
Load this config and add a drift.
In [3]:
Copied!
CONFIG = load_configs('LCLS2_OTR0H04')
CONFIG['beamline_info']
CONFIG = load_configs('LCLS2_OTR0H04')
CONFIG['beamline_info']
Out[3]:
{'name': 'LCLS2',
'species': 'electron',
'Lquad': 0.1244,
'energy': 80000000.0,
'Twiss0': [1e-06, 1e-06, 5.01, 5.01, 0.049, 0.049],
'rMatx': [1, 2.2, 0, 1],
'rMaty': [1, 2.2, 0, 1]}
Simulation¶
In [4]:
Copied!
from pyemittance.simulation import BeamSim
from pyemittance.simulation import BeamSim
In [5]:
Copied!
BUNCH_PARAMS0 = {
'total_charge': 50e-12,
'norm_emit_x': 1e-6,
'norm_emit_y': 2e-6,
'beta_x': 10,
'alpha_x': -1,
'beta_y': 11,
'alpha_y': -2,
'energy': 80e6,
'species':'electron'
}
BUNCH_PARAMS0 = {
'total_charge': 50e-12,
'norm_emit_x': 1e-6,
'norm_emit_y': 2e-6,
'beta_x': 10,
'alpha_x': -1,
'beta_y': 11,
'alpha_y': -2,
'energy': 80e6,
'species':'electron'
}
Create the simulation
In [6]:
Copied!
sim = BeamSim(bunch_params=BUNCH_PARAMS0, beamline_info=CONFIG['beamline_info'])
sim = BeamSim(bunch_params=BUNCH_PARAMS0, beamline_info=CONFIG['beamline_info'])
In [7]:
Copied!
sim.screen_sigma('x'), sim.screen_sigma('y')
sim.screen_sigma('x'), sim.screen_sigma('y')
Out[7]:
(np.float64(0.00031697503929054493), np.float64(0.0005391478977579372))
Set the scanning quadrupole (in machine units) and get the x and y beam sizes
In [8]:
Copied!
sim.quad_value = 2
sim.screen_beam_sizes()
sim.quad_value = 2
sim.screen_beam_sizes()
Out[8]:
(np.float64(0.0001262773648737005), np.float64(0.0011887442170694614))
In [9]:
Copied!
sim.plot_screen()
sim.plot_screen()
Set the noise level. This just adds random numbers with a maximum value of this level.
In [10]:
Copied!
sim.screen.noise=50
sim.plot_screen()
sim.screen.noise=50
sim.plot_screen()
Simple interaction
In [11]:
Copied!
from ipywidgets import interact
def f(quad_value):
sim.quad_value = quad_value
sim.plot_screen()
f(1)
# interact(f, quad_value=(-4, 4, .1), vmax=128)
from ipywidgets import interact
def f(quad_value):
sim.quad_value = quad_value
sim.plot_screen()
f(1)
# interact(f, quad_value=(-4, 4, .1), vmax=128)
In [12]:
Copied!
quad_vals = np.linspace(-2,2, 20)
meas = np.array([sim.beam_size_meas(v) for v in quad_vals])
meas_x = meas[:,0]
meas_y = meas[:,1]
quad_vals = np.linspace(-2,2, 20)
meas = np.array([sim.beam_size_meas(v) for v in quad_vals])
meas_x = meas[:,0]
meas_y = meas[:,1]
In [13]:
Copied!
plt.plot(quad_vals, meas_x*1e3, label='x')
plt.plot(quad_vals, meas_y*1e3, label='y')
plt.xlabel('quad value (machine units)')
plt.ylabel('Beam size (mm)')
plt.legend()
plt.plot(quad_vals, meas_x*1e3, label='x')
plt.plot(quad_vals, meas_y*1e3, label='y')
plt.xlabel('quad value (machine units)')
plt.ylabel('Beam size (mm)')
plt.legend()
Out[13]:
<matplotlib.legend.Legend at 0x7efdb57061d0>
Emittance calculation¶
In [14]:
Copied!
ef = EmitCalc({'x': quad_vals,'y': quad_vals},
{'x': meas_x ,'y': meas_y},
{'x': meas_x*0.03 ,'y': meas_y*0.03},
config_dict=CONFIG,
)
ef.plot = True
result = ef.get_emit()
result['norm_emit_x'], result['norm_emit_y'], result['beta_x'], result['beta_y'], result['alpha_x'], result['alpha_y']
ef = EmitCalc({'x': quad_vals,'y': quad_vals},
{'x': meas_x ,'y': meas_y},
{'x': meas_x*0.03 ,'y': meas_y*0.03},
config_dict=CONFIG,
)
ef.plot = True
result = ef.get_emit()
result['norm_emit_x'], result['norm_emit_y'], result['beta_x'], result['beta_y'], result['alpha_x'], result['alpha_y']
Out[14]:
(np.float64(9.99999999999999e-07), np.float64(1.9999999999999995e-06), np.float64(10.0), np.float64(10.999999999999998), np.float64(-1.000000000000001), np.float64(-1.999999999999999))
In [15]:
Copied!
print(ef.summary())
print(ef.summary())
"
Emittance Calculation Summary
Emittance x: 1.000 +/- 0.022 mm mrad
Emittance y: 2.000 +/- 0.046 mm mrad
Before scanning quad:
x y
norm_emit 1.00 2.00 (mm-mrad)
beta 10.00 11.00 (m)
alpha -1.00 -2.00 (1)
Image Analysis¶
PyEmittance has its own image analysis tools. Let's check these.
In [16]:
Copied!
sim.quad_value = -2
sim.plot_screen()
sim.quad_value = -2
sim.plot_screen()
Get a raw image. Note that images are always (row, column), in image coordinates (0,0) is in the top left).
In [17]:
Copied!
im = sim.screen_image()
im.shape
im = sim.screen_image()
im.shape
Out[17]:
(1040, 1392)
These are the true beam sizes
In [18]:
Copied!
sim_sigma_x, sim_sigma_y = sim.screen_beam_sizes()
sim_sigma_x, sim_sigma_y = sim.screen_beam_sizes()
In [19]:
Copied!
from pyemittance.image import Image
from pyemittance.image import Image
In [20]:
Copied!
raw_im = sim.screen_image()
im = Image(raw_im, sim.screen.nrow, sim.screen.ncol)
im.reshape_im()
plt.imshow(im.proc_image, vmax=128)
raw_im = sim.screen_image()
im = Image(raw_im, sim.screen.nrow, sim.screen.ncol)
im.reshape_im()
plt.imshow(im.proc_image, vmax=128)
Out[20]:
<matplotlib.image.AxesImage at 0x7efdac197bb0>
In [21]:
Copied!
profx, profy = im.get_im_projection()
plt.plot(profx, label="x-profile")
plt.plot(profy, label="y-profile")
plt.legend()
profx, profy = im.get_im_projection()
plt.plot(profx, label="x-profile")
plt.plot(profy, label="y-profile")
plt.legend()
Out[21]:
<matplotlib.legend.Legend at 0x7efdac1e3190>
In [22]:
Copied!
fit_res = im.get_sizes(method = "gaussian", show_plots = True)
xsize = fit_res["xrms"]
ysize = fit_res["yrms"]
xsize_error = fit_res["xrms_err"]
ysize_error = fit_res["yrms_err"]
x_amplitude = fit_res["xamp"]
y_amplitude = fit_res["yamp"]
fit_res = im.get_sizes(method = "gaussian", show_plots = True)
xsize = fit_res["xrms"]
ysize = fit_res["yrms"]
xsize_error = fit_res["xrms_err"]
ysize_error = fit_res["yrms_err"]
x_amplitude = fit_res["xamp"]
y_amplitude = fit_res["yamp"]
In [23]:
Copied!
sim_sigma_x, sim_sigma_y = sim.screen_beam_sizes()
resolution = sim.screen.resolution
meas_sigma_x = xsize * resolution
meas_sigma_y = ysize * resolution
sim_sigma_x, sim_sigma_y = sim.screen_beam_sizes()
resolution = sim.screen.resolution
meas_sigma_x = xsize * resolution
meas_sigma_y = ysize * resolution
These agree fairly well:
In [24]:
Copied!
meas_sigma_x/sim_sigma_x
meas_sigma_x/sim_sigma_x
Out[24]:
np.float64(0.9994258455662021)
In [25]:
Copied!
meas_sigma_y/sim_sigma_y
meas_sigma_y/sim_sigma_y
Out[25]:
np.float64(1.0015905084436798)
In [26]:
Copied!
im.proc_image
im.proc_image
Out[26]:
array([[ 6, 23, 31, ..., 4, 35, 45],
[38, 39, 45, ..., 21, 36, 45],
[46, 46, 6, ..., 24, 0, 8],
...,
[ 8, 7, 42, ..., 32, 30, 30],
[21, 24, 14, ..., 9, 1, 5],
[ 4, 9, 7, ..., 4, 2, 15]], shape=(1040, 1392), dtype=int16)