Skip to content
Snippets Groups Projects
Unverified Commit a7e66c47 authored by tegenolf's avatar tegenolf Committed by GitHub
Browse files

Add notebook to week 6

parent ee7b50d2
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id:incomplete-medline tags:
<h1 style="text-align: center; vertical-align: middle;">Numerical Methods in Accelerator Physics</h1>
<h2 style="text-align: center; vertical-align: middle;">Python examples -- Week 6</h2>
%% Cell type:markdown id:executive-television tags:
<h2>Run this first!</h2>
Imports and modules:
%% Cell type:code id:biological-product tags:
``` python
from config6 import (np, plt, plot_rfwave, tqdm, trange,
beta, gamma, Machine, track_one_turn,
charge, mass, emittance, hamiltonian,
plot_hamiltonian, plot_rf_overview,
plot_dist, plot_mp)
from scipy.constants import m_p, e, c
%matplotlib inline
```
%% Cell type:markdown id:transsexual-renewal tags:
If the progress bar by `tqdm` (`trange`) later in this document does not work, run this:
%% Cell type:code id:maritime-warner tags:
``` python
#!jupyter nbextension enable --py widgetsnbextension
```
%% Cell type:markdown id:present-mother tags:
<h2>Simulation of Full CERN PS Acceleration Ramp</h2>
<h3>CERN PS Machine parameters</h3>
%% Cell type:markdown id:taken-reminder tags:
The CERN Proton Synchrotron
- has a circumference of 2π·100m
- takes protons from the PS Booster at a kinetic energy of 2GeV corresponding to a $\gamma=3.13$
- injects with 50kV of rf voltage, up to 200kV for ramp
- runs at harmonic $h=7$
- has a momentum compaction factor of $\alpha_c=0.027$
- typical acceleration rate of (up to) $\dot{B}=2$ T/s, the bending radius is $\rho=70.08$ m
%% Cell type:markdown id:outdoor-police tags:
We start by instantiating the PS, `Machine(...)`:
(<i>hint: hit both `Shift+Tab` keys inside the parentheses `()` below to get info about the possible arguments to `Machine` and the initial values</i>)
%% Cell type:code id:developed-canadian tags:
``` python
m = Machine()
assert m.phi_s > 0, "machine is not accelerating...?"
```
%% Cell type:markdown id:accepted-table tags:
You can check the rf systems setup by the following plot command from the previous lecture:
%% Cell type:code id:diagnostic-gibson tags:
``` python
plot_rf_overview(m);
```
%% Cell type:markdown id:configured-terminology tags:
We initialise a Gaussian bunch distribution with very small rms bunch length $\sigma_z=1$ m (so that the small-amplitude approximation holds):
%% Cell type:code id:figured-evening tags:
``` python
sigma_z=1
```
%% Cell type:markdown id:reduced-penalty tags:
The "matched" rms momentum spread $\sigma_{\Delta p}$ (remember, $\sigma_{\Delta p}$ and $\sigma_z$ are linked via equal Hamiltonian values $\mathcal{H}_0$, the equilibrium condition):
%% Cell type:code id:transparent-webmaster tags:
``` python
sigma_deltap = np.sqrt(
2 * m.p0() / -m.eta(0) *
charge * m.voltage * np.pi * m.harmonic / (beta(gamma(m.p0())) * c * m.circumference**2)
) * sigma_z
sigma_deltap
```
%% Cell type:markdown id:exempt-lender tags:
<h3>Generating Macro-particles via Box-Muller</h3>
%% Cell type:markdown id:realistic-marsh tags:
Limit by machine precision: the smallest number at FP64 is $\varepsilon\approx2^{-53}$, therefore one can only generate pseudo-random numbers from the Gaussian distribution up to an amplitude of $x$ where $\exp(-x^2/2)=2^{-53}$, i.e.
%% Cell type:code id:reduced-aside tags:
``` python
np.sqrt(2*-np.log(2**-53))
```
%% Cell type:markdown id:homeless-universe tags:
$\implies$ given $\sigma_z=1\,$</i>m<i>, no particles can be generated outside of $z=8.6\,$m and the equivalent Hamiltonian contour in phase space)
%% Cell type:code id:brief-presence tags:
``` python
N = 1000 # Number of macro-particles
```
%% Cell type:code id:classified-dietary tags:
``` python
np.random.seed(12345) # set seed to get always the same "random" distribution
z = np.random.normal(loc=0, scale=sigma_z, size=N) # generate N particle positions with sigma_z around z=0
deltap = np.random.normal(loc=0, scale=sigma_deltap, size=N) # generate N particle momenta with sigma_deltap around deltap=0
```
%% Cell type:code id:polyphonic-wagner tags:
``` python
plot_hamiltonian(m)
plt.scatter(z, deltap / m.p0(), marker='.', s=1)
```
%% Cell type:markdown id:extended-surfing tags:
<h3> Compute duration of ramp</h3>
$(\Delta\gamma)_\mathrm{turn} = \cfrac{\Delta E_\mathrm{tot}}{m_0c^2} = \cfrac{qV\sin(\varphi_s)}{m_0c^2}$
such that accelerating from $\gamma_\mathrm{ref}=3.1$ to $\gamma_\mathrm{ref}=27.7$ takes as many turns as
$n_\mathrm{turns}=\cfrac{27.7-3.1}{(\Delta\gamma)_\mathrm{turn}}$
%% Cell type:code id:quiet-rover tags:
``` python
dgamma_per_turn = charge * m.voltage * np.sin(m.phi_s) / (mass * c**2)
n_turns = int(np.ceil((27.7-3.13) / dgamma_per_turn))
n_turns
```
%% Cell type:markdown id:tropical-culture tags:
Record longitudinal emittance during tracking:
%% Cell type:code id:sufficient-negative tags:
``` python
epsn_z = np.zeros(n_turns, dtype=np.float64)
epsn_z[0] = emittance(z, deltap)
```
%% Cell type:markdown id:worth-currency tags:
Let's go tracking!
%% Cell type:code id:cooked-consciousness tags:
``` python
for i_turn in trange(1, n_turns):
z, deltap = track_one_turn(z, deltap, m)
epsn_z[i_turn] = emittance(z, deltap)
```
%% Cell type:markdown id:informal-agent tags:
We have reached the extraction energy of $\gamma=27.7$:
%% Cell type:code id:front-brunei tags:
``` python
m.gamma_ref
```
%% Cell type:markdown id:given-absence tags:
<h3>How did we do?</h3>
Check the rms emittance:
%% Cell type:code id:sharp-rescue tags:
``` python
plt.plot(np.arange(n_turns) / 100000, epsn_z / e)
plt.xlabel('Turns [100k]')
plt.ylabel('$\epsilon_z$ [eV.s]');
```
%% Cell type:markdown id:typical-collection tags:
$\implies$ Something went wrong, since emittance has increased significantly after a few thousand turns!
You can use the following phase-space plots as diagnostics. For this, you can stop the tracking after a certain turn to investigate, e.g. by changing the value of `n_turns` inside the `trange` counter in the `for` loop.
%% Cell type:code id:furnished-links tags:
``` python
plot_hamiltonian(m);
plt.scatter(z % 600, deltap / m.p0(), marker='.', s=1)
```
%% Cell type:code id:electrical-curve tags:
``` python
np.random.seed(12345) # set seed to get always the same "random" distribution
z = np.random.normal(loc=0, scale=sigma_z, size=N) # generate N particle positions with sigma_z around z=0
deltap = np.random.normal(loc=0, scale=sigma_deltap, size=N) # generate N particle momenta with sigma_deltap around deltap=0
m = Machine()
```
%% Cell type:code id:related-privacy tags:
``` python
for i_turn in trange(1,int(n_turns/100)):
z, deltap = track_one_turn(z, deltap, m)
epsn_z[i_turn] = emittance(z, deltap)
```
%% Cell type:code id:boring-comedy tags:
``` python
print(m.gamma_ref)
plot_hamiltonian(m);
plt.scatter(z, deltap / m.p0(), marker='.', s=1)
```
%% Cell type:code id:faced-princess tags:
``` python
m.gamma_ref
```
%% Cell type:code id:constant-newark tags:
``` python
np.sqrt(1/m.alpha_c)
```
%% Cell type:markdown id:amber-retreat tags:
<h3>Crossing transition</h3>
%% Cell type:code id:limited-reset tags:
``` python
np.random.seed(12345) # set seed to get always the same "random" distribution
z = np.random.normal(loc=0, scale=sigma_z, size=N) # generate N particle positions with sigma_z around z=0
deltap = np.random.normal(loc=0, scale=sigma_deltap, size=N) # generate N particle momenta with sigma_deltap around deltap=0
m = Machine()
```
%% Cell type:code id:visible-school tags:
``` python
phi_s_1 = np.pi - m.phi_s # synchronous phase after transition calculated from synchronous phase before transition
for i_turn in trange(1, n_turns):
z, deltap = track_one_turn(z, deltap, m)
epsn_z[i_turn] = emittance(z, deltap)
condition = m.gamma_ref > np.sqrt(1/m.alpha_c) # condition to change synchronous phase: gamma above gamma_transition
if condition:
m.phi_s = phi_s_1
```
%% Cell type:code id:capable-ladder tags:
``` python
plt.plot(np.arange(n_turns) / 100000, epsn_z / e)
plt.xlabel('Turns [100k]')
plt.ylabel('$\epsilon_z$ [eV.s]');
```
%% Cell type:markdown id:velvet-technician tags:
$\implies$ Emittance conserved up to about 2% if phase jump at transition is included. An additional jump in transition energy can be introduced to further reduce the emittance growth.
%% Cell type:markdown id:ae20d4a8-a9b7-44a3-9425-c6b2d893c141 tags:
<h2>PyHEADTAIL</h2>
<h3>RF Buckets in PyHEADTAIL</h3>
`PyHEADTAIL` provides a class to represent rf buckets (for plotting as well as for matching)
%% Cell type:code id:71dad750-f7c1-4381-9f7e-407a97c07e5b tags:
``` python
from PyHEADTAIL.trackers.rf_bucket import RFBucket
```
%% Cell type:markdown id:8d6ccb37-ce27-4195-bac1-00f89f3698fa tags:
We define a convenience function to provide `RFBucket` instance given a `Machine` instance:
%% Cell type:code id:b0499317-39f1-494c-a31e-0f78bae28411 tags:
``` python
def get_pyht_rfbucket(machine):
m = machine
deltap_per_turn = charge * m.voltage / (beta(gamma(m.p0())) * c) * np.sin(m.phi_s)
rfb = RFBucket(m.circumference, m.gamma_ref, mass, charge, [m.alpha_c], deltap_per_turn, [m.harmonic], [m.voltage], [np.pi + m.phi_s])
# PyHEADTAIL has a different convention for the phase and is offset by pi compared to our lecture
return rfb
```
%% Cell type:markdown id:7a8af807-0422-4664-b509-5aa3d92ff35e tags:
<h3>Visualising the Distributions in the RF Bucket</h3>
%% Cell type:code id:bd8760ec-8bc6-4ef3-ab92-f4128d60a5de tags:
``` python
m = Machine(gamma_ref=3.13)
```
%% Cell type:code id:9c8820d4-2aaf-4cfb-976f-6765b6827395 tags:
``` python
rfb = get_pyht_rfbucket(m)
```
%% Cell type:code id:5356375e-6127-4cff-8451-7e8c534621a0 tags:
``` python
from PyHEADTAIL.particles.rfbucket_matching import (ThermalDistribution, WaterbagDistribution, ParabolicDistribution)
```
%% Cell type:code id:884fbafd-c180-4942-97e5-4abb2078e45b tags:
``` python
sigma_z = 8
```
%% Cell type:markdown id:63a1dc42-5f32-44af-8529-87ac778762b9 tags:
Computing the (initial) guess for $\mathcal{H}_0$ based on the small-amplitude approximation:
%% Cell type:code id:f4cf9828-321f-44a4-994d-1a5977634959 tags:
``` python
H0 = rfb.guess_H0(sigma_z, from_variable='sigma')
H0
```
%% Cell type:code id:58189c7a-755e-4202-8963-450df183ef18 tags:
``` python
plot_dist(ThermalDistribution, rfb, H0=0.05*H0);
```
%% Cell type:code id:7b5a10a6-3dac-41a0-8cd4-2e5944803f16 tags:
``` python
plot_dist(WaterbagDistribution, rfb, H0=2*H0)
```
%% Cell type:code id:27b62728-ba57-4ef9-9f01-e78dab20da03 tags:
``` python
plot_dist(ParabolicDistribution, rfb, H0=H0*2)
```
%% Cell type:code id:d1f9bbc3-5614-45de-9a86-4218daa1eef1 tags:
``` python
```
%% Cell type:code id:d82d4bfc-2b95-46fd-abc8-ef7fee8ad21e tags:
``` python
```
%% Cell type:code id:189c0360-5951-4a3f-b5fb-23aef3be6699 tags:
``` python
```
%% Cell type:markdown id:cb45ec0d-9701-4b32-abf3-b1a50c89553e tags:
Matching algorithm implemented in the `RFBucketMatcher` class in `PyHEADTAIL`
%% Cell type:code id:82aad28a-6034-4a3c-97cd-586649bc787c tags:
``` python
from PyHEADTAIL.particles.generators import RFBucketMatcher
```
%% Cell type:code id:dbe611ba-b719-4882-b37e-9afe74c0b014 tags:
``` python
rfb_matcher = RFBucketMatcher(rfb, ThermalDistribution, sigma_z=sigma_z)
rfb_matcher.integrationmethod = 'cumtrapz'
```
%% Cell type:markdown id:d1955099-6310-4904-a4a0-7165b6ab72fb tags:
Calling the `RFBucketMatcher.generate` method will
(1.) iterate on $\mathcal{H}_0$ until the numerical integration of $\psi(\mathcal{H})$ for the bunch length converges to $\hat{\sigma}_z$, and then
(2.) sample this $\psi(\mathcal{H})$ by <b>rejection sampling</b> (see previous lecture) to generate the macro-particle phase-space coordinates $(z,\delta)$
%% Cell type:code id:0446ef82-1470-437b-9abc-31d17cf3ec64 tags:
``` python
z, delta, _, _ = rfb_matcher.generate(int(1e5))
```
%% Cell type:code id:b27ac4a3-d224-4e1a-9c86-73946a9e583a tags:
``` python
print("Converged value of H0: " + str(rfb_matcher.psi_object.H0))
print("Small-amplitude approximation: " + str(rfb.guess_H0(sigma_z, from_variable='sigma')))
```
%% Cell type:markdown id:f9d774c3-3dbb-4abb-9ed6-3b421c720f98 tags:
$\implies$ For smaller target $\hat{\sigma}_z$ the values are closer together.
%% Cell type:markdown id:19929620-574d-41f1-bf8b-88b3d4fd10e8 tags:
Let's have a look at the generated macro-particle distribution:
%% Cell type:code id:98bb9062-c240-4fb9-8f14-e07d6d4d77a1 tags:
``` python
plot_mp(z, delta, rfb);
```
%% Cell type:markdown id:23d74231-fd49-44d9-a127-501e0bf2b99a tags:
Does the rms bunch length of the macro-particle distribution match the chosen target $\hat{\sigma}_z$?
%% Cell type:code id:b92822f5-582e-47bd-a46b-144e040a8043 tags:
``` python
print("RMS bunch length: " + str(np.std(z)))
print("Target sigma_z: " + str(sigma_z))
```
%% Cell type:markdown id:acc29308-7c1f-4afa-9575-ed0189255f9b tags:
<h2>Root-finding</h2>
<h3>Let's use scipy.optimize ...</h3>
%% Cell type:code id:c8d090fd-eb15-47f6-9391-11e2de309d32 tags:
``` python
from scipy.optimize import brentq, newton
```
%% Cell type:markdown id:9807ca23-733f-4440-925f-56fe27b873bd tags:
At what $x$ does $\cfrac{1}{\sqrt{x}+1}$ take the value `0.4`?
%% Cell type:code id:e676a536-1fab-4762-a7a6-61fd173928b0 tags:
``` python
def f(x):
return 1 / (np.sqrt(x) + 1) - 0.4
```
%% Cell type:markdown id:e5184f7d-a7f1-4d7a-b304-e9a877df8b20 tags:
Brent-Dekker algorithm with interval $x\in[0, 4]$:
%% Cell type:code id:f5fb0a95-f8d4-496f-adbe-1e3eb1bbd87f tags:
``` python
brentq(f, 0, 4)
```
%% Cell type:markdown id:2812695d-dc9b-4548-8203-a7a6fb4e4e32 tags:
<h3>... and Newton's Secant method</h3>
%% Cell type:markdown id:384e3c1f-916a-4938-bee4-eb5d30f3e806 tags:
Secant method with initial values $x_0=0$, $x_1=10^{-4}$:
%% Cell type:code id:a81bdb20-73d6-4a38-9b72-b8f12e89a03a tags:
``` python
newton(f,0)
```
%% Cell type:markdown id:b3a79de2-4c83-44a2-9af0-8a4de02f71c1 tags:
<h3>How does the function look like?</h3>
%% Cell type:code id:a7a4b9ac-d8b6-4173-a6cc-71677a83e364 tags:
``` python
x = np.linspace(0, 4, 1000)
```
%% Cell type:code id:e98a563d-618c-4ced-9104-86491335242e tags:
``` python
plt.plot(x, f(x))
plt.axhline(0, c='k', lw=2)
plt.xlabel('$x$')
plt.ylabel('$f(x)$');
```
%% Cell type:markdown id:9f85826c-5f43-42cc-b8cc-3a10d1192801 tags:
<h3>Implementing the Secant method</h3>
%% Cell type:code id:2d9a869d-2580-4674-9746-97e47deb177f tags:
``` python
def secant_method(f, x0, x1, iterations, rtol=2e-12):
"""Return the root calculated using the secant method."""
for i in range(iterations):
x2 = x1 - f(x1) * (x1 - x0) / float(f(x1) - f(x0))
if np.abs(x2 - x1) / x1 < rtol:
break
x0, x1 = x1, x2
return x2
```
%% Cell type:code id:e0e38a3b-5b40-42cb-902b-14deba599c1e tags:
``` python
secant_method(f, 0, 1, 100)
```
%% Cell type:markdown id:d845eda7-42f8-4e85-8b8c-edefc28eb0db tags:
(The `scipy` implementation of the secant method in `newton()` [computes](https://github.com/scipy/scipy/blob/v1.9.3/scipy/optimize/_zeros_py.py#L330) the second guess $x_1$ as $x_1=x_0\cdot 1.0001\pm 0.0001$.)
%% Cell type:markdown id:b35e9444-6f32-453f-90d3-306636bd7f87 tags:
<h2>Numerical emittance growth</h2>
%% Cell type:markdown id:aba93342-c608-47d7-b263-b3e4f97c2ec8 tags:
Start with the bi-Gaussian (simulation as previous lecture):
%% Cell type:code id:bbbcee55-3dd5-407a-8f59-d44b974578e0 tags:
``` python
m = Machine(phi_s=0)
```
%% Cell type:code id:fb8c6faf-d25d-474b-adca-fa4cb50263a7 tags:
``` python
sigma_z = 13.5
```
%% Cell type:code id:45d704d6-a229-40bf-b83a-c59a319f1932 tags:
``` python
def generate_gaussian_in_rfbucket(N, sigma_z, machine, seed=12345, margin=0.05):
'''Generate a bi-Gaussian distribution with N macro-particles,
rms bunch length sigma_z and a matched sigma_deltap via the
machine settings.
'''
np.random.seed(seed)
m = machine
sigma_deltap = np.sqrt(
2 * m.p0() / -m.eta(0) *
charge * m.voltage * np.pi * m.harmonic / (beta(gamma(m.p0())) * c * m.circumference**2)
) * sigma_z
z_ini = np.random.normal(loc=0, scale=sigma_z, size=N)
deltap_ini = np.random.normal(loc=0, scale=sigma_deltap, size=N)
H_safetymargin = margin * hamiltonian(0, 0, m)
H_values = hamiltonian(z_ini, deltap_ini, m) - H_safetymargin
while any(H_values >= 0):
mask_bad = H_values >= 0
N_bad = np.sum(mask_bad)
print (N_bad)
# re-initialise bad particles:
z_ini[mask_bad] = np.random.normal(loc=0, scale=sigma_z, size=N_bad)
deltap_ini[mask_bad] = np.random.normal(loc=0, scale=sigma_deltap, size=N_bad)
# re-evaluate rejection condition
H_values = hamiltonian(z_ini, deltap_ini, m) - H_safetymargin
return z_ini, deltap_ini
```
%% Cell type:code id:1eb0b492-b2fc-4ecd-8de0-55fa81dd81d8 tags:
``` python
N = 10000
n_turns = 5000
```
%% Cell type:code id:f9021cbc-4e26-4847-88bd-9fda118c36ed tags:
``` python
z_ini, deltap_ini = generate_gaussian_in_rfbucket(N, sigma_z, m)
```
%% Cell type:code id:423f9dc9-c246-42da-861b-e0255dc34f4d tags:
``` python
plot_mp(z_ini, deltap_ini / m.p0(), rfb=get_pyht_rfbucket(m), n_bins=20);
```
%% Cell type:markdown id:a3c290ef-e08e-43d3-b28a-c788ca72b5b1 tags:
Track the bi-Gaussian distribution...
%% Cell type:code id:4d49d207-236d-434b-9097-29941e8a5ba1 tags:
``` python
z = np.zeros((n_turns, N), dtype=np.float64)
deltap = np.zeros_like(z)
z[0] = z_ini
deltap[0] = deltap_ini
```
%% Cell type:code id:df20e1d7-7748-4c96-be03-0aec3cdf1322 tags:
``` python
for i_turn in trange(1, n_turns):
z[i_turn], deltap[i_turn] = track_one_turn(z[i_turn - 1], deltap[i_turn - 1], m)
```
%% Cell type:markdown id:607dd837-74b5-4258-9f70-5fb4098b2858 tags:
The rms emittance evolution:
%% Cell type:code id:ae3aa012-7673-4e4a-9176-37a6e8185161 tags:
``` python
epsn_z = np.array([emittance(z_i, deltap_i) for z_i, deltap_i in zip(z, deltap)])
ylim_m = np.median(4 * np.pi * epsn_z / e)
ylim_d = 1.1 * np.max(np.abs(ylim_m - 4 * np.pi * epsn_z / e))
```
%% Cell type:code id:e7121496-7d6e-4e0c-bfe4-fe93d0e24da6 tags:
``` python
plt.plot(4 * np.pi * epsn_z / e)
plt.ylim(ylim_m - ylim_d, ylim_m + ylim_d)
plt.xlabel('Turns')
plt.ylabel('$4\pi\epsilon_z$ [eV.s]');
```
%% Cell type:markdown id:b83c18bd-7a7a-4411-b6e5-14950c44fe8e tags:
$\leadsto$ the Gaussian particle distribution is <b>not exactly</b> in equilibrium for sufficiently large rms values in the nonlinear potential, the particles <b>filament</b> and the rms emittance grows (a little)!
$\implies$ compare to using full nonlinear Hamiltonian to construct PDF $\psi(\mathcal{H})\propto\exp\left(\cfrac{\mathcal{H}}{\mathcal{H}_0}\right)$
%% Cell type:code id:c30eff4c-3b47-4758-9b69-f015eb8ffa44 tags:
``` python
rfb = get_pyht_rfbucket(m)
rfb_matcher = RFBucketMatcher(rfb, ThermalDistribution, sigma_z=sigma_z)
rfb_matcher.integrationmethod = 'cumtrapz'
```
%% Cell type:code id:0a60a6f1-876e-4f9d-b0ae-dae66b218188 tags:
``` python
z_ini, delta_ini, _, _ = rfb_matcher.generate(N)
```
%% Cell type:code id:382bc26d-321d-4dbf-bb65-dbbe49827bbd tags:
``` python
deltap_ini = delta_ini * m.p0()
```
%% Cell type:code id:8ef7c870-cd39-4fcd-9c23-160e3980ccb2 tags:
``` python
plot_mp(z_ini, delta_ini, rfb, n_bins=20);
```
%% Cell type:markdown id:5f818b3e-df01-4551-b519-14436b114c86 tags:
Track the matched thermal distribution...
%% Cell type:code id:b160b0a7-4ad2-46fd-8a81-7b6ab778917b tags:
``` python
z = np.zeros((n_turns, N), dtype=np.float64)
deltap = np.zeros_like(z)
z[0] = z_ini
deltap[0] = deltap_ini
```
%% Cell type:code id:e28afb33-0562-4128-ab00-5aa24608509f tags:
``` python
for i_turn in trange(1, n_turns):
z[i_turn], deltap[i_turn] = track_one_turn(z[i_turn - 1], deltap[i_turn - 1], m)
```
%% Cell type:markdown id:a9a97d61-7998-465d-afbe-d3f6c00acddb tags:
The rms emittance evolution:
%% Cell type:markdown id:fae06e09-37bf-4032-8f6f-43d44cb321b6 tags:
The rms emittance evolution:
%% Cell type:code id:4e1e39a1-5f92-47af-840a-7ca55f48adc4 tags:
``` python
epsn_z = np.array([emittance(z_i, deltap_i) for z_i, deltap_i in zip(z, deltap)])
ylim_m = np.median(4 * np.pi * epsn_z / e)
```
%% Cell type:code id:943c4c16-c27c-43ef-90ba-d76b5a96d4d4 tags:
``` python
plt.plot(4 * np.pi * epsn_z / e)
plt.ylim(ylim_m - ylim_d, ylim_m + ylim_d)
plt.xlabel('Turns')
plt.ylabel('$4\pi\epsilon_z$ [eV.s]');
```
%% Cell type:markdown id:f04d5b2a-564b-4a15-a113-486f46748eae tags:
$\implies$ this result shows that the nonlinearly matched thermal distribution is in equilibrium from the start (up to macro-particle noise, the fluctuations reduce with $1/\sqrt{N}$)!
%% Cell type:markdown id:4a335edf-1cb0-4a9b-9499-3679ce396e5d tags:
<h2>Physical emittance growth: Dipole injection mismatch</h2>
%% Cell type:code id:dc77bb22-52f8-4273-bb53-3a09c3e7e324 tags:
``` python
m = Machine(phi_s=0)
sigma_z = 8
z_ini, deltap_ini = generate_gaussian_in_rfbucket(N, sigma_z, m, margin=0.15)
```
%% Cell type:markdown id:baef42d4-f6e7-4af1-a6d6-ffba009ffcbb tags:
Simulate a dipole injection mismatch (e.g. when the rf phase is not well synchronised between the injector and the synchrotron):
%% Cell type:code id:471d66b4-0574-4d6f-a167-4bd7ba001156 tags:
``` python
z_ini -= 0.5 * sigma_z
```
%% Cell type:markdown id:53e01f17-642f-43bf-ac8c-30c0cb7d9c41 tags:
4 meter mismatch in $z$ correspond to a phase mismatch of 16 degree:
%% Cell type:code id:b952ef70-5a26-4354-b2d9-21c652db06cc tags:
``` python
4 / (m.circumference / m.harmonic) * 360
```
%% Cell type:code id:39c3dce3-1df1-43df-828f-653860c2d656 tags:
``` python
plot_mp(z_ini, deltap_ini / m.p0(), rfb=get_pyht_rfbucket(m), n_bins=20);
```
%% Cell type:markdown id:95944d0d-3053-4639-a9ef-fb1bd9ed1126 tags:
$\implies$ note the offset towards negative $z$, the contours of the macro-particle density are no longer matched to the Hamiltonian contours.
%% Cell type:markdown id:c6800012-8075-4472-b721-c47a7a1de54f tags:
The safety `margin` inside the separatrix (where no particles are generated in `generate_gaussian_in_rfbucket`) should be chosen large enough such that no particles are located outside the rf bucket after the mismatch:
%% Cell type:code id:1d86c381-37a4-404c-aa63-987be0aa19a2 tags:
``` python
assert all(hamiltonian(z_ini, deltap_ini, m) < 0), 'particles have been generated outside the rf bucket!'
```
%% Cell type:markdown id:c41a169f-41ae-4e3c-b2e5-6aa2d8e30674 tags:
Tracking the mismatched distribution of macro-particles:
%% Cell type:code id:5beb74ef-1408-4738-9117-c82aa6fae6d4 tags:
``` python
z = np.zeros((n_turns, N), dtype=np.float64)
deltap = np.zeros_like(z)
z[0] = z_ini
deltap[0] = deltap_ini
```
%% Cell type:code id:5d4168ce-213a-4695-ab0a-55e91f42c02d tags:
``` python
for i_turn in trange(1, n_turns):
z[i_turn], deltap[i_turn] = track_one_turn(z[i_turn - 1], deltap[i_turn - 1], m)
```
%% Cell type:markdown id:0d5c6857-6693-4dae-85b4-ac7dd8fb1071 tags:
<h3>Centroid results</h3>
%% Cell type:code id:3677d73a-542a-4965-8f16-38e60b4e5e8c tags:
``` python
plt.plot(np.mean(z, axis=1))
plt.xlabel('Turns')
plt.ylabel(r'$\langle z \rangle$ [m]');
```
%% Cell type:markdown id:e0334d3b-bd04-49e0-bd90-4d8fc8e8bea9 tags:
$\implies$ exponential decay of the initial offset (due to the non-linearity of the rf bucket)
%% Cell type:markdown id:d7107e24-9854-40d1-9523-95f5d1e24822 tags:
<h3>RMS bunch length results</h3>
%% Cell type:code id:14148630-7eff-49ca-b1e4-4dacc1cd97e0 tags:
``` python
plt.plot(np.std(z, axis=1))
plt.xlabel('Turns')
plt.ylabel(r'$\sigma_z$ [m]');
```
%% Cell type:markdown id:6e0b8011-7571-4280-8b6c-a989b885b094 tags:
$\implies$ saturation of the rms bunch length growth
%% Cell type:markdown id:645f0d2b-344a-4830-b015-ff3843c50e78 tags:
<h3>RMS emittance results</h3>
%% Cell type:code id:d4e0f5d1-8833-4595-bacb-70023f184a7c tags:
``` python
epsn_z = np.array([emittance(z_i, deltap_i) for z_i, deltap_i in zip(z, deltap)])
```
%% Cell type:code id:e0f375a6-28ad-4c39-813f-c85cb6021f73 tags:
``` python
plt.plot(epsn_z / e)
plt.xlabel('Turns')
plt.ylabel('$\epsilon_z$ [eV.s]');
```
%% Cell type:markdown id:7788ba39-d406-46d2-a5f0-87f2762eb1db tags:
$\implies$ in this example, 10% emittance growth as a result of the 4 meter injection offset.
%% Cell type:code id:27c3287f-29da-46ff-bed8-e4808f3de18c tags:
``` python
plot_mp(z[-1], deltap[-1] / m.p0(), rfb=get_pyht_rfbucket(m), n_bins=40);
```
%% Cell type:markdown id:d40cdc11-5ebb-4090-976e-f99aab567ab5 tags:
$\implies$ the filamentation of the macro-particle distribution is clearly visible!
%% Cell type:markdown id:6523b6d7-2984-4eb8-b8d3-2968a93cba41 tags:
<h2>Physical emittance growth: Quadrupole injection mismatch</h2>
%% Cell type:code id:ea3648e7-7b00-4d17-b0bb-6d0f9fa41b45 tags:
``` python
m = Machine(phi_s=0)
sigma_z = 8
z_ini, deltap_ini = generate_gaussian_in_rfbucket(N, sigma_z, m, margin=0.15)
```
%% Cell type:markdown id:09a54392-f60b-4563-9c10-793328382fd9 tags:
Simulate a quadrupole injection mismatch (e.g. when the rf voltage (rf bucket height) is not matched between the injector and the synchrotron):
%% Cell type:code id:a77e1f8c-89e6-44a7-b7ed-ef37ca41476f tags:
``` python
deltap_ini *= 0.5
```
%% Cell type:code id:edba9fe9-61e1-4343-906c-abcb62a76b2e tags:
``` python
plot_mp(z_ini, deltap_ini / m.p0(), rfb=get_pyht_rfbucket(m), n_bins=20);
```
%% Cell type:code id:19ba693a-f643-47fb-ad0b-47632f7f4940 tags:
``` python
```
%% Cell type:markdown id:0360ab47-0c87-44ea-a45f-fd9b1700b531 tags:
$\implies$ note the squeezed rms momentum spread, the contours of the macro-particle density are no longer matched to the Hamiltonian contours.
%% Cell type:code id:5d37bd16-d02d-4d62-a19b-ffeb5c948c9c tags:
``` python
```
%% Cell type:code id:fd445783-47b6-4315-a067-a5d516fe4424 tags:
``` python
```
%% Cell type:markdown id:cc5856eb-9c81-44ba-9457-2a18cc706937 tags:
Tracking the mismatched distribution of macro-particles:
%% Cell type:code id:2ad36092-7775-49e4-b1a4-3796e097cf00 tags:
``` python
```
%% Cell type:code id:42df2535-76cf-4647-b217-ff65e1ca0ab2 tags:
``` python
z = np.zeros((n_turns, N), dtype=np.float64)
deltap = np.zeros_like(z)
z[0] = z_ini
deltap[0] = deltap_ini
```
%% Cell type:code id:8550522f-11b0-4b53-bf27-e44e1f3f1696 tags:
``` python
for i_turn in trange(1, n_turns):
z[i_turn], deltap[i_turn] = track_one_turn(z[i_turn - 1], deltap[i_turn - 1], m)
```
%% Cell type:markdown id:faa79c31-bcab-4ab8-b198-b3dd8114e897 tags:
<h3>Centroid results</h3>
%% Cell type:code id:2a1d11bc-c25c-4f61-84a4-0b1aad5be9e2 tags:
``` python
plt.plot(np.mean(z, axis=1))
plt.xlabel('Turns')
plt.ylabel(r'$\langle z \rangle$ [m]');
```
%% Cell type:markdown id:f5b846ea-c561-4d05-96b7-86a6a18ff723 tags:
$\implies$ only residual centroid fluctuations (due to macro-particle noise), note the amplitude of the oscillation in comparison to the rf bucket length!
%% Cell type:markdown id:c81c20cb-5118-4129-8b73-11b256e31ada tags:
<h3>RMS bunch length results</h3>
%% Cell type:code id:e1c65718-496f-4882-8cb5-5cc6bea08bd7 tags:
``` python
plt.plot(np.std(z, axis=1))
plt.xlabel('Turns')
plt.ylabel(r'$\sigma_z$ [m]');
```
%% Cell type:markdown id:069a9bd6-bd05-4554-a7e7-6e7c9eeb696d tags:
$\implies$ exponential decay of the initial momentum mismatch (due to the non-linearity of the rf bucket)
%% Cell type:markdown id:d3204663-1fbe-49d9-af46-eef592089372 tags:
<h3>RMS emittance results</h3>
%% Cell type:code id:1bae04d7-57e7-47d5-bd16-9960bf9d8141 tags:
``` python
epsn_z = np.array([emittance(z_i, deltap_i) for z_i, deltap_i in zip(z, deltap)])
```
%% Cell type:code id:9bc47eca-c715-4352-9e9e-6f18112c9d57 tags:
``` python
plt.plot(epsn_z / e)
plt.xlabel('Turns')
plt.ylabel('$\epsilon_z$ [eV.s]');
```
%% Cell type:markdown id:bc13e46e-0d5f-4a08-bd2c-adff48bf2380 tags:
$\implies$ in this example, 20% emittance growth as a result of the 50% momentum spread mismatch.
%% Cell type:code id:02cb8496-e4d3-4131-bda8-f88957c8ff75 tags:
``` python
plot_mp(z[-1], deltap[-1] / m.p0(), rfb=get_pyht_rfbucket(m), n_bins=40);
```
%% Cell type:markdown id:58404467-c2fd-491c-87f5-42848bd0e7d9 tags:
$\implies$ again, the filamentation of the macro-particle distribution is clearly visible!
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment