Example-42: Tune (Tune spread from quafrupole errors)

[1]:
# Import

from random import random
from pprint import pprint

import torch
from torch import Tensor

from pathlib import Path

import matplotlib
from matplotlib import pyplot as plt
matplotlib.rcParams['text.usetex'] = True

from model.library.line import Line

from model.command.external import load_sdds
from model.command.external import load_lattice
from model.command.build import build
from model.command.tune import tune
[2]:
# Load ELEGANT twiss

path = Path('ic.twiss')
parameters, columns = load_sdds(path)

nu_qx:Tensor = torch.tensor(parameters['nux'] % 1, dtype=torch.float64)
nu_qy:Tensor = torch.tensor(parameters['nuy'] % 1, dtype=torch.float64)

psi_qx:Tensor = torch.tensor(parameters['dnux/dp'], dtype=torch.float64)
psi_qy:Tensor = torch.tensor(parameters['dnuy/dp'], dtype=torch.float64)
[3]:
# Build and setup lattice

# Note, sextupoles are turned off and dipoles are linear

# Load ELEGANT table

path = Path('ic.lte')
data = load_lattice(path)

# Build ELEGANT table

ring:Line = build('RING', 'ELEGANT', data)
ring.flatten()

# Merge drifts

ring.merge()

# Turn off sextupoles and set linear dipoles

for element in ring:
    if element.__class__.__name__ == 'Sextupole':
        element.ms = 0.0
    if element.__class__.__name__ == 'Dipole':
        element.linear = True

# Set number of elements of different kinds

nb = ring.describe['BPM']
nq = ring.describe['Quadrupole']
ns = ring.describe['Sextupole']
[4]:
# Compute tune spread using MC

kns = 5.0E-3*torch.randn((8192, nq), dtype=torch.float64)

nuqx, nuqy = torch.vmap(lambda kn: tune(ring, [kn], ('kn', ['Quadrupole'], None, None), matched=True, limit=1, epsilon=None), chunk_size=1024)(kns).T

# Spread

print(nuqx.std(-1))
print(nuqy.std(-1))
print()

# Plot samples

fig, (ax, ay) = plt.subplots(1, 2, figsize=(12, 5))
ax.hist((nuqx - nuqx.mean()).cpu().numpy(), bins=100, range=(-5.0E-3, +5.0E-3), color='blue', alpha=0.7)
ay.hist((nuqy - nuqy.mean()).cpu().numpy(), bins=100, range=(-5.0E-3, +5.0E-3), color='blue', alpha=0.7)
plt.tight_layout()
plt.show()
tensor(0.0012, dtype=torch.float64)
tensor(0.0008, dtype=torch.float64)

../_images/examples_model-41_4_1.png
[5]:
# Compute tune derivatives and estimate spread from linear surrogate model using MC

# Compute derivative

kn = torch.zeros(nq, dtype=torch.float64)
dnu_dk = torch.func.jacrev(lambda kn: tune(ring, [kn], ('kn', ['Quadrupole'], None, None), matched=True, limit=1, epsilon=None))(kn)

# Sample

kns = 5.0E-3*torch.randn((8192, nq), dtype=torch.float64)
nuqx, nuqy = dnu_dk @ kns.T

# Spread

print(nuqx.std(-1))
print(nuqy.std(-1))
print()

# Plot samples

fig, (ax, ay) = plt.subplots(1, 2, figsize=(12, 5))
ax.hist((nuqx - nuqx.mean()).cpu().numpy(), bins=100, range=(-5.0E-3, +5.0E-3), color='blue', alpha=0.7)
ay.hist((nuqy - nuqy.mean()).cpu().numpy(), bins=100, range=(-5.0E-3, +5.0E-3), color='blue', alpha=0.7)
plt.tight_layout()
plt.show()
tensor(0.0012, dtype=torch.float64)
tensor(0.0008, dtype=torch.float64)

../_images/examples_model-41_5_1.png
[6]:
# Compute spread using error propagation

print((dnu_dk @ (5.0E-3*torch.eye(nq,  dtype=torch.float64))**2 @ dnu_dk.T).diag().sqrt())
tensor([0.0012, 0.0008], dtype=torch.float64)