Something went wrong on our end
-
Gamelin Alexis authoredGamelin Alexis authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
tools.py 6.09 KiB
# -*- coding: utf-8 -*-
"""
Tools and utilities functions for the Wakefield and impedances classes.
@author: Alexis Gamelin
@date: 14/01/2020
"""
import pandas as pd
import numpy as np
from scipy.integrate import trapz
from CollectiveEffect.wakefield import Wakefield, Impedance, WakeFunction
#from ..Tracking.synchrotron import Synchrotron
def read_CST(file, impedance_type='long'):
"""Read CST file format into an Impedance object."""
df = pd.read_csv(file, comment="#", header = None, sep = "\t",
names = ["Frequency","Real","Imaginary"])
df["Frequency"] = df["Frequency"]*1e9
df.set_index("Frequency", inplace = True)
result = Impedance(variable = df.index,
function = df["Real"] + 1j*df["Imaginary"],
impedance_type=impedance_type)
return result
def read_IW2D(file, impedance_type='long'):
"""Read IW2D file format into an Impedance object."""
df = pd.read_csv(file, sep = ' ', header = None,
names = ["Frequency","Real","Imaginary"], skiprows=1)
df.set_index("Frequency", inplace = True)
result = Impedance(variable = df.index,
function = df["Real"] + 1j*df["Imaginary"],
impedance_type=impedance_type)
return result
def loss_factor(wake, sigma):
"""
Compute the loss factor or the kick factor of a Wakefield, Impedance or
WakeFunction for a Gaussian bunch. Formulas from Eq. (2) p239 and
Eq.(7) p240 of [1].
Parameters
----------
wake: Wakefield, Impedance or WakeFunction object.
sigma: RMS bunch length in [s]
Returns
-------
kloss: loss factor in [V/C] or kick factor in [V/C/m] depanding on the
impedance type.
[1] Handbook of accelerator physics and engineering, 3rd printing.
"""
if isinstance(wake, Impedance):
positive_index = wake.data.index > 0
omega = 2*np.pi*wake.data.index[positive_index]
gaussian_spectrum = np.exp(-1*omega**2*sigma**2)
if(wake.impedance_type == "long"):
kloss = trapz(x = omega,
y = 1/np.pi*wake.data["real"][positive_index]*gaussian_spectrum)
if(wake.impedance_type == "xdip" or wake.impedance_type == "ydip"):
kloss = trapz(x = omega,
y = 1/np.pi*wake.data["imag"][positive_index]*gaussian_spectrum)
if isinstance(wake, WakeFunction):
# To be implemented.
pass
if isinstance(wake, Wakefield):
# To be implemented. Create field in wakefield with values ?
pass
return kloss
def spectral_density(frequency, sigma, m = 1, mode="Hermite"):
"""
Compute the spectral density of different modes for various values of the
head-tail mode number, based on Table 1 p238 of [1].
Parameters
----------
frequency : list or numpy array
sample points of the spectral density in [Hz]
sigma : float
RMS bunch length in [s]
m : int, optional
head-tail (or azimutal/synchrotron) mode number
mode: str, optional
type of the mode taken into account for the computation:
-"Hermite" modes for Gaussian bunches
Returns
-------
numpy array
[1] Handbook of accelerator physics and engineering, 3rd printing.
"""
if mode == "Hermite":
return (2*np.pi*frequency*sigma)**(2*m)*np.exp(
-1*(2*np.pi*frequency*sigma)**2)
else:
raise NotImplementedError("Not implemanted yet.")
def effective_impedance(ring, imp, m, mu, sigma, xi=None, mode="Hermite"):
"""
Compute the effective (longitudinal or transverse) impedance.
Formulas from Eq. (1) and (2) p238 of [1].
Parameters
----------
ring : Synchrotron object
imp : Impedance object
mu : int
coupled bunch mode number, goes from 0 to (M-1) where M is the
number of bunches
m : int
head-tail (or azimutal/synchrotron) mode number
sigma : float
RMS bunch length in [s]
xi : float, optional
(non-normalized) chromaticity
mode: str, optional
type of the mode taken into account for the computation:
-"Hermite" modes for Gaussian bunches
Returns
-------
Zeff : float
effective impedance in [ohm] or in [ohm/m] depanding on the impedance
type.
[1] Handbook of accelerator physics and engineering, 3rd printing.
"""
if not isinstance(imp, Impedance):
raise TypeError("{} should be an Impedance object.".format(imp))
fmin = imp.data.index.min()
fmax = imp.data.index.max()
if fmin > 0:
raise ValueError("A double sided impedance spectrum, with positive and"
" negative frequencies, is needed to compute the "
"effective impedance.")
if mode == "Hermite":
def h(f):
return spectral_density(frequency=f, sigma=sigma, m=m,
mode="Hermite")
else:
raise NotImplementedError("Not implemanted yet.")
M = 416
pmax = fmax/(ring.f0 * M) - 50
pmin = fmin/(ring.f0 * M) + 50
p = np.arange(pmin,pmax+1)
if imp.impedance_type == "long":
fp = ring.f0*(p*M + mu + m*ring.tune[2])
fp = fp[np.nonzero(fp)] # Avoid division by 0
num = np.sum( imp(fp) * h(fp) / (fp*2*np.pi) )
den = np.sum( h(fp) )
Zeff = num/den
elif imp.impedance_type == "xdip" or imp.impedance_type == "ydip":
if imp.impedance_type == "xdip":
tuneXY = ring.tune[0]
xi = ring.chro[0]
elif imp.impedance_type == "ydip":
tuneXY = ring.tune[1]
xi = ring.chro[1]
fp = ring.f0*(p*M + mu + tuneXY + m*ring.tuneS)
f_xi = xi/ring.eta*ring.f0
num = np.sum( imp(fp) * h(fp - f_xi) )
den = np.sum( h(fp - f_xi) )
Zeff = num/den
else:
raise TypeError("Effective impedance is only defined for long, xdip"
" and ydip impedance type.")
return Zeff