from .elements import Element
import numpy
import io
from scipy.constants import c as clight
[docs]class InsertionDeviceKickMap(Element):
"""
Insertion Device Element. Valid for a parallel electron beam.
Pascale ELLEAUME, "A New Approach to the Electron Beam Dynamics in
Undulators and Wigglers". EPAC1992 0661.
European Synchrotron Radiation Facility.
BP 220, F-38043 Grenoble, France
"""
_BUILD_ATTRIBUTES = Element._BUILD_ATTRIBUTES + ['Nslice',
'Filename_in',
'Energy']
[docs] def readRadiaFieldMap(self, file_in_name):
"""
Read a RadiaField map and return
"""
with io.open(file_in_name, mode="r", encoding="utf-8") as f:
"""
File, where :
- the first data line is the length in meters
- the second data line is the number of points in the h. plane
- the third data line is the number of points in the v. plane
- each data block comes after a START
- first the horizontal data block, and second the
vertical data block
- each block is a table with axes
- comments start with #
"""
# File example:
# #comment in line 1
# #comment in line 2
# Length_in_m
# #comment in line 4
# Number of points in horizontal plane :nh
# #comment in line 6
# Number of points in vertical plane :nv
# #comment in line 8
# START
# pos_point1h pos_point2h ... pos_pointnh
# pos_point1v
# ... horizontal kick_map(nv,nh)
# pos_pointnv
# START
# pos_point1h pos_point2h ... pos_pointnh
# pos_point1v
# ... vertical kick_map(nv,nh)
# pos_pointnv
# (EOL)
data_lines = 0 # line not starting with '#'
header_lines = 0 # line starting with '#'
block_counter = 0 # START of the hor.map and START of the vert.map
for line in f:
sline = line.split()
if sline[0] == '#': # line is comment
header_lines += 1
else:
data_lines += 1
if data_lines == 1: # get the element length
el_length = float(sline[0])
elif data_lines == 2: # get the number of hor. points
h_points = int(sline[0])
elif data_lines == 3: # get the number of ver. points
v_points = int(sline[0])
# initialize element kicks and table_axes
kick_map = numpy.zeros((v_points, h_points))
haxis = numpy.zeros(h_points)
vaxis = numpy.zeros(v_points)
else:
# read block of data
if sline[0] == "START" or sline[0] == "START\n":
block_counter += 1
block_lines = 0
if block_lines == 1:
haxis = sline
if block_lines > 1:
# minus one due to python index starting at zero
# and minus another one due
# to the column labels in first line
vaxis[block_lines - 2] = float(sline[0])
kick_map[block_lines - 2][:] = sline[1:]
if block_lines > v_points:
block_lines = 0
if block_counter == 1:
hkickmap = numpy.copy(kick_map)
table_cols1 = haxis
table_rows1 = vaxis
if block_counter == 2:
vkickmap = numpy.copy(kick_map)
table_cols2 = haxis
table_rows2 = vaxis
block_lines += 1
return el_length, hkickmap, vkickmap, table_cols1, table_rows1, \
table_cols2, table_rows2
[docs] def sorted_table(self, table_in, sorted_index, order_axis):
# numpy.asfortranarray makes a copy of contiguous memory positions
table_out = numpy.copy(table_in)
for i, iis in zip(range(len(sorted_index)), sorted_index):
if order_axis == 'col':
table_out[:, i] = table_in[:, iis]
if order_axis == 'row':
table_out[i, :] = table_in[iis, :]
table_out2 = numpy.asfortranarray(table_out)
return table_out2
[docs] def set_DriftPass(self):
setattr(self, 'PassMethod', 'DriftPass')
[docs] def set_IdTablePass(self):
setattr(self, 'PassMethod', 'IdTablePass')
[docs] def get_PassMethod(self):
return getattr(self, 'PassMethod')
def __init__(self,
family_name: str,
Nslice: float,
Filename_in: str,
Energy: float,
**kwargs):
"""
Args:
family_name: family name
Nslice: number of slices in ... tracking??? integrator ???
Filename_in: input filename
Energy: particle energy in GeV
Default PassMethod: ``IdTablePass``
"""
# 2023jan18 fix bug with element print
# 2023jan15 first release
# orblancog
# read the input data
el_length, hkickmap, vkickmap, \
table_cols1, table_rows1, \
table_cols2, table_rows2 \
= self.readRadiaFieldMap(Filename_in)
# set to float
table_cols1array = numpy.array(table_cols1, dtype='float64')
table_rows1array = numpy.array(table_rows1, dtype='float64')
table_cols2array = numpy.array(table_cols2, dtype='float64')
table_rows2array = numpy.array(table_rows2, dtype='float64')
# Reorder table_axes and kick maps
cols1sorted_index = numpy.argsort(table_cols1array)
table_cols1array.sort()
rows1sorted_index = numpy.argsort(table_rows1array)
table_rows1array.sort()
cols2sorted_index = numpy.argsort(table_cols2array)
table_cols2array.sort()
rows2sorted_index = numpy.argsort(table_rows2array)
table_rows2array.sort()
hkickmap_a = self.sorted_table(hkickmap, cols1sorted_index, 'col')
hkickmap = self.sorted_table(hkickmap_a, rows1sorted_index, 'row')
vkickmap_a = self.sorted_table(vkickmap, cols2sorted_index, 'col')
vkickmap = self.sorted_table(vkickmap_a, rows2sorted_index, 'row')
# Field to kick factors
Brho = 1e9 * Energy/clight
factor = 1.0/(Brho**2)
xkick = factor * hkickmap
ykick = factor * vkickmap
factor1 = -1.0/(Brho)
xtable = table_cols1array
ytable = table_rows1array
# Create element properties
kwargs.setdefault('PassMethod', 'IdTablePass')
kwargs.setdefault('Filename_in', Filename_in)
kwargs.setdefault('Energy', Energy)
kwargs.setdefault('Nslice', Nslice)
kwargs.setdefault('Length', el_length)
kwargs.setdefault('xkick', xkick)
kwargs.setdefault('ykick', ykick)
kwargs.setdefault('xtable', xtable)
kwargs.setdefault('ytable', ytable)
# Set Element inherited properties
# pyat issue #522
# elem = Element('name', PassMethod='IdTablePass', \
# xkick=x0, ykick=x1, \
# xtable=x2, ytable=x3, Nslice=x4)
super(InsertionDeviceKickMap, self).__init__(family_name, **kwargs)