'''
Impedance module to manage impedance object creation from a txt file exported
from CST database or thanks to (i) the resisitive wall impedance model (for a
circular pipe) or (ii) broad-band resonator model.
The created impedance consists in a list of numpy.ndarray: the first element is
the frequency vector, the second is the impedance.
* date: 12/12/2022
* author: Francesco Giordano, Elena de la Fuente, Leonardo Sito
'''
import numpy as np
import csv
import copy
from bihc.plot import Plot
[docs]class Impedance(Plot):
"""Define a Beam Coupling Impedance curve.
It is possible to define an impedance curve using: (i) the broad-band
resonator model, (ii) the resistive wall impedance of a circular beam pipe,
and (iii) importing an external impedance text file (like the one that can
be exported from CST).
Mixin class for the Beam class.
Parameters
----------
f : numpy.ndarray, default np.linspace(0.1,2e9,int(1e5))
Frequency array of the impedance curve.
This is the numpy array containing the frequency point of the impedance
curve. by default it is a linearly spaced vector going from 0.1 Hz to 2
GHz with 10'000 points.
CST_file : str, deafult 0
File name with extension of the txt file containing the impedance curve.
Attributes
----------
f : numpy.ndarray, default np.linspace(0.1,2e9,int(1e5))
Frequency array of the impedance curve [Hz].
Zr : numpy.ndarray
Real part of the impedance in the speciefied frequency points [Ohm].
Zi : numpy.ndarray
Imaginary part of the impedance in the speciefied frequency points [Ohm].
"""
def __init__(self, f=np.linspace(0.1,2e9,int(1e5)), CST_file=None):
self.f = f
self.Zr = np.zeros(len(f))
self.Zi = np.zeros(len(f))
self.isResonatorImpedance = False
self.isRWImpedance = False
if CST_file is not None:
self.getImpedanceFromCST(CST_file)
def __add__(self, Zn):
Z = Impedance(self.f)
if not np.array_equal(self.f, Zn.f):
Z.Zr = np.interp(self.f, Zn.f, Zn.Zr)
Z.Zi = np.interp(self.f, Zn.f, Zn.Zi)
else:
Z.Zr = Zn.Zr
Z.Zi = Zn.Zi
Z.Zr += self.Zr
Z.Zi += self.Zi
return Z
[docs] def getResonatorImpedance(self,Rs,Qr,fr,f=np.linspace(0.1,2e9,int(1e5))):
"""Creating the impedance curve from the broad-band resonator model.
This methods creates an impedance curve using the broad-band resonator
model. It requires a shunt impedance value, a quality factor value, and
the resonant frequency value.
Parameters
----------
Rs : float
Shunt impedance in the broad-band resonator model [Ohm].
Qr : float
Quality factor in the broad-band resonator model.
fr : float
Resonant frequency in the broad-band resonator model [Hz].
Returns
-------
[f, Z] : numpy.ndarray list
Impedance curve. Returns the list of numpy arrays [frequency,
complex impedance].
"""
f = self.f
mask1 = f == 0
f[mask1] = 1e-5
Z = Rs/(1+ 1j*Qr*(f/fr - fr/f))
self.Zr = np.real(Z)
self.Zi = np.imag(Z)
self.Z = Z
self.f = f
self.fr = fr
self.Rs = Rs
self.Qr = Qr
self.isResonatorImpedance = True
return [self.f, self.Zr+1j*self.Zi]
[docs] def getRWImpedance(self, L ,b, sigma, f=np.linspace(0.1,2e9,int(1e5))):
"""Creating the impedance curve from the resistive wall impedance model.
This methods creates an impedance curve using the resistive wall
impedance model for a resistive pipe of circular section with.
It considers the thick wall regime.
Parameters
----------
L : float
Length of the pipe in the resistive wall impedance model [m].
b : float
Pipe radius in the resisitve wall impedance model [m].
sigma : float
Electrical conductivity of the pipe in the resistive wall
impedance model [Siemens/m].
Returns
-------
[f, Z] : numpy.ndarray list
Impedance curve. Returns the list of numpy arrays [frequency,
complex impedance].
"""
Z0 = 376.73
c = 3e8
f = self.f
# !n.b. problem with the signum
Z = (1+1j*np.sign(f))*L/(2*np.pi*b)*np.sqrt(Z0*np.abs(2*np.pi*f)/(2*c*sigma))
self.Zr = np.real(Z)
self.Zi = np.imag(Z)
self.Z = Z
self.b = b
self.sigma = sigma
self.L = L
self.isRWImpedance = True
return [self.f, self.Zr+1j*self.Zi]
[docs] def getImpedanceFromCST(self, path, unit='GHz', skip_header=2, skip_footer=0):
"""Creating the impedance curve from CST file.
This methods creates an impedance curve using a txt file, usually
exported from CST.
Parameters
----------
path : str
File name with extension.
unit : str, default "GHz"
Units of the frquency array from the simulation. Either GHz or MHz.
skip_header : int, default 2
Line index to skip to at the beginning of the txt file.
skip_footer : int, default 0
Line index from which to skip to at the end of the txt file.
Returns
-------
[f, Z] : numpy.ndarray list
Impedance curve. Returns the list of numpy arrays [frequency,
complex impedance].
"""
data = np.genfromtxt(path, skip_header=skip_header, skip_footer=skip_footer)
if unit == 'GHz':
self.f= data[:,0]*1e9
elif unit == 'MHz':
self.f= data[:,0]*1e6
self.Zr = data[:,1]
try:
self.Zi = data[:,2]
except:
self.Zi = np.zeros_like(self.Zr)
self.Z = self.Zr+1j*self.Zi
return [self.f, self.Z]
[docs] def getImpedanceFromPandas(self, path, unit='GHz'):
import pandas as pd
data = pd.read_csv(path)
data.columns = ['f', 'Zr']
if unit == 'GHz':
data['f']= data['f']*1e9
elif unit == 'MHz':
data['f']= data['f']*1e6
self.f = data['f']
self.Zr = data['Zr']
self.isResonatorImpedance = False
return data
[docs] def copy(self):
obj = type(self).__new__(self.__class__)
obj.__dict__.update(self.__dict__)
return obj
[docs] def getFrequencyRegions(self, vlines=None, figsize=[12,6], dpi=200):
'''Interactively select points
of the impedance curve. The list of
points will be returned and saved in
`self.freqregions`. How to use:
- place cursor + Spacebar --> pick
- Delete --> unpick
- Enter --> End picking
Returns
-------
freqregions: list
np.array of frequencies of picked points [Hz]
'''
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=figsize, dpi=dpi)
ax.plot(self.f*1e-9, self.Zr, color='k')
if vlines is not None:
ax.vlines(vlines*1e-9, ymin=np.min(self.Zr), ymax=np.max(self.Zr), color='k', linestyle='dashed', alpha=0.4)
ax.set_ylabel('Impedance [$\Omega$]')
ax.set_xlabel('Frequency [GHz]')
ax.set_title('Spacebar --> pick | Delete --> unpick | Enter --> Finish', color='red')
fig.tight_layout()
#get frequencies
points = plt.ginput(n=0, timeout=0, mouse_add=None, mouse_pop=None, mouse_stop=None)
self.freqregions = np.array(points)[:,0]*1e9
plt.close()
return self.freqregions