diff --git a/machines/soleil.py b/machines/soleil.py index d2fc712fab174388805c9711e5d4c11e1a4994f2..c8ac723c058f7c2d1860dc7859939cd7b0adbf55 100644 --- a/machines/soleil.py +++ b/machines/soleil.py @@ -32,30 +32,32 @@ def soleil(mode = 'Uniform'): # mean values beta = np.array([3, 1.3]) alpha = np.array([0, 0]) - disp = np.array([0, 0]) - dispp = np.array([0, 0]) - mean_val = Optics(beta, alpha, disp, dispp) - - ring = Synchrotron(h, L, E0, particle, ac=ac, U0=U0, tau=tau, - mean_optics=mean_val, emit=emit, tune=tune, - sigma_delta=sigma_delta, sigma_0=sigma_0, - chro=chro) - - if mode == 'Uniform': - beam = Beam(ring, ) - elif mode == 'Hybrid': - pass - elif mode == 'Low alpha/25': - pass - elif mode == 'Low alpha/25': - pass - elif mode == '8 bunches': - pass - elif mode == 'Single': - pass - else: - raise ValueError("{} is not a correct operation mode.".format(mode)) + dispersion = np.array([0, 0, 0, 0]) + optics = Optics(local_beta=beta, local_alpha=alpha, + local_dispersion=dispersion) + ring = Synchrotron(h, optics, particle, L=L, E0=E0, ac=ac, U0=U0, tau=tau, + emit=emit, tune=tune, sigma_delta=sigma_delta, + sigma_0=sigma_0, chro=chro) return ring +def v0313(): + """ + + """ + + h = 416 + particle = Electron() + tau = np.array([7.3e-3, 13.1e-3, 11.7e-3]) + emit = np.array([53.47e-12, 53.47e-12]) + sigma_0 = 9.2e-12 + sigma_delta = 9e-4 + + lattice_file = "SOLEIL_U_74BA_HOA_SYM02_V0313_cleaned.mat" + optics = Optics(lattice_file=lattice_file) + + ring = Synchrotron(h, optics, particle, tau=tau, emit=emit, + sigma_0=sigma_0, sigma_delta=sigma_delta) + + return ring \ No newline at end of file diff --git a/tracking/element.py b/tracking/element.py index c7811a7c9a39e894479e76f9069595cd4a8914e5..bee031ce02323679e53832f4745196ef33dc1666 100644 --- a/tracking/element.py +++ b/tracking/element.py @@ -153,11 +153,10 @@ class TransverseMap(Element): def __init__(self, ring): self.ring = ring - self.alpha = self.ring.mean_optics.alpha - self.beta = self.ring.mean_optics.beta - self.gamma = self.ring.mean_optics.gamma - self.disp = self.ring.mean_optics.disp - self.dispp = self.ring.mean_optics.dispp + self.alpha = self.ring.optics.local_alpha + self.beta = self.ring.optics.local_beta + self.gamma = self.ring.optics.local_gamma + self.dispersion = self.ring.optics.local_dispersion self.phase_advance = self.ring.tune[0:2]*2*np.pi @Element.parallel @@ -182,19 +181,19 @@ class TransverseMap(Element): # Horizontal matrix[0,0,:] = np.cos(phase_advance_x) + self.alpha[0]*np.sin(phase_advance_x) matrix[0,1,:] = self.beta[0]*np.sin(phase_advance_x) - matrix[0,2,:] = self.disp[0] + matrix[0,2,:] = self.dispersion[0] matrix[1,0,:] = -1*self.gamma[0]*np.sin(phase_advance_x) matrix[1,1,:] = np.cos(phase_advance_x) - self.alpha[0]*np.sin(phase_advance_x) - matrix[1,2,:] = self.dispp[0] + matrix[1,2,:] = self.dispersion[1] matrix[2,2,:] = 1 # Vertical matrix[3,3,:] = np.cos(phase_advance_y) + self.alpha[1]*np.sin(phase_advance_y) matrix[3,4,:] = self.beta[1]*np.sin(phase_advance_y) - matrix[3,5,:] = self.disp[1] + matrix[3,5,:] = self.dispersion[2] matrix[4,3,:] = -1*self.gamma[1]*np.sin(phase_advance_y) matrix[4,4,:] = np.cos(phase_advance_y) - self.alpha[1]*np.sin(phase_advance_y) - matrix[4,5,:] = self.dispp[1] + matrix[4,5,:] = self.dispersion[3] matrix[5,5,:] = 1 x = matrix[0,0,:]*bunch["x"] + matrix[0,1,:]*bunch["xp"] + matrix[0,2,:]*bunch["delta"] diff --git a/tracking/optics.py b/tracking/optics.py index 39b8fba50ac50932b9ec092c782c974151c4f854..089b271adddd11dcfa063f9c5d4d93e19d9f25dd 100644 --- a/tracking/optics.py +++ b/tracking/optics.py @@ -5,14 +5,121 @@ Created on Wed Mar 11 18:00:28 2020 @author: gamelina """ +import at +import numpy as np +import matplotlib.pyplot as plt +from scipy.interpolate import interp1d + + class Optics: """Handle optic functions""" - def __init__(self, beta, alpha, disp, dispp): - self.beta = beta - self.alpha = alpha - self.disp = disp - self.dispp = dispp + def __init__(self, lattice_file=None, local_beta=None, local_alpha=None, + local_dispersion=None, **kwargs): + + if lattice_file is not None: + self.use_local_values = False + self.load_from_AT(lattice_file, **kwargs) + if local_beta is None: + self.local_beta = np.mean(self.beta_array) + else: + self.local_beta = local_beta + if local_alpha is None: + self.local_alpha = np.mean(self.alpha_array) + else: + self.local_alpha = local_alpha + if local_dispersion is None: + self.local_dispersion = np.zeros((4,)) + else: + self.local_dispersion = local_dispersion + self.local_gamma = (1 + self.local_alpha**2)/self.local_beta + + else: + self.use_local_values = True + self.local_beta = local_beta + self.local_alpha = local_alpha + self.local_gamma = (1 + self.local_alpha**2)/self.local_beta + self.local_dispersion = local_dispersion + + def load_from_AT(self, lattice_file, **kwargs): + + self.n_points = int(kwargs.get("n_points", 1e3)) + periodicity = kwargs.get("periodicity") + + self.lattice = at.load_lattice(lattice_file) + lattice = self.lattice.slice(slices=self.n_points) + refpts = np.arange(0, len(lattice)) + twiss0, tune, chrom, twiss = at.get_twiss(lattice, refpts=refpts, + get_chrom=True) + + if periodicity is None: + self.periodicity = lattice.periodicity + else: + self.periodicity = periodicity + + for i in range(self.periodicity-1): + pos = np.append(twiss.s_pos, twiss.s_pos + twiss.s_pos[-1]*(i+1)) + + self.position = pos + self.beta_array = np.tile(twiss.beta.T, self.periodicity) + self.alpha_array = np.tile(twiss.alpha.T, self.periodicity) + self.gamma_array = (1 + self.alpha_array**2)/self.beta_array + self.dispersion_array = np.tile(twiss.dispersion.T, self.periodicity) + self.tune = tune * self.periodicity + self.chro = chrom * self.periodicity + self.ac = at.get_mcf(self.lattice) + + self.setup_interpolation() + + + def setup_interpolation(self): + self.betaX = interp1d(self.position, self.beta_array[0,:], + kind='linear') + self.betaY = interp1d(self.position, self.beta_array[1,:], + kind='linear') + self.alphaX = interp1d(self.position, self.alpha_array[0,:], + kind='linear') + self.alphaY = interp1d(self.position, self.alpha_array[1,:], + kind='linear') + self.gammaX = interp1d(self.position, self.gamma_array[0,:], + kind='linear') + self.gammaY = interp1d(self.position, self.gamma_array[1,:], + kind='linear') + self.dispX = interp1d(self.position, self.dispersion_array[0,:], + kind='linear') + self.disppX = interp1d(self.position, self.dispersion_array[1,:], + kind='linear') + self.dispY = interp1d(self.position, self.dispersion_array[2,:], + kind='linear') + self.disppY = interp1d(self.position, self.dispersion_array[3,:], + kind='linear') + + def beta(self, position): + if self.use_local_values: + return np.outer(self.local_beta, np.ones_like(position)) + else: + beta = [self.betaX(position), self.betaY(position)] + return np.array(beta) + + def alpha(self, position): + if self.use_local_values: + return np.outer(self.local_alpha, np.ones_like(position)) + else: + alpha = [self.alphaX(position), self.alphaY(position)] + return np.array(alpha) + + def gamma(self, position): + if self.use_local_values: + return np.outer(self.local_gamma, np.ones_like(position)) + else: + gamma = [self.gammaX(position), self.gammaY(position)] + return np.array(gamma) + + def dispersion(self, position): + if self.use_local_values: + return np.outer(self.local_dispersion, np.ones_like(position)) + else: + dispersion = [self.dispX(position), self.disppX(position), + self.dispY(position), self.disppY(position)] + return np.array(dispersion) + - @property - def gamma(self): - return (1 + self.alpha**2)/self.beta \ No newline at end of file diff --git a/tracking/particles.py b/tracking/particles.py index 0839a2f2e55d09b54490317e0b56128c1d174479..4b1b783d8da6e84a7cdd1905390f4d255d396a17 100644 --- a/tracking/particles.py +++ b/tracking/particles.py @@ -248,17 +248,17 @@ class Bunch: if cov is None: sigma_0 = kwargs.get("sigma_0", self.ring.sigma_0) sigma_delta = kwargs.get("sigma_delta", self.ring.sigma_delta) - optics = kwargs.get("optics", self.ring.mean_optics) + optics = kwargs.get("optics", self.ring.optics) cov = np.zeros((6,6)) - cov[0,0] = self.ring.emit[0]*optics.beta[0] - cov[1,1] = self.ring.emit[0]*optics.gamma[0] - cov[0,1] = -1*self.ring.emit[0]*optics.alpha[0] - cov[1,0] = -1*self.ring.emit[0]*optics.alpha[0] - cov[2,2] = self.ring.emit[1]*optics.beta[1] - cov[3,3] = self.ring.emit[1]*optics.gamma[1] - cov[2,3] = -1*self.ring.emit[1]*optics.alpha[1] - cov[3,2] = -1*self.ring.emit[1]*optics.alpha[1] + cov[0,0] = self.ring.emit[0]*optics.local_beta[0] + cov[1,1] = self.ring.emit[0]*optics.local_gamma[0] + cov[0,1] = -1*self.ring.emit[0]*optics.local_alpha[0] + cov[1,0] = -1*self.ring.emit[0]*optics.local_alpha[0] + cov[2,2] = self.ring.emit[1]*optics.local_beta[1] + cov[3,3] = self.ring.emit[1]*optics.local_gamma[1] + cov[2,3] = -1*self.ring.emit[1]*optics.local_alpha[1] + cov[3,2] = -1*self.ring.emit[1]*optics.local_alpha[1] cov[4,4] = sigma_0**2 cov[5,5] = sigma_delta**2 diff --git a/tracking/synchrotron.py b/tracking/synchrotron.py index bc3d7ada308940c0af3d446c80c619b99233d2c3..b32c3bc64a5eff43ac96f57ca1272e3fcb6c76cd 100644 --- a/tracking/synchrotron.py +++ b/tracking/synchrotron.py @@ -12,21 +12,33 @@ from scipy.constants import c, e class Synchrotron: """ Synchrotron class to store main properties. """ - def __init__(self, h, L, E0, particle, **kwargs): - self.particle = particle + + def __init__(self, h, optics, particle, **kwargs): self._h = h - self.L = L - self.E0 = E0 # Nominal (total) energy of the ring [eV] + self.particle = particle + self.optics = optics - self.ac = kwargs.get('ac') # Momentum compaction factor - self.U0 = kwargs.get('U0') # Energy loss per turn [eV] + if self.optics.use_local_values == False: + self.L = kwargs.get('L', self.optics.lattice.circumference) + self.E0 = kwargs.get('E0', self.optics.lattice.energy) + self.ac = kwargs.get('ac', self.optics.ac) + self.tune = kwargs.get('tune', self.optics.tune) + self.chro = kwargs.get('chro', self.optics.chro) + self.U0 = kwargs.get('U0', self.optics.lattice.energy_loss) + else: + self.L = kwargs.get('L') # Ring circumference [m] + self.E0 = kwargs.get('E0') # Nominal (total) energy of the ring [eV] + self.ac = kwargs.get('ac') # Momentum compaction factor + self.tune = kwargs.get('tune') # X/Y/S tunes + self.chro = kwargs.get('chro') # X/Y (non-normalized) chromaticities + self.U0 = kwargs.get('U0') # Energy loss per turn [eV] + self.tau = kwargs.get('tau') # X/Y/S damping times [s] self.sigma_delta = kwargs.get('sigma_delta') # Equilibrium energy spread self.sigma_0 = kwargs.get('sigma_0') # Natural bunch length [s] self.tune = kwargs.get('tune') # X/Y/S tunes self.emit = kwargs.get('emit') # X/Y emittances in [m.rad] self.chro = kwargs.get('chro') # X/Y (non-normalized) chromaticities - self.mean_optics = kwargs.get('mean_optics') # Optics object with mean values @property def h(self): @@ -34,7 +46,7 @@ class Synchrotron: return self._h @h.setter - def h(self,value): + def h(self, value): self._h = value self.L = self.L # call setter @@ -145,8 +157,12 @@ class Synchrotron: def sigma(self): """RMS beam size at equilibrium""" sigma = np.zeros((4,)) - sigma[0] = (self.emit[0]*self.mean_optics.beta[0] + self.mean_optics.disp[0]**2*self.sigma_delta)**0.5 - sigma[1] = (self.emit[0]*self.mean_optics.alpha[0] + self.mean_optics.dispp[0]**2*self.sigma_delta)**0.5 - sigma[2] = (self.emit[1]*self.mean_optics.beta[1] + self.mean_optics.disp[1]**2*self.sigma_delta)**0.5 - sigma[3] = (self.emit[1]*self.mean_optics.alpha[1] + self.mean_optics.dispp[1]**2*self.sigma_delta)**0.5 + sigma[0] = (self.emit[0]*self.optics.local_beta[0] + + self.optics.local_dispersion[0]**2*self.sigma_delta)**0.5 + sigma[1] = (self.emit[0]*self.optics.local_alpha[0] + + self.optics.local_dispersion[1]**2*self.sigma_delta)**0.5 + sigma[2] = (self.emit[1]*self.optics.local_beta[1] + + self.optics.local_dispersion[2]**2*self.sigma_delta)**0.5 + sigma[3] = (self.emit[1]*self.optics.local_alpha[1] + + self.optics.local_dispersion[3]**2*self.sigma_delta)**0.5 return sigma \ No newline at end of file