Code owners
Assign users and groups as approvers for specific file changes. Learn more.
pyTransfocator_single.py 26.78 KiB
import numpy as np
from scipy.optimize import root_scalar
import xraylib
from transfocator_calcs import lookup_diameter, materials_to_deltas, materials_to_linear_attenuatio, calc_lookup_tablen
MAT_MACRO = 'MAT'
NLENS_MACRO = 'NUMLENS'
RADIUS_MACRO = 'RADIUS'
LOC_MACRO = 'LOC'
THICKERR_MACRO = 'THICKERR'
"""
pyDevice TO DO:
WHAT inputs change the focal size arrays? Energy, what else?
WHAT inputs change the search through the arrays? desired focal size, what else?
IOC init functions
-get lens stack parameters (# of lenses in each stack, radius, location, thickness, thickness error) -- from substitution file but put into PVs? Update with autosave?
-get source info
-energy from from ID IOC
-hor/vert sizes and divergence (also energy dependent)
-lens diameter table? What is it doing?
-desired focal size is changed --> what needs updating? --> nothing, just need to search focal size array again
-multiple flags: is focal size achievable? is it achievable at sample?
recalc function -- should probably be same as init function
-energy is updated --> what needs updating?
-what else could user/staff change? sample position?
"""
# Beamline input block
energy = 15000.0 # Energy in eV
energy_keV = energy/1000.0 # Energy in keV
wl = 1239.84 / (energy * 10**9)
d_StoL1 = 51.9 # Source-to-CRL1 distance, in m
d_StoL2 = 62.1 # Source-to-CRL2 distance, in m
d_Stof = 66.2 # Source-to-focus distance, in m
#slit1_H = 500.0e-6 # H slit size before CRL 1
#slit1_V = 300.0e-6 # V slit size before CRL 1
# CRL input block
d_min = 3.0e-5 # Minimum thickness at the apex in m
stack_d = 50.0e-3 # Stack thickness in m
L1_n = np.array([1, 1, 1, 1, 1, 1, 2, 4, 8, 16]) # CRL1 number of lenses in each stack
L1_R = np.array([2.0e-3, 1.0e-3, 5.0e-4, 3.0e-4, 2.0e-4, 1.0e-4, 1.0e-4, 1.0e-4, 1.0e-4, 1.0e-4]) # CRL1 lens radius in each stack
L1_mater= np.array(["Be", "Be", "Be", "Be", "Be", "Be", "Be", "Be", "Be", "Be"]) # CRL1 lens material in each stack
L1_loc = np.array([4.5, 3.5, 2.5, 1.5, 0.5, -0.5, -1.5, -2.5, -3.5, -4.5])*stack_d # CRL1 lens stack location relative to center stack, positive means upstream
L1_HE = np.array([1.0e-6, 1.0e-6, 1.0e-6, 1.0e-6, 1.0e-6, 1.0e-6, 1.4e-6, 2.0e-6, 2.8e-6, 4.0e-6]) # CRL1 lens RMS thickness error
# Source size input block
L_und = 4.7 # undulator length
sigmaH_e = 14.8e-6 # Sigma electron source size in H direction in m
sigmaV_e = 3.7e-6 # Sigma electron source size in V direction in m
sigmaHp_e = 2.8e-6 # Sigma electron divergence in H direction in rad
sigmaVp_e = 1.5e-6 # Sigma electron divergence in V direction in rad
sigmaH = (sigmaH_e**2 + wl*L_und/2/np.pi/np.pi)**0.5
sigmaV = (sigmaV_e**2 + wl*L_und/2/np.pi/np.pi)**0.5
sigmaHp = (sigmaHp_e**2 + wl/L_und/2)**0.5
sigmaVp = (sigmaVp_e**2 + wl/L_und/2)**0.5
def absorptionaperture(x, n1mud, sigma, n1mur):
numerator = np.exp(-(x**2/(2*sigma**2))) * np.exp(-n1mur*(x**2) - n1mud)
denominator = np.exp(-n1mud)
return numerator / denominator - 0.5
def find_levels(array, levels, direction='forward'):
"""
Find the first indices at which the array crosses specified levels and the corresponding crossed values.
Parameters:
array (numpy.ndarray): An array of numbers.
levels (float or numpy.ndarray): A number or an array of levels to find crossings.
direction (str, optional): The searching direction. Defaults to 'forward'.
Can be either 'forward' or 'backward'.
Returns:
tuple: A tuple containing two arrays:
- An array of first indices at which the array crosses the specified levels.
- An array of first crossed values at the corresponding indices.
"""
# Convert a single level to a numpy array
if isinstance(levels, (int, float)):
levels = np.array([levels])
indices = []
values = []
# Compute the max and min of the array ignoring NaNs
max_val = np.nanmax(array)
min_val = np.nanmin(array)
for level in levels:
# If level is out of bounds
if level > max_val or level < min_val:
indices.append(-1)
values.append(np.nan)
continue
crossings = []
if direction == 'forward':
for i in range(1, len(array)):
if np.isnan(array[i - 1]) or np.isnan(array[i]):
continue
if (array[i - 1] < level <= array[i]) or (array[i - 1] > level >= array[i]):
crossings.append(i - 1)
break
elif direction == 'backward':
for i in range(len(array) - 2, -1, -1):
if np.isnan(array[i + 1]) or np.isnan(array[i]):
continue
if (array[i + 1] < level <= array[i]) or (array[i + 1] > level >= array[i]):
crossings.append(i)
break
else:
raise ValueError("Invalid direction. It should be either 'forward' or 'backward'.")
if len(crossings) > 0:
idx = crossings[0]
indices.append(idx)
values.append(array[idx])
else:
# In case no crossing is found within the range
indices.append(-1)
values.append(np.nan)
return np.array(indices), np.array(values)
def Single_CRL2D_control(fsize):
L1_D = np.zeros(L1_R.size) # CRL1 diameters for each stack
for i in range(L1_R.size):
L1_D[i] = lookup_diameter(L1_R[i])
L1_delta = materials_to_deltas(L1_mater, energy_keV) # delta values for CRL1 stacks
L1_mu = materials_to_linear_attenuation(L1_mater, energy_keV) # mu values for CRL1 stacks
L1_Feq = L1_R/(2*L1_n*L1_delta) + L1_loc # CRL1 equivalent f in m for each stack
L1_index_n = 2**L1_Feq.size # Total number of combinations for CRL1
L1_invF_list= np.zeros(L1_index_n) # List of equivalent 1/f in m^-1 for CRL1
for i in range(L1_index_n):
L1_invF_list[i] = np.sum(index_to_binary_list(i, L1_Feq.size)/L1_Feq)
# Sort the L1_invF list (to avoid zigzagging)
L1_invF_list_sort_indices = np.argsort(L1_invF_list)
L1_invF_list_sorted = L1_invF_list[L1_invF_list_sort_indices]
q1_list = 1/(L1_invF_list_sorted - 1/d_StoL1) # focal position of CRL1 for all configurations (sorted)
dq1_list = q1_list - (d_Stof - d_StoL1)
# Start generating focal size list as a function of CRL1 setting
sigma1H = (sigmaH**2 + (sigmaHp*d_StoL1)**2)**0.5 # sigma beam size before CRL1
sigma1V = (sigmaV**2 + (sigmaVp*d_StoL1)**2)**0.5 # sigma beam size before CRL1
L1_n1mud_list = np.zeros(L1_index_n) # List of n1*mu*d_min for all possible CRL1 configurations
L1_n1muR_list = np.zeros(L1_index_n) # List of n1*mu/R for all possible CRL1 configurations
aperL1H_list = np.zeros(L1_index_n) # absorption H aperture of CRL1 for all configurations
aperL1V_list = np.zeros(L1_index_n) # absorption V aperture of CRL1 for all configurations
diameter1_list = np.zeros(L1_index_n) # CRL1 diameter for all possible configurations
FWHM1H_list = np.zeros(L1_index_n) # H focal size at the focus of CRL1
FWHM1V_list = np.zeros(L1_index_n) # V focal size at the focus of CRL1
Strehl_list = np.zeros(L1_index_n) # Strehl ratio based on lens thickness error
for i in range(L1_index_n):
# absorption aperture is a function of CRL absorption/physical aperture, incident beam size, and physical slits
L1_n1mud_list[i] = np.sum(index_to_binary_list(L1_invF_list_sort_indices[i], L1_Feq.size)*np.array(L1_mu*L1_n*d_min))
L1_n1muR_list[i] = np.sum(index_to_binary_list(L1_invF_list_sort_indices[i], L1_Feq.size)*np.array(L1_mu*L1_n/L1_R))
solution = root_scalar(absorptionaperture, args=(L1_n1mud_list[i], sigma1H, L1_n1muR_list[i]), bracket=[0.0, 2*sigma1H], method='bisect')
aperL1H_list[i] = solution.root*2.0
solution = root_scalar(absorptionaperture, args=(L1_n1mud_list[i], sigma1V, L1_n1muR_list[i]), bracket=[0.0, 2*sigma1V], method='bisect')
aperL1V_list[i] = solution.root*2.0
mask = (np.array(index_to_binary_list(L1_invF_list_sort_indices[i], L1_Feq.size)) == 1)
if np.all(mask == False):
diameter1_list[i] = np.inf
else:
diameter1_list[i] = np.min(L1_D[mask])
aperL1H_list[i] = min(aperL1H_list[i], diameter1_list[i], slit1_H)
aperL1V_list[i] = min(aperL1V_list[i], diameter1_list[i], slit1_V)
phase_error_tmp = np.linalg.norm(index_to_binary_list(L1_invF_list_sort_indices[i], L1_Feq.size)*np.array(L1_HE*L1_delta)*2*np.pi/wl)
Strehl_list[i] = np.exp(-phase_error_tmp**2)
# FWHMbeam size at CRL1 focus
FWHM1H_list = ((0.88*wl*q1_list/aperL1H_list)**2 + (2.355*sigmaH*q1_list/d_StoL1)**2)**0.5
FWHM1V_list = ((0.88*wl*q1_list/aperL1V_list)**2 + (2.355*sigmaV*q1_list/d_StoL1)**2)**0.5
if flag_HE:
FWHM1H_list *= (Strehl_list)**(-0.5)
FWHM1V_list *= (Strehl_list)**(-0.5)
FWHM_list = (FWHM1H_list*FWHM1V_list)**0.5
indices, values = find_levels(FWHM_list, fsize, direction='backward')
index = indices[0]
if index == -1:
print(f"Cannot achieve the focal size {fsize*1.0e6:.2f} μm")
else:
print("======== Find size at focus ========================================")
print(f"Energy: {energy_keV} keV")
print(f"CRL1 configuration index in sorted list is {index}")
print(f"CRL1 configuration index is {L1_invF_list_sort_indices[index]} or {index_to_binary_list(L1_invF_list_sort_indices[index], L1_Feq.size)}")
print(f"CRL1 f is {1/L1_invF_list_sorted[index]:.2f} m, focus at q1 = {q1_list[index]:.2f} m")
print(f"Focal size is {FWHM1H_list[index]*1.0e6:.2f} μm x {FWHM1V_list[index]*1.0e6:.2f} μm at the focal point ({dq1_list[index]*1e3:.1f} mm from sample)")
FWHM1H_atsample_list = (FWHM1H_list**2 + (aperL1H_list*dq1_list/q1_list)**2)**0.5
FWHM1V_atsample_list = (FWHM1V_list**2 + (aperL1V_list*dq1_list/q1_list)**2)**0.5
FWHM_atsample_list = (FWHM1H_atsample_list*FWHM1V_atsample_list)**0.5
indices, values = find_levels(FWHM_atsample_list, fsize, direction='forward')
index2 = indices[0]
if index2 == -1:
print(f"Cannot achieve the bame size {fsize*1.0e6:.2f} μm at sample")
else:
print("======== Find size at sample =======================================")
print(f"CRL1 configuration index in sorted list is {index2}")
print(f"CRL1 configuration index is {L1_invF_list_sort_indices[index2]} or {index_to_binary_list(L1_invF_list_sort_indices[index2], L1_Feq.size)}")
print(f"CRL1 f is {1/L1_invF_list_sorted[index2]:.2f} m, focus at q1 = {q1_list[index2]:.2f} m ({dq1_list[index2]*1e3:.1f} mm from sample)")
print(f"Beam size is {FWHM1H_atsample_list[index2]*1.0e6:.2f} μm x {FWHM1V_atsample_list[index2]*1.0e6:.2f} μm at the sample position)")
indices, values = find_levels(dq1_list, 0.0, direction='backward')
index3 = indices[0]
if index == -1:
print(f"Cannot find combination to focus close to sample")
else:
print("======== Find configuration focus close to the sample ==============")
print(f"CRL1 configuration index in sorted list is {index3}")
print(f"CRL1 configuration index is {L1_invF_list_sort_indices[index3]} or {index_to_binary_list(L1_invF_list_sort_indices[index3], L1_Feq.size)}")
print(f"CRL1 f is {1/L1_invF_list_sorted[index3]:.2f} m, focus at q1 = {q1_list[index3]:.2f} m ({dq1_list[index3]*1e3:.1f} mm from sample)")
print(f"Beam size is {FWHM1H_atsample_list[index3]*1.0e6:.2f} μm x {FWHM1V_atsample_list[index3]*1.0e6:.2f} μm at the sample position)")
return
if __name__ == "__main__":
flag_HE = True
fsize = 50.0e-6 # Desired focal size in m (area average of h and v size)
#Single_CRL2D_control(fsize) # Find the best configuration for a single transfocator system
'''
Update the following to accommodate XS code
'''
class singleTF():
def __init__(self, beam = {}, beamline = {}, crl = {}, slits = {}):
# Initialize beamline layout variables
self.d_StoL = 51.9 # Source-to-CRL1 distance, in m
self.d_Stof = 66.2 # Source-to-focus distance, in m
self.setSource(beam)
self.setBeamline(beamline)
self.setCRL(crl)
self.setSlits(slits)
# Initialize lens variables
self.L1_n = np.array([1, 1, 1, 1, 1, 1, 2, 4, 8, 16]) # CRL1 number of lenses in each stack
self.L1_R = np.array([2.0e-3, 1.0e-3, 5.0e-4, 3.0e-4, 2.0e-4, 1.0e-4, 1.0e-4, 1.0e-4, 1.0e-4, 1.0e-4]) # CRL1 lens radius in each stack
self.L1_mater= np.array(["Be", "Be", "Be", "Be", "Be", "Be", "Be", "Be", "Be", "Be"]) # CRL1 lens material in each stack
self.L1_loc = np.array([4.5, 3.5, 2.5, 1.5, 0.5, -0.5, -1.5, -2.5, -3.5, -4.5])*stack_d # CRL1 lens stack location relative to center stack, positive means upstream
self.L1_HE = np.array([1.0e-6, 1.0e-6, 1.0e-6, 1.0e-6, 1.0e-6, 1.0e-6, 1.4e-6, 2.0e-6, 2.8e-6, 4.0e-6]) # CRL1 lens RMS thickness error
self.Lens_diameter_table = [
(50, 450.0),
(100, 632.0),
(200, 894.0),
(300, 1095.0),
(500, 1414.0),
(1000, 2000.0),
(1500, 2450.0),
]
# Convert the lookup table to a dictionary for faster lookup
self.Lens_diameter_dict = {int(col1): col2 for col1, col2 in Lens_diameter_table}
# Initialize pre-CRL slit size
self.slit1_H = 500.0e-6 # H slit size before CRL 1
self.slit1_V = 300.0e-6 # V slit size before CRL 1
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^12) whose binary representation indicates which lenses are in or out
self.num_lense = 12 # Number of lenses in system
self.verbosity = True
self.lookupTable = []
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 : Sigma electron divergence in H direction in rad
sigmaVp_e : Sigma electron divergence in V direction in rad
'''
#Default values
energy = 15
L_und = 4.7
sigmaH_e = 14.8e-6
sigmaV_e = 3.7e-6
sigmaHp = 2.8e-6
sigmaVp_e = 1.5e-6
if 'energy' in beam_properties.keys():
self.setEnergy(beam_properites['energy'])
else:
self.setEnergy(energy)
if 'L_und' in beam_properties.keys():
self.L_und = beam_properties['L_und']
else:
self.L_und = L_und
if 'sigmaH_e' in beam_properties.keys():
self.sigmaH_e = beam_properties['sigmaH_e']
else:
self.sigmaH_e = sigmaH_e
if 'sigmaV_e' in beam_properties.keys():
self.sigmaV_e = beam_properties['sigmaV_e']
else:
self.sigmaV_e = sigmaV_e
if 'sigmaHp_e' in beam_properties.keys():
self.sigmaHp_e = beam_properties['sigmaHp_e']
else:
self.sigmaHp_e = sigmaHp_e
if 'sigmaVp_e' in beam_properties.keys():
self.sigmaVp_e = beam_properties['sigmaVp_e']
else:
self.sigmaVp_e = sigmaVp_e
self.sigmaH = (self.sigmaH_e**2 + self.wl*self.L_und/2/np.pi/np.pi)**0.5
self.sigmaV = (self.sigmaV_e**2 + self.wl*self.L_und/2/np.pi/np.pi)**0.5
self.sigmaHp = (self.sigmaHp_e**2 + self.wl/self.L_und/2)**0.5
self.sigmaVp = (self.sigmaVp_e**2 + self.wl/self.L_und/2)**0.5
def setupBeamline(self, beamline_properties):
'''
Beamline properties can contain entries for the following
d_StoL1 : Source-to-CRL1 distance, in m
d_Stof : Source-to-focus distance, in m
'''
# Default values
d_StoL = 51.9
d_Stof = 66.2
if 'd_StoL' in beam_properties.keys():
self.d_StoL = beam_properties['d_StoL']
else:
self.d_StoL = d_StoL
if 'd_Stof' in beam_properties.keys():
self.d_Stof = beam_properties['d_Stof']
else:
self.d_Stof = d_Stof
def setupCRL(self, crl_properties):
'''
CRL properties can contiain entries for the following
d_min : Minimum thickness at the apex in m
stack_d : Stack thickness in m
'''
#Default values
stack_d = 50.0e-3
d_min = 3.0e-5
if 'd_min' in crl_properties.keys():
self.d_min = crl_properties['d_min']
else:
self.d_min = d_min
if 'stack_d' in crl_properties.keys():
self.stack_d = crl_properties['stack_d']
else:
self.stack_d = stack_d
def setupSlits(self, slit_properties):
'''
Setting up properties of slits
'''
#Default values
pass
def setLensCount(self, lensCount):
self.numLens =
def setupLookupTable(self, subs_file, n_lenses, energy = 8.0):
'''
lookup table created after IOC startup (after filter materials and
thicknesses are set
'''
print(80*'#')
print('Setting up lens control...')
self.num_lenses = n_lenses
self.energy = energy
#read in substitutions file
try:
subsFile = open(subs_file,"r")
except:
raise RuntimeError(f"Substiution file ({subsFile}) not found.")
subsFileContent = subsFile.readlines()
subsFile.close()
macros = subsFileContent[2].replace('{','').replace('}','').replace(',','').split()
lens_properties = {key: [] for key in macros} # dictionary of lists
for i in range(self.num_filters):
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])
except:
raise RuntimeError(f"Number of lenses ({self.num_lenses}) doesn't match substitution file")
self.numlens = []
self.radius = []
self.materials = []
self.lens_loc = []
self.lens_thickerr = []
# get number of lens for each lens from lens properties dictionary-list
print('Getting lens materials...')
if NLENS_MACRO in macros:
self.numlens = 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 RAD_MACRO in macros:
self.radius = lens_properties[RAD_MACRO]
print('Radius of lenses read in.\n')
else:
raise RuntimeError(f"Radius macro ({RAD_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 = [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_loc = lens_properties[LOC_MACRO]*self.stack_d
print('Location of lenses read in.\n')
else:
raise RuntimeError(f"Location macro ({RAD_MACRO}) not found in substituion file")
# get thicknesses errprfrom lens properties dictionary-list
print('Getting lens thickness error...')
if TERR_MACRO in macros:
self.lens_thickerr = [float(i) for i in lens_properties[TERR_MACRO]]
print('Lens thickness errors read in.\n')
else:
raise RuntimeError(f"Thickness errors macro ({TERR_MACRO}) not found in substituion file")
print('Constructing lookup table...')
self.construct_lookup_table()
print('Lookup table calculation complete.\n')
print('Filter control setup complete.')
print(80*'#')
def construct_lookup_table(self):
self.lookupTable = calc_lookup_table(self.num_configs, self.radius,
self.material, self.energy, self.numlens,
self.lens_loc)
self.culledTable()
def cull_lookup_table(self):
'''
Culls the lookup table based on lenses that are locked and or disabled
'''
self.culledSize = 2**(self.num_lenses - (self.outMask | self.inMask).bit_count())
if self.verbose: print(f'Operating spaced now at {self.culledSize} configurations')
if self.verbose: print(f'Culling table with in mask {self.inMask} and out mask {self.outMask}')
self.culledConfigs = np.empty(self.culledSize, dtype=int)
self.culledTable = np.empty(self.culledSize)
j = 0
for i in range(2**self.num_lenses):
if ((i & self.outMask == 0) and (i & self.inMask == self.inMask)):
self.culledConfigs[j]=i
self.culledTable[j] = self.lookupTable[i]
j += 1
self.sort_lookup_table()
def sort_lookup_table(self):
'''
'''
if self.verbose: print(f'Sorting culled lookup table of length {len(self.culledTable)}')
self.sorted_index = np.argsort(self.culledTable)
def setInMask(self, inMask):
'''
update mask for lenses that are locked in
'''
self.inMask = int(inMask)
self.cull_lookup_table()
if self.verbose: print(f'Converting culled index via in Mask')
self.convertCulledIndex()
if self.verbose: print(f'Updating filter RBV via in Mask')
self.updateLensRBV()
if self.verbose: print(f'Setting in mask RBV to {self.inMask}')
pydev.iointr('new_inMask', int(self.inMask))
def setOutMask(self, outMask):
'''
update mask for lenses that must remain out (either disabled or locked)
'''
self.outMask = int(outMask)
self.cull_lookup_table()
if self.verbose: print(f'Converting culled index via out Mask')
self.convertCulledIndex()
if self.verbose: print(f'Updating filter RBV via out Mask')
self.updateLensRBV()
if self.verbose: print(f'Setting out mask RBV to {self.outMask}')
pydev.iointr('new_outMask', int(self.outMask))
def convertCulledIndex(self):
'''
When available configs change, need to update index so that tweaks
continue to work
'''
if self.verbose: print('Converting ...')
self.culledIndex = (np.where(self.culledConfigs == self.config))[0][0]
if self.verbose: print(f'Culled index is {self.culledIndex}')
self.culledIndexSorted = self.sorted_index.tolist().index(self.culledIndex)
if self.verbose: print(f'Sorted culled index is {self.culledIndexSorted}')
def setFocalSizeActual(self):
'''
'''
self.focalSize_actual = self.culledTable[self.culledIndex]
def find_config(self):
'''
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
if self.verbose: print(f'Searching for config closest to {self.focalSize}')
self.culledIndex = np.argmin(np.abs(self.culledTable - self.focalSize))
if self.verbose: print(f'Config index found at {self.culledIndex}')
self.culledIndexSorted = self.sorted_index.tolist().index(self.culledIndex)
if self.verbose: print(f'Sorted config index found at {self.culledIndexSorted}')
# Update PVs
self.setFocalSizeActual()
self.updateLensConfigPV()
self.updateLensRBV()
self.updateFocalSizeRBVs()
def updateSlitSize(self, size, slit):
'''
Slit size updates are propagated to CRL object from EPICS. The beam
size lookup table is then recalculated.
'''
if slit = 'hor':
self.slit1_H = float(size) # H slit size before CRL 1
elif slit == 'vert':
self.slit1_V = float(size) # V slit size before CRL 1
else
# Need error handling
break
self.construct_lookup_table()
def setEnergy(self, energy):
'''
Sets various forms of energy
'''
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 nm(?)
def updateE(self, energy):
'''
Beam energy updates are propagated to CRL object from EPICS. The beam
size lookup table is then recalculated.
'''
# Energy variable sent from IOC as a string
self.setEnergy(energy)
self.construct_lookup_table()
# Do I need to find what the current config would produce as far as focal size and location?
# self.focalSizeRBV =
def updateFsize(self, focalSize):
'''
User updates desired focal size. Lookup table is traversed to find nearest
to desired.
'''
# focalPoint variable sent from IOC as a string
self.focalSize = float(focalSize)
self.find_config()
# def calc_lenses(self):
# self.lenses = (self.energy * self.focalPoint) % 4096
# pydev.iointr('new_lens_config', self.lenses)
def updateLensConfigPV(self):
'''
'''
self.config = self.culledConfigs[self.culledIndex]
pydev.iointr('new_lenses', int(self.config))
def updateLensRBV(self):
'''
'''
pydev.iointr('new_index', int(self.culledIndexSorted))
def updateFocalSizeRBVs(self):
'''
'''
pydev.iointr('new_fSize', self.focalSize_actual)
def updateVerbosity(self, verbosity):
'''
Turn on minor printing
'''
print(f'Verbosity set to {verbosity}')
self.verbose = verbosity