Files
x03da/script/MultiRegionScan.py
gac-x03da 4a033729e9 Closedown
2023-09-12 16:35:02 +02:00

353 lines
12 KiB
Python

"""
Line/vector/area/holo scan of multiple spectral regions
save this script into your script/user folder before editing!
usage:
1. uncomment one of the MOTORS lines.
add another line if necessary.
2. uncomment one of the scan blocks and adjust the parameters.
add another block if necessary.
3. declare the regions.
4. add the regions to the REGIONS list.
5. run the script.
revisions
230912 DFS30 analyser with peakAnalyser EPICS layer
"""
# dummy scan (time series)
#MOTORS = [dummy]
# photon energy scan (do not include 'ephot' in regions in this case!)
#MOTORS = [Eph]
# phi scan
#MOTORS = [ManipulatorPhi]
# holo scan
MOTORS = (ManipulatorPhi, ManipulatorTheta)
# 2D YZ scan
#MOTORS = [ManipulatorY, ManipulatorZ]
# line scan [start, stop, step]
#POSITIONS = [0., 5., 0.5]
#SCAN = 'lscan'
# vector scan [pos1, pos2, pos3, ...]
#POSITIONS = [200., 300., 400., 500.]
#SCAN = 'vscan'
# area scan [(start1, start2), (stop1, stop2), (step1, step2)]
# corresponding to (positioner1, positioner2)
#POSITIONS = [(-1., 114.), (+1., 116.), (20, 20)]
#ZIGZAG = True
#SCAN = 'ascan'
# holo scan
PHI_RANGE = (-45, 45) # (tuple (min, max))
THETA_RANGE = (-11.0, 55.0) # (tuple (min, max))
STEPS = (15.0, 1) # (tuple (phi, theta))
ZIGZAG = True
POSITIONS = [(PHI_RANGE[0], THETA_RANGE[0]), (PHI_RANGE[1], THETA_RANGE[1]), STEPS]
SCAN = 'ascan'
# seconds to wait between positioning command and triggering the detector
LATENCY = 0.0
# region setup
#
# for each region, define a python dictionary with the following items.
# optional items can be left unspecified and will default to the indicated values.
# for swept mode, include 'elo', 'ehi', 'estep', 'iter' values, but do not include 'efix'.
# for fixed mode, include 'efix' value, but do not include 'elo', 'ehi', 'estep', 'iter'.
#
# 'name': user-specific name of the region (for graph title and RegionName attribute in data file)
# 'elo': lower kinetic energy boundary of the spectrum
# 'ehi': upper kinetic energy boundary of the spectrum
# 'estep': energy step size
# 'efix': center kinetic energy in fixed mode
# 'epass': pass energy
# 'ephot': photon energy (default: unchanged)
# 'tstep': dwell time in seconds
# 'iter': number of iterations/sweeps (default 1)
# 'cis': True = constant initial state (photoemission line), False = constant final state (Auger peak), (default False)
# 'slit': exit slit (default: unchanged)
REGION1 = {'name': 'Peak 1', 'elo': 164.50, 'ehi': 166.20, 'estep': 0.05, 'epass': 50., 'tstep': 0.12, 'iter': 1}
REGION2 = {'name': 'multiplet 1', 'elo': 167.50, 'ehi': 171.01, 'estep': 0.03, 'epass': 50., 'tstep': 0.12, 'iter': 1}
REGION3 = {'name': 'multiplet 2', 'elo': 173.5, 'ehi': 176.8, 'estep': 0.03, 'epass': 50., 'tstep': 0.12, 'iter': 1}
# list of region dictionaries to execute at each scan position
REGIONS = [REGION1, REGION2, REGION3]
# close beam shutter and turn off analyser at the end of the scan
CLOSE_SHUTTER_AT_END = True
# --- DO NOT EDIT BELOW THIS LINE! ---
set_exec_pars(keep=False)
set_exec_pars(compression=True)
def check_region(region):
"""
check region dictionary items and apply defaults where necessary
"""
region['fixed'] = 'efix' in region
if region['fixed']:
region['elo'] = region['efix']
region['ehi'] = region['efix']
if 'iter' not in region:
region['iter'] = 1
print("region {0}: setting default iter = {1}".format(region['name'], region['iter']))
if 'cis' not in region:
region['cis'] = False
print("region {0}: setting default cis = {1}".format(region['name'], region['cis']))
class SpectrumReader(ReadonlyRegisterBase, ReadonlyRegisterArray):
"""
pseudo-device class to acquire and read out a Scienta spectrum per region.
this devices starts the spectrum acquisition and organises the data file.
"""
def initialize(self):
#super(SpectrumReader, self).initialize()
self.scan_index = -1
def create_datasets(self):
path = get_exec_pars().scanPath + self.region_name + "/"
self.channel_begin_dataset_name = path + "ScientaChannelBegin"
self.channel_end_dataset_name = path + "ScientaChannelEnd"
create_dataset(self.channel_begin_dataset_name, 'd')
create_dataset(self.channel_end_dataset_name, 'd')
if self.region['fixed']:
self.channel_center_dataset_name = path + "ScientaChannelCenter"
create_dataset(self.channel_center_dataset_name, 'd')
else:
self.step_energy_dataset_name = path + "ScientaStepEnergy"
create_dataset(self.step_energy_dataset_name, 'd')
if 'epass' in self.region:
self.pass_energy_dataset_name = path + "ScientaPassEnergy"
create_dataset(self.pass_energy_dataset_name, 'd')
if 'tstep' in self.region:
self.step_time_dataset_name = path + "ScientaStepTime"
create_dataset(self.step_time_dataset_name, 'd')
if 'iter' in self.region:
self.iterations_dataset_name = path + "ScientaIterations"
create_dataset(self.iterations_dataset_name, 'd')
if 'slit' in self.region:
self.slit_dataset_name = path + "ExitSlit"
create_dataset(self.slit_dataset_name, 'd')
def setup(self):
# print("spectrum.setup")
if self.scan_index != get_exec_pars().index:
self.scan_index = get_exec_pars().index
self.create_datasets()
print "scan {0}, region {1} ({2})".format(self.scan_index, self.region_index, self.region['name'])
edelta = 0.0
try:
ephot = self.region['ephot']
Eph.move(ephot)
except KeyError:
ephot = Eph.take(100)
if isinstance(ephot, float) and ephot > 0.:
try:
if self.region['cis']:
edelta = ephot - self.ephot_start
except AttributeError:
self.ephot_start = ephot
elo = self.region['elo'] + edelta
ehi = self.region['ehi'] + edelta
if self.region['fixed']:
Scienta.setAcquisitionMode("Fixed")
Scienta.centerEnergy.write(elo)
append_dataset(self.channel_center_dataset_name, elo)
else:
Scienta.setAcquisitionMode("Sweep Energy")
Scienta.lowEnergy.write(elo)
Scienta.highEnergy.write(ehi)
Scienta.stepSize.write(self.region['estep'])
append_dataset(self.step_energy_dataset_name, self.region['estep'])
try:
Scienta.setPassEnergy(int(self.region['epass']))
append_dataset(self.pass_energy_dataset_name, self.region['epass'])
except KeyError:
pass
try:
Scienta.stepTime.write(self.region['tstep'])
append_dataset(self.step_time_dataset_name, self.region['tstep'])
except KeyError:
pass
try:
Scienta.setIterations(self.region['iter'])
append_dataset(self.iterations_dataset_name, self.region['iter'])
except KeyError:
pass
try:
ExitSlit.write(self.region['slit'])
append_dataset(self.slit_dataset_name, self.region['slit'])
time.sleep(0.5)
ExitSlitY.waitReady(5000)
except KeyError:
pass
Scienta.update()
def read(self):
# print("spectrum.read")
global current_region_index
current_region_index = self.region_index
self.setup()
# print("Acquiring region {0}.".format(self.region['name']))
trig_scienta()
time.sleep(0.1)
sp = Scienta.getSpectrum().read()
append_dataset(self.channel_begin_dataset_name, Scienta.getChannelBegin().getValue())
append_dataset(self.channel_end_dataset_name, Scienta.getChannelEnd().getValue())
return sp
def getSize(self):
# this is called before the scan starts - we have to predict the spectrum size
# wrong values don't seem to affect the data files, however
if self.region['fixed']:
nx = 1066
else:
nx = int((self.region['ehi'] - self.region['elo']) / self.region['estep']) + 1
return nx
class ImageReader(ReadonlyRegisterBase, ReadonlyRegisterMatrix):
"""
pseudo-device class to read out the Scienta image per region.
this device just reads out the Scienta image that has been acquired by SpectrumReader.
"""
def read(self):
# print("image.read")
return Scienta.getDataMatrix().read()
def getWidth(self):
# this is called before the scan starts - we have to predict the spectrum size
# wrong values don't seem to affect the data files, however
if self.region['fixed']:
nx = 1066
else:
nx = int((self.region['ehi'] - self.region['elo']) / self.region['estep']) + 1
return nx
def getHeight(self):
# this is called before the scan starts - the number of slices is an independent parameter
ny = Scienta.slices.read()
return ny
class SimpleDeviceReader(Readable):
"""
pseudo-device class to read out another device once per region.
the device must be set assigned to the source attribute.
"""
def read(self):
return self.source.read()
def setup_live_plots(regions):
global live_plots
global current_region_index
names = [region['name'] for region in regions]
live_plots = plot(None, names, title="Live Spectra")
current_region_index = 0
def update_live_plots():
global live_plots
global current_region_index
try:
while get_context().state.running:
y = Scienta.spectrum.take(100)
x = Scienta.spectrumX
try:
series = live_plots[current_region_index].getSeries(0)
series.setData(x, y)
except IndexError:
pass
time.sleep(1.0)
finally:
print "Stopping live spectra"
def do_scan(scan, motors, positions, regions, latency):
"""
set up detectors and run the scan
for each region we have to add a SpectrumReader and an ImageReader pseudo-device to the SENSORS list.
the order SpectrumReader, ImageReader is important because the SpectrumReader triggers the Scienta,
whereafter the ImageReader reads the image.
"""
global SENSORS
SENSORS = []
for (index, region) in enumerate(regions):
check_region(region)
reader = SpectrumReader()
reader.region_index = index
reader.region_name = "region{0}".format(index + 1)
reader.region = region
reader.initialize()
set_device_alias(reader, reader.region_name + "/ScientaSpectrum")
SENSORS.append(reader)
image = ImageReader()
image.region_index = index
image.region = region
image.initialize()
set_device_alias(image, reader.region_name + "/ScientaImage")
SENSORS.append(image)
dev = SimpleDeviceReader()
dev.source = SampleCurrent
set_device_alias(dev, reader.region_name + "/SampleCurrent")
SENSORS.append(dev)
dev = SimpleDeviceReader()
dev.source = RefCurrent
set_device_alias(dev, reader.region_name + "/RefCurrent")
SENSORS.append(dev)
adjust_sensors()
set_adc_averaging()
if scan == 'ascan':
ascan(motors, SENSORS, positions[0], positions[1], positions[2], latency, False, zigzag = True, before_read=wait_beam, after_read = after_readout)
elif scan == 'lscan':
lscan(motors, SENSORS, positions[0], positions[1], positions[2], latency, False, before_read=wait_beam, after_read = after_readout)
elif scan == 'vscan':
vscan(motors, SENSORS, positions, True, latency,False, before_read=wait_beam, after_read = after_readout)
else:
print('unknown scan mode {}'.format(scan))
for (index, region) in enumerate(regions):
set_attribute(get_exec_pars().scanPath + "region{0}/ScientaSpectrum".format(index + 1), "RegionName", region['name'])
set_attribute(get_exec_pars().scanPath + "region{0}/ScientaImage".format(index + 1), "RegionName", region['name'])
set_attribute(get_exec_pars().scanPath, "Regions", [region['name'] for region in regions])
try:
setup_live_plots(REGIONS)
task = fork(update_live_plots)
do_scan(SCAN, MOTORS, POSITIONS, REGIONS, LATENCY)
finally:
if CLOSE_SHUTTER_AT_END:
after_scan()