Newer
Older
import numpy as np
import tomllib
import xraylib
from transfocator_calcs import lookup_diameter, materials_to_deltas, materials_to_linear_attenuation
from transfocator_calcs import find_levels, get_densities
from transfocator_calcs import calc_1x_lu_table, calc_2x_lu_table, calc_kb_lu_table
from transfocator_calcs import calc_2xCRL_focus
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from transfocator_calcs import SYSTEM_TYPE
OE_MACRO = 'OE'
MAT_MACRO = 'MAT'
NLENS_MACRO = 'NUMLENS'
RADIUS_MACRO = 'RADIUS'
LOC_MACRO = 'LOC'
THICKERR_MACRO = 'THICKERR'
'''
Config variables
Beam Properties
energy : energy in keV
L_und : undulator length in m
sigmaH_e : Sigma electron source size in H direction in m
sigmaV_e : Sigma electron source size in V direction in m
sigmaHp : Sigma electron divergence in H direction in rad
sigmaVp_e : Sigma electron divergence in V direction in rad
Beamline properties
d_StoL1 : Source-to-CRL1 distance, in m
d_StoL2 : Source-to-CRL2 distance, in m
d_Stof : Source-to-focus distance, in m
CRL properties
d_min : Minimum thickness at the apex in m
stack_d : Stack thickness in m
stacks : number stacks in systems
KB properties
... : ...
... : ...
'''
DEFAULT_CONFIG = {'beam':{'energy': 15, 'L_und': 4.7, 'sigmaH_e': 14.8e-6,
'sigmaV_e': 3.7e-6, 'sigmaHp_e': 2.8e-6, 'sigmaVp_e': 1.5e-6},
'beamline': {'d_StoL1': 51.9, 'd_StoL2': 62.1, 'd_Stof': 66.2},
'crl':[{'stacks': 10, 'stack_d': 50.0e-3, 'd_min': 3.0e-5}],
'kb':{'KBH_L': 180.0e-3, 'KBH_q': 380.0e-3, 'KB_theta': 2.5e-3,
'KBV_L': 300.0e-3, 'KBV_q': 640.0e-3, 'KBH_p_limit': 1.0,
'KBV_p_limit': 1.0 }}
def separate_by_oe(property_list, oe_list, desired_oe):
'''
Description:
Lens properties are read in from substitutions file but not separated by
which transfocator they belong to. This functions separates by optical
elemente (i.e. transfocator)
Parameters:
property_list : list containing a property of all lenses
oe_list : list containing transfocator assignement of all lenses
desired_oe : which oe does user wnat properties for
Returns:
list of desired_oe's property values
'''
return [prop for (prop, oe) in zip(property_list, oe_list) if oe == desired_oe]
class focusingSystem():
def __init__(self, crl_setup = None, beam_config = DEFAULT_CONFIG['beam'],
beamline_config = DEFAULT_CONFIG['beamline'],
crl_configs = DEFAULT_CONFIG['crl'],
kb_config = DEFAULT_CONFIG['kb'], sysType = SYSTEM_TYPE.singleCRL):
'''
Description:
Focusing system object -- either single CRL, double CRL or single
CRL + KB.
crl_setup : ...
Default: None
beam_config : ...
Default: DEFAULT_CONFIG['beam']
beamline_config : ...
Default: DEFAULT_CONFIG['beamline']
crl_configs : ...
Default: DEFAULT_CONFIG['crl']
kb_config : ...
Default: DEFAULT_CONFIG['kb']
sysType : ...
Default: SYSTEM_TYPE.singleCRL
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
'''
self.verbose = True
self.elements = []
self.n_elements = 0
if crl_setup is None:
beam = beam_config
beamline = beamline_config
crl = crl_configs
kb = kb_config
self.sysType = sysType
else:
with open(crl_setup, "rb") as f:
config = tomllib.load(f)
beam = config['beam']
beamline = config['beamline']
#check config for the crl, crl1, crl2, kb and use this to determine system type
crl = []
if "crl" in config:
self.n_elements+=1
crl.append(config['crl'])
self.sysType = SYSTEM_TYPE.singleCRL
self.elements.append('1')
if "kb" in config:
self.n_elements+=1
kb = config['kb']
self.sysType = SYSTEM_TYPE.CRLandKB
self.elements.append('kb')
if "crl1" in config:
self.n_elements+=1
crl.append(config['crl1'])
self.sysType = SYSTEM_TYPE.singleCRL
self.elements.append('1')
if "crl2" in config:
self.n_elements+=1
crl.append(config['crl2'])
self.sysType = SYSTEM_TYPE.doubleCRL
self.elements.append('2')
# Setup beam properties
self.beam = {}
self.setupSource(beam)
# Setup beamline position of elements
self.bl = {}
self.setupBeamline(beamline)
# Setup element properties
self.crl = {}
self.setupCRL(crl)
if self.sysType is SYSTEM_TYPE.CRLandKB:
self.setupKB(kb)
# Initialize slit sizes to 0
self.slits = {}
self.setupSlits()
#<----------------------------------------------------------------------
# Are these needed at initialization
#TODO -- any generalizations? Yes but how? Need to do by elements?
self.energy = 0 # gets value from an ao (incoming beam energy)
self.focalSize = 0 # get value from an ao (desired focal length)
self.lenses = 0 # sets integer (2^10) whose binary representation indicates which lenses are in or out
#---------------------------------------------------------------------->
#initialize dictionary for crl indices of current state
if self.sysType is SYSTEM_TYPE.doubleCRL:
self.index = {'1':0,'2':0}
else:
self.index = {'1':0}
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
self.lookupTable = []
self.thickerr_flag = True
def setupSource(self, beam_properties):
'''
Beam properties can have entries for the following
energy : energy in keV
L_und : undulator length in m
sigmaH_e : Sigma electron source size in H direction in m
sigmaV_e : Sigma electron source size in V direction in m
sigmaHp_e : Sigma electron divergence in H direction in rad
sigmaVp_e : Sigma electron divergence in V direction in rad
'''
self.setEnergy(beam_properties['energy'])
self.L_und = beam_properties['L_und']
self.sigmaH_e = beam_properties['sigmaH_e']
self.sigmaV_e = beam_properties['sigmaV_e']
self.sigmaHp_e = beam_properties['sigmaHp_e']
self.sigmaVp_e = beam_properties['sigmaVp_e']
self.setupSourceEnergyDependent()
def setEnergy(self, energy):
'''
Sets various forms of energy
'''
if energy > 0.0001:
self.energy = float(energy)
self.energy_eV = self.energy*1000.0 # Energy in keV
self.wl = 1239.84 / (self.energy_eV * 10**9) #Wavelength in m
if self.verbose: print(f'Setting energy to {self.energy} keV')
def setupSourceEnergyDependent(self):
'''
Sets various energy dependent source parameters. Called whenever energy
is updated
'''
self.beam['sigmaH'] = (self.sigmaH_e**2 + self.wl*self.L_und/2/np.pi/np.pi)**0.5
self.beam['sigmaV'] = (self.sigmaV_e**2 + self.wl*self.L_und/2/np.pi/np.pi)**0.5
self.beam['sigmaHp'] = (self.sigmaHp_e**2 + self.wl/self.L_und/2)**0.5
self.beam['sigmaVp'] = (self.sigmaVp_e**2 + self.wl/self.L_und/2)**0.5
def setupBeamline(self, beamline_properties, num=1):
'''
Beamline properties can contain entries for the following
d_StoL1 : Source-to-CRL1 distance, in m
d_StoL2 : Source-to-CRL2 distance, in m
d_Stof : Source-to-sample distance, in m
'''
self.bl['d_StoL1'] = beamline_properties['d_StoL1']
self.bl['d_Stof'] = beamline_properties['d_Stof']
if self.sysType is SYSTEM_TYPE.doubleCRL:
self.bl['d_StoL2'] = beamline_properties['d_StoL2']
# if self.sysType is singleCRLandKB # KB doesn't have location???
# self.bl['d_StoKB'] = beamline_properties['d_StoKB']
def setupCRL(self, crl):
'''
Looks through crl (list of transforcators) for entries for the following
d_min : Minimum thickness at the apex in m
stack_d : Stack thickness in m
stacks : number of stacks in system
'''
for elem, tf in enumerate(crl):
self.crl[str(elem+1)]= {'d_min': tf['d_min'], 'stack_d': tf['stack_d'], 'stacks': tf['stacks']}
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
def setupKB(self, kb):
'''
Looks through kb for kb properties
KBH_L : KBH length
KBH_q : KBH q
KB_theta : KB mirror angle
KBV_L : KBV length
KBV_q : KBV q
KBH_p_limit : Minimum p that KBH can achieve, so abs(KBH_p) > KBH_p_min
KBV_p_limit : Minimum p that KBV can achieve, so abs(KBV_p) > KBV_p_min
'''
self.kb['KBH_L'] = kb['KBH_L']
self.kb['KBH_q'] = kb['KBH_q']
self.kb['KB_theta'] = kb['KB_theta']
self.kb['KBV_L'] = kb['KBV_L']
self.kb['KBV_q'] = kb['KBV_q']
self.kb['KBH_p_limit'] = kb['KBH_p_limit']
self.kb['KBV_p_limit'] = kb['KBV_p_limit']
def setupSlits(self):
'''
Initializes slit sizes to 0
'''
self.slits['1'] = {'hor':0,'vert':0}
if self.sysType is SYSTEM_TYPE.doubleCRL:
self.slits['2'] = {'hor':0,'vert':0}
if self.sysType is SYSTEM_TYPE.CRLandKB:
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
self.slits['KB'] = {'hor':0,'vert':0}
def updateSlitSize(self, size, oe, slit):
'''
Slit size updates are propagated to CRL object from EPICS. The beam
size lookup table is then recalculated.
'''
self.slits[oe][slit] = float(size)
if self.verbose: print(f"{oe} {slit} slit is set to {self.slits[oe][slit]}")
def updateSlitSizeRBV(self, oe, slit):
'''
Update proper slit size
'''
oes = oe if isinstance(oe, list) else [oe]
for element in oes:
intr_string = 'updated_slitSize_'+element+'_'+slit
pydev.iointr(intr_string, float(self.slits[element][slit]))
if self.verbose: print(f"{oe} {slit} slit size RBV udpated to {self.slits[element][slit]}")
def parseSubsFile(self, subs_file):
'''
Description:
Parameters:
... : ...
Returns:
... : ...
'''
#read in substitutions file
try:
subsFile = open(subs_file,"r")
except:
raise RuntimeError(f"Substiution file ({subsFile}) not found.")
# Remove empty lines and comments
subsFileContent = [line for line in subsFile.readlines() if (line.strip() and not line.startswith('#'))]
subsFile.close()
macros = subsFileContent[2].replace('{','').replace('}','').replace(',','').split()
lens_properties = {key: [] for key in macros} # dictionary of lists
for i in range(self.total_stacks):
try:
xx = subsFileContent[3+i].replace('{','').replace('}','').replace(',','').replace('"','').split()
lens_properties[macros[0]].append(xx[0])
lens_properties[macros[1]].append(xx[1])
lens_properties[macros[2]].append(xx[2])
lens_properties[macros[3]].append(xx[3])
lens_properties[macros[4]].append(xx[4])
lens_properties[macros[5]].append(xx[5])
lens_properties[macros[6]].append(xx[6])
lens_properties[macros[7]].append(xx[7])
raise RuntimeError(f"Number of lenses ({self.total_stacks}) doesn't match substitution file")
self.numlens = []
self.radius = []
self.materials = []
self.lens_loc = []
self.lens_thickerr = []
# get number of lens for each lens stack from lens properties dictionary-list
print('Getting OE assignments...')
if OE_MACRO in macros:
self.oe_num = np.array([int(i) for i in lens_properties[OE_MACRO]])
print('OE assignments read in.\n')
else:
raise RuntimeError(f"OE assignemnt macro ({OE_MACRO}) not found in substituion file")
# get number of lens for each lens stack from lens properties dictionary-list
print('Getting lens counts...')
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
if NLENS_MACRO in macros:
self.numlens = np.array([int(i) for i in lens_properties[NLENS_MACRO]])
print('Number of lens read in.\n')
else:
raise RuntimeError(f"Number of lenses macro ({NLENS_MACRO}) not found in substituion file")
# get radii for each lens from lens properties dictionary-list
print('Getting lens\' radii...')
if RADIUS_MACRO in macros:
self.radius = np.array([float(i) for i in lens_properties[RADIUS_MACRO]])
print('Radius of lenses read in.\n')
else:
raise RuntimeError(f"Radius macro ({RADIUS_MACRO}) not found in substituion file")
# get materials from lens properties dictionary-list
print('Getting lens materials...')
if MAT_MACRO in macros:
self.materials = lens_properties[MAT_MACRO]
print('Lens material read in.\n')
else:
raise RuntimeError(f"Material macro ({MAT_MACRO}) not found in substituion file")
# get densities from local definition (for compounds) or from xraylib (for elements)
densities = get_densities(self.materials)
self.densities = np.array([densities[material] for material in self.materials])
# get location of each lens from lens properties dictionary-list
print('Getting lens\' locations...')
if LOC_MACRO in macros:
self.lens_locations = np.array([float(l)*self.crl[str(self.oe_num[i])]['stack_d'] for i,l in enumerate(lens_properties[LOC_MACRO])])
print('Location of lenses read in.\n')
else:
raise RuntimeError(f"Location macro ({LOC_MACRO}) not found in substituion file")
# get thicknesses errprfrom lens properties dictionary-list
print('Getting lens thickness error...')
if THICKERR_MACRO in macros:
self.lens_thickerr = np.array([float(i) for i in lens_properties[THICKERR_MACRO]])
print('Lens thickness errors read in.\n')
else:
raise RuntimeError(f"Thickness errors macro ({THICKERR_MACRO}) not found in substituion file")
def setupLookupTable(self, subs_file, n_stacks):
Description:
-lookup table created after IOC startup
-called directly by ioc startup file
-Note: energy and slit size are updated before this is called but table
calculation is disabled during their updates
Parameters
subs_file : EPICS substitutions file
n_stacks : list of number of stacks in each transfocator system
'''
print(80*'#')
print('Setting up lens control...')
# convert number of lenses to list
self.num_stacks = n_stacks if isinstance(n_stacks, list) else [n_stacks]
self.total_stacks = sum(self.num_stacks)
# Since element configs are paired, the lookup table size will be equal
# to minimum possible configs for one of the system's elements
self.num_configs = 2**(min(self.num_stacks))
self.parseSubsFile(subs_file)
# Dictionary of config chosen for each element
self.config = {}
# Dictionary of total possible configs for each element
for i, n in enumerate(self.num_stacks): self.configs[str(i+1)] = np.arange(2**n)
self.lens_count = {}
self.lens_count['1'] = separate_by_oe(self.numlens, self.oe_num, 1)
self.lens_count['2'] = separate_by_oe(self.numlens, self.oe_num, 2)
print(self.lens_count)
self.radii = {}
self.radii['1'] = separate_by_oe(self.radius, self.oe_num, 1)
self.radii['2'] = separate_by_oe(self.radius, self.oe_num, 2)
self.mat = {}
self.mat['1'] = separate_by_oe(self.materials, self.oe_num, 1)
self.mat['2'] = separate_by_oe(self.materials, self.oe_num, 2)
self.lens_loc = {}
self.lens_loc['1'] = separate_by_oe(self.lens_locations, self.oe_num, 1)
self.lens_loc['2'] = separate_by_oe(self.lens_locations, self.oe_num, 2)
self.thickerr = {}
self.thickerr['1'] = separate_by_oe(self.lens_thickerr, self.oe_num, 1)
self.thickerr['2'] = separate_by_oe(self.lens_thickerr, self.oe_num, 2)
print('Constructing lookup table...')
self.construct_lookup_table()
print('Lookup table calculation complete.\n')
print('Transfocator control setup complete.')
print(80*'#')
def construct_lookup_table(self):
'''
Description:
Constructs lookup table for three types of systems:
-single transfocator
-double transfocator
-transfocator + KB mirror
Updates IOC waveforms and rBS with output of table constructions
Should be called after beam energy or slits size changes
'''
if self.sysType == SYSTEM_TYPE.singleCRL:
arr_a, dict_b, dict_c = calc_1x_lu_table(self.num_configs,
self.radii['1'], self.mat['1'],
self.energy, self.wl,
self.lens_count['1'], self.lens_loc['1'],
self.slits['1']['hor'], self.slits['1']['vert'],
self.thickerr['1'],
flag_HE = self.thickerr_flag,
verbose = self.verbose)
elif self.sysType == SYSTEM_TYPE.doubleCRL:
arr_a, dict_b, dict_c, arr_d = calc_2x_lu_table(self.num_configs,
self.radii['1'], self.mat['1'],
self.radii['2'], self.mat['2'],
self.energy, self.wl,
self.lens_count, self.lens_loc['1'],
self.lens_loc['2'],
self.beam, self.bl, self.crl,
self.slits,
self.thickerr['1'],
self.thickerr['2'],
flag_HE = self.thickerr_flag,
verbose = self.verbose)
self.index1to2_sorted = arr_d
elif self.sysType == SYSTEM_TYPE.CRLandKB:
arr_a, dict_b, dict_c = calc_kb_lu_table(self.num_configs,
self.radii['1'], self.mat['1'],
self.energy, self.wl,
self.lens_count['1'], self.lens_loc['1'],
self.thickerr['1'],
flag_HE = self.thickerr_flag,
verbose = self.verbose)
self.lookupTable = arr_a
self.sorted_invF_index = dict_b
self.sorted_invF = dict_c
self.updateEnergyRBV()
self.updateSlitSizeRBV(self.elements, 'hor')
self.updateSlitSizeRBV(self.elements, 'vert')
self.updateLookupWaveform()
self.updateInvFWaveform()
self.updateLookupConfigs()
if self.sysType == SYSTEM_TYPE.doubleCRL:
self.setFocalSizeActual(offTable = True)
else:
self.setFocalSizeActual(offTable = False)
self.updateFocalSizeRBVs()
Description
Updates energy readback PV. To be called after lookup table calculated
'''
pydev.iointr('updated_E', float(self.energy))
def updateInvFWaveform(self):
'''
Description
Puts invF lists into waveform PVs after lookup table calculatione
'''
pydev.iointr('new_invFind_list_1', self.sorted_invF_index['1'].tolist())
pydev.iointr('new_invF_list_1', self.sorted_invF['1'].tolist())
if self.sorted_invF_index['2'] is not None:
pydev.iointr('new_invFind_list_2', self.sorted_invF_index['2'].tolist())
pydev.iointr('new_invF_list_2', self.sorted_invF['2'].tolist())
def updateLookupConfigs(self):
'''
Description
Puts lookup table config integers into waveform PV after lookup
table calculation
'''
pydev.iointr('new_configs_1', self.configs['1'].tolist())
if self.sysType == SYSTEM_TYPE.doubleCRL:
pydev.iointr('new_configs_2', self.configs['2'].tolist())
def updateIndex(self, sortedIndex, oe):
'''
Description
User has updated desired sorted index for either CRL1 or CRL2. In
double CRL case, this update (either of CRL1 or CRL2**) moves the
system off the lookup table.
If double CRL system's CRL1 index is moved, it will move on lookup
table, so CRL2 will be udpated as well
**In practice, user should only move CRL2 in the double case
Parameters
sortedIndex: string
configuration index to set optical element to
oe: string
Label of optical element
'''
if self.verbose: print(f'Setting {oe} to index {sortedIndex}')
self.indexSorted[oe] = int(sortedIndex)
if oe == '1':
self.index['1'] = self.sorted_invF_index['1'][self.indexSorted['1']]
if self.sysType == SYSTEM_TYPE.doubleCRL:
self.indexSorted['2'] = self.index1to2_sorted[self.indexSorted['1']]
self.index['2'] = self.sorted_invF_index['2'][self.indexSorted['2']]
elif oe == '2':
self.index['2'] = self.sorted_invF_index['2'][self.indexSorted['2']]
if oe == '2':
self.setFocalSizeActual(offTable = False)
self.updateLensConfigPV()
self.updateLensRBV()
self.updateFocalSizeRBVs()
def updateFsize(self, focalSize):
'''
Descriptoin:
User updates desired focal size. Lookup table is traversed to find
nearest to desired.
Parameters:
focalSize: string
Desired focal size (in m)
'''
# focalPoint variable sent from IOC as a string
self.focalSize = float(focalSize)
if self.verbose: print(f'Setting focal size to {self.focalSize}')
self.find_config()
def find_config(self):
'''
Description:
User selected focal size, this function finds nearest acheivable
focal size from the lookup table
'''
# Code to search lookup table for nearest focal size to desired; note the
# lookup table is already sorted by 1/f
if self.verbose: print(f'Searching for config closest to {self.focalSize}')
# simple approach
# self.indexSorted = np.argmin(np.abs(self.lookupTable - self.focalSize))
# XS approach -- can handle nan but in pydev application don't have a good
# way to "transmit" errors (i.e. no solution found) to user.
indices, _ = find_levels(self.lookupTable, self.focalSize, direction='forward')
self.indexSorted['1'] = indices[0]
if self.verbose: print(f"1/f-sorted config index found at {self.indexSorted['1']}")
self.index['1'] = self.sorted_invF_index['1'][self.indexSorted['1']]
if self.verbose: print(f"CRL 1 config index found at {self.index['1']}")
if self.sysType == SYSTEM_TYPE.doubleCRL:
self.indexSorted['2'] = self.index1to2_sorted[self.indexSorted['1']]
self.index['2'] = self.sorted_invF_index['2'][self.indexSorted['2']]
if self.verbose: print(f"CRL 2 config index found at {self.index['2']}")
if self.verbose: print(f'Updating RBVs')
self.setFocalSizeActual(offTable = False)
self.updateLensConfigPV()
self.updateLensRBV()
self.updateFocalSizeRBVs()
def setFocalSizeActual(self, offTable = False):
'''
Description:
Gets the actual focal size for new system configuration
Parameters:
offTable: boolean
For double CRL, when 2nd CRL is changed this parameter is True and
causes calculation of the focal size for the new CRL 2 + current
CRL 1 index
if self.verbose: print(f'Setting actual focal size')
self.focalSize_actual = self.lookupTable[self.indexSorted['1']]
self.focalSize_actual = calc_2xCRL_focus(self.index['1'], self.index['2'],
self.radii['1'], self.mat['1'],
self.radii['2'], self.mat['2'],
self.energy, self.wl,
self.lens_count, self.lens_loc['1'],
self.lens_loc['2'],
self.beam, self.bl, self.crl,
self.slits,
self.thickerr['1'],
self.thickerr['2'],
flag_HE = self.thickerr_flag,
verbose = self.verbose)
def updateLensConfigPV(self):
'''
Description:
Updates optical element config PVs for which stacks need to be in/out
if self.verbose: print(f'Setting lens configuration PV for CRL 1')
if self.sysType == SYSTEM_TYPE.doubleCRL:
if self.verbose: print(f'Setting lens configuration PV for CRL 2')
pydev.iointr('new_lenses_2', int(self.config['2']))
def updateLensRBV(self):
'''
Description;
Updates optical elements config index PVs
if self.verbose: print(f"Setting lens configuration index RBV for CRL 1: {self.indexSorted['1']}")
pydev.iointr('new_index_1', int(self.indexSorted['1']))
if self.sysType == SYSTEM_TYPE.doubleCRL:
if self.verbose: print(f"Setting lens configuration index RBV for CRL 2: {self.indexSorted['2']}")
pydev.iointr('new_index_2', int(self.indexSorted['2']))
Description:
Updated focal size readback PV
if self.verbose: print(f'Setting actual focal size to {self.focalSize_actual}')
pydev.iointr('new_fSize', self.focalSize_actual)
def getPreviewFocalSize(self, sortedIndex):
'''
Description:
Finds focal size for desired index
Parameters:
sortedIndex: string
index user would like preview focal size
fSize_preview = self.lookupTable[int(sortedIndex)]
if self.verbose: print(f'Preview focal sizes for {sortedIndex} is {fSize_preview}')
pydev.iointr('new_preview', fSize_preview)
def setThickerrFlag(self, flag):
'''
Description:
User has updated thickness error flag.
Parameters:
flag : string
converted to boolean with
True: each stack's thickness error is used in focal size calculation
False: stack thickness error NOT used in focal size calculation
'''
self.thickerr_flag = int(flag)
if self.verbose: print(f'Thickness Error Flag set to {flag}')
self.updateThickerrFlagRBV()
def updateThickerrFlagRBV(self):
'''
Description:
Thickness error flag has been updated; readback PV is set
'''
if self.verbose: print(f'Thickness Error Flag RBV set to {self.thickerr_flag}')
pydev.iointr('updated_thickerr_Flag', self.thickerr_flag)
def updateE(self, energy):
'''
Description:
Beam energy updates are propagated to CRL object from EPICS. The beam
size lookup table is then recalculated.
Parameters:
energy: string
beam energy in keV
'''
if energy > 0.0001:
# Energy variable sent from IOC as a string
self.setEnergy(energy)
# Update beam properties that are dependent on energy
self.setupSourceEnergyDependent()
else:
if verbose: print(f'Invalid energy setting: {energy} kev; staying at {self.energy} keV')
Description:
Puts lookup table focal sizes into waveform PV
'''
pydev.iointr('new_lookupTable', self.lookupTable.tolist())
Description:
Turn on messages to iocConsole from python code
Parameters:
verbosity: string
numerical (0 or 1, for now) value to later be used as boolean