From b6036c6d191e8c59a602a158d400b30daeee795f Mon Sep 17 00:00:00 2001 From: FXI Operator Date: Tue, 30 May 2023 17:01:23 -0400 Subject: [PATCH 1/9] Initial pass at hardware triggered flyscanning --- startup/02-accelerator.py | 3 + startup/10-area-detector.py | 15 + startup/11-txm_motor.py | 20 +- startup/18-zebra.py | 1186 +++++++++++++++++++++++++++++++++ startup/40-scan_pre_define.py | 36 +- startup/41-scans.py | 3 +- startup/44-scans_other.py | 30 +- startup/45-baseline.py | 2 +- startup/46-zebra_flyer.py | 188 ++++++ startup/80-load_scan.py | 17 +- startup/91-functions.py | 34 + startup/98-user_scan.py | 189 ++++-- startup/99-umacro.py | 4 +- 13 files changed, 1627 insertions(+), 100 deletions(-) create mode 100644 startup/02-accelerator.py create mode 100644 startup/18-zebra.py create mode 100644 startup/46-zebra_flyer.py diff --git a/startup/02-accelerator.py b/startup/02-accelerator.py new file mode 100644 index 0000000..a69f1d9 --- /dev/null +++ b/startup/02-accelerator.py @@ -0,0 +1,3 @@ +from ophyd import EpicsSignalRO + +sr_current = EpicsSignalRO('SR:OPS-BI{DCCT:1}I:Real-I', name='sr_current') \ No newline at end of file diff --git a/startup/10-area-detector.py b/startup/10-area-detector.py index eda8190..7349337 100644 --- a/startup/10-area-detector.py +++ b/startup/10-area-detector.py @@ -362,6 +362,21 @@ def resume(self): getattr(Andor, k).ensure_nonblocking() Andor.hdf5.time_stamp.name = "Andor_timestamps" +# added by XH +Marana = AndorKlass("XF:18IDB-ES{Det:Marana1}", name="Andor") +Marana.cam.ensure_nonblocking() +# Andor.read_attrs = ['hdf5', 'stats1', 'stats5'] +Marana.read_attrs = ['hdf5'] +#Andor.read_attrs = ["hdf5", "stats1"] +#Andor.stats1.read_attrs = ["total"] +# Andor.stats5.read_attrs = ['total'] +Marana.hdf5.read_attrs = ["time_stamp"] +Marana.stage_sigs["cam.image_mode"] = 0 +#for k in ("image", "stats1", "trans1", "roi1", "proc1"): +# getattr(Andor, k).ensure_nonblocking() +for k in ("image", "trans1", "roi1", "proc1"): + getattr(Marana, k).ensure_nonblocking() +Marana.hdf5.time_stamp.name = "Andor_timestamps" # vlm = Manta("XF:18IDB-BI{VLM:1}", name="vlm") # detA1.read_attrs = ['hdf5', 'stats1', 'stats5'] diff --git a/startup/11-txm_motor.py b/startup/11-txm_motor.py index 1e49469..50b46eb 100644 --- a/startup/11-txm_motor.py +++ b/startup/11-txm_motor.py @@ -94,10 +94,12 @@ class MyBaseMotor(EpicsMotor): motor_res = Cpt(EpicsSignalRO, ".MRES") encoder_res = Cpt(EpicsSignalRO, ".ERES") motor_stat = Cpt(EpicsSignalRO, ".STAT") + base_velo = Cpt(EpicsSignalRO, ".VBAS") + max_velo = Cpt(EpicsSignalRO, ".VMAX") motor_calib = Cpt(EpicsSignal, ".SET") low_limit = Cpt(EpicsSignal, ".LLM") high_limit = Cpt(EpicsSignal, ".HLM") - step_size = Cpt(EpicsSignal, ".TWV") + step_size = Cpt(EpicsSignal, ".TWV") class MyEpicsMotor(MyBaseMotor): @@ -117,7 +119,8 @@ class Condenser(Device): class Zoneplate(Device): x = Cpt(MyEpicsMotor, "{ZP:1-Ax:X}Mtr") - y = Cpt(MyEpicsMotor, "{ZP:1-Ax:Y}Mtr") + #y = Cpt(MyEpicsMotor, "{ZP:1-Ax:Y}Mtr") + y = Cpt(MyEpicsMotor, "{BLens:1-Ax:Y}Mtr") z = Cpt(MyBaseMotor, "{TXM-ZP:1-Ax:Z}Mtr") @@ -135,7 +138,8 @@ class PhaseRing(Device): class BetrandLens(Device): x = Cpt(MyEpicsMotor, "{BLens:1-Ax:X}Mtr") - y = Cpt(MyEpicsMotor, "{BLens:1-Ax:Y}Mtr") + #y = Cpt(MyEpicsMotor, "{BLens:1-Ax:Y}Mtr") + y = Cpt(MyEpicsMotor, "{ZP:1-Ax:Y}Mtr") z = Cpt(MyBaseMotor, "{BLens:1-Ax:Z}Mtr") @@ -144,7 +148,7 @@ class TXMSampleStage(Device): sy = Cpt(MyEpicsMotor, "{Env:1-Ax:Yl}Mtr") sz = Cpt(MyEpicsMotor, "{Env:1-Ax:Zl}Mtr") pi_x = Cpt(MyBaseMotor, "{TXM:1-Ax:X}Mtr") - pi_r = Cpt(MyEpicsMotor, "{TXM:1-Ax:R}Mtr") + pi_r = Cpt(MyEpicsMotor, "{TXM:2-Ax:R}Mtr") class DetSupport(Device): @@ -232,11 +236,11 @@ class Scint(Device): aper.y, aper.z, zp.x, - zp.y, + #zp.y, zp.z, - phase_ring.x, - phase_ring.y, - phase_ring.z, + #phase_ring.x, + #phase_ring.y, + #phase_ring.z, betr.x, betr.y, betr.z, diff --git a/startup/18-zebra.py b/startup/18-zebra.py new file mode 100644 index 0000000..44fef06 --- /dev/null +++ b/startup/18-zebra.py @@ -0,0 +1,1186 @@ +import os +import threading +import h5py +import datetime +import numpy as np +import time as ttime + +from ophyd import Device, EpicsSignal, EpicsSignalRO +from ophyd import Component as Cpt +from ophyd import FormattedComponent as FC +from ophyd.areadetector.filestore_mixins import ( + new_short_uid, + resource_factory, + FileStorePluginBase, + FileStoreHDF5, +) +from ophyd.sim import NullStatus +from databroker.assets.handlers import HandlerBase +from nslsii.detectors.zebra import Zebra, EpicsSignalWithRBV +from ophyd.device import Staged, RedundantStaging, OrderedDict +from bluesky.plan_stubs import abs_set + + +# minimum detector acquisition period in second for full frame size +# will move this definition to areaDetector.py +DET_MIN_AP = {"Andor": 0.05, "Marana": 0.01, "Oryx": 0.005} + +# default velocity for rotating rotary stage back to starting position +# will move this definition to motors.py +ROT_BACK_VEL = 30 + +BIN_FACS = {"Andor": {0: 1, 1: 2, 2: 3, 3: 4, 4:8}, "Marana": {}, "Oryx": {}} + +class ZebraPositionCaptureData(Device): + + """ + Data arrays for the Zebra position capture function and their metadata. + + ## Not all variables are needed at FXI - CD + """ + + # Data arrays + div1 = Cpt(EpicsSignal, "PC_DIV1") + div2 = Cpt(EpicsSignal, "PC_DIV2") + div3 = Cpt(EpicsSignal, "PC_DIV3") + div4 = Cpt(EpicsSignal, "PC_DIV4") + enc1 = Cpt(EpicsSignal, "PC_ENC1") + enc2 = Cpt(EpicsSignal, "PC_ENC2") + enc3 = Cpt(EpicsSignal, "PC_ENC3") + enc4 = Cpt(EpicsSignal, "PC_ENC4") + filt1 = Cpt(EpicsSignal, "PC_FILT1") + filt2 = Cpt(EpicsSignal, "PC_FILT2") + filt3 = Cpt(EpicsSignal, "PC_FILT3") + filt4 = Cpt(EpicsSignal, "PC_FILT4") + time = Cpt(EpicsSignal, "PC_TIME") + + # Array sizes + num_cap = Cpt(EpicsSignal, "PC_NUM_CAP") + num_down = Cpt(EpicsSignal, "PC_NUM_DOWN") + + # BOOLs to denote arrays with data + cap_enc1_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B0") + cap_enc2_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B1") + cap_enc3_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B2") + cap_enc4_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B3") + cap_filt1_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B4") + cap_filt2_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B5") + cap_div1_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B6") + cap_div2_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B7") + cap_div3_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B8") + cap_div4_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B9") + + def stage(self): + super().stage() + + def unstage(self): + super().unstage() + + +class ZebraPositionCapture(Device): + + """ + Signals for the position capture function of the Zebra + """ + + # Configuration settings and status PVs + enc = Cpt(EpicsSignalWithRBV, "PC_ENC") + egu = Cpt(EpicsSignalRO, "M1:EGU") + dir = Cpt(EpicsSignalWithRBV, "PC_DIR") + tspre = Cpt(EpicsSignalWithRBV, "PC_TSPRE") + trig_source = Cpt(EpicsSignalWithRBV, "PC_ARM_SEL") + arm = Cpt(EpicsSignal, "PC_ARM") + disarm = Cpt(EpicsSignal, "PC_DISARM") + armed = Cpt(EpicsSignalRO, "PC_ARM_OUT") + gate_source = Cpt(EpicsSignalWithRBV, "PC_GATE_SEL") + gate_start = Cpt(EpicsSignalWithRBV, "PC_GATE_START") + gate_width = Cpt(EpicsSignalWithRBV, "PC_GATE_WID") + gate_step = Cpt(EpicsSignalWithRBV, "PC_GATE_STEP") + gate_num = Cpt(EpicsSignalWithRBV, "PC_GATE_NGATE") + gate_ext = Cpt(EpicsSignalWithRBV, "PC_GATE_INP") + gated = Cpt(EpicsSignalRO, "PC_GATE_OUT") + pulse_source = Cpt(EpicsSignalWithRBV, "PC_PULSE_SEL") + pulse_start = Cpt(EpicsSignalWithRBV, "PC_PULSE_START") + pulse_width = Cpt(EpicsSignalWithRBV, "PC_PULSE_WID") + pulse_step = Cpt(EpicsSignalWithRBV, "PC_PULSE_STEP") + pulse_max = Cpt(EpicsSignalWithRBV, "PC_PULSE_MAX") + pulse = Cpt(EpicsSignalRO, "PC_PULSE_OUT") + enc_pos1_sync = Cpt(EpicsSignal, "M1:SETPOS.PROC") + enc_pos2_sync = Cpt(EpicsSignal, "M2:SETPOS.PROC") + enc_pos3_sync = Cpt(EpicsSignal, "M3:SETPOS.PROC") + enc_pos4_sync = Cpt(EpicsSignal, "M4:SETPOS.PROC") + enc_res1 = Cpt(EpicsSignal, "M1:MRES") + enc_res2 = Cpt(EpicsSignal, "M2:MRES") + enc_res3 = Cpt(EpicsSignal, "M3:MRES") + enc_res4 = Cpt(EpicsSignal, "M4:MRES") + data_in_progress = Cpt(EpicsSignalRO, "ARRAY_ACQ") + block_state_reset = Cpt(EpicsSignal, "SYS_RESET.PROC") + data = Cpt(ZebraPositionCaptureData, "") + + def stage(self): + self.arm.put(1) + super().stage() + + def unstage(self): + self.disarm.put(1) + self.block_state_reset.put(1) + super().unstage() + + +class FXIZebraOR(Device): + # I really appreciate the different indexing for input source + # Thank you for that + + use1 = Cpt(EpicsSignal, "_ENA:B0") + use2 = Cpt(EpicsSignal, "_ENA:B1") + use3 = Cpt(EpicsSignal, "_ENA:B2") + use4 = Cpt(EpicsSignal, "_ENA:B3") + input_source1 = Cpt(EpicsSignal, "_INP1") + input_source2 = Cpt(EpicsSignal, "_INP2") + input_source3 = Cpt(EpicsSignal, "_INP3") + input_source4 = Cpt(EpicsSignal, "_INP4") + invert1 = Cpt(EpicsSignal, "_INV:B0") + invert2 = Cpt(EpicsSignal, "_INV:B1") + invert3 = Cpt(EpicsSignal, "_INV:B2") + invert4 = Cpt(EpicsSignal, "_INV:B3") + + def stage(self): + super().stage() + + def unstage(self): + super().unstage() + + +class ZebraAND(Device): + # I really appreciate the different indexing for input source + # Thank you for that + use1 = Cpt(EpicsSignal, "_ENA:B0") + use2 = Cpt(EpicsSignal, "_ENA:B1") + use3 = Cpt(EpicsSignal, "_ENA:B2") + use4 = Cpt(EpicsSignal, "_ENA:B3") + input_source1 = Cpt(EpicsSignal, "_INP1") + input_source2 = Cpt(EpicsSignal, "_INP2") + input_source3 = Cpt(EpicsSignal, "_INP3") + input_source4 = Cpt(EpicsSignal, "_INP4") + invert1 = Cpt(EpicsSignal, "_INV:B0") + invert2 = Cpt(EpicsSignal, "_INV:B1") + invert3 = Cpt(EpicsSignal, "_INV:B2") + invert4 = Cpt(EpicsSignal, "_INV:B3") + + def stage(self): + super().stage() + + def unstage(self): + super().unstage() + + +class ZebraPulse(Device): + width = Cpt(EpicsSignalWithRBV, "WID") + input_addr = Cpt(EpicsSignalWithRBV, "INP") + input_str = Cpt(EpicsSignalRO, "INP:STR", string=True) + input_status = Cpt(EpicsSignalRO, "INP:STA") + delay = Cpt(EpicsSignalWithRBV, "DLY") + delay_sync = Cpt(EpicsSignal, "DLY:SYNC") + time_units = Cpt(EpicsSignalWithRBV, "PRE", string=True) + output = Cpt(EpicsSignal, "OUT") + + input_edge = FC(EpicsSignal, "{self._zebra_prefix}POLARITY:{self._edge_addr}") + + _edge_addrs = { + 1: "BC", + 2: "BD", + 3: "BE", + 4: "BF", + } + + def __init__( + self, + prefix, + *, + index=None, + parent=None, + configuration_attrs=None, + read_attrs=None, + **kwargs, + ): + if read_attrs is None: + read_attrs = ["input_addr", "input_edge", "delay", "width", "time_units"] + if configuration_attrs is None: + configuration_attrs = [] + + zebra = parent + self.index = index + self._zebra_prefix = zebra.prefix + self._edge_addr = self._edge_addrs[index] + + super().__init__( + prefix, + configuration_attrs=configuration_attrs, + read_attrs=read_attrs, + parent=parent, + **kwargs, + ) + + def stage(self): + super().stage() + + def unstage(self): + super().unstage() + + +class FXIZebra(Zebra): + """ + FXI Zebra device. + """ + + pc = Cpt(ZebraPositionCapture, "") + or1 = Cpt(FXIZebraOR, "OR1") # XF:18ID-ES:1{Dev:Zebra1}:OR1_INV:B0 + or2 = Cpt(FXIZebraOR, "OR2") + or3 = Cpt(FXIZebraOR, "OR3") + or4 = Cpt(FXIZebraOR, "OR4") + and1 = Cpt(ZebraAND, "AND1") # XF:18ID-ES:1{Dev:Zebra2}:AND1_ENA:B0 + and2 = Cpt(ZebraAND, "AND2") + and3 = Cpt(ZebraAND, "AND3") + and4 = Cpt(ZebraAND, "AND4") + pulse1 = Cpt(ZebraPulse, "PULSE1_", index=1) # XF:18ID-ES:1{Dev:Zebra1}:PULSE1_INP + pulse2 = Cpt(ZebraPulse, "PULSE2_", index=2) + pulse3 = Cpt(ZebraPulse, "PULSE3_", index=3) + pulse4 = Cpt(ZebraPulse, "PULSE4_", index=4) + + def stage(self): + super().stage() + + def unstage(self): + super().unstage() + + def __init__(self, prefix, *, read_attrs=None, configuration_attrs=None, **kwargs): + if read_attrs is None: + read_attrs = [] + if configuration_attrs is None: + configuration_attrs = [] + + super().__init__( + prefix, + read_attrs=read_attrs, + configuration_attrs=configuration_attrs, + **kwargs, + ) + + +## Class below requires FXI specific changes +class FXITomoFlyer(Device): + """ + This is the flyer object for the Zebra. + This is the position based flyer. + """ + root_path = "/nsls2/data/fxi-new/legacy/" + write_path_template = f"zebra/%Y/%m/%d/" + read_path_template = f"zebra/%Y/%m/%d/" + reg_root = f"zebra/" + + KNOWN_DETS = {"Andor", "Marana", "Oryx"} + rot_axis = zps.pi_r + # dummy_axis = ophyd.sim.SynAxis(name="TOMO_DUMMY") + + scn_modes = { + 0: "standard", # a single scan in a given angle range + 1: "snaked: multiple files", # back-forth rocking scan with each swing being saved into a file + 2: "snaked: single file", # back-forth rocking scan being saved into a single file + } + + dft_pulse_wid = {'ms': 0.002, 's': 0.0005, '10s': 0.003} # 0: ms # 1: s # 2: 10s + pc_trig_dir = {1: 0, -1: 1} # 1: positive, -1: negative + scan_cfg = {} + pc_cfg = {} + _staging_delay = 0.010 + tspre = 's' ## ['ms', 's', '10s'] + + def __init__(self, dets, zebra, *, reg=db.reg, scn_mode=0, **kwargs): + super().__init__("", parent=None, **kwargs) + self._state = "idle" + self._dets = dets + self._filestore_resource = None + self._encoder = zebra + self._document_cache = [] # self._document_cache defines resource and datum documents + self._stage_sigs = {} + self._last_bulk = None # self._last_bulk defines event document + + self.reg = reg + self.scn_mode = self.scn_modes[scn_mode] + self.extra_stage_sigs = {} + self.shutter_delay = 0.1 # unit: deg; _shutter_delay/rot_vel > unibliz shutter opening time 1.5ms + self.use_shutter = True + + FXITomoFlyer.set_wait(self._encoder.pc.block_state_reset, 1) + + ############### Zebra Setup ############### + ## PC Tab + FXITomoFlyer.set_wait(self._encoder.pc.data.cap_enc1_bool, 1) + FXITomoFlyer.set_wait(self._encoder.pc.data.cap_enc2_bool, 0) + FXITomoFlyer.set_wait(self._encoder.pc.data.cap_enc3_bool, 0) + FXITomoFlyer.set_wait(self._encoder.pc.data.cap_enc4_bool, 0) + FXITomoFlyer.set_wait(self._encoder.pc.data.cap_filt1_bool, 0) + FXITomoFlyer.set_wait(self._encoder.pc.data.cap_filt2_bool, 0) + FXITomoFlyer.set_wait(self._encoder.pc.data.cap_div1_bool, 0) + FXITomoFlyer.set_wait(self._encoder.pc.data.cap_div2_bool, 0) + FXITomoFlyer.set_wait(self._encoder.pc.data.cap_div3_bool, 0) + FXITomoFlyer.set_wait(self._encoder.pc.data.cap_div4_bool, 0) + + FXITomoFlyer.set_wait(self._encoder.pc.enc, 0) # 0: Enc1, 1: Enc2, 2: Enc3, 3: Enc4, + FXITomoFlyer.set_wait(self._encoder.pc.dir, 0) # 0: Positive, 1: Negative + FXITomoFlyer.set_wait(self._encoder.pc.tspre, 1) # 0: ms, 1: s, 2: 10s + + ## AND tab -- can be used for external triggering + FXITomoFlyer.set_wait(self._encoder.and1.use1, 0) # 0: No, 1: Yes + FXITomoFlyer.set_wait(self._encoder.and1.use2, 0) + FXITomoFlyer.set_wait(self._encoder.and1.use3, 0) + FXITomoFlyer.set_wait(self._encoder.and1.use4, 0) + FXITomoFlyer.set_wait(self._encoder.and1.input_source1, 0) + FXITomoFlyer.set_wait(self._encoder.and1.input_source2, 0) + FXITomoFlyer.set_wait(self._encoder.and1.input_source3, 0) + FXITomoFlyer.set_wait(self._encoder.and1.input_source4, 0) + FXITomoFlyer.set_wait(self._encoder.and1.invert1, 0) # 0: No, 1: Yes + FXITomoFlyer.set_wait(self._encoder.and1.invert2, 0) + FXITomoFlyer.set_wait(self._encoder.and1.invert3, 0) + FXITomoFlyer.set_wait(self._encoder.and1.invert4, 0) + + ## OR Tab -- can be used for diagnose + FXITomoFlyer.set_wait(self._encoder.or1.use1, 0) # 0: No, 1: Yes + FXITomoFlyer.set_wait(self._encoder.or1.use2, 0) + FXITomoFlyer.set_wait(self._encoder.or1.use3, 0) + FXITomoFlyer.set_wait(self._encoder.or1.use4, 0) + FXITomoFlyer.set_wait(self._encoder.or1.input_source1, 0) + FXITomoFlyer.set_wait(self._encoder.or1.input_source2, 0) + FXITomoFlyer.set_wait(self._encoder.or1.input_source3, 0) + FXITomoFlyer.set_wait(self._encoder.or1.input_source4, 0) + FXITomoFlyer.set_wait(self._encoder.or1.invert1, 0) # 0 = No, 1 = Yes + FXITomoFlyer.set_wait(self._encoder.or1.invert2, 0) + FXITomoFlyer.set_wait(self._encoder.or1.invert3, 0) + FXITomoFlyer.set_wait(self._encoder.or1.invert4, 0) + + ## PULSE tab -- set for fast shutter + FXITomoFlyer.set_wait(self._encoder.pulse1.input_addr, 31) + FXITomoFlyer.set_wait(self._encoder.pulse1.input_edge, 0) # 0 = rising, 1 = falling + FXITomoFlyer.set_wait(self._encoder.pulse1.delay, 0) + FXITomoFlyer.set_wait(self._encoder.pulse2.input_addr, 31) + FXITomoFlyer.set_wait(self._encoder.pulse2.input_edge, 1) # 0 = rising, 1 = falling + FXITomoFlyer.set_wait(self._encoder.pulse2.delay, 0) + + ## ENC tab + FXITomoFlyer.set_wait(self._encoder.pc.enc_pos1_sync, 1) + FXITomoFlyer.set_wait(self._encoder.pc.enc_pos2_sync, 0) + FXITomoFlyer.set_wait(self._encoder.pc.enc_pos3_sync, 0) + FXITomoFlyer.set_wait(self._encoder.pc.enc_pos4_sync, 0) + + ## SYS tab + FXITomoFlyer.set_wait(self._encoder.output1.ttl.addr, 53) # PC_PULSE --> TTL1 --> Camera + FXITomoFlyer.set_wait(self._encoder.output2.ttl.addr, 52) # PC_PULSE --> TTL2 --> fast shutter + FXITomoFlyer.set_wait(self._encoder.output3.ttl.addr, 0) + FXITomoFlyer.set_wait(self._encoder.output4.ttl.addr, 0) + + @property + def encoder(self): + return self._encoder + + @property + def detectors(self): + return tuple(self._dets) + + @detectors.setter + def detectors(self, value): + dets = tuple(value) + if not all(d.name in self.KNOWN_DETS for d in dets): + raise ValueError( + f"One or more of {[d.name for d in dets]}" + f"is not known to the zebra. " + f"The known detectors are {self.KNOWN_DETS})" + ) + self._dets = dets + + def preset_zebra(self, pc_cfg={}): + ############### PC Arm + FXITomoFlyer.set_wait(self._encoder.pc.trig_source, 0, wait=0.01) # 0 = Soft, 1 = External + ############### PC Pulse + # yield from abs_set(self._encoder.pc.pulse_width, self.dft_pulse_wid[self.tspre], wait=True) + + ############### PULSE -- set unibliz trigger to 'external exposure' + if self.use_shutter: + FXITomoFlyer.set_wait(self._encoder.pulse1.time_units, self.tspre) + self._encoder.pulse1.width.put(self.dft_pulse_wid[self.tspre], timeout=10) + ttime.sleep(0.01) + FXITomoFlyer.set_wait(self._encoder.pulse2.time_units, self.tspre) + self._encoder.pulse2.width.put(self.dft_pulse_wid[self.tspre], timeout=10) + ttime.sleep(0.01) + FXITomoFlyer.set_wait(self._encoder.output2.ttl.addr, 52) + else: + FXITomoFlyer.set_wait(self._encoder.output2.ttl.addr, 29) + if self.scn_mode == "standard": + ############### PC Tab ############### + ## PC Gate + FXITomoFlyer.set_wait(self._encoder.pc.gate_source, 0, wait=0.01) # 0 = Position, 1 = Time, 2 = External + FXITomoFlyer.set_wait(self._encoder.pc.gate_step, 0) + FXITomoFlyer.set_wait(self._encoder.pc.gate_num, 1) + + ## PC Pulse + FXITomoFlyer.set_wait(self._encoder.pc.pulse_source, 0, wait=0.01) # 0 = Position, 1 = Time, 2 = External + self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) + ttime.sleep(0.01) + elif self.scn_mode == "snaked: multiple files": + ############### PC Tab ############### + ## PC Gate + FXITomoFlyer.set_wait(self._encoder.pc.gate_source, 0, wait=0.01) # 0 = Position, 1 = Time, 2 = External + FXITomoFlyer.set_wait(self._encoder.pc.gate_step, 0) + FXITomoFlyer.set_wait(self._encoder.pc.gate_num, 1) + + ## PC Pulse + FXITomoFlyer.set_wait(self._encoder.pc.pulse_source, 0, wait=0.01) # 0 = Position, 1 = Time, 2 = External + self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) + ttime.sleep(0.01) + elif self.scn_mode == "snaked: single file": + ############### PC Tab ############### + ## PC Gate + FXITomoFlyer.set_wait(self._encoder.pc.gate_source, 2, wait=0.01) # 0 = Position, 1 = Time, 2 = External + FXITomoFlyer.set_wait(self._encoder.pc.gate_ext, 29) + FXITomoFlyer.set_wait(self._encoder.pc.gate_num, 0) + + ## PC Pulse + FXITomoFlyer.set_wait(self._encoder.pc.pulse_source, 1, wait=0.01) # 0 = Position, 1 = Time, 2 = External + self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) + ttime.sleep(0.01) + + for key, val in pc_cfg[self.scn_mode].items(): + getattr(self._encoder.pc, key).put(val, timeout=10) + ttime.sleep(0.01) + + def make_filename(self): + """Make a filename. + Taken/Modified from ophyd.areadetector.filestore_mixins + This is a hook so that the read and write paths can either be modified + or created on disk prior to configuring the areaDetector plugin. + Returns + ------- + filename : str + The start of the filename + read_path : str + Path that ophyd can read from + write_path : str + Path that the IOC can write to + """ + filename = f"{new_short_uid()}.h5" + formatter = datetime.now().strftime + write_path = formatter(f"{self.root_path}{self.write_path_template}") + read_path = formatter(f"{self.root_path}{self.read_path_template}") + return filename, read_path, write_path + + def stage(self): + # self.set_stage_sigs() + self._stage_with_delay() + super.stage() + + def _stage_with_delay(self): + # Staging taken from https://github.com/bluesky/ophyd/blob/master/ophyd/device.py + # Device - BlueskyInterface + """Stage the device for data collection. + This method is expected to put the device into a state where + repeated calls to :meth:`~BlueskyInterface.trigger` and + :meth:`~BlueskyInterface.read` will 'do the right thing'. + Staging not idempotent and should raise + :obj:`RedundantStaging` if staged twice without an + intermediate :meth:`~BlueskyInterface.unstage`. + This method should be as fast as is feasible as it does not return + a status object. + The return value of this is a list of all of the (sub) devices + stage, including it's self. This is used to ensure devices + are not staged twice by the :obj:`~bluesky.run_engine.RunEngine`. + This is an optional method, if the device does not need + staging behavior it should not implement `stage` (or + `unstage`). + Returns + ------- + devices : list + list including self and all child devices staged + """ + if self._staged == Staged.no: + pass # to short-circuit checking individual cases + elif self._staged == Staged.yes: + raise RedundantStaging( + "Device {!r} is already staged. " "Unstage it first.".format(self) + ) + elif self._staged == Staged.partially: + raise RedundantStaging( + "Device {!r} has been partially staged. " + "Maybe the most recent unstaging " + "encountered an error before finishing. " + "Try unstaging again.".format(self) + ) + self.log.debug("Staging %s", self.name) + self._staged = Staged.partially + + # Resolve any stage_sigs keys given as strings: 'a.b' -> self.a.b + stage_sigs = OrderedDict() + for k, v in self.stage_sigs.items(): + if isinstance(k, str): + # Device.__getattr__ handles nested attr lookup + stage_sigs[getattr(self, k)] = v + else: + stage_sigs[k] = v + + # Read current values, to be restored by unstage() + original_vals = {sig: sig.get() for sig in stage_sigs} + + # We will add signals and values from original_vals to + # self._original_vals one at a time so that + # we can undo our partial work in the event of an error. + + # Apply settings. + devices_staged = [] + try: + for sig, val in stage_sigs.items(): + self.log.debug( + "Setting %s to %r (original value: %r)", + self.name, + val, + original_vals[sig], + ) + sig.set(val, timeout=10).wait() + ttime.sleep(self._staging_delay) + # It worked -- now add it to this list of sigs to unstage. + self._original_vals[sig] = original_vals[sig] + devices_staged.append(self) + + # Call stage() on child devices. + for attr in self._sub_devices: + device = getattr(self, attr) + if hasattr(device, "stage"): + device.stage() + devices_staged.append(device) + except Exception: + self.log.debug( + "An exception was raised while staging %s or " + "one of its children. Attempting to restore " + "original settings before re-raising the " + "exception.", + self.name, + ) + self.unstage() + raise + else: + self._staged = Staged.yes + return devices_staged + + def unstage(self): + self._unstage_with_delay() + + def _unstage_with_delay(self): + # Staging taken from https://github.com/bluesky/ophyd/blob/master/ophyd/device.py + # Device - BlueskyInterface + """Unstage the device. + This method returns the device to the state it was prior to the + last `stage` call. + This method should be as fast as feasible as it does not + return a status object. + This method must be idempotent, multiple calls (without a new + call to 'stage') have no effect. + Returns + ------- + devices : list + list including self and all child devices unstaged + """ + self.log.debug("Unstaging %s", self.name) + self._staged = Staged.partially + devices_unstaged = [] + + # Call unstage() on child devices. + for attr in self._sub_devices[::-1]: + device = getattr(self, attr) + if hasattr(device, "unstage"): + device.unstage() + devices_unstaged.append(device) + + # Restore original values. + for sig, val in reversed(list(self._original_vals.items())): + self.log.debug("Setting %s back to its original value: %r)", self.name, val) + sig.set(val, timeout=10).wait() + ttime.sleep(self._staging_delay) + self._original_vals.pop(sig) + devices_unstaged.append(self) + + self._staged = Staged.no + return devices_unstaged + + def kickoff(self, *, scn_cfg={}): + self._encoder.pc.arm.put(0) + ttime.sleep(self._staging_delay) + self._state = "kicked off" + + if scn_cfg["ang_s"] < scn_cfg["ang_e"]: + self._encoder.pc.dir.put(0) + try: + self.rot_axis.user_setpoint.put(scn_cfg["ang_s"] - scn_cfg["taxi_dist"]) + except Exception as e: + print(e) + print("Cannot move rotary stage to its taxi position.") + return + else: + self._encoder.pc.dir.put(1) + try: + self.rot_axis.user_setpoint.put(scn_cfg["ang_s"] + scn_cfg["taxi_dist"]) + except Exception as e: + print(e) + print("Cannot move rotary stage to its taxi position.") + return + + if scn_cfg["scn_mode"] == "snaked: multiple files": + self._encoder.pc.gate_start.put(scn_cfg["ang_s"]) + + # sync rotary stage encoder + self._encoder.pc.enc_pos1_sync.put(1) + ttime.sleep(self._staging_delay) + + # Do a block reset on the zebra + self._encoder.pc.block_state_reset.put(1) + ttime.sleep(self._staging_delay) + + return NullStatus() + + def complete(self): + """ + Call this when all needed data has been collected. This has no idea + whether that is true, so it will obligingly stop immediately. It is + up to the caller to ensure that the motion is actually complete. + """ + + amk_debug_flag = False + + # Our acquisition complete PV is: XF:05IDD-ES:1{Dev:Zebra1}:ARRAY_ACQ + t0 = ttime.monotonic() + while self._encoder.pc.data_in_progress.get() == 1: + ttime.sleep(self._staging_delay) + if (ttime.monotonic() - t0) > 60: + print(f"{self.name} is behaving badly!") + self._encoder.pc.disarm.put(1) + ttime.sleep(0.100) + if self._encoder.pc.data_in_progress.get() == 1: + raise TimeoutError + + self._state = "complete" + self._encoder.pc.block_state_reset.put(1) + + for d in self._dets: + d.stop() + + # Set filename/path for zebra data + f, rp, wp = self.make_filename() + self.__filename = f + self.__read_filepath = os.path.join(rp, self.__filename) + self.__write_filepath = os.path.join(wp, self.__filename) + + self.__filestore_resource, datum_factory_z = resource_factory( + "ZEBRA_HDF51", + root="/", + resource_path=self.__read_filepath, + resource_kwargs={}, + path_semantics="posix", + ) + + time_datum = datum_factory_z({"column": "time"}) + enc1_datum = datum_factory_z({"column": "enc1"}) + + # self._document_cache defines resource and datum documents + self._document_cache = [("resource", self.__filestore_resource)] + self._document_cache.extend( + ("datum", d) + for d in ( + time_datum, + enc1_datum, + ) + ) + + # grab the asset documents from all of the child detectors + for d in self._dets: + self._document_cache.extend(d.collect_asset_docs()) + + # Write the file. + # @timer_wrapper + def get_zebra_data(): + export_zebra_data(self._encoder, self.__write_filepath) + get_zebra_data() + + # Yield a (partial) Event document. The RunEngine will put this + # into metadatastore, as it does all readings. + self._last_bulk = { + "time": ttime.time(), + "seq_num": 1, + "data": { + "time": time_datum["datum_id"], + "enc1": enc1_datum["datum_id"], + }, + "timestamps": { + "time": time_datum["datum_id"], # not a typo# + "enc1": time_datum["datum_id"], + }, + } + for d in self._dets: + reading = d.read() + self._last_bulk["data"].update({k: v["value"] for k, v in reading.items()}) + self._last_bulk["timestamps"].update( + {k: v["timestamp"] for k, v in reading.items()} + ) + print(f"\nin complete: {type(self._last_bulk)=}\n{type(self._document_cache)=}\n") + + return NullStatus() + + def describe_collect(self): + ext_spec = "FileStore:" + + spec = { + "external": ext_spec, + "dtype": "array", + "shape": [self.encoder.pc.data.time.get().shape[0]], + "source": "", # make this the PV of the array the det is writing + } + + desc = OrderedDict() # desc defines data_keys + for chan in ( + "time", + "enc1", + ): + desc[chan] = spec + desc[chan]["source"] = getattr(self._encoder.pc.data, chan).pvname + + # Handle the detectors we are going to get + for d in self._dets: + desc.update(d.describe()) + for chan in d.describe().keys(): + desc[chan]['shape'] = [self.encoder.pc.data.time.get().shape[0]] + print(f"\nin describe_collect {desc=}\n") + + # # Handle the ion chamber that the zebra is collecting + # desc["i0"] = spec + # desc["i0"]["source"] = self._sis.mca2.pvname + # desc["i0_time"] = spec + # desc["i0_time"]["source"] = self._sis.mca1.pvname + # desc["im"] = spec + # desc["im"]["source"] = self._sis.mca3.pvname + # desc["it"] = spec + # desc["it"]["source"] = self._sis.mca4.pvname + + return {"primary": desc} + + def collect(self): + # Create records in the FileStore database. + # move this to stage because I thinkt hat describe_collect needs the + # resource id + # TODO use ophyd.areadectector.filestoer_mixins.resllource_factory here + if self._last_bulk is None: + raise Exception( + "the order of complete and collect is brittle and out " + "of sync. This device relies on in-order and 1:1 calls " + "between complete and collect to correctly create and stash " + "the asset registry documents" + ) + # self._last_bulk defines a event document + yield self._last_bulk + self._last_bulk = None + self._state = "idle" + + def collect_asset_docs(self): + # print(f"\nin collect_asset_docs {self._document_cache=}\n") + yield from iter(list(self._document_cache)) + self._document_cache.clear() + + def stop(self): + self._encoder.pc.block_state_reset.put(1) + pass + + def pause(self): + "Pausing in the middle of a kickoff nukes the partial dataset." + self._encoder.pc.block_state_reset.put(1) + for d in self._dets: + if hasattr(d, "settings"): + d.settings.acquire.put(0) + if hasattr(d, "cam"): + d.cam.acquire.put(0) + self._state = "idle" + self.unstage() + + def resume(self): + self.unstage() + self.stage() + + def preset_flyer(self, scn_cfg): + yield from FXITomoFlyer.bin_det(self.detectors[0], scn_cfg["bin_fac"]) + yield from FXITomoFlyer.init_mot_r(scn_cfg) + yield from FXITomoFlyer.set_cam_mode(self.detectors[0], stage="pre-scan") + scn_cfg = FXITomoFlyer.cal_cam_rot_params(self.detectors[0], scn_cfg) + pc_cfg = FXITomoFlyer.cal_zebra_pc_params(scn_cfg) + self.preset_zebra(pc_cfg) + print("preset_flyer is done") + return scn_cfg, pc_cfg + + def set_pc_step_for_scan(self, scn_cfg, pc_cfg): + yield from abs_set(self.encoder.pc.dir, pc_cfg[self.scn_modes[scn_cfg["scn_mode"]]]["dir"], wait=True) + yield from abs_set(self.encoder.pc.gate_start, pc_cfg[self.scn_modes[scn_cfg["scn_mode"]]]["gate_start"], wait=True) + + @staticmethod + def set_wait(field, val, wait=0.01): + while field.get() != val: + field.put(val, timeout=10) + ttime.sleep(0.01) + ttime.sleep(wait) + + @classmethod + def cal_cam_rot_params(cls, det, scn_cfg): + """_summary_ + + Args: + det (str): choose from the set {"Andor", "Marana", "Oryx"} + scn_cfg (dict): scan configuration parameters composed of + 'scn_mode': choose between { + 0: "standard", # a single scan in a given angle range + 1: "snaked: single file", # back-forth rocking scan being saved into a single file + 2: "snaked: multiple files" # back-forth rocking scan with each swing being saved into a file + } + 'exp_t': detector exposure time in second, + 'acq_p': acquisition period in second, + 'bin_fac': detector binning factor, + 'ang_s': scan starting angle, + 'ang_e': scan end angle, + 'num_swing': number of sub-scans; motion from one side to another side is defined as one swing + 'vel': rotation velocity in deg/sec, + 'tacc': rotation stage acceleration in sec, + "taxi_dist": taxi distance in unit deg + + Returns: + dict: scan_cfg + """ + ############### calculate detector parameters ############### + acq_p = FXITomoFlyer.check_cam_exp(det, scn_cfg["acq_p"], scn_cfg["bin_fac"]) + + if acq_p > scn_cfg["acq_p"]: + print( + "Acquisition period is too small for the camera. Reset acquisition period to minimum allowed exposure time." + ) + scn_cfg["acq_p"] = acq_p + + if scn_cfg["exp_t"] > acq_p - 0.002: + scn_cfg["exp_t"] = acq_p - 0.002 + + # det.cam.acquire_period.put(scan_cfg["acq_p"]) + # det.cam.acquire_time.put(scan_cfg["exp_t"]) + # det.cam.image_mode.put(0) # 0: 'Fixed', 1: 'Continuous' + # det.cam.trigger_mode.put( + # 4 + # ) # 0: 'Internal', 1: 'External Start', 2: 'External Exposure', 3: 'Software', 4: 'External' + + ############### calculate rotary stage parameters ############### + if scn_cfg["tacc"] <= 0: + print("Acceleration time cannot be smaller than 0. Reset it to 1 second.") + scn_cfg["tacc"] = 1 + + if scn_cfg["vel"] > cls.rot_axis.max_velo.get(): + print( + "Designed velocity exceeds the maximum allowed velocity. Reset it to the maximum allowed velocity" + ) + scn_cfg["vel"] = cls.rot_axis.max_velo.get() + elif scn_cfg["vel"] < cls.rot_axis.base_velo.get(): + print( + "Designed velocity is smaller than the minimum allowed velocity. Reset it to the minimum allowed velocity" + ) + scn_cfg["vel"] = cls.rot_axis.base_velo.get() + + taxi_dist = np.ceil( + (scn_cfg["vel"] - cls.rot_axis.base_velo.get()) * scn_cfg["tacc"] / 2 + ) + if not (cls.rot_axis.low_limit_switch.get() == 0 and cls.rot_axis.high_limit_switch.get() == 0): + if (scn_cfg["ang_s"] - taxi_dist) < cls.rot_axis.low_limit.get(): + print("Rotation range is beyond the low limit of the rotary stage. Quit!") + return None + elif (scn_cfg["ang_s"] - taxi_dist) > cls.rot_axis.high_limit.get(): + print("Rotation range is beyond the high limit of the rotary stage. Quit!") + return None + + if (scn_cfg["ang_e"] + taxi_dist) < cls.rot_axis.low_limit.get(): + print("Rotation range is beyond the low limit of the rotary stage. Quit!") + return None + elif (scn_cfg["ang_e"] + taxi_dist) > cls.rot_axis.high_limit.get(): + print("Rotation range is beyond the high limit of the rotary stage. Quit!") + return None + + scn_cfg["taxi_dist"] = taxi_dist + scn_cfg["num_images"] = ( + int( + abs( + (scn_cfg["ang_e"] - scn_cfg["ang_s"]) / scn_cfg["vel"] / scn_cfg["acq_p"] + ) + ) + + 1 + ) + scn_cfg["ang_step"] = scn_cfg["vel"] * scn_cfg["acq_p"] + + if scn_cfg["ang_s"] < scn_cfg["ang_e"]: + scn_cfg["rot_dir"] = 1 + else: + scn_cfg["rot_dir"] = -1 + + return scn_cfg + + @staticmethod + def check_cam_exp(det, acq_p, bin_fac): + acq_min = DET_MIN_AP[det.name] / BIN_FACS[det.name][bin_fac] + acq_p = max(acq_min, acq_p) + return acq_p + + @classmethod + def cal_zebra_pc_params(cls, scn_cfg): + """_summary_ + + Args: + scn_cfg (dict): scan configuration parameters composed of + 'scn_mode': choose between { + 0: "standard", # a single scan in a given angle range + 1: "snaked: single file", # back-forth rocking scan being saved into a single file + 2: "snaked: multiple files" # back-forth rocking scan with each swing being saved into a file + } + 'exp_t': detector exposure time in second, + 'acq_p': acquisition period in second, + 'bin_fac': detector binning factor, + 'ang_s': scan starting angle, + 'ang_e': scan end angle, + 'num_swing': number of sub-scans; motion from one side to another side is defined as one swing + 'vel': rotation velocity in deg/sec, + 'tacc': rotation stage acceleration in sec, + "taxi_dist": taxi distance in unit deg + + Returns: + dict: pc_cfg + """ + pc_cfg = { + "standard": {}, + "snaked: multiple files": {}, + "snaked: single file": {}, + } + if cls.scn_modes[scn_cfg["scn_mode"]] == "standard": + pc_cfg["standard"]["gate_start"] = scn_cfg["ang_s"] + pc_cfg["standard"]["gate_width"] = abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + pc_cfg["standard"]["pulse_start"] = 0 + pc_cfg["standard"]["pulse_width"] = ( + cls.dft_pulse_wid[cls.tspre] * scn_cfg["vel"] + ) + pc_cfg["standard"]["pulse_step"] = round(scn_cfg["ang_step"], 3) + pc_cfg["standard"]["pulse_max"] = int(round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / pc_cfg["standard"]["pulse_step"] + + 1 + )) + pc_cfg["standard"]["gate_width"] = ( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + + pc_cfg["standard"]["pulse_step"] + ) + elif cls.scn_modes[scn_cfg["scn_mode"]] == "snaked: multiple files": + pc_cfg["snaked: multiple files"]["pulse_start"] = 0 + pc_cfg["snaked: multiple files"]["pulse_width"] = ( + cls.dft_pulse_wid[cls.tspre] * scn_cfg["vel"] + ) + pc_cfg["snaked: multiple files"]["pulse_step"] = round(scn_cfg["ang_step"], 3) + pc_cfg["snaked: multiple files"]["pulse_max"] = int(round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / pc_cfg["snaked: multiple files"]["pulse_step"] + + 1 + )) + pc_cfg["snaked: multiple files"]["gate_width"] = ( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + + pc_cfg["snaked: multiple files"]["pulse_step"] + ) + elif cls.scn_modes[scn_cfg["scn_mode"]] == "snaked: single file": + pc_cfg["snaked: single file"]["pulse_start"] = 0 + pc_cfg["snaked: single file"]["pulse_width"] = cls.dft_pulse_wid[cls.tspre] + pc_cfg["snaked: single file"]["pulse_step"] = round(scn_cfg["acq_p"], 4) + pc_cfg["snaked: single file"]["pulse_max"] = int(int(round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / (scn_cfg["acq_p"] * scn_cfg["vel"]) + + 2 * scn_cfg["tacc"] / scn_cfg["acq_p"] + + 1 + )) * scn_cfg["num_swing"]) + else: + print("Unrecognized scan mode. Quit") + return None + pc_cfg[cls.scn_modes[scn_cfg["scn_mode"]]]["dir"] = cls.pc_trig_dir[scn_cfg["rot_dir"]] + return pc_cfg + + @staticmethod + def compose_scn_cfg( + scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, tacc, num_swing + ): + scn_cfg = {} + scn_cfg["scn_mode"] = scn_mode + scn_cfg["exp_t"] = exp_t + scn_cfg["acq_p"] = acq_p + scn_cfg["bin_fac"] = 0 if bin_fac is None else bin_fac + scn_cfg["ang_s"] = ang_s + scn_cfg["ang_e"] = ang_e + scn_cfg["vel"] = vel + scn_cfg["tacc"] = tacc + scn_cfg["num_swing"] = num_swing + return scn_cfg + + @staticmethod + def prime_det(det): + if not list(det.hdf5.array_size.get()): + if det.cam.trigger_mode.get() != 0: + yield from abs_set(det.cam.trigger_mode, 0, wait=True) + if det.cam.image_mode.get() != 0: + yield from abs_set(det.cam.image_mode, 0, wait=True) + yield from abs_set(det.cam.num_images, 5, wait=True) + yield from abs_set(det.cam.acquire, 1, wait=True) + + @staticmethod + def bin_det(det, bin_fac): + yield from abs_set(det.cam.acquire, 0, wait=True) + if bin_fac is None: + bin_fac = 0 + if int(bin_fac) not in [0, 1, 2, 3, 4]: + raise ValueError("binnng must be in [0, 1, 2, 3, 4]") + yield from abs_set(det.binning, bin_fac, wait=True) + FXITomoFlyer.prime_det(det) + + @staticmethod + def def_abs_out_pos( + x_out, + y_out, + z_out, + r_out, + rel_out_flag, + ): + (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() + if rel_out_flag: + mot_x_out = x_ini + x_out if not (x_out is None) else x_ini + mot_y_out = y_ini + y_out if not (y_out is None) else y_ini + mot_z_out = z_ini + z_out if not (z_out is None) else z_ini + mot_r_out = r_ini + r_out if not (r_out is None) else r_ini + else: + mot_x_out = x_out if not (x_out is None) else x_ini + mot_y_out = y_out if not (y_out is None) else y_ini + mot_z_out = z_out if not (z_out is None) else z_ini + mot_r_out = r_out if not (r_out is None) else r_ini + return mot_x_out, mot_y_out, mot_z_out, mot_r_out + + @staticmethod + def get_txm_cur_pos(): + x_ini = zps.sx.position + y_ini = zps.sy.position + z_ini = zps.sz.position + r_ini = zps.pi_r.position + return x_ini, y_ini, z_ini, r_ini + + @staticmethod + def init_mot_r(scn_cfg): + # cur_pos = zps.pi_r.position + # if abs(ini_r - cur_pos) > 360: + # cur_pos = ini_r // 360 * 360 + ini_r % 360 + # yield from abs_set(zps.pi_r.motor_calib, 1, wait=True) + # yield from abs_set(zps.pi_r.user_setpoint, cur_pos, wait=True) + # yield from abs_set(zps.pi_r.motor_calib, 0, wait=True) + # yield from abs_set(zps.pi_r.acceleration, 1, wait=True) + # yield from abs_set(zps.pi_r.velocity, 30, wait=True) + # yield from abs_set(zps.pi_r.user_setpoint, ini_r, wait=True) + cur_pos = zps.pi_r.position + yield from abs_set(zps.pi_r.offset_freeze_switch, 1) + ang_max = max(scn_cfg["ang_s"], scn_cfg["ang_e"]) + + if ang_max > 0: + yield from abs_set(zps.pi_r.user_offset, np.ceil(ang_max / 360) * 360) + + if abs(scn_cfg["ang_s"] - cur_pos) > 360: + cur_pos = scn_cfg["ang_s"] // 360 * 360 + cur_pos % 360 + zps.pi_r.set_current_position(cur_pos) + yield from abs_set(zps.pi_r.acceleration, 1, wait=True) + yield from abs_set(zps.pi_r.velocity, 30, wait=True) + yield from abs_set(zps.pi_r, scn_cfg["ang_s"], wait=True) + + @staticmethod + def set_cam_step_for_scan(det, scn_cfg): + # yield from abs_set(det.cam.acquire_time, scn_cfg["exp_t"], wait=True) + # yield from abs_set(det.cam.acquire_period, scn_cfg["acq_p"], wait=True) + # yield from abs_set(det.hdf5.num_capture, scn_cfg["num_images"], wait=True) + # yield from abs_set(det.cam.num_images, scn_cfg["num_images"], wait=True) + # yield from abs_set(det.cam.acquire, 1, wait=True) + FXITomoFlyer.set_wait(det.cam.acquire_time, scn_cfg["exp_t"]) + FXITomoFlyer.set_wait(det.cam.acquire_period, scn_cfg["acq_p"]) + FXITomoFlyer.set_wait(det.hdf5.num_capture, scn_cfg["num_images"]) + FXITomoFlyer.set_wait(det.cam.num_images, scn_cfg["num_images"]) + # FXITomoFlyer.set_wait(det.cam.acquire, 1, wait=0.2) + yield from abs_set(det.cam.acquire, 1, wait=True) + + @staticmethod + def set_mot_r_step_for_scan(scn_cfg): + # yield from abs_set(zps.pi_r.acceleration, scn_cfg["tacc"], wait=True) + # yield from abs_set(zps.pi_r.velocity, scn_cfg["vel"], wait=True) + # yield from abs_set(zps.pi_r.user_setpoint, scn_cfg["ang_s"], wait=True) + FXITomoFlyer.set_wait(zps.pi_r.acceleration, scn_cfg["tacc"]) + FXITomoFlyer.set_wait(zps.pi_r.velocity, scn_cfg["vel"]) + FXITomoFlyer.set_wait(zps.pi_r.user_setpoint, scn_cfg["ang_s"] - scn_cfg["rot_dir"] * scn_cfg["taxi_dist"]) + + @staticmethod + def set_cam_mode(cam, stage="pre-scan"): + if stage == "pre-scan": + yield from abs_set(cam.cam.image_mode, 0) + yield from abs_set(cam.cam.trigger_mode, 4) + elif stage == "ref-scan": + yield from abs_set(cam.cam.image_mode, 0) + yield from abs_set(cam.cam.trigger_mode, 0) + elif stage == "post-scan": + yield from abs_set(cam.cam.image_mode, 1) + yield from abs_set(cam.cam.trigger_mode, 0) + + +Zebra = FXIZebra( + "XF:18ID-ES:1{Dev:Zebra1}:", + name="Zebra", + read_attrs=["pc.data.enc1", "pc.data.time"], +) + +tomo_flyer = FXITomoFlyer( + list((Andor,)), + Zebra, + name="tomo_flyer", +) + + +def export_zebra_data(zebra, filepath): + j = 0 + while zebra.pc.data_in_progress.get() == 1: + print("Waiting for zebra...") + ttime.sleep(0.1) + j += 1 + if j > 10: + print("THE ZEBRA IS BEHAVING BADLY CARRYING ON") + break + + time_d = zebra.pc.data.time.get() + enc1_d = zebra.pc.data.enc1.get() + + size = (len(time_d),) + with h5py.File(filepath, "w") as f: + dset0 = f.create_dataset("time", size, dtype="f") + dset0[...] = np.array(time_d) + dset1 = f.create_dataset("enc1", size, dtype="f") + dset1[...] = np.array(enc1_d) + + +class ZebraHDF5Handler(HandlerBase): + HANDLER_NAME = "ZEBRA_HDF51" + + def __init__(self, resource_fn): + self._handle = h5py.File(resource_fn, "r") + + def __call__(self, *, column): + return self._handle[column][:] + + def close(self): + self._handle.close() + self._handle = None + super().close() + + +db.reg.register_handler("ZEBRA_HDF51", ZebraHDF5Handler, overwrite=True) diff --git a/startup/40-scan_pre_define.py b/startup/40-scan_pre_define.py index f73e26d..4b8ff5b 100644 --- a/startup/40-scan_pre_define.py +++ b/startup/40-scan_pre_define.py @@ -49,7 +49,7 @@ def _take_image(detectors, motor, num, stream_name="primary"): def _set_Andor_chunk_size(detectors, chunk_size): for detector in detectors: yield from unstage(detector) - yield from bps.configure(Andor, {"cam.num_images": chunk_size}) + yield from abs_set(Andor.cam.num_images, chunk_size, wait=True) for detector in detectors: yield from stage(detector) @@ -177,4 +177,36 @@ def _set_rotation_speed(rs=30): yield from abs_set(zps.pi_r.velocity, rs) -# +def _move_sample(x_pos, y_pos, z_pos, r_pos, repeat=1): + """_summary_ + + Args: + x_pos (float): absolute position x-stage moving to + y_pos (float): absolute position y-stage moving to + z_pos (float): absolute position z-stage moving to + r_pos (float): absolute position r-stage moving to + repeat (int, optional): number of trials. Defaults to 1. + """ + for i in range(repeat): + yield from mv(zps.pi_r, r_pos) + yield from mv(zps.sx, x_pos, zps.sy, y_pos, zps.sz, z_pos) + + +def _take_ref_image( + cams, + mots_pos = {}, + num=1, + chunk_size=1, + stream_name="flat", + simu=False, +): + if stream_name == "flat": + yield from _move_sample( + mots_pos["x"], mots_pos["y"], mots_pos["z"], mots_pos["r"], repeat=2 + ) + yield from _open_shutter_xhx(simu) + elif stream_name == "dark": + yield from _close_shutter_xhx(simu) + yield from _set_Andor_chunk_size(cams, chunk_size) + yield from _take_image(cams, [], num, stream_name=stream_name) + diff --git a/startup/41-scans.py b/startup/41-scans.py index 877fc1e..946986c 100644 --- a/startup/41-scans.py +++ b/startup/41-scans.py @@ -262,7 +262,8 @@ def fly_scan( motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] detectors = [Andor, ic3] - offset_angle = -1 * rs + #offset_angle = -1 * rs + offset_angle = 0 current_rot_angle = zps.pi_r.position target_rot_angle = current_rot_angle + relative_rot_angle _md = { diff --git a/startup/44-scans_other.py b/startup/44-scans_other.py index b6270ea..e8dd557 100644 --- a/startup/44-scans_other.py +++ b/startup/44-scans_other.py @@ -1,7 +1,7 @@ -try: - print(detA1) -except: - detA1 = None +# try: +# print(detA1) +# except: +# detA1 = None def test_test(): @@ -123,6 +123,7 @@ def inner_scan(): def test_scan2( exposure_time=0.1, + period_time=0.1, out_x=-100, out_y=-100, out_z=0, @@ -153,7 +154,7 @@ def test_scan2( num_img: int, number of images to take """ - yield from _set_andor_param(exposure_time, exposure_time, 1) + yield from _set_andor_param(exposure_time, period_time, 1) detectors = [Andor] motor_x_ini = zps.sx.position @@ -260,6 +261,7 @@ def z_scan( note="", md=None, simu=False, + cam=Andor ): """ scan the zone-plate to find best focus @@ -286,24 +288,24 @@ def z_scan( """ - detectors = [Andor] + detectors = [cam] motor = zp.z z_ini = motor.position # zp.z intial position z_start = z_ini + start z_stop = z_ini + stop - # detectors = [Andor] + # detectors = [cam] y_ini = zps.sy.position # sample y position (initial) y_out = ( y_ini + out_y if not (out_y is None) else y_ini ) # sample y position (out-position) x_ini = zps.sx.position x_out = x_ini + out_x if not (out_x is None) else x_ini - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, chunk_size) - yield from mv(Andor.cam.acquire_time, exposure_time) + yield from mv(cam.cam.acquire, 0) + yield from mv(cam.cam.image_mode, 0) + yield from mv(cam.cam.num_images, chunk_size) + yield from mv(cam.cam.acquire_time, exposure_time) period_cor = max(exposure_time + 0.01, 0.05) - yield from mv(Andor.cam.acquire_period, period_cor) + yield from mv(cam.cam.acquire_period, period_cor) _md = { "detectors": [det.name for det in detectors], @@ -365,10 +367,10 @@ def inner_scan(): yield from mv(zps.sy, y_ini) yield from mv(zp.z, z_ini) # yield from abs_set(shutter_open, 1, wait=True) - yield from mv(Andor.cam.image_mode, 1) + yield from mv(cam.cam.image_mode, 1) uid = yield from inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(cam.cam.image_mode, 1) yield from _close_shutter(simu=simu) txt = get_scan_parameter() insert_text(txt) diff --git a/startup/45-baseline.py b/startup/45-baseline.py index 9250063..f60a2c7 100644 --- a/startup/45-baseline.py +++ b/startup/45-baseline.py @@ -3,7 +3,7 @@ zp, aper, clens, - phase_ring, + #phase_ring, betr, zps, DetU, diff --git a/startup/46-zebra_flyer.py b/startup/46-zebra_flyer.py new file mode 100644 index 0000000..fdfd043 --- /dev/null +++ b/startup/46-zebra_flyer.py @@ -0,0 +1,188 @@ +from bluesky.plan_stubs import kickoff, collect, complete + + +def tomo_zfly( + scn_mode=0, + exp_t=0.05, + acq_p=0.05, + ang_s=0, + ang_e=180, + vel=3, + acc_t=1, + num_swing=1, + out_x=None, + out_y=None, + out_z=None, + out_r=None, + rel_out_flag=True, + flts=[], + rot_back_velo=30, + bin_fac=None, + note="", + md=None, + simu=False, + cam=Andor, + flyer=tomo_flyer, +): + """_summary_ + + Args: + scn_mode (int, optional): _description_. Defaults to 0. + exp_t (float, optional): _description_. Defaults to 0.05. + acq_p (float, optional): _description_. Defaults to 0.05. + ang_s (float or None, optional): _description_. Defaults to None. + ang_e (float, optional): _description_. Defaults to 180. + vel (int, optional): _description_. Defaults to 3. + acc_t (float, optional): _description_. Defaults to 1. + num_swing (int, optional): _description_. Defaults to 1. + out_x (float, optional): _description_. Defaults to None. + out_y (float, optional): _description_. Defaults to None. + out_z (float, optional): _description_. Defaults to None. + out_r (float, optional): _description_. Defaults to None. + flts (list, optional): _description_. Defaults to []. + rot_back_velo (int, optional): _description_. Defaults to 30. + binning (int, optional): _description_. Defaults to None. + note (str, optional): _description_. Defaults to "". + md (dict, optional): _description_. Defaults to None. + simu (bool, optional): _description_. Defaults to False. + cam (ophyd.Device, optional): detector; choose between Andor, Marana, and Oryx. + + Raises: + ValueError: _description_ + + Returns: + _type_: _description_ + + Yields: + _type_: _description_ + """ + global ZONE_PLATE + mots = [zps.sx, zps.sy, zps.sz, zps.pi_r] + flyer.detectors = [ + cam, + ] + scn_cfg = FXITomoFlyer.compose_scn_cfg( + scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, acc_t, num_swing + ) + yield from flyer.preset_flyer(scn_cfg) + (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() + (mot_x_out, mot_y_out, mot_z_out, mot_r_out) = FXITomoFlyer.def_abs_out_pos( + out_x, out_y, out_z, out_r, rel_out_flag + ) + + _md = { + "detectors": [flyer.detectors[0].name], + "motors": [mot.name for mot in mots], + "XEng": XEng.position, + "storage_ring_current (mA)": round(sr_current.get(), 1), + "plan_args": { + "exposure_time": scn_cfg["exp_t"], + "start_angle": scn_cfg["ang_s"], + "end_angle": scn_cfg["ang_e"], + "acquisition_period": scn_cfg["acq_p"], + "number_of_swings": scn_cfg["num_swing"], + "out_x": mot_x_out, + "out_y": mot_y_out, + "out_z": mot_z_out, + "out_r": mot_r_out, + "slew_speed": scn_cfg["vel"], + "rel_out_flag": rel_out_flag, + "filters": ["filter{}".format(t) for t in flts] if flts else "None", + "binning": 0 if scn_cfg["bin_fac"] is None else scn_cfg["bin_fac"], + "note": note if note else "None", + "zone_plate": ZONE_PLATE, + }, + "plan_name": "tomo_zfly", + "num_bkg_images": 10, + "num_dark_images": 10, + "plan_pattern": "linspace", + "plan_pattern_module": "numpy", + "hints": {}, + "operator": "FXI", + "note": note if note else "None", + "zone_plate": ZONE_PLATE, + } + _md.update(md or {}) + + print('preset done') + @stage_decorator(list(flyer.detectors) + list(mots)) + @run_decorator(md=_md) + def inner_fly_plan(): + yield from select_filters(flts) + for ii in range(scn_cfg["num_swing"]): + yield from _open_shutter_xhx(simu) + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) + st.wait(timeout=10) + + yield from abs_set(flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True) + yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + + yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + t0 = ttime.monotonic() + yield from abs_set( + zps.pi_r, + scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + wait=True + ) + + t1 = ttime.monotonic() + while int(flyer.encoder.pc.gated.get()): + if ttime.monotonic() - t1 > 60: + print("Scan finished abnormally. Quit!") + return + yield from bps.sleep(flyer._staging_delay) + print(ttime.time()) + print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") + st = yield from complete(flyer, wait=True) + st.wait(timeout=10) + yield from collect(flyer) + if (scn_cfg["num_swing"] > 1) and ( + flyer.scn_modes[scn_cfg["scn_mode"]] != "snaked: single file" + ): + (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( + scn_cfg["ang_e"], + scn_cfg["ang_s"], + ) + scn_cfg["rot_dir"] *= -1 + pc_cfg[scn_cfg["scn_mode"]]["dir"] = flyer.pc_trig_dir[int(scn_cfg["rot_dir"])] + # yield from flyer.set_mot_r_step_for_scan(scn_cfg) + yield from flyer.set_pc_step_for_scan(scn_cfg, pc_cfg) + else: + yield from FXITomoFlyer.init_mot_r(scn_cfg) + + yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") + yield from _take_ref_image( + [cam], + mots_pos={ + "x": mot_x_out, + "y": mot_y_out, + "z": mot_z_out, + "r": mot_r_out, + }, + num=1, + chunk_size=10, + stream_name="flat", + simu=simu, + ) + yield from _take_ref_image( + [cam], + mots_pos={}, + num=1, + chunk_size=10, + stream_name="dark", + simu=simu, + ) + yield from _move_sample( + x_ini, + y_ini, + z_ini, + r_ini, + repeat=2, + ) + yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") + yield from select_filters([]) + + uid = yield from inner_fly_plan() + print("scan finished") + return uid diff --git a/startup/80-load_scan.py b/startup/80-load_scan.py index d1d8427..1892b61 100644 --- a/startup/80-load_scan.py +++ b/startup/80-load_scan.py @@ -28,9 +28,20 @@ def get_fly_scan_angle(scan_id): timestamp_mot = timestamp_to_float(pos["time"]) img_ini_timestamp = timestamp_tomo[0] - mot_ini_timestamp = timestamp_mot[ - 1 - ] # timestamp_mot[1] is the time when taking dark image + + # something not correct in rotary stage. + # we do following correction on 2023_5_16 + # mot_ini_timestamp = timestamp_mot[1] # timestamp_mot[1] is the time when taking dark image + + n = len(timestamp_mot) + for idx in range(1, n): + ts1 = timestamp_mot[idx] - timestamp_mot[idx-1] + ts2 = timestamp_mot[idx+1] - timestamp_mot[idx] + if ts1 < 0.25 and ts2 < 0.25: + break + mot_ini_timestamp = timestamp_mot[idx] + ## end modifing + tomo_time = timestamp_tomo - img_ini_timestamp mot_time = timestamp_mot - mot_ini_timestamp diff --git a/startup/91-functions.py b/startup/91-functions.py index 994e953..39569c8 100644 --- a/startup/91-functions.py +++ b/startup/91-functions.py @@ -1428,6 +1428,40 @@ def get_scan_motor_pos(scan_id): except: pass +def get_scan_motor_xyz(scan_id): + df = db[scan_id].table("baseline").T + mot = BlueskyMagics.positioners + for i in mot: + try: + mot_name = i.name + if mot_name == 'zps_sx' or mot_name == 'zps_sy' or mot_name == 'zps_sz' or mot_name == 'zps_pi_r': + if mot_name[:3] == "pzt": + print(f"{mot_name:>16s} :: {df[1][mot_name]: 14.6f} ") + else: + mot_parent_name = i.parent.name + offset_name = f"{mot_name}_user_offset" + offset_dir = f"{mot_name}_user_offset_dir" + offset_val = db[scan_id].config_data(mot_parent_name)["baseline"][0][ + offset_name + ] + offset_dir_val = db[scan_id].config_data(mot_parent_name)["baseline"][ + 0 + ][offset_dir] + print( + f"{mot_name:>16s} :: {df[1][mot_name]: 14.6f} {i.motor_egu.value:>4s} ---> {df[2][mot_name]: 14.6f} {i.motor_egu.value:>4s} offset = {offset_val: 14.6f} {offset_dir_val: 1d}" + ) + except: + pass + try: + for tmp in db[ + scan_id + ].start.keys(): # if 'T_enabled' has been assigned in md in scan()' + if "T_" in tmp: + get_lakeshore_param(scan_id, print_flag=1) + break + except: + pass + def reprint_scan(scan_id): from bluesky.callbacks.best_effort import BestEffortCallback diff --git a/startup/98-user_scan.py b/startup/98-user_scan.py index 94f9fdc..7c0bacf 100644 --- a/startup/98-user_scan.py +++ b/startup/98-user_scan.py @@ -640,6 +640,7 @@ def _multi_pos_xanes_3D_xh( enable_z=True ): yield from select_filters(flts) + # yield from _bin_cam(binning) n = len(x_list) for rep in range(repeat): for i in range(n): @@ -829,7 +830,7 @@ def _multi_pos_xanes_2D_xh( print(out_r) global ZONE_PLATE yield from select_filters(flts) - binning = yield from _bin_cam(binning) + # yield from _bin_cam(binning) detectors = [Andor, ic3, ic4] #print(f"{exposure_time=}") @@ -972,7 +973,7 @@ def inner_scan(): # close shutter and sleep yield from _close_shutter_xhx(simu) # sleep - if rep < repeat_num: + if rep < repeat_num - 1: print(f"\nsleep for {sleep_time} seconds ...") yield from bps.sleep(sleep_time) yield from inner_scan() @@ -1077,11 +1078,21 @@ def _exp_t_sanity_check(exp_t, binning=None): def _bin_cam(binning): + yield from mv(Andor.cam.acquire, 0) + yield from bps.sleep(1) if binning is None: binning = 0 if int(binning) not in [0, 1, 2, 3, 4]: raise ValueError("binnng must be in [0, 1, 2, 3, 4]") yield from mv(Andor.binning, binning) + yield from bps.sleep(1) + yield from mv(Andor.cam.image_mode, 0) + yield from bps.sleep(1) + yield from mv(Andor.cam.num_images, 5) + yield from bps.sleep(1) + yield from mv(Andor.cam.acquire, 1) + yield from bps.sleep(1) + yield from mv(Andor.cam.acquire, 0) return int(binning) @@ -1172,13 +1183,14 @@ def _take_dark_image_xhx( num=1, chunk_size=1, stream_name="dark", - simu=False + simu=False, + cam=Andor ): yield from _close_shutter_xhx(simu) - original_num_images = yield from rd(Andor.cam.num_images) - yield from _set_Andor_chunk_size(detectors, chunk_size) + original_num_images = yield from rd(cam.cam.num_images) + yield from _set_Andor_chunk_size_xhx(detectors, chunk_size, cam) yield from _take_image(detectors, [], num, stream_name=stream_name) - yield from _set_Andor_chunk_size(detectors, original_num_images) + yield from _set_Andor_chunk_size_xhx(detectors, original_num_images, cam) def _take_bkg_image_xhx( @@ -1193,16 +1205,35 @@ def _take_bkg_image_xhx( rot_first_flag=1, stream_name="flat", simu=False, - enable_z=False + enable_z=False, + cam=Andor ): yield from _move_sample_out_xhx( out_x, out_y, out_z, out_r, repeat=2, rot_first_flag=rot_first_flag, enable_z=enable_z ) - original_num_images = yield from rd(Andor.cam.num_images) - yield from _set_Andor_chunk_size(detectors, chunk_size) + original_num_images = yield from rd(cam.cam.num_images) + yield from _set_Andor_chunk_size_xhx(detectors, chunk_size, cam) yield from _take_image(detectors, [], num, stream_name=stream_name) - yield from _set_Andor_chunk_size(detectors, original_num_images) + yield from _set_Andor_chunk_size_xhx(detectors, original_num_images, cam) + + +def _set_Andor_chunk_size_xhx(detectors, chunk_size, cam): + for detector in detectors: + yield from unstage(detector) + yield from bps.configure(cam, {"cam.num_images": chunk_size}) + for detector in detectors: + yield from stage(detector) + + +def _set_andor_param_xhx(exposure_time=0.1, period=0.1, chunk_size=1, binning=[1, 1], cam=Andor): + yield from mv(cam.cam.acquire, 0) + yield from mv(cam.cam.image_mode, 0) + yield from mv(cam.cam.num_images, chunk_size) + period_cor = period + yield from mv(cam.cam.acquire_time, exposure_time) + # yield from abs_set(cam.cam.acquire_period, period_cor) + cam.cam.acquire_period.put(period_cor) def multi_edge_xanes( @@ -1374,17 +1405,16 @@ def multi_edge_xanes2( if repeat is None: repeat = 1 repeat = int(repeat) - start_angle=zps.pi_r.position + if start_angle is None: + start_angle=zps.pi_r.position if scan_type == "2D": if binning is None: binning = 0 - binning = yield from _bin_cam(binning) - - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, 5) - yield from mv(Andor.cam.acquire, 1) + # binning = yield from _bin_cam(binning) + print(1) x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) + print(2) for elem in elements: for key in flts.keys(): if elem.split("_")[0] == key.split("_")[0]: @@ -1428,7 +1458,7 @@ def multi_edge_xanes2( relative_move_flag=relative_move_flag, note=note, md=None, - sleep_time=0, + sleep_time=sleep, repeat_num=repeat, binning=binning, flts=flt, @@ -1493,7 +1523,7 @@ def multi_edge_xanes2( simu=simu, relative_move_flag=relative_move_flag, rot_first_flag=1, - sleep_time=0, + sleep_time=sleep, binning=binning, flts=flt, repeat=repeat, @@ -1539,7 +1569,8 @@ def fly_scan2( md=None, move_to_ini_pos=True, simu=False, - enable_z=True + enable_z=True, + cam=Andor ): """ Inputs: @@ -1582,12 +1613,15 @@ def fly_scan2( False: will really close/open shutter """ - yield from mv(Andor.cam.acquire, 0) + yield from mv(cam.cam.acquire, 0) if binning is None: binning = 0 if int(binning) not in [0, 1, 2, 3, 4]: raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) + yield from mv(cam.binning, binning) + yield from mv(cam.cam.image_mode, 0) + yield from mv(cam.cam.num_images, 5) + yield from mv(cam.cam.acquire, 1) global ZONE_PLATE motor_x_ini = zps.sx.position @@ -1614,7 +1648,7 @@ def fly_scan2( else: motor = [zps.sx, zps.sy, zps.pi_r] - dets = [Andor, ic3] + dets = [cam, ic3] taxi_ang = -1 * rs cur_rot_ang = zps.pi_r.position tgt_rot_ang = cur_rot_ang + rel_rot_ang @@ -1659,8 +1693,9 @@ def fly_scan2( else: _md["hints"].setdefault("dimensions", dimensions) - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=20, binning=binning + yield from _set_andor_param_xhx( + exposure_time=exposure_time, period=period, chunk_size=20, binning=binning, + cam=cam ) yield from _set_rotation_speed(rs=np.abs(rs)) print("set rotation speed: {} deg/sec".format(rs)) @@ -1669,24 +1704,25 @@ def fly_scan2( @bpp.monitor_during_decorator([zps.pi_r]) @run_decorator(md=_md) def fly_inner_scan(): - yield from select_filters(flts) + # yield from select_filters(flts) yield from bps.sleep(1) # close shutter, dark images: numer=chunk_size (e.g.20) print("\nshutter closed, taking dark images...") yield from _take_dark_image_xhx( dets, motor, num=1, chunk_size=20, - stream_name="dark", simu=simu + stream_name="dark", simu=simu, + cam=cam ) # open shutter, tomo_images - true_period = yield from rd(Andor.cam.acquire_period) + true_period = yield from rd(cam.cam.acquire_period) rot_time = np.abs(rel_rot_ang) / np.abs(rs) num_img = int(rot_time / true_period) + int(10 * rs) yield from _open_shutter_xhx(simu=simu) print("\nshutter opened, taking tomo images...") - yield from _set_Andor_chunk_size(dets, chunk_size=num_img) + yield from _set_Andor_chunk_size_xhx(dets, num_img, cam) # yield from mv(zps.pi_r, cur_rot_ang + taxi_ang) status = yield from abs_set(zps.pi_r, tgt_rot_ang, wait=False) # yield from bps.sleep(1) @@ -1712,7 +1748,8 @@ def fly_inner_scan(): rot_first_flag=rot_first_flag, stream_name="flat", simu=simu, - enable_z=enable_z + enable_z=enable_z, + cam=cam ) yield from _close_shutter_xhx(simu=simu) if move_to_ini_pos: @@ -1727,8 +1764,8 @@ def fly_inner_scan(): ) uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) - yield from select_filters([]) + yield from mv(cam.cam.image_mode, 1) + # yield from select_filters([]) print("scan finished") return uid @@ -1754,7 +1791,8 @@ def fly_scan3( simu=False, noDark=False, noFlat=False, - enable_z=True + enable_z=True, + cam=Andor ): """ Inputs: @@ -1797,7 +1835,7 @@ def fly_scan3( False: will really close/open shutter """ - yield from mv(Andor.cam.acquire, 0) + yield from mv(cam.cam.acquire, 0) global ZONE_PLATE yield from select_filters(flts) @@ -1805,7 +1843,7 @@ def fly_scan3( binning = 0 if int(binning) not in [0, 1, 2, 3, 4]: raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) + yield from mv(cam.binning, binning) motor_x_ini = zps.sx.position motor_y_ini = zps.sy.position @@ -1831,7 +1869,7 @@ def fly_scan3( else: motor = [zps.sx, zps.sy, zps.pi_r] - dets = [Andor, ic3] + dets = [cam, ic3] taxi_ang = -1 * rs cur_rot_ang = zps.pi_r.position tgt_rot_ang = cur_rot_ang + rel_rot_ang @@ -1879,8 +1917,9 @@ def fly_scan3( else: _md["hints"].setdefault("dimensions", dimensions) - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=20, binning=binning + yield from _set_andor_param_xhx( + exposure_time=exposure_time, period=period, chunk_size=20, binning=binning, + cam=cam ) yield from _set_rotation_speed(rs=np.abs(rs)) print("set rotation speed: {} deg/sec".format(rs)) @@ -1893,17 +1932,18 @@ def fly_inner_scan(): if not noDark: print("\nshutter closed, taking dark images...") yield from _take_dark_image_xhx( - dets, motor, num=1, chunk_size=20, stream_name="dark", simu=simu + dets, motor, num=1, chunk_size=20, stream_name="dark", simu=simu, + cam=cam ) # open shutter, tomo_images - true_period = yield from rd(Andor.cam.acquire_period) + true_period = yield from rd(cam.cam.acquire_period) rot_time = np.abs(rel_rot_ang) / np.abs(rs) num_img = int(rot_time / true_period) + 2 yield from _open_shutter_xhx(simu=simu) print("\nshutter opened, taking tomo images...") - yield from _set_Andor_chunk_size(dets, chunk_size=num_img) + yield from _set_Andor_chunk_size_xhx(dets, num_img, cam) # yield from mv(zps.pi_r, cur_rot_ang + taxi_ang) status = yield from abs_set(zps.pi_r, tgt_rot_ang, wait=False) # yield from bps.sleep(1) @@ -1930,7 +1970,8 @@ def fly_inner_scan(): rot_first_flag=rot_first_flag, stream_name="flat", simu=simu, - enable_z=enable_z + enable_z=enable_z, + cam=cam ) if not noDark: @@ -1947,7 +1988,7 @@ def fly_inner_scan(): enable_z=enable_z ) uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(cam.cam.image_mode, 1) yield from select_filters([]) print("scan finished") return uid @@ -2198,13 +2239,12 @@ def mosaic_fly_scan_xh( relative_move_flag=True, simu=False, note="", - enable_z=True + enable_z=True, + repeat=1, + sleep=0 ): - if binning is None: - binning = 0 - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) + yield from select_filters(flts) + binning = yield from _bin_cam(binning) if x_ini is None: x_ini = zps.sx.position @@ -2222,28 +2262,33 @@ def mosaic_fly_scan_xh( txt3 = "\n###############################################" txt = txt1 + txt2 + txt3 print(txt) - yield from select_filters(flts) + yield from bps.sleep(1) - for y in y_list: - for z in z_list: - for x in x_list: - yield from mv(zps.sx, x, zps.sy, y, zps.sz, z) - yield from fly_scan2( - exposure_time=exposure_time, - start_angle=start_angle, - rel_rot_ang=rel_rot_ang, - period=period, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - rs=rs, - relative_move_flag=relative_move_flag, - note=note, - simu=simu, - rot_first_flag=True, - enable_z=enable_z - ) + + for ii in range(int(repeat)): + for y in y_list: + for z in z_list: + for x in x_list: + yield from mv(zps.sx, x, zps.sy, y, zps.sz, z) + yield from fly_scan2( + exposure_time=exposure_time, + start_angle=start_angle, + rel_rot_ang=rel_rot_ang, + period=period, + out_x=out_x, + out_y=out_y, + out_z=out_z, + out_r=out_r, + rs=rs, + relative_move_flag=relative_move_flag, + note=f'{note}; pos_y: {y}, pos_z: {z}, pos_x: {x}; iteration: {ii}', + simu=simu, + rot_first_flag=True, + enable_z=enable_z + ) + if ii < int(repeat) - 1: + print(f'sleeping for {sleep} seconds before iteration #{ii+1}') + yield from bps.sleep(sleep) yield from mv(zps.sx, x_ini, zps.sy, y_ini, zps.sz, z_ini, zps.pi_r, r_ini) yield from select_filters([]) @@ -3202,6 +3247,12 @@ def cal_ccd_zp_xh(eng, mag, zp_cfg=None): INPUT: eng: X-ray energy in keV mag: zone plate magnification + OUTPUTS: + p: sample-zp distance in mm + det_pos: detector-sample distance in mm + wl: X-ray wavelength + na: numerical aperture + f: zp focal length in mm """ if zp_cfg is None: zp_cfg={'D': 244, 'dr': 30} # 'D': zp diameter in um; 'dr': outmost zone width in nm diff --git a/startup/99-umacro.py b/startup/99-umacro.py index 1fd345a..f3e58fd 100644 --- a/startup/99-umacro.py +++ b/startup/99-umacro.py @@ -535,8 +535,8 @@ def test_Andor_stage_unstage(n=500): def Read_timestampRecord(return_flag=0): fsave_root = '/tmp/Andor_test' - fn_ts = fsave_root + '/ts.txt' - fn_sid = fsave_root + '/scan_id_list.txt' + fn_ts = fsave_root + '/ts_20230308.txt' + fn_sid = fsave_root + '/scan_id_list_20230308.txt' ts = np.loadtxt(fn_ts) sid = np.loadtxt(fn_sid) ts_stage = ts[::2] From 766a1ffc3c0da68ace6cfdbfd593b03a805b531e Mon Sep 17 00:00:00 2001 From: FXI Operator Date: Wed, 31 May 2023 11:43:14 -0400 Subject: [PATCH 2/9] Replace acquire put with trigger --- startup/18-zebra.py | 7 ++++--- startup/46-zebra_flyer.py | 7 ++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/startup/18-zebra.py b/startup/18-zebra.py index 44fef06..60dffd5 100644 --- a/startup/18-zebra.py +++ b/startup/18-zebra.py @@ -650,7 +650,7 @@ def complete(self): up to the caller to ensure that the motion is actually complete. """ - amk_debug_flag = False + print("\nin complet: complete starts") # Our acquisition complete PV is: XF:05IDD-ES:1{Dev:Zebra1}:ARRAY_ACQ t0 = ttime.monotonic() @@ -727,6 +727,7 @@ def get_zebra_data(): {k: v["timestamp"] for k, v in reading.items()} ) print(f"\nin complete: {type(self._last_bulk)=}\n{type(self._document_cache)=}\n") + print(f"\nin complete: {(self._last_bulk)=}\n{(self._document_cache)=}\n") return NullStatus() @@ -769,7 +770,7 @@ def describe_collect(self): def collect(self): # Create records in the FileStore database. - # move this to stage because I thinkt hat describe_collect needs the + # move this to stage because I think that describe_collect needs the # resource id # TODO use ophyd.areadectector.filestoer_mixins.resllource_factory here if self._last_bulk is None: @@ -1110,7 +1111,7 @@ def set_cam_step_for_scan(det, scn_cfg): FXITomoFlyer.set_wait(det.hdf5.num_capture, scn_cfg["num_images"]) FXITomoFlyer.set_wait(det.cam.num_images, scn_cfg["num_images"]) # FXITomoFlyer.set_wait(det.cam.acquire, 1, wait=0.2) - yield from abs_set(det.cam.acquire, 1, wait=True) + # yield from abs_set(det.cam.acquire, 1, wait=True) @staticmethod def set_mot_r_step_for_scan(scn_cfg): diff --git a/startup/46-zebra_flyer.py b/startup/46-zebra_flyer.py index fdfd043..334555b 100644 --- a/startup/46-zebra_flyer.py +++ b/startup/46-zebra_flyer.py @@ -1,4 +1,5 @@ from bluesky.plan_stubs import kickoff, collect, complete +from bluesky.utils import short_uid def tomo_zfly( @@ -115,9 +116,13 @@ def inner_fly_plan(): st.wait(timeout=10) yield from abs_set(flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True) - yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + det_stream = short_uid('dets') + for d in flyer.detectors: + yield from bps.trigger(d, group=det_stream) + yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) t0 = ttime.monotonic() yield from abs_set( From ef626008896dbd0d275bea5db21627b91583b1e0 Mon Sep 17 00:00:00 2001 From: FXI Operator Date: Sat, 10 Jun 2023 11:16:13 -0400 Subject: [PATCH 3/9] tomo_zfly working version --- startup/.42-scans_legacy.py.swx | 0 startup/18-zebra.py | 439 ++++++----- startup/18-zebra.py~ | 1157 +++++++++++++++++++++++++++ startup/44-scans_other.py | 3 +- startup/46-zebra_flyer.py | 480 ++++++++++- startup/46-zebra_flyer.py~ | 210 +++++ startup/98-user_scan.py | 1317 ++++++++++++++++++++++--------- 7 files changed, 3015 insertions(+), 591 deletions(-) create mode 100644 startup/.42-scans_legacy.py.swx create mode 100644 startup/18-zebra.py~ create mode 100644 startup/46-zebra_flyer.py~ diff --git a/startup/.42-scans_legacy.py.swx b/startup/.42-scans_legacy.py.swx new file mode 100644 index 0000000..e69de29 diff --git a/startup/18-zebra.py b/startup/18-zebra.py index 60dffd5..7cdefd8 100644 --- a/startup/18-zebra.py +++ b/startup/18-zebra.py @@ -1,5 +1,4 @@ import os -import threading import h5py import datetime import numpy as np @@ -8,12 +7,8 @@ from ophyd import Device, EpicsSignal, EpicsSignalRO from ophyd import Component as Cpt from ophyd import FormattedComponent as FC -from ophyd.areadetector.filestore_mixins import ( - new_short_uid, - resource_factory, - FileStorePluginBase, - FileStoreHDF5, -) +from ophyd.utils import set_and_wait +from ophyd.areadetector.filestore_mixins import new_short_uid, resource_factory from ophyd.sim import NullStatus from databroker.assets.handlers import HandlerBase from nslsii.detectors.zebra import Zebra, EpicsSignalWithRBV @@ -29,7 +24,8 @@ # will move this definition to motors.py ROT_BACK_VEL = 30 -BIN_FACS = {"Andor": {0: 1, 1: 2, 2: 3, 3: 4, 4:8}, "Marana": {}, "Oryx": {}} +BIN_FACS = {"Andor": {0: 1, 1: 2, 2: 3, 3: 4, 4: 8}, "Marana": {}, "Oryx": {}} + class ZebraPositionCaptureData(Device): @@ -273,6 +269,7 @@ class FXITomoFlyer(Device): This is the flyer object for the Zebra. This is the position based flyer. """ + root_path = "/nsls2/data/fxi-new/legacy/" write_path_template = f"zebra/%Y/%m/%d/" read_path_template = f"zebra/%Y/%m/%d/" @@ -288,12 +285,12 @@ class FXITomoFlyer(Device): 2: "snaked: single file", # back-forth rocking scan being saved into a single file } - dft_pulse_wid = {'ms': 0.002, 's': 0.0005, '10s': 0.003} # 0: ms # 1: s # 2: 10s - pc_trig_dir = {1: 0, -1: 1} # 1: positive, -1: negative + dft_pulse_wid = {"ms": 0.002, "s": 0.0005, "10s": 0.003} # 0: ms # 1: s # 2: 10s + pc_trig_dir = {1: 0, -1: 1} # 1: positive, -1: negative scan_cfg = {} pc_cfg = {} _staging_delay = 0.010 - tspre = 's' ## ['ms', 's', '10s'] + tspre = "s" ## ['ms', 's', '10s'] def __init__(self, dets, zebra, *, reg=db.reg, scn_mode=0, **kwargs): super().__init__("", parent=None, **kwargs) @@ -301,9 +298,11 @@ def __init__(self, dets, zebra, *, reg=db.reg, scn_mode=0, **kwargs): self._dets = dets self._filestore_resource = None self._encoder = zebra - self._document_cache = [] # self._document_cache defines resource and datum documents + self._document_cache = ( + [] + ) # self._document_cache defines resource and datum documents self._stage_sigs = {} - self._last_bulk = None # self._last_bulk defines event document + self._last_bulk = None # self._last_bulk defines event document self.reg = reg self.scn_mode = self.scn_modes[scn_mode] @@ -311,72 +310,74 @@ def __init__(self, dets, zebra, *, reg=db.reg, scn_mode=0, **kwargs): self.shutter_delay = 0.1 # unit: deg; _shutter_delay/rot_vel > unibliz shutter opening time 1.5ms self.use_shutter = True - FXITomoFlyer.set_wait(self._encoder.pc.block_state_reset, 1) + set_and_wait(self._encoder.pc.block_state_reset, 1) ############### Zebra Setup ############### ## PC Tab - FXITomoFlyer.set_wait(self._encoder.pc.data.cap_enc1_bool, 1) - FXITomoFlyer.set_wait(self._encoder.pc.data.cap_enc2_bool, 0) - FXITomoFlyer.set_wait(self._encoder.pc.data.cap_enc3_bool, 0) - FXITomoFlyer.set_wait(self._encoder.pc.data.cap_enc4_bool, 0) - FXITomoFlyer.set_wait(self._encoder.pc.data.cap_filt1_bool, 0) - FXITomoFlyer.set_wait(self._encoder.pc.data.cap_filt2_bool, 0) - FXITomoFlyer.set_wait(self._encoder.pc.data.cap_div1_bool, 0) - FXITomoFlyer.set_wait(self._encoder.pc.data.cap_div2_bool, 0) - FXITomoFlyer.set_wait(self._encoder.pc.data.cap_div3_bool, 0) - FXITomoFlyer.set_wait(self._encoder.pc.data.cap_div4_bool, 0) - - FXITomoFlyer.set_wait(self._encoder.pc.enc, 0) # 0: Enc1, 1: Enc2, 2: Enc3, 3: Enc4, - FXITomoFlyer.set_wait(self._encoder.pc.dir, 0) # 0: Positive, 1: Negative - FXITomoFlyer.set_wait(self._encoder.pc.tspre, 1) # 0: ms, 1: s, 2: 10s + set_and_wait(self._encoder.pc.data.cap_enc1_bool, 1) + set_and_wait(self._encoder.pc.data.cap_enc2_bool, 0) + set_and_wait(self._encoder.pc.data.cap_enc3_bool, 0) + set_and_wait(self._encoder.pc.data.cap_enc4_bool, 0) + set_and_wait(self._encoder.pc.data.cap_filt1_bool, 0) + set_and_wait(self._encoder.pc.data.cap_filt2_bool, 0) + set_and_wait(self._encoder.pc.data.cap_div1_bool, 0) + set_and_wait(self._encoder.pc.data.cap_div2_bool, 0) + set_and_wait(self._encoder.pc.data.cap_div3_bool, 0) + set_and_wait(self._encoder.pc.data.cap_div4_bool, 0) + + set_and_wait(self._encoder.pc.enc, 0) # 0: Enc1, 1: Enc2, 2: Enc3, 3: Enc4, + set_and_wait(self._encoder.pc.dir, 0) # 0: Positive, 1: Negative + set_and_wait(self._encoder.pc.tspre, 1) # 0: ms, 1: s, 2: 10s ## AND tab -- can be used for external triggering - FXITomoFlyer.set_wait(self._encoder.and1.use1, 0) # 0: No, 1: Yes - FXITomoFlyer.set_wait(self._encoder.and1.use2, 0) - FXITomoFlyer.set_wait(self._encoder.and1.use3, 0) - FXITomoFlyer.set_wait(self._encoder.and1.use4, 0) - FXITomoFlyer.set_wait(self._encoder.and1.input_source1, 0) - FXITomoFlyer.set_wait(self._encoder.and1.input_source2, 0) - FXITomoFlyer.set_wait(self._encoder.and1.input_source3, 0) - FXITomoFlyer.set_wait(self._encoder.and1.input_source4, 0) - FXITomoFlyer.set_wait(self._encoder.and1.invert1, 0) # 0: No, 1: Yes - FXITomoFlyer.set_wait(self._encoder.and1.invert2, 0) - FXITomoFlyer.set_wait(self._encoder.and1.invert3, 0) - FXITomoFlyer.set_wait(self._encoder.and1.invert4, 0) + set_and_wait(self._encoder.and1.use1, 0) # 0: No, 1: Yes + set_and_wait(self._encoder.and1.use2, 0) + set_and_wait(self._encoder.and1.use3, 0) + set_and_wait(self._encoder.and1.use4, 0) + set_and_wait(self._encoder.and1.input_source1, 0) + set_and_wait(self._encoder.and1.input_source2, 0) + set_and_wait(self._encoder.and1.input_source3, 0) + set_and_wait(self._encoder.and1.input_source4, 0) + set_and_wait(self._encoder.and1.invert1, 0) # 0: No, 1: Yes + set_and_wait(self._encoder.and1.invert2, 0) + set_and_wait(self._encoder.and1.invert3, 0) + set_and_wait(self._encoder.and1.invert4, 0) ## OR Tab -- can be used for diagnose - FXITomoFlyer.set_wait(self._encoder.or1.use1, 0) # 0: No, 1: Yes - FXITomoFlyer.set_wait(self._encoder.or1.use2, 0) - FXITomoFlyer.set_wait(self._encoder.or1.use3, 0) - FXITomoFlyer.set_wait(self._encoder.or1.use4, 0) - FXITomoFlyer.set_wait(self._encoder.or1.input_source1, 0) - FXITomoFlyer.set_wait(self._encoder.or1.input_source2, 0) - FXITomoFlyer.set_wait(self._encoder.or1.input_source3, 0) - FXITomoFlyer.set_wait(self._encoder.or1.input_source4, 0) - FXITomoFlyer.set_wait(self._encoder.or1.invert1, 0) # 0 = No, 1 = Yes - FXITomoFlyer.set_wait(self._encoder.or1.invert2, 0) - FXITomoFlyer.set_wait(self._encoder.or1.invert3, 0) - FXITomoFlyer.set_wait(self._encoder.or1.invert4, 0) + set_and_wait(self._encoder.or1.use1, 0) # 0: No, 1: Yes + set_and_wait(self._encoder.or1.use2, 0) + set_and_wait(self._encoder.or1.use3, 0) + set_and_wait(self._encoder.or1.use4, 0) + set_and_wait(self._encoder.or1.input_source1, 0) + set_and_wait(self._encoder.or1.input_source2, 0) + set_and_wait(self._encoder.or1.input_source3, 0) + set_and_wait(self._encoder.or1.input_source4, 0) + set_and_wait(self._encoder.or1.invert1, 0) # 0 = No, 1 = Yes + set_and_wait(self._encoder.or1.invert2, 0) + set_and_wait(self._encoder.or1.invert3, 0) + set_and_wait(self._encoder.or1.invert4, 0) ## PULSE tab -- set for fast shutter - FXITomoFlyer.set_wait(self._encoder.pulse1.input_addr, 31) - FXITomoFlyer.set_wait(self._encoder.pulse1.input_edge, 0) # 0 = rising, 1 = falling - FXITomoFlyer.set_wait(self._encoder.pulse1.delay, 0) - FXITomoFlyer.set_wait(self._encoder.pulse2.input_addr, 31) - FXITomoFlyer.set_wait(self._encoder.pulse2.input_edge, 1) # 0 = rising, 1 = falling - FXITomoFlyer.set_wait(self._encoder.pulse2.delay, 0) + set_and_wait(self._encoder.pulse1.input_addr, 31) + set_and_wait(self._encoder.pulse1.input_edge, 0) # 0 = rising, 1 = falling + set_and_wait(self._encoder.pulse1.delay, 0) + set_and_wait(self._encoder.pulse2.input_addr, 31) + set_and_wait(self._encoder.pulse2.input_edge, 1) # 0 = rising, 1 = falling + set_and_wait(self._encoder.pulse2.delay, 0) ## ENC tab - FXITomoFlyer.set_wait(self._encoder.pc.enc_pos1_sync, 1) - FXITomoFlyer.set_wait(self._encoder.pc.enc_pos2_sync, 0) - FXITomoFlyer.set_wait(self._encoder.pc.enc_pos3_sync, 0) - FXITomoFlyer.set_wait(self._encoder.pc.enc_pos4_sync, 0) + set_and_wait(self._encoder.pc.enc_pos1_sync, 1) + set_and_wait(self._encoder.pc.enc_pos2_sync, 0) + set_and_wait(self._encoder.pc.enc_pos3_sync, 0) + set_and_wait(self._encoder.pc.enc_pos4_sync, 0) ## SYS tab - FXITomoFlyer.set_wait(self._encoder.output1.ttl.addr, 53) # PC_PULSE --> TTL1 --> Camera - FXITomoFlyer.set_wait(self._encoder.output2.ttl.addr, 52) # PC_PULSE --> TTL2 --> fast shutter - FXITomoFlyer.set_wait(self._encoder.output3.ttl.addr, 0) - FXITomoFlyer.set_wait(self._encoder.output4.ttl.addr, 0) + set_and_wait(self._encoder.output1.ttl.addr, 53) # PC_PULSE --> TTL1 --> Camera + set_and_wait( + self._encoder.output2.ttl.addr, 52 + ) # PC_PULSE --> TTL2 --> fast shutter + set_and_wait(self._encoder.output3.ttl.addr, 0) + set_and_wait(self._encoder.output4.ttl.addr, 0) @property def encoder(self): @@ -399,58 +400,76 @@ def detectors(self, value): def preset_zebra(self, pc_cfg={}): ############### PC Arm - FXITomoFlyer.set_wait(self._encoder.pc.trig_source, 0, wait=0.01) # 0 = Soft, 1 = External + yield from abs_set( + self._encoder.pc.trig_source, 0, wait=True + ) # 0 = Soft, 1 = External ############### PC Pulse # yield from abs_set(self._encoder.pc.pulse_width, self.dft_pulse_wid[self.tspre], wait=True) ############### PULSE -- set unibliz trigger to 'external exposure' if self.use_shutter: - FXITomoFlyer.set_wait(self._encoder.pulse1.time_units, self.tspre) - self._encoder.pulse1.width.put(self.dft_pulse_wid[self.tspre], timeout=10) - ttime.sleep(0.01) - FXITomoFlyer.set_wait(self._encoder.pulse2.time_units, self.tspre) - self._encoder.pulse2.width.put(self.dft_pulse_wid[self.tspre], timeout=10) - ttime.sleep(0.01) - FXITomoFlyer.set_wait(self._encoder.output2.ttl.addr, 52) + yield from abs_set(self._encoder.pulse1.time_units, self.tspre, wait=True) + yield from abs_set( + self._encoder.pulse1.width, self.dft_pulse_wid[self.tspre], wait=True + ) + yield from abs_set(self._encoder.pulse2.time_units, self.tspre, wait=True) + yield from abs_set( + self._encoder.pulse2.width, self.dft_pulse_wid[self.tspre], wait=True + ) + yield from abs_set(self._encoder.output2.ttl.addr, 52, wait=True) else: - FXITomoFlyer.set_wait(self._encoder.output2.ttl.addr, 29) + yield from abs_set(self._encoder.output2.ttl.addr, 29, wait=True) if self.scn_mode == "standard": ############### PC Tab ############### ## PC Gate - FXITomoFlyer.set_wait(self._encoder.pc.gate_source, 0, wait=0.01) # 0 = Position, 1 = Time, 2 = External - FXITomoFlyer.set_wait(self._encoder.pc.gate_step, 0) - FXITomoFlyer.set_wait(self._encoder.pc.gate_num, 1) + yield from abs_set( + self._encoder.pc.gate_source, 0, wait=True + ) # 0 = Position, 1 = Time, 2 = External + yield from abs_set(self._encoder.pc.gate_step, 0, wait=True) + yield from abs_set(self._encoder.pc.gate_num, 1, wait=True) ## PC Pulse - FXITomoFlyer.set_wait(self._encoder.pc.pulse_source, 0, wait=0.01) # 0 = Position, 1 = Time, 2 = External - self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) - ttime.sleep(0.01) + yield from abs_set( + self._encoder.pc.pulse_source, 0, wait=True + ) # 0 = Position, 1 = Time, 2 = External + yield from abs_set( + self._encoder.pc.pulse_width, self.dft_pulse_wid[self.tspre], wait=True + ) elif self.scn_mode == "snaked: multiple files": ############### PC Tab ############### ## PC Gate - FXITomoFlyer.set_wait(self._encoder.pc.gate_source, 0, wait=0.01) # 0 = Position, 1 = Time, 2 = External - FXITomoFlyer.set_wait(self._encoder.pc.gate_step, 0) - FXITomoFlyer.set_wait(self._encoder.pc.gate_num, 1) + yield from abs_set( + self._encoder.pc.gate_source, 0, wait=True + ) # 0 = Position, 1 = Time, 2 = External + yield from abs_set(self._encoder.pc.gate_step, 0, wait=True) + yield from abs_set(self._encoder.pc.gate_num, 1, wait=True) ## PC Pulse - FXITomoFlyer.set_wait(self._encoder.pc.pulse_source, 0, wait=0.01) # 0 = Position, 1 = Time, 2 = External - self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) - ttime.sleep(0.01) + yield from abs_set( + self._encoder.pc.pulse_source, 0, wait=True + ) # 0 = Position, 1 = Time, 2 = External + set_and_wait( + self._encoder.pc.pulse_width, self.dft_pulse_wid[self.tspre], rtol=0.1 + ) elif self.scn_mode == "snaked: single file": ############### PC Tab ############### ## PC Gate - FXITomoFlyer.set_wait(self._encoder.pc.gate_source, 2, wait=0.01) # 0 = Position, 1 = Time, 2 = External - FXITomoFlyer.set_wait(self._encoder.pc.gate_ext, 29) - FXITomoFlyer.set_wait(self._encoder.pc.gate_num, 0) + yield from abs_set( + self._encoder.pc.gate_source, 2, wait=True + ) # 0 = Position, 1 = Time, 2 = External + yield from abs_set(self._encoder.pc.gate_ext, 29, wait=True) + yield from abs_set(self._encoder.pc.gate_num, 0, wait=True) ## PC Pulse - FXITomoFlyer.set_wait(self._encoder.pc.pulse_source, 1, wait=0.01) # 0 = Position, 1 = Time, 2 = External - self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) - ttime.sleep(0.01) + yield from abs_set( + self._encoder.pc.pulse_source, 1, wait=True + ) # 0 = Position, 1 = Time, 2 = External + set_and_wait( + self._encoder.pc.pulse_width, self.dft_pulse_wid[self.tspre], rtol=0.1 + ) for key, val in pc_cfg[self.scn_mode].items(): - getattr(self._encoder.pc, key).put(val, timeout=10) - ttime.sleep(0.01) + set_and_wait(getattr(self._encoder.pc, key), val, rtol=0.1) def make_filename(self): """Make a filename. @@ -683,8 +702,8 @@ def complete(self): path_semantics="posix", ) - time_datum = datum_factory_z({"column": "time"}) - enc1_datum = datum_factory_z({"column": "enc1"}) + time_datum = datum_factory_z({"column": "zebra_time"}) + enc1_datum = datum_factory_z({"column": "enc1_pi_r"}) # self._document_cache defines resource and datum documents self._document_cache = [("resource", self.__filestore_resource)] @@ -704,6 +723,7 @@ def complete(self): # @timer_wrapper def get_zebra_data(): export_zebra_data(self._encoder, self.__write_filepath) + get_zebra_data() # Yield a (partial) Event document. The RunEngine will put this @@ -712,49 +732,50 @@ def get_zebra_data(): "time": ttime.time(), "seq_num": 1, "data": { - "time": time_datum["datum_id"], - "enc1": enc1_datum["datum_id"], + "zebra_time": time_datum["datum_id"], + "enc1_pi_r": enc1_datum["datum_id"], }, "timestamps": { - "time": time_datum["datum_id"], # not a typo# - "enc1": time_datum["datum_id"], + "zebra_time": time_datum["datum_id"], # not a typo# + "enc1_pi_r": time_datum["datum_id"], }, } + for d in self._dets: reading = d.read() self._last_bulk["data"].update({k: v["value"] for k, v in reading.items()}) self._last_bulk["timestamps"].update( {k: v["timestamp"] for k, v in reading.items()} ) - print(f"\nin complete: {type(self._last_bulk)=}\n{type(self._document_cache)=}\n") + print( + f"\nin complete: {type(self._last_bulk)=}\n{type(self._document_cache)=}\n" + ) print(f"\nin complete: {(self._last_bulk)=}\n{(self._document_cache)=}\n") return NullStatus() def describe_collect(self): ext_spec = "FileStore:" + num_zebra_data = self.encoder.pc.data.time.get().shape[0] spec = { "external": ext_spec, "dtype": "array", - "shape": [self.encoder.pc.data.time.get().shape[0]], + "shape": [num_zebra_data], "source": "", # make this the PV of the array the det is writing } - desc = OrderedDict() # desc defines data_keys - for chan in ( - "time", - "enc1", - ): - desc[chan] = spec - desc[chan]["source"] = getattr(self._encoder.pc.data, chan).pvname + desc = OrderedDict() # desc defines data_keys + + desc["zebra_time"] = spec + desc["zebra_time"]["source"] = getattr(self._encoder.pc.data, "enc1").pvname + desc["enc1_pi_r"] = spec + desc["enc1_pi_r"]["source"] = getattr(self._encoder.pc.data, "enc1").pvname # Handle the detectors we are going to get - for d in self._dets: + for d in self.detectors: desc.update(d.describe()) - for chan in d.describe().keys(): - desc[chan]['shape'] = [self.encoder.pc.data.time.get().shape[0]] - print(f"\nin describe_collect {desc=}\n") + # print(f"\nin describe_collect {desc=}\n") # # Handle the ion chamber that the zebra is collecting # desc["i0"] = spec @@ -786,7 +807,6 @@ def collect(self): self._state = "idle" def collect_asset_docs(self): - # print(f"\nin collect_asset_docs {self._document_cache=}\n") yield from iter(list(self._document_cache)) self._document_cache.clear() @@ -815,20 +835,23 @@ def preset_flyer(self, scn_cfg): yield from FXITomoFlyer.set_cam_mode(self.detectors[0], stage="pre-scan") scn_cfg = FXITomoFlyer.cal_cam_rot_params(self.detectors[0], scn_cfg) pc_cfg = FXITomoFlyer.cal_zebra_pc_params(scn_cfg) - self.preset_zebra(pc_cfg) + yield from self.preset_zebra(pc_cfg) print("preset_flyer is done") return scn_cfg, pc_cfg - def set_pc_step_for_scan(self, scn_cfg, pc_cfg): - yield from abs_set(self.encoder.pc.dir, pc_cfg[self.scn_modes[scn_cfg["scn_mode"]]]["dir"], wait=True) - yield from abs_set(self.encoder.pc.gate_start, pc_cfg[self.scn_modes[scn_cfg["scn_mode"]]]["gate_start"], wait=True) + def set_pc_step_for_scan(self, scn_mode, pc_cfg): + yield from abs_set(self.encoder.pc.dir, pc_cfg[scn_mode]["dir"], wait=True) + yield from abs_set( + self.encoder.pc.gate_start, pc_cfg[scn_mode]["gate_start"], wait=True + ) - @staticmethod - def set_wait(field, val, wait=0.01): - while field.get() != val: - field.put(val, timeout=10) - ttime.sleep(0.01) - ttime.sleep(wait) + # @staticmethod + # def set_wait(field, val, wait=0.01): + # set_and_wait(field, val, poll_time=wait) + # # while field.get() != val: + # # field.put(val, timeout=10) + # # ttime.sleep(0.01) + # # ttime.sleep(wait) @classmethod def cal_cam_rot_params(cls, det, scn_cfg): @@ -867,13 +890,6 @@ def cal_cam_rot_params(cls, det, scn_cfg): if scn_cfg["exp_t"] > acq_p - 0.002: scn_cfg["exp_t"] = acq_p - 0.002 - # det.cam.acquire_period.put(scan_cfg["acq_p"]) - # det.cam.acquire_time.put(scan_cfg["exp_t"]) - # det.cam.image_mode.put(0) # 0: 'Fixed', 1: 'Continuous' - # det.cam.trigger_mode.put( - # 4 - # ) # 0: 'Internal', 1: 'External Start', 2: 'External Exposure', 3: 'Software', 4: 'External' - ############### calculate rotary stage parameters ############### if scn_cfg["tacc"] <= 0: print("Acceleration time cannot be smaller than 0. Reset it to 1 second.") @@ -893,31 +909,53 @@ def cal_cam_rot_params(cls, det, scn_cfg): taxi_dist = np.ceil( (scn_cfg["vel"] - cls.rot_axis.base_velo.get()) * scn_cfg["tacc"] / 2 ) - if not (cls.rot_axis.low_limit_switch.get() == 0 and cls.rot_axis.high_limit_switch.get() == 0): + if not ( + cls.rot_axis.low_limit_switch.get() == 0 + and cls.rot_axis.high_limit_switch.get() == 0 + ): if (scn_cfg["ang_s"] - taxi_dist) < cls.rot_axis.low_limit.get(): - print("Rotation range is beyond the low limit of the rotary stage. Quit!") + print( + "Rotation range is beyond the low limit of the rotary stage. Quit!" + ) return None elif (scn_cfg["ang_s"] - taxi_dist) > cls.rot_axis.high_limit.get(): - print("Rotation range is beyond the high limit of the rotary stage. Quit!") + print( + "Rotation range is beyond the high limit of the rotary stage. Quit!" + ) return None if (scn_cfg["ang_e"] + taxi_dist) < cls.rot_axis.low_limit.get(): - print("Rotation range is beyond the low limit of the rotary stage. Quit!") + print( + "Rotation range is beyond the low limit of the rotary stage. Quit!" + ) return None elif (scn_cfg["ang_e"] + taxi_dist) > cls.rot_axis.high_limit.get(): - print("Rotation range is beyond the high limit of the rotary stage. Quit!") + print( + "Rotation range is beyond the high limit of the rotary stage. Quit!" + ) return None scn_cfg["taxi_dist"] = taxi_dist - scn_cfg["num_images"] = ( - int( - abs( - (scn_cfg["ang_e"] - scn_cfg["ang_s"]) / scn_cfg["vel"] / scn_cfg["acq_p"] + scn_cfg["ang_step"] = scn_cfg["vel"] * scn_cfg["acq_p"] + if cls.scn_modes[scn_cfg["scn_mode"]] == "snaked: single file": + scn_cfg["num_images"] = int( + int( + round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / (scn_cfg["acq_p"] * scn_cfg["vel"]) + + 2 * scn_cfg["tacc"] / scn_cfg["acq_p"] + ) + ) + * scn_cfg["num_swing"] + ) + else: + scn_cfg["num_images"] = int( + round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / round(scn_cfg["ang_step"], 3) + + 1 ) ) - + 1 - ) - scn_cfg["ang_step"] = scn_cfg["vel"] * scn_cfg["acq_p"] if scn_cfg["ang_s"] < scn_cfg["ang_e"]: scn_cfg["rot_dir"] = 1 @@ -962,18 +1000,19 @@ def cal_zebra_pc_params(cls, scn_cfg): "snaked: single file": {}, } if cls.scn_modes[scn_cfg["scn_mode"]] == "standard": - pc_cfg["standard"]["gate_start"] = scn_cfg["ang_s"] - pc_cfg["standard"]["gate_width"] = abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) pc_cfg["standard"]["pulse_start"] = 0 pc_cfg["standard"]["pulse_width"] = ( cls.dft_pulse_wid[cls.tspre] * scn_cfg["vel"] ) pc_cfg["standard"]["pulse_step"] = round(scn_cfg["ang_step"], 3) - pc_cfg["standard"]["pulse_max"] = int(round( - abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) - / pc_cfg["standard"]["pulse_step"] - + 1 - )) + pc_cfg["standard"]["pulse_max"] = int( + round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / pc_cfg["standard"]["pulse_step"] + + 1 + ) + ) + pc_cfg["standard"]["gate_start"] = scn_cfg["ang_s"] pc_cfg["standard"]["gate_width"] = ( abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + pc_cfg["standard"]["pulse_step"] @@ -983,30 +1022,44 @@ def cal_zebra_pc_params(cls, scn_cfg): pc_cfg["snaked: multiple files"]["pulse_width"] = ( cls.dft_pulse_wid[cls.tspre] * scn_cfg["vel"] ) - pc_cfg["snaked: multiple files"]["pulse_step"] = round(scn_cfg["ang_step"], 3) - pc_cfg["snaked: multiple files"]["pulse_max"] = int(round( - abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) - / pc_cfg["snaked: multiple files"]["pulse_step"] - + 1 - )) + pc_cfg["snaked: multiple files"]["pulse_step"] = round( + scn_cfg["ang_step"], 3 + ) + pc_cfg["snaked: multiple files"]["pulse_max"] = int( + round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / pc_cfg["snaked: multiple files"]["pulse_step"] + + 1 + ) + ) + # pc_cfg["snaked: multiple files"]["gate_start"] = scn_cfg["ang_s"] pc_cfg["snaked: multiple files"]["gate_width"] = ( abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + pc_cfg["snaked: multiple files"]["pulse_step"] ) elif cls.scn_modes[scn_cfg["scn_mode"]] == "snaked: single file": + pc_cfg["snaked: single file"]["pulse_start"] = 0 pc_cfg["snaked: single file"]["pulse_start"] = 0 pc_cfg["snaked: single file"]["pulse_width"] = cls.dft_pulse_wid[cls.tspre] pc_cfg["snaked: single file"]["pulse_step"] = round(scn_cfg["acq_p"], 4) - pc_cfg["snaked: single file"]["pulse_max"] = int(int(round( - abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) - / (scn_cfg["acq_p"] * scn_cfg["vel"]) - + 2 * scn_cfg["tacc"] / scn_cfg["acq_p"] - + 1 - )) * scn_cfg["num_swing"]) + pc_cfg["snaked: single file"]["pulse_max"] = int( + int( + round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / (scn_cfg["acq_p"] * scn_cfg["vel"]) + + 2 * scn_cfg["tacc"] / scn_cfg["acq_p"] + ) + ) + * scn_cfg["num_swing"] + ) + # pc_cfg["snaked: single file"]["gate_start"] = scn_cfg["ang_s"] + # pc_cfg["snaked: single file"]["gate_width"] = else: print("Unrecognized scan mode. Quit") return None - pc_cfg[cls.scn_modes[scn_cfg["scn_mode"]]]["dir"] = cls.pc_trig_dir[scn_cfg["rot_dir"]] + pc_cfg[cls.scn_modes[scn_cfg["scn_mode"]]]["dir"] = cls.pc_trig_dir[ + scn_cfg["rot_dir"] + ] return pc_cfg @staticmethod @@ -1076,19 +1129,10 @@ def get_txm_cur_pos(): @staticmethod def init_mot_r(scn_cfg): - # cur_pos = zps.pi_r.position - # if abs(ini_r - cur_pos) > 360: - # cur_pos = ini_r // 360 * 360 + ini_r % 360 - # yield from abs_set(zps.pi_r.motor_calib, 1, wait=True) - # yield from abs_set(zps.pi_r.user_setpoint, cur_pos, wait=True) - # yield from abs_set(zps.pi_r.motor_calib, 0, wait=True) - # yield from abs_set(zps.pi_r.acceleration, 1, wait=True) - # yield from abs_set(zps.pi_r.velocity, 30, wait=True) - # yield from abs_set(zps.pi_r.user_setpoint, ini_r, wait=True) cur_pos = zps.pi_r.position yield from abs_set(zps.pi_r.offset_freeze_switch, 1) - ang_max = max(scn_cfg["ang_s"], scn_cfg["ang_e"]) - + ang_max = max(scn_cfg["ang_s"], scn_cfg["ang_e"]) + if ang_max > 0: yield from abs_set(zps.pi_r.user_offset, np.ceil(ang_max / 360) * 360) @@ -1101,38 +1145,31 @@ def init_mot_r(scn_cfg): @staticmethod def set_cam_step_for_scan(det, scn_cfg): - # yield from abs_set(det.cam.acquire_time, scn_cfg["exp_t"], wait=True) - # yield from abs_set(det.cam.acquire_period, scn_cfg["acq_p"], wait=True) - # yield from abs_set(det.hdf5.num_capture, scn_cfg["num_images"], wait=True) - # yield from abs_set(det.cam.num_images, scn_cfg["num_images"], wait=True) - # yield from abs_set(det.cam.acquire, 1, wait=True) - FXITomoFlyer.set_wait(det.cam.acquire_time, scn_cfg["exp_t"]) - FXITomoFlyer.set_wait(det.cam.acquire_period, scn_cfg["acq_p"]) - FXITomoFlyer.set_wait(det.hdf5.num_capture, scn_cfg["num_images"]) - FXITomoFlyer.set_wait(det.cam.num_images, scn_cfg["num_images"]) - # FXITomoFlyer.set_wait(det.cam.acquire, 1, wait=0.2) - # yield from abs_set(det.cam.acquire, 1, wait=True) - + yield from abs_set(det.cam.acquire_time, scn_cfg["exp_t"], wait=True) + yield from abs_set(det.hdf5.num_capture, scn_cfg["num_images"], wait=True) + yield from abs_set(det.cam.num_images, scn_cfg["num_images"], wait=True) + @staticmethod def set_mot_r_step_for_scan(scn_cfg): - # yield from abs_set(zps.pi_r.acceleration, scn_cfg["tacc"], wait=True) - # yield from abs_set(zps.pi_r.velocity, scn_cfg["vel"], wait=True) - # yield from abs_set(zps.pi_r.user_setpoint, scn_cfg["ang_s"], wait=True) - FXITomoFlyer.set_wait(zps.pi_r.acceleration, scn_cfg["tacc"]) - FXITomoFlyer.set_wait(zps.pi_r.velocity, scn_cfg["vel"]) - FXITomoFlyer.set_wait(zps.pi_r.user_setpoint, scn_cfg["ang_s"] - scn_cfg["rot_dir"] * scn_cfg["taxi_dist"]) + yield from abs_set(zps.pi_r.acceleration, scn_cfg["tacc"], wait=True) + yield from abs_set(zps.pi_r.velocity, scn_cfg["vel"], wait=True) + yield from abs_set( + zps.pi_r.user_setpoint, + scn_cfg["ang_s"] - scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + wait=True, + ) @staticmethod def set_cam_mode(cam, stage="pre-scan"): if stage == "pre-scan": - yield from abs_set(cam.cam.image_mode, 0) - yield from abs_set(cam.cam.trigger_mode, 4) + yield from abs_set(cam.cam.image_mode, 0, wait=True) + yield from abs_set(cam.cam.trigger_mode, 4, wait=True) elif stage == "ref-scan": - yield from abs_set(cam.cam.image_mode, 0) - yield from abs_set(cam.cam.trigger_mode, 0) + yield from abs_set(cam.cam.image_mode, 0, wait=True) + yield from abs_set(cam.cam.trigger_mode, 0, wait=True) elif stage == "post-scan": - yield from abs_set(cam.cam.image_mode, 1) - yield from abs_set(cam.cam.trigger_mode, 0) + yield from abs_set(cam.cam.image_mode, 1, wait=True) + yield from abs_set(cam.cam.trigger_mode, 0, wait=True) Zebra = FXIZebra( @@ -1163,9 +1200,9 @@ def export_zebra_data(zebra, filepath): size = (len(time_d),) with h5py.File(filepath, "w") as f: - dset0 = f.create_dataset("time", size, dtype="f") + dset0 = f.create_dataset("zebra_time", size, dtype="f") dset0[...] = np.array(time_d) - dset1 = f.create_dataset("enc1", size, dtype="f") + dset1 = f.create_dataset("enc1_pi_r", size, dtype="f") dset1[...] = np.array(enc1_d) diff --git a/startup/18-zebra.py~ b/startup/18-zebra.py~ new file mode 100644 index 0000000..d83b117 --- /dev/null +++ b/startup/18-zebra.py~ @@ -0,0 +1,1157 @@ +import os +import h5py +import datetime +import numpy as np +import time as ttime + +from ophyd import Device, EpicsSignal, EpicsSignalRO +from ophyd import Component as Cpt +from ophyd import FormattedComponent as FC +from ophyd.utils import set_and_wait +from ophyd.areadetector.filestore_mixins import ( + new_short_uid, + resource_factory +) +from ophyd.sim import NullStatus +from databroker.assets.handlers import HandlerBase +from nslsii.detectors.zebra import Zebra, EpicsSignalWithRBV +from ophyd.device import Staged, RedundantStaging, OrderedDict +from bluesky.plan_stubs import abs_set + + +# minimum detector acquisition period in second for full frame size +# will move this definition to areaDetector.py +DET_MIN_AP = {"Andor": 0.05, "Marana": 0.01, "Oryx": 0.005} + +# default velocity for rotating rotary stage back to starting position +# will move this definition to motors.py +ROT_BACK_VEL = 30 + +BIN_FACS = {"Andor": {0: 1, 1: 2, 2: 3, 3: 4, 4:8}, "Marana": {}, "Oryx": {}} + +class ZebraPositionCaptureData(Device): + + """ + Data arrays for the Zebra position capture function and their metadata. + + ## Not all variables are needed at FXI - CD + """ + + # Data arrays + div1 = Cpt(EpicsSignal, "PC_DIV1") + div2 = Cpt(EpicsSignal, "PC_DIV2") + div3 = Cpt(EpicsSignal, "PC_DIV3") + div4 = Cpt(EpicsSignal, "PC_DIV4") + enc1 = Cpt(EpicsSignal, "PC_ENC1") + enc2 = Cpt(EpicsSignal, "PC_ENC2") + enc3 = Cpt(EpicsSignal, "PC_ENC3") + enc4 = Cpt(EpicsSignal, "PC_ENC4") + filt1 = Cpt(EpicsSignal, "PC_FILT1") + filt2 = Cpt(EpicsSignal, "PC_FILT2") + filt3 = Cpt(EpicsSignal, "PC_FILT3") + filt4 = Cpt(EpicsSignal, "PC_FILT4") + time = Cpt(EpicsSignal, "PC_TIME") + + # Array sizes + num_cap = Cpt(EpicsSignal, "PC_NUM_CAP") + num_down = Cpt(EpicsSignal, "PC_NUM_DOWN") + + # BOOLs to denote arrays with data + cap_enc1_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B0") + cap_enc2_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B1") + cap_enc3_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B2") + cap_enc4_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B3") + cap_filt1_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B4") + cap_filt2_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B5") + cap_div1_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B6") + cap_div2_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B7") + cap_div3_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B8") + cap_div4_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B9") + + def stage(self): + super().stage() + + def unstage(self): + super().unstage() + + +class ZebraPositionCapture(Device): + + """ + Signals for the position capture function of the Zebra + """ + + # Configuration settings and status PVs + enc = Cpt(EpicsSignalWithRBV, "PC_ENC") + egu = Cpt(EpicsSignalRO, "M1:EGU") + dir = Cpt(EpicsSignalWithRBV, "PC_DIR") + tspre = Cpt(EpicsSignalWithRBV, "PC_TSPRE") + trig_source = Cpt(EpicsSignalWithRBV, "PC_ARM_SEL") + arm = Cpt(EpicsSignal, "PC_ARM") + disarm = Cpt(EpicsSignal, "PC_DISARM") + armed = Cpt(EpicsSignalRO, "PC_ARM_OUT") + gate_source = Cpt(EpicsSignalWithRBV, "PC_GATE_SEL") + gate_start = Cpt(EpicsSignalWithRBV, "PC_GATE_START") + gate_width = Cpt(EpicsSignalWithRBV, "PC_GATE_WID") + gate_step = Cpt(EpicsSignalWithRBV, "PC_GATE_STEP") + gate_num = Cpt(EpicsSignalWithRBV, "PC_GATE_NGATE") + gate_ext = Cpt(EpicsSignalWithRBV, "PC_GATE_INP") + gated = Cpt(EpicsSignalRO, "PC_GATE_OUT") + pulse_source = Cpt(EpicsSignalWithRBV, "PC_PULSE_SEL") + pulse_start = Cpt(EpicsSignalWithRBV, "PC_PULSE_START") + pulse_width = Cpt(EpicsSignalWithRBV, "PC_PULSE_WID") + pulse_step = Cpt(EpicsSignalWithRBV, "PC_PULSE_STEP") + pulse_max = Cpt(EpicsSignalWithRBV, "PC_PULSE_MAX") + pulse = Cpt(EpicsSignalRO, "PC_PULSE_OUT") + enc_pos1_sync = Cpt(EpicsSignal, "M1:SETPOS.PROC") + enc_pos2_sync = Cpt(EpicsSignal, "M2:SETPOS.PROC") + enc_pos3_sync = Cpt(EpicsSignal, "M3:SETPOS.PROC") + enc_pos4_sync = Cpt(EpicsSignal, "M4:SETPOS.PROC") + enc_res1 = Cpt(EpicsSignal, "M1:MRES") + enc_res2 = Cpt(EpicsSignal, "M2:MRES") + enc_res3 = Cpt(EpicsSignal, "M3:MRES") + enc_res4 = Cpt(EpicsSignal, "M4:MRES") + data_in_progress = Cpt(EpicsSignalRO, "ARRAY_ACQ") + block_state_reset = Cpt(EpicsSignal, "SYS_RESET.PROC") + data = Cpt(ZebraPositionCaptureData, "") + + def stage(self): + self.arm.put(1) + super().stage() + + def unstage(self): + self.disarm.put(1) + self.block_state_reset.put(1) + super().unstage() + + +class FXIZebraOR(Device): + # I really appreciate the different indexing for input source + # Thank you for that + + use1 = Cpt(EpicsSignal, "_ENA:B0") + use2 = Cpt(EpicsSignal, "_ENA:B1") + use3 = Cpt(EpicsSignal, "_ENA:B2") + use4 = Cpt(EpicsSignal, "_ENA:B3") + input_source1 = Cpt(EpicsSignal, "_INP1") + input_source2 = Cpt(EpicsSignal, "_INP2") + input_source3 = Cpt(EpicsSignal, "_INP3") + input_source4 = Cpt(EpicsSignal, "_INP4") + invert1 = Cpt(EpicsSignal, "_INV:B0") + invert2 = Cpt(EpicsSignal, "_INV:B1") + invert3 = Cpt(EpicsSignal, "_INV:B2") + invert4 = Cpt(EpicsSignal, "_INV:B3") + + def stage(self): + super().stage() + + def unstage(self): + super().unstage() + + +class ZebraAND(Device): + # I really appreciate the different indexing for input source + # Thank you for that + use1 = Cpt(EpicsSignal, "_ENA:B0") + use2 = Cpt(EpicsSignal, "_ENA:B1") + use3 = Cpt(EpicsSignal, "_ENA:B2") + use4 = Cpt(EpicsSignal, "_ENA:B3") + input_source1 = Cpt(EpicsSignal, "_INP1") + input_source2 = Cpt(EpicsSignal, "_INP2") + input_source3 = Cpt(EpicsSignal, "_INP3") + input_source4 = Cpt(EpicsSignal, "_INP4") + invert1 = Cpt(EpicsSignal, "_INV:B0") + invert2 = Cpt(EpicsSignal, "_INV:B1") + invert3 = Cpt(EpicsSignal, "_INV:B2") + invert4 = Cpt(EpicsSignal, "_INV:B3") + + def stage(self): + super().stage() + + def unstage(self): + super().unstage() + + +class ZebraPulse(Device): + width = Cpt(EpicsSignalWithRBV, "WID") + input_addr = Cpt(EpicsSignalWithRBV, "INP") + input_str = Cpt(EpicsSignalRO, "INP:STR", string=True) + input_status = Cpt(EpicsSignalRO, "INP:STA") + delay = Cpt(EpicsSignalWithRBV, "DLY") + delay_sync = Cpt(EpicsSignal, "DLY:SYNC") + time_units = Cpt(EpicsSignalWithRBV, "PRE", string=True) + output = Cpt(EpicsSignal, "OUT") + + input_edge = FC(EpicsSignal, "{self._zebra_prefix}POLARITY:{self._edge_addr}") + + _edge_addrs = { + 1: "BC", + 2: "BD", + 3: "BE", + 4: "BF", + } + + def __init__( + self, + prefix, + *, + index=None, + parent=None, + configuration_attrs=None, + read_attrs=None, + **kwargs, + ): + if read_attrs is None: + read_attrs = ["input_addr", "input_edge", "delay", "width", "time_units"] + if configuration_attrs is None: + configuration_attrs = [] + + zebra = parent + self.index = index + self._zebra_prefix = zebra.prefix + self._edge_addr = self._edge_addrs[index] + + super().__init__( + prefix, + configuration_attrs=configuration_attrs, + read_attrs=read_attrs, + parent=parent, + **kwargs, + ) + + def stage(self): + super().stage() + + def unstage(self): + super().unstage() + + +class FXIZebra(Zebra): + """ + FXI Zebra device. + """ + + pc = Cpt(ZebraPositionCapture, "") + or1 = Cpt(FXIZebraOR, "OR1") # XF:18ID-ES:1{Dev:Zebra1}:OR1_INV:B0 + or2 = Cpt(FXIZebraOR, "OR2") + or3 = Cpt(FXIZebraOR, "OR3") + or4 = Cpt(FXIZebraOR, "OR4") + and1 = Cpt(ZebraAND, "AND1") # XF:18ID-ES:1{Dev:Zebra2}:AND1_ENA:B0 + and2 = Cpt(ZebraAND, "AND2") + and3 = Cpt(ZebraAND, "AND3") + and4 = Cpt(ZebraAND, "AND4") + pulse1 = Cpt(ZebraPulse, "PULSE1_", index=1) # XF:18ID-ES:1{Dev:Zebra1}:PULSE1_INP + pulse2 = Cpt(ZebraPulse, "PULSE2_", index=2) + pulse3 = Cpt(ZebraPulse, "PULSE3_", index=3) + pulse4 = Cpt(ZebraPulse, "PULSE4_", index=4) + + def stage(self): + super().stage() + + def unstage(self): + super().unstage() + + def __init__(self, prefix, *, read_attrs=None, configuration_attrs=None, **kwargs): + if read_attrs is None: + read_attrs = [] + if configuration_attrs is None: + configuration_attrs = [] + + super().__init__( + prefix, + read_attrs=read_attrs, + configuration_attrs=configuration_attrs, + **kwargs, + ) + + +## Class below requires FXI specific changes +class FXITomoFlyer(Device): + """ + This is the flyer object for the Zebra. + This is the position based flyer. + """ + root_path = "/nsls2/data/fxi-new/legacy/" + write_path_template = f"zebra/%Y/%m/%d/" + read_path_template = f"zebra/%Y/%m/%d/" + reg_root = f"zebra/" + + KNOWN_DETS = {"Andor", "Marana", "Oryx"} + rot_axis = zps.pi_r + # dummy_axis = ophyd.sim.SynAxis(name="TOMO_DUMMY") + + scn_modes = { + 0: "standard", # a single scan in a given angle range + 1: "snaked: multiple files", # back-forth rocking scan with each swing being saved into a file + 2: "snaked: single file", # back-forth rocking scan being saved into a single file + } + + dft_pulse_wid = {'ms': 0.002, 's': 0.0005, '10s': 0.003} # 0: ms # 1: s # 2: 10s + pc_trig_dir = {1: 0, -1: 1} # 1: positive, -1: negative + scan_cfg = {} + pc_cfg = {} + _staging_delay = 0.010 + tspre = 's' ## ['ms', 's', '10s'] + + def __init__(self, dets, zebra, *, reg=db.reg, scn_mode=0, **kwargs): + super().__init__("", parent=None, **kwargs) + self._state = "idle" + self._dets = dets + self._filestore_resource = None + self._encoder = zebra + self._document_cache = [] # self._document_cache defines resource and datum documents + self._stage_sigs = {} + self._last_bulk = None # self._last_bulk defines event document + + self.reg = reg + self.scn_mode = self.scn_modes[scn_mode] + self.extra_stage_sigs = {} + self.shutter_delay = 0.1 # unit: deg; _shutter_delay/rot_vel > unibliz shutter opening time 1.5ms + self.use_shutter = True + + set_and_wait(self._encoder.pc.block_state_reset, 1) + + ############### Zebra Setup ############### + ## PC Tab + set_and_wait(self._encoder.pc.data.cap_enc1_bool, 1) + set_and_wait(self._encoder.pc.data.cap_enc2_bool, 0) + set_and_wait(self._encoder.pc.data.cap_enc3_bool, 0) + set_and_wait(self._encoder.pc.data.cap_enc4_bool, 0) + set_and_wait(self._encoder.pc.data.cap_filt1_bool, 0) + set_and_wait(self._encoder.pc.data.cap_filt2_bool, 0) + set_and_wait(self._encoder.pc.data.cap_div1_bool, 0) + set_and_wait(self._encoder.pc.data.cap_div2_bool, 0) + set_and_wait(self._encoder.pc.data.cap_div3_bool, 0) + set_and_wait(self._encoder.pc.data.cap_div4_bool, 0) + + set_and_wait(self._encoder.pc.enc, 0) # 0: Enc1, 1: Enc2, 2: Enc3, 3: Enc4, + set_and_wait(self._encoder.pc.dir, 0) # 0: Positive, 1: Negative + set_and_wait(self._encoder.pc.tspre, 1) # 0: ms, 1: s, 2: 10s + + ## AND tab -- can be used for external triggering + set_and_wait(self._encoder.and1.use1, 0) # 0: No, 1: Yes + set_and_wait(self._encoder.and1.use2, 0) + set_and_wait(self._encoder.and1.use3, 0) + set_and_wait(self._encoder.and1.use4, 0) + set_and_wait(self._encoder.and1.input_source1, 0) + set_and_wait(self._encoder.and1.input_source2, 0) + set_and_wait(self._encoder.and1.input_source3, 0) + set_and_wait(self._encoder.and1.input_source4, 0) + set_and_wait(self._encoder.and1.invert1, 0) # 0: No, 1: Yes + set_and_wait(self._encoder.and1.invert2, 0) + set_and_wait(self._encoder.and1.invert3, 0) + set_and_wait(self._encoder.and1.invert4, 0) + + ## OR Tab -- can be used for diagnose + set_and_wait(self._encoder.or1.use1, 0) # 0: No, 1: Yes + set_and_wait(self._encoder.or1.use2, 0) + set_and_wait(self._encoder.or1.use3, 0) + set_and_wait(self._encoder.or1.use4, 0) + set_and_wait(self._encoder.or1.input_source1, 0) + set_and_wait(self._encoder.or1.input_source2, 0) + set_and_wait(self._encoder.or1.input_source3, 0) + set_and_wait(self._encoder.or1.input_source4, 0) + set_and_wait(self._encoder.or1.invert1, 0) # 0 = No, 1 = Yes + set_and_wait(self._encoder.or1.invert2, 0) + set_and_wait(self._encoder.or1.invert3, 0) + set_and_wait(self._encoder.or1.invert4, 0) + + ## PULSE tab -- set for fast shutter + set_and_wait(self._encoder.pulse1.input_addr, 31) + set_and_wait(self._encoder.pulse1.input_edge, 0) # 0 = rising, 1 = falling + set_and_wait(self._encoder.pulse1.delay, 0) + set_and_wait(self._encoder.pulse2.input_addr, 31) + set_and_wait(self._encoder.pulse2.input_edge, 1) # 0 = rising, 1 = falling + set_and_wait(self._encoder.pulse2.delay, 0) + + ## ENC tab + set_and_wait(self._encoder.pc.enc_pos1_sync, 1) + set_and_wait(self._encoder.pc.enc_pos2_sync, 0) + set_and_wait(self._encoder.pc.enc_pos3_sync, 0) + set_and_wait(self._encoder.pc.enc_pos4_sync, 0) + + ## SYS tab + set_and_wait(self._encoder.output1.ttl.addr, 53) # PC_PULSE --> TTL1 --> Camera + set_and_wait(self._encoder.output2.ttl.addr, 52) # PC_PULSE --> TTL2 --> fast shutter + set_and_wait(self._encoder.output3.ttl.addr, 0) + set_and_wait(self._encoder.output4.ttl.addr, 0) + + @property + def encoder(self): + return self._encoder + + @property + def detectors(self): + return tuple(self._dets) + + @detectors.setter + def detectors(self, value): + dets = tuple(value) + if not all(d.name in self.KNOWN_DETS for d in dets): + raise ValueError( + f"One or more of {[d.name for d in dets]}" + f"is not known to the zebra. " + f"The known detectors are {self.KNOWN_DETS})" + ) + self._dets = dets + + def preset_zebra(self, pc_cfg={}): + ############### PC Arm + set_and_wait(self._encoder.pc.trig_source, 0) # 0 = Soft, 1 = External + ############### PC Pulse + # yield from abs_set(self._encoder.pc.pulse_width, self.dft_pulse_wid[self.tspre], wait=True) + + ############### PULSE -- set unibliz trigger to 'external exposure' + if self.use_shutter: + set_and_wait(self._encoder.pulse1.time_units, self.tspre) + self._encoder.pulse1.width.put(self.dft_pulse_wid[self.tspre], timeout=10) + ttime.sleep(0.01) + set_and_wait(self._encoder.pulse2.time_units, self.tspre) + self._encoder.pulse2.width.put(self.dft_pulse_wid[self.tspre], timeout=10) + ttime.sleep(0.01) + set_and_wait(self._encoder.output2.ttl.addr, 52) + else: + set_and_wait(self._encoder.output2.ttl.addr, 29) + if self.scn_mode == "standard": + ############### PC Tab ############### + ## PC Gate + set_and_wait(self._encoder.pc.gate_source, 0) # 0 = Position, 1 = Time, 2 = External + set_and_wait(self._encoder.pc.gate_step, 0) + set_and_wait(self._encoder.pc.gate_num, 1) + + ## PC Pulse + set_and_wait(self._encoder.pc.pulse_source, 0) # 0 = Position, 1 = Time, 2 = External + self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) + ttime.sleep(0.01) + elif self.scn_mode == "snaked: multiple files": + ############### PC Tab ############### + ## PC Gate + set_and_wait(self._encoder.pc.gate_source, 0) # 0 = Position, 1 = Time, 2 = External + set_and_wait(self._encoder.pc.gate_step, 0) + set_and_wait(self._encoder.pc.gate_num, 1) + + ## PC Pulse + set_and_wait(self._encoder.pc.pulse_source, 0) # 0 = Position, 1 = Time, 2 = External + self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) + ttime.sleep(0.01) + elif self.scn_mode == "snaked: single file": + ############### PC Tab ############### + ## PC Gate + set_and_wait(self._encoder.pc.gate_source, 2) # 0 = Position, 1 = Time, 2 = External + set_and_wait(self._encoder.pc.gate_ext, 29) + set_and_wait(self._encoder.pc.gate_num, 0) + + ## PC Pulse + set_and_wait(self._encoder.pc.pulse_source, 1) # 0 = Position, 1 = Time, 2 = External + self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) + ttime.sleep(0.01) + + for key, val in pc_cfg[self.scn_mode].items(): + getattr(self._encoder.pc, key).put(val, timeout=10) + ttime.sleep(0.01) + + def make_filename(self): + """Make a filename. + Taken/Modified from ophyd.areadetector.filestore_mixins + This is a hook so that the read and write paths can either be modified + or created on disk prior to configuring the areaDetector plugin. + Returns + ------- + filename : str + The start of the filename + read_path : str + Path that ophyd can read from + write_path : str + Path that the IOC can write to + """ + filename = f"{new_short_uid()}.h5" + formatter = datetime.now().strftime + write_path = formatter(f"{self.root_path}{self.write_path_template}") + read_path = formatter(f"{self.root_path}{self.read_path_template}") + return filename, read_path, write_path + + def stage(self): + # self.set_stage_sigs() + self._stage_with_delay() + super.stage() + + def _stage_with_delay(self): + # Staging taken from https://github.com/bluesky/ophyd/blob/master/ophyd/device.py + # Device - BlueskyInterface + """Stage the device for data collection. + This method is expected to put the device into a state where + repeated calls to :meth:`~BlueskyInterface.trigger` and + :meth:`~BlueskyInterface.read` will 'do the right thing'. + Staging not idempotent and should raise + :obj:`RedundantStaging` if staged twice without an + intermediate :meth:`~BlueskyInterface.unstage`. + This method should be as fast as is feasible as it does not return + a status object. + The return value of this is a list of all of the (sub) devices + stage, including it's self. This is used to ensure devices + are not staged twice by the :obj:`~bluesky.run_engine.RunEngine`. + This is an optional method, if the device does not need + staging behavior it should not implement `stage` (or + `unstage`). + Returns + ------- + devices : list + list including self and all child devices staged + """ + if self._staged == Staged.no: + pass # to short-circuit checking individual cases + elif self._staged == Staged.yes: + raise RedundantStaging( + "Device {!r} is already staged. " "Unstage it first.".format(self) + ) + elif self._staged == Staged.partially: + raise RedundantStaging( + "Device {!r} has been partially staged. " + "Maybe the most recent unstaging " + "encountered an error before finishing. " + "Try unstaging again.".format(self) + ) + self.log.debug("Staging %s", self.name) + self._staged = Staged.partially + + # Resolve any stage_sigs keys given as strings: 'a.b' -> self.a.b + stage_sigs = OrderedDict() + for k, v in self.stage_sigs.items(): + if isinstance(k, str): + # Device.__getattr__ handles nested attr lookup + stage_sigs[getattr(self, k)] = v + else: + stage_sigs[k] = v + + # Read current values, to be restored by unstage() + original_vals = {sig: sig.get() for sig in stage_sigs} + + # We will add signals and values from original_vals to + # self._original_vals one at a time so that + # we can undo our partial work in the event of an error. + + # Apply settings. + devices_staged = [] + try: + for sig, val in stage_sigs.items(): + self.log.debug( + "Setting %s to %r (original value: %r)", + self.name, + val, + original_vals[sig], + ) + sig.set(val, timeout=10).wait() + ttime.sleep(self._staging_delay) + # It worked -- now add it to this list of sigs to unstage. + self._original_vals[sig] = original_vals[sig] + devices_staged.append(self) + + # Call stage() on child devices. + for attr in self._sub_devices: + device = getattr(self, attr) + if hasattr(device, "stage"): + device.stage() + devices_staged.append(device) + except Exception: + self.log.debug( + "An exception was raised while staging %s or " + "one of its children. Attempting to restore " + "original settings before re-raising the " + "exception.", + self.name, + ) + self.unstage() + raise + else: + self._staged = Staged.yes + return devices_staged + + def unstage(self): + self._unstage_with_delay() + + def _unstage_with_delay(self): + # Staging taken from https://github.com/bluesky/ophyd/blob/master/ophyd/device.py + # Device - BlueskyInterface + """Unstage the device. + This method returns the device to the state it was prior to the + last `stage` call. + This method should be as fast as feasible as it does not + return a status object. + This method must be idempotent, multiple calls (without a new + call to 'stage') have no effect. + Returns + ------- + devices : list + list including self and all child devices unstaged + """ + self.log.debug("Unstaging %s", self.name) + self._staged = Staged.partially + devices_unstaged = [] + + # Call unstage() on child devices. + for attr in self._sub_devices[::-1]: + device = getattr(self, attr) + if hasattr(device, "unstage"): + device.unstage() + devices_unstaged.append(device) + + # Restore original values. + for sig, val in reversed(list(self._original_vals.items())): + self.log.debug("Setting %s back to its original value: %r)", self.name, val) + sig.set(val, timeout=10).wait() + ttime.sleep(self._staging_delay) + self._original_vals.pop(sig) + devices_unstaged.append(self) + + self._staged = Staged.no + return devices_unstaged + + def kickoff(self, *, scn_cfg={}): + self._encoder.pc.arm.put(0) + ttime.sleep(self._staging_delay) + self._state = "kicked off" + + if scn_cfg["ang_s"] < scn_cfg["ang_e"]: + self._encoder.pc.dir.put(0) + try: + self.rot_axis.user_setpoint.put(scn_cfg["ang_s"] - scn_cfg["taxi_dist"]) + except Exception as e: + print(e) + print("Cannot move rotary stage to its taxi position.") + return + else: + self._encoder.pc.dir.put(1) + try: + self.rot_axis.user_setpoint.put(scn_cfg["ang_s"] + scn_cfg["taxi_dist"]) + except Exception as e: + print(e) + print("Cannot move rotary stage to its taxi position.") + return + + if scn_cfg["scn_mode"] == "snaked: multiple files": + self._encoder.pc.gate_start.put(scn_cfg["ang_s"]) + + # sync rotary stage encoder + self._encoder.pc.enc_pos1_sync.put(1) + ttime.sleep(self._staging_delay) + + # Do a block reset on the zebra + self._encoder.pc.block_state_reset.put(1) + ttime.sleep(self._staging_delay) + + return NullStatus() + + def complete(self): + """ + Call this when all needed data has been collected. This has no idea + whether that is true, so it will obligingly stop immediately. It is + up to the caller to ensure that the motion is actually complete. + """ + + print("\nin complet: complete starts") + + # Our acquisition complete PV is: XF:05IDD-ES:1{Dev:Zebra1}:ARRAY_ACQ + t0 = ttime.monotonic() + while self._encoder.pc.data_in_progress.get() == 1: + ttime.sleep(self._staging_delay) + if (ttime.monotonic() - t0) > 60: + print(f"{self.name} is behaving badly!") + self._encoder.pc.disarm.put(1) + ttime.sleep(0.100) + if self._encoder.pc.data_in_progress.get() == 1: + raise TimeoutError + + self._state = "complete" + self._encoder.pc.block_state_reset.put(1) + + for d in self._dets: + d.stop() + + # Set filename/path for zebra data + f, rp, wp = self.make_filename() + self.__filename = f + self.__read_filepath = os.path.join(rp, self.__filename) + self.__write_filepath = os.path.join(wp, self.__filename) + + self.__filestore_resource, datum_factory_z = resource_factory( + "ZEBRA_HDF51", + root="/", + resource_path=self.__read_filepath, + resource_kwargs={}, + path_semantics="posix", + ) + + time_datum = datum_factory_z({"column": "zebra_time"}) + enc1_datum = datum_factory_z({"column": "enc1_pi_r"}) + + # self._document_cache defines resource and datum documents + self._document_cache = [("resource", self.__filestore_resource)] + self._document_cache.extend( + ("datum", d) + for d in ( + time_datum, + enc1_datum, + ) + ) + + # grab the asset documents from all of the child detectors + for d in self._dets: + self._document_cache.extend(d.collect_asset_docs()) + + # Write the file. + # @timer_wrapper + def get_zebra_data(): + export_zebra_data(self._encoder, self.__write_filepath) + get_zebra_data() + + # Yield a (partial) Event document. The RunEngine will put this + # into metadatastore, as it does all readings. + self._last_bulk = { + "time": ttime.time(), + "seq_num": 1, + "data": { + "zebra_time": time_datum["datum_id"], + "enc1_pi_r": enc1_datum["datum_id"], + }, + "timestamps": { + "zebra_time": time_datum["datum_id"], # not a typo# + "enc1_pi_r": time_datum["datum_id"], + }, + } + + for d in self._dets: + reading = d.read() + self._last_bulk["data"].update({k: v["value"] for k, v in reading.items()}) + self._last_bulk["timestamps"].update( + {k: v["timestamp"] for k, v in reading.items()} + ) + print(f"\nin complete: {type(self._last_bulk)=}\n{type(self._document_cache)=}\n") + print(f"\nin complete: {(self._last_bulk)=}\n{(self._document_cache)=}\n") + + return NullStatus() + + def describe_collect(self): + ext_spec = "FileStore:" + num_zebra_data = self.encoder.pc.data.time.get().shape[0] + + spec = { + "external": ext_spec, + "dtype": "array", + "shape": [num_zebra_data], + "source": "", # make this the PV of the array the det is writing + } + + desc = OrderedDict() # desc defines data_keys + + desc['zebra_time'] = spec + desc['zebra_time']["source"] = getattr(self._encoder.pc.data, 'enc1').pvname + desc['enc1_pi_r'] = spec + desc['enc1_pi_r']["source"] = getattr(self._encoder.pc.data, 'enc1').pvname + + # Handle the detectors we are going to get + for d in self.detectors: + desc.update(d.describe()) + # print(f"\nin describe_collect {desc=}\n") + + # # Handle the ion chamber that the zebra is collecting + # desc["i0"] = spec + # desc["i0"]["source"] = self._sis.mca2.pvname + # desc["i0_time"] = spec + # desc["i0_time"]["source"] = self._sis.mca1.pvname + # desc["im"] = spec + # desc["im"]["source"] = self._sis.mca3.pvname + # desc["it"] = spec + # desc["it"]["source"] = self._sis.mca4.pvname + + return {"primary": desc} + + def collect(self): + # Create records in the FileStore database. + # move this to stage because I think that describe_collect needs the + # resource id + # TODO use ophyd.areadectector.filestoer_mixins.resllource_factory here + if self._last_bulk is None: + raise Exception( + "the order of complete and collect is brittle and out " + "of sync. This device relies on in-order and 1:1 calls " + "between complete and collect to correctly create and stash " + "the asset registry documents" + ) + # self._last_bulk defines a event document + yield self._last_bulk + self._last_bulk = None + self._state = "idle" + + def collect_asset_docs(self): + yield from iter(list(self._document_cache)) + self._document_cache.clear() + + def stop(self): + self._encoder.pc.block_state_reset.put(1) + pass + + def pause(self): + "Pausing in the middle of a kickoff nukes the partial dataset." + self._encoder.pc.block_state_reset.put(1) + for d in self._dets: + if hasattr(d, "settings"): + d.settings.acquire.put(0) + if hasattr(d, "cam"): + d.cam.acquire.put(0) + self._state = "idle" + self.unstage() + + def resume(self): + self.unstage() + self.stage() + + def preset_flyer(self, scn_cfg): + yield from FXITomoFlyer.bin_det(self.detectors[0], scn_cfg["bin_fac"]) + yield from FXITomoFlyer.init_mot_r(scn_cfg) + yield from FXITomoFlyer.set_cam_mode(self.detectors[0], stage="pre-scan") + scn_cfg = FXITomoFlyer.cal_cam_rot_params(self.detectors[0], scn_cfg) + pc_cfg = FXITomoFlyer.cal_zebra_pc_params(scn_cfg) + self.preset_zebra(pc_cfg) + print("preset_flyer is done") + return scn_cfg, pc_cfg + + def set_pc_step_for_scan(self, scn_cfg, pc_cfg): + yield from abs_set(self.encoder.pc.dir, pc_cfg[self.scn_modes[scn_cfg["scn_mode"]]]["dir"], wait=True) + yield from abs_set(self.encoder.pc.gate_start, pc_cfg[self.scn_modes[scn_cfg["scn_mode"]]]["gate_start"], wait=True) + + @staticmethod + def set_wait(field, val, wait=0.01): + set_and_wait(field, val, poll_time=wait) + # while field.get() != val: + # field.put(val, timeout=10) + # ttime.sleep(0.01) + # ttime.sleep(wait) + + @classmethod + def cal_cam_rot_params(cls, det, scn_cfg): + """_summary_ + + Args: + det (str): choose from the set {"Andor", "Marana", "Oryx"} + scn_cfg (dict): scan configuration parameters composed of + 'scn_mode': choose between { + 0: "standard", # a single scan in a given angle range + 1: "snaked: single file", # back-forth rocking scan being saved into a single file + 2: "snaked: multiple files" # back-forth rocking scan with each swing being saved into a file + } + 'exp_t': detector exposure time in second, + 'acq_p': acquisition period in second, + 'bin_fac': detector binning factor, + 'ang_s': scan starting angle, + 'ang_e': scan end angle, + 'num_swing': number of sub-scans; motion from one side to another side is defined as one swing + 'vel': rotation velocity in deg/sec, + 'tacc': rotation stage acceleration in sec, + "taxi_dist": taxi distance in unit deg + + Returns: + dict: scan_cfg + """ + ############### calculate detector parameters ############### + acq_p = FXITomoFlyer.check_cam_exp(det, scn_cfg["acq_p"], scn_cfg["bin_fac"]) + + if acq_p > scn_cfg["acq_p"]: + print( + "Acquisition period is too small for the camera. Reset acquisition period to minimum allowed exposure time." + ) + scn_cfg["acq_p"] = acq_p + + if scn_cfg["exp_t"] > acq_p - 0.002: + scn_cfg["exp_t"] = acq_p - 0.002 + + ############### calculate rotary stage parameters ############### + if scn_cfg["tacc"] <= 0: + print("Acceleration time cannot be smaller than 0. Reset it to 1 second.") + scn_cfg["tacc"] = 1 + + if scn_cfg["vel"] > cls.rot_axis.max_velo.get(): + print( + "Designed velocity exceeds the maximum allowed velocity. Reset it to the maximum allowed velocity" + ) + scn_cfg["vel"] = cls.rot_axis.max_velo.get() + elif scn_cfg["vel"] < cls.rot_axis.base_velo.get(): + print( + "Designed velocity is smaller than the minimum allowed velocity. Reset it to the minimum allowed velocity" + ) + scn_cfg["vel"] = cls.rot_axis.base_velo.get() + + taxi_dist = np.ceil( + (scn_cfg["vel"] - cls.rot_axis.base_velo.get()) * scn_cfg["tacc"] / 2 + ) + if not (cls.rot_axis.low_limit_switch.get() == 0 and cls.rot_axis.high_limit_switch.get() == 0): + if (scn_cfg["ang_s"] - taxi_dist) < cls.rot_axis.low_limit.get(): + print("Rotation range is beyond the low limit of the rotary stage. Quit!") + return None + elif (scn_cfg["ang_s"] - taxi_dist) > cls.rot_axis.high_limit.get(): + print("Rotation range is beyond the high limit of the rotary stage. Quit!") + return None + + if (scn_cfg["ang_e"] + taxi_dist) < cls.rot_axis.low_limit.get(): + print("Rotation range is beyond the low limit of the rotary stage. Quit!") + return None + elif (scn_cfg["ang_e"] + taxi_dist) > cls.rot_axis.high_limit.get(): + print("Rotation range is beyond the high limit of the rotary stage. Quit!") + return None + + scn_cfg["taxi_dist"] = taxi_dist + scn_cfg["num_images"] = ( + int( + abs( + (scn_cfg["ang_e"] - scn_cfg["ang_s"]) / scn_cfg["vel"] / scn_cfg["acq_p"] + ) + ) + + 1 + ) + scn_cfg["ang_step"] = scn_cfg["vel"] * scn_cfg["acq_p"] + + if scn_cfg["ang_s"] < scn_cfg["ang_e"]: + scn_cfg["rot_dir"] = 1 + else: + scn_cfg["rot_dir"] = -1 + + return scn_cfg + + @staticmethod + def check_cam_exp(det, acq_p, bin_fac): + acq_min = DET_MIN_AP[det.name] / BIN_FACS[det.name][bin_fac] + acq_p = max(acq_min, acq_p) + return acq_p + + @classmethod + def cal_zebra_pc_params(cls, scn_cfg): + """_summary_ + + Args: + scn_cfg (dict): scan configuration parameters composed of + 'scn_mode': choose between { + 0: "standard", # a single scan in a given angle range + 1: "snaked: single file", # back-forth rocking scan being saved into a single file + 2: "snaked: multiple files" # back-forth rocking scan with each swing being saved into a file + } + 'exp_t': detector exposure time in second, + 'acq_p': acquisition period in second, + 'bin_fac': detector binning factor, + 'ang_s': scan starting angle, + 'ang_e': scan end angle, + 'num_swing': number of sub-scans; motion from one side to another side is defined as one swing + 'vel': rotation velocity in deg/sec, + 'tacc': rotation stage acceleration in sec, + "taxi_dist": taxi distance in unit deg + + Returns: + dict: pc_cfg + """ + pc_cfg = { + "standard": {}, + "snaked: multiple files": {}, + "snaked: single file": {}, + } + if cls.scn_modes[scn_cfg["scn_mode"]] == "standard": + pc_cfg["standard"]["gate_start"] = scn_cfg["ang_s"] + pc_cfg["standard"]["gate_width"] = abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + pc_cfg["standard"]["pulse_start"] = 0 + pc_cfg["standard"]["pulse_width"] = ( + cls.dft_pulse_wid[cls.tspre] * scn_cfg["vel"] + ) + pc_cfg["standard"]["pulse_step"] = round(scn_cfg["ang_step"], 3) + pc_cfg["standard"]["pulse_max"] = int(round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / pc_cfg["standard"]["pulse_step"] + + 1 + )) + pc_cfg["standard"]["gate_width"] = ( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + + pc_cfg["standard"]["pulse_step"] + ) + elif cls.scn_modes[scn_cfg["scn_mode"]] == "snaked: multiple files": + pc_cfg["snaked: multiple files"]["pulse_start"] = 0 + pc_cfg["snaked: multiple files"]["pulse_width"] = ( + cls.dft_pulse_wid[cls.tspre] * scn_cfg["vel"] + ) + pc_cfg["snaked: multiple files"]["pulse_step"] = round(scn_cfg["ang_step"], 3) + pc_cfg["snaked: multiple files"]["pulse_max"] = int(round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / pc_cfg["snaked: multiple files"]["pulse_step"] + + 1 + )) + pc_cfg["snaked: multiple files"]["gate_width"] = ( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + + pc_cfg["snaked: multiple files"]["pulse_step"] + ) + elif cls.scn_modes[scn_cfg["scn_mode"]] == "snaked: single file": + pc_cfg["snaked: single file"]["pulse_start"] = 0 + pc_cfg["snaked: single file"]["pulse_width"] = cls.dft_pulse_wid[cls.tspre] + pc_cfg["snaked: single file"]["pulse_step"] = round(scn_cfg["acq_p"], 4) + pc_cfg["snaked: single file"]["pulse_max"] = int(int(round( + abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + / (scn_cfg["acq_p"] * scn_cfg["vel"]) + + 2 * scn_cfg["tacc"] / scn_cfg["acq_p"] + + 1 + )) * scn_cfg["num_swing"]) + else: + print("Unrecognized scan mode. Quit") + return None + pc_cfg[cls.scn_modes[scn_cfg["scn_mode"]]]["dir"] = cls.pc_trig_dir[scn_cfg["rot_dir"]] + return pc_cfg + + @staticmethod + def compose_scn_cfg( + scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, tacc, num_swing + ): + scn_cfg = {} + scn_cfg["scn_mode"] = scn_mode + scn_cfg["exp_t"] = exp_t + scn_cfg["acq_p"] = acq_p + scn_cfg["bin_fac"] = 0 if bin_fac is None else bin_fac + scn_cfg["ang_s"] = ang_s + scn_cfg["ang_e"] = ang_e + scn_cfg["vel"] = vel + scn_cfg["tacc"] = tacc + scn_cfg["num_swing"] = num_swing + return scn_cfg + + @staticmethod + def prime_det(det): + if not list(det.hdf5.array_size.get()): + if det.cam.trigger_mode.get() != 0: + yield from abs_set(det.cam.trigger_mode, 0, wait=True) + if det.cam.image_mode.get() != 0: + yield from abs_set(det.cam.image_mode, 0, wait=True) + yield from abs_set(det.cam.num_images, 5, wait=True) + yield from abs_set(det.cam.acquire, 1, wait=True) + + @staticmethod + def bin_det(det, bin_fac): + yield from abs_set(det.cam.acquire, 0, wait=True) + if bin_fac is None: + bin_fac = 0 + if int(bin_fac) not in [0, 1, 2, 3, 4]: + raise ValueError("binnng must be in [0, 1, 2, 3, 4]") + yield from abs_set(det.binning, bin_fac, wait=True) + FXITomoFlyer.prime_det(det) + + @staticmethod + def def_abs_out_pos( + x_out, + y_out, + z_out, + r_out, + rel_out_flag, + ): + (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() + if rel_out_flag: + mot_x_out = x_ini + x_out if not (x_out is None) else x_ini + mot_y_out = y_ini + y_out if not (y_out is None) else y_ini + mot_z_out = z_ini + z_out if not (z_out is None) else z_ini + mot_r_out = r_ini + r_out if not (r_out is None) else r_ini + else: + mot_x_out = x_out if not (x_out is None) else x_ini + mot_y_out = y_out if not (y_out is None) else y_ini + mot_z_out = z_out if not (z_out is None) else z_ini + mot_r_out = r_out if not (r_out is None) else r_ini + return mot_x_out, mot_y_out, mot_z_out, mot_r_out + + @staticmethod + def get_txm_cur_pos(): + x_ini = zps.sx.position + y_ini = zps.sy.position + z_ini = zps.sz.position + r_ini = zps.pi_r.position + return x_ini, y_ini, z_ini, r_ini + + @staticmethod + def init_mot_r(scn_cfg): + cur_pos = zps.pi_r.position + yield from abs_set(zps.pi_r.offset_freeze_switch, 1) + ang_max = max(scn_cfg["ang_s"], scn_cfg["ang_e"]) + + if ang_max > 0: + yield from abs_set(zps.pi_r.user_offset, np.ceil(ang_max / 360) * 360) + + if abs(scn_cfg["ang_s"] - cur_pos) > 360: + cur_pos = scn_cfg["ang_s"] // 360 * 360 + cur_pos % 360 + zps.pi_r.set_current_position(cur_pos) + yield from abs_set(zps.pi_r.acceleration, 1, wait=True) + yield from abs_set(zps.pi_r.velocity, 30, wait=True) + yield from abs_set(zps.pi_r, scn_cfg["ang_s"], wait=True) + + @staticmethod + def set_cam_step_for_scan(det, scn_cfg): + set_and_wait(det.cam.acquire_time, scn_cfg["exp_t"], rtol=0.01) + set_and_wait(det.hdf5.num_capture, scn_cfg["num_images"]) + set_and_wait(det.cam.num_images, scn_cfg["num_images"]) + + @staticmethod + def set_mot_r_step_for_scan(scn_cfg): + yield from abs_set(zps.pi_r.acceleration, scn_cfg["tacc"], wait=True) + yield from abs_set(zps.pi_r.velocity, scn_cfg["vel"], wait=True) + yield from abs_set(zps.pi_r.user_setpoint, scn_cfg["ang_s"] - scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], wait=True) + + @staticmethod + def set_cam_mode(cam, stage="pre-scan"): + if stage == "pre-scan": + yield from abs_set(cam.cam.image_mode, 0) + yield from abs_set(cam.cam.trigger_mode, 4) + elif stage == "ref-scan": + yield from abs_set(cam.cam.image_mode, 0) + yield from abs_set(cam.cam.trigger_mode, 0) + elif stage == "post-scan": + yield from abs_set(cam.cam.image_mode, 1) + yield from abs_set(cam.cam.trigger_mode, 0) + + +Zebra = FXIZebra( + "XF:18ID-ES:1{Dev:Zebra1}:", + name="Zebra", + read_attrs=["pc.data.enc1", "pc.data.time"], +) + +tomo_flyer = FXITomoFlyer( + list((Andor,)), + Zebra, + name="tomo_flyer", +) + + +def export_zebra_data(zebra, filepath): + j = 0 + while zebra.pc.data_in_progress.get() == 1: + print("Waiting for zebra...") + ttime.sleep(0.1) + j += 1 + if j > 10: + print("THE ZEBRA IS BEHAVING BADLY CARRYING ON") + break + + time_d = zebra.pc.data.time.get() + enc1_d = zebra.pc.data.enc1.get() + + size = (len(time_d),) + with h5py.File(filepath, "w") as f: + dset0 = f.create_dataset("zebra_time", size, dtype="f") + dset0[...] = np.array(time_d) + dset1 = f.create_dataset("enc1_pi_r", size, dtype="f") + dset1[...] = np.array(enc1_d) + + +class ZebraHDF5Handler(HandlerBase): + HANDLER_NAME = "ZEBRA_HDF51" + + def __init__(self, resource_fn): + self._handle = h5py.File(resource_fn, "r") + + def __call__(self, *, column): + return self._handle[column][:] + + def close(self): + self._handle.close() + self._handle = None + super().close() + + +db.reg.register_handler("ZEBRA_HDF51", ZebraHDF5Handler, overwrite=True) diff --git a/startup/44-scans_other.py b/startup/44-scans_other.py index e8dd557..6613938 100644 --- a/startup/44-scans_other.py +++ b/startup/44-scans_other.py @@ -19,6 +19,7 @@ def test_scan( num_img=10, num_bkg=10, note="", + period=0.1, simu=False, md=None, ): @@ -42,7 +43,7 @@ def test_scan( num_bkg: int, number of backgroud image to take """ - yield from _set_andor_param(exposure_time, exposure_time, 1) + yield from _set_andor_param(exposure_time, period, 1) detectors = [Andor] y_ini = zps.sy.position diff --git a/startup/46-zebra_flyer.py b/startup/46-zebra_flyer.py index 334555b..54b1bb0 100644 --- a/startup/46-zebra_flyer.py +++ b/startup/46-zebra_flyer.py @@ -1,4 +1,4 @@ -from bluesky.plan_stubs import kickoff, collect, complete +from bluesky.plan_stubs import kickoff, collect, complete, wait from bluesky.utils import short_uid @@ -58,14 +58,16 @@ def tomo_zfly( _type_: _description_ """ global ZONE_PLATE - mots = [zps.sx, zps.sy, zps.sz, zps.pi_r] + # mots = [zps.sx, zps.sy, zps.sz, zps.pi_r] + mots = [zps.sx, zps.sy, zps.sz] flyer.detectors = [ cam, ] + flyer.scn_mode = flyer.scn_modes[scn_mode] scn_cfg = FXITomoFlyer.compose_scn_cfg( scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, acc_t, num_swing ) - yield from flyer.preset_flyer(scn_cfg) + scn_cfg, pc_cfg = yield from flyer.preset_flyer(scn_cfg) (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() (mot_x_out, mot_y_out, mot_z_out, mot_r_out) = FXITomoFlyer.def_abs_out_pos( out_x, out_y, out_z, out_r, rel_out_flag @@ -104,32 +106,46 @@ def tomo_zfly( "zone_plate": ZONE_PLATE, } _md.update(md or {}) + print("preset done") - print('preset done') - @stage_decorator(list(flyer.detectors) + list(mots)) + @stage_decorator(list(mots)) @run_decorator(md=_md) def inner_fly_plan(): yield from select_filters(flts) - for ii in range(scn_cfg["num_swing"]): + if flyer.scn_mode == "snaked: single file": + yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) yield from _open_shutter_xhx(simu) + for d in flyer.detectors: + try: + d.stage() + except: + d.unstage() + d.stage() st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) st.wait(timeout=10) + # yield from abs_set(flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True) - yield from abs_set(flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True) - FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) - FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) - - det_stream = short_uid('dets') + det_stream = short_uid("dets") for d in flyer.detectors: yield from bps.trigger(d, group=det_stream) + wait(det_stream) yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) t0 = ttime.monotonic() - yield from abs_set( - zps.pi_r, - scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], - wait=True - ) + for ii in range(scn_cfg["num_swing"]): + yield from abs_set( + zps.pi_r, + scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + wait=True, + ) + (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( + scn_cfg["ang_e"], + scn_cfg["ang_s"], + ) + scn_cfg["rot_dir"] *= -1 + if ii == scn_cfg["num_swing"] - 1: + set_and_wait(flyer.encoder.pc.disarm, 1) t1 = ttime.monotonic() while int(flyer.encoder.pc.gated.get()): @@ -142,19 +158,75 @@ def inner_fly_plan(): st = yield from complete(flyer, wait=True) st.wait(timeout=10) yield from collect(flyer) - if (scn_cfg["num_swing"] > 1) and ( - flyer.scn_modes[scn_cfg["scn_mode"]] != "snaked: single file" - ): - (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( - scn_cfg["ang_e"], - scn_cfg["ang_s"], + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return + else: + for ii in range(scn_cfg["num_swing"]): + yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + yield from _open_shutter_xhx(simu) + for d in flyer.detectors: + try: + d.stage() + except: + d.unstage() + d.stage() + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) + st.wait(timeout=10) + yield from abs_set( + flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True ) - scn_cfg["rot_dir"] *= -1 - pc_cfg[scn_cfg["scn_mode"]]["dir"] = flyer.pc_trig_dir[int(scn_cfg["rot_dir"])] - # yield from flyer.set_mot_r_step_for_scan(scn_cfg) - yield from flyer.set_pc_step_for_scan(scn_cfg, pc_cfg) - else: - yield from FXITomoFlyer.init_mot_r(scn_cfg) + + det_stream = short_uid("dets") + for d in flyer.detectors: + yield from bps.trigger(d, group=det_stream) + wait(det_stream) + + yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + t0 = ttime.monotonic() + yield from abs_set( + zps.pi_r, + scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + wait=True, + ) + + t1 = ttime.monotonic() + while int(flyer.encoder.pc.gated.get()): + if ttime.monotonic() - t1 > 60: + print("Scan finished abnormally. Quit!") + return + yield from bps.sleep(flyer._staging_delay) + print(ttime.time()) + print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") + st = yield from complete(flyer, wait=True) + st.wait(timeout=10) + yield from collect(flyer) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + + if scn_cfg["num_swing"] > 1: + (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( + scn_cfg["ang_e"], + scn_cfg["ang_s"], + ) + scn_cfg["rot_dir"] *= -1 + # breakpoint() + pc_cfg[flyer.scn_mode]["gate_start"] = scn_cfg["ang_s"] + pc_cfg[flyer.scn_mode]["dir"] = flyer.pc_trig_dir[ + int(scn_cfg["rot_dir"]) + ] + yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) + + scn_cfg["ang_s"] = r_ini + yield from FXITomoFlyer.init_mot_r(scn_cfg) yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") yield from _take_ref_image( @@ -178,6 +250,12 @@ def inner_fly_plan(): stream_name="dark", simu=simu, ) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None yield from _move_sample( x_ini, y_ini, @@ -188,6 +266,352 @@ def inner_fly_plan(): yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") yield from select_filters([]) + # def inner_fly_plan(): + # yield from select_filters(flts) + # for ii in range(scn_cfg["num_swing"]): + # yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + # print(5) + # yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + # print(6) + # yield from _open_shutter_xhx(simu) + # print(7) + # for d in flyer.detectors: + # try: + # d.stage() + # except: + # d.unstage() + # d.stage() + # st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) + # st.wait(timeout=10) + # yield from abs_set(flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True) + + # det_stream = short_uid("dets") + # for d in flyer.detectors: + # yield from bps.trigger(d, group=det_stream) + # wait(det_stream) + + # yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + # t0 = ttime.monotonic() + # yield from abs_set( + # zps.pi_r, + # scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + # wait=True, + # ) + + # t1 = ttime.monotonic() + # while int(flyer.encoder.pc.gated.get()): + # if ttime.monotonic() - t1 > 60: + # print("Scan finished abnormally. Quit!") + # return + # yield from bps.sleep(flyer._staging_delay) + # print(ttime.time()) + # print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") + # st = yield from complete(flyer, wait=True) + # st.wait(timeout=10) + # yield from collect(flyer) + # for d in flyer.detectors: + # try: + # d.unstage() + # except: + # print(f"Cannot unstage detector {d.name}") + # return None + + # if flyer.scn_mode == "snaked: single file": + # (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( + # scn_cfg["ang_e"], + # scn_cfg["ang_s"], + # ) + # scn_cfg["rot_dir"] *= -1 + # elif scn_cfg["num_swing"] > 1: + # (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( + # scn_cfg["ang_e"], + # scn_cfg["ang_s"], + # ) + # scn_cfg["rot_dir"] *= -1 + # # breakpoint() + # pc_cfg[flyer.scn_mode]["gate_start"] = scn_cfg["ang_s"] + # pc_cfg[flyer.scn_mode]["dir"] = flyer.pc_trig_dir[ + # int(scn_cfg["rot_dir"]) + # ] + # yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) + + # scn_cfg["ang_s"] = r_ini + # yield from FXITomoFlyer.init_mot_r(scn_cfg) + + # yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") + # yield from _take_ref_image( + # [cam], + # mots_pos={ + # "x": mot_x_out, + # "y": mot_y_out, + # "z": mot_z_out, + # "r": mot_r_out, + # }, + # num=1, + # chunk_size=10, + # stream_name="flat", + # simu=simu, + # ) + # yield from _take_ref_image( + # [cam], + # mots_pos={}, + # num=1, + # chunk_size=10, + # stream_name="dark", + # simu=simu, + # ) + # for d in flyer.detectors: + # try: + # d.unstage() + # except: + # print(f"Cannot unstage detector {d.name}") + # return None + # yield from _move_sample( + # x_ini, + # y_ini, + # z_ini, + # r_ini, + # repeat=2, + # ) + # yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") + # yield from select_filters([]) + uid = yield from inner_fly_plan() print("scan finished") return uid + + +def tomo_grid_zfly( + scn_mode=0, + exp_t=0.05, + acq_p=0.05, + ang_s=0, + ang_e=180, + vel=3, + acc_t=1, + grid_nodes={}, + num_swing=1, + out_x=None, + out_y=None, + out_z=None, + out_r=None, + rel_out_flag=True, + flts=[], + rot_back_velo=30, + bin_fac=None, + note="", + md=None, + simu=False, + cam=Andor, + flyer=tomo_flyer, +): + """_summary_ + + Args: + scn_mode (int, optional): _description_. Defaults to 0. + exp_t (float, optional): _description_. Defaults to 0.05. + acq_p (float, optional): _description_. Defaults to 0.05. + ang_s (float or None, optional): _description_. Defaults to None. + ang_e (float, optional): _description_. Defaults to 180. + vel (int, optional): _description_. Defaults to 3. + acc_t (float, optional): _description_. Defaults to 1. + grid_nodes (dic, optional): a dictionary with two items, one is + a list of motors that loop with rotary stage; another is a table + that lists all the grid nodes. + num_swing (int, optional): _description_. Defaults to 1. + out_x (float, optional): _description_. Defaults to None. + out_y (float, optional): _description_. Defaults to None. + out_z (float, optional): _description_. Defaults to None. + out_r (float, optional): _description_. Defaults to None. + flts (list, optional): _description_. Defaults to []. + rot_back_velo (int, optional): _description_. Defaults to 30. + binning (int, optional): _description_. Defaults to None. + note (str, optional): _description_. Defaults to "". + md (dict, optional): _description_. Defaults to None. + simu (bool, optional): _description_. Defaults to False. + cam (ophyd.Device, optional): detector; choose between Andor, Marana, and Oryx. + + Raises: + ValueError: _description_ + + Returns: + _type_: _description_ + + Yields: + _type_: _description_ + """ + global ZONE_PLATE + # mots = [zps.sx, zps.sy, zps.sz, zps.pi_r] + mots = [zps.sx, zps.sy, zps.sz] + flyer.detectors = [ + cam, + ] + flyer.scn_mode = flyer.scn_modes[scn_mode] + scn_cfg = FXITomoFlyer.compose_scn_cfg( + scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, acc_t, num_swing + ) + scn_cfg, pc_cfg = yield from flyer.preset_flyer(scn_cfg) + (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() + (mot_x_out, mot_y_out, mot_z_out, mot_r_out) = FXITomoFlyer.def_abs_out_pos( + out_x, out_y, out_z, out_r, rel_out_flag + ) + + _md = { + "detectors": [flyer.detectors[0].name], + "motors": [mot.name for mot in mots], + "XEng": XEng.position, + "storage_ring_current (mA)": round(sr_current.get(), 1), + "plan_args": { + "exposure_time": scn_cfg["exp_t"], + "start_angle": scn_cfg["ang_s"], + "end_angle": scn_cfg["ang_e"], + "acquisition_period": scn_cfg["acq_p"], + "number_of_swings": scn_cfg["num_swing"], + "slew_speed": scn_cfg["vel"], + "acceleration": scn_cfg["acc_t"], + "grid_mots": "none" + if not grid_nodes + else [mot.name for mot in grid_nodes["mots"]], + "grid_nodes": "none" if not grid_nodes else grid_nodes["pos"], + "out_x": mot_x_out, + "out_y": mot_y_out, + "out_z": mot_z_out, + "out_r": mot_r_out, + "rel_out_flag": rel_out_flag, + "filters": ["filter{}".format(t) for t in flts] if flts else "None", + "binning": 0 if scn_cfg["bin_fac"] is None else scn_cfg["bin_fac"], + "note": note if note else "None", + "zone_plate": ZONE_PLATE, + }, + "plan_name": "tomo_grid_zfly", + "num_bkg_images": 10, + "num_dark_images": 10, + "plan_pattern": "linspace", + "plan_pattern_module": "numpy", + "hints": {}, + "operator": "FXI", + "note": note if note else "None", + "zone_plate": ZONE_PLATE, + } + _md.update(md or {}) + + all_mots = list(set(list(mots) + list(grid_nodes["mots"]))) + print("preset done") + + # @stage_decorator(list(mots)) + @run_decorator(md=_md) + def inner_fly_plan(): + yield from select_filters(flts) + for jj in grid_nodes["pos"] or range(1): + yield from mv(*zip(grid_nodes["mots"], jj)) + for mot in all_mots: + mot.stage() + + for ii in range(scn_cfg["num_swing"]): + if (ii == 0) or (scn_mode == 1): + yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + for d in flyer.detectors: + try: + d.stage() + except: + d.unstage() + d.stage() + + yield from _open_shutter_xhx(simu) + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) + st.wait(timeout=10) + yield from abs_set( + flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True + ) + + det_stream = short_uid("dets") + for d in flyer.detectors: + yield from bps.trigger(d, group=det_stream) + wait(det_stream) + + yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + t0 = ttime.monotonic() + yield from abs_set( + zps.pi_r, + scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + wait=True, + ) + + t1 = ttime.monotonic() + while int(flyer.encoder.pc.gated.get()): + if ttime.monotonic() - t1 > 60: + print("Scan finished abnormally. Quit!") + return + yield from bps.sleep(flyer._staging_delay) + print(ttime.time()) + print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") + st = yield from complete(flyer, wait=True) + st.wait(timeout=10) + yield from collect(flyer) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + if (scn_cfg["num_swing"] > 1) and ( + flyer.scn_mode != "snaked: single file" + ): + (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( + scn_cfg["ang_e"], + scn_cfg["ang_s"], + ) + scn_cfg["rot_dir"] *= -1 + pc_cfg[flyer.scn_mode]["gate_start"] = scn_cfg["ang_s"] + pc_cfg[scn_cfg["scn_mode"]]["dir"] = flyer.pc_trig_dir[ + int(scn_cfg["rot_dir"]) + ] + yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) + else: + yield from FXITomoFlyer.init_mot_r(scn_cfg) + + for mot in all_mots: + mot.unstage() + + yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") + yield from _take_ref_image( + [cam], + mots_pos={ + "x": mot_x_out, + "y": mot_y_out, + "z": mot_z_out, + "r": mot_r_out, + }, + num=1, + chunk_size=10, + stream_name="flat", + simu=simu, + ) + yield from _take_ref_image( + [cam], + mots_pos={}, + num=1, + chunk_size=10, + stream_name="dark", + simu=simu, + ) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + yield from _move_sample( + x_ini, + y_ini, + z_ini, + r_ini, + repeat=2, + ) + yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") + yield from select_filters([]) + + uid = yield from inner_fly_plan() + print("scan finished") + return uid diff --git a/startup/46-zebra_flyer.py~ b/startup/46-zebra_flyer.py~ new file mode 100644 index 0000000..e78b24b --- /dev/null +++ b/startup/46-zebra_flyer.py~ @@ -0,0 +1,210 @@ +from bluesky.plan_stubs import kickoff, collect, complete, wait +from bluesky.utils import short_uid + + +def tomo_zfly( + scn_mode=0, + exp_t=0.05, + acq_p=0.05, + ang_s=0, + ang_e=180, + vel=3, + acc_t=1, + num_swing=1, + out_x=None, + out_y=None, + out_z=None, + out_r=None, + rel_out_flag=True, + flts=[], + rot_back_velo=30, + bin_fac=None, + note="", + md=None, + simu=False, + cam=Andor, + flyer=tomo_flyer, +): + """_summary_ + + Args: + scn_mode (int, optional): _description_. Defaults to 0. + exp_t (float, optional): _description_. Defaults to 0.05. + acq_p (float, optional): _description_. Defaults to 0.05. + ang_s (float or None, optional): _description_. Defaults to None. + ang_e (float, optional): _description_. Defaults to 180. + vel (int, optional): _description_. Defaults to 3. + acc_t (float, optional): _description_. Defaults to 1. + num_swing (int, optional): _description_. Defaults to 1. + out_x (float, optional): _description_. Defaults to None. + out_y (float, optional): _description_. Defaults to None. + out_z (float, optional): _description_. Defaults to None. + out_r (float, optional): _description_. Defaults to None. + flts (list, optional): _description_. Defaults to []. + rot_back_velo (int, optional): _description_. Defaults to 30. + binning (int, optional): _description_. Defaults to None. + note (str, optional): _description_. Defaults to "". + md (dict, optional): _description_. Defaults to None. + simu (bool, optional): _description_. Defaults to False. + cam (ophyd.Device, optional): detector; choose between Andor, Marana, and Oryx. + + Raises: + ValueError: _description_ + + Returns: + _type_: _description_ + + Yields: + _type_: _description_ + """ + global ZONE_PLATE + mots = [zps.sx, zps.sy, zps.sz, zps.pi_r] + flyer.detectors = [ + cam, + ] + scn_cfg = FXITomoFlyer.compose_scn_cfg( + scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, acc_t, num_swing + ) + yield from flyer.preset_flyer(scn_cfg) + (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() + (mot_x_out, mot_y_out, mot_z_out, mot_r_out) = FXITomoFlyer.def_abs_out_pos( + out_x, out_y, out_z, out_r, rel_out_flag + ) + + _md = { + "detectors": [flyer.detectors[0].name], + "motors": [mot.name for mot in mots], + "XEng": XEng.position, + "storage_ring_current (mA)": round(sr_current.get(), 1), + "plan_args": { + "exposure_time": scn_cfg["exp_t"], + "start_angle": scn_cfg["ang_s"], + "end_angle": scn_cfg["ang_e"], + "acquisition_period": scn_cfg["acq_p"], + "number_of_swings": scn_cfg["num_swing"], + "out_x": mot_x_out, + "out_y": mot_y_out, + "out_z": mot_z_out, + "out_r": mot_r_out, + "slew_speed": scn_cfg["vel"], + "rel_out_flag": rel_out_flag, + "filters": ["filter{}".format(t) for t in flts] if flts else "None", + "binning": 0 if scn_cfg["bin_fac"] is None else scn_cfg["bin_fac"], + "note": note if note else "None", + "zone_plate": ZONE_PLATE, + }, + "plan_name": "tomo_zfly", + "num_bkg_images": 10, + "num_dark_images": 10, + "plan_pattern": "linspace", + "plan_pattern_module": "numpy", + "hints": {}, + "operator": "FXI", + "note": note if note else "None", + "zone_plate": ZONE_PLATE, + } + _md.update(md or {}) + + print('preset done') + @stage_decorator(list(mots)) + @run_decorator(md=_md) + def inner_fly_plan(): + yield from select_filters(flts) + for ii in range(scn_cfg["num_swing"]): + FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + yield from _open_shutter_xhx(simu) + for d in flyer.detectors: + try: + d.stage() + except: + d.unstage() + d.stage() + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) + st.wait(timeout=10) + yield from abs_set(flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True) + + det_stream = short_uid('dets') + for d in flyer.detectors: + yield from bps.trigger(d, group=det_stream) + wait(det_stream) + + yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + t0 = ttime.monotonic() + yield from abs_set( + zps.pi_r, + scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + wait=True + ) + + t1 = ttime.monotonic() + while int(flyer.encoder.pc.gated.get()): + if ttime.monotonic() - t1 > 60: + print("Scan finished abnormally. Quit!") + return + yield from bps.sleep(flyer._staging_delay) + print(ttime.time()) + print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") + st = yield from complete(flyer, wait=True) + st.wait(timeout=10) + yield from collect(flyer) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + if (scn_cfg["num_swing"] > 1) and ( + flyer.scn_modes[scn_cfg["scn_mode"]] != "snaked: single file" + ): + (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( + scn_cfg["ang_e"], + scn_cfg["ang_s"], + ) + scn_cfg["rot_dir"] *= -1 + pc_cfg[scn_cfg["scn_mode"]]["dir"] = flyer.pc_trig_dir[int(scn_cfg["rot_dir"])] + yield from flyer.set_pc_step_for_scan(scn_cfg, pc_cfg) + else: + yield from FXITomoFlyer.init_mot_r(scn_cfg) + + yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") + yield from _take_ref_image( + [cam], + mots_pos={ + "x": mot_x_out, + "y": mot_y_out, + "z": mot_z_out, + "r": mot_r_out, + }, + num=1, + chunk_size=10, + stream_name="flat", + simu=simu, + ) + yield from _take_ref_image( + [cam], + mots_pos={}, + num=1, + chunk_size=10, + stream_name="dark", + simu=simu, + ) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + yield from _move_sample( + x_ini, + y_ini, + z_ini, + r_ini, + repeat=2, + ) + yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") + yield from select_filters([]) + + uid = yield from inner_fly_plan() + print("scan finished") + return uid diff --git a/startup/98-user_scan.py b/startup/98-user_scan.py index 7c0bacf..3a17eef 100644 --- a/startup/98-user_scan.py +++ b/startup/98-user_scan.py @@ -54,6 +54,7 @@ def send_email(subject, import sys from ophyd.sim import SynAxis + def select_filters(flts=[]): for key, item in FILTERS.items(): yield from mv(item, 0) @@ -256,7 +257,6 @@ def insitu_xanes_scan( def user_fly_scan( exposure_time=0.1, period=0.1, chunk_size=20, rs=1, note="", simu=False, md=None ): - """ motor_x_ini = zps.pi_x.position # motor_x_out = motor_x_ini + txm_out_x @@ -583,11 +583,11 @@ def _xanes_3D_xh( note="", binning=1, flts=[], - enable_z=True + enable_z=True, ): for eng in eng_list: yield from move_zp_ccd(eng, move_flag=1) - my_note = note + f"_energy={eng}" + my_note = f"{note}@energy={eng}" yield from bps.sleep(1) print(f"current energy: {eng}") @@ -607,7 +607,7 @@ def _xanes_3D_xh( rot_first_flag=rot_first_flag, flts=flts, binning=binning, - enable_z=enable_z + enable_z=enable_z, ) yield from bps.sleep(1) yield from mv(Andor.cam.image_mode, 1) @@ -637,7 +637,7 @@ def _multi_pos_xanes_3D_xh( flts=[], repeat=1, ref_flat_scan=False, - enable_z=True + enable_z=True, ): yield from select_filters(flts) # yield from _bin_cam(binning) @@ -652,13 +652,15 @@ def _multi_pos_xanes_3D_xh( z_list[i] = zps.sz.position if r_list[i] is None: r_list[i] = zps.pi_r.position - yield from _move_sample_in_xhx(x_list[i], - y_list[i], - z_list[i], - r_list[i], - repeat=2, - trans_first_flag=1, - enable_z=enable_z) + yield from _move_sample_in_xhx( + x_list[i], + y_list[i], + z_list[i], + r_list[i], + repeat=2, + trans_first_flag=1, + enable_z=enable_z, + ) yield from _xanes_3D_xh( eng_list, exposure_time=exposure_time, @@ -675,7 +677,7 @@ def _multi_pos_xanes_3D_xh( rot_first_flag=rot_first_flag, note=note, binning=binning, - enable_z=enable_z + enable_z=enable_z, ) if ref_flat_scan: motor_x_ini = zps.sx.position @@ -684,24 +686,38 @@ def _multi_pos_xanes_3D_xh( motor_r_ini = zps.pi_r.position if relative_move_flag: - motor_x_out = motor_x_ini + out_x if not (out_x is None) else motor_x_ini - motor_y_out = motor_y_ini + out_y if not (out_y is None) else motor_y_ini - motor_z_out = motor_z_ini + out_z if not (out_z is None) else motor_z_ini - motor_r_out = motor_r_ini + out_r if not (out_r is None) else motor_r_ini + motor_x_out = ( + motor_x_ini + out_x if not (out_x is None) else motor_x_ini + ) + motor_y_out = ( + motor_y_ini + out_y if not (out_y is None) else motor_y_ini + ) + motor_z_out = ( + motor_z_ini + out_z if not (out_z is None) else motor_z_ini + ) + motor_r_out = ( + motor_r_ini + out_r if not (out_r is None) else motor_r_ini + ) else: motor_x_out = out_x if not (out_x is None) else motor_x_ini motor_y_out = out_y if not (out_y is None) else motor_y_ini motor_z_out = out_z if not (out_z is None) else motor_z_ini motor_r_out = out_r if not (out_r is None) else motor_r_ini - yield from _move_sample_out_xhx(motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - repeat=2, - rot_first_flag=1, - enable_z=enable_z) - for ii in [eng_list[-1], eng_list[int(eng_list.shape[0]/2)], eng_list[0]]: + yield from _move_sample_out_xhx( + motor_x_out, + motor_y_out, + motor_z_out, + motor_r_out, + repeat=2, + rot_first_flag=1, + enable_z=enable_z, + ) + for ii in [ + eng_list[-1], + eng_list[int(eng_list.shape[0] / 2)], + eng_list[0], + ]: yield from move_zp_ccd(ii, move_flag=1) my_note = note + f"_ref_flat@energy={ii}_keV" yield from bps.sleep(1) @@ -709,32 +725,235 @@ def _multi_pos_xanes_3D_xh( if period: yield from fly_scan2( - exposure_time, - start_angle=start_angle, - relative_rot_angle=relative_rot_angle, - period=period, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - rs=rs, - relative_move_flag=relative_move_flag, - note=my_note, - simu=simu, - rot_first_flag=1, - binning=binning, - flts=flts, - enable_z=enable_z - ) + exposure_time, + start_angle=start_angle, + relative_rot_angle=relative_rot_angle, + period=period, + out_x=None, + out_y=None, + out_z=None, + out_r=None, + rs=rs, + relative_move_flag=relative_move_flag, + note=my_note, + simu=simu, + rot_first_flag=1, + binning=binning, + flts=flts, + enable_z=enable_z, + ) else: print(f"invalid binning {binning}") - yield from _move_sample_in_xhx(motor_x_ini, - motor_y_ini, - motor_z_ini, - motor_r_ini, - repeat=2, - trans_first_flag=1, - enable_z=enable_z) + yield from _move_sample_in_xhx( + motor_x_ini, + motor_y_ini, + motor_z_ini, + motor_r_ini, + repeat=2, + trans_first_flag=1, + enable_z=enable_z, + ) + print(f"sleep for {sleep_time} sec\n\n\n\n") + yield from bps.sleep(sleep_time) + + +def _xanes_3D_zebra_xh( + eng_list, + exp_t=0.05, + acq_p=0.06, + ang_s=0, + ang_e=180, + vel=4, + acc_t=1, + out_x=None, + out_y=None, + out_z=None, + out_r=None, + simu=False, + rel_out_flag=1, + note="", + bin_fac=1, + flts=[], + cam=Andor, + flyer=tomo_flyer, +): + for eng in eng_list: + yield from move_zp_ccd(eng, move_flag=1) + my_note = f"{note}@energy={eng}" + yield from bps.sleep(1) + print(f"current energy: {eng}") + + yield from tomo_zfly( + scn_mode=0, + exp_t=exp_t, + acq_p=acq_p, + ang_s=ang_s, + ang_e=ang_e, + vel=vel, + acc_t=acc_t, + out_x=out_x, + out_y=out_y, + out_z=out_z, + out_r=out_r, + rel_out_flag=rel_out_flag, + flts=flts, + rot_back_velo=30, + bin_fac=bin_fac, + note=my_note, + md=None, + simu=simu, + cam=cam, + flyer=flyer, + num_swing=1, + ) + + +def _multi_pos_xanes_3D_zebra_xh( + eng_list, + x_list, + y_list, + z_list, + r_list, + exp_t=0.05, + acq_p=0.05, + ang_s=0, + ang_e=180, + vel=2, + acc_t=1, + out_x=0, + out_y=0, + out_z=0, + out_r=0, + simu=False, + rel_out_flag=1, + note="", + sleep_time=0, + bin_fac=1, + flts=[], + repeat=1, + ref_flat_scan=False, + cam=Andor, + flyer=tomo_flyer, +): + yield from select_filters(flts) + n = len(x_list) + for rep in range(repeat): + for i in range(n): + if x_list[i] is None: + x_list[i] = zps.sx.position + if y_list[i] is None: + y_list[i] = zps.sy.position + if z_list[i] is None: + z_list[i] = zps.sz.position + if r_list[i] is None: + r_list[i] = zps.pi_r.position + yield from _move_sample_in_xhx( + x_list[i], + y_list[i], + z_list[i], + r_list[i], + repeat=2, + trans_first_flag=1, + enable_z=enable_z, + ) + yield from _xanes_3D_zebra_xh( + eng_list, + exp_t=exp_t, + acq_p=acq_p, + ang_s=ang_s, + ang_e=ang_e, + vel=vel, + acc_t=acc_t, + out_x=out_x, + out_y=out_y, + out_z=out_z, + out_r=out_r, + simu=simu, + rel_out_flag=rel_out_flag, + note=note, + bin_fac=bin_fac, + flts=flts, + cam=cam, + flyer=flyer, + ) + + if ref_flat_scan: + motor_x_ini = zps.sx.position + motor_y_ini = zps.sy.position + motor_z_ini = zps.sz.position + motor_r_ini = zps.pi_r.position + + if rel_out_flag: + motor_x_out = ( + motor_x_ini + out_x if not (out_x is None) else motor_x_ini + ) + motor_y_out = ( + motor_y_ini + out_y if not (out_y is None) else motor_y_ini + ) + motor_z_out = ( + motor_z_ini + out_z if not (out_z is None) else motor_z_ini + ) + motor_r_out = ( + motor_r_ini + out_r if not (out_r is None) else motor_r_ini + ) + else: + motor_x_out = out_x if not (out_x is None) else motor_x_ini + motor_y_out = out_y if not (out_y is None) else motor_y_ini + motor_z_out = out_z if not (out_z is None) else motor_z_ini + motor_r_out = out_r if not (out_r is None) else motor_r_ini + + yield from _move_sample_out_xhx( + motor_x_out, + motor_y_out, + motor_z_out, + motor_r_out, + repeat=2, + rot_first_flag=1, + enable_z=True, + ) + for ii in [ + eng_list[-1], + eng_list[int(eng_list.shape[0] / 2)], + eng_list[0], + ]: + yield from move_zp_ccd(ii, move_flag=1) + my_note = f"{note}_ref_flat@energy={ii}keV" + yield from bps.sleep(1) + print(f"current energy: {ii}") + + yield from tomo_zfly( + scn_mode=0, + exp_t=exp_t, + acq_p=acq_p, + ang_s=ang_s, + ang_e=ang_e, + vel=vel, + acc_t=acc_t, + out_x=0, + out_y=0, + out_z=0, + out_r=0, + rel_out_flag=True, + flts=flts, + rot_back_velo=30, + bin_fac=bin_fac, + note=my_note, + md=None, + simu=simu, + cam=cam, + flyer=flyer, + num_swing=1, + ) + + yield from _move_sample_in_xhx( + motor_x_ini, + motor_y_ini, + motor_z_ini, + motor_r_ini, + repeat=2, + trans_first_flag=1, + enable_z=True, + ) print(f"sleep for {sleep_time} sec\n\n\n\n") yield from bps.sleep(sleep_time) @@ -759,7 +978,7 @@ def _multi_pos_xanes_2D_xh( md=None, binning=0, flts=[], - enable_z=True + enable_z=True, ): """ Different from multipos_2D_xanes_scan. In the current scan, it take image at all locations and then move out sample to take background image. @@ -833,9 +1052,9 @@ def _multi_pos_xanes_2D_xh( # yield from _bin_cam(binning) detectors = [Andor, ic3, ic4] - #print(f"{exposure_time=}") - #period = yield from _exp_t_sanity_check(exposure_time, binning=binning) - #print('444:', period) + # print(f"{exposure_time=}") + # period = yield from _exp_t_sanity_check(exposure_time, binning=binning) + # print('444:', period) period = exposure_time yield from mv(Andor.cam.acquire, 0) yield from _set_andor_param(exposure_time, period=period, chunk_size=chunk_size) @@ -888,9 +1107,9 @@ def _multi_pos_xanes_2D_xh( "out_r": out_r, "repeat_num": repeat_num, "exposure_time": exposure_time, - "period":period, - "binning":binning, - "filters":["filter{}".format(t) for t in flts] if flts else "None", + "period": period, + "binning": binning, + "filters": ["filter{}".format(t) for t in flts] if flts else "None", "sleep_time": sleep_time, "chunk_size": chunk_size, "relative_move_flag": relative_move_flag, @@ -938,36 +1157,40 @@ def inner_scan(): print(f"repeat multi-pos xanes scan #{rep}") for eng in eng_list: yield from move_zp_ccd(eng, move_flag=1, info_flag=0) - yield from _open_shutter_xhx(simu) + yield from _open_shutter_xhx(simu) # take image at multiple positions for i in range(num): - yield from _move_sample_in_xhx(x_list[i], - y_list[i], - z_list[i], - r_list[i], - repeat=2, - trans_first_flag=1, - enable_z=enable_z) + yield from _move_sample_in_xhx( + x_list[i], + y_list[i], + z_list[i], + r_list[i], + repeat=2, + trans_first_flag=1, + enable_z=enable_z, + ) yield from trigger_and_read(list(detectors) + motor) - yield from _take_bkg_image_xhx(motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - detectors, - motor, - num=1, - chunk_size=chunk_size, - stream_name="flat", - simu=simu, - enable_z=enable_z + yield from _take_bkg_image_xhx( + motor_x_out, + motor_y_out, + motor_z_out, + motor_r_out, + detectors, + motor, + num=1, + chunk_size=chunk_size, + stream_name="flat", + simu=simu, + enable_z=enable_z, ) - yield from _move_sample_in_xhx(motor_x_ini, - motor_y_ini, - motor_z_ini, - motor_r_ini, - repeat=2, - trans_first_flag=1, - enable_z=enable_z + yield from _move_sample_in_xhx( + motor_x_ini, + motor_y_ini, + motor_z_ini, + motor_r_ini, + repeat=2, + trans_first_flag=1, + enable_z=enable_z, ) # end of eng_list # close shutter and sleep @@ -976,6 +1199,7 @@ def inner_scan(): if rep < repeat_num - 1: print(f"\nsleep for {sleep_time} seconds ...") yield from bps.sleep(sleep_time) + yield from inner_scan() @@ -1012,7 +1236,7 @@ def _mk_eng_list(elem, bulk=False): + "/eng_list_" + elem.split("_")[0] + "_xanes_standard_83pnt.txt" - ) + ) elif elem.split("_")[-1] == "101": eng_list = np.genfromtxt( "/nsls2/data/fxi-new/shared/config/xanes_ref/" @@ -1036,63 +1260,58 @@ def _mk_eng_list(elem, bulk=False): + "/eng_list_" + elem.split("_")[0] + "_xanes_standard_diff.txt" - ) + ) return eng_list def _exp_t_sanity_check(exp_t, binning=None): if binning is None: binning = 0 - if binning == 0: # 1x1 - #print('000') + if binning == 0: # 1x1 + # print('000') if exp_t < 0.05: period = 0.05 - #print('111') + # print('111') else: period = exp_t - #print('222') - elif binning == 1: # 2x2 + # print('222') + elif binning == 1: # 2x2 if exp_t < 0.025: period = 0.025 else: period = exp_t - elif binning == 2: # 3x3 + elif binning == 2: # 3x3 if exp_t < 0.017: period = 0.017 else: period = exp_t - elif binning == 3: # 4x4 + elif binning == 3: # 4x4 if exp_t < 0.0125: period = 0.0125 else: period = exp_t - elif binning == 4: # 8x8 + elif binning == 4: # 8x8 if exp_t < 0.00625: period = 0.00625 else: period = exp_t else: period = None - #print('333:', period) + # print('333:', period) return period -def _bin_cam(binning): - yield from mv(Andor.cam.acquire, 0) - yield from bps.sleep(1) +def _bin_cam(binning, cam=Andor): + yield from abs_set(cam.cam.acquire, 0, wait=True) if binning is None: binning = 0 if int(binning) not in [0, 1, 2, 3, 4]: raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) - yield from bps.sleep(1) - yield from mv(Andor.cam.image_mode, 0) - yield from bps.sleep(1) - yield from mv(Andor.cam.num_images, 5) - yield from bps.sleep(1) - yield from mv(Andor.cam.acquire, 1) - yield from bps.sleep(1) - yield from mv(Andor.cam.acquire, 0) + yield from abs_set(cam.binning, binning, wait=True) + yield from abs_set(cam.cam.image_mode, 0, wait=True) + yield from abs_set(cam.cam.num_images, 5, wait=True) + yield from abs_set(cam.cam.acquire, 1, wait=True) + yield from abs_set(cam.cam.acquire, 0, wait=True) return int(binning) @@ -1117,9 +1336,9 @@ def _sort_in_pos(in_pos_list): return (x_list, y_list, z_list, r_list) - -def _move_sample_out_xhx(out_x, out_y, out_z, out_r, repeat=1, - rot_first_flag=1, enable_z=False): +def _move_sample_out_xhx( + out_x, out_y, out_z, out_r, repeat=1, rot_first_flag=1, enable_z=False +): """ move out by relative distance """ @@ -1154,8 +1373,9 @@ def _move_sample_out_xhx(out_x, out_y, out_z, out_r, repeat=1, yield from mv(zps.pi_r, r_out) -def _move_sample_in_xhx(in_x, in_y, in_z, in_r, repeat=1, - trans_first_flag=1, enable_z=False): +def _move_sample_in_xhx( + in_x, in_y, in_z, in_r, repeat=1, trans_first_flag=1, enable_z=False +): """ move in at absolute position """ @@ -1178,13 +1398,7 @@ def _move_sample_in_xhx(in_x, in_y, in_z, in_r, repeat=1, def _take_dark_image_xhx( - detectors, - motor, - num=1, - chunk_size=1, - stream_name="dark", - simu=False, - cam=Andor + detectors, motor, num=1, chunk_size=1, stream_name="dark", simu=False, cam=Andor ): yield from _close_shutter_xhx(simu) original_num_images = yield from rd(cam.cam.num_images) @@ -1206,11 +1420,16 @@ def _take_bkg_image_xhx( stream_name="flat", simu=False, enable_z=False, - cam=Andor + cam=Andor, ): yield from _move_sample_out_xhx( - out_x, out_y, out_z, out_r, repeat=2, - rot_first_flag=rot_first_flag, enable_z=enable_z + out_x, + out_y, + out_z, + out_r, + repeat=2, + rot_first_flag=rot_first_flag, + enable_z=enable_z, ) original_num_images = yield from rd(cam.cam.num_images) yield from _set_Andor_chunk_size_xhx(detectors, chunk_size, cam) @@ -1226,14 +1445,172 @@ def _set_Andor_chunk_size_xhx(detectors, chunk_size, cam): yield from stage(detector) -def _set_andor_param_xhx(exposure_time=0.1, period=0.1, chunk_size=1, binning=[1, 1], cam=Andor): - yield from mv(cam.cam.acquire, 0) - yield from mv(cam.cam.image_mode, 0) - yield from mv(cam.cam.num_images, chunk_size) +def _set_andor_param_xhx( + exposure_time=0.1, period=0.1, chunk_size=1, binning=[1, 1], cam=Andor +): + yield from abs_set(cam.cam.acquire, 0, wait=True) + yield from abs_set(cam.cam.image_mode, 0, wait=True) + yield from abs_set(cam.cam.num_images, chunk_size, wait=True) period_cor = period - yield from mv(cam.cam.acquire_time, exposure_time) - # yield from abs_set(cam.cam.acquire_period, period_cor) - cam.cam.acquire_period.put(period_cor) + yield from abs_set(cam.cam.acquire_time, exposure_time, wait=True) + yield from abs_set(cam.cam.acquire_period, period_cor, wait=True) + + +def multi_edge_xanes_zebra( + elems=["Ni_wl"], + scan_type="3D", + flts={"Ni_filters": [1, 2, 3]}, + exp_t={"Ni_exp": 0.05}, + acq_p={"Ni_period": 0.05}, + ang_s=0, + ang_e=180, + vel=6, + acc_t=1, + in_pos_list=[[None, None, None, None]], + out_pos=[None, None, None, None], + note="", + rel_out_flag=0, + bin_fac=None, + bulk=False, + bulk_intgr=10, + simu=False, + sleep=0, + repeat=None, + ref_flat_scan=False, + cam=Andor, + flyer=tomo_flyer +): + yield from mv(cam.cam.acquire, 0) + if repeat is None: + repeat = 1 + repeat = int(repeat) + + if scan_type == "2D": + if bin_fac is None: + bin_fac = 0 + # binning = yield from _bin_cam(binning) + print(1) + + x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) + for elem in elems: + for key in flts.keys(): + if elem.split("_")[0] == key.split("_")[0]: + flt = flts[key] + break + else: + flt = [] + for key in exp_t.keys(): + if elem.split("_")[0] == key.split("_")[0]: + print(f"{exp_t[key]=}") + exposure = exp_t[key] + print(elem, exposure) + break + else: + exposure = 0.05 + print("use default exposure time 0.05s") + for key in period_time.keys(): + if elem.split("_")[0] == key.split("_")[0]: + yield from mv(Andor.cam.acquire_time, exposure) + yield from bps.sleep(2) + # yield from mv(Andor.cam.acquire_period, period_time[key]) + # period = yield from rd(Andor.cam.acquire_period) + # period = yield from _exp_t_sanity_check(period, binning=binning) + # print(elem, f"{period=}") + break + eng_list = _mk_eng_list(elem, bulk=False) + print(f"{out_pos=}") + yield from _multi_pos_xanes_2D_xh( + eng_list, + x_list, + y_list, + z_list, + r_list, + out_x=out_pos[0], + out_y=out_pos[1], + out_z=out_pos[2], + out_r=out_pos[3], + exposure_time=exposure, + chunk_size=5, + simu=simu, + relative_move_flag=rel_out_flag, + note=note, + md=None, + sleep_time=sleep, + repeat_num=repeat, + binning=bin_fac, + flts=flt, + enable_z=True, + ) + elif scan_type == "3D": + if bin_fac is None: + bin_fac = 1 + bin_fac = yield from _bin_cam(bin_fac) + + yield from abs_set(Andor.cam.image_mode, 0, wait=True) + yield from abs_set(Andor.cam.num_images, 5, wait=True) + yield from abs_set(Andor.cam.acquire, 1, wait=True) + + x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) + for elem in elems: + for key in flts.keys(): + if elem.split("_")[0] == key.split("_")[0]: + flt = flts[key] + break + else: + flt = [] + for key in exp_t.keys(): + if elem.split("_")[0] == key.split("_")[0]: + exposure = exp_t[key] + print(elem, exposure) + break + else: + exposure = 0.05 + print("use default exposure time 0.05s") + for key in acq_p.keys(): + if elem.split("_")[0] == key.split("_")[0]: + acquire_period = acq_p[key] + break + eng_list = _mk_eng_list(elem, bulk=False) + + yield from _multi_pos_xanes_3D_zebra_xh( + eng_list, + x_list, + y_list, + z_list, + r_list, + exp_t=exposure, + acq_p=acquire_period, + ang_s=ang_s, + ang_e=ang_e, + vel=vel, + acc_t=acc_t, + out_x=out_pos[0], + out_y=out_pos[1], + out_z=out_pos[2], + out_r=out_pos[3], + simu=simu, + rel_out_flag=rel_out_flag, + note=note, + sleep_time=sleep, + bin_fac=bin_fac, + flts=flts, + repeat=1, + ref_flat_scan=ref_flat_scan, + cam=cam, + flyer=flyer, + ) + + if bulk: + eng_list = _mk_eng_list(elem, bulk=True) + zpx = zp.x.position + apx = aper.x.position + cdx = clens.x.position + yield from mvr(zp.x, -6500, clens.x, 6500, aper.x, -4000) + xxanes_scan(eng_list, delay_time=0.2, intgr=bulk_intgr, note=note) + yield from mv(clens.x, cdx, aper.x, apx, zp.x, zpx) + + else: + print("wrong scan type") def multi_edge_xanes( @@ -1251,7 +1628,7 @@ def multi_edge_xanes( binning=None, simu=False, ref_flat_scan=False, - enable_z=True + enable_z=True, ): yield from mv(Andor.cam.acquire, 0) x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) @@ -1289,21 +1666,261 @@ def multi_edge_xanes( out_z=out_pos[2], out_r=out_pos[3], exposure_time=exposure, - chunk_size=chunk_size, + chunk_size=chunk_size, + simu=simu, + relative_move_flag=relative_move_flag, + note=note, + md=None, + sleep_time=0, + repeat_num=1, + enable_z=enable_z, + ) + elif scan_type == "3D": + if binning is None: + binning = 1 + if int(binning) not in [0, 1, 2, 3, 4]: + raise ValueError("binnng must be in [0, 1, 2, 3, 4]") + yield from mv(Andor.binning, binning) + + yield from _multi_pos_xanes_3D_xh( + eng_list, + x_list, + y_list, + z_list, + r_list, + exposure_time=exposure, + relative_rot_angle=rel_rot_ang, + rs=rs, + out_x=out_pos[0], + out_y=out_pos[1], + out_z=out_pos[2], + out_r=out_pos[3], + note=note, + simu=simu, + relative_move_flag=relative_move_flag, + rot_first_flag=1, + sleep_time=0, + repeat=1, + enable_z=enable_z, + ) + if ref_flat_scan: + motor_x_ini = zps.sx.position + motor_y_ini = zps.sy.position + motor_z_ini = zps.sz.position + motor_r_ini = zps.pi_r.position + + if relative_move_flag: + motor_x_out = ( + motor_x_ini + out_pos[0] + if not (out_pos[0] is None) + else motor_x_ini + ) + motor_y_out = ( + motor_y_ini + out_pos[1] + if not (out_pos[1] is None) + else motor_y_ini + ) + motor_z_out = ( + motor_z_ini + out_pos[2] + if not (out_pos[2] is None) + else motor_z_ini + ) + motor_r_out = ( + motor_r_ini + out_pos[3] + if not (out_pos[3] is None) + else motor_r_ini + ) + else: + motor_x_out = ( + out_pos[0] if not (out_pos[0] is None) else motor_x_ini + ) + motor_y_out = ( + out_pos[1] if not (out_pos[1] is None) else motor_y_ini + ) + motor_z_out = ( + out_pos[2] if not (out_pos[2] is None) else motor_z_ini + ) + motor_r_out = ( + out_pos[0] if not (out_pos[3] is None) else motor_r_ini + ) + + _move_sample_out_xhx( + motor_x_out, + motor_y_out, + motor_z_out, + motor_r_out, + repeat=2, + rot_first_flag=1, + enable_z=enable_z, + ) + for ii in [ + eng_list[0], + eng_list[int(eng_list.shape[0] / 2)], + eng_list[-1], + ]: + yield from move_zp_ccd(ii, move_flag=1) + my_note = note + f"_ref_flat@energy={ii}_keV" + yield from bps.sleep(1) + print(f"current energy: {ii}") + + # period = _exp_t_sanity_check(exposure, binning) + period = exposure + if period: + yield from fly_scan2( + exposure, + start_angle=None, + rel_rot_ang=rel_rot_ang, + period=period, + out_x=out_pos[0], + out_y=out_pos[1], + out_z=out_pos[2], + out_r=out_pos[3], + rs=rs, + relative_move_flag=relative_move_flag, + note=my_note, + simu=simu, + rot_first_flag=1, + enable_z=enable_z, + ) + else: + print(f"invalid binning {binning}") + _move_sample_in_xhx( + motor_x_ini, + motor_y_ini, + motor_z_ini, + motor_r_ini, + repeat=2, + trans_first_flag=1, + enable_z=enable_z, + ) + else: + print("wrong scan type") + return + + +def multi_edge_xanes2( + elements=["Ni_wl"], + scan_type="3D", + flts={"Ni_filters": [1, 2, 3]}, + exposure_time={"Ni_exp": 0.05}, + period_time={"Ni_period": 0.05}, + rel_rot_ang=185, + start_angle=None, + rs=6, + in_pos_list=[[None, None, None, None]], + out_pos=[None, None, None, None], + note="", + relative_move_flag=0, + binning=None, + bulk=False, + bulk_intgr=10, + simu=False, + sleep=0, + repeat=None, + ref_flat_scan=False, + enable_z=True, +): + yield from mv(Andor.cam.acquire, 0) + if repeat is None: + repeat = 1 + repeat = int(repeat) + if start_angle is None: + start_angle = zps.pi_r.position + if scan_type == "2D": + if binning is None: + binning = 0 + # binning = yield from _bin_cam(binning) + print(1) + + x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) + print(2) + for elem in elements: + for key in flts.keys(): + if elem.split("_")[0] == key.split("_")[0]: + flt = flts[key] + break + else: + flt = [] + for key in exposure_time.keys(): + if elem.split("_")[0] == key.split("_")[0]: + print(f"{exposure_time[key]=}") + exposure = exposure_time[key] + print(elem, exposure) + break + else: + exposure = 0.05 + print("use default exposure time 0.05s") + for key in period_time.keys(): + if elem.split("_")[0] == key.split("_")[0]: + yield from mv(Andor.cam.acquire_time, exposure) + yield from bps.sleep(2) + # yield from mv(Andor.cam.acquire_period, period_time[key]) + # period = yield from rd(Andor.cam.acquire_period) + # period = yield from _exp_t_sanity_check(period, binning=binning) + # print(elem, f"{period=}") + break + eng_list = _mk_eng_list(elem, bulk=False) + print(f"{out_pos=}") + yield from _multi_pos_xanes_2D_xh( + eng_list, + x_list, + y_list, + z_list, + r_list, + out_x=out_pos[0], + out_y=out_pos[1], + out_z=out_pos[2], + out_r=out_pos[3], + exposure_time=exposure, + chunk_size=5, simu=simu, relative_move_flag=relative_move_flag, note=note, md=None, - sleep_time=0, - repeat_num=1, - enable_z=enable_z + sleep_time=sleep, + repeat_num=repeat, + binning=binning, + flts=flt, + enable_z=enable_z, ) - elif scan_type == "3D": - if binning is None: - binning = 1 - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) + elif scan_type == "3D": + if binning is None: + binning = 1 + binning = yield from _bin_cam(binning) + + yield from mv(Andor.cam.image_mode, 0) + yield from mv(Andor.cam.num_images, 5) + yield from mv(Andor.cam.acquire, 1) + + x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) + for elem in elements: + for key in flts.keys(): + if elem.split("_")[0] == key.split("_")[0]: + flt = flts[key] + break + else: + flt = [] + for key in exposure_time.keys(): + if elem.split("_")[0] == key.split("_")[0]: + exposure = exposure_time[key] + print(elem, exposure) + break + else: + exposure = 0.05 + print("use default exposure time 0.05s") + for key in period_time.keys(): + print(f"{period_time=}\n{key=}") + if elem.split("_")[0] == key.split("_")[0]: + print(f"{exposure=}") + yield from mv(Andor.cam.acquire_time, exposure) + yield from bps.sleep(2) + print(f"{period_time[key]=}") + # yield from mv(Andor.cam.acquire_period, period_time[key]) + # period = yield from rd(Andor.cam.acquire_period) + # period = _exp_t_sanity_check(period, binning) + # print(elem, f"{period=}") + break + eng_list = _mk_eng_list(elem, bulk=False) yield from _multi_pos_xanes_3D_xh( eng_list, @@ -1312,7 +1929,10 @@ def multi_edge_xanes( z_list, r_list, exposure_time=exposure, + start_angle=start_angle, relative_rot_angle=rel_rot_ang, + # period=period, + period=exposure, rs=rs, out_x=out_pos[0], out_y=out_pos[1], @@ -1322,69 +1942,39 @@ def multi_edge_xanes( simu=simu, relative_move_flag=relative_move_flag, rot_first_flag=1, - sleep_time=0, - repeat=1, - enable_z=enable_z + sleep_time=sleep, + binning=binning, + flts=flt, + repeat=repeat, ) - if ref_flat_scan: - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - if relative_move_flag: - motor_x_out = motor_x_ini + out_pos[0] if not (out_pos[0] is None) else motor_x_ini - motor_y_out = motor_y_ini + out_pos[1] if not (out_pos[1] is None) else motor_y_ini - motor_z_out = motor_z_ini + out_pos[2] if not (out_pos[2] is None) else motor_z_ini - motor_r_out = motor_r_ini + out_pos[3] if not (out_pos[3] is None) else motor_r_ini - else: - motor_x_out = out_pos[0] if not (out_pos[0] is None) else motor_x_ini - motor_y_out = out_pos[1] if not (out_pos[1] is None) else motor_y_ini - motor_z_out = out_pos[2] if not (out_pos[2] is None) else motor_z_ini - motor_r_out = out_pos[0] if not (out_pos[3] is None) else motor_r_ini - - _move_sample_out_xhx(motor_x_out, motor_y_out, motor_z_out, motor_r_out, - repeat=2, rot_first_flag=1, enable_z=enable_z) - for ii in [eng_list[0], eng_list[int(eng_list.shape[0]/2)], eng_list[-1]]: - yield from move_zp_ccd(ii, move_flag=1) - my_note = note + f"_ref_flat@energy={ii}_keV" - yield from bps.sleep(1) - print(f"current energy: {ii}") + if bulk: + eng_list = _mk_eng_list(elem, bulk=True) + zpx = zp.x.position + apx = aper.x.position + cdx = clens.x.position + yield from mv(zp.x, -6500 + zpx) + yield from mv(clens.x, 6500 + cds) + yield from mv(aper.x, -4000 + apx) + xxanes_scan(eng_list, delay_time=0.2, intgr=bulk_intgr, note=note) + yield from mv(clens.x, cds) + yield from mv(aper.x, apx) + yield from mv(zp.x, zpx) - #period = _exp_t_sanity_check(exposure, binning) - period = exposure - if period: - yield from fly_scan2( - exposure, - start_angle=None, - rel_rot_ang=rel_rot_ang, - period=period, - out_x=out_pos[0], - out_y=out_pos[1], - out_z=out_pos[2], - out_r=out_pos[3], - rs=rs, - relative_move_flag=relative_move_flag, - note=my_note, - simu=simu, - rot_first_flag=1, - enable_z=enable_z - ) - else: - print(f"invalid binning {binning}") - _move_sample_in_xhx(motor_x_ini, motor_y_ini, motor_z_ini, motor_r_ini, - repeat=2, trans_first_flag=1, enable_z=enable_z) - else: - print("wrong scan type") - return + else: + print("wrong scan type") + # if itr != repeat - 1: + # yield from bps.sleep(sleep) + # print(f"repeat # {itr} finished") -def multi_edge_xanes2( + +def multi_edge_xanes3( elements=["Ni_wl"], scan_type="3D", - flts={"Ni_filters":[1, 2, 3]}, - exposure_time={"Ni_exp":0.05}, - period_time={"Ni_period":0.05}, + flts={"Ni_filters": [1, 2, 3]}, + exposure_time={"Ni_exp": 0.05}, + period_time={"Ni_period": 0.05}, rel_rot_ang=185, start_angle=None, rs=6, @@ -1399,14 +1989,14 @@ def multi_edge_xanes2( sleep=0, repeat=None, ref_flat_scan=False, - enable_z=True + enable_z=True, ): - yield from mv(Andor.cam.acquire, 0) + yield from abs_set(Andor.cam.acquire, 0) if repeat is None: repeat = 1 repeat = int(repeat) if start_angle is None: - start_angle=zps.pi_r.position + start_angle = zps.pi_r.position if scan_type == "2D": if binning is None: binning = 0 @@ -1424,7 +2014,7 @@ def multi_edge_xanes2( flt = [] for key in exposure_time.keys(): if elem.split("_")[0] == key.split("_")[0]: - print(f'{exposure_time[key]=}') + print(f"{exposure_time[key]=}") exposure = exposure_time[key] print(elem, exposure) break @@ -1433,45 +2023,46 @@ def multi_edge_xanes2( print("use default exposure time 0.05s") for key in period_time.keys(): if elem.split("_")[0] == key.split("_")[0]: - yield from mv(Andor.cam.acquire_time, exposure) + yield from abs_set(Andor.cam.acquire_time, exposure) yield from bps.sleep(2) # yield from mv(Andor.cam.acquire_period, period_time[key]) # period = yield from rd(Andor.cam.acquire_period) - #period = yield from _exp_t_sanity_check(period, binning=binning) + # period = yield from _exp_t_sanity_check(period, binning=binning) # print(elem, f"{period=}") break eng_list = _mk_eng_list(elem, bulk=False) - print(f'{out_pos=}') + print(f"{out_pos=}") yield from _multi_pos_xanes_2D_xh( - eng_list, - x_list, - y_list, - z_list, - r_list, - out_x=out_pos[0], - out_y=out_pos[1], - out_z=out_pos[2], - out_r=out_pos[3], - exposure_time=exposure, - chunk_size=5, - simu=simu, - relative_move_flag=relative_move_flag, - note=note, - md=None, - sleep_time=sleep, - repeat_num=repeat, - binning=binning, - flts=flt, - enable_z=enable_z - ) + eng_list, + x_list, + y_list, + z_list, + r_list, + out_x=out_pos[0], + out_y=out_pos[1], + out_z=out_pos[2], + out_r=out_pos[3], + exposure_time=exposure, + chunk_size=5, + simu=simu, + relative_move_flag=relative_move_flag, + note=note, + md=None, + sleep_time=sleep, + repeat_num=repeat, + binning=binning, + flts=flt, + enable_z=enable_z, + ) elif scan_type == "3D": if binning is None: binning = 1 binning = yield from _bin_cam(binning) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, 5) - yield from mv(Andor.cam.acquire, 1) + yield from abs_set(Andor.cam.image_mode, 0) + yield from abs_set(Andor.cam.trigger_mode, 0) + yield from abs_set(Andor.cam.num_images, 5) + yield from abs_set(Andor.cam.acquire, 1) x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) for elem in elements: @@ -1492,43 +2083,39 @@ def multi_edge_xanes2( for key in period_time.keys(): print(f"{period_time=}\n{key=}") if elem.split("_")[0] == key.split("_")[0]: - print(f'{exposure=}') - yield from mv(Andor.cam.acquire_time, exposure) + print(f"{exposure=}") + yield from abs_set(Andor.cam.acquire_time, exposure) yield from bps.sleep(2) - print(f'{period_time[key]=}') - # yield from mv(Andor.cam.acquire_period, period_time[key]) - # period = yield from rd(Andor.cam.acquire_period) - # period = _exp_t_sanity_check(period, binning) - # print(elem, f"{period=}") + print(f"{period_time[key]=}") break eng_list = _mk_eng_list(elem, bulk=False) - + yield from _multi_pos_xanes_3D_xh( - eng_list, - x_list, - y_list, - z_list, - r_list, - exposure_time=exposure, - start_angle=start_angle, - relative_rot_angle=rel_rot_ang, - # period=period, - period=exposure, - rs=rs, - out_x=out_pos[0], - out_y=out_pos[1], - out_z=out_pos[2], - out_r=out_pos[3], - note=note, - simu=simu, - relative_move_flag=relative_move_flag, - rot_first_flag=1, - sleep_time=sleep, - binning=binning, - flts=flt, - repeat=repeat, - ) - + eng_list, + x_list, + y_list, + z_list, + r_list, + exposure_time=exposure, + start_angle=start_angle, + relative_rot_angle=rel_rot_ang, + # period=period, + period=exposure, + rs=rs, + out_x=out_pos[0], + out_y=out_pos[1], + out_z=out_pos[2], + out_r=out_pos[3], + note=note, + simu=simu, + relative_move_flag=relative_move_flag, + rot_first_flag=1, + sleep_time=sleep, + binning=binning, + flts=flt, + repeat=repeat, + ) + if bulk: eng_list = _mk_eng_list(elem, bulk=True) zpx = zp.x.position @@ -1541,14 +2128,10 @@ def multi_edge_xanes2( yield from mv(clens.x, cds) yield from mv(aper.x, apx) yield from mv(zp.x, zpx) - + else: print("wrong scan type") - #if itr != repeat - 1: - # yield from bps.sleep(sleep) - #print(f"repeat # {itr} finished") - def fly_scan2( exposure_time=0.05, @@ -1570,7 +2153,7 @@ def fly_scan2( move_to_ini_pos=True, simu=False, enable_z=True, - cam=Andor + cam=Andor, ): """ Inputs: @@ -1642,7 +2225,7 @@ def fly_scan2( motor_y_out = out_y if not (out_y is None) else motor_y_ini motor_z_out = out_z if not (out_z is None) else motor_z_ini motor_r_out = out_r if not (out_r is None) else motor_r_ini - + if enable_z: motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] else: @@ -1659,7 +2242,7 @@ def fly_scan2( "ion_chamber": ic3.name, "plan_args": { "exposure_time": exposure_time, - "start_angle": start_angle if start_angle else 'None', + "start_angle": start_angle if start_angle else "None", "relative_rot_angle": rel_rot_ang, "period": period, "out_x": out_x, @@ -1694,8 +2277,11 @@ def fly_scan2( _md["hints"].setdefault("dimensions", dimensions) yield from _set_andor_param_xhx( - exposure_time=exposure_time, period=period, chunk_size=20, binning=binning, - cam=cam + exposure_time=exposure_time, + period=period, + chunk_size=20, + binning=binning, + cam=cam, ) yield from _set_rotation_speed(rs=np.abs(rs)) print("set rotation speed: {} deg/sec".format(rs)) @@ -1710,9 +2296,7 @@ def fly_inner_scan(): # close shutter, dark images: numer=chunk_size (e.g.20) print("\nshutter closed, taking dark images...") yield from _take_dark_image_xhx( - dets, motor, num=1, chunk_size=20, - stream_name="dark", simu=simu, - cam=cam + dets, motor, num=1, chunk_size=20, stream_name="dark", simu=simu, cam=cam ) # open shutter, tomo_images @@ -1749,7 +2333,7 @@ def fly_inner_scan(): stream_name="flat", simu=simu, enable_z=enable_z, - cam=cam + cam=cam, ) yield from _close_shutter_xhx(simu=simu) if move_to_ini_pos: @@ -1760,7 +2344,7 @@ def fly_inner_scan(): motor_r_ini, trans_first_flag=rot_first_flag, repeat=2, - enable_z=enable_z + enable_z=enable_z, ) uid = yield from fly_inner_scan() @@ -1792,7 +2376,7 @@ def fly_scan3( noDark=False, noFlat=False, enable_z=True, - cam=Andor + cam=Andor, ): """ Inputs: @@ -1918,8 +2502,11 @@ def fly_scan3( _md["hints"].setdefault("dimensions", dimensions) yield from _set_andor_param_xhx( - exposure_time=exposure_time, period=period, chunk_size=20, binning=binning, - cam=cam + exposure_time=exposure_time, + period=period, + chunk_size=20, + binning=binning, + cam=cam, ) yield from _set_rotation_speed(rs=np.abs(rs)) print("set rotation speed: {} deg/sec".format(rs)) @@ -1932,8 +2519,13 @@ def fly_inner_scan(): if not noDark: print("\nshutter closed, taking dark images...") yield from _take_dark_image_xhx( - dets, motor, num=1, chunk_size=20, stream_name="dark", simu=simu, - cam=cam + dets, + motor, + num=1, + chunk_size=20, + stream_name="dark", + simu=simu, + cam=cam, ) # open shutter, tomo_images @@ -1971,7 +2563,7 @@ def fly_inner_scan(): stream_name="flat", simu=simu, enable_z=enable_z, - cam=cam + cam=cam, ) if not noDark: @@ -1985,8 +2577,9 @@ def fly_inner_scan(): motor_r_ini, trans_first_flag=rot_first_flag, repeat=2, - enable_z=enable_z - ) + enable_z=enable_z, + ) + uid = yield from fly_inner_scan() yield from mv(cam.cam.image_mode, 1) yield from select_filters([]) @@ -1994,7 +2587,8 @@ def fly_inner_scan(): return uid -def rock_scan(exp_t=0.05, +def rock_scan( + exp_t=0.05, period=0.05, t_span=10, start_angle=None, @@ -2015,7 +2609,7 @@ def rock_scan(exp_t=0.05, simu=False, noDark=False, noFlat=False, - enable_z=True + enable_z=True, ): """ Inputs: @@ -2088,7 +2682,7 @@ def rock_scan(exp_t=0.05, motor_z_out = out_z if not (out_z is None) else motor_z_ini motor_r_out = out_r if not (out_r is None) else motor_r_ini - rev = int(np.ceil(t_span/(2*rel_rot_ang/rs))) + 1 + rev = int(np.ceil(t_span / (2 * rel_rot_ang / rs))) + 1 mots = [zps.pi_r] dets = [Andor] @@ -2103,8 +2697,8 @@ def rock_scan(exp_t=0.05, "start_angle": start_angle, "relative_rot_angle": rel_rot_ang, "period": period, - "time_span":t_span, - "rock_velocity":rs, + "time_span": t_span, + "rock_velocity": rs, "out_x": out_x, "out_y": out_y, "out_z": out_z, @@ -2137,8 +2731,7 @@ def rock_scan(exp_t=0.05, else: _md["hints"].setdefault("dimensions", dimensions) - yield from _set_andor_param( - exposure_time=exp_t, period=period, chunk_size=20) + yield from _set_andor_param(exposure_time=exp_t, period=period, chunk_size=20) true_period = yield from rd(Andor.cam.acquire_period) num_img = int(t_span / true_period) + 2 @@ -2156,8 +2749,7 @@ def rock_inner_scan(): if not noDark: print("\nshutter closed, taking dark images...") yield from _take_dark_image_xhx( - dets, mots, num=1, chunk_size=20, - stream_name="dark", simu=simu + dets, mots, num=1, chunk_size=20, stream_name="dark", simu=simu ) # open shutter, tomo_images @@ -2165,15 +2757,15 @@ def rock_inner_scan(): yield from _open_shutter_xhx(simu=simu) print("\nshutter opened, taking tomo images...") yield from abs_set(zps.pi_r, start_angle, wait=True) - + # modified based on trigger_and_read tgt, old_tgt = tgt_ang, start_angle - yield from trigger(Andor, group='Andor', wait=False) + yield from trigger(Andor, group="Andor", wait=False) for ii in range(rev): yield from mv(zps.pi_r, tgt) old_tgt, tgt = tgt, old_tgt - yield from bps.wait(group='Andor') - yield from bps.create('primary') + yield from bps.wait(group="Andor") + yield from bps.create("primary") yield from bps.read(Andor) yield from bps.save() @@ -2194,7 +2786,7 @@ def rock_inner_scan(): rot_first_flag=rot_first_flag, stream_name="flat", simu=simu, - enable_z=enable_z + enable_z=enable_z, ) if move_to_ini_pos: @@ -2205,7 +2797,7 @@ def rock_inner_scan(): motor_r_ini, trans_first_flag=rot_first_flag, repeat=2, - enable_z=enable_z + enable_z=enable_z, ) yield from select_filters([]) @@ -2241,7 +2833,7 @@ def mosaic_fly_scan_xh( note="", enable_z=True, repeat=1, - sleep=0 + sleep=0, ): yield from select_filters(flts) binning = yield from _bin_cam(binning) @@ -2262,7 +2854,7 @@ def mosaic_fly_scan_xh( txt3 = "\n###############################################" txt = txt1 + txt2 + txt3 print(txt) - + yield from bps.sleep(1) for ii in range(int(repeat)): @@ -2281,13 +2873,13 @@ def mosaic_fly_scan_xh( out_r=out_r, rs=rs, relative_move_flag=relative_move_flag, - note=f'{note}; pos_y: {y}, pos_z: {z}, pos_x: {x}; iteration: {ii}', + note=f"{note}; pos_y: {y}, pos_z: {z}, pos_x: {x}; iteration: {ii}", simu=simu, rot_first_flag=True, - enable_z=enable_z + enable_z=enable_z, ) if ii < int(repeat) - 1: - print(f'sleeping for {sleep} seconds before iteration #{ii+1}') + print(f"sleeping for {sleep} seconds before iteration #{ii+1}") yield from bps.sleep(sleep) yield from mv(zps.sx, x_ini, zps.sy, y_ini, zps.sz, z_ini, zps.pi_r, r_ini) yield from select_filters([]) @@ -2710,14 +3302,14 @@ def mosaic_2D_xh( scan_x_flag=1, flts=[], md=None, - enable_z=True + enable_z=True, ): yield from mv(Andor.cam.acquire, 0) - #zp_z_pos = zp.z.position - #DetU_z_pos = DetU.z.position - #M = (DetU_z_pos / zp_z_pos - 1) * 10.0 + # zp_z_pos = zp.z.position + # DetU_z_pos = DetU.z.position + # M = (DetU_z_pos / zp_z_pos - 1) * 10.0 M = GLOBAL_MAG - pxl = 6.5 / M * (2560./img_sizeX) + pxl = 6.5 / M * (2560.0 / img_sizeX) global ZONE_PLATE if enable_z: @@ -2804,7 +3396,9 @@ def mosaic_2D_inner(): yield from select_filters(flts) # take dark image print("take 5 dark image") - yield from _take_dark_image_xhx(dets, motor, num=5, stream_name="dark", simu=simu) + yield from _take_dark_image_xhx( + dets, motor, num=5, stream_name="dark", simu=simu + ) print("open shutter ...") yield from _open_shutter_xhx(simu) @@ -2838,7 +3432,7 @@ def mosaic_2D_inner(): stream_name="flat", simu=simu, rot_first_flag=rot_first_flag, - enable_z=enable_z + enable_z=enable_z, ) # move sample in @@ -2849,7 +3443,7 @@ def mosaic_2D_inner(): motor_r_ini, repeat=1, trans_first_flag=1 - rot_first_flag, - enable_z=enable_z + enable_z=enable_z, ) if len(flts): yield from select_filters(flts) @@ -2877,7 +3471,6 @@ def dummy_scan( rot_back_velo=30, repeat=1, ): - yield from mv(Andor.cam.acquire, 0) motor_x_ini = zps.sx.position motor_y_ini = zps.sy.position @@ -2898,7 +3491,7 @@ def dummy_scan( motor_z_out = out_z if not (out_z is None) else motor_z_ini motor_r_out = out_r if not (out_r is None) else motor_r_ini - #motors = [zps.sx, zps.sy, zps.sz, zps.pi_r] + # motors = [zps.sx, zps.sy, zps.sz, zps.pi_r] motors = [zps.sx, zps.sy, zps.pi_r] dets = [Andor, ic3] @@ -2932,12 +3525,12 @@ def fly_inner_scan(): yield from bps.sleep(1) yield from abs_set(zps.sx, motor_x_out, wait=True) yield from abs_set(zps.sy, motor_y_out, wait=True) - #yield from abs_set(zps.sz, motor_z_out, wait=True) + # yield from abs_set(zps.sz, motor_z_out, wait=True) yield from abs_set(zps.pi_r, motor_r_out, wait=True) yield from abs_set(zps.sx, motor_x_ini, wait=True) yield from abs_set(zps.sy, motor_y_ini, wait=True) - #yield from abs_set(zps.sz, motor_z_ini, wait=True) + # yield from abs_set(zps.sz, motor_z_ini, wait=True) yield from abs_set(zps.pi_r, motor_r_ini, wait=True) for ii in range(repeat): @@ -2969,7 +3562,7 @@ def radiographic_record( if int(binning) not in [0, 1, 2, 3, 4]: raise ValueError("binnng must be in [0, 1, 2, 3, 4]") yield from mv(Andor.binning, binning) - + yield from mv(Andor.cam.acquire, 0) motor_x_ini = zps.sx.position motor_y_ini = zps.sy.position @@ -3080,44 +3673,46 @@ def multi_pos_2D_and_3D_xanes( note="", relative_move_flag=0, simu=False, - enable_z=True + enable_z=True, ): for ii in range(repeat_num): for elem in elements: - en = elem.split('_')[0] - yield from multi_edge_xanes(elements=[en+'_101'], - scan_type="2D", - flts={en+'_filters':[]}, - exposure_time=exposure_time_2D, - rel_rot_ang=rel_rot_ang, - rs=rs, - in_pos_list=sam_in_pos_list_2D[en+'_2D_in_pos_list'], - out_pos=sam_out_pos_list_2D[en+'_2D_out_pos_list'], - chunk_size=chunk_size, - note=note+'2D_xanes'+elem, - relative_move_flag=relative_move_flag, - binning=None, - simu=False, - ref_flat_scan=False, - enable_z=enable_z - ) - yield from multi_edge_xanes(elements=[elem], - scan_type="3D", - flts=flts, - exposure_time=exposure_time_3D, - rel_rot_ang=rel_rot_ang, - rs=rs, - in_pos_list=sam_in_pos_list_3D[en+'_3D_in_pos_list'], - out_pos=sam_out_pos_list_3D[en+'_3D_out_pos_list'], - chunk_size=chunk_size, - note=note+'3D_xanes'+elem, - relative_move_flag=relative_move_flag, - binning=None, - simu=False, - ref_flat_scan=False, - enable_z=enable_z - ) - if ii < repeat_num-1: + en = elem.split("_")[0] + yield from multi_edge_xanes( + elements=[en + "_101"], + scan_type="2D", + flts={en + "_filters": []}, + exposure_time=exposure_time_2D, + rel_rot_ang=rel_rot_ang, + rs=rs, + in_pos_list=sam_in_pos_list_2D[en + "_2D_in_pos_list"], + out_pos=sam_out_pos_list_2D[en + "_2D_out_pos_list"], + chunk_size=chunk_size, + note=note + "2D_xanes" + elem, + relative_move_flag=relative_move_flag, + binning=None, + simu=False, + ref_flat_scan=False, + enable_z=enable_z, + ) + yield from multi_edge_xanes( + elements=[elem], + scan_type="3D", + flts=flts, + exposure_time=exposure_time_3D, + rel_rot_ang=rel_rot_ang, + rs=rs, + in_pos_list=sam_in_pos_list_3D[en + "_3D_in_pos_list"], + out_pos=sam_out_pos_list_3D[en + "_3D_out_pos_list"], + chunk_size=chunk_size, + note=note + "3D_xanes" + elem, + relative_move_flag=relative_move_flag, + binning=None, + simu=False, + ref_flat_scan=False, + enable_z=enable_z, + ) + if ii < repeat_num - 1: yield from bps.sleep(sleep_time) @@ -3255,15 +3850,18 @@ def cal_ccd_zp_xh(eng, mag, zp_cfg=None): f: zp focal length in mm """ if zp_cfg is None: - zp_cfg={'D': 244, 'dr': 30} # 'D': zp diameter in um; 'dr': outmost zone width in nm - #wl = 12.39847/eng/10 # in nm - wl = 6.6261e-34 * 299792458/(1.602176565e-19 * eng) * 1e6 # in nm - na = wl/2/zp_cfg['dr'] # numberical apeture in rad - dof = wl/(na**2)/1000 # depth of focus in um - f = zp_cfg['dr'] * zp_cfg['D'] / wl / 1000 # focal length in mm - p = f * (mag + 1) / mag # object distance from zone plate in mm - q = p * mag # detector distance from zone plate in mm - det_pos = p + q # ccd detector distance from the sample in mm + zp_cfg = { + "D": 244, + "dr": 30, + } # 'D': zp diameter in um; 'dr': outmost zone width in nm + # wl = 12.39847/eng/10 # in nm + wl = 6.6261e-34 * 299792458 / (1.602176565e-19 * eng) * 1e6 # in nm + na = wl / 2 / zp_cfg["dr"] # numberical apeture in rad + dof = wl / (na**2) / 1000 # depth of focus in um + f = zp_cfg["dr"] * zp_cfg["D"] / wl / 1000 # focal length in mm + p = f * (mag + 1) / mag # object distance from zone plate in mm + q = p * mag # detector distance from zone plate in mm + det_pos = p + q # ccd detector distance from the sample in mm return p, det_pos, wl, na, f @@ -3560,7 +4158,6 @@ def damon_scan( sleep_time=1, note="", ): - export_pdf(1) insert_text('start "damon_scan"') x_list = np.array(x_list) @@ -3835,7 +4432,6 @@ def user_fly_only( bkg_scan_id=0, md=None, ): - global ZONE_PLATE motor_x_ini = zps.sx.position motor_y_ini = zps.sy.position @@ -4253,7 +4849,6 @@ def trim_points_to_polygon(xyz_list, poly): return xyz_list_out - def qingchao_scan( eng_list, x_list1, From 1a76771888cc21f8f6b1fdb49de2de86cb3ea03e Mon Sep 17 00:00:00 2001 From: FXI Operator Date: Sat, 10 Jun 2023 13:57:48 -0400 Subject: [PATCH 4/9] add sleep time to tomo_zfly/tomo_grid_zfly; multi_edge_xanes_zebra works --- startup/18-zebra.py | 9 +- startup/46-zebra_flyer.py | 274 ++++++++++++++++++++++++++++---------- startup/98-user_scan.py | 26 +++- 3 files changed, 231 insertions(+), 78 deletions(-) diff --git a/startup/18-zebra.py b/startup/18-zebra.py index 7cdefd8..58c8d52 100644 --- a/startup/18-zebra.py +++ b/startup/18-zebra.py @@ -1064,7 +1064,7 @@ def cal_zebra_pc_params(cls, scn_cfg): @staticmethod def compose_scn_cfg( - scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, tacc, num_swing + scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, tacc, mb_vel, num_swing ): scn_cfg = {} scn_cfg["scn_mode"] = scn_mode @@ -1075,6 +1075,7 @@ def compose_scn_cfg( scn_cfg["ang_e"] = ang_e scn_cfg["vel"] = vel scn_cfg["tacc"] = tacc + scn_cfg["mb_vel"] = mb_vel scn_cfg["num_swing"] = num_swing return scn_cfg @@ -1086,11 +1087,11 @@ def prime_det(det): if det.cam.image_mode.get() != 0: yield from abs_set(det.cam.image_mode, 0, wait=True) yield from abs_set(det.cam.num_images, 5, wait=True) - yield from abs_set(det.cam.acquire, 1, wait=True) + yield from abs_set(det.cam.acquire, 1, wait=False) @staticmethod def bin_det(det, bin_fac): - yield from abs_set(det.cam.acquire, 0, wait=True) + yield from abs_set(det.cam.acquire, 0, wait=False) if bin_fac is None: bin_fac = 0 if int(bin_fac) not in [0, 1, 2, 3, 4]: @@ -1140,7 +1141,7 @@ def init_mot_r(scn_cfg): cur_pos = scn_cfg["ang_s"] // 360 * 360 + cur_pos % 360 zps.pi_r.set_current_position(cur_pos) yield from abs_set(zps.pi_r.acceleration, 1, wait=True) - yield from abs_set(zps.pi_r.velocity, 30, wait=True) + yield from abs_set(zps.pi_r.velocity, scn_cfg["mb_vel"], wait=True) yield from abs_set(zps.pi_r, scn_cfg["ang_s"], wait=True) @staticmethod diff --git a/startup/46-zebra_flyer.py b/startup/46-zebra_flyer.py index 54b1bb0..7b3612b 100644 --- a/startup/46-zebra_flyer.py +++ b/startup/46-zebra_flyer.py @@ -22,6 +22,7 @@ def tomo_zfly( note="", md=None, simu=False, + sleep=0, cam=Andor, flyer=tomo_flyer, ): @@ -58,14 +59,27 @@ def tomo_zfly( _type_: _description_ """ global ZONE_PLATE - # mots = [zps.sx, zps.sy, zps.sz, zps.pi_r] + sleep_plan = _schedule_sleep(sleep, num_swing) + if not sleep_plan: + print(f"A wrong sleep pattern {sleep=} and {num_swing=} breaks the scan. Quit") + return + mots = [zps.sx, zps.sy, zps.sz] flyer.detectors = [ cam, ] flyer.scn_mode = flyer.scn_modes[scn_mode] scn_cfg = FXITomoFlyer.compose_scn_cfg( - scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, acc_t, num_swing + scn_mode, + exp_t, + acq_p, + bin_fac, + ang_s, + ang_e, + vel, + acc_t, + rot_back_velo, + num_swing, ) scn_cfg, pc_cfg = yield from flyer.preset_flyer(scn_cfg) (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() @@ -83,16 +97,18 @@ def tomo_zfly( "start_angle": scn_cfg["ang_s"], "end_angle": scn_cfg["ang_e"], "acquisition_period": scn_cfg["acq_p"], + "slew_speed": scn_cfg["vel"], + "mv_back_vel": scn_cfg["mb_vel"], + "acceleration": scn_cfg["tacc"], "number_of_swings": scn_cfg["num_swing"], "out_x": mot_x_out, "out_y": mot_y_out, "out_z": mot_z_out, "out_r": mot_r_out, - "slew_speed": scn_cfg["vel"], - "rel_out_flag": rel_out_flag, "filters": ["filter{}".format(t) for t in flts] if flts else "None", "binning": 0 if scn_cfg["bin_fac"] is None else scn_cfg["bin_fac"], "note": note if note else "None", + "sleep": sleep, "zone_plate": ZONE_PLATE, }, "plan_name": "tomo_zfly", @@ -224,6 +240,9 @@ def inner_fly_plan(): int(scn_cfg["rot_dir"]) ] yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) + + if ii < scn_cfg["num_swing"] - 1: + bps.sleep(sleep_plan[ii]) scn_cfg["ang_s"] = r_ini yield from FXITomoFlyer.init_mot_r(scn_cfg) @@ -401,6 +420,7 @@ def tomo_grid_zfly( bin_fac=None, note="", md=None, + sleep=0, simu=False, cam=Andor, flyer=tomo_flyer, @@ -441,14 +461,26 @@ def tomo_grid_zfly( _type_: _description_ """ global ZONE_PLATE - # mots = [zps.sx, zps.sy, zps.sz, zps.pi_r] + sleep_plan = _schedule_sleep(sleep, num_swing) + if not sleep_plan: + print(f"A wrong sleep pattern {sleep=} and {num_swing=} breaks the scan. Quit") + return mots = [zps.sx, zps.sy, zps.sz] flyer.detectors = [ cam, ] flyer.scn_mode = flyer.scn_modes[scn_mode] scn_cfg = FXITomoFlyer.compose_scn_cfg( - scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, acc_t, num_swing + scn_mode, + exp_t, + acq_p, + bin_fac, + ang_s, + ang_e, + vel, + acc_t, + rot_back_velo, + num_swing, ) scn_cfg, pc_cfg = yield from flyer.preset_flyer(scn_cfg) (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() @@ -466,9 +498,10 @@ def tomo_grid_zfly( "start_angle": scn_cfg["ang_s"], "end_angle": scn_cfg["ang_e"], "acquisition_period": scn_cfg["acq_p"], - "number_of_swings": scn_cfg["num_swing"], "slew_speed": scn_cfg["vel"], - "acceleration": scn_cfg["acc_t"], + "mv_back_vel": scn_cfg["mb_vel"], + "acceleration": scn_cfg["tacc"], + "number_of_swings": scn_cfg["num_swing"], "grid_mots": "none" if not grid_nodes else [mot.name for mot in grid_nodes["mots"]], @@ -495,81 +528,163 @@ def tomo_grid_zfly( } _md.update(md or {}) - all_mots = list(set(list(mots) + list(grid_nodes["mots"]))) + if grid_nodes: + all_mots = list(set(list(mots) + list(grid_nodes["mots"]))) + else: + all_mots = list(list(mots)) print("preset done") # @stage_decorator(list(mots)) @run_decorator(md=_md) def inner_fly_plan(): yield from select_filters(flts) - for jj in grid_nodes["pos"] or range(1): - yield from mv(*zip(grid_nodes["mots"], jj)) + for jj in grid_nodes["pos"] if grid_nodes else range(1): + print(1) + if grid_nodes: + yield from mv(*zip(grid_nodes["mots"], jj)) for mot in all_mots: mot.stage() - - for ii in range(scn_cfg["num_swing"]): - if (ii == 0) or (scn_mode == 1): + print(2) + + # for ii in range(scn_cfg["num_swing"]): + # if (ii == 0) or (scn_mode == 1): + # yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + # yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + # for d in flyer.detectors: + # try: + # d.stage() + # except: + # d.unstage() + # d.stage() + + # yield from _open_shutter_xhx(simu) + # st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) + # st.wait(timeout=10) + # yield from abs_set( + # flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True + # ) + + # det_stream = short_uid("dets") + # for d in flyer.detectors: + # yield from bps.trigger(d, group=det_stream) + # wait(det_stream) + + # yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + # t0 = ttime.monotonic() + # yield from abs_set( + # zps.pi_r, + # scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + # wait=True, + # ) + + # t1 = ttime.monotonic() + # while int(flyer.encoder.pc.gated.get()): + # if ttime.monotonic() - t1 > 60: + # print("Scan finished abnormally. Quit!") + # return + # yield from bps.sleep(flyer._staging_delay) + # print(ttime.time()) + # print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") + # st = yield from complete(flyer, wait=True) + # st.wait(timeout=10) + # yield from collect(flyer) + # for d in flyer.detectors: + # try: + # d.unstage() + # except: + # print(f"Cannot unstage detector {d.name}") + # return None + # if (scn_cfg["num_swing"] > 1) and ( + # flyer.scn_mode != "snaked: single file" + # ): + # (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( + # scn_cfg["ang_e"], + # scn_cfg["ang_s"], + # ) + # scn_cfg["rot_dir"] *= -1 + # pc_cfg[flyer.scn_mode]["gate_start"] = scn_cfg["ang_s"] + # pc_cfg[scn_cfg["scn_mode"]]["dir"] = flyer.pc_trig_dir[ + # int(scn_cfg["rot_dir"]) + # ] + # yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) + # else: + # yield from FXITomoFlyer.init_mot_r(scn_cfg) + if flyer.scn_mode == "snaked: single file": + print( + "Scan mode 'snaked: single file' is not currently supported. Quit!" + ) + yield from select_filters([]) + yield from _move_sample( + x_ini, + y_ini, + z_ini, + r_ini, + repeat=2, + ) + return + else: + for ii in range(scn_cfg["num_swing"]): yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + yield from _open_shutter_xhx(simu) for d in flyer.detectors: try: d.stage() except: d.unstage() d.stage() + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) + st.wait(timeout=10) + yield from abs_set( + flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True + ) - yield from _open_shutter_xhx(simu) - st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) - st.wait(timeout=10) - yield from abs_set( - flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True - ) - - det_stream = short_uid("dets") - for d in flyer.detectors: - yield from bps.trigger(d, group=det_stream) - wait(det_stream) - - yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) - t0 = ttime.monotonic() - yield from abs_set( - zps.pi_r, - scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], - wait=True, - ) - - t1 = ttime.monotonic() - while int(flyer.encoder.pc.gated.get()): - if ttime.monotonic() - t1 > 60: - print("Scan finished abnormally. Quit!") - return - yield from bps.sleep(flyer._staging_delay) - print(ttime.time()) - print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") - st = yield from complete(flyer, wait=True) - st.wait(timeout=10) - yield from collect(flyer) - for d in flyer.detectors: - try: - d.unstage() - except: - print(f"Cannot unstage detector {d.name}") - return None - if (scn_cfg["num_swing"] > 1) and ( - flyer.scn_mode != "snaked: single file" - ): - (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( - scn_cfg["ang_e"], - scn_cfg["ang_s"], + det_stream = short_uid("dets") + for d in flyer.detectors: + yield from bps.trigger(d, group=det_stream) + wait(det_stream) + + yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + t0 = ttime.monotonic() + yield from abs_set( + zps.pi_r, + scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + wait=True, ) - scn_cfg["rot_dir"] *= -1 - pc_cfg[flyer.scn_mode]["gate_start"] = scn_cfg["ang_s"] - pc_cfg[scn_cfg["scn_mode"]]["dir"] = flyer.pc_trig_dir[ - int(scn_cfg["rot_dir"]) - ] - yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) - else: - yield from FXITomoFlyer.init_mot_r(scn_cfg) + + t1 = ttime.monotonic() + while int(flyer.encoder.pc.gated.get()): + if ttime.monotonic() - t1 > 60: + print("Scan finished abnormally. Quit!") + return + yield from bps.sleep(flyer._staging_delay) + print(ttime.time()) + print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") + st = yield from complete(flyer, wait=True) + st.wait(timeout=10) + yield from collect(flyer) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + + if scn_cfg["num_swing"] > 1: + (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( + scn_cfg["ang_e"], + scn_cfg["ang_s"], + ) + scn_cfg["rot_dir"] *= -1 + # breakpoint() + pc_cfg[flyer.scn_mode]["gate_start"] = scn_cfg["ang_s"] + pc_cfg[flyer.scn_mode]["dir"] = flyer.pc_trig_dir[ + int(scn_cfg["rot_dir"]) + ] + yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) + + if ii < scn_cfg["num_swing"] - 1: + bps.sleep(sleep_plan[ii]) for mot in all_mots: mot.unstage() @@ -612,6 +727,29 @@ def inner_fly_plan(): yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") yield from select_filters([]) - uid = yield from inner_fly_plan() - print("scan finished") - return uid + uid = yield from inner_fly_plan() + print("scan finished") + return uid + + +def _schedule_sleep(sleep, num_scan): + sleep_plan = {} + for ii in range(1, num_scan - 1): + if isinstance(sleep, list): + if len(sleep) != num_scan - 2: + print( + f"The list of sleep time has length {len(sleep)} that is inconsistent \ + with the number of scans {num_scan}. \ + The length of sleep time should be {num_scan - 2}" + ) + return False + else: + sleep_plan[ii] = sleep[ii] + return sleep_plan + elif isinstance(sleep, int) or isinstance(sleep, float): + sleep_plan[ii] = sleep + return sleep_plan + else: + print(f"Unrecognized sleep pattern {sleep}. Quit.") + return False + diff --git a/startup/98-user_scan.py b/startup/98-user_scan.py index 3a17eef..23f4a72 100644 --- a/startup/98-user_scan.py +++ b/startup/98-user_scan.py @@ -779,6 +779,7 @@ def _xanes_3D_zebra_xh( ): for eng in eng_list: yield from move_zp_ccd(eng, move_flag=1) + print(121) my_note = f"{note}@energy={eng}" yield from bps.sleep(1) print(f"current energy: {eng}") @@ -836,6 +837,7 @@ def _multi_pos_xanes_3D_zebra_xh( flyer=tomo_flyer, ): yield from select_filters(flts) + print(11) n = len(x_list) for rep in range(repeat): for i in range(n): @@ -854,8 +856,9 @@ def _multi_pos_xanes_3D_zebra_xh( r_list[i], repeat=2, trans_first_flag=1, - enable_z=enable_z, + enable_z=True, ) + print(12) yield from _xanes_3D_zebra_xh( eng_list, exp_t=exp_t, @@ -1480,16 +1483,17 @@ def multi_edge_xanes_zebra( cam=Andor, flyer=tomo_flyer ): + print(-2) yield from mv(cam.cam.acquire, 0) if repeat is None: repeat = 1 repeat = int(repeat) + print(-1) if scan_type == "2D": if bin_fac is None: bin_fac = 0 # binning = yield from _bin_cam(binning) - print(1) x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) for elem in elems: @@ -1542,15 +1546,21 @@ def multi_edge_xanes_zebra( enable_z=True, ) elif scan_type == "3D": + print(-0.9) if bin_fac is None: bin_fac = 1 bin_fac = yield from _bin_cam(bin_fac) + print(-0.8) - yield from abs_set(Andor.cam.image_mode, 0, wait=True) - yield from abs_set(Andor.cam.num_images, 5, wait=True) - yield from abs_set(Andor.cam.acquire, 1, wait=True) + set_and_wait(Andor.cam.image_mode, 0) + print(-0.89) + set_and_wait(Andor.cam.num_images, 5) + print(-0.88) + yield from abs_set(Andor.cam.acquire, 1, wait=False) + print(-0.7) x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) + print(0) for elem in elems: for key in flts.keys(): if elem.split("_")[0] == key.split("_")[0]: @@ -1558,6 +1568,7 @@ def multi_edge_xanes_zebra( break else: flt = [] + print(0.1) for key in exp_t.keys(): if elem.split("_")[0] == key.split("_")[0]: exposure = exp_t[key] @@ -1566,12 +1577,15 @@ def multi_edge_xanes_zebra( else: exposure = 0.05 print("use default exposure time 0.05s") + print(0.2) for key in acq_p.keys(): if elem.split("_")[0] == key.split("_")[0]: acquire_period = acq_p[key] break + print(0.3) eng_list = _mk_eng_list(elem, bulk=False) + print(1) yield from _multi_pos_xanes_3D_zebra_xh( eng_list, x_list, @@ -1593,7 +1607,7 @@ def multi_edge_xanes_zebra( note=note, sleep_time=sleep, bin_fac=bin_fac, - flts=flts, + flts=flt, repeat=1, ref_flat_scan=ref_flat_scan, cam=cam, From 727a8398977c86b82c53be0b3211705defa091fc Mon Sep 17 00:00:00 2001 From: FXI Operator Date: Sat, 10 Jun 2023 15:54:33 -0400 Subject: [PATCH 5/9] remove commented and print lines in 18, 46, 98 --- startup/18-zebra.py | 14 --- startup/46-zebra_flyer.py | 183 +------------------------------------- startup/98-user_scan.py | 19 +--- 3 files changed, 5 insertions(+), 211 deletions(-) diff --git a/startup/18-zebra.py b/startup/18-zebra.py index 58c8d52..a4a6c2d 100644 --- a/startup/18-zebra.py +++ b/startup/18-zebra.py @@ -403,8 +403,6 @@ def preset_zebra(self, pc_cfg={}): yield from abs_set( self._encoder.pc.trig_source, 0, wait=True ) # 0 = Soft, 1 = External - ############### PC Pulse - # yield from abs_set(self._encoder.pc.pulse_width, self.dft_pulse_wid[self.tspre], wait=True) ############### PULSE -- set unibliz trigger to 'external exposure' if self.use_shutter: @@ -492,7 +490,6 @@ def make_filename(self): return filename, read_path, write_path def stage(self): - # self.set_stage_sigs() self._stage_with_delay() super.stage() @@ -845,14 +842,6 @@ def set_pc_step_for_scan(self, scn_mode, pc_cfg): self.encoder.pc.gate_start, pc_cfg[scn_mode]["gate_start"], wait=True ) - # @staticmethod - # def set_wait(field, val, wait=0.01): - # set_and_wait(field, val, poll_time=wait) - # # while field.get() != val: - # # field.put(val, timeout=10) - # # ttime.sleep(0.01) - # # ttime.sleep(wait) - @classmethod def cal_cam_rot_params(cls, det, scn_cfg): """_summary_ @@ -1032,7 +1021,6 @@ def cal_zebra_pc_params(cls, scn_cfg): + 1 ) ) - # pc_cfg["snaked: multiple files"]["gate_start"] = scn_cfg["ang_s"] pc_cfg["snaked: multiple files"]["gate_width"] = ( abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) + pc_cfg["snaked: multiple files"]["pulse_step"] @@ -1052,8 +1040,6 @@ def cal_zebra_pc_params(cls, scn_cfg): ) * scn_cfg["num_swing"] ) - # pc_cfg["snaked: single file"]["gate_start"] = scn_cfg["ang_s"] - # pc_cfg["snaked: single file"]["gate_width"] = else: print("Unrecognized scan mode. Quit") return None diff --git a/startup/46-zebra_flyer.py b/startup/46-zebra_flyer.py index 7b3612b..4a0e5bb 100644 --- a/startup/46-zebra_flyer.py +++ b/startup/46-zebra_flyer.py @@ -140,7 +140,6 @@ def inner_fly_plan(): d.stage() st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) st.wait(timeout=10) - # yield from abs_set(flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True) det_stream = short_uid("dets") for d in flyer.detectors: @@ -234,7 +233,6 @@ def inner_fly_plan(): scn_cfg["ang_s"], ) scn_cfg["rot_dir"] *= -1 - # breakpoint() pc_cfg[flyer.scn_mode]["gate_start"] = scn_cfg["ang_s"] pc_cfg[flyer.scn_mode]["dir"] = flyer.pc_trig_dir[ int(scn_cfg["rot_dir"]) @@ -242,6 +240,7 @@ def inner_fly_plan(): yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) if ii < scn_cfg["num_swing"] - 1: + print(f"Sleeping {sleep_plan[ii]} seconds before {ii}th scan ...") bps.sleep(sleep_plan[ii]) scn_cfg["ang_s"] = r_ini @@ -285,116 +284,6 @@ def inner_fly_plan(): yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") yield from select_filters([]) - # def inner_fly_plan(): - # yield from select_filters(flts) - # for ii in range(scn_cfg["num_swing"]): - # yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) - # print(5) - # yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) - # print(6) - # yield from _open_shutter_xhx(simu) - # print(7) - # for d in flyer.detectors: - # try: - # d.stage() - # except: - # d.unstage() - # d.stage() - # st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) - # st.wait(timeout=10) - # yield from abs_set(flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True) - - # det_stream = short_uid("dets") - # for d in flyer.detectors: - # yield from bps.trigger(d, group=det_stream) - # wait(det_stream) - - # yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) - # t0 = ttime.monotonic() - # yield from abs_set( - # zps.pi_r, - # scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], - # wait=True, - # ) - - # t1 = ttime.monotonic() - # while int(flyer.encoder.pc.gated.get()): - # if ttime.monotonic() - t1 > 60: - # print("Scan finished abnormally. Quit!") - # return - # yield from bps.sleep(flyer._staging_delay) - # print(ttime.time()) - # print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") - # st = yield from complete(flyer, wait=True) - # st.wait(timeout=10) - # yield from collect(flyer) - # for d in flyer.detectors: - # try: - # d.unstage() - # except: - # print(f"Cannot unstage detector {d.name}") - # return None - - # if flyer.scn_mode == "snaked: single file": - # (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( - # scn_cfg["ang_e"], - # scn_cfg["ang_s"], - # ) - # scn_cfg["rot_dir"] *= -1 - # elif scn_cfg["num_swing"] > 1: - # (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( - # scn_cfg["ang_e"], - # scn_cfg["ang_s"], - # ) - # scn_cfg["rot_dir"] *= -1 - # # breakpoint() - # pc_cfg[flyer.scn_mode]["gate_start"] = scn_cfg["ang_s"] - # pc_cfg[flyer.scn_mode]["dir"] = flyer.pc_trig_dir[ - # int(scn_cfg["rot_dir"]) - # ] - # yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) - - # scn_cfg["ang_s"] = r_ini - # yield from FXITomoFlyer.init_mot_r(scn_cfg) - - # yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") - # yield from _take_ref_image( - # [cam], - # mots_pos={ - # "x": mot_x_out, - # "y": mot_y_out, - # "z": mot_z_out, - # "r": mot_r_out, - # }, - # num=1, - # chunk_size=10, - # stream_name="flat", - # simu=simu, - # ) - # yield from _take_ref_image( - # [cam], - # mots_pos={}, - # num=1, - # chunk_size=10, - # stream_name="dark", - # simu=simu, - # ) - # for d in flyer.detectors: - # try: - # d.unstage() - # except: - # print(f"Cannot unstage detector {d.name}") - # return None - # yield from _move_sample( - # x_ini, - # y_ini, - # z_ini, - # r_ini, - # repeat=2, - # ) - # yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") - # yield from select_filters([]) - uid = yield from inner_fly_plan() print("scan finished") return uid @@ -534,81 +423,15 @@ def tomo_grid_zfly( all_mots = list(list(mots)) print("preset done") - # @stage_decorator(list(mots)) @run_decorator(md=_md) def inner_fly_plan(): yield from select_filters(flts) for jj in grid_nodes["pos"] if grid_nodes else range(1): - print(1) if grid_nodes: yield from mv(*zip(grid_nodes["mots"], jj)) for mot in all_mots: mot.stage() - print(2) - - # for ii in range(scn_cfg["num_swing"]): - # if (ii == 0) or (scn_mode == 1): - # yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) - # yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) - # for d in flyer.detectors: - # try: - # d.stage() - # except: - # d.unstage() - # d.stage() - - # yield from _open_shutter_xhx(simu) - # st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) - # st.wait(timeout=10) - # yield from abs_set( - # flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True - # ) - - # det_stream = short_uid("dets") - # for d in flyer.detectors: - # yield from bps.trigger(d, group=det_stream) - # wait(det_stream) - - # yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) - # t0 = ttime.monotonic() - # yield from abs_set( - # zps.pi_r, - # scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], - # wait=True, - # ) - - # t1 = ttime.monotonic() - # while int(flyer.encoder.pc.gated.get()): - # if ttime.monotonic() - t1 > 60: - # print("Scan finished abnormally. Quit!") - # return - # yield from bps.sleep(flyer._staging_delay) - # print(ttime.time()) - # print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") - # st = yield from complete(flyer, wait=True) - # st.wait(timeout=10) - # yield from collect(flyer) - # for d in flyer.detectors: - # try: - # d.unstage() - # except: - # print(f"Cannot unstage detector {d.name}") - # return None - # if (scn_cfg["num_swing"] > 1) and ( - # flyer.scn_mode != "snaked: single file" - # ): - # (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( - # scn_cfg["ang_e"], - # scn_cfg["ang_s"], - # ) - # scn_cfg["rot_dir"] *= -1 - # pc_cfg[flyer.scn_mode]["gate_start"] = scn_cfg["ang_s"] - # pc_cfg[scn_cfg["scn_mode"]]["dir"] = flyer.pc_trig_dir[ - # int(scn_cfg["rot_dir"]) - # ] - # yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) - # else: - # yield from FXITomoFlyer.init_mot_r(scn_cfg) + if flyer.scn_mode == "snaked: single file": print( "Scan mode 'snaked: single file' is not currently supported. Quit!" @@ -676,7 +499,6 @@ def inner_fly_plan(): scn_cfg["ang_s"], ) scn_cfg["rot_dir"] *= -1 - # breakpoint() pc_cfg[flyer.scn_mode]["gate_start"] = scn_cfg["ang_s"] pc_cfg[flyer.scn_mode]["dir"] = flyer.pc_trig_dir[ int(scn_cfg["rot_dir"]) @@ -684,6 +506,7 @@ def inner_fly_plan(): yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) if ii < scn_cfg["num_swing"] - 1: + print(f"Sleeping {sleep_plan[ii]} seconds before {ii}th scan ...") bps.sleep(sleep_plan[ii]) for mot in all_mots: diff --git a/startup/98-user_scan.py b/startup/98-user_scan.py index 23f4a72..d64f882 100644 --- a/startup/98-user_scan.py +++ b/startup/98-user_scan.py @@ -779,7 +779,6 @@ def _xanes_3D_zebra_xh( ): for eng in eng_list: yield from move_zp_ccd(eng, move_flag=1) - print(121) my_note = f"{note}@energy={eng}" yield from bps.sleep(1) print(f"current energy: {eng}") @@ -837,7 +836,6 @@ def _multi_pos_xanes_3D_zebra_xh( flyer=tomo_flyer, ): yield from select_filters(flts) - print(11) n = len(x_list) for rep in range(repeat): for i in range(n): @@ -858,7 +856,6 @@ def _multi_pos_xanes_3D_zebra_xh( trans_first_flag=1, enable_z=True, ) - print(12) yield from _xanes_3D_zebra_xh( eng_list, exp_t=exp_t, @@ -1483,12 +1480,10 @@ def multi_edge_xanes_zebra( cam=Andor, flyer=tomo_flyer ): - print(-2) yield from mv(cam.cam.acquire, 0) if repeat is None: repeat = 1 repeat = int(repeat) - print(-1) if scan_type == "2D": if bin_fac is None: @@ -1546,21 +1541,15 @@ def multi_edge_xanes_zebra( enable_z=True, ) elif scan_type == "3D": - print(-0.9) if bin_fac is None: bin_fac = 1 bin_fac = yield from _bin_cam(bin_fac) - print(-0.8) - set_and_wait(Andor.cam.image_mode, 0) - print(-0.89) - set_and_wait(Andor.cam.num_images, 5) - print(-0.88) + yield from abs_set(Andor.cam.image_mode, 0, wait=True) + yield from abs_set(Andor.cam.num_images, 5, wait=True) yield from abs_set(Andor.cam.acquire, 1, wait=False) - print(-0.7) x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) - print(0) for elem in elems: for key in flts.keys(): if elem.split("_")[0] == key.split("_")[0]: @@ -1568,7 +1557,6 @@ def multi_edge_xanes_zebra( break else: flt = [] - print(0.1) for key in exp_t.keys(): if elem.split("_")[0] == key.split("_")[0]: exposure = exp_t[key] @@ -1577,15 +1565,12 @@ def multi_edge_xanes_zebra( else: exposure = 0.05 print("use default exposure time 0.05s") - print(0.2) for key in acq_p.keys(): if elem.split("_")[0] == key.split("_")[0]: acquire_period = acq_p[key] break - print(0.3) eng_list = _mk_eng_list(elem, bulk=False) - print(1) yield from _multi_pos_xanes_3D_zebra_xh( eng_list, x_list, From b30f159a5f37f2cbbd15a2d43b6d586e23bf78e5 Mon Sep 17 00:00:00 2001 From: FXI Operator Date: Tue, 18 Jun 2024 13:16:33 -0400 Subject: [PATCH 6/9] updates on 2024_06_18 --- 1146 | 4823 --------------------------------- startup/10-area-detector.py | 38 +- startup/11-txm_motor.py | 9 +- startup/18-zebra.py | 34 +- startup/20-global_param.py | 5 + startup/40-scan_pre_define.py | 6 + startup/41-scans.py | 47 +- startup/44-scans_other.py | 224 +- startup/45-baseline.py | 2 +- startup/46-zebra_flyer.py | 683 ++++- startup/80-load_scan.py | 233 +- startup/90-image_util.py | 50 +- startup/91-functions.py | 25 +- startup/98-user_scan.py | 778 +++++- startup/99-umacro.py | 47 + 15 files changed, 1808 insertions(+), 5196 deletions(-) delete mode 100644 1146 diff --git a/1146 b/1146 deleted file mode 100644 index 043fe42..0000000 --- a/1146 +++ /dev/null @@ -1,4823 +0,0 @@ -''' -import datetime -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.mime.base import MIMEBase -from email import encoders - -def send_email(subject, - body, - hostname, - port, - user, - password, - recipients, - attachment_path=None): - """Sends an email, and possibly an attachment, to the given recipients. - Args: - subject: The email subject text. - body: The email body text. - host: Hostname of the SMTP email server. - port: Port on the host to connect on. - user: Email address to send the email from. - password: Password of the sending address. - recipients: A list of email addresses to send the message to. - attachment_path (optional): Path to the attachment file. - """ - # Create message and add body - msg = MIMEMultipart() - msg['Subject'] = subject - msg['From'] = user - msg['To'] = ', '.join(recipients) - msg.attach(MIMEText(body)) - # Add attachment to message - if attachment_path != None: - attachment = open(attachment_path, "rb") - part = MIMEBase('application', 'octet-stream') - part.set_payload(attachment.read()) - encoders.encode_base64(part) - part.add_header('Content-Disposition', - 'attachment; filename="{}"'.format(attachment_path)) - msg.attach(part) - # Send the message - server = smtplib.SMTP(hostname, port) - server.starttls() - server.login(user, password) - server.sendmail(from_addr = user, - to_addrs = recipients, - msg = msg.as_string()) - server.quit() - -''' - -import numpy as np -import sys - - -def select_filters(flts=[]): - yield from _close_shutter(simu=False) - for key, item in filters.items(): - yield from mv(item, 0) - for ii in flts: - yield from mv(filters["filter" + str(ii)], 1) - - -def user_scan( - exposure_time, - period, - out_x, - out_y, - out_z, - rs=1, - out_r=0, - xanes_flag=False, - xanes_angle=0, - note="", -): - # Ni - angle_ini = 0 - yield from mv(zps.pi_r, angle_ini) - print("start taking tomo and xanes of Ni") - yield from move_zp_ccd(8.35, move_flag=1) - yield from fly_scan( - exposure_time, - relative_rot_angle=180, - period=period, - out_x=out_x, - out_y=out_y, - out_z=out_z, - rs=rs, - parkpos=out_r, - note=note + "_8.35keV", - ) - yield from bps.sleep(2) - yield from move_zp_ccd(8.3, move_flag=1) - yield from fly_scan( - exposure_time, - relative_rot_angle=180, - period=period, - out_x=out_x, - out_y=out_y, - out_z=out_z, - rs=rs, - parkpos=out_r, - note=note + "8.3keV", - ) - yield from mv(zps.pi_r, xanes_angle) - if xanes_flag: - yield from xanes_scan2( - eng_list_Ni, - exposure_time, - chunk_size=5, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - note=note + "_xanes", - ) - yield from mv(zps.pi_r, angle_ini) - - """ - # Co - print('start taking tomo and xanes of Co') - yield from mv(zps.pi_r, angle_ini) - yield from move_zp_ccd(7.75, move_flag=1) - yield from fly_scan(0.05, relative_rot_angle=180, period=0.05, out_x=out_x, out_y=out_y,out_z=0, rs=2, parkpos=0, note=note) - - yield from move_zp_ccd(7.66, move_flag=1) - yield from fly_scan(0.05, relative_rot_angle=180, period=0.05, out_x=out_x, out_y=out_y,out_z=0, rs=2, parkpos=0, note=note) - yield from mv(zps.pi_r, xanes_angle) - if xanes_flag: - yield from xanes_scan2(eng_list_Co, 0.05, chunk_size=5, out_x=out_x, out_y=out_y,note=note) - yield from mv(zps.pi_r, angle_ini) - - # Mn - - print('start taking tomo and xanes of Mn') - yield from mv(zps.pi_r, angle_ini) - yield from move_zp_ccd(6.59, move_flag=1) - yield from fly_scan(0.05, relative_rot_angle=180, period=0.05, out_x=out_x, out_y=out_y,out_z=0, rs=2, parkpos=0, note=note) - - yield from move_zp_ccd(6.49, move_flag=1) - yield from fly_scan(0.05, relative_rot_angle=180, period=0.05, out_x=out_x, out_y=out_y,out_z=0, rs=2, parkpos=0, note=note) - yield from mv(zps.pi_r, xanes_angle) - if xanes_flag: - yield from xanes_scan2(eng_list_Mn, 0.1, chunk_size=5, out_x=out_x, out_y=out_y,note=note) - yield from mv(zps.pi_r, angle_ini) - - """ - - -def user_xanes(out_x, out_y, note=""): - """ - yield from move_zp_ccd(7.4, move_flag=1, xanes_flag='2D') - yield from bps.sleep(1) - yield from xanes_scan2(eng_list_Co, 0.05, chunk_size=5, out_x=out_x, out_y=out_y, note=note) - yield from bps.sleep(5) - """ - print("please wait for 5 sec...starting Ni xanes") - yield from move_zp_ccd(8.3, move_flag=1) - yield from bps.sleep(1) - yield from xanes_scan2( - eng_list_Ni, 0.05, chunk_size=5, out_x=out_x, out_y=out_y, note=note - ) - - -""" -def user_flyscan(out_x, out_y, note=''): - yield from move_zp_ccd(8.35, move_flag=1, xanes_flag='2D') - yield from bps.sleep(1) - yield from fly_scan(0.05, relative_rot_angle=180, period=0.05, out_x=out_x, out_y=out_y,out_z=0, rs=2, parkpos=0, note=note) - yield from move_zp_ccd(8.3, move_flag=1, xanes_flag='2D') - yield from bps.sleep(1) - yield from fly_scan(0.05, relative_rot_angle=180, period=0.05, out_x=out_x, out_y=out_y,out_z=0, rs=2, parkpos=0, note=note) - - yield from move_zp_ccd(7.75, move_flag=1, xanes_flag='2D') - yield from bps.sleep(1) - yield from fly_scan(0.05, relative_rot_angle=180, period=0.05, out_x=out_x, out_y=out_y,out_z=0, rs=2, parkpos=0, note=note) - yield from move_zp_ccd(7.66, move_flag=1, xanes_flag='2D') - yield from bps.sleep(1) - yield from fly_scan(0.05, relative_rot_angle=180, period=0.05, out_x=out_x, out_y=out_y,out_z=0, rs=2, parkpos=0, note=note) - - yield from move_zp_ccd(6.59, move_flag=1, xanes_flag='2D') - yield from bps.sleep(1) - yield from fly_scan(0.05, relative_rot_angle=180, period=0.05, out_x=out_x, out_y=out_y,out_z=0, rs=2, parkpos=0, note=note) - yield from move_zp_ccd(6.49, move_flag=1, xanes_flag='2D') - yield from bps.sleep(1) - yield from fly_scan(0.05, relative_rot_angle=180, period=0.05, out_x=out_x, out_y=out_y,out_z=0, rs=2, parkpos=0, note=note) -""" - - -def overnight_fly(): - insert_text("start William Zhou in-situ scan at 10min interval for 70 times:") - for i in range(70): - print(f"current scan# {i}") - yield from abs_set(shutter_open, 1) - yield from sleep(1) - yield from abs_set(shutter_open, 1) - yield from sleep(2) - yield from fly_scan( - exposure_time=0.05, - relative_rot_angle=180, - period=0.05, - chunk_size=20, - out_x=0, - out_y=0, - out_z=1000, - out_r=0, - rs=3, - simu=False, - note="WilliamZhou_DOW_Water_drying_insitu_scan@8.6keV,w/filter 1&2", - ) - - yield from abs_set(shutter_close, 1) - yield from sleep(1) - yield from abs_set(shutter_close, 1) - yield from sleep(2) - yield from bps.sleep(520) - insert_text("finished pin-situ scan") - - -def insitu_xanes_scan( - eng_list, - exposure_time=0.2, - out_x=0, - out_y=0, - out_z=0, - out_r=0, - repeat_num=1, - sleep_time=1, - note="None", -): - insert_text("start from now on, taking in-situ NMC charge/discharge xanes scan:") - for i in range(repeat_num): - print(f"scan #{i}\n") - yield from xanes_scan2( - eng_list, - exposure_time=exposure_time, - chunk_size=2, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - note=f"{note}_#{i}", - ) - current_time = str(datetime.now().time())[:8] - print(f"current time is {current_time}") - insert_text(f"current scan finished at: {current_time}") - yield from abs_set(shutter_close, 1) - yield from bps.sleep(1) - yield from abs_set(shutter_close, 1) - print(f"\nI'm sleeping for {sleep_time} sec ...\n") - yield from bps.sleep(sleep_time) - insert_text("finished in-situ xanes scan !!") - - -def user_fly_scan( - exposure_time=0.1, period=0.1, chunk_size=20, rs=1, note="", simu=False, md=None -): - - """ - motor_x_ini = zps.pi_x.position - # motor_x_out = motor_x_ini + txm_out_x - motor_y_ini = zps.sy.position - motor_y_out = motor_y_ini + out_y - motor_z_ini = zps.sz.position - motor_z_out = motor_z_ini + out_z - motor_r_ini = zps.pi_r.position - motor_r_out = motor_r_ini + out_r - """ - motor_r_ini = zps.pi_r.position - motor = [zps.sx, zps.sy, zps.sz, zps.pi_r, zps.pi_x] - - dets = [Andor, ic3] - taxi_ang = -2.0 * rs - cur_rot_ang = zps.pi_r.position - - # tgt_rot_ang = cur_rot_ang + rel_rot_ang - _md = { - "detectors": ["Andor"], - "motors": [mot.name for mot in motor], - "XEng": XEng.position, - "ion_chamber": ic3.name, - "plan_args": { - "exposure_time": exposure_time, - "period": period, - "chunk_size": chunk_size, - "rs": rs, - "note": note if note else "None", - }, - "plan_name": "fly_scan", - "num_bkg_images": chunk_size, - "num_dark_images": chunk_size, - "chunk_size": chunk_size, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - "motor_pos": wh_pos(print_on_screen=0), - } - _md.update(md or {}) - try: - dimensions = [(zps.pi_r.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=chunk_size - ) - - print("set rotation speed: {} deg/sec".format(rs)) - - @stage_decorator(list(dets) + motor) - @bpp.monitor_during_decorator([zps.pi_r]) - @run_decorator(md=_md) - def inner_scan(): - # close shutter, dark images: numer=chunk_size (e.g.20) - print("\nshutter closed, taking dark images...") - yield from _take_dark_image(dets, motor, num_dark=1, simu=simu) - - yield from mv(zps.pi_x, 0) - yield from mv(zps.pi_r, -50) - yield from _set_rotation_speed(rs=rs) - # open shutter, tomo_images - yield from _open_shutter(simu=simu) - print("\nshutter opened, taking tomo images...") - yield from mv(zps.pi_r, -50 + taxi_ang) - status = yield from abs_set(zps.pi_r, 50, wait=False) - yield from bps.sleep(2) - while not status.done: - yield from trigger_and_read(list(dets) + motor) - # bkg images - print("\nTaking background images...") - yield from _set_rotation_speed(rs=30) - yield from mv(zps.pi_r, 0) - - yield from mv(zps.pi_x, 12) - yield from mv(zps.pi_r, 70) - yield from trigger_and_read(list(dets) + motor) - - yield from _close_shutter(simu=simu) - yield from mv(zps.pi_r, 0) - yield from mv(zps.pi_x, 0) - yield from mv(zps.pi_x, 0) - # yield from mv(zps.pi_r, motor_r_ini) - - uid = yield from inner_scan() - print("scan finished") - txt = get_scan_parameter() - insert_text(txt) - print(txt) - return uid - - -def tmp_scan(): - x = np.array([0, 1, 2, 3]) * 0.015 * 2560 + zps.sx.position - y = np.array([0, 1, 2, 3]) * 0.015 * 2160 + zps.sy.position - - i = 0 - j = 0 - for xx in x: - i += 1 - for yy in y: - j += 1 - print(f"current {i}_{j}: x={xx}, y={yy}") - yield from mv(zps.sx, xx, zps.sy, yy) - yield from xanes_scan2( - eng_Ni_list_xanes, - 0.05, - chunk_size=4, - out_x=2000, - out_y=0, - out_z=0, - out_r=0, - simu=False, - note="NCM532_72cycle_discharge_{i}_{j}", - ) - - -def mosaic_fly_scan( - x_list, - y_list, - z_list, - r_list, - exposure_time=0.1, - rel_rot_ang=150, - period=0.1, - chunk_size=20, - out_x=None, - out_y=None, - out_z=4400, - out_r=90, - rs=1, - note="", - simu=False, - relative_move_flag=0, - traditional_sequence_flag=0, -): - txt = "start mosaic_fly_scan, containing following fly_scan\n" - insert_text(txt) - insert_text("x_list = ") - insert_text(str(x_list)) - insert_text("y_list = ") - insert_text(str(y_list)) - insert_text("z_list = ") - insert_text(str(z_list)) - insert_text("r_list = ") - insert_text(str(r_list)) - - nx = len(x_list) - ny = len(y_list) - for i in range(ny): - for j in range(nx): - success = False - count = 1 - while not success and count < 20: - try: - RE( - mv( - zps.sx, - x_list[j], - zps.sy, - y_list[i], - zps.sz, - z_list[i], - zps.pi_r, - r_list[i], - ) - ) - RE( - fly_scan( - exposure_time, - relative_rot_angle, - period, - chunk_size, - out_x, - out_y, - out_z, - out_r, - rs, - note, - simu, - relative_move_flag, - traditional_sequence_flag, - md=None, - ) - ) - success = True - except: - count += 1 - RE.abort() - Andor.unstage() - print("sleeping for 30 sec") - RE(bps.sleep(30)) - txt = f"Redo scan at x={x_list[i]}, y={y_list[i]}, z={z_list[i]} for {count} times" - print(txt) - insert_text(txt) - txt = "mosaic_fly_scan finished !!\n" - insert_text(txt) - - -def mosaic2d_lists(x_start, x_end, x_step, y_start, y_end, y_step, z, r): - x_range = list(range(x_start, x_end + x_step, x_step)) - y_range = list(range(y_start, y_end + y_step, y_step)) - x_list = x_range * len(y_range) - y_list = [] - for y in y_range: - y_list.extend([y] * len(x_range)) - z_list = [z] * len(x_list) - r_list = [r] * len(x_list) - return x_list, y_list, z_list, r_list - - -def multi_pos_3D_xanes( - eng_list, - x_list=[0], - y_list=[0], - z_list=[0], - r_list=[0], - exposure_time=0.05, - rel_rot_ang=182, - rs=2, -): - """ - the sample_out position is in its absolute value: - will move sample to out_x (um) out_y (um) out_z(um) and out_r (um) to take background image - - to run: - - RE(multi_pos_3D_xanes(Ni_eng_list, x_list=[a, b, c], y_list=[aa,bb,cc], z_list=[aaa,bbb, ccc], r_list=[0, 0, 0], exposure_time=0.05, rel_rot_ang=185, rs=3, out_x=1500, out_y=-1500, out_z=-770, out_r=0, note='NC') - """ - num_pos = len(x_list) - for i in range(num_pos): - print(f"currently, taking 3D xanes at position {i}\n") - yield from mv( - zps.sx, x_list[i], zps.sy, y_list[i], zps.sz, z_list[i], zps.pi_r, r_list[i] - ) - yield from bps.sleep(2) - note_pos = note + f"position_{i}" - yield from xanes_3D( - eng_list, - exposure_time=exposure_time, - relative_rot_angle=rel_rot_ang, - period=exposure_time, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - rs=rs, - simu=False, - relative_move_flag=0, - traditional_sequence_flag=1, - note=note_pos, - ) - insert_text(f"finished 3D xanes scan for {note_pos}") - - -def mk_eng_list(elem, bulk=False): - if bulk: - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem.split("_")[0] - + "/eng_list_" - + elem.split("_")[0] - + "_xanes_standard_dense.txt" - ) - else: - if elem.split("_")[-1] == "wl": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem.split("_")[0] - + "/eng_list_" - + elem.split("_")[0] - + "_xanes_standard_21pnt.txt" - ) - elif elem.split("_")[-1] == "101": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem.split("_")[0] - + "/eng_list_" - + elem.split("_")[0] - + "_xanes_standard_101pnt.txt" - ) - elif elem.split("_")[-1] == "63": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem.split("_")[0] - + "/eng_list_" - + elem.split("_")[0] - + "_xanes_standard_63pnt.txt" - ) - return eng_list - - -def sort_in_pos(in_pos_list): - x_list = [] - y_list = [] - z_list = [] - r_list = [] - for ii in range(len(in_pos_list)): - x_list.append( - zps.sx.position if in_pos_list[ii][0] is None else in_pos_list[ii][0] - ) - y_list.append( - zps.sy.position if in_pos_list[ii][1] is None else in_pos_list[ii][1] - ) - z_list.append( - zps.sz.position if in_pos_list[ii][2] is None else in_pos_list[ii][2] - ) - r_list.append( - zps.pi_r.position if in_pos_list[ii][3] is None else in_pos_list[ii][3] - ) - # if in_pos_list[ii][0] is None: - # x_list.append(zps.sx.position) - # else: - # x_list.append(in_pos_list[ii][0]) - - # if in_pos_list[ii][1] is None: - # y_list.append(zps.sy.position) - # else: - # y_list.append(in_pos_list[ii][1]) - - # if in_pos_list[ii][2] is None: - # z_list.append(zps.sz.position) - # else: - # z_list.append(in_pos_list[ii][2]) - - # if in_pos_list[ii][3] is None: - # r_list.append(zps.pi_r.position) - # else: - # r_list.append(in_pos_list[ii][3]) - - return (x_list, y_list, z_list, r_list) - - -def multi_edge_xanes( - elements=["Ni_wl"], - scan_type="3D", - filters={"Ni_filters": [1, 2, 3]}, - exposure_time={"Ni_exp": 0.05}, - rel_rot_ang=185, - rs=1, - in_pos_list=[[None, None, None, None]], - out_pos=[None, None, None, None], - chunk_size=5, - note="", - relative_move_flag=0, - binning=None, - simu=False, -): - yield from mv(Andor.cam.acquire, 0) - cam_bin = {0: "[1x1]", 1: "[2x2]", 2: "[3x3]", 3: "[4x4]", 4: "[8x8]"} - x_list, y_list, z_list, r_list = sort_in_pos(in_pos_list) - for elem in elements: - for key in filters.keys(): - if elem.split("_")[0] == key.split("_")[0]: - yield from select_filters(filters[key]) - break - else: - yield from select_filters([]) - for key in exposure_time.keys(): - if elem.split("_")[0] == key.split("_")[0]: - exposure = exposure_time[key] - print(elem, exposure) - break - else: - exposure = 0.05 - print("use default exposure time 0.05s") - eng_list = mk_eng_list(elem, bulk=False) - if scan_type == "2D": - if binning is None: - binning = 0 - # ans = input( - # f"You are going to conduct 2D XANES with camera binning of {cam_bin[binning]}. Proceed? (Y/n)" - # ) - # if ans.upper() == "N": - # return - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) - - yield from multipos_2D_xanes_scan2( - eng_list, - x_list, - y_list, - z_list, - r_list, - out_x=out_pos[0], - out_y=out_pos[1], - out_z=out_pos[2], - out_r=out_pos[3], - exposure_time=exposure, - chunk_size=chunk_size, - simu=simu, - relative_move_flag=relative_move_flag, - note=note, - md=None, - sleep_time=0, - repeat_num=1, - ) - elif scan_type == "3D": - if binning is None: - binning = 1 - # ans = input( - # f"You are going to conduct 3D XANES with camera binning of {cam_bin[binning]}. Proceed? (Y/n)" - # ) - # if ans.upper() == "N": - # return - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) - - yield from multi_pos_xanes_3D( - eng_list, - x_list, - y_list, - z_list, - r_list, - exposure_time=exposure, - relative_rot_angle=rel_rot_ang, - rs=rs, - out_x=out_pos[0], - out_y=out_pos[1], - out_z=out_pos[2], - out_r=out_pos[3], - note=note, - simu=simu, - relative_move_flag=relative_move_flag, - rot_first_flag=1, - sleep_time=0, - repeat=1, - ) - else: - print("wrong scan type") - return - - -def multi_edge_xanes2( - elements=["Ni_wl"], - scan_type="3D", - filters={"Ni_filters": [1, 2, 3]}, - exposure_time={"Ni_exp": 0.05}, - rel_rot_ang=185, - rs=1, - in_pos_list=[[None, None, None, None]], - out_pos=[None, None, None, None], - note="", - relative_move_flag=0, - binning=None, - bulk=False, - bulk_intgr=10, - simu=False, - sleep=0, - repeat=None, -): - yield from mv(Andor.cam.acquire, 0) - cam_bin = {0: "[1x1]", 1: "[2x2]", 2: "[3x3]", 3: "[4x4]", 4: "[8x8]"} - if repeat is None: - repeat = 1 - repeat = int(repeat) - - for itr in range(repeat): - x_list, y_list, z_list, r_list = sort_in_pos(in_pos_list) - for elem in elements: - for key in filters.keys(): - if elem.split("_")[0] == key.split("_")[0]: - yield from select_filters(filters[key]) - else: - yield from select_filters([]) - for key in exposure_time.keys(): - if elem.split("_")[0] == key.split("_")[0]: - exposure = exposure_time[key] - print(elem, exposure) - else: - exposure = 0.05 - print("use default exposure time 0.05s") - eng_list = mk_eng_list(elem, bulk=False) - if scan_type == "2D": - if binning is None: - binning = 0 - # ans = input( - # f"You are going to conduct 2D XANES with camera binning of {cam_bin[binning]}. Proceed? (Y/n)" - # ) - # if ans.upper() == "N": - # return - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) - - yield from multipos_2D_xanes_scan2( - eng_list, - x_list, - y_list, - z_list, - r_list, - out_x=out_pos[0], - out_y=out_pos[1], - out_z=out_pos[2], - out_r=out_pos[3], - exposure_time=exposure, - chunk_size=5, - simu=simu, - relative_move_flag=relative_move_flag, - note=note, - md=None, - sleep_time=0, - repeat_num=1, - ) - elif scan_type == "3D": - if binning is None: - binning = 1 - # ans = input( - # f"You are going to conduct 3D XANES with camera binning of {cam_bin[binning]}. Proceed? (Y/n)" - # ) - # if ans.upper() == "N": - # return - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) - - yield from multi_pos_xanes_3D( - eng_list, - x_list, - y_list, - z_list, - r_list, - exposure_time=exposure, - relative_rot_angle=rel_rot_ang, - rs=rs, - out_x=out_pos[0], - out_y=out_pos[1], - out_z=out_pos[2], - out_r=out_pos[3], - note=note, - simu=simu, - relative_move_flag=relative_move_flag, - rot_first_flag=1, - sleep_time=0, - repeat=1, - ) - else: - print("wrong scan type") - - if bulk: - eng_list = mk_eng_list(elem, bulk=True) - zpx = zp.x.position - apx = aper.x.position - cdx = clens.x.position - yield from mv(zp.x, -6500 + zpx) - yield from mv(clens.x, 6500 + cds) - yield from mv(aper.x, -4000 + apx) - xxanes_scan(eng_list, delay_time=0.2, intgr=bulk_intgr, note=note) - yield from mv(clens.x, cds) - yield from mv(aper.x, apx) - yield from mv(zp.x, zpx) - if itr != repeat - 1: - yield from bps.sleep(sleep) - print(f"repeat # {itr} finished") - - -def fly_scan2( - exposure_time=0.05, - start_angle=None, - rel_rot_ang=180, - period=0.05, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - rs=3, - relative_move_flag=1, - rot_first_flag=1, - filters=[], - rot_back_velo=30, - binning=None, - note="", - md=None, - move_to_ini_pos=True, - simu=False, -): - """ - Inputs: - ------- - exposure_time: float, in unit of sec - - start_angle: float - starting angle - - rel_rot_ang: float, - total rotation angles start from current rotary stage (zps.pi_r) position - - period: float, in unit of sec - period of taking images, "period" should >= "exposure_time" - - out_x: float, default is 0 - relative movement of sample in "x" direction using zps.sx to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_y: float, default is 0 - relative movement of sample in "y" direction using zps.sy to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_z: float, default is 0 - relative movement of sample in "z" direction using zps.sz to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_r: float, default is 0 - relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - rs: float, default is 1 - rotation speed in unit of deg/sec - - note: string - adding note to the scan - - simu: Bool, default is False - True: will simulate closing/open shutter without really closing/opening - False: will really close/open shutter - - """ - yield from mv(Andor.cam.acquire, 0) - if binning is None: - binning = 0 - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) - - global ZONE_PLATE - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - if not (start_angle is None): - yield from mv(zps.pi_r, start_angle) - - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if not (out_x is None) else motor_x_ini - motor_y_out = motor_y_ini + out_y if not (out_y is None) else motor_y_ini - motor_z_out = motor_z_ini + out_z if not (out_z is None) else motor_z_ini - motor_r_out = motor_r_ini + out_r if not (out_r is None) else motor_r_ini - else: - motor_x_out = out_x if not (out_x is None) else motor_x_ini - motor_y_out = out_y if not (out_y is None) else motor_y_ini - motor_z_out = out_z if not (out_z is None) else motor_z_ini - motor_r_out = out_r if not (out_r is None) else motor_r_ini - - motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - - dets = [Andor, ic3] - taxi_ang = -1 * rs - cur_rot_ang = zps.pi_r.position - tgt_rot_ang = cur_rot_ang + rel_rot_ang - _md = { - "detectors": ["Andor"], - "motors": [mot.name for mot in motor], - "XEng": XEng.position, - "ion_chamber": ic3.name, - "plan_args": { - "exposure_time": exposure_time, - "start_angle": start_angle, - "relative_rot_angle": rel_rot_ang, - "period": period, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "out_r": out_r, - "rs": rs, - "relative_move_flag": relative_move_flag, - "rot_first_flag": rot_first_flag, - "filters": [t.name for t in filters] if filters else "None", - "binning": "None" if binning is None else binning, - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - }, - "plan_name": "fly_scan", - "num_bkg_images": 20, - "num_dark_images": 20, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - #'motor_pos': wh_pos(print_on_screen=0), - } - _md.update(md or {}) - try: - dimensions = [(zps.pi_r.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=20, binning=binning - ) - yield from _set_rotation_speed(rs=np.abs(rs)) - print("set rotation speed: {} deg/sec".format(rs)) - - @stage_decorator(list(dets) + motor) - @bpp.monitor_during_decorator([zps.pi_r]) - @run_decorator(md=_md) - def fly_inner_scan(): - select_filters(flts=[]) - yield from bps.sleep(1) - - # close shutter, dark images: numer=chunk_size (e.g.20) - print("\nshutter closed, taking dark images...") - yield from _take_dark_image( - dets, motor, num=1, chunk_size=20, stream_name="dark", simu=simu - ) - - # open shutter, tomo_images - true_period = yield from rd(Andor.cam.acquire_period) - rot_time = np.abs(rel_rot_ang) / np.abs(rs) - num_img = int(rot_time / true_period) + 2 - - yield from _open_shutter(simu=simu) - print("\nshutter opened, taking tomo images...") - yield from _set_Andor_chunk_size(dets, chunk_size=num_img) - # yield from mv(zps.pi_r, cur_rot_ang + taxi_ang) - status = yield from abs_set(zps.pi_r, tgt_rot_ang, wait=False) - # yield from bps.sleep(1) - yield from _take_image(dets, motor, num=1, stream_name="primary") - while not status.done: - yield from bps.sleep(0.01) - # yield from trigger_and_read(list(dets) + motor) - - # bkg images - print("\nTaking background images...") - yield from _set_rotation_speed(rs=rot_back_velo) - # yield from abs_set(zps.pi_r.velocity, rs) - - yield from _take_bkg_image( - motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - dets, - [], - num=1, - chunk_size=20, - rot_first_flag=rot_first_flag, - stream_name="flat", - simu=simu, - ) - yield from _close_shutter(simu=simu) - if move_to_ini_pos: - yield from _move_sample_in( - motor_x_ini, - motor_y_ini, - motor_z_ini, - motor_r_ini, - trans_first_flag=rot_first_flag, - repeat=3, - ) - for flt in filters: - yield from mv(flt, 0) - - uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) - print("scan finished") - txt = get_scan_parameter(print_flag=0) - insert_text(txt) - print(txt) - return uid - - -def fly_scan3( - exposure_time=0.05, - start_angle=None, - rel_rot_ang=180, - period=0.05, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - rs=3, - relative_move_flag=1, - rot_first_flag=1, - flts=[], - rot_back_velo=30, - binning=None, - note="", - md=None, - move_to_ini_pos=True, - simu=False, - noDark=False, - noFlat=False, -): - """ - Inputs: - ------- - exposure_time: float, in unit of sec - - start_angle: float - starting angle - - rel_rot_ang: float, - total rotation angles start from current rotary stage (zps.pi_r) position - - period: float, in unit of sec - period of taking images, "period" should >= "exposure_time" - - out_x: float, default is 0 - relative movement of sample in "x" direction using zps.sx to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_y: float, default is 0 - relative movement of sample in "y" direction using zps.sy to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_z: float, default is 0 - relative movement of sample in "z" direction using zps.sz to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_r: float, default is 0 - relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - rs: float, default is 1 - rotation speed in unit of deg/sec - - note: string - adding note to the scan - - simu: Bool, default is False - True: will simulate closing/open shutter without really closing/opening - False: will really close/open shutter - - """ - yield from mv(Andor.cam.acquire, 0) - global ZONE_PLATE - - if binning is None: - binning = 0 - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) - - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - if not (start_angle is None): - yield from mv(zps.pi_r, start_angle) - - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if not (out_x is None) else motor_x_ini - motor_y_out = motor_y_ini + out_y if not (out_y is None) else motor_y_ini - motor_z_out = motor_z_ini + out_z if not (out_z is None) else motor_z_ini - motor_r_out = motor_r_ini + out_r if not (out_r is None) else motor_r_ini - else: - motor_x_out = out_x if not (out_x is None) else motor_x_ini - motor_y_out = out_y if not (out_y is None) else motor_y_ini - motor_z_out = out_z if not (out_z is None) else motor_z_ini - motor_r_out = out_r if not (out_r is None) else motor_r_ini - - motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - - dets = [Andor, ic3] - taxi_ang = -1 * rs - cur_rot_ang = zps.pi_r.position - tgt_rot_ang = cur_rot_ang + rel_rot_ang - _md = { - "detectors": ["Andor"], - "motors": [mot.name for mot in motor], - "XEng": XEng.position, - "ion_chamber": ic3.name, - "plan_args": { - "exposure_time": exposure_time, - "start_angle": start_angle, - "relative_rot_angle": rel_rot_ang, - "period": period, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "out_r": out_r, - "rs": rs, - "relative_move_flag": relative_move_flag, - "rot_first_flag": rot_first_flag, - "filters": [filters[t].name for t in flts] if flts else "None", - "binning": binning, - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - "noDark": "True" if noDark else "False", - "noFlat": "True" if noFlat else "False", - }, - "plan_name": "fly_scan3", - "num_bkg_images": 20, - "num_dark_images": 20, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - #'motor_pos': wh_pos(print_on_screen=0), - } - _md.update(md or {}) - try: - dimensions = [(zps.pi_r.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=20, binning=binning - ) - yield from _set_rotation_speed(rs=np.abs(rs)) - print("set rotation speed: {} deg/sec".format(rs)) - - @stage_decorator(list(dets) + motor) - @bpp.monitor_during_decorator([zps.pi_r]) - @run_decorator(md=_md) - def fly_inner_scan(): - for flt in filters: - yield from mv(flt, 1) - yield from mv(flt, 1) - yield from bps.sleep(1) - - # close shutter, dark images: numer=chunk_size (e.g.20) - if not noDark: - print("\nshutter closed, taking dark images...") - yield from _take_dark_image( - dets, motor, num=1, chunk_size=20, stream_name="dark", simu=simu - ) - - # open shutter, tomo_images - true_period = yield from rd(Andor.cam.acquire_period) - rot_time = np.abs(rel_rot_ang) / np.abs(rs) - num_img = int(rot_time / true_period) + 2 - - yield from _open_shutter(simu=simu) - print("\nshutter opened, taking tomo images...") - yield from _set_Andor_chunk_size(dets, chunk_size=num_img) - # yield from mv(zps.pi_r, cur_rot_ang + taxi_ang) - status = yield from abs_set(zps.pi_r, tgt_rot_ang, wait=False) - # yield from bps.sleep(1) - yield from _take_image(dets, motor, num=1, stream_name="primary") - while not status.done: - yield from bps.sleep(0.01) - # yield from trigger_and_read(list(dets) + motor) - - # bkg images - print("\nTaking background images...") - yield from _set_rotation_speed(rs=rot_back_velo) - # yield from abs_set(zps.pi_r.velocity, rs) - - if not noFlat: - yield from _take_bkg_image( - motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - dets, - [], - num=1, - chunk_size=20, - rot_first_flag=rot_first_flag, - stream_name="flat", - simu=simu, - ) - - if not noDark: - yield from _close_shutter(simu=simu) - - if move_to_ini_pos: - yield from _move_sample_in( - motor_x_ini, - motor_y_ini, - motor_z_ini, - motor_r_ini, - trans_first_flag=rot_first_flag, - repeat=3, - ) - for flt in filters: - yield from mv(flt, 0) - - uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) - print("scan finished") - txt = get_scan_parameter(print_flag=0) - insert_text(txt) - print(txt) - return uid - - -def rock_scan(exp_t=0.05, - period=0.05, - t_span=10, - start_angle=None, - rel_rot_ang=30, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - rs=30, - relative_move_flag=1, - rot_first_flag=1, - rot_back_velo=30, - filters=[], - binning=None, - note="", - md=None, - move_to_ini_pos=True, - simu=False, - noDark=False, - noFlat=False, -): - """ - Inputs: - ------- - exp_t: float, in unit of sec - - start_angle: float - starting angle - - rel_rot_ang: float, - total rotation angles start from current rotary stage (zps.pi_r) position - - period: float, in unit of sec - period of taking images, "period" should >= "exposure_time" - - out_x: float, default is 0 - relative movement of sample in "x" direction using zps.sx to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_y: float, default is 0 - relative movement of sample in "y" direction using zps.sy to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_z: float, default is 0 - relative movement of sample in "z" direction using zps.sz to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_r: float, default is 0 - relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - rs: float, default is 1 - rotation speed in unit of deg/sec - - note: string - adding note to the scan - - simu: Bool, default is False - True: will simulate closing/open shutter without really closing/opening - False: will really close/open shutter - - """ - yield from mv(Andor.cam.acquire, 0) - global ZONE_PLATE - - if binning is None: - binning = 0 - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) - - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - if not (start_angle is None): - yield from mv(zps.pi_r, start_angle) - else: - start_angle = zps.pi_r.position - - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if not (out_x is None) else motor_x_ini - motor_y_out = motor_y_ini + out_y if not (out_y is None) else motor_y_ini - motor_z_out = motor_z_ini + out_z if not (out_z is None) else motor_z_ini - motor_r_out = motor_r_ini + out_r if not (out_r is None) else motor_r_ini - else: - motor_x_out = out_x if not (out_x is None) else motor_x_ini - motor_y_out = out_y if not (out_y is None) else motor_y_ini - motor_z_out = out_z if not (out_z is None) else motor_z_ini - motor_r_out = out_r if not (out_r is None) else motor_r_ini - - motor = [zps.pi_r] - - dets = [Andor] - tgt_rot_ang = start_angle + rel_rot_ang - _md = { - "detectors": ["Andor"], - "motors": [mot.name for mot in motor], - "XEng": XEng.position, - "ion_chamber": ic3.name, - "plan_args": { - "exposure_time": exp_t, - "start_angle": start_angle, - "relative_rot_angle": rel_rot_ang, - "period": period, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "out_r": out_r, - "rs": rs, - "relative_move_flag": relative_move_flag, - "rot_first_flag": rot_first_flag, - "filters": [t.name for t in filters] if filters else "None", - "binning": binning, - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - "noDark": "True" if noDark else "False", - "noFlat": "True" if noFlat else "False", - }, - "plan_name": "rock_scan", - "num_bkg_images": 20, - "num_dark_images": 20, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - #'motor_pos': wh_pos(print_on_screen=0), - } - _md.update(md or {}) - try: - dimensions = [(zps.pi_r.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - yield from _set_andor_param( - exposure_time=exp_t, period=period, chunk_size=20, binning=binning - ) - yield from _set_rotation_speed(rs=np.abs(rs)) - print("set rotation speed: {} deg/sec".format(rs)) - - @stage_decorator(dets + motor) - @bpp.monitor_during_decorator([zps.pi_r]) - @run_decorator(md=_md) - def rock_inner_scan(): - for flt in filters: - yield from mv(flt, 1) - yield from mv(flt, 1) - yield from bps.sleep(1) - - # close shutter, dark images: numer=chunk_size (e.g.20) - if not noDark: - print("\nshutter closed, taking dark images...") - yield from _take_dark_image( - dets, motor, num=1, chunk_size=20, stream_name="dark", simu=simu - ) - - # open shutter, tomo_images - true_period = yield from rd(Andor.cam.acquire_period) - rot_time = np.abs(rel_rot_ang) / np.abs(rs) - num_img = int(rot_time / true_period) + 2 - - yield from _open_shutter(simu=simu) - print("\nshutter opened, taking tomo images...") - yield from _set_Andor_chunk_size(dets, chunk_size=num_img) - # yield from mv(zps.pi_r, cur_rot_ang + taxi_ang) - status = yield from abs_set(zps.pi_r, tgt_rot_ang, wait=False) - # yield from bps.sleep(1) - yield from _take_image(dets, motor, num=1, stream_name="primary") - while not status.done: - yield from bps.sleep(0.01) - # yield from trigger_and_read(list(dets) + motor) - - # bkg images - print("\nTaking background images...") - yield from _set_rotation_speed(rs=rot_back_velo) - # yield from abs_set(zps.pi_r.velocity, rs) - - if not noFlat: - yield from _take_bkg_image( - motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - dets, - [], - num=1, - chunk_size=20, - rot_first_flag=rot_first_flag, - stream_name="flat", - simu=simu, - ) - - if not noDark: - yield from _close_shutter(simu=simu) - - if move_to_ini_pos: - yield from _move_sample_in( - motor_x_ini, - motor_y_ini, - motor_z_ini, - motor_r_ini, - trans_first_flag=rot_first_flag, - repeat=3, - ) - for flt in filters: - yield from mv(flt, 0) - - uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) - print("scan finished") - txt = get_scan_parameter(print_flag=0) - insert_text(txt) - print(txt) - return uid - - - - -def mosaic_fly_scan_xh( - x_ini=None, - y_ini=None, - z_ini=None, - x_num_steps=1, - y_num_steps=1, - z_num_steps=1, - x_step_size=0, - y_step_size=0, - z_step_size=0, - exposure_time=0.1, - period=0.1, - rs=4, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - start_angle=None, - rel_rot_ang=180, - binning=0, - relative_move_flag=True, - simu=False, - note="", -): - if binning is None: - binning = 0 - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) - - if x_ini is None: - x_ini = zps.sx.position - if y_ini is None: - y_ini = zps.sy.position - if z_ini is None: - z_ini = zps.sz.position - - y_list = y_ini + np.arange(y_num_steps) * y_step_size - x_list = x_ini + np.arange(x_num_steps) * x_step_size - z_list = z_ini + np.arange(z_num_steps) * z_step_size - txt1 = "\n###############################################" - txt2 = "\n####### start mosaic tomography scan ######" - txt3 = "\n###############################################" - txt = txt1 + txt2 + txt3 - print(txt) - for y in y_list: - for z in z_list: - for x in x_list: - yield from mv(zps.sx, x, zps.sy, y, zps.sz, z) - yield from fly_scan( - exposure_time=exposure_time, - start_angle=start_angle, - relative_rot_angle=rel_rot_ang, - period=period, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - rs=rs, - relative_move_flag=relative_move_flag, - note=note, - simu=simu, - rot_first_flag=True, - ) - - -def grid_z_scan( - zstart=-0.03, - zstop=0.03, - zsteps=5, - gmesh=[[-5, 0, 5], [-5, 0, 5]], - out_x=-100, - out_y=-100, - chunk_size=10, - exposure_time=0.1, - note="", - md=None, - simu=False, -): - """ - scan the zone-plate to find best focus - use as: - z_scan(zstart=-0.03, zstop=0.03, zsteps=5, gmesh=[[-5, 0, 5], [-5, 0, 5]], out_x=-100, out_y=-100, chunk_size=10, exposure_time=0.1, fn='/home/xf18id/Documents/tmp/z_scan.h5', note='', md=None) - - Input: - --------- - zstart: float, relative starting position of zp_z - - zstop: float, relative zstop position of zp_z - - zsteps: int, number of zstep between [zstart, zstop] - - out_x: float, relative amount to move sample out for zps.sx - - out_y: float, relative amount to move sample out for zps.sy - - chunk_size: int, number of images per each subscan (for Andor camera) - - exposure_time: float, exposure time for each image - - note: str, experiment notes - - """ - yield from mv(Andor.cam.acquire, 0) - dets = [Andor] - motor = zp.z - z_ini = motor.position # zp.z intial position - z_start = z_ini + zstart - z_stop = z_ini + zstop - zp_x_ini = zp.x.position - zp_y_ini = zp.y.position - # dets = [Andor] - y_ini = zps.sy.position # sample y position (initial) - y_out = ( - y_ini + out_y if not (out_y is None) else y_ini - ) # sample y position (out-position) - x_ini = zps.sx.position - x_out = x_ini + out_x if not (out_x is None) else x_ini - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, chunk_size) - yield from mv(Andor.cam.acquire_time, exposure_time) - period_cor = max(exposure_time + 0.01, 0.05) - yield from mv(Andor.cam.acquire_period, period_cor) - - _md = { - "detectors": [det.name for det in dets], - "motors": [motor.name], - "XEng": XEng.position, - "plan_args": { - "zstart": zstart, - "zstop": zstop, - "zsteps": zsteps, - "gmesh": gmesh, - "out_x": out_x, - "out_y": out_y, - "chunk_size": chunk_size, - "exposure_time": exposure_time, - "note": note if note else "None", - }, - "plan_name": "grid_z_scan", - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "motor_pos": wh_pos(print_on_screen=0), - } - _md.update(md or {}) - my_var = np.linspace(z_start, z_stop, zsteps) - try: - dimensions = [(motor.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - @stage_decorator(list(dets) + [motor]) - @run_decorator(md=_md) - def inner_scan(): - yield from _open_shutter(simu=simu) - for xx in gmesh[0]: - for yy in gmesh[1]: - yield from mv(zp.x, zp_x_ini + xx, zp.y, zp_y_ini + yy, wait=True) - # yield from mv(zp.y, zp_y_ini+yy, wait=True) - yield from bps.sleep(1) - for x in my_var: - yield from mv(motor, x) - yield from trigger_and_read(list(dets) + [motor], name="primary") - # backgroud images - yield from mv(zps.sx, x_out, zps.sy, y_out, wait=True) - yield from bps.sleep(1) - yield from trigger_and_read(list(dets) + [motor], name="flat") - yield from mv(zps.sx, x_ini, zps.sy, y_ini, wait=True) - yield from bps.sleep(1) - # yield from mv(zps.sy, y_ini, wait=True) - yield from _close_shutter(simu=simu) - yield from bps.sleep(1) - yield from trigger_and_read(list(dets) + [motor], name="dark") - - yield from mv(zps.sx, x_ini) - yield from mv(zps.sy, y_ini) - yield from mv(zp.z, z_ini) - yield from mv(zp.x, zp_x_ini, zp.y, zp_y_ini, wait=True) - yield from mv(Andor.cam.image_mode, 1) - - uid = yield from inner_scan() - yield from mv(Andor.cam.image_mode, 1) - yield from _close_shutter(simu=simu) - txt = get_scan_parameter() - insert_text(txt) - print(txt) - - -def xxanes_scan( - eng_list, - delay_time=0.5, - intgr=1, - dets=[ic1, ic2, ic3], - note="", - md=None, - repeat=None, - sleep=1200, -): - """ - eng_list: energy list in keV - delay_time: delay_time between each energy step, in unit of sec - note: string; optional, description of the scan - """ - if repeat is None: - repeat = 1 - repeat = int(repeat) - - check_eng_range([eng_list[0], eng_list[-1]]) - print(0) - yield from _open_shutter(simu=False) - # dets=[ic1, ic2, ic3] - motor_x = XEng - motor_x_ini = motor_x.position # initial position of motor_x - - # added by XH -- start - motor_y = dcm - # added by XH -- end - - _md = { - "detectors": "".join(ii.name + " " for ii in dets), - "motors": [motor_x.name, motor_y.name], - "XEng": XEng.position, - "plan_name": "xxanes", - "plan_args": { - "eng": eng_list, - "detectors": "".join(ii.name + " " for ii in dets), - "delay_time": delay_time, - "repeat": intgr, - "note": note if note else "None", - }, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - } - _md.update(md or {}) - try: - dimensions = [ - (motor_x.hints["fields"], "primary"), - (motor_y.hints["fields"], "primary"), - ] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - @stage_decorator(list(dets) + [motor_x, motor_y]) - @run_decorator(md=_md) - def eng_inner_scan(): - for eng in eng_list: - yield from mv(motor_x, eng) - yield from bps.sleep(delay_time) - yield from bps.repeat( - partial(bps.trigger_and_read, list(dets) + [motor_x, motor_y]), - num=intgr, - delay=0.01, - ) - # yield from trigger_and_read(list(dets) + [motor_x, motor_y]) - yield from mv(motor_x, motor_x_ini) - - for itr in range(repeat): - yield from _open_shutter(simu=False) - yield from eng_inner_scan() - yield from _close_shutter(simu=False) - if itr != (repeat - 1): - yield from bps.sleep(sleep) - print(f"repeat # {itr} finished") - - -def xxanes_scan2( - eng_list, dets=[ic1, ic2, ic3], note="", md=None, repeat=1, sleep=100, simu=False -): - """ - eng_list: energy list in keV - note: string; optional, description of the scan - """ - repeat = int(repeat) - - check_eng_range([eng_list[0], eng_list[-1]]) - print(0) - XEng_target = eng_list[-1] - # motor_x = dcm - # motor_y = XEng - XEng_ini = XEng.position # initial position of motor_x - dcm_vel_ini = dcm.th1.velocity.value - # yield from mv(dcm.th1.velocity, 0.1) - # yield from mv(XEng, eng_list[0]) - - ang0 = np.arcsin(12.398 / eng_list[0] / 2 / (5.43 / np.sqrt(3))) - ang1 = np.arcsin(12.398 / eng_list[-1] / 2 / (5.43 / np.sqrt(3))) - dcm_vel = ( - 10 - * 180 - * np.abs(ang1 - ang0) - / np.pi - / (4 * np.abs(eng_list[0] - eng_list[-1])) - / 1000 - ) - if dcm_vel < 0.00279: - dcm_vel = 0.00279 - intgr = int(np.ceil(10 * 180 * np.abs(ang1 - ang0) / np.pi / dcm_vel)) - - _md = { - "detectors": "".join(ii.name + " " for ii in dets), - "motors": [XEng.name, dcm.name], - "XEng": XEng.position, - "plan_name": "xxanes2", - "plan_args": { - "eng": eng_list, - "detectors": "".join(ii.name + " " for ii in dets), - "repeat": repeat, - "IC_rate": 10, - "dcm velocity": dcm_vel, - "note": note if note else "None", - }, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - } - _md.update(md or {}) - try: - dimensions = [ - (XEng.hints["fields"], "primary"), - (dcm.hints["fields"], "primary"), - ] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - @stage_decorator(list(dets) + [XEng, dcm]) - @bpp.monitor_during_decorator( - [XEng.user_readback, dcm.th1.user_readback] + list(dets) - ) - @run_decorator(md=_md) - def eng_inner_scan(): - while XEng.moving: - yield from bps.sleep(1) - status = yield from abs_set(XEng, XEng_target, wait=False) - # yield from trigger_and_read(dets + [dcm, XEng], name=stream_name) - yield from bps.repeat( - partial(bps.trigger_and_read, list(dets) + [XEng, dcm]), - num=intgr, - delay=0.1, - ) - while not status.done: - yield from bps.sleep(0.01) - - for itr in range(repeat): - yield from _open_shutter(simu=simu) - yield from mv(dcm.th1.velocity, 0.1) - yield from mv(XEng, eng_list[0]) - yield from mv(dcm.th1.velocity, dcm_vel) - yield from bps.sleep(1) - yield from eng_inner_scan() - yield from _close_shutter(simu=simu) - if itr != (repeat - 1): - yield from bps.sleep(sleep) - print(f"repeat # {itr} finished") - - yield from mv(dcm.th1.velocity, 0.1) - yield from mv(XEng, XEng_ini) - yield from mv(dcm.th1.velocity, dcm_vel_ini) - - -def mosaic_2D_rel_grid_xh( - mot1=zps.sx, - mot1_start=-100, - mot1_end=100, - mot1_points=6, - mot2=zps.sy, - mot2_start=-50, - mot2_end=50, - mot2_points=6, - mot2_snake=False, - out_x=100, - out_y=100, - exp_time=0.1, - chunk_size=1, - note="", - md=None, - simu=False, -): - yield from mv(Andor.cam.acquire, 0) - dets = [Andor] - y_ini = zps.sy.position # sample y position (initial) - y_out = ( - y_ini + out_y if not (out_y is None) else y_ini - ) # sample y position (out-position) - x_ini = zps.sx.position - x_out = x_ini + out_x if not (out_x is None) else x_ini - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, chunk_size) - yield from mv(Andor.cam.acquire_time, exp_time) - period_cor = max(exp_time + 0.01, 0.05) - yield from mv(Andor.cam.acquire_period, period_cor) - - _md = { - "detectors": [det.name for det in dets], - "motors": [mot1.name, mot2.name], - "XEng": XEng.position, - "plan_args": { - "mot1_start": mot1_start, - "mot1_stop": mot1_end, - "mot1_pnts": mot1_points, - "mot2_start": mot2_start, - "mot2_stop": mot2_end, - "mot2_pnts": mot2_points, - "mot2_snake": mot2_snake, - "out_x": out_x, - "out_y": out_y, - "exposure_time": exp_time, - "note": note if note else "", - }, - "plan_name": "raster_scan_xh", - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "motor_pos": wh_pos(print_on_screen=0), - } - _md.update(md or {}) - - # @stage_decorator(list(dets) + [mot1, mot2]) - # @run_decorator(md=_md) - # def inner_scan(): - yield from _open_shutter(simu=simu) - yield from rel_grid_scan( - dets, - mot1, - mot1_start, - mot1_end, - mot1_points, - mot2, - mot2_start, - mot2_end, - mot2_points, - mot2_snake, - ) - yield from mv(zps.sx, x_out, zps.sy, y_out, wait=True) - yield from stage(Andor) - yield from bps.sleep(1) - yield from trigger_and_read(list(dets) + [mot1, mot2], name="flat") - yield from mv(zps.sx, x_ini, zps.sy, y_ini, wait=True) - yield from bps.sleep(1) - yield from _close_shutter(simu=simu) - yield from stage(Andor) - yield from bps.sleep(1) - yield from trigger_and_read(list(dets) + [mot1, mot2], name="dark") - - yield from mv(zps.sx, x_ini) - yield from mv(zps.sy, y_ini) - yield from unstage(Andor) - yield from mv(Andor.cam.image_mode, 1) - yield from _close_shutter(simu=simu) - - -def mosaic_2D_xh( - x_range=[-1, 1], - y_range=[-1, 1], - exposure_time=0.1, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - img_sizeX=2560, - img_sizeY=2160, - simu=False, - relative_move_flag=1, - rot_first_flag=1, - note="", - scan_x_flag=1, - filters=[], - md=None, -): - yield from mv(Andor.cam.acquire, 0) - zp_z_pos = zps.sz.position - DetU_z_pos = DetU.z.position - M = (DetU_z_pos / zp_z_pos - 1) * 10.0 - pxl = 6.5 / M - - global ZONE_PLATE - motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - dets = [Andor, ic3] - yield from _set_andor_param( - exposure_time=exposure_time, period=exposure_time, chunk_size=1 - ) - - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if not (out_x is None) else motor_x_ini - motor_y_out = motor_y_ini + out_y if not (out_y is None) else motor_y_ini - motor_z_out = motor_z_ini + out_z if not (out_z is None) else motor_z_ini - motor_r_out = motor_r_ini + out_r if not (out_r is None) else motor_r_ini - else: - motor_x_out = out_x if not (out_x is None) else motor_x_ini - motor_y_out = out_y if not (out_y is None) else motor_y_ini - motor_z_out = out_z if not (out_z is None) else motor_z_ini - motor_r_out = out_r if not (out_r is None) else motor_r_ini - - img_sizeX = np.int(img_sizeX) - img_sizeY = np.int(img_sizeY) - x_range = np.int_(x_range) - y_range = np.int_(y_range) - - print("hello1") - _md = { - "detectors": [det.name for det in dets], - "motors": [mot.name for mot in motor], - "num_bkg_images": 5, - "num_dark_images": 5, - "x_range": x_range, - "y_range": y_range, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "exposure_time": exposure_time, - "XEng": XEng.position, - "plan_args": { - "x_range": x_range, - "y_range": y_range, - "exposure_time": exposure_time, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "out_r": out_r, - "img_sizeX": img_sizeX, - "img_sizeY": img_sizeY, - "pxl": pxl, - "note": note if note else "None", - "relative_move_flag": relative_move_flag, - "rot_first_flag": rot_first_flag, - "note": note if note else "None", - "scan_x_flag": scan_x_flag, - "zone_plate": ZONE_PLATE, - }, - "plan_name": "mosaic_2D_xh", - "hints": {}, - "operator": "FXI", - "zone_plate": ZONE_PLATE, - "note": note if note else "None", - #'motor_pos': wh_pos(print_on_screen=0), - } - _md.update(md or {}) - try: - dimensions = [(motor.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - @stage_decorator(list(dets) + motor) - @run_decorator(md=_md) - def mosaic_2D_inner(): - if len(filters): - for filt in filters: - yield from mv(filt, 1) - yield from bps.sleep(0.5) - # take dark image - print("take 5 dark image") - yield from _take_dark_image(dets, motor, num=5, stream_name="dark", simu=simu) - - print("open shutter ...") - yield from _open_shutter(simu) - - print("taking mosaic image ...") - for ii in np.arange(x_range[0], x_range[1] + 1): - if scan_x_flag == 1: - # yield from mv(zps.sx, motor_x_ini + ii * img_sizeX * pxl * 1.0 / 1000) - yield from abs_set( - zps.sx, motor_x_ini + ii * img_sizeX * pxl, wait=True - ) - else: - # yield from mv(zps.sz, motor_z_ini + ii * img_sizeX * pxl * 1.0 / 1000) - yield from abs_set( - zps.sz, motor_z_ini + ii * img_sizeX * pxl, wait=True - ) - # sleep_time = (x_range[-1] - x_range[0]) * img_sizeX * pxl * 1.0 / 1000 / 600 - # yield from bps.sleep(sleep_time) - for jj in np.arange(y_range[0], y_range[1] + 1): - # yield from mv(zps.sy, motor_y_ini + jj * img_sizeY * pxl * 1.0 / 1000) - yield from abs_set( - zps.sy, motor_y_ini + jj * img_sizeY * pxl, wait=True - ) - yield from _take_image(dets, motor, 1) - # yield from trigger_and_read(list(dets) + motor) - - print("moving sample out to take 5 background image") - - yield from _take_bkg_image( - motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - dets, - motor, - num=5, - stream_name="flat", - simu=simu, - rot_first_flag=rot_first_flag, - ) - - # move sample in - yield from _move_sample_in( - motor_x_ini, - motor_y_ini, - motor_z_ini, - motor_r_ini, - repeat=1, - trans_first_flag=1 - rot_first_flag, - ) - if len(filters): - for filt in filters: - yield from mv(filt, 0) - yield from bps.sleep(0.5) - print("closing shutter") - yield from _close_shutter(simu) - - yield from mosaic_2D_inner() - - -def dummy_scan( - exposure_time=0.1, - start_angle=None, - rel_rot_ang=180, - period=0.15, - out_x=None, - out_y=2000, - out_z=None, - out_r=None, - rs=1, - note="", - simu=False, - relative_move_flag=1, - rot_first_flag=1, - filters=[], - rot_back_velo=30, - repeat=1, -): - - yield from mv(Andor.cam.acquire, 0) - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - if not (start_angle is None): - yield from mv(zps.pi_r, start_angle) - - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if not (out_x is None) else motor_x_ini - motor_y_out = motor_y_ini + out_y if not (out_y is None) else motor_y_ini - motor_z_out = motor_z_ini + out_z if not (out_z is None) else motor_z_ini - motor_r_out = motor_r_ini + out_r if not (out_r is None) else motor_r_ini - else: - motor_x_out = out_x if not (out_x is None) else motor_x_ini - motor_y_out = out_y if not (out_y is None) else motor_y_ini - motor_z_out = out_z if not (out_z is None) else motor_z_ini - motor_r_out = out_r if not (out_r is None) else motor_r_ini - - motors = [zps.sx, zps.sy, zps.sz, zps.pi_r] - - dets = [Andor, ic3] - taxi_ang = -2 * rs - cur_rot_ang = zps.pi_r.position - - tgt_rot_ang = cur_rot_ang + rel_rot_ang - _md = {"dummy scan": "dummy scan"} - - yield from mv(Andor.cam.acquire, 0) - yield from _set_andor_param(exposure_time=exposure_time, period=period) - yield from mv(Andor.cam.image_mode, 1) - yield from mv(Andor.cam.acquire, 1) - - @stage_decorator(motors) - @bpp.monitor_during_decorator([zps.pi_r]) - @run_decorator(md=_md) - def fly_inner_scan(): - # open shutter, tomo_images - yield from _open_shutter(simu=simu) - print("\nshutter opened, taking tomo images...") - yield from _set_rotation_speed(rs=rs) - yield from mv(zps.pi_r, cur_rot_ang + taxi_ang) - status = yield from abs_set(zps.pi_r, tgt_rot_ang, wait=False) - while not status.done: - yield from bps.sleep(1) - yield from _set_rotation_speed(rs=30) - print("set rotation speed: {} deg/sec".format(rs)) - status = yield from abs_set(zps.pi_r, cur_rot_ang + taxi_ang, wait=False) - while not status.done: - yield from bps.sleep(1) - yield from abs_set(zps.sx, motor_x_out, wait=True) - yield from abs_set(zps.sy, motor_y_out, wait=True) - yield from abs_set(zps.sz, motor_z_out, wait=True) - yield from abs_set(zps.pi_r, motor_r_out, wait=True) - - yield from abs_set(zps.sx, motor_x_ini, wait=True) - yield from abs_set(zps.sy, motor_y_ini, wait=True) - yield from abs_set(zps.sz, motor_z_ini, wait=True) - yield from abs_set(zps.pi_r, motor_r_ini, wait=True) - - for ii in range(repeat): - yield from fly_inner_scan() - print("{}th scan finished".format(ii)) - yield from _set_rotation_speed(rs=rot_back_velo) - print("dummy scan finished") - - -def radiographic_record( - exp_t=0.1, - period=0.1, - t_span=10, - stop=True, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - filters=[], - md={}, - note="", - simu=False, - rot_first_flag=1, - relative_move_flag=1, -): - yield from mv(Andor.cam.acquire, 0) - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if not (out_x is None) else motor_x_ini - motor_y_out = motor_y_ini + out_y if not (out_y is None) else motor_y_ini - motor_z_out = motor_z_ini + out_z if not (out_z is None) else motor_z_ini - motor_r_out = motor_r_ini + out_r if not (out_r is None) else motor_r_ini - else: - motor_x_out = out_x if not (out_x is None) else motor_x_ini - motor_y_out = out_y if not (out_y is None) else motor_y_ini - motor_z_out = out_z if not (out_z is None) else motor_z_ini - motor_r_out = out_r if not (out_r is None) else motor_r_ini - - motors = [zps.sx, zps.sy, zps.sz, zps.pi_r] - - dets = [Andor, ic3] - _md = { - "detectors": ["Andor"], - # "motors": [mot.name for mot in motors], - "XEng": XEng.position, - "ion_chamber": ic3.name, - "plan_args": { - "exposure_time": exp_t, - "period": period, - "time_span": t_span, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "out_r": out_r, - "filters": [filt.name for filt in filters] if filters else "None", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - }, - "plan_name": "radiographic_record", - "num_bkg_images": 20, - "num_dark_images": 20, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - } - _md.update(md or {}) - - yield from mv(Andor.cam.acquire, 0) - yield from _set_andor_param(exposure_time=exp_t, period=period) - yield from mv(Andor.cam.image_mode, 0) - - @stage_decorator(list(dets)) - # @bpp.monitor_during_decorator([Andor.cam.num_images_counter]) - @run_decorator(md=_md) - def rad_record_inner(): - yield from _open_shutter(simu=simu) - for flt in filters: - yield from mv(flt, 1) - yield from mv(flt, 1) - yield from bps.sleep(1) - - yield from mv(Andor.cam.num_images, int(t_span / period)) - yield from trigger_and_read([Andor], name="primary") - - yield from mv( - zps.sx, - motor_x_out, - zps.sy, - motor_y_out, - zps.sz, - motor_z_out, - zps.pi_r, - motor_r_out, - ) - yield from mv(Andor.cam.num_images, 20) - yield from trigger_and_read([Andor], name="flat") - yield from _close_shutter(simu=simu) - yield from mv( - zps.sx, - motor_x_ini, - zps.sy, - motor_y_ini, - zps.sz, - motor_z_ini, - zps.pi_r, - motor_r_ini, - ) - yield from trigger_and_read([Andor], name="dark") - yield from mv(Andor.cam.image_mode, 1) - for flt in filters: - yield from mv(flt, 0) - - yield from rad_record_inner() - - -# def multi_pos_2D_and_3D_xanes(elements=['Ni'], sam_in_pos_list_2D=[[[0, 0, 0, 0],]], sam_out_pos_list_2D=[[[0, 0, 0, 0],]], sam_in_pos_list_3D=[[[0, 0, 0, 0],]], sam_out_pos_list_3D=[[[0, 0, 0, 0],]], exposure_time=[0.05], rel_rot_ang=182, relative_move_flag=False, rs=1, note=''): -# sam_in_pos_list_2D = np.asarray(sam_in_pos_list_2D) -# sam_out_pos_list_2D = np.asarray(sam_out_pos_list_2D) -# sam_in_pos_list_3D = np.asarray(sam_in_pos_list_3D) -# sam_out_pos_list_3D = np.asarray(sam_out_pos_list_3D) -# exposure_time = np.asarray(exposure_time) -# if exposure_time.shape[0] == 1: -# exposure_time = np.ones(len(elements))*exposure_time[0] -# elif len(elements) != exposure_time.shape[0]: -# # to do in bs manner -# pass -# -# eng_list = [] -# for ii in elements: -# eng_list.append(list(np.genfromtxt('/nsls2/data/fxi-new/shared/config/xanes_ref/'+ii+'/eng_list_'+ii+'_xanes_standard.txt'))) -# -# for ii in range(sam_in_pos_list_2D.shape[0]): -# for jj in range(len(elements)): -# x_list = [sam_in_pos_list_2D[ii, :, 0]] -# y_list = [sam_in_pos_list_2D[ii, :, 1]] -# z_list = [sam_in_pos_list_2D[ii, :, 2]] -# r_list = [sam_in_pos_list_2D[ii, :, 3]] -# out_x = sam_out_pos_list_2D[ii, :, 0] -# out_y = sam_out_pos_list_2D[ii, :, 1] -# out_z = sam_out_pos_list_2D[ii, :, 2] -# out_r = sam_out_pos_list_2D[ii, :, 3] -# yield from multipos_2D_xanes_scan2(eng_list[jj], x_list, y_list, z_list, r_list, -# out_x=out_x, out_y=out_y, out_z=out_z, out_r=out_r, -# exposure_time=exposure_time[jj], chunk_size=5, -# simu=False, relative_move_flag=relative_move_flag, note=note, md=None, sleep_time=0, repeat_num=1) -# -# for ii in range(sam_in_pos_list_3D.shape[0]): -# for jj in range(len(elements)): -# x_list = [sam_in_pos_list_3D[ii, :, 0]] -# y_list = [sam_in_pos_list_3D[ii, :, 1]] -# z_list = [sam_in_pos_list_3D[ii, :, 2]] -# r_list = [sam_in_pos_list_3D[ii, :, 3]] -# out_x = sam_out_pos_list_3D[ii, :, 0] -# out_y = sam_out_pos_list_3D[ii, :, 1] -# out_z = sam_out_pos_list_3D[ii, :, 2] -# out_r = sam_out_pos_list_3D[ii, :, 3] -# yield from multi_pos_3D_xanes(eng_list[jj], x_list, y_list, z_list, r_list, -# exposure_time=exposure_time[jj], relative_rot_angle=rel_rot_ang, rs=rs, -# out_x=out_x, out_y=out_y, out_z=out_z, out_r=out_r, note=note, simu=False, -# relative_move_flag=relative_move_flag, traditional_sequence_flag=1, sleep_time=0, repeat=1) - - -# def multi_pos_2D_xanes_and_3D_tomo(elements=['Ni'], sam_in_pos_list_2D=[[[0, 0, 0, 0]]], sam_out_pos_list_2D=[[[0, 0, 0, 0]]], sam_in_pos_list_3D=[[[0, 0, 0, 0]]], sam_out_pos_list_3D=[[[0, 0, 0, 0]]], -# exposure_time_2D=[0.05], exposure_time_3D=[0.05], relative_rot_angle=182, rs=1, eng_3D=[8.4], note='', relative_move_flag=False): -# sam_in_pos_list_2D = np.asarray(sam_in_pos_list_2D) -# sam_out_pos_list_2D = np.asarray(sam_out_pos_list_2D) -# sam_in_pos_list_3D = np.asarray(sam_in_pos_list_3D) -# sam_out_pos_list_3D = np.asarray(sam_out_pos_list_3D) -# exposure_time_2D = np.asarray(exposure_time_2D) -# exposure_time_3D = np.asarray(exposure_time_3D) -# if exposure_time_2D.shape[0] == 1: -# exposure_time_2D = np.ones(len(elements))*exposure_time_2D[0] -# elif len(elements) != exposure_time_2D.shape[0]: -# # to do in bs manner -# pass -# -# if exposure_time_3D.shape[0] == 1: -# exposure_time_3D = np.ones(len(elements))*exposure_time_3D[0] -# elif len(elements) != exposure_time_3D.shape[0]: -# # to do in bs manner -# pass -# -# eng_list = [] -# for ii in elements: -# eng_list.append(list(np.genfromtxt('/nsls2/data/fxi-new/shared/config/xanes_ref/'+ii+'/eng_list_'+ii+'_xanes_standard.txt'))) -# -# for ii in range(sam_in_pos_list_2D.shape[0]): -# for jj in range(len(elements)): -# x_list = sam_in_pos_list_2D[ii, :, 0] -# y_list = sam_in_pos_list_2D[ii, :, 1] -# z_list = sam_in_pos_list_2D[ii, :, 2] -# r_list = sam_in_pos_list_2D[ii, :, 3] -# out_x = sam_out_pos_list_2D[ii, 0] -# out_y = sam_out_pos_list_2D[ii, 1] -# out_z = sam_out_pos_list_2D[ii, 2] -# out_r = sam_out_pos_list_2D[ii, 3] -# print(x_list) -# print(y_list) -# print(z_list) -# print(r_list) -# print(out_x) -# print(out_y) -# print(out_z) -# print(out_r) -# yield from multipos_2D_xanes_scan2(eng_list[jj], x_list, y_list, z_list, r_list, -# out_x=out_x, out_y=out_y, out_z=out_z, out_r=out_r, -# exposure_time=exposure_time_2D[jj], chunk_size=5, -# simu=False, relative_move_flag=relative_move_flag, note=note, md=None, sleep_time=0, repeat_num=1) -# -# for ii in range(sam_in_pos_list_3D.shape[0]): -# for jj in range(len(elements)): -# x_list = sam_in_pos_list_3D[ii, :, 0] -# y_list = sam_in_pos_list_3D[ii, :, 1] -# z_list = sam_in_pos_list_3D[ii, :, 2] -# r_list = sam_in_pos_list_3D[ii, :, 3] -# out_x = sam_out_pos_list_3D[ii, 0] -# out_y = sam_out_pos_list_3D[ii, 1] -# out_z = sam_out_pos_list_3D[ii, 2] -# out_r = sam_out_pos_list_3D[ii, 3] -# yield from multi_pos_xanes_3D(eng_3D, x_list, y_list, z_list, r_list, -# exposure_time=exposure_time_3D[jj], relative_rot_angle=rel_rot_ang, rs=rs, -# out_x=out_x, out_y=out_y, out_z=out_z, out_r=out_r, note=note, simu=False, -# relative_move_flag=relative_move_flag, traditional_sequence_flag=1, sleep_time=0, repeat=1) - - -############ old routine: 2D routine works but 3D routine has some bugs -- start -# def multi_pos_2D_and_3D_xanes(elements=['Ni_short'], filters=[[1, 2, 3]], sam_in_pos_list_2D=[[[0, 0, 0, 0]]], sam_out_pos_list_2D=[[[0, 0, 0, 0]]], sam_in_pos_list_3D=[[[0, 0, 0, 0]]], sam_out_pos_list_3D=[[[0, 0, 0, 0]]], -# exposure_time_2D=[0.05], exposure_time_3D=[0.05], relative_rot_angle=182, rs=1, sleep_time=0, repeat_num=1, note='', relative_move_flag=0, simu=False): -# """ -# pos_list layer structure: 1st layer -> energy -# 2nd layer -> multiple positions at the given energy -# 3rd layer -> individual postion in the multiple poistion list -# """ -# for kk in range(repeat_num): -# sam_in_pos_list_2D = np.asarray(sam_in_pos_list_2D) -# sam_out_pos_list_2D = np.asarray(sam_out_pos_list_2D) -# sam_in_pos_list_3D = np.asarray(sam_in_pos_list_3D) -# sam_out_pos_list_3D = np.asarray(sam_out_pos_list_3D) -# exposure_time_2D = np.asarray(exposure_time_2D) -# exposure_time_3D = np.asarray(exposure_time_3D) -# if exposure_time_2D.shape[0] == 1: -# exposure_time_2D = np.ones(len(elements))*exposure_time_2D[0] -# elif len(elements) != exposure_time_2D.shape[0]: -# # to do in bs manner -# pass -# -# if exposure_time_3D.shape[0] == 1: -# exposure_time_3D = np.ones(len(elements))*exposure_time_3D[0] -# elif len(elements) != exposure_time_3D.shape[0]: -# # to do in bs manner -# pass -# -# eng_list = [] -# for ii in elements: -# if ii.split('_')[1] == 'wl': -# eng_list.append(list(np.genfromtxt('/nsls2/data/fxi-new/shared/config/xanes_ref/'+ii.split('_')[0]+'/eng_list_'+ii.split('_')[0]+'_s_xanes_standard_21pnt.txt'))) -# elif ii.split('_')[1] == '101': -# eng_list.append(list(np.genfromtxt('/nsls2/data/fxi-new/shared/config/xanes_ref/'+ii+'/eng_list_'+ii+'_xanes_standard_101pnt.txt'))) -# elif ii.split('_')[1] == '63': -# eng_list.append(list(np.genfromtxt('/nsls2/data/fxi-new/shared/config/xanes_ref/'+ii+'/eng_list_'+ii+'_xanes_standard_63pnt.txt'))) -# -# eng_list = np.array(eng_list) -# -# if sam_in_pos_list_2D.size != 0: -# for ii in range(sam_in_pos_list_2D.shape[0]): -# for jj in range(len(elements)): -# if filters[jj]: -# select_filters(filters[jj]) -## yield from _close_shutter(simu=simu) -## yield from mv(filter1, 0) -## yield from mv(filter2, 0) -## yield from mv(filter3, 0) -## yield from mv(filter4, 0) -## for flt in filters[jj]: -## if flt == 'filter1': -## yield from mv(filter1, 1) -## elif flt == 'filter2': -## yield from mv(filter2, 1) -## elif flt == 'filter3': -## yield from mv(filter3, 1) -## elif flt == 'filter4': -## yield from mv(filter4, 1) -# x_list = sam_in_pos_list_2D[ii, :, 0] -# y_list = sam_in_pos_list_2D[ii, :, 1] -# z_list = sam_in_pos_list_2D[ii, :, 2] -# r_list = sam_in_pos_list_2D[ii, :, 3] -# out_x = sam_out_pos_list_2D[ii, :, 0] -# out_y = sam_out_pos_list_2D[ii, :, 1] -# out_z = sam_out_pos_list_2D[ii, :, 2] -# out_r = sam_out_pos_list_2D[ii, :, 3] -# print(x_list) -# print(y_list) -# print(z_list) -# print(r_list) -# print(out_x) -# print(out_y) -# print(out_z) -# print(out_r) -# yield from multipos_2D_xanes_scan2(eng_list[jj], x_list, y_list, z_list, r_list, -# out_x=out_x, out_y=out_y, out_z=out_z, out_r=out_r, -# exposure_time=exposure_time_2D[jj], chunk_size=5, -# simu=simu, relative_move_flag=relative_move_flag, note=note, md=None, sleep_time=0, repeat_num=1) -# -# if sam_in_pos_list_3D.size != 0: -# for ii in range(sam_in_pos_list_3D.shape[0]): -# for jj in range(len(elements)): -# if filters[jj]: -# select_filters(filters[jj]) -## yield from _close_shutter(simu=simu) -## yield from mv(filter1, 0) -## yield from mv(filter2, 0) -## yield from mv(filter3, 0) -## yield from mv(filter4, 0) -## for flt in filters[jj]: -## if flt == 'filter1': -## yield from mv(filter1, 1) -## elif flt == 'filter2': -## yield from mv(filter2, 1) -## elif flt == 'filter3': -## yield from mv(filter3, 1) -## elif flt == 'filter4': -## yield from mv(filter4, 1) -# x_list = sam_in_pos_list_3D[ii, :, 0] -# y_list = sam_in_pos_list_3D[ii, :, 1] -# z_list = sam_in_pos_list_3D[ii, :, 2] -# r_list = sam_in_pos_list_3D[ii, :, 3] -# out_x = sam_out_pos_list_3D[ii, :, 0] -# out_y = sam_out_pos_list_3D[ii, :, 1] -# out_z = sam_out_pos_list_3D[ii, :, 2] -# out_r = sam_out_pos_list_3D[ii, :, 3] -# print(x_list, out_x, out_y, out_z, out_r) -# yield from multi_pos_xanes_3D(eng_list[jj], x_list, y_list, z_list, r_list, -# exposure_time=exposure_time_3D[jj], relative_rot_angle=rel_rot_ang, rs=rs, -# out_x=out_x, out_y=out_y, out_z=out_z, out_r=out_r, note=note, simu=simu, -# relative_move_flag=relative_move_flag, traditional_sequence_flag=1, sleep_time=0, repeat=1) -# if kk != (repeat_num-1): -# print(f'We are in multi_pos_2D_and_3D_xanes cycle # {kk}; we are going to sleep for {sleep_time} seconds ...') -# yield from bps.sleep(sleep_time) -############ old routine: 2D routine works but 3D routine has some bugs -- end - - -def multi_pos_2D_and_3D_xanes( - elements=["Ni_wl"], - filters={"Ni_filters": [1, 2, 3]}, - sam_in_pos_list_2D={"Ni_2D_in_pos_list": [[0, 0, 0, 0]]}, - sam_out_pos_list_2D={"Ni_2D_out_pos_list": [[0, 0, 0, 0]]}, - sam_in_pos_list_3D={"Ni_3D_in_pos_list": [[0, 0, 0, 0]]}, - sam_out_pos_list_3D={"Ni_3D_out_pos_list": [[0, 0, 0, 0]]}, - exposure_time_2D={"Ni_2D_exp": 0.05}, - exposure_time_3D={"Ni_3D_exp": 0.05}, - rel_rot_ang=185, - rs=1, - sleep_time=0, - repeat_num=1, - note="", - relative_move_flag=0, - simu=False, -): - - yield from mv(Andor.cam.acquire, 0) - xanes2D = {} - xanes3D = {} - for kk in range(repeat_num): - for elem in elements: - ### if there is a filter combination is defined for the element - for key, item in sam_in_pos_list_2D.items(): - if elem.split("_")[0] == key.split("_")[0]: - xanes2D[elem + "_2D"] = {} - xanes2D[elem + "_2D"]["eng"] = elem - xanes2D[elem + "_2D"]["in_pos"] = item - xanes2D[elem + "_2D"]["in_pos_defined"] = True - for key, item in filters.items(): - if elem.split("_")[0] == key.split("_")[0]: - xanes2D[elem + "_2D"]["filter"] = item - else: - xanes2D[elem + "_2D"]["filter"] = [] - for key, item in sam_out_pos_list_2D.items(): - if elem.split("_")[0] == key.split("_")[0]: - xanes2D[elem + "_2D"]["out_pos"] = item - xanes2D[elem + "_2D"]["out_pos_defined"] = True - for key, item in exposure_time_2D.items(): - if elem.split("_")[0] == key.split("_")[0]: - xanes2D[elem + "_2D"]["exposure"] = item - xanes2D[elem + "_2D"]["exposure_defined"] = True - if not ( - xanes2D[elem + "_2D"]["in_pos_defined"] - & xanes2D[elem + "_2D"]["out_pos_defined"] - & xanes2D[elem + "_2D"]["exposure_defined"] - ): - print(elem + " 2D scan setup is not correct. Quit.") - sys.exit() - for elem in elements: - ### if there is a filter combination is defined for the element - for key, item in sam_in_pos_list_3D.items(): - if elem.split("_")[0] == key.split("_")[0]: - xanes3D[elem + "_3D"] = {} - xanes3D[elem + "_3D"]["eng"] = elem - xanes3D[elem + "_3D"]["in_pos"] = item - xanes3D[elem + "_3D"]["in_pos_defined"] = True - for key, item in filters.items(): - if elem.split("_")[0] == key.split("_")[0]: - xanes3D[elem + "_3D"]["filter"] = item - else: - xanes3D[elem + "_3D"]["filter"] = [] - for key, item in sam_out_pos_list_3D.items(): - if elem.split("_")[0] == key.split("_")[0]: - xanes3D[elem + "_3D"]["out_pos"] = item - xanes3D[elem + "_3D"]["out_pos_defined"] = True - for key, item in exposure_time_3D.items(): - if elem.split("_")[0] == key.split("_")[0]: - xanes3D[elem + "_3D"]["exposure"] = item - xanes3D[elem + "_3D"]["exposure_defined"] = True - if not ( - xanes3D[elem + "_3D"]["in_pos_defined"] - & xanes3D[elem + "_3D"]["out_pos_defined"] - & xanes3D[elem + "_3D"]["exposure_defined"] - ): - print(elem + " 3D scan setup is not correct. Quit.") - sys.exit() - for elem2D in xanes2D: - x_list_2D = [] - y_list_2D = [] - z_list_2D = [] - r_list_2D = [] - out_x_2D = [] - out_y_2D = [] - out_z_2D = [] - out_r_2D = [] - for inpos in elem2D["in_pos"]: - x_list_2D.append(inpos[0]) - y_list_2D.append(inpos[1]) - z_list_2D.append(inpos[2]) - r_list_2D.append(inpos[3]) - for outpos in elem2D["out_pos"]: - out_x_2D.append(outpos[0]) - out_y_2D.append(outpos[1]) - out_z_2D.append(outpos[2]) - out_r_2D.append(outpos[3]) - if len(x_list_2D) != len(out_x_2D): - print("x_list_2D and out_x_2D are not equal in length. Quit.") - sys.exit() - - select_filters(elem2D["filter"]) - - if elem2D["eng"].split("_")[-1] == "wl": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem2D["eng"].split("_")[0] - + "/eng_list_" - + elem2D["eng"].split("_")[0] - + "_s_xanes_standard_21pnt.txt" - ) - elif elem2D["eng"].split("_")[-1] == "101": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem2D["eng"].split("_") - + "/eng_list_" - + elem2D["eng"].split("_") - + "_xanes_standard_101pnt.txt" - ) - elif elem2D["eng"].split("_")[-1] == "63": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem2D["eng"].split("_") - + "/eng_list_" - + elem2D["eng"].split("_") - + "_xanes_standard_63pnt.txt" - ) - - yield from multipos_2D_xanes_scan2( - eng_list, - x_list_2D, - y_list_2D, - z_list_2D, - r_list_2D, - out_x=out_x_2D, - out_y=out_y_2D, - out_z=out_z_2D, - out_r=out_r_2D, - exposure_time=elem2D["exposure"], - chunk_size=5, - simu=simu, - relative_move_flag=relative_move_flag, - note=note, - md=None, - sleep_time=0, - repeat_num=1, - ) - - for elem3D in xanes3D: - x_list_3D = [] - y_list_3D = [] - z_list_3D = [] - r_list_3D = [] - out_x_3D = [] - out_y_3D = [] - out_z_3D = [] - out_r_3D = [] - for inpos in elem3D["in_pos"]: - x_list_3D.append(inpos[0]) - y_list_3D.append(inpos[1]) - z_list_3D.append(inpos[2]) - r_list_3D.append(inpos[3]) - for outpos in elem3D["out_pos"]: - out_x_3D.append(outpos[0]) - out_y_3D.append(outpos[1]) - out_z_3D.append(outpos[2]) - out_r_3D.append(outpos[3]) - if len(x_list_3D) != len(out_x_3D): - print("x_list_3D and out_x_3D are not equal in length. Quit.") - sys.exit() - - select_filters(elem3D["filter"]) - - if elem3D["eng"].split("_")[-1] == "wl": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem3D["eng"].split("_")[0] - + "/eng_list_" - + elem3D["eng"].split("_")[0] - + "_s_xanes_standard_21pnt.txt" - ) - elif elem3D["eng"].split("_")[-1] == "101": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem3D["eng"].split("_") - + "/eng_list_" - + elem3D["eng"].split("_") - + "_xanes_standard_101pnt.txt" - ) - elif elem3D["eng"].split("_")[-1] == "63": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem3D["eng"].split("_") - + "/eng_list_" - + elem3D["eng"].split("_") - + "_xanes_standard_63pnt.txt" - ) - - yield from multi_pos_xanes_3D( - eng_list, - x_list_3D, - y_list_3D, - z_list_3D, - r_list_3D, - exposure_time == elem3D["exposure"], - relative_rot_angle=rel_rot_ang, - rs=rs, - out_x=out_x_3D, - out_y=out_y_3D, - out_z=out_z_3D, - out_r=out_r_3D, - note=note, - simu=simu, - relative_move_flag=relative_move_flag, - traditional_sequence_flag=1, - sleep_time=0, - repeat=1, - ) - - -# find = False -# defined = False -# for flt_elem in filters.keys(): -# if elem.split("_")[0] == flt_elem.split("_")[0]: -# find = True -# if find is False: -# print("There is not filters defined for ", elem, "!") -# sys.exit(1) -# -# ### if there are 2D_sam_in and 2D_sam_out positions defined for the element -# find = False -# for in_elem in sam_in_pos_list_2D.keys(): -# if elem.split("_")[0] == in_elem.split("_")[0]: -# find = True -# if find: -# find = False -# for out_elem in sam_out_pos_list_2D.keys(): -# if elem.split("_")[0] == out_elem.split("_")[0]: -# find = True -# if find is False: -# print( -# elem, "2D_in_pos_list and", elem, "2D_in_pos_list dont match!" -# ) -# sys.exit(1) -# if find: -# find = False -# for exp_elem in exposure_time_2D.keys(): -# print(1, elem.split("_"), exp_elem.split("_"), find) -# if elem.split("_")[0] == exp_elem.split("_")[0]: -# find = True -# if find is False: -# print(2, elem.split("_"), exp_elem.split("_")) -# print("There is not exposure_time_2D defined for", elem) -# sys.exit(1) -# if find: -# defined = True -# -# ### if there are 3D_sam_in and 3D_sam_out positions defined for the element -# find = False -# for in_elem in sam_in_pos_list_3D.keys(): -# if elem.split("_")[0] == in_elem.split("_")[0]: -# find = True -# if find: -# find = False -# for out_elem in sam_out_pos_list_3D.keys(): -# if elem.split("_")[0] == out_elem.split("_")[0]: -# find = True -# if find is False: -# print( -# elem, "3D_in_pos_list and", elem, "3D_in_pos_list dont match!" -# ) -# sys.exit(1) -# if find: -# find = False -# for exp_elem in exposure_time_3D.keys(): -# if elem.split("_")[0] == exp_elem.split("_")[0]: -# find = True -# if find is False: -# print("There is not exposure_time_3D defined for", elem) -# sys.exit(1) -# if find: -# defined = True -# -# if not defined: -# print("There is neither 2D nor 3D position list defined for", elem) -# sys.exit() -# -# for elem in elements: -# select_filters(filters[elem.split("_")[0] + "_filters"]) -# -# if ii.split("_")[1] == "wl": -# eng_list = np.genfromtxt( -# "/nsls2/data/fxi-new/shared/config/xanes_ref/" -# + ii.split("_")[0] -# + "/eng_list_" -# + ii.split("_")[0] -# + "_s_xanes_standard_21pnt.txt" -# ) -# elif ii.split("_")[1] == "101": -# eng_list = np.genfromtxt( -# "/nsls2/data/fxi-new/shared/config/xanes_ref/" -# + ii -# + "/eng_list_" -# + ii -# + "_xanes_standard_101pnt.txt" -# ) -# elif ii.split("_")[1] == "63": -# eng_list = np.genfromtxt( -# "/nsls2/data/fxi-new/shared/config/xanes_ref/" -# + ii -# + "/eng_list_" -# + ii -# + "_xanes_standard_63pnt.txt" -# ) -# -# if sam_in_pos_list_2D[elem.split("_")[0] + "_2D_in_pos_list"]: -# x_list_2D = np.asarray( -# sam_in_pos_list_2D[elem.split("_")[0] + "_2D_in_pos_list"] -# )[0, :] -# y_list_2D = np.asarray( -# sam_in_pos_list_2D[elem.split("_")[0] + "_2D_in_pos_list"] -# )[1, :] -# z_list_2D = np.asarray( -# sam_in_pos_list_2D[elem.split("_")[0] + "_2D_in_pos_list"] -# )[2, :] -# r_list_2D = np.asarray( -# sam_in_pos_list_2D[elem.split("_")[0] + "_2D_in_pos_list"] -# )[3, :] -# if sam_out_pos_list_2D[elem.split("_")[0] + "_2D_out_pos_list"]: -# out_x_2D = np.asarray( -# sam_out_pos_list_2D[elem.split("_")[0] + "_2D_out_pos_list"] -# )[0, :] -# out_y_2D = np.asarray( -# sam_out_pos_list_2D[elem.split("_")[0] + "_2D_out_pos_list"] -# )[1, :] -# out_z_2D = np.asarray( -# sam_out_pos_list_2D[elem.split("_")[0] + "_2D_out_pos_list"] -# )[2, :] -# out_r_2D = np.asarray( -# sam_out_pos_list_2D[elem.split("_")[0] + "_2D_out_pos_list"] -# )[3, :] -# else: -# print(elem, "_2D_out_pos_list is not defined!") -# sys.exit(1) -# -# if exposure_time_2D[elem.split("_")[0] + "_2D_exp"]: -# exp_2D = exposure_time_2D[elem.split("_")[0] + "_2D_exp"] -# else: -# print(elem, "_2D_exp is not defined!") -# sys.exit(1) -# -# yield from multipos_2D_xanes_scan2( -# eng_list, -# x_list_2D, -# y_list_2D, -# z_list_2D, -# r_list_2D, -# out_x=out_x_2D, -# out_y=out_y_2D, -# out_z=out_z_2D, -# out_r=out_r_2D, -# exposure_time=exp_2D, -# chunk_size=5, -# simu=simu, -# relative_move_flag=relative_move_flag, -# note=note, -# md=None, -# sleep_time=0, -# repeat_num=1, -# ) -# -# if sam_in_pos_list_3D[elem.split("_")[0] + "_3D_in_pos_list"]: -# x_list_3D = np.asarray( -# sam_in_pos_list_3D[elem.split("_")[0] + "_3D_in_pos_list"] -# )[0, :] -# y_list_3D = np.asarray( -# sam_in_pos_list_3D[elem.split("_")[0] + "_3D_in_pos_list"] -# )[1, :] -# z_list_3D = np.asarray( -# sam_in_pos_list_3D[elem.split("_")[0] + "_3D_in_pos_list"] -# )[2, :] -# r_list_3D = np.asarray( -# sam_in_pos_list_3D[elem.split("_")[0] + "_3D_in_pos_list"] -# )[3, :] -# if sam_out_pos_list_3D[elem.split("_")[0] + "_3D_out_pos_list"]: -# out_x_3D = np.asarray( -# sam_out_pos_list_3D[elem.split("_")[0] + "_3D_out_pos_list"] -# )[0, :] -# out_y_3D = np.asarray( -# sam_out_pos_list_3D[elem.split("_")[0] + "_3D_out_pos_list"] -# )[1, :] -# out_z_3D = np.asarray( -# sam_out_pos_list_3D[elem.split("_")[0] + "_3D_out_pos_list"] -# )[2, :] -# out_r_3D = np.asarray( -# sam_out_pos_list_3D[elem.split("_")[0] + "_3D_out_pos_list"] -# )[3, :] -# else: -# print(elem, "_3D_out_pos_list is not defined!") -# sys.exit(1) -# if exposure_time_3D[elem.split("_")[0] + "_3D_exp"]: -# exp_3D = exposure_time_3D[elem.split("_")[0] + "_3D_exp"] -# else: -# print(elem, "_3D_exp is not defined!") -# sys.exit(1) -# -# yield from multi_pos_xanes_3D( -# eng_list, -# x_list_3D, -# y_list_3D, -# z_list_3D, -# r_list_3D, -# exposure_time=exp_3D, -# relative_rot_angle=rel_rot_ang, -# rs=rs, -# out_x=out_x_3D, -# out_y=out_y_3D, -# out_z=out_z_3D, -# out_r=out_r_3D, -# note=note, -# simu=simu, -# relative_move_flag=relative_move_flag, -# traditional_sequence_flag=1, -# sleep_time=0, -# repeat=1, -# ) -# -# if kk != (repeat_num - 1): -# print( -# f"We are in multi_pos_2D_and_3D_xanes cycle # {kk}; we are going to sleep for {sleep_time} seconds ..." -# ) -# yield from bps.sleep(sleep_time) - - -# for kk in range(repeat_num): -# for elem in elements: -# ### if there is a filter combination is defined for the element -# find = False -# defined = False -# for flt_elem in filters.keys(): -# if elem.split("_")[0] == flt_elem.split("_")[0]: -# find = True -# if find is False: -# print("There is not filters defined for ", elem, "!") -# sys.exit(1) -# -# ### if there are 2D_sam_in and 2D_sam_out positions defined for the element -# find = False -# for in_elem in sam_in_pos_list_2D.keys(): -# if elem.split("_")[0] == in_elem.split("_")[0]: -# find = True -# if find: -# find = False -# for out_elem in sam_out_pos_list_2D.keys(): -# if elem.split("_")[0] == out_elem.split("_")[0]: -# find = True -# if find is False: -# print( -# elem, "2D_in_pos_list and", elem, "2D_in_pos_list dont match!" -# ) -# sys.exit(1) -# if find: -# find = False -# for exp_elem in exposure_time_2D.keys(): -# print(1, elem.split("_"), exp_elem.split("_"), find) -# if elem.split("_")[0] == exp_elem.split("_")[0]: -# find = True -# if find is False: -# print(2, elem.split("_"), exp_elem.split("_")) -# print("There is not exposure_time_2D defined for", elem) -# sys.exit(1) -# if find: -# defined = True -# -# ### if there are 3D_sam_in and 3D_sam_out positions defined for the element -# find = False -# for in_elem in sam_in_pos_list_3D.keys(): -# if elem.split("_")[0] == in_elem.split("_")[0]: -# find = True -# if find: -# find = False -# for out_elem in sam_out_pos_list_3D.keys(): -# if elem.split("_")[0] == out_elem.split("_")[0]: -# find = True -# if find is False: -# print( -# elem, "3D_in_pos_list and", elem, "3D_in_pos_list dont match!" -# ) -# sys.exit(1) -# if find: -# find = False -# for exp_elem in exposure_time_3D.keys(): -# if elem.split("_")[0] == exp_elem.split("_")[0]: -# find = True -# if find is False: -# print("There is not exposure_time_3D defined for", elem) -# sys.exit(1) -# if find: -# defined = True -# -# if not defined: -# print("There is neither 2D nor 3D position list defined for", elem) -# sys.exit() -# -# for elem in elements: -# select_filters(filters[elem.split("_")[0] + "_filters"]) -# -# if ii.split("_")[1] == "wl": -# eng_list = np.genfromtxt( -# "/nsls2/data/fxi-new/shared/config/xanes_ref/" -# + ii.split("_")[0] -# + "/eng_list_" -# + ii.split("_")[0] -# + "_s_xanes_standard_21pnt.txt" -# ) -# elif ii.split("_")[1] == "101": -# eng_list = np.genfromtxt( -# "/nsls2/data/fxi-new/shared/config/xanes_ref/" -# + ii -# + "/eng_list_" -# + ii -# + "_xanes_standard_101pnt.txt" -# ) -# elif ii.split("_")[1] == "63": -# eng_list = np.genfromtxt( -# "/nsls2/data/fxi-new/shared/config/xanes_ref/" -# + ii -# + "/eng_list_" -# + ii -# + "_xanes_standard_63pnt.txt" -# ) -# -# if sam_in_pos_list_2D[elem.split("_")[0] + "_2D_in_pos_list"]: -# x_list_2D = np.asarray( -# sam_in_pos_list_2D[elem.split("_")[0] + "_2D_in_pos_list"] -# )[0, :] -# y_list_2D = np.asarray( -# sam_in_pos_list_2D[elem.split("_")[0] + "_2D_in_pos_list"] -# )[1, :] -# z_list_2D = np.asarray( -# sam_in_pos_list_2D[elem.split("_")[0] + "_2D_in_pos_list"] -# )[2, :] -# r_list_2D = np.asarray( -# sam_in_pos_list_2D[elem.split("_")[0] + "_2D_in_pos_list"] -# )[3, :] -# if sam_out_pos_list_2D[elem.split("_")[0] + "_2D_out_pos_list"]: -# out_x_2D = np.asarray( -# sam_out_pos_list_2D[elem.split("_")[0] + "_2D_out_pos_list"] -# )[0, :] -# out_y_2D = np.asarray( -# sam_out_pos_list_2D[elem.split("_")[0] + "_2D_out_pos_list"] -# )[1, :] -# out_z_2D = np.asarray( -# sam_out_pos_list_2D[elem.split("_")[0] + "_2D_out_pos_list"] -# )[2, :] -# out_r_2D = np.asarray( -# sam_out_pos_list_2D[elem.split("_")[0] + "_2D_out_pos_list"] -# )[3, :] -# else: -# print(elem, "_2D_out_pos_list is not defined!") -# sys.exit(1) -# -# if exposure_time_2D[elem.split("_")[0] + "_2D_exp"]: -# exp_2D = exposure_time_2D[elem.split("_")[0] + "_2D_exp"] -# else: -# print(elem, "_2D_exp is not defined!") -# sys.exit(1) -# -# yield from multipos_2D_xanes_scan2( -# eng_list, -# x_list_2D, -# y_list_2D, -# z_list_2D, -# r_list_2D, -# out_x=out_x_2D, -# out_y=out_y_2D, -# out_z=out_z_2D, -# out_r=out_r_2D, -# exposure_time=exp_2D, -# chunk_size=5, -# simu=simu, -# relative_move_flag=relative_move_flag, -# note=note, -# md=None, -# sleep_time=0, -# repeat_num=1, -# ) -# -# if sam_in_pos_list_3D[elem.split("_")[0] + "_3D_in_pos_list"]: -# x_list_3D = np.asarray( -# sam_in_pos_list_3D[elem.split("_")[0] + "_3D_in_pos_list"] -# )[0, :] -# y_list_3D = np.asarray( -# sam_in_pos_list_3D[elem.split("_")[0] + "_3D_in_pos_list"] -# )[1, :] -# z_list_3D = np.asarray( -# sam_in_pos_list_3D[elem.split("_")[0] + "_3D_in_pos_list"] -# )[2, :] -# r_list_3D = np.asarray( -# sam_in_pos_list_3D[elem.split("_")[0] + "_3D_in_pos_list"] -# )[3, :] -# if sam_out_pos_list_3D[elem.split("_")[0] + "_3D_out_pos_list"]: -# out_x_3D = np.asarray( -# sam_out_pos_list_3D[elem.split("_")[0] + "_3D_out_pos_list"] -# )[0, :] -# out_y_3D = np.asarray( -# sam_out_pos_list_3D[elem.split("_")[0] + "_3D_out_pos_list"] -# )[1, :] -# out_z_3D = np.asarray( -# sam_out_pos_list_3D[elem.split("_")[0] + "_3D_out_pos_list"] -# )[2, :] -# out_r_3D = np.asarray( -# sam_out_pos_list_3D[elem.split("_")[0] + "_3D_out_pos_list"] -# )[3, :] -# else: -# print(elem, "_3D_out_pos_list is not defined!") -# sys.exit(1) -# if exposure_time_3D[elem.split("_")[0] + "_3D_exp"]: -# exp_3D = exposure_time_3D[elem.split("_")[0] + "_3D_exp"] -# else: -# print(elem, "_3D_exp is not defined!") -# sys.exit(1) -# -# yield from multi_pos_xanes_3D( -# eng_list, -# x_list_3D, -# y_list_3D, -# z_list_3D, -# r_list_3D, -# exposure_time=exp_3D, -# relative_rot_angle=rel_rot_ang, -# rs=rs, -# out_x=out_x_3D, -# out_y=out_y_3D, -# out_z=out_z_3D, -# out_r=out_r_3D, -# note=note, -# simu=simu, -# relative_move_flag=relative_move_flag, -# traditional_sequence_flag=1, -# sleep_time=0, -# repeat=1, -# ) -# -# if kk != (repeat_num - 1): -# print( -# f"We are in multi_pos_2D_and_3D_xanes cycle # {kk}; we are going to sleep for {sleep_time} seconds ..." -# ) -# yield from bps.sleep(sleep_time) - - -def multi_pos_2D_xanes_and_3D_tomo( - elements=["Ni"], - sam_in_pos_list_2D=[[[0, 0, 0, 0]]], - sam_out_pos_list_2D=[[[0, 0, 0, 0]]], - sam_in_pos_list_3D=[[[0, 0, 0, 0]]], - sam_out_pos_list_3D=[[[0, 0, 0, 0]]], - exposure_time_2D=[0.05], - exposure_time_3D=[0.05], - rel_rot_ang=0, - rs=1, - eng_3D=[10, 60], - note="", - relative_move_flag=0, - simu=False, -): - yield from mv(Andor.cam.acquire, 0) - sam_in_pos_list_2D = np.asarray(sam_in_pos_list_2D) - sam_out_pos_list_2D = np.asarray(sam_out_pos_list_2D) - sam_in_pos_list_3D = np.asarray(sam_in_pos_list_3D) - sam_out_pos_list_3D = np.asarray(sam_out_pos_list_3D) - exposure_time_2D = np.asarray(exposure_time_2D) - exposure_time_3D = np.asarray(exposure_time_3D) - if exposure_time_2D.shape[0] == 1: - exposure_time_2D = np.ones(len(elements)) * exposure_time_2D[0] - elif len(elements) != exposure_time_2D.shape[0]: - # to do in bs manner - pass - - if exposure_time_3D.shape[0] == 1: - exposure_time_3D = np.ones(len(elements)) * exposure_time_3D[0] - elif len(elements) != exposure_time_3D.shape[0]: - # to do in bs manner - pass - - eng_list = [] - for ii in elements: - eng_list.append( - list( - np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + ii - + "/eng_list_" - + ii - + "_xanes_standard.txt" - ) - ) - ) - eng_list = np.array(eng_list) - - if sam_in_pos_list_2D.size != 0: - for ii in range(sam_in_pos_list_2D.shape[0]): - for jj in range(len(elements)): - x_list = sam_in_pos_list_2D[ii, :, 0] - y_list = sam_in_pos_list_2D[ii, :, 1] - z_list = sam_in_pos_list_2D[ii, :, 2] - r_list = sam_in_pos_list_2D[ii, :, 3] - out_x = sam_out_pos_list_2D[ii, :, 0] - out_y = sam_out_pos_list_2D[ii, :, 1] - out_z = sam_out_pos_list_2D[ii, :, 2] - out_r = sam_out_pos_list_2D[ii, :, 3] - print(x_list) - print(y_list) - print(z_list) - print(r_list) - print(out_x) - print(out_y) - print(out_z) - print(out_r) - yield from multipos_2D_xanes_scan2( - eng_list[jj], - x_list, - y_list, - z_list, - r_list, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - exposure_time=exposure_time_2D[jj], - chunk_size=5, - simu=simu, - relative_move_flag=relative_move_flag, - note=note, - md=None, - sleep_time=0, - repeat_num=1, - ) - - if sam_in_pos_list_3D.size != 0: - for ii in range(sam_in_pos_list_3D.shape[0]): - for jj in range(len(elements)): - x_list = sam_in_pos_list_3D[ii, :, 0] - y_list = sam_in_pos_list_3D[ii, :, 1] - z_list = sam_in_pos_list_3D[ii, :, 2] - r_list = sam_in_pos_list_3D[ii, :, 3] - out_x = sam_out_pos_list_3D[ii, :, 0] - out_y = sam_out_pos_list_3D[ii, :, 1] - out_z = sam_out_pos_list_3D[ii, :, 2] - out_r = sam_out_pos_list_3D[ii, :, 3] - yield from multi_pos_xanes_3D( - eng_list[jj, eng_3D], - x_list, - y_list, - z_list, - r_list, - exposure_time=exposure_time_3D[jj], - relative_rot_angle=rel_rot_ang, - rs=rs, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - note=note, - simu=simu, - relative_move_flag=relative_move_flag, - traditional_sequence_flag=1, - sleep_time=0, - repeat=1, - ) - - -def zps_motor_scan_with_Andor( - motors, - starts, - ends, - num_steps, - out_x=100, - out_y=0, - out_z=0, - out_r=0, - exposure_time=None, - period=None, - chunk_size=1, - note="", - relative_move_flag=1, - simu=False, - rot_first_flag=0, - md=None, -): - global ZONE_PLATE - dets = [Andor, ic3] - - # if len(out_x) != len(motors): - # out_x = [out_x[0]] * len(motors) - # - # if len(out_y) != len(motors): - # out_y = [out_y[0]] * len(motors) - # - # if len(out_z) != len(motors): - # out_z = [out_z[0]] * len(motors) - # - # if len(out_r) != len(motors): - # out_r = [out_r[0]] * len(motors) - - def _set_andor_param(): - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, chunk_size) - yield from mv(Andor.cam.acquire_time, exposure_time) - yield from mv(Andor.cam.acquire_period, exposure_time) - - if exposure_time is not None: - yield from _set_andor_param() - - mot_ini = [] - mot_start = [] - mot_end = [] - for start, end, motor in zip(starts, ends, motors): - mot_ini.append(getattr(motor, "position")) - mot_start.append(getattr(motor, "position") + start) - mot_end.append(getattr(motor, "position") + end) - - mot_num_step = np.int_(num_steps) - # - # - # motor_out = [] - # if relative_move_flag: - # for motor in motors: - # motor_out.append(motor_ini + out) - - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if out_x else motor_x_ini - motor_y_out = motor_y_ini + out_y if out_y else motor_y_ini - motor_z_out = motor_z_ini + out_z if out_z else motor_z_ini - motor_r_out = motor_r_ini + out_r if out_r else motor_r_ini - else: - motor_x_out = out_x if out_x else motor_x_ini - motor_y_out = out_y if out_y else motor_y_ini - motor_z_out = out_z if out_z else motor_z_ini - motor_r_out = out_r if out_r else motor_r_ini - - print("hello1") - _md = { - "detectors": [det.name for det in dets], - "motors": [mot.name for mot in motors], - "num_bkg_images": 5, - "num_dark_images": 5, - "mot_start": starts, - "motor_end": ends, - "motor_num_step": mot_num_step, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "out_r": out_r, - "exposure_time": exposure_time, - "chunk_size": chunk_size, - "XEng": XEng.position, - "plan_args": { - "mot_start": mot_start, - "mot_end": mot_end, - "mot_num_step": mot_num_step, - "exposure_time": exposure_time, - "chunk_size": chunk_size, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "out_r": out_r, - "note": note if note else "None", - "relative_move_flag": relative_move_flag, - "rot_first_flag": rot_first_flag, - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - }, - "plan_name": "zps_motor_scan_with_Andor", - "hints": {}, - "operator": "FXI", - "zone_plate": ZONE_PLATE, - "note": note if note else "None", - #'motor_pos': wh_pos(print_on_screen=0), - } - _md.update(md or {}) - try: - dimensions = [(motors.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - @stage_decorator(list(dets) + motors) - @run_decorator(md=_md) - def zps_motor_scan_inner(): - # take dark image - print("take 5 dark image") - yield from _take_dark_image(dets, motors, num_dark=5) - - print("open shutter ...") - yield from _open_shutter(simu) - - print("taking mosaic image ...") - if len(motors) == 1: - mot_pos = np.linspace( - mot_start[0], mot_end[0], mot_num_step[0], endpoint=False - ) - elif len(motors) == 2: - mot_pos_coor1, mot_pos_coor2 = np.meshgrid( - np.linspace(mot_start[0], mot_end[0], mot_num_step[0], endpoint=False), - np.linspace(mot_start[1], mot_end[1], mot_num_step[1], endpoint=False), - ) - mot_pos = np.array([mot_pos_coor1.flatten(), mot_pos_coor2.flatten()]) - elif len(motors) == 3: - mot_pos_coor1, mot_pos_coor2, mot_pos_coor3 = np.meshgrid( - np.linspace(mot_start[0], mot_end[0], mot_num_step[0], endpoint=False), - np.linspace(mot_start[1], mot_end[1], mot_num_step[1], endpoint=False), - np.linspace(mot_start[2], mot_end[2], mot_num_step[2], endpoint=False), - ) - mot_pos = np.array( - [ - mot_pos_coor1.flatten(), - mot_pos_coor2.flatten(), - mot_pos_coor3.flatten(), - ] - ) - elif len(motors) == 4: - mot_pos_coor1, mot_pos_coor2, mot_pos_coor3, mot_pos_coor4 = np.meshgrid( - np.linspace(mot_start[0], mot_end[0], mot_num_step[0], endpoint=False), - np.linspace(mot_start[1], mot_end[1], mot_num_step[1], endpoint=False), - np.linspace(mot_start[2], mot_end[2], mot_num_step[2], endpoint=False), - np.linspace(mot_start[3], mot_end[3], mot_num_step[3], endpoint=False), - ) - mot_pos = np.array( - [ - mot_pos_coor1.flatten(), - mot_pos_coor2.flatten(), - mot_pos_coor3.flatten(), - mot_pos_coor4.flatten(), - ] - ) - - for jj in range(mot_pos.shape[1]): - # yield from mv(motors, mot_pos[:, jj]) - for ii in range(len(motors)): - yield from mv(motors[ii], mot_pos[ii, jj]) - yield from _take_image(dets, motors, 1) - - print("moving sample out to take 5 background image") - yield from _take_bkg_image( - motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - dets, - motors, - num_bkg=5, - simu=simu, - traditional_sequence_flag=rot_first_flag, - ) - - # move sample in - yield from _move_sample_in( - motor_x_ini, - motor_y_ini, - motor_z_ini, - motor_r_ini, - repeat=1, - trans_first_flag=1 - rot_first_flag, - ) - - print("closing shutter") - yield from _close_shutter(simu) - - yield from zps_motor_scan_inner() - yield from mv(Andor.cam.image_mode, 1) - print("scan finished") - txt = get_scan_parameter() - insert_text(txt) - print(txt) - - -def diff_tomo( - sam_in_pos_list=[ - [0, 0, 0, 0], - ], - sam_out_pos_list=[ - [0, 0, 0, 0], - ], - exposure=[0.05], - period=[0.05], - rel_rot_ang=182, - rs=1, - eng=None, - note="", - filters=[], - relative_move_flag=0, - md=None, -): - sam_in_pos_list = np.array(sam_in_pos_list) - sam_out_pos_list = np.array(sam_out_pos_list) - - if eng is None: - print("Please specify two energies as a list for differential tomo scans.") - return - - if len(exposure) != sam_in_pos_list.shape[0]: - exposure = np.ones(sam_in_pos_list.shape[0]) * exposure[0] - - if len(period) != sam_in_pos_list.shape[0]: - period = np.ones(sam_in_pos_list.shape[0]) * period[0] - - for jj in range(sam_in_pos_list.shape[0]): - for ii in range(len(eng)): - yield from move_zp_ccd( - eng[ii], move_flag=1, info_flag=1, move_clens_flag=0, move_det_flag=0 - ) - yield from mv( - zps.sx, - sam_in_pos_list[jj, 0], - zps.sy, - sam_in_pos_list[jj, 1], - zps.sz, - sam_in_pos_list[jj, 2], - ) - yield from mv(zps.pi_r, sam_in_pos_list[jj, 3]) - yield from fly_scan( - exposure_time=exposure[jj], - relative_rot_angle=rel_rot_ang, - period=period[jj], - chunk_size=20, - out_x=sam_out_pos_list[jj, 0], - out_y=sam_out_pos_list[jj, 1], - out_z=sam_out_pos_list[jj, 2], - out_r=sam_out_pos_list[jj, 3], - rs=rs, - note=note, - simu=False, - relative_move_flag=relative_move_flag, - traditional_sequence_flag=1, - filters=filters, - md=md, - ) - - -def damon_scan( - eng_list1, - eng_list2, - x_list, - y_list, - z_list, - r_list, - exposure_time1=10.0, - exposure_time2=10.0, - chunk_size1=1, - chunk_size2=1, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - iters=10, - sleep_time=1, - note="", -): - - export_pdf(1) - insert_text('start "damon_scan"') - x_list = np.array(x_list) - y_list = np.array(y_list) - z_list = np.array(z_list) - for n in range(iters): - print(f"iteration # {n+1} / {iters}") - """ - yield from move_zp_ccd(6.5) - for i in range(4): - yield from mv(filter2, 1) - yield from mv(filter4, 1) - yield from xanes_scan2() # for Mn - - - yield from move_zp_ccd(9) - for i in range(4): - yield from mv(filter2, 1) - yield from mv(filter4, 1) - yield from xanes_scan2() # for Cu - """ - - yield from move_zp_ccd(6.5, move_flag=1, move_clens_flag=1, move_det_flag=0) - # for i in range(4): - # yield from mv(filter1, 0) - # yield from mv(filter2, 0) - # yield from mv(filter3, 0) - # yield from mv(filter4, 0) - # yield from mv(ssa.v_gap, 1) - - # yield from multipos_2D_xanes_scan2(eng_list1, x_list, y_list, z_list, r_list, out_x, out_y, out_z, out_r, repeat_num=1, exposure_time=exposure_time1, sleep_time=0, chunk_size=chunk_size1, relative_move_flag=1, note=note) - - # once move energy above 8.86 keV, we have a sample shift of -40(x) and -20(y), - # the sample at focus will not be at rotation center, but it is ok if doing 2D XANES - - yield from move_zp_ccd( - eng_list2[0], move_flag=1, move_clens_flag=1, move_det_flag=0 - ) - for i in range(4): - yield from mv(filter1, 0) - yield from mv(filter2, 0) - yield from mv(filter3, 1) - yield from mv(filter4, 1) - yield from mv(ssa.v_gap, 0.2) - yield from multipos_2D_xanes_scan2( - eng_list2, - x_list, - y_list, - z_list, - r_list, - out_x, - out_y, - out_z, - out_r, - repeat_num=1, - exposure_time=exposure_time2, - sleep_time=0, - chunk_size=chunk_size2, - relative_move_flag=1, - note=note, - ) - - print(f"sleep for {sleep_time} sec") - yield from bps.sleep(sleep_time) - - print(f"finished scan, now moving back to {eng_list1[0]} keV") - yield from mv(zps.sx, x_list[0], zps.sy, y_list[0], zps.sz, z_list[0]) - yield from move_zp_ccd( - eng_list1[0], move_flag=1, move_clens_flag=1, move_det_flag=0 - ) - insert_text('finish "damon scan"') - - -def user_fly_scan( - exposure_time=0.1, - rel_rot_ang=180, - period=0.15, - chunk_size=20, - out_x=None, - out_y=2000, - out_z=None, - out_r=None, - rs=1, - note="", - simu=False, - relative_move_flag=1, - traditional_sequence_flag=1, - filters=[], - md=None, -): - """ - Inputs: - ------- - exposure_time: float, in unit of sec - - rel_rot_ang: float, - total rotation angles start from current rotary stage (zps.pi_r) position - - period: float, in unit of sec - period of taking images, "period" should >= "exposure_time" - - chunk_size: int, default setting is 20 - number of images taken for each trigger of Andor camera - - out_x: float, default is 0 - relative movement of sample in "x" direction using zps.sx to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_y: float, default is 0 - relative movement of sample in "y" direction using zps.sy to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_z: float, default is 0 - relative movement of sample in "z" direction using zps.sz to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_r: float, default is 0 - relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - rs: float, default is 1 - rotation speed in unit of deg/sec - - note: string - adding note to the scan - - simu: Bool, default is False - True: will simulate closing/open shutter without really closing/opening - False: will really close/open shutter - - """ - global ZONE_PLATE - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if out_x else motor_x_ini - motor_y_out = motor_y_ini + out_y if out_y else motor_y_ini - motor_z_out = motor_z_ini + out_z if out_z else motor_z_ini - motor_r_out = motor_r_ini + out_r if out_r else motor_r_ini - else: - motor_x_out = out_x if out_x else motor_x_ini - motor_y_out = out_y if out_y else motor_y_ini - motor_z_out = out_z if out_z else motor_z_ini - motor_r_out = out_r if out_r else motor_r_ini - - motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - - dets = [Andor, ic3] - taxi_ang = -0.5 * rs - cur_rot_ang = zps.pi_r.position - - tgt_rot_ang = cur_rot_ang + rel_rot_ang - _md = { - "detectors": ["Andor"], - "motors": [mot.name for mot in motor], - "XEng": XEng.position, - "ion_chamber": ic3.name, - "plan_args": { - "exposure_time": exposure_time, - "relative_rot_angle": rel_rot_ang, - "period": period, - "chunk_size": chunk_size, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "out_r": out_r, - "rs": rs, - "relative_move_flag": relative_move_flag, - "traditional_sequence_flag": traditional_sequence_flag, - "filters": [filt.name for filt in filters] if filters else "None", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - }, - "plan_name": "fly_scan", - "num_bkg_images": 20, - "num_dark_images": 20, - "chunk_size": chunk_size, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - #'motor_pos': wh_pos(print_on_screen=0), - } - _md.update(md or {}) - try: - dimensions = [(zps.pi_r.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - # yield from _set_andor_param(exposure_time=exposure_time, period=period, chunk_size=chunk_size) - yield from _set_rotation_speed(rs=rs) - print("set rotation speed: {} deg/sec".format(rs)) - - @stage_decorator(list(dets) + motor) - @bpp.monitor_during_decorator([zps.pi_r]) - @run_decorator(md=_md) - def fly_inner_scan(): - # close shutter, dark images: numer=chunk_size (e.g.20) - print("\nshutter closed, taking dark images...") - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=20 - ) - yield from _take_dark_image(dets, motor, num_dark=1, simu=simu) - yield from bps.sleep(1) - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=chunk_size - ) - - # open shutter, tomo_images - yield from _open_shutter(simu=simu) - print("\nshutter opened, taking tomo images...") - yield from mv(zps.pi_r, cur_rot_ang + taxi_ang) - status = yield from abs_set(zps.pi_r, tgt_rot_ang, wait=False) - yield from bps.sleep(1) - while not status.done: - yield from trigger_and_read(list(dets) + motor) - # bkg images - print("\nTaking background images...") - yield from _set_rotation_speed(rs=30) - # yield from abs_set(zps.pi_r.velocity, rs) - for flt in filters: - yield from mv(flt, 1) - yield from mv(flt, 1) - yield from bps.sleep(1) - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=20 - ) - yield from _take_bkg_image( - motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - dets, - motor, - num_bkg=1, - simu=False, - traditional_sequence_flag=traditional_sequence_flag, - ) - yield from _close_shutter(simu=simu) - yield from _move_sample_in( - motor_x_ini, - motor_y_ini, - motor_z_ini, - motor_r_ini, - trans_first_flag=traditional_sequence_flag, - ) - for flt in filters: - yield from mv(flt, 0) - - uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) - print("scan finished") - txt = get_scan_parameter(print_flag=0) - insert_text(txt) - print(txt) - return uid - - -def user_fly_only( - exposure_time=0.1, - end_rot_angle=180, - period=0.15, - chunk_size=20, - rs=1, - note="", - simu=False, - dark_scan_id=0, - bkg_scan_id=0, - md=None, -): - - global ZONE_PLATE - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - - dets = [Andor, ic3] - # taxi_ang = 0 #-0.5 * rs * np.sign(rel_rot_ang) - cur_rot_ang = zps.pi_r.position - - tgt_rot_ang = end_rot_angle - _md = { - "detectors": ["Andor"], - "motors": [mot.name for mot in motor], - "XEng": XEng.position, - "ion_chamber": ic3.name, - "plan_args": { - "exposure_time": exposure_time, - "end_rot_angle": end_rot_angle, - "period": period, - "chunk_size": chunk_size, - "rs": rs, - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - "dark_scan_id": dark_scan_id, - "bkg_scan_id": bkg_scan_id, - }, - "plan_name": "user_fly_only", - "chunk_size": chunk_size, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - #'motor_pos': wh_pos(print_on_screen=0), - } - _md.update(md or {}) - try: - dimensions = [(zps.pi_r.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=chunk_size - ) - yield from _set_rotation_speed(rs=rs) - print("set rotation speed: {} deg/sec".format(rs)) - - @stage_decorator(list(dets) + motor) - @bpp.monitor_during_decorator([zps.pi_r]) - @run_decorator(md=_md) - def fly_inner_scan(): - yield from _open_shutter(simu=simu) - status = yield from abs_set(zps.pi_r, tgt_rot_ang, wait=False) - while not status.done: - yield from trigger_and_read(list(dets) + motor) - - uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) - print("scan finished") - # yield from _set_rotation_speed(rs=30) - txt = get_scan_parameter(print_flag=0) - insert_text(txt) - print(txt) - return uid - - -def user_dark_only(exposure_time=0.1, chunk_size=20, note="", simu=False, md=None): - """ - Take dark field images. - - Inputs: - ------- - exposure_time: float, in unit of sec - - chunk_size: int, default setting is 20 - number of images taken for each trigger of Andor camera - - note: string - adding note to the scan - - simu: Bool, default is False - True: will simulate closing/open shutter without really closing/opening - False: will really close/open shutter - - """ - global ZONE_PLATE - period = exposure_time # default to exposure time for backgrounds - dets = [Andor, ic3] - motor = [] - - _md = { - "detectors": ["Andor"], - "XEng": XEng.position, - "ion_chamber": ic3.name, - "plan_args": { - "exposure_time": exposure_time, - "chunk_size": chunk_size, - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - }, - "plan_name": "user_dark_only", - "chunk_size": chunk_size, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - #'motor_pos': wh_pos(print_on_screen=0), - } - _md.update(md or {}) - try: - dimensions = [(zps.pi_r.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=chunk_size - ) - - @stage_decorator(list(dets) + motor) - @run_decorator(md=_md) - def inner_scan(): - yield from _set_andor_param( - exposure_time=exposure_time, period=period, chunk_size=chunk_size - ) - yield from _take_dark_image(dets, motor, num_dark=1, simu=simu) - - uid = yield from inner_scan() - yield from mv(Andor.cam.image_mode, 1) - print("dark finished") - txt = get_scan_parameter(print_flag=0) - insert_text(txt) - print(txt) - return uid - - -def user_bkg_only( - exposure_time=0.1, - chunk_size=20, - out_x=None, - out_y=2000, - out_z=None, - out_r=None, - note="", - simu=False, - relative_move_flag=1, - traditional_sequence_flag=1, - md=None, -): - """ - Move sample out of the way and take background (aka flat) images. - - Inputs: - ------- - exposure_time: float, in unit of sec - - chunk_size: int, default setting is 20 - number of images taken for each trigger of Andor camera - - out_x: float, default is 0 - relative movement of sample in "x" direction using zps.sx to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_y: float, default is 0 - relative movement of sample in "y" direction using zps.sy to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_z: float, default is 0 - relative movement of sample in "z" direction using zps.sz to move out sample (in unit of um) - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - out_r: float, default is 0 - relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample - NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - - note: string - adding note to the scan - - simu: Bool, default is False - True: will simulate closing/open shutter without really closing/opening - False: will really close/open shutter - - """ - global ZONE_PLATE - period = exposure_time # default to exposure time for backgrounds - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if out_x else motor_x_ini - motor_y_out = motor_y_ini + out_y if out_y else motor_y_ini - motor_z_out = motor_z_ini + out_z if out_z else motor_z_ini - motor_r_out = motor_r_ini + out_r if out_r else motor_r_ini - else: - motor_x_out = out_x if out_x else motor_x_ini - motor_y_out = out_y if out_y else motor_y_ini - motor_z_out = out_z if out_z else motor_z_ini - motor_r_out = out_r if out_r else motor_r_ini - - motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - - dets = [Andor, ic3] - cur_rot_ang = zps.pi_r.position - - _md = { - "detectors": ["Andor"], - "motors": [mot.name for mot in motor], - "XEng": XEng.position, - "ion_chamber": ic3.name, - "plan_args": { - "exposure_time": exposure_time, - "chunk_size": chunk_size, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "out_r": out_r, - "relative_move_flag": relative_move_flag, - "traditional_sequence_flag": traditional_sequence_flag, - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - }, - "plan_name": "user_bkg_only", - "chunk_size": chunk_size, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - #'motor_pos': wh_pos(print_on_screen=0), - } - _md.update(md or {}) - try: - dimensions = [(zps.pi_r.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - # yield from _set_andor_param(exposure_time=exposure_time, period=period, chunk_size=chunk_size) - - @stage_decorator(list(dets) + motor) - @bpp.monitor_during_decorator([zps.pi_r]) - @run_decorator(md=_md) - def fly_inner_scan(): - yield from _open_shutter(simu=simu) - # bkg images - print("\nTaking background images...") - yield from _set_rotation_speed(rs=30) - yield from _take_bkg_image( - motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - dets, - motor, - num_bkg=1, - simu=False, - traditional_sequence_flag=traditional_sequence_flag, - ) - yield from _move_sample_in( - motor_x_ini, - motor_y_ini, - motor_z_ini, - motor_r_ini, - trans_first_flag=traditional_sequence_flag, - ) - - uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) - print("bkg finished") - txt = get_scan_parameter(print_flag=0) - insert_text(txt) - print(txt) - return uid - - -def user_multiple_fly_scans( - xyz_list, - bkg_every_x_scans=10, - exposure_time=0.1, - angle=70, - period=0.15, - chunk_size=20, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - rs=1, - note="", - simu=False, - relative_move_flag=0, - traditional_sequence_flag=1, - md=None, -): - # first take dark field - dark_scan_id = yield from user_dark_only(exposure_time, chunk_size, note, simu, md) - # open shutter for rest of data taking - yield from _open_shutter(simu=simu) - print("\nshutter opened") - - bkg_index = 0 - bkg_scan_id = None - for i, pos in enumerate(xyz_list): - x, y, z = pos - if i == 0 or bkg_index + bkg_every_x_scans <= i: - # take background - bkg_scan_id = yield from user_bkg_only( - exposure_time, - chunk_size, - out_x, - out_y, - out_z, - out_r, - note, - simu, - relative_move_flag, - traditional_sequence_flag, - md, - ) - bkg_index = i - # mv x, y, z, r position - yield from mv(zps.sx, x, zps.sy, y, zps.sz, z, zps.pi_r, angle) - # take tomo - angle *= -1 # rocker scan, switch angle back and forth - while True: - try: - scan_id = yield from user_fly_only( - exposure_time, - angle, - period, - chunk_size, - rs, - note, - simu, - dark_scan_id, - bkg_scan_id, - md, - ) - break - except Exception as e: - print(e) - print("Finished scans %s - %s" % (dark_scan_id, scan_id)) - - -def user_mosaic_gen(x_start, x_stop, x_step, y_start, y_stop, y_step, z_pos): - xyz_list = [] - for y in range(y_start, y_stop + y_step, y_step): - for x in range(x_start, x_stop + x_step, x_step): - xyz_list.append((x, y, z_pos)) - return xyz_list - - -def user_hex_mosaic_xyz( - x_start, x_stop, x_step, x_offset, y_start, y_stop, y_step, z_pos -): - xyz_list = [] - # apply the x_offse every other row - apply_offset = False - for y in range(y_start, y_stop + y_step, y_step): - if apply_offset: - offset = x_offset - else: - offset = 0 - apply_offset = not apply_offset - for x in range(x_start, x_stop + x_step, x_step): - xyz_list.append((x + offset, y, z_pos)) - return xyz_list - - -def v4_z_offset(xyz_list): - # offset is only dependent on y - new_xyz_list = [] - for x, y, z in xyz_list: - z = 50 + (56 - 50) * (-3873 - y) / (-1000) - new_xyz_list.append((x, y, z)) - return new_xyz_list - - -def point_inside_polygon(x, y, poly): - n = len(poly) - inside = False - - p1x, p1y = poly[0] - for i in range(n + 1): - p2x, p2y = poly[i % n] - if y > min(p1y, p2y): - if y <= max(p1y, p2y): - if x <= max(p1x, p2x): - if p1y != p2y: - xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x - if p1x == p2x or x <= xinters: - inside = not inside - p1x, p1y = p2x, p2y - - return inside - - -def trim_points_to_polygon(xyz_list, poly): - xyz_list_out = [] - for x, y, z in xyz_list: - if point_inside_polygon(x, y, poly): - xyz_list_out.append((x, y, z)) - return xyz_list_out - - -def ming_scan(): - x_list = [-892, -899, -865, -843, -782] - y_list = [384, 437, 431, 427, 807] - z_list = [-718, -726, -719, -715, -700] - r_list = [0, 0, 0, 0, 0] - out_x = -443 - out_y = 425 - out_z = -727 - out_r = 1 - # yield from multipos_2D_xanes_scan2(Ni_eng_list_short, x_list, y_list, z_list, r_list, out_x, out_y, out_z, out_r, repeat_num=1, exposure_time=0.04, sleep_time=1, chunk_size=5, simu=False, relative_move_flag=0, note='P95_NMC_Ag_700_0.2C_20cy')) - - for i in range(4, 6): - txt = f"start 3D xanes at pos {i}" - insert_text(txt) - print(txt) - yield from mv( - zps.sx, x_list[i], zps.sy, y_list[i], zps.sz, z_list[i], zps.pi_r, -80 - ) - yield from xanes_3D( - eng_list=Ni_eng_list_in_situ, - exposure_time=0.03, - relative_rot_angle=160, - period=0.03, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - rs=3, - simu=False, - relative_move_flag=0, - traditional_sequence_flag=1, - note=f"p95_700_Ag_0.2C_20cy_pos_{i+1}", - ) - - -def ming_scan2(): - yield from move_zp_ccd(6.5) - yield from bps.sleep(5) - yield from mv(zps.sx, -782, zps.sy, 807, zps.sz, -700, zps.pi_r, -80) - yield from xanes_3D( - eng_list=Mn_eng_list_in_situ, - exposure_time=0.025, - relative_rot_angle=160, - period=0.025, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - rs=6, - simu=False, - relative_move_flag=0, - traditional_sequence_flag=1, - note=f"p95_700_Ag_0.2C_20cy_pos_5_Mn", - ) - print("start Co xanes") - yield from move_zp_ccd(7.8) - yield from mv(zps.sx, -782, zps.sy, 807, zps.sz, -700, zps.pi_r, -80) - yield from bps.sleep(5) - yield from xanes_3D( - eng_list=Co_eng_list_in_situ, - exposure_time=0.02, - relative_rot_angle=160, - period=0.025, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - rs=6, - simu=False, - relative_move_flag=0, - traditional_sequence_flag=1, - note=f"p95_700_Ag_0.2C_20cy_pos_5_Co", - ) - print("start Mn xanes") - yield from move_zp_ccd(8.2) - yield from mv(zps.sx, -782, zps.sy, 807, zps.sz, -700, zps.pi_r, -80) - yield from bps.sleep(5) - yield from xanes_3D( - eng_list=Ni_eng_list_in_situ, - exposure_time=0.015, - relative_rot_angle=160, - period=0.025, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - rs=6, - simu=False, - relative_move_flag=0, - traditional_sequence_flag=1, - note=f"p95_700_Ag_0.2C_20cy_pos_5_Ni", - ) - - -def ming_scan3(): - - x_3D = [395, 513] - y_3D = [1067, 756] - z_3D = [-496, -508] - r_3D = [-80, -80] - - yield from move_zp_ccd(8.2) - yield from mv(zps.sx, x_3D[0], zps.sy, y_3D[0], zps.sz, z_3D[0], zps.pi_r, -80) - yield from xanes_3D( - eng_list=Ni_eng_list_in_situ, - exposure_time=0.02, - relative_rot_angle=160, - period=0.02, - out_x=2000, - out_y=None, - out_z=None, - out_r=1, - rs=6, - simu=False, - relative_move_flag=0, - traditional_sequence_flag=1, - note=f"p95_600_Ag_pristine_pos1", - ) - - yield from mv(zps.sx, x_3D[1], zps.sy, y_3D[1], zps.sz, z_3D[1], zps.pi_r, -80) - yield from xanes_3D( - eng_list=Ni_eng_list_in_situ, - exposure_time=0.02, - relative_rot_angle=160, - period=0.02, - out_x=2000, - out_y=None, - out_z=None, - out_r=1, - rs=6, - simu=False, - relative_move_flag=0, - traditional_sequence_flag=1, - note=f"p95_600_Ag_pristine_pos2", - ) - - -def qingchao_scan( - eng_list, - x_list1, - y_list1, - z_list1, - r_list1, - x_list2, - y_list2, - z_list2, - r_list2, - sleep_time=0, - num=1, -): - for i in range(num): - print(f"repeat # {i}") - for j in range(5): - yield from mv(filter3, 1, filter4, 1) - yield from multipos_2D_xanes_scan2( - eng_list, - x_list=x_list1, - y_list=y_list1, - z_list=z_list1, - r_list=r_list1, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - repeat_num=1, - exposure_time=0.1, - sleep_time=sleep_time, - chunk_size=5, - relative_move_flag=True, - note="622_filter3+4", - ) - for j in range(5): - yield from mv(filter3, 0, filter4, 1) - yield from multipos_2D_xanes_scan2( - eng_list, - x_list=x_list2, - y_list=y_list2, - z_list=z_list2, - r_list=r_list2, - out_x=out_x, - out_y=out_y, - out_z=out_z, - out_r=out_r, - repeat_num=1, - exposure_time=0.1, - sleep_time=sleep_time, - chunk_size=5, - relative_move_flag=True, - note="622_filter4", - ) - print(f"sleep for {sleep_time} sec ...") - yield from bps.sleep(sleep_time) - - -def ming(): - for i in range(2): - yield from multipos_2D_xanes_scan2( - Ni_list_2D, - x_list, - y_list, - z_list, - r_list, - out_x=None, - out_y=None, - out_z=950, - out_r=-90, - exposure_time=0.1, - repeat_num=3, - sleep_time=600, - relative_move_flag=0, - chunk_size=5, - simu=False, - note="N83_insitu_pristine_filter_2+3+4", - ) - yield from movpos(2, x_list, y_list, z_list, r_list) - yield from mv(zps.pi_r, -70) - yield from xanes_3D( - Ni_list_3D, - exposure_time=0.1, - relative_rot_angle=140, - period=0.1, - out_x=None, - out_y=None, - out_z=2500, - out_r=-20, - rs=3, - simu=False, - relative_move_flag=1, - note="N83_pos2", - ) - yield from mv(zps.pi_r, 0) - - yield from multipos_2D_xanes_scan2( - Ni_list_2D, - x_list, - y_list, - z_list, - r_list, - out_x=None, - out_y=None, - out_z=950, - out_r=-90, - exposure_time=0.1, - repeat_num=3, - sleep_time=600, - relative_move_flag=0, - chunk_size=5, - simu=False, - note="N83_insitu_pristine_filter_2+3+4", - ) - yield from movpos(4, x_list, y_list, z_list, r_list) - yield from mv(zps.pi_r, -70) - yield from xanes_3D( - Ni_list_3D, - exposure_time=0.1, - relative_rot_angle=145, - period=0.1, - out_x=None, - out_y=None, - out_z=2500, - out_r=-20, - rs=3, - simu=False, - relative_move_flag=1, - note="N83_pos4", - ) - yield from mv(zps.pi_r, 0) - - insert_text("take xanes of full_eng_list") - for i in range(1): - yield from multipos_2D_xanes_scan2( - Ni_eng_list_63pnt, - x_list, - y_list, - z_list, - r_list, - out_x=None, - out_y=None, - out_z=950, - out_r=-90, - exposure_time=0.1, - repeat_num=3, - sleep_time=600, - relative_move_flag=0, - chunk_size=5, - simu=False, - note="N83_insitu_pristine_filter_2+3+4", - ) - - for j in range(4): - insert_text(f"taking 3D xanes at pos{j}\n") - yield from movpos(j, x_list, y_list, z_list, r_list) - yield from mv(zps.pi_r, -70) - yield from xanes_3D( - Ni_list_3D, - exposure_time=0.1, - relative_rot_angle=140, - period=0.1, - out_x=None, - out_y=None, - out_z=2500, - out_r=-20, - rs=3, - simu=False, - relative_move_flag=1, - note=f"N83_pos{j}", - ) - yield from mv(zps.pi_r, 0) - - for i in range(4): - yield from multipos_2D_xanes_scan2( - Ni_list_2D, - x_list, - y_list, - z_list, - r_list, - out_x=None, - out_y=None, - out_z=950, - out_r=-90, - exposure_time=0.1, - repeat_num=3, - sleep_time=600, - relative_move_flag=0, - chunk_size=5, - simu=False, - note="N83_insitu_pristine_filter_2+3+4", - ) - yield from movpos(2, x_list, y_list, z_list, r_list) - yield from mv(zps.pi_r, -70) - yield from xanes_3D( - Ni_list_3D, - exposure_time=0.1, - relative_rot_angle=140, - period=0.1, - out_x=None, - out_y=None, - out_z=2500, - out_r=-20, - rs=3, - simu=False, - relative_move_flag=1, - note="N83_pos2", - ) - yield from mv(zps.pi_r, 0) - - yield from multipos_2D_xanes_scan2( - Ni_list_2D, - x_list, - y_list, - z_list, - r_list, - out_x=None, - out_y=None, - out_z=950, - out_r=-90, - exposure_time=0.1, - repeat_num=3, - sleep_time=600, - relative_move_flag=0, - chunk_size=5, - simu=False, - note="N83_insitu_pristine_filter_2+3+4", - ) - yield from movpos(4, x_list, y_list, z_list, r_list) - yield from mv(zps.pi_r, -70) - yield from xanes_3D( - Ni_list_3D, - exposure_time=0.1, - relative_rot_angle=145, - period=0.1, - out_x=None, - out_y=None, - out_z=2500, - out_r=-20, - rs=3, - simu=False, - relative_move_flag=1, - note="N83_pos4", - ) - yield from mv(zps.pi_r, 0) - - -def scan_change_expo_time( - x_range, - y_range, - t1, - t2, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - img_sizeX=2560, - img_sizeY=2160, - pxl=20, - relative_move_flag=1, - note="", - simu=False, - sleep_time=0, - md=None, -): - """ - take image - """ - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - dets = [Andor, ic3] - - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if out_x else motor_x_ini - motor_y_out = motor_y_ini + out_y if out_y else motor_y_ini - motor_z_out = motor_z_ini + out_z if out_z else motor_z_ini - motor_r_out = motor_r_ini + out_r if out_r else motor_r_ini - else: - motor_x_out = out_x if out_x else motor_x_ini - motor_y_out = out_y if out_y else motor_y_ini - motor_z_out = out_z if out_z else motor_z_ini - motor_r_out = out_r if out_r else motor_r_ini - motor_eng = XEng - motor = [motor_eng, zps.sx, zps.sy, zps.sz, zps.pi_r] - - _md = { - "detectors": [det.name for det in dets], - "x_ray_energy": XEng.position, - "plan_args": { - "x_range": x_range, - "y_range": y_range, - "t1": t1, - "t2": t2, - "out_x": out_x, - "out_y": out_y, - "out_z": out_z, - "out_r": out_r, - "img_sizeX": img_sizeX, - "img_sizeY": img_sizeY, - "pxl": pxl, - "relative_move_flag": relative_move_flag, - "note": note if note else "None", - "sleep_time": sleep_time, - }, - "plan_name": "scan_change_expo_time", - "hints": {}, - "operator": "FXI", - "zone_plate": ZONE_PLATE, - } - _md.update(md or {}) - try: - dimensions = [(motor.hints["fields"], "primary")] - except (AttributeError, KeyError): - pass - else: - _md["hints"].setdefault("dimensions", dimensions) - - @stage_decorator(list(dets) + motor) - @run_decorator(md=_md) - def inner(): - # take dark image - print(f"take 5 dark image with exposure = {t1}") - yield from _set_andor_param(exposure_time=t1, period=t1, chunk_size=1) - yield from _take_dark_image(dets, motor, num_dark=5, simu=simu) - print(f"take 5 dark image with exposure = {t2}") - yield from _set_andor_param(exposure_time=t2, period=t2, chunk_size=1) - yield from _take_dark_image(dets, motor, num_dark=5, simu=simu) - - print("open shutter ...") - yield from _open_shutter(simu) - for ii in np.arange(x_range[0], x_range[1] + 1): - for jj in np.arange(y_range[0], y_range[1] + 1): - yield from mv(zps.sx, motor_x_ini + ii * img_sizeX * pxl * 1.0 / 1000) - yield from mv(zps.sy, motor_y_ini + jj * img_sizeY * pxl * 1.0 / 1000) - yield from bps.sleep(0.1) - print(f"set exposure time = {t1}") - yield from _set_andor_param(exposure_time=t1, period=t1, chunk_size=1) - yield from bps.sleep(sleep_time) - yield from _take_image(dets, motor, 1) - print(f"set exposure time = {t2}") - yield from _set_andor_param(exposure_time=t2, period=t2, chunk_size=1) - yield from bps.sleep(sleep_time) - yield from _take_image(dets, motor, 1) - print(f"take bkg image with exposure time = {t1}") - yield from _set_andor_param(exposure_time=t1, period=t1, chunk_size=1) - yield from bps.sleep(sleep_time) - yield from _take_bkg_image( - motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - dets, - motor, - num_bkg=5, - simu=simu, - ) - print(f"take bkg image with exposure time = {t2}") - yield from _set_andor_param(exposure_time=t2, period=t2, chunk_size=1) - yield from bps.sleep(sleep_time) - yield from _take_bkg_image( - motor_x_out, - motor_y_out, - motor_z_out, - motor_r_out, - dets, - motor, - num_bkg=5, - simu=simu, - ) - - yield from _move_sample_in( - motor_x_ini, - motor_y_ini, - motor_z_ini, - motor_r_ini, - repeat=1, - trans_first_flag=0, - ) - - print("closing shutter") - yield from _close_shutter(simu) - - yield from inner() - txt = get_scan_parameter() - insert_text(txt) - print(txt) diff --git a/startup/10-area-detector.py b/startup/10-area-detector.py index 7349337..9d549f7 100644 --- a/startup/10-area-detector.py +++ b/startup/10-area-detector.py @@ -20,7 +20,11 @@ from ophyd.areadetector.cam import AreaDetectorCam from ophyd.areadetector.detectors import DetectorBase from nslsii.ad33 import StatsPluginV33, CamV33Mixin -#from nslsii.ad33 import SingleTriggerV33 + + +from nslsii.ad33 import SingleTriggerV33 + + from ophyd.areadetector.trigger_mixins import TriggerBase, ADTriggerStatus from ophyd.device import Staged from ophyd.status import SubscriptionStatus @@ -44,7 +48,7 @@ def describe(self): ) return res - +''' class SingleTriggerV33(TriggerBase): _status_type = ADTriggerStatus @@ -68,7 +72,7 @@ def acquire_complete(*args, old_value, value, **kwargs): self.dispatch(self._image_name, ttime.time()) return status - +''' class AndorCam(CamV33Mixin, AreaDetectorCam): def __init__(self, *args, **kwargs): AreaDetectorCam.__init__(self, *args, **kwargs) @@ -219,7 +223,7 @@ def resume(self): self.hdf5._generate_resource(res_kwargs) return super().resume() - @timing + #@timing def stage(self): import itertools if self.cam.detector_state.get() != 0: @@ -237,7 +241,7 @@ def stage(self): else: raise - @timing + #@timing def unstage(self, *args, **kwargs): import itertools #self._acquisition_signal.put(0, wait=True) @@ -275,9 +279,12 @@ class Manta(SingleTrigger, AreaDetector): HDF5PluginWithFileStore, suffix="HDF1:", # write_path_template="/nsls2/data/fxi-new/legacy/Andor/%Y/%m/%d/", - write_path_template="/nsls2/data/fxi-new/legacy/Andor//%Y/%m/%d/", - # write_path_template = '/dev/shm/', - root="/nsls2/data/fxi-new/legacy/Andor/", + write_path_template="/nsls2/data/fxi-new/legacy/Oryx/%Y/%m/%d/", + # write_path_template='/tmp/test_2022/%Y/%m/%d/' , + # write_path_template="/nsls2/data/fxi-new/assets/default/%Y/%m/%d/", + # write_path_template="/nsls2/data/fxi-new/legacy/Andor//%Y/%m/%d/", + # root="/nsls2/data/fxi-new/assets/default", + root="/nsls2/data/fxi-new/legacy/Oryx", # write_path_template='/tmp/', # root='/', ) @@ -362,6 +369,9 @@ def resume(self): getattr(Andor, k).ensure_nonblocking() Andor.hdf5.time_stamp.name = "Andor_timestamps" +######################################### +''' comment away this section when Marana is disconnected + # added by XH Marana = AndorKlass("XF:18IDB-ES{Det:Marana1}", name="Andor") Marana.cam.ensure_nonblocking() @@ -378,6 +388,8 @@ def resume(self): getattr(Marana, k).ensure_nonblocking() Marana.hdf5.time_stamp.name = "Andor_timestamps" +''' +############################################# # vlm = Manta("XF:18IDB-BI{VLM:1}", name="vlm") # detA1.read_attrs = ['hdf5', 'stats1', 'stats5'] # detA1.read_attrs = ['hdf5'] @@ -387,6 +399,16 @@ def resume(self): # vlm.hdf5.read_attrs = [] +Oryx = Manta("XF:18IDB-ES{Det:Oryx1}", name="Oryx") +#Oryx.cam.ensure_nonblocking() +Oryx.read_attrs = ["hdf5"] +#Oryx.stats1.read_attrs = ["total"] +Oryx.hdf5.read_attrs = ["time_stamp"] +Oryx.stage_sigs["cam.image_mode"] = 1 +for k in ("image", ): + getattr(Oryx, k).ensure_nonblocking() +Oryx.hdf5.time_stamp.name = "Oryx_timestamps" + #for det in [detA1, Andor]: for det in [detA1]: det.stats1.total.kind = "hinted" diff --git a/startup/11-txm_motor.py b/startup/11-txm_motor.py index 50b46eb..1a688e1 100644 --- a/startup/11-txm_motor.py +++ b/startup/11-txm_motor.py @@ -140,15 +140,16 @@ class BetrandLens(Device): x = Cpt(MyEpicsMotor, "{BLens:1-Ax:X}Mtr") #y = Cpt(MyEpicsMotor, "{BLens:1-Ax:Y}Mtr") y = Cpt(MyEpicsMotor, "{ZP:1-Ax:Y}Mtr") - z = Cpt(MyBaseMotor, "{BLens:1-Ax:Z}Mtr") + #z = Cpt(MyBaseMotor, "{BLens:1-Ax:Z}Mtr") class TXMSampleStage(Device): sx = Cpt(MyEpicsMotor, "{Env:1-Ax:Xl}Mtr") sy = Cpt(MyEpicsMotor, "{Env:1-Ax:Yl}Mtr") sz = Cpt(MyEpicsMotor, "{Env:1-Ax:Zl}Mtr") - pi_x = Cpt(MyBaseMotor, "{TXM:1-Ax:X}Mtr") + # pi_x = Cpt(MyBaseMotor, "{TXM:1-Ax:X}Mtr") pi_r = Cpt(MyEpicsMotor, "{TXM:2-Ax:R}Mtr") + #pi_r = Cpt(MyEpicsMotor, "{TXM:1-Ax:R}Mtr") class DetSupport(Device): @@ -243,11 +244,11 @@ class Scint(Device): #phase_ring.z, betr.x, betr.y, - betr.z, + #betr.z, zps.sx, zps.sy, zps.sz, - zps.pi_x, + #zps.pi_x, zps.pi_r, DetU.x, DetU.y, diff --git a/startup/18-zebra.py b/startup/18-zebra.py index a4a6c2d..bc5be14 100644 --- a/startup/18-zebra.py +++ b/startup/18-zebra.py @@ -665,9 +665,6 @@ def complete(self): whether that is true, so it will obligingly stop immediately. It is up to the caller to ensure that the motion is actually complete. """ - - print("\nin complet: complete starts") - # Our acquisition complete PV is: XF:05IDD-ES:1{Dev:Zebra1}:ARRAY_ACQ t0 = ttime.monotonic() while self._encoder.pc.data_in_progress.get() == 1: @@ -744,10 +741,10 @@ def get_zebra_data(): self._last_bulk["timestamps"].update( {k: v["timestamp"] for k, v in reading.items()} ) - print( - f"\nin complete: {type(self._last_bulk)=}\n{type(self._document_cache)=}\n" - ) - print(f"\nin complete: {(self._last_bulk)=}\n{(self._document_cache)=}\n") + # print( + # f"\nin complete: {type(self._last_bulk)=}\n{type(self._document_cache)=}\n" + # ) + # print(f"\nin complete: {(self._last_bulk)=}\n{(self._document_cache)=}\n") return NullStatus() @@ -829,7 +826,7 @@ def resume(self): def preset_flyer(self, scn_cfg): yield from FXITomoFlyer.bin_det(self.detectors[0], scn_cfg["bin_fac"]) yield from FXITomoFlyer.init_mot_r(scn_cfg) - yield from FXITomoFlyer.set_cam_mode(self.detectors[0], stage="pre-scan") + # yield from FXITomoFlyer.set_cam_mode(self.detectors[0], stage="pre-scan") scn_cfg = FXITomoFlyer.cal_cam_rot_params(self.detectors[0], scn_cfg) pc_cfg = FXITomoFlyer.cal_zebra_pc_params(scn_cfg) yield from self.preset_zebra(pc_cfg) @@ -1075,15 +1072,22 @@ def prime_det(det): yield from abs_set(det.cam.num_images, 5, wait=True) yield from abs_set(det.cam.acquire, 1, wait=False) + @staticmethod + def stop_det(det): + for _ in range(5): + yield from abs_set(det.cam.acquire, 0, wait=True) + yield from abs_set(det.hdf5.capture, 0, wait=True) + @staticmethod def bin_det(det, bin_fac): - yield from abs_set(det.cam.acquire, 0, wait=False) - if bin_fac is None: - bin_fac = 0 - if int(bin_fac) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from abs_set(det.binning, bin_fac, wait=True) - FXITomoFlyer.prime_det(det) + if det.binning.value != bin_fac: + yield from abs_set(det.cam.acquire, 0, wait=False) + if bin_fac is None: + bin_fac = 0 + if int(bin_fac) not in [0, 1, 2, 3, 4]: + raise ValueError("binnng must be in [0, 1, 2, 3, 4]") + yield from abs_set(det.binning, bin_fac, wait=True) + FXITomoFlyer.prime_det(det) @staticmethod def def_abs_out_pos( diff --git a/startup/20-global_param.py b/startup/20-global_param.py index ce2034a..bfa297f 100644 --- a/startup/20-global_param.py +++ b/startup/20-global_param.py @@ -24,3 +24,8 @@ CALIBER_FLAG = 1 CALIBER = {} + +ZP_8keV = {"D": 244, + "dr": 30} +ZP_9keV = {"D": 550, + "dr": 50} diff --git a/startup/40-scan_pre_define.py b/startup/40-scan_pre_define.py index 4b8ff5b..7398393 100644 --- a/startup/40-scan_pre_define.py +++ b/startup/40-scan_pre_define.py @@ -210,3 +210,9 @@ def _take_ref_image( yield from _set_Andor_chunk_size(cams, chunk_size) yield from _take_image(cams, [], num, stream_name=stream_name) + +def _prime_cam(cam=Andor): + yield from abs_set(cam.cam.image_mode, 0, wait=True) + yield from abs_set(cam.cam.num_images, 5, wait=True) + yield from abs_set(cam.cam.acquire, 1, wait=True) + diff --git a/startup/41-scans.py b/startup/41-scans.py index 946986c..b49f991 100644 --- a/startup/41-scans.py +++ b/startup/41-scans.py @@ -317,12 +317,12 @@ def fly_scan( @bpp.monitor_during_decorator([zps.pi_r]) @run_decorator(md=_md) def fly_inner_scan(): - ''' + for flt in filters: yield from mv(flt, 1) yield from mv(flt, 1) yield from bps.sleep(1) - ''' + # close shutter, dark images: numer=chunk_size (e.g.20) print("\nshutter closed, taking dark images...") yield from _take_dark_image( @@ -622,9 +622,11 @@ def xanes_scan2( filters=[], rot_first_flag=1, simu=False, + return_ini=True, md=None, ): """ + changed something Different from xanes_scan: In xanes_scan2, it moves out sample and take background image at each energy Scan the energy and take 2D image @@ -736,10 +738,12 @@ def xanes_scan2( @stage_decorator(list(detectors) + motor) @run_decorator(md=_md) def xanes_inner_scan(): + ''' if len(filters): for filt in filters: yield from mv(filt, 1) yield from bps.sleep(0.5) + ''' yield from _set_rotation_speed(rs=30) # take dark image print(f"\ntake {chunk_size} dark images...") @@ -753,6 +757,10 @@ def xanes_inner_scan(): yield from _open_shutter(simu) for eng in eng_list: + if len(filters): + for filt in filters: + yield from mv(filt, 0) + yield from bps.sleep(0.5) yield from _xanes_per_step( eng, detectors, @@ -762,7 +770,10 @@ def xanes_inner_scan(): info_flag=0, stream_name="primary", ) - + if len(filters): + for filt in filters: + yield from mv(filt, 1) + yield from bps.sleep(0.5) yield from _take_bkg_image( motor_x_out, motor_y_out, @@ -784,13 +795,20 @@ def xanes_inner_scan(): repeat=2, trans_first_flag=rot_first_flag, ) + ''' if len(filters): for filt in filters: yield from mv(filt, 0) yield from bps.sleep(0.5) - yield from move_zp_ccd(eng_ini, move_flag=1, info_flag=0) + ''' + if return_ini: + yield from move_zp_ccd(eng_ini, move_flag=1, info_flag=0) print("closing shutter") yield from _close_shutter(simu=simu) + if len(filters): + for filt in filters: + yield from mv(filt, 0) + yield from bps.sleep(0.5) yield from xanes_inner_scan() yield from mv(Andor.cam.image_mode, 1) @@ -963,6 +981,8 @@ def xanes_inner_scan(): repeat=2, rot_first_flag=rot_first_flag, ) + yield from move_zp_ccd(eng_list[0]) + yield from bps.sleep(5) print(f"\ntake bkg image after xanes scan, {chunk_size} per each energy...") for eng in eng_list: yield from _xanes_per_step( @@ -1836,11 +1856,7 @@ def raster_2D_scan( @stage_decorator(list(detectors) + motor) @run_decorator(md=_md) def raster_2D_inner(): - - if len(filters): - for filt in filters: - yield from mv(filt, 1) - yield from bps.sleep(0.5) + select_filters(filters) # take dark image print("take 5 dark image") @@ -1890,12 +1906,9 @@ def raster_2D_inner(): repeat=1, trans_first_flag=1 - rot_first_flag, ) - if len(filters): - for filt in filters: - yield from mv(filt, 0) - yield from bps.sleep(0.5) + select_filters([]) print("closing shutter") - yield from _close_shutter(simu) + yield from _close_shutter(simu=simu) yield from raster_2D_inner() txt = get_scan_parameter() @@ -2391,7 +2404,6 @@ def multipos_2D_xanes_scan2( out_y=None, out_z=None, out_r=None, - repeat_num=1, exposure_time=0.2, sleep_time=1, chunk_size=5, @@ -2459,6 +2471,7 @@ def multipos_2D_xanes_scan2( note: string """ + repeat_num = 1 print(eng_list) print(x_list) print(y_list) @@ -3106,6 +3119,7 @@ def xanes_3D( simu=False, relative_move_flag=1, rot_first_flag=1, + filters=[], note="", binning=[2, 2], ): @@ -3129,6 +3143,7 @@ def xanes_3D( out_r=out_r, rs=rs, relative_move_flag=relative_move_flag, + filters=filters, note=my_note, simu=simu, rot_first_flag=rot_first_flag, @@ -3363,5 +3378,3 @@ def tomo_mosaic_scan( txt = txt1 + txt4 + txt3 insert_text(txt) - -# diff --git a/startup/44-scans_other.py b/startup/44-scans_other.py index 6613938..a1efbf4 100644 --- a/startup/44-scans_other.py +++ b/startup/44-scans_other.py @@ -16,10 +16,15 @@ def test_scan( out_y=-100, out_z=0, out_r=0, + period=0.1, num_img=10, - num_bkg=10, + #num_bkg=10, + relative_move_flag=False, + sleep_time=0, + rot_first_flag=1, + close_shutter_in_scan=False, note="", - period=0.1, + simu=False, md=None, ): @@ -46,6 +51,24 @@ def test_scan( yield from _set_andor_param(exposure_time, period, 1) detectors = [Andor] + motors = [zps.sx, zps.sy, zps.sz, zps.pi_r] + + motor_x_ini = zps.sx.position + motor_y_ini = zps.sy.position + motor_z_ini = zps.sz.position + motor_r_ini = zps.pi_r.position + if relative_move_flag: + motor_x_out = motor_x_ini + out_x if not (out_x is None) else motor_x_ini + motor_y_out = motor_y_ini + out_y if not (out_y is None) else motor_y_ini + motor_z_out = motor_z_ini + out_z if not (out_z is None) else motor_z_ini + motor_r_out = motor_r_ini + out_r if not (out_r is None) else motor_r_ini + else: + motor_x_out = out_x if not (out_x is None) else motor_x_ini + motor_y_out = out_y if not (out_y is None) else motor_y_ini + motor_z_out = out_z if not (out_z is None) else motor_z_ini + motor_r_out = out_r if not (out_r is None) else motor_r_ini + + ''' y_ini = zps.sy.position y_out = y_ini + out_y if not (out_y is None) else y_ini x_ini = zps.sx.position @@ -54,6 +77,8 @@ def test_scan( z_out = z_ini + out_z if not (out_z is None) else z_ini r_ini = zps.pi_r.position r_out = r_ini + out_r if not (out_r is None) else r_ini + ''' + _md = { "detectors": ["Andor"], "XEng": XEng.position, @@ -64,7 +89,10 @@ def test_scan( "out_z": out_z, "out_r": out_r, "num_img": num_img, - "num_bkg": num_bkg, + #"num_bkg": num_bkg, + "relative_move_flag": relative_move_flag, + "close_shutter_in_scan":close_shutter_in_scan, + "sleep_time":sleep_time, "note": note if note else "None", }, "plan_name": "test_scan", @@ -82,34 +110,63 @@ def test_scan( @run_decorator(md=_md) def inner_scan(): yield from _open_shutter(simu=simu) - """ - yield from abs_set(shutter_open, 1, wait=True) - yield from bps.sleep(2) - yield from abs_set(shutter_open, 1, wait=True) - yield from bps.sleep(2) - """ + for i in range(num_img): yield from trigger_and_read(list(detectors)) + if close_shutter_in_scan: + yield from _close_shutter(simu=simu) + print(f'sleep for {sleep_time} sec...') + yield from bps.sleep(sleep_time) + if close_shutter_in_scan: + yield from _open_shutter(simu=simu) + # taking out sample and take background image + print(f'\nmove sample out and take 20 backgound image') + yield from _take_bkg_image( + motor_x_out, + motor_y_out, + motor_z_out, + motor_r_out, + detectors, + [], + num=1, + chunk_size=20, + rot_first_flag=rot_first_flag, + stream_name="flat", + simu=simu, + ) + ''' yield from mv(zps.pi_r, r_out) yield from mv(zps.sz, z_out) - yield from mv(zps.sx, x_out, zps.sy, y_out) + yield from mv(zps.sx, x_out, zps.sy, y_out) for i in range(num_bkg): yield from trigger_and_read(list(detectors)) - # close shutter, taking dark image - yield from _close_shutter(simu=simu) - """ - yield from abs_set(shutter_close, 1, wait=True) - yield from bps.sleep(1) - yield from abs_set(shutter_close, 1, wait=True) - yield from bps.sleep(1) - """ + ''' + + print(f'\nclose shutter and take 20 dark image') + yield from _take_dark_image(detectors, motors, num=1, chunk_size=20, stream_name="dark", simu=simu) + ''' for i in range(num_bkg): yield from trigger_and_read(list(detectors)) + # close shutter, taking dark image + yield from _close_shutter(simu=simu) + ''' + + print('move sample back to initial position') + yield from _move_sample_in( + motor_x_ini, + motor_y_ini, + motor_z_ini, + motor_r_ini, + trans_first_flag=rot_first_flag, + repeat=3, + ) + ''' yield from mv(zps.sz, z_ini) yield from mv(zps.pi_r, r_ini) yield from mv(zps.sx, x_ini, zps.sy, y_ini) + ''' # yield from abs_set(shutter_open, 1, wait=True) uid = yield from inner_scan() @@ -252,19 +309,22 @@ def inner_scan(): def z_scan( + scan_motor='zp_z', start=-0.03, stop=0.03, steps=5, out_x=-100, out_y=-100, + out_z=0, chunk_size=10, exposure_time=0.1, + relative_move_flag=1, note="", md=None, - simu=False, - cam=Andor ): """ + + scan the zone-plate to find best focus use as: z_scan(start=-0.03, stop=0.03, steps=5, out_x=-100, out_y=-100, chunk_size=10, exposure_time=0.1, fn='/home/xf18id/Documents/tmp/z_scan.h5', note='', md=None) @@ -289,28 +349,60 @@ def z_scan( """ - detectors = [cam] - motor = zp.z - z_ini = motor.position # zp.z intial position - z_start = z_ini + start - z_stop = z_ini + stop - # detectors = [cam] + detectors = [Andor] + motor = [zps.sx, zps.sy, zps.sz, zps.sz, zp.z] + + x_ini = zps.sx.position + y_ini = zps.sy.position + z_ini = zps.sz.position + r_ini = zps.pi_r.position + + if relative_move_flag: + x_out = x_ini + out_x if not (out_x is None) else x_ini + y_out = y_ini + out_y if not (out_y is None) else y_ini + z_out = z_ini + out_z if not (out_z is None) else z_ini + + else: + x_out = out_x if not (out_x is None) else x_ini + y_out = out_y if not (out_y is None) else y_ini + z_out = out_z if not (out_z is None) else z_ini + + + if scan_motor == 'zp_x': + zp_ini = zp.x.position # zp.x intial position + real_motor = zp.x + if scan_motor == 'zp_y': + real_motor = zp.y + zp_ini = zp.y.position # zp.y intial position + else: + zp_ini = zp.z.position # zp.z intial position + real_motor = zp.z + + + + zp_start = zp_ini + start + zp_stop = zp_ini + stop + ''' + # detectors = [Andor] y_ini = zps.sy.position # sample y position (initial) y_out = ( y_ini + out_y if not (out_y is None) else y_ini ) # sample y position (out-position) x_ini = zps.sx.position x_out = x_ini + out_x if not (out_x is None) else x_ini - yield from mv(cam.cam.acquire, 0) - yield from mv(cam.cam.image_mode, 0) - yield from mv(cam.cam.num_images, chunk_size) - yield from mv(cam.cam.acquire_time, exposure_time) - period_cor = max(exposure_time + 0.01, 0.05) - yield from mv(cam.cam.acquire_period, period_cor) + z_ini = zps.sz.position + z_out = z_ini if not (out_z is None) else z_ini + ''' + + period = max(exposure_time + 0.01, 0.05) + + yield from _set_andor_param( + exposure_time=exposure_time, period=period, chunk_size=chunk_size + ) _md = { "detectors": [det.name for det in detectors], - "motors": [motor.name], + "motors": [mot.name for mot in motor], "XEng": XEng.position, "plan_args": { "start": start, @@ -318,6 +410,7 @@ def z_scan( "steps": steps, "out_x": out_x, "out_y": out_y, + "out_z": out_z, "chunk_size": chunk_size, "exposure_time": exposure_time, "note": note if note else "None", @@ -327,10 +420,10 @@ def z_scan( "plan_pattern_module": "numpy", "hints": {}, "operator": "FXI", - "motor_pos": wh_pos(print_on_screen=0), + #'motor_pos': wh_pos(print_on_screen=0), } _md.update(md or {}) - my_var = np.linspace(z_start, z_stop, steps) + my_var = np.linspace(zp_start, zp_stop, steps) try: dimensions = [(motor.hints["fields"], "primary")] except (AttributeError, KeyError): @@ -338,47 +431,42 @@ def z_scan( else: _md["hints"].setdefault("dimensions", dimensions) - @stage_decorator(list(detectors) + [motor]) + @stage_decorator(list(detectors) + motor) @run_decorator(md=_md) - def inner_scan(): - # yield from abs_set(shutter_open, 1, wait=True) - # yield from bps.sleep(1) - # yield from abs_set(shutter_open, 1) - # yield from bps.sleep(1) - yield from _open_shutter(simu=simu) - for x in my_var: - yield from mv(motor, x) - yield from trigger_and_read(list(detectors) + [motor]) - # backgroud images - yield from mv(zps.sx, x_out) - yield from mv(zps.sy, y_out) - yield from mv(zps.sx, x_out, zps.sy, y_out) - yield from bps.sleep(0.5) - yield from trigger_and_read(list(detectors) + [motor]) - yield from _close_shutter(simu=simu) - yield from bps.sleep(0.5) - yield from trigger_and_read(list(detectors) + [motor]) - # dark images - # yield from abs_set(shutter_close, 1, wait=True) - # yield from bps.sleep(1) - # yield from abs_set(shutter_close, 1) - # yield from bps.sleep(1) + def z_inner_scan(): + + # take dark image + yield from _take_dark_image(detectors, motor) + yield from _open_shutter() + for pos in my_var: + yield from mv(zps.sx, x_ini, zps.sy, y_ini) + yield from mv(real_motor, pos) + yield from bps.sleep(0.1) + yield from mv(zps.sx, x_ini, zps.sy, y_ini) + yield from mv(real_motor, pos) + yield from bps.sleep(0.1) + yield from _take_image(detectors, motor=motor, num=1) + yield from _take_bkg_image( + out_x=x_out, + out_y=y_out, + out_z=z_out, + out_r=0, + detectors=detectors, + motor=motor, + chunk_size=chunk_size, + ) + # move back zone_plate and sample y - yield from mv(zps.sx, x_ini) - yield from mv(zps.sy, y_ini) - yield from mv(zp.z, z_ini) + yield from mv(zps.sx, x_ini, zps.sy, y_ini, zps.sz, z_ini, zp.z, zp_ini) # yield from abs_set(shutter_open, 1, wait=True) - yield from mv(cam.cam.image_mode, 1) - uid = yield from inner_scan() - yield from mv(cam.cam.image_mode, 1) - yield from _close_shutter(simu=simu) + yield from z_inner_scan() + yield from mv(Andor.cam.image_mode, 1) + yield from _close_shutter(simu=False) txt = get_scan_parameter() insert_text(txt) print(txt) - return uid - def z_scan2( start=-0.03, diff --git a/startup/45-baseline.py b/startup/45-baseline.py index f60a2c7..3269130 100644 --- a/startup/45-baseline.py +++ b/startup/45-baseline.py @@ -4,7 +4,7 @@ aper, clens, #phase_ring, - betr, + #betr, zps, DetU, XEng, diff --git a/startup/46-zebra_flyer.py b/startup/46-zebra_flyer.py index 4a0e5bb..e3f4422 100644 --- a/startup/46-zebra_flyer.py +++ b/startup/46-zebra_flyer.py @@ -29,7 +29,11 @@ def tomo_zfly( """_summary_ Args: - scn_mode (int, optional): _description_. Defaults to 0. + scn_mode (int, optional): + 0: "standard", # a single scan in a given angle range + 1: "snaked: multiple files", # back-forth rocking scan with each swing being saved into a file + 2: "snaked: single file", # back-forth rocking scan being saved into a single file + Defaults to 0. exp_t (float, optional): _description_. Defaults to 0.05. acq_p (float, optional): _description_. Defaults to 0.05. ang_s (float or None, optional): _description_. Defaults to None. @@ -59,6 +63,8 @@ def tomo_zfly( _type_: _description_ """ global ZONE_PLATE + FXITomoFlyer.stop_det(cam) + yield from bps.sleep(2) sleep_plan = _schedule_sleep(sleep, num_swing) if not sleep_plan: print(f"A wrong sleep pattern {sleep=} and {num_swing=} breaks the scan. Quit") @@ -93,6 +99,7 @@ def tomo_zfly( "XEng": XEng.position, "storage_ring_current (mA)": round(sr_current.get(), 1), "plan_args": { + "scan_mode": scn_cfg["scn_mode"], "exposure_time": scn_cfg["exp_t"], "start_angle": scn_cfg["ang_s"], "end_angle": scn_cfg["ang_e"], @@ -109,7 +116,6 @@ def tomo_zfly( "binning": 0 if scn_cfg["bin_fac"] is None else scn_cfg["bin_fac"], "note": note if note else "None", "sleep": sleep, - "zone_plate": ZONE_PLATE, }, "plan_name": "tomo_zfly", "num_bkg_images": 10, @@ -124,12 +130,16 @@ def tomo_zfly( _md.update(md or {}) print("preset done") - @stage_decorator(list(mots)) + #@stage_decorator(list(mots)) @run_decorator(md=_md) def inner_fly_plan(): yield from select_filters(flts) + if flyer.scn_mode == "snaked: single file": + yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") + yield from bps.sleep(1) yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + yield from bps.sleep(1) yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) yield from _open_shutter_xhx(simu) for d in flyer.detectors: @@ -138,6 +148,9 @@ def inner_fly_plan(): except: d.unstage() d.stage() + for mot in mots: + mot.stage() + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) st.wait(timeout=10) @@ -147,6 +160,7 @@ def inner_fly_plan(): wait(det_stream) yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + t0 = ttime.monotonic() for ii in range(scn_cfg["num_swing"]): yield from abs_set( @@ -168,7 +182,7 @@ def inner_fly_plan(): print("Scan finished abnormally. Quit!") return yield from bps.sleep(flyer._staging_delay) - print(ttime.time()) + # print(ttime.time()) print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") st = yield from complete(flyer, wait=True) st.wait(timeout=10) @@ -179,9 +193,15 @@ def inner_fly_plan(): except: print(f"Cannot unstage detector {d.name}") return - else: + for mot in mots: + mot.unstage() + elif flyer.scn_mode == "standard": + print(sleep_plan) for ii in range(scn_cfg["num_swing"]): + yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") + yield from bps.sleep(1) yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + yield from bps.sleep(1) yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) yield from _open_shutter_xhx(simu) for d in flyer.detectors: @@ -190,18 +210,128 @@ def inner_fly_plan(): except: d.unstage() d.stage() + for mot in mots: + mot.stage() + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) st.wait(timeout=10) + + det_stream = short_uid("dets") + for d in flyer.detectors: + yield from bps.trigger(d, group=det_stream) + wait(det_stream) + + set_and_wait( + flyer.encoder.pc.gate_start, scn_cfg["ang_s"], rtol=0.1 + ) + yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + + t0 = ttime.monotonic() yield from abs_set( - flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True + zps.pi_r, + scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + wait=True, + ) + + t1 = ttime.monotonic() + while int(flyer.encoder.pc.gated.get()): + if ttime.monotonic() - t1 > 60: + print("Scan finished abnormally. Quit!") + return + yield from bps.sleep(flyer._staging_delay) + # print(ttime.time()) + print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") + st = yield from complete(flyer, wait=True) + st.wait(timeout=10) + yield from collect(flyer) + + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + for mot in mots: + mot.unstage() + + scn_cfg["ang_s"] = r_ini + yield from FXITomoFlyer.init_mot_r(scn_cfg) + + yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") + yield from bps.sleep(1) + yield from _take_ref_image( + [cam], + mots_pos={ + "x": mot_x_out, + "y": mot_y_out, + "z": mot_z_out, + "r": mot_r_out, + }, + num=1, + chunk_size=10, + stream_name="flat", + simu=simu, + ) + yield from _take_ref_image( + [cam], + mots_pos={}, + num=1, + chunk_size=10, + stream_name="dark", + simu=simu, + ) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + yield from _move_sample( + x_ini, + y_ini, + z_ini, + r_ini, + repeat=2, ) + yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") + + if ii < (scn_cfg["num_swing"] - 1): + print(f" Sleeping {sleep_plan[ii]} seconds before {ii+1}th scan ... ".center(100, "#")) + print("\n") + yield from bps.sleep(sleep_plan[ii]) + yield from select_filters([]) + elif flyer.scn_mode == "snaked: multiple files": + yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") + yield from bps.sleep(1) + for mot in mots: + mot.stage() + + for ii in range(scn_cfg["num_swing"]): + yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + yield from _open_shutter_xhx(simu) + for d in flyer.detectors: + try: + d.stage() + except: + d.unstage() + d.stage() + # for mot in mots: + # mot.stage() + + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) + st.wait(timeout=10) det_stream = short_uid("dets") for d in flyer.detectors: yield from bps.trigger(d, group=det_stream) wait(det_stream) + set_and_wait( + flyer.encoder.pc.gate_start, scn_cfg["ang_s"], rtol=0.1 + ) yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + t0 = ttime.monotonic() yield from abs_set( zps.pi_r, @@ -215,7 +345,7 @@ def inner_fly_plan(): print("Scan finished abnormally. Quit!") return yield from bps.sleep(flyer._staging_delay) - print(ttime.time()) + # print(ttime.time()) print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") st = yield from complete(flyer, wait=True) st.wait(timeout=10) @@ -226,6 +356,8 @@ def inner_fly_plan(): except: print(f"Cannot unstage detector {d.name}") return None + # for mot in mots: + # mot.unstage() if scn_cfg["num_swing"] > 1: (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( @@ -238,55 +370,54 @@ def inner_fly_plan(): int(scn_cfg["rot_dir"]) ] yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) - - if ii < scn_cfg["num_swing"] - 1: - print(f"Sleeping {sleep_plan[ii]} seconds before {ii}th scan ...") - bps.sleep(sleep_plan[ii]) - - scn_cfg["ang_s"] = r_ini - yield from FXITomoFlyer.init_mot_r(scn_cfg) - - yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") - yield from _take_ref_image( - [cam], - mots_pos={ - "x": mot_x_out, - "y": mot_y_out, - "z": mot_z_out, - "r": mot_r_out, - }, - num=1, - chunk_size=10, - stream_name="flat", - simu=simu, - ) - yield from _take_ref_image( - [cam], - mots_pos={}, - num=1, - chunk_size=10, - stream_name="dark", - simu=simu, - ) - for d in flyer.detectors: - try: - d.unstage() - except: - print(f"Cannot unstage detector {d.name}") - return None - yield from _move_sample( - x_ini, - y_ini, - z_ini, - r_ini, - repeat=2, - ) - yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") + + scn_cfg["ang_s"] = r_ini + yield from FXITomoFlyer.init_mot_r(scn_cfg) + for mot in mots: + mot.unstage() + + yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") + yield from bps.sleep(1) + yield from _take_ref_image( + [cam], + mots_pos={ + "x": mot_x_out, + "y": mot_y_out, + "z": mot_z_out, + "r": mot_r_out, + }, + num=1, + chunk_size=10, + stream_name="flat", + simu=simu, + ) + yield from _take_ref_image( + [cam], + mots_pos={}, + num=1, + chunk_size=10, + stream_name="dark", + simu=simu, + ) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + yield from _move_sample( + x_ini, + y_ini, + z_ini, + r_ini, + repeat=2, + ) + yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") yield from select_filters([]) - uid = yield from inner_fly_plan() + yield from inner_fly_plan() print("scan finished") - return uid + # return uid def tomo_grid_zfly( @@ -317,7 +448,11 @@ def tomo_grid_zfly( """_summary_ Args: - scn_mode (int, optional): _description_. Defaults to 0. + scn_mode (int, optional): + 0: "standard", # a single scan in a given angle range + 1: "snaked: multiple files", # back-forth rocking scan with each swing being saved into a file + 2: "snaked: single file", # back-forth rocking scan being saved into a single file + Defaults to 0. exp_t (float, optional): _description_. Defaults to 0.05. acq_p (float, optional): _description_. Defaults to 0.05. ang_s (float or None, optional): _description_. Defaults to None. @@ -354,7 +489,8 @@ def tomo_grid_zfly( if not sleep_plan: print(f"A wrong sleep pattern {sleep=} and {num_swing=} breaks the scan. Quit") return - mots = [zps.sx, zps.sy, zps.sz] + #mots = [zps.sx, zps.sy, zps.sz] + mots = [zps.sx, zps.sz] flyer.detectors = [ cam, ] @@ -383,6 +519,7 @@ def tomo_grid_zfly( "XEng": XEng.position, "storage_ring_current (mA)": round(sr_current.get(), 1), "plan_args": { + "scan_mode": scn_cfg["scn_mode"], "exposure_time": scn_cfg["exp_t"], "start_angle": scn_cfg["ang_s"], "end_angle": scn_cfg["ang_e"], @@ -403,7 +540,7 @@ def tomo_grid_zfly( "filters": ["filter{}".format(t) for t in flts] if flts else "None", "binning": 0 if scn_cfg["bin_fac"] is None else scn_cfg["bin_fac"], "note": note if note else "None", - "zone_plate": ZONE_PLATE, + "sleep": sleep, }, "plan_name": "tomo_grid_zfly", "num_bkg_images": 10, @@ -426,12 +563,7 @@ def tomo_grid_zfly( @run_decorator(md=_md) def inner_fly_plan(): yield from select_filters(flts) - for jj in grid_nodes["pos"] if grid_nodes else range(1): - if grid_nodes: - yield from mv(*zip(grid_nodes["mots"], jj)) - for mot in all_mots: - mot.stage() - + for ii in range(scn_cfg["num_swing"]): if flyer.scn_mode == "snaked: single file": print( "Scan mode 'snaked: single file' is not currently supported. Quit!" @@ -445,9 +577,15 @@ def inner_fly_plan(): repeat=2, ) return - else: - for ii in range(scn_cfg["num_swing"]): + elif flyer.scn_mode == "standard": + for jj in grid_nodes["pos"] if grid_nodes else range(1): + if grid_nodes: + for kk in range(len(grid_nodes["mots"])): + yield from mv(grid_nodes["mots"][kk], jj[kk]) + yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") + yield from bps.sleep(1) yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + yield from bps.sleep(1) yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) yield from _open_shutter_xhx(simu) for d in flyer.detectors: @@ -456,18 +594,130 @@ def inner_fly_plan(): except: d.unstage() d.stage() + for mot in all_mots: + mot.stage() + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) st.wait(timeout=10) + + det_stream = short_uid("dets") + for d in flyer.detectors: + yield from bps.trigger(d, group=det_stream) + wait(det_stream) + + set_and_wait( + flyer.encoder.pc.gate_start, scn_cfg["ang_s"], rtol=0.1 + ) + yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + + t0 = ttime.monotonic() yield from abs_set( - flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True + zps.pi_r, + scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + wait=True, + ) + + t1 = ttime.monotonic() + while int(flyer.encoder.pc.gated.get()): + if ttime.monotonic() - t1 > 60: + print("Scan finished abnormally. Quit!") + return + yield from bps.sleep(flyer._staging_delay) + # print(ttime.time()) + print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") + st = yield from complete(flyer, wait=True) + st.wait(timeout=10) + yield from collect(flyer) + + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + for mot in all_mots: + mot.unstage() + + yield from FXITomoFlyer.init_mot_r(scn_cfg) + + yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") + yield from bps.sleep(1) + yield from _take_ref_image( + [cam], + mots_pos={ + "x": mot_x_out, + "y": mot_y_out, + "z": mot_z_out, + "r": mot_r_out, + }, + num=1, + chunk_size=10, + stream_name="flat", + simu=simu, + ) + yield from _take_ref_image( + [cam], + mots_pos={}, + num=1, + chunk_size=10, + stream_name="dark", + simu=simu, + ) + + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + + yield from _move_sample( + x_ini, + y_ini, + z_ini, + r_ini, + repeat=2, ) + yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") + + if ii < scn_cfg["num_swing"] - 1: + print(f" Sleeping {sleep_plan[ii]} seconds before {ii+1}th scan ... ".center(100, "#")) + print("\n") + yield from bps.sleep(sleep_plan[ii]) + elif flyer.scn_mode == "snaked: multiple files": + for mot in all_mots: + mot.stage() + + yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") + yield from bps.sleep(1) + for jj in grid_nodes["pos"] if grid_nodes else range(1): + if grid_nodes: + for kk in range(len(grid_nodes["mots"])): + yield from mv(grid_nodes["mots"][kk], jj[kk]) + yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + yield from bps.sleep(1) + yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + yield from _open_shutter_xhx(simu) + for d in flyer.detectors: + try: + d.stage() + except: + d.unstage() + d.stage() + + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) + st.wait(timeout=10) det_stream = short_uid("dets") for d in flyer.detectors: yield from bps.trigger(d, group=det_stream) wait(det_stream) + set_and_wait( + flyer.encoder.pc.gate_start, scn_cfg["ang_s"], rtol=0.1 + ) yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + t0 = ttime.monotonic() yield from abs_set( zps.pi_r, @@ -481,7 +731,7 @@ def inner_fly_plan(): print("Scan finished abnormally. Quit!") return yield from bps.sleep(flyer._staging_delay) - print(ttime.time()) + # print(ttime.time()) print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") st = yield from complete(flyer, wait=True) st.wait(timeout=10) @@ -492,6 +742,8 @@ def inner_fly_plan(): except: print(f"Cannot unstage detector {d.name}") return None + # for mot in all_mots: + # mot.unstage() if scn_cfg["num_swing"] > 1: (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( @@ -505,74 +757,277 @@ def inner_fly_plan(): ] yield from flyer.set_pc_step_for_scan(flyer.scn_mode, pc_cfg) - if ii < scn_cfg["num_swing"] - 1: - print(f"Sleeping {sleep_plan[ii]} seconds before {ii}th scan ...") - bps.sleep(sleep_plan[ii]) + for mot in all_mots: + mot.unstage() - for mot in all_mots: - mot.unstage() - - yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") - yield from _take_ref_image( - [cam], - mots_pos={ - "x": mot_x_out, - "y": mot_y_out, - "z": mot_z_out, - "r": mot_r_out, - }, - num=1, - chunk_size=10, - stream_name="flat", - simu=simu, - ) - yield from _take_ref_image( - [cam], - mots_pos={}, - num=1, - chunk_size=10, - stream_name="dark", - simu=simu, - ) - for d in flyer.detectors: - try: - d.unstage() - except: - print(f"Cannot unstage detector {d.name}") - return None - yield from _move_sample( - x_ini, - y_ini, - z_ini, - r_ini, - repeat=2, - ) - yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") - yield from select_filters([]) + yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") + yield from bps.sleep(1) + yield from _take_ref_image( + [cam], + mots_pos={ + "x": mot_x_out, + "y": mot_y_out, + "z": mot_z_out, + "r": mot_r_out, + }, + num=1, + chunk_size=10, + stream_name="flat", + simu=simu, + ) + yield from _take_ref_image( + [cam], + mots_pos={}, + num=1, + chunk_size=10, + stream_name="dark", + simu=simu, + ) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return None + yield from _move_sample( + x_ini, + y_ini, + z_ini, + r_ini, + repeat=2, + ) + yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") + yield from select_filters([]) uid = yield from inner_fly_plan() print("scan finished") return uid + +def tomo_grid_zfly2( + scn_mode=0, + exp_t=0.05, + acq_p=0.05, + ang_s=0, + ang_e=180, + vel=3, + acc_t=1, + grid_nodes={}, + num_swing=1, + out_x=None, + out_y=None, + out_z=None, + out_r=None, + rel_out_flag=True, + flts=[], + rot_back_velo=30, + bin_fac=None, + note="", + md=None, + sleep=0, + simu=False, + cam=Andor, + flyer=tomo_flyer, +): + """_summary_ + + Args: + scn_mode (int, optional): + 0: "standard", # a single scan in a given angle range + 1: "snaked: multiple files", # back-forth rocking scan with each swing being saved into a file + 2: "snaked: single file", # back-forth rocking scan being saved into a single file + Defaults to 0. + exp_t (float, optional): _description_. Defaults to 0.05. + acq_p (float, optional): _description_. Defaults to 0.05. + ang_s (float or None, optional): _description_. Defaults to None. + ang_e (float, optional): _description_. Defaults to 180. + vel (int, optional): _description_. Defaults to 3. + acc_t (float, optional): _description_. Defaults to 1. + grid_nodes (dic, optional): a dictionary with two items, one is + a list of motors that loop with rotary stage; another is a table + that lists all the grid nodes. + num_swing (int, optional): _description_. Defaults to 1. + out_x (float, optional): _description_. Defaults to None. + out_y (float, optional): _description_. Defaults to None. + out_z (float, optional): _description_. Defaults to None. + out_r (float, optional): _description_. Defaults to None. + flts (list, optional): _description_. Defaults to []. + rot_back_velo (int, optional): _description_. Defaults to 30. + binning (int, optional): _description_. Defaults to None. + note (str, optional): _description_. Defaults to "". + md (dict, optional): _description_. Defaults to None. + simu (bool, optional): _description_. Defaults to False. + cam (ophyd.Device, optional): detector; choose between Andor, Marana, and Oryx. + + Raises: + ValueError: _description_ + + Returns: + _type_: _description_ + + Yields: + _type_: _description_ + """ + global ZONE_PLATE + sleep_plan = _schedule_sleep(sleep, num_swing) + if not sleep_plan: + print(f"A wrong sleep pattern {sleep=} and {num_swing=} breaks the scan. Quit") + return + #mots = [zps.sx, zps.sy, zps.sz] + mots = [zps.sx, zps.sz] + flyer.detectors = [ + cam, + ] + flyer.scn_mode = flyer.scn_modes[scn_mode] + scn_cfg = FXITomoFlyer.compose_scn_cfg( + scn_mode, + exp_t, + acq_p, + bin_fac, + ang_s, + ang_e, + vel, + acc_t, + rot_back_velo, + num_swing, + ) + scn_cfg, pc_cfg = yield from flyer.preset_flyer(scn_cfg) + (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() + (mot_x_out, mot_y_out, mot_z_out, mot_r_out) = FXITomoFlyer.def_abs_out_pos( + out_x, out_y, out_z, out_r, rel_out_flag + ) + + _md = { + "detectors": [flyer.detectors[0].name], + "motors": [mot.name for mot in mots], + "XEng": XEng.position, + "storage_ring_current (mA)": round(sr_current.get(), 1), + "plan_args": { + "scan_mode": scn_cfg["scn_mode"], + "exposure_time": scn_cfg["exp_t"], + "start_angle": scn_cfg["ang_s"], + "end_angle": scn_cfg["ang_e"], + "acquisition_period": scn_cfg["acq_p"], + "slew_speed": scn_cfg["vel"], + "mv_back_vel": scn_cfg["mb_vel"], + "acceleration": scn_cfg["tacc"], + "number_of_swings": scn_cfg["num_swing"], + "grid_mots": "none" + if not grid_nodes + else [mot.name for mot in grid_nodes["mots"]], + "grid_nodes": "none" if not grid_nodes else grid_nodes["pos"], + "out_x": mot_x_out, + "out_y": mot_y_out, + "out_z": mot_z_out, + "out_r": mot_r_out, + "rel_out_flag": rel_out_flag, + "filters": ["filter{}".format(t) for t in flts] if flts else "None", + "binning": 0 if scn_cfg["bin_fac"] is None else scn_cfg["bin_fac"], + "note": note if note else "None", + "sleep": sleep, + }, + "plan_name": "tomo_grid_zfly", + "num_bkg_images": 10, + "num_dark_images": 10, + "plan_pattern": "linspace", + "plan_pattern_module": "numpy", + "hints": {}, + "operator": "FXI", + "note": note if note else "None", + "zone_plate": ZONE_PLATE, + } + _md.update(md or {}) + + if grid_nodes: + all_mots = list(set(list(mots) + list(grid_nodes["mots"]))) + else: + all_mots = list(list(mots)) + print("preset done") + + # @run_decorator(md=_md) + # def inner_fly_plan(): + for jj in grid_nodes["pos"]: + for kk in range(len(grid_nodes["mots"])): + yield from mv(grid_nodes["mots"][kk], jj[kk]) + yield from tomo_zfly(scn_mode=scn_mode, + exp_t=exp_t, + acq_p=acq_p, + ang_s=ang_s, + ang_e=ang_e, + vel=vel, + acc_t=acc_t, + num_swing=1, + out_x=out_x, + out_y=out_y, + out_z=out_z, + out_r=out_r, + rel_out_flag=rel_out_flag, + flts=flts, + rot_back_velo=rot_back_velo, + bin_fac=bin_fac, + note=note, + md=None, + sleep=sleep, + simu=simu, + cam=cam, + flyer=flyer) + + # yield from inner_fly_plan() + print("scan finished") + # return uid + + def _schedule_sleep(sleep, num_scan): sleep_plan = {} - for ii in range(1, num_scan - 1): + if num_scan == 1: + sleep_plan[0] = 0 + return sleep_plan + elif num_scan > 1: if isinstance(sleep, list): - if len(sleep) != num_scan - 2: + if len(sleep) != num_scan - 1: print( f"The list of sleep time has length {len(sleep)} that is inconsistent \ with the number of scans {num_scan}. \ - The length of sleep time should be {num_scan - 2}" + The length of sleep time should be {num_scan - 1}" ) return False else: - sleep_plan[ii] = sleep[ii] + for ii in range(0, num_scan - 1): + sleep_plan[ii] = sleep[ii] return sleep_plan elif isinstance(sleep, int) or isinstance(sleep, float): - sleep_plan[ii] = sleep + for ii in range(0, num_scan - 1): + sleep_plan[ii] = sleep return sleep_plan else: print(f"Unrecognized sleep pattern {sleep}. Quit.") return False + else: + print(f"Invalid num_scan {num_scan}. Quit!") + return False + +def prep_grid_dic(pos, mots=[zps.sx, zps.sy, zps.sz]): + """ + mots: TXM sample stage motors zps.sx, zps.sy, zps.sz + pos: dictionary with keys "x", "y", and "z". The value + under each key is a list that defines the start, end + and step of grid + """ + grid_nodes = {} + grid_nodes["mots"] = mots + pos = [] + tem = [] + for ii in range(len(mots)): + num = int(round((pos[ii][1] - pos[ii][0]) / pos[ii][2])) + tem[ii] = np.linspace(pos[ii][0], pos[ii][1], num, endpoint=True) + m = np.meshgrid(*tem) + pos = list(zip(*(ii.flat for ii in m))) + """ y = np.linspace(pos["y"][0], pos["y"][1], pos["y"][2]) + for y in range(pos["y"][0], pos["y"][1], pos["y"][2]): + for x in range(pos["x"][0], pos["x"][1], pos["x"][2]): + for z in range(pos["x"][0], pos["z"][1], pos["z"][2]): + pos.append([x, y, z]) """ + grid_nodes["pos"] = pos + return grid_nodes \ No newline at end of file diff --git a/startup/80-load_scan.py b/startup/80-load_scan.py index 1892b61..a08f91b 100644 --- a/startup/80-load_scan.py +++ b/startup/80-load_scan.py @@ -8,6 +8,8 @@ from datetime import datetime +import gc +from skimage.transform import rescale, resize def timestamp_to_float(t): @@ -72,16 +74,19 @@ def export_scan(scan_id, scan_id_end=None, binning=4, date_end_by=None, fpath=No if isinstance(scan_id, int): scan_id = [scan_id] for item in scan_id: - - # export_single_scan(int(item), binning) - custom_export(int(item), binning, date_end_by=date_end_by, fpath=fpath) - db.reg.clear_process_cache() + try: + custom_export(int(item), binning, date_end_by=date_end_by, fpath=fpath) + db.reg.clear_process_cache() + except: + print(f'fail to export {item}') else: for i in range(scan_id, scan_id_end + 1): - # export_single_scan(int(i), binning) - custom_export(int(i), binning, date_end_by=date_end_by, fpath=fpath) - db.reg.clear_process_cache() - + try: + # export_single_scan(int(i), binning) + custom_export(int(i), binning, date_end_by=date_end_by, fpath=fpath) + db.reg.clear_process_cache() + except: + print(f'fail to export {i}') def custom_export(scan_id, binning=4, date_end_by=None, fpath=None): """ @@ -96,7 +101,7 @@ def custom_export(scan_id, binning=4, date_end_by=None, fpath=None): uid = sid.start["uid"] timestamp = sid.start["time"] ts = pd.to_datetime(timestamp, unit="s").tz_localize("US/Eastern") - date_end = covert_date_to_datetime(date_end_by) + date_end = pd.Timestamp(date_end_by,).tz_localize('US/Eastern') if ts < date_end: export_single_scan(uid, binning) break @@ -528,6 +533,80 @@ def export_xanes_scan(h, fpath=None): ) +def export_xanes_scan_with_binning(h, fpath=None, binning=1): + if fpath is None: + fpath = "./" + else: + if not fpath[-1] == "/": + fpath += "/" + zp_z_pos = h.table("baseline")["zp_z"][1] + DetU_z_pos = h.table("baseline")["DetU_z"][1] + M = (DetU_z_pos / zp_z_pos - 1) * 10.0 + pxl_sz = 6500.0 / M + scan_type = h.start["plan_name"] + # scan_type = 'xanes_scan' + uid = h.start["uid"] + note = h.start["note"] + scan_id = h.start["scan_id"] + scan_time = h.start["time"] + try: + x_eng = h.start["XEng"] + except: + x_eng = h.start["x_ray_energy"] + chunk_size = h.start["chunk_size"] + num_eng = h.start["num_eng"] + + img_xanes_avg = [] + img_bkg_avg = [] + img_list = list(h.data("Andor_image", stream_name="primary")) + bkg_list = list(h.data("Andor_image", stream_name="flat")) + for i in trange(num_eng): + img_xanes_sub = np.array(img_list[i]) + img_xanes_sub_avg = np.median(img_xanes_sub, axis=0) + img_bin1 = rescale(img_xanes_sub_avg, 1/binning) + img_xanes_avg.append(img_bin1) + + img_bkg_sub = np.array(bkg_list[i]) + img_bkg_sub_avg = np.median(img_bkg_sub, axis=0) + img_bin2 = rescale(img_bkg_sub_avg, 1/binning) + img_bkg_avg.append(img_bin2) + + + + img_dark = np.array(list(h.data("Andor_image", stream_name="dark"))) + img_dark_avg = np.mean(img_dark, axis=1)[0] + img_bin = rescale(img_dark_avg, 1/binning) + img_dark_avg = np.expand_dims(img_bin, axis=0) + eng_list = list(h.start["eng_list"]) + + img_xanes_norm = (img_xanes_avg - img_dark_avg) * 1.0 / (img_bkg_avg - img_dark_avg) + img_xanes_norm[np.isnan(img_xanes_norm)] = 0 + img_xanes_norm[np.isinf(img_xanes_norm)] = 0 + fname = fpath + scan_type + "_id_" + str(scan_id) + ".h5" + with h5py.File(fname, "w") as hf: + hf.create_dataset("uid", data=uid) + hf.create_dataset("scan_id", data=scan_id) + hf.create_dataset("note", data=str(note)) + hf.create_dataset("scan_time", data=scan_time) + hf.create_dataset("X_eng", data=eng_list) + hf.create_dataset("img_bkg", data=np.array(img_bkg_avg, dtype=np.float32)) + hf.create_dataset("img_dark", data=np.array(img_dark_avg, dtype=np.float32)) + hf.create_dataset("img_xanes", data=np.array(img_xanes_norm, dtype=np.float32)) + hf.create_dataset("Magnification", data=M) + hf.create_dataset("Pixel Size", data=str(pxl_sz) + "nm") + + try: + write_lakeshore_to_file(h, fname) + except: + print("fails to write lakeshore info into {fname}") + + del ( + img_dark_avg, + img_bkg_avg, + img_xanes_avg, + img_xanes_norm, + ) + def export_xanes_scan_img_only(h, fpath=None): if fpath is None: fpath = "./" @@ -612,10 +691,14 @@ def export_z_scan(h, fpath=None): num = h.start["plan_args"]["steps"] chunk_size = h.start["plan_args"]["chunk_size"] note = h.start["plan_args"]["note"] if h.start["plan_args"]["note"] else "None" - img = np.array(list(h.data("Andor_image"))) - img_zscan = np.mean(img[:num], axis=1) - img_bkg = np.mean(img[num], axis=0, keepdims=True) - img_dark = np.mean(img[-1], axis=0, keepdims=True) + + img_zscan = np.mean(np.array(list(h.data("Andor_image", stream_name="primary"))), axis=1) + img_bkg = np.mean(np.array(list(h.data("Andor_image", stream_name="flat"))), axis=1).squeeze() + img_dark = np.mean(np.array(list(h.data("Andor_image", stream_name="dark"))), axis=1).squeeze() + # img = np.array(list(h.data("Andor_image", stream_name="primary"))) + # img_zscan = np.mean(img[:num], axis=1) + # img_bkg = np.mean(img[num], axis=0, keepdims=False) + # img_dark = np.mean(img[-1], axis=0, keepdims=False) img_norm = (img_zscan - img_dark) / (img_bkg - img_dark) img_norm[np.isnan(img_norm)] = 0 img_norm[np.isinf(img_norm)] = 0 @@ -638,7 +721,7 @@ def export_z_scan(h, fpath=None): except: print("fails to write lakeshore info into {fname}") - del img, img_zscan, img_bkg, img_dark, img_norm + del img_zscan, img_bkg, img_dark, img_norm def export_z_scan2(h, fpath=None): @@ -709,48 +792,59 @@ def export_test_scan(h, fpath=None): DetU_z_pos = h.table("baseline")["DetU_z"][1] M = (DetU_z_pos / zp_z_pos - 1) * 10.0 pxl_sz = 6500.0 / M - import tifffile - scan_type = h.start["plan_name"] - scan_id = h.start["scan_id"] uid = h.start["uid"] + note = h.start["note"] + scan_id = h.start["scan_id"] + scan_time = h.start["time"] try: x_eng = h.start["XEng"] except: x_eng = h.start["x_ray_energy"] num = h.start["plan_args"]["num_img"] - num_bkg = h.start["plan_args"]["num_bkg"] - note = h.start["plan_args"]["note"] if h.start["plan_args"]["note"] else "None" - img = np.squeeze(np.array(list(h.data("Andor_image")))) - assert len(img.shape) == 3, "load test_scan fails..." - img_test = img[:num] - img_bkg = np.mean(img[num : num + num_bkg], axis=0, keepdims=True) - img_dark = np.mean(img[-num_bkg:], axis=0, keepdims=True) - img_norm = (img_test - img_dark) / (img_bkg - img_dark) + + img = np.array(list(h.data("Andor_image", stream_name="primary")))[:,0] + try: + img_dark = np.array(list(h.data("Andor_image", stream_name="dark")))[0] + img_dark_avg = np.median(img_dark, axis=0, keepdims=True) + except: + img_dark = np.zeros((1, img.shape[1], img.shape[2])) + img_dark_avg = img_dark + img_bkg = np.array(list(h.data("Andor_image", stream_name="flat")))[0] + img_bkg_avg = np.median(img_bkg, axis=0, keepdims=True) + + img_norm = (img - img_dark_avg) * 1.0 / (img_bkg_avg - img_dark_avg) img_norm[np.isnan(img_norm)] = 0 img_norm[np.isinf(img_norm)] = 0 - # fn = h.start['plan_args']['fn'] fname = fpath + scan_type + "_id_" + str(scan_id) + ".h5" - fname_tif = fpath + scan_type + "_id_" + str(scan_id) + ".tif" with h5py.File(fname, "w") as hf: hf.create_dataset("uid", data=uid) hf.create_dataset("scan_id", data=scan_id) - hf.create_dataset("X_eng", data=x_eng) hf.create_dataset("note", data=str(note)) - hf.create_dataset("img_bkg", data=img_bkg) - hf.create_dataset("img_dark", data=img_dark) - hf.create_dataset("img", data=np.array(img_test, dtype=np.float32)) + hf.create_dataset("scan_time", data=scan_time) + hf.create_dataset("X_eng", data=x_eng) + hf.create_dataset("img_bkg", data=np.array(img_bkg_avg, dtype=np.float32)) + hf.create_dataset("img_dark", data=np.array(img_dark_avg, dtype=np.float32)) + hf.create_dataset("img", data=np.array(img, dtype=np.float32)) hf.create_dataset("img_norm", data=np.array(img_norm, dtype=np.float32)) hf.create_dataset("Magnification", data=M) hf.create_dataset("Pixel Size", data=str(pxl_sz) + "nm") - # tifffile.imsave(fname_tif, img_norm) try: write_lakeshore_to_file(h, fname) except: print("fails to write lakeshore info into {fname}") - del img, img_test, img_bkg, img_dark, img_norm + del ( + img_dark, + img_dark_avg, + img_bkg, + img_bkg_avg, + #img_xanes, + #img_xanes_avg, + img_norm, + img + ) def export_test_scan2(h, fpath=None): @@ -1219,21 +1313,54 @@ def export_multipos_2D_xanes_scan2(h, fpath=None): DetU_z_pos = h.table("baseline")["DetU_z"][1] M = (DetU_z_pos / zp_z_pos - 1) * 10.0 pxl_sz = 6500.0 / M - try: - repeat_num = h.start["plan_args"]["repeat_num"] - except: - repeat_num = 1 + repeat_num = 1 img_xanes = np.array(list(h.data("Andor_image", stream_name="primary"))) img_dark = np.array(list(h.data("Andor_image", stream_name="dark"))) img_bkg = np.array(list(h.data("Andor_image", stream_name="flat"))) - img_xanes = np.mean(img_xanes, axis=1) - img_dark = np.mean(img_dark, axis=1) - img_bkg = np.mean(img_bkg, axis=1) + img_xanes = np.median(img_xanes, axis=1) + img_dark = np.median(img_dark, axis=1) + img_bkg = np.median(img_bkg, axis=1) eng_list = list(h.start["eng_list"]) + len_img = len(img_xanes) + len_bkg = len(img_bkg) + + idx = int(len_img // num_pos) + + id_end = int(min(idx, len_bkg) * num_pos) + img_xanes = img_xanes[:id_end] + eng_list = eng_list[:id_end] + + + for j in range(num_pos): + img = img_xanes[j::num_pos] + img_n = (img - img_dark) / (img_bkg - img_dark) + fn = fpath + fname = (f"{fn}{scan_type}_id_{scan_id}_pos_{j:02d}.h5") + + try: + print(f"saving {fname}") + with h5py.File(fname, "w") as hf: + hf.create_dataset("uid", data=uid) + hf.create_dataset("scan_id", data=scan_id) + hf.create_dataset("note", data=str(note)) + hf.create_dataset("scan_time", data=scan_time) + hf.create_dataset("X_eng", data=eng_list) + hf.create_dataset("img_bkg", data=np.array(img_bkg, dtype=np.float32)) + hf.create_dataset("img_dark", data=np.array(img_dark, dtype=np.float32)) + hf.create_dataset("img_xanes", data=np.array(img_n, dtype=np.float32)) + hf.create_dataset("Magnification", data=M) + hf.create_dataset("Pixel Size", data=str(pxl_sz) + "nm") + except Exception as err: + print(err) + del img_xanes + del img_bkg + gc.collect() + ''' + for repeat in range(repeat_num): # revised here try: print(f"repeat: {repeat}") @@ -1280,7 +1407,8 @@ def export_multipos_2D_xanes_scan2(h, fpath=None): del img_xanes del img_bkg del img_dark - del img_p, img_p_n + #del img_p, img_p_n + ''' def export_multipos_2D_xanes_scan3(h, fpath=None): @@ -1470,6 +1598,29 @@ def export_user_fly_only(h, fpath=None): del imgs +def batch_export_flyscan(sid1, sid2): + n = sid2 - sid1 + k = 0 + while True: + sid_last = db[-2].start['scan_id'] + if sid_last == sid2 or k >= n: + break + else: + for sid in range(sid1, sid2+1): + if sid > sid_last: + break + else: + file_exist = 0 + fn_fly = np.sort(glob.glob('fly*.h5')) + for fn in fn_fly: + if str(sid) in fn: + file_exist = 1 + break + if file_exist == 0: + k = k + 1 + export_scan(sid) + + def export_scan_change_expo_time(h, fpath=None, save_range_x=[], save_range_y=[]): from skimage import io diff --git a/startup/90-image_util.py b/startup/90-image_util.py index 07aac01..b5dc276 100644 --- a/startup/90-image_util.py +++ b/startup/90-image_util.py @@ -7,7 +7,7 @@ import scipy.ndimage as sn from scipy.ndimage.filters import median_filter as medfilt from PIL import Image - +from matplotlib.widgets import Slider def pad(img, thick, direction): @@ -19,7 +19,28 @@ def pad(img, thick, direction): img: 2d or 3d array 2D or 3D images thick: int - padding thickness for all directions + padding thickness for all directionsdef plot3D(data,axis=0,index_init=None): + fig, ax = plt.subplots() + if index_init is None: + index_init = int(data.shape[axis]//2) + im = ax.imshow(data.take(index_init,axis=axis)) + fig.subplots_adjust(bottom=0.15) + axslide = fig.add_axes([0.1, 0.03, 0.8, 0.03]) + im_slider = Slider( + ax=axslide, + label='index', + valmin=0, + valmax=data.shape[axis] - 1, + valstep=1, + valinit=index_init, + ) + def update(val): + im.set_data(data.take(val,axis=axis)) + fig.canvas.draw_idle() + + im_slider.on_changed(update) + plt.show() + return im_slider if thick == odd, automatically increase it to thick+1 direction: int 0: padding in axes = 0 (2D or 3D image) @@ -812,5 +833,30 @@ def rm_phase_ramp_manual_2d(array, x_shift, y_shift): return tmp + +def plot3D(data,axis=0,index_init=None): + fig, ax = plt.subplots() + if index_init is None: + index_init = int(data.shape[axis]//2) + im = ax.imshow(data.take(index_init,axis=axis)) + fig.subplots_adjust(bottom=0.15) + axslide = fig.add_axes([0.1, 0.03, 0.8, 0.03]) + im_slider = Slider( + ax=axslide, + label='index', + valmin=0, + valmax=data.shape[axis] - 1, + valstep=1, + valinit=index_init, + ) + def update(val): + im.set_data(data.take(val,axis=axis)) + fig.canvas.draw_idle() + + im_slider.on_changed(update) + plt.show() + return im_slider + + if __name__ == "__main__": pass diff --git a/startup/91-functions.py b/startup/91-functions.py index 39569c8..a5b7e55 100644 --- a/startup/91-functions.py +++ b/startup/91-functions.py @@ -95,7 +95,7 @@ def record_calib_pos_new(n): CALIBER[f"DetU_x_pos{n}"] = DetU.x.position CALIBER[f"aper_x_pos{n}"] = aper.x.position CALIBER[f"aper_y_pos{n}"] = aper.y.position - CALIBER[f"txm_x_pos{n}"] = zps.pi_x.position + #CALIBER[f"txm_x_pos{n}"] = zps.pi_x.position mag = (DetU.z.position / zp.z.position - 1) * GLOBAL_VLM_MAG CALIBER[f"mag{n}"] = np.round(mag * 100) / 100.0 @@ -432,6 +432,8 @@ def find_nearest(data, value): yield from mv(zp.z, zp_final, det.z, det_final, XEng, eng_new) yield from mv(aper.x, aper_x_target, aper.y, aper_y_target) + + #yield from mv(DetU.x, DetU_x_ini + (det_final-det_ini)/400*0.15) if move_clens_flag: yield from mv( clens.x, @@ -448,7 +450,7 @@ def find_nearest(data, value): # yield from mv(pzt_dcm_th2.setpos, pzt_dcm_th2_target, pzt_dcm_chi2.setpos, pzt_dcm_chi2_target) # yield from mv(pzt_dcm_chi2.setpos, pzt_dcm_chi2_target) - yield from bps.sleep(0.1) + yield from bps.sleep(0.5) if abs(eng_new - eng_ini) >= 0.005: t = 10 * abs(eng_new - eng_ini) t = min(t, 2) @@ -1370,13 +1372,26 @@ def get_scan_timestamp_legacy(scan_id, return_flag=0): return scan_time.split("#")[-1] -def get_scan_timestamp(scan_id, return_flag=0): - h = db[scan_id] +def get_scan_timestamp(scan_id, return_flag=0, date_end_by=None, print_flag=1): + tmp = list(db(scan_id=scan_id)) + n = len(tmp) + if date_end_by is None: + h = db[scan_id] + else: + for sid in tmp: + uid = sid.start["uid"] + timestamp = sid.start["time"] + ts = pd.to_datetime(timestamp, unit="s").tz_localize("US/Eastern") + date_end = pd.Timestamp(date_end_by,).tz_localize('US/Eastern') + if ts < date_end: + h = db[uid] + scan_id = h.start["scan_id"] timestamp = h.start["time"] dt = datetime.fromtimestamp(timestamp) t = dt.strftime('%Y-%m-%d %H:%M:%S') - print(t) + if print_flag: + print(t) if return_flag: return t diff --git a/startup/98-user_scan.py b/startup/98-user_scan.py index d64f882..9fe2859 100644 --- a/startup/98-user_scan.py +++ b/startup/98-user_scan.py @@ -587,7 +587,7 @@ def _xanes_3D_xh( ): for eng in eng_list: yield from move_zp_ccd(eng, move_flag=1) - my_note = f"{note}@energy={eng}" + my_note = f"{note}@energy={eng}keV" yield from bps.sleep(1) print(f"current energy: {eng}") @@ -719,7 +719,7 @@ def _multi_pos_xanes_3D_xh( eng_list[0], ]: yield from move_zp_ccd(ii, move_flag=1) - my_note = note + f"_ref_flat@energy={ii}_keV" + my_note = note + f"_ref_flat@energy={ii}keV" yield from bps.sleep(1) print(f"current energy: {ii}") @@ -779,7 +779,7 @@ def _xanes_3D_zebra_xh( ): for eng in eng_list: yield from move_zp_ccd(eng, move_flag=1) - my_note = f"{note}@energy={eng}" + my_note = f"{note}@energy={eng}keV" yield from bps.sleep(1) print(f"current energy: {eng}") @@ -1153,10 +1153,56 @@ def inner_scan(): ) yield from _open_shutter_xhx(simu) - for rep in range(repeat_num): - print(f"repeat multi-pos xanes scan #{rep}") - for eng in eng_list: - yield from move_zp_ccd(eng, move_flag=1, info_flag=0) + if len(eng_list) > 1: + for rep in range(repeat_num): + print(f"repeat multi-pos xanes scan #{rep}") + for eng in eng_list: + yield from move_zp_ccd(eng, move_flag=1, info_flag=0) + yield from _open_shutter_xhx(simu) + # take image at multiple positions + for i in range(num): + yield from _move_sample_in_xhx( + x_list[i], + y_list[i], + z_list[i], + r_list[i], + repeat=2, + trans_first_flag=1, + enable_z=enable_z, + ) + yield from trigger_and_read(list(detectors) + motor) + yield from _take_bkg_image_xhx( + motor_x_out, + motor_y_out, + motor_z_out, + motor_r_out, + detectors, + motor, + num=1, + chunk_size=chunk_size, + stream_name="flat", + simu=simu, + enable_z=enable_z, + ) + yield from _move_sample_in_xhx( + motor_x_ini, + motor_y_ini, + motor_z_ini, + motor_r_ini, + repeat=2, + trans_first_flag=1, + enable_z=enable_z, + ) + # end of eng_list + # close shutter and sleep + yield from _close_shutter_xhx(simu) + # sleep + if rep < repeat_num - 1: + print(f"\nsleep for {sleep_time} seconds ...") + yield from bps.sleep(sleep_time) + elif len(eng_list) == 1: + for rep in range(repeat_num): + print(f"repeat multi-pos xanes scan #{rep}") yield from _open_shutter_xhx(simu) # take image at multiple positions for i in range(num): @@ -1192,76 +1238,79 @@ def inner_scan(): trans_first_flag=1, enable_z=enable_z, ) - # end of eng_list - # close shutter and sleep - yield from _close_shutter_xhx(simu) - # sleep - if rep < repeat_num - 1: - print(f"\nsleep for {sleep_time} seconds ...") - yield from bps.sleep(sleep_time) - + # close shutter and sleep + yield from _close_shutter_xhx(simu) + # sleep + if rep < repeat_num - 1: + print(f"\nsleep for {sleep_time} seconds ...") + yield from bps.sleep(sleep_time) yield from inner_scan() def _mk_eng_list(elem, bulk=False): - if bulk: - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem.split("_")[0] - + "/eng_list_" - + elem.split("_")[0] - + "_xanes_standard_dense.txt" - ) - else: - if elem.split("_")[-1] == "wl": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem.split("_")[0] - + "/eng_list_" - + elem.split("_")[0] - + "_xanes_standard_21pnt.txt" - ) - if elem.split("_")[-1] == "41": + if isinstance(elem, str): + if bulk: eng_list = np.genfromtxt( "/nsls2/data/fxi-new/shared/config/xanes_ref/" + elem.split("_")[0] + "/eng_list_" + elem.split("_")[0] - + "_xanes_standard_41pnt.txt" + + "_xanes_standard_dense.txt" ) - if elem.split("_")[-1] == "83": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem.split("_")[0] - + "/eng_list_" - + elem.split("_")[0] - + "_xanes_standard_83pnt.txt" - ) - elif elem.split("_")[-1] == "101": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem.split("_")[0] - + "/eng_list_" - + elem.split("_")[0] - + "_xanes_standard_101pnt.txt" - ) - elif elem.split("_")[-1] == "63": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem.split("_")[0] - + "/eng_list_" - + elem.split("_")[0] - + "_xanes_standard_63pnt.txt" - ) - elif elem.split("_")[-1] == "diff": - eng_list = np.genfromtxt( - "/nsls2/data/fxi-new/shared/config/xanes_ref/" - + elem.split("_")[0] - + "/eng_list_" - + elem.split("_")[0] - + "_xanes_standard_diff.txt" - ) - return eng_list + else: + if elem.split("_")[-1] == "wl": + eng_list = np.genfromtxt( + "/nsls2/data/fxi-new/shared/config/xanes_ref/" + + elem.split("_")[0] + + "/eng_list_" + + elem.split("_")[0] + + "_xanes_standard_21pnt.txt" + ) + if elem.split("_")[-1] == "41": + eng_list = np.genfromtxt( + "/nsls2/data/fxi-new/shared/config/xanes_ref/" + + elem.split("_")[0] + + "/eng_list_" + + elem.split("_")[0] + + "_xanes_standard_41pnt.txt" + ) + if elem.split("_")[-1] == "83": + eng_list = np.genfromtxt( + "/nsls2/data/fxi-new/shared/config/xanes_ref/" + + elem.split("_")[0] + + "/eng_list_" + + elem.split("_")[0] + + "_xanes_standard_83pnt.txt" + ) + elif elem.split("_")[-1] == "101": + eng_list = np.genfromtxt( + "/nsls2/data/fxi-new/shared/config/xanes_ref/" + + elem.split("_")[0] + + "/eng_list_" + + elem.split("_")[0] + + "_xanes_standard_101pnt.txt" + ) + elif elem.split("_")[-1] == "63": + eng_list = np.genfromtxt( + "/nsls2/data/fxi-new/shared/config/xanes_ref/" + + elem.split("_")[0] + + "/eng_list_" + + elem.split("_")[0] + + "_xanes_standard_63pnt.txt" + ) + elif elem.split("_")[-1] == "diff": + eng_list = np.genfromtxt( + "/nsls2/data/fxi-new/shared/config/xanes_ref/" + + elem.split("_")[0] + + "/eng_list_" + + elem.split("_")[0] + + "_xanes_standard_diff.txt" + ) + if eng_list[0] < eng_list[-1]: + eng_list = eng_list[::-1] + return eng_list + else: + return elem def _exp_t_sanity_check(exp_t, binning=None): @@ -1302,17 +1351,21 @@ def _exp_t_sanity_check(exp_t, binning=None): def _bin_cam(binning, cam=Andor): - yield from abs_set(cam.cam.acquire, 0, wait=True) - if binning is None: - binning = 0 - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from abs_set(cam.binning, binning, wait=True) - yield from abs_set(cam.cam.image_mode, 0, wait=True) - yield from abs_set(cam.cam.num_images, 5, wait=True) - yield from abs_set(cam.cam.acquire, 1, wait=True) - yield from abs_set(cam.cam.acquire, 0, wait=True) - return int(binning) + if cam.binning.value != binning: + yield from abs_set(cam.cam.acquire, 0, wait=True) + if binning is None: + binning = 0 + if int(binning) not in [0, 1, 2, 3, 4]: + raise ValueError("binnng must be in [0, 1, 2, 3, 4]") + yield from abs_set(cam.binning, binning, wait=True) + yield from abs_set(cam.cam.image_mode, 0, wait=True) + yield from abs_set(cam.cam.num_images, 5, wait=True) + yield from abs_set(cam.cam.acquire, 1, wait=True) + yield from abs_set(cam.cam.acquire, 0, wait=True) + yield from _prime_cam(cam) + return int(binning) + else: + return None def _sort_in_pos(in_pos_list): @@ -1360,7 +1413,9 @@ def _move_sample_out_xhx( if rot_first_flag: yield from mv(zps.pi_r, r_out) yield from mv(zps.sx, x_out, zps.sy, y_out, zps.sz, z_out) + #yield from mv(zps.sx, x_out, zps.sz, z_out) else: + #yield from mv(zps.sx, x_out, zps.sy, y_out, zps.sz, z_out) yield from mv(zps.sx, x_out, zps.sy, y_out, zps.sz, z_out) yield from mv(zps.pi_r, r_out) else: @@ -1383,10 +1438,12 @@ def _move_sample_in_xhx( for i in range(repeat): if trans_first_flag: yield from mv(zps.sx, in_x, zps.sy, in_y, zps.sz, in_z) + #yield from mv(zps.sx, in_x, zps.sz, in_z) yield from mv(zps.pi_r, in_r) else: yield from mv(zps.pi_r, in_r) yield from mv(zps.sx, in_x, zps.sy, in_y, zps.sz, in_z) + #yield from mv(zps.sx, in_x, zps.sz, in_z) else: for i in range(repeat): if trans_first_flag: @@ -1456,7 +1513,7 @@ def _set_andor_param_xhx( yield from abs_set(cam.cam.acquire_period, period_cor, wait=True) -def multi_edge_xanes_zebra( +def multi_edge_xanes_zebra_legacy( elems=["Ni_wl"], scan_type="3D", flts={"Ni_filters": [1, 2, 3]}, @@ -1507,14 +1564,10 @@ def multi_edge_xanes_zebra( else: exposure = 0.05 print("use default exposure time 0.05s") - for key in period_time.keys(): + for key in acq_p.keys(): if elem.split("_")[0] == key.split("_")[0]: yield from mv(Andor.cam.acquire_time, exposure) yield from bps.sleep(2) - # yield from mv(Andor.cam.acquire_period, period_time[key]) - # period = yield from rd(Andor.cam.acquire_period) - # period = yield from _exp_t_sanity_check(period, binning=binning) - # print(elem, f"{period=}") break eng_list = _mk_eng_list(elem, bulk=False) print(f"{out_pos=}") @@ -1612,6 +1665,129 @@ def multi_edge_xanes_zebra( print("wrong scan type") +def multi_edge_xanes_zebra( + edge_list={"Ni": "Ni_wl"}, + scan_type="3D", + flts={"Ni": [1, 2, 3]}, + exp_t={"Ni": 0.05}, + acq_p={"Ni": 0.05}, + ang_s=0, + ang_e=180, + vel=6, + acc_t=1, + in_pos_list=[[None, None, None, None]], + out_pos=[None, None, None, None], + note="", + rel_out_flag=0, + bin_fac=None, + bulk=False, + bulk_intgr=10, + simu=False, + sleep=0, + repeat=None, + ref_flat_scan=False, + cam=Andor, + flyer=tomo_flyer +): + yield from mv(cam.cam.acquire, 0) + if repeat is None: + repeat = 1 + repeat = int(repeat) + + if bin_fac is None and scan_type == "2D": + bin_fac = 0 + elif bin_fac is None and scan_type == "3D": + bin_fac = 1 + + x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) + for elem in edge_list.keys(): + for key in flts.keys(): + if elem == key: + flt = flts[key] + break + else: + flt = [] + for key in exp_t.keys(): + if elem == key: + exposure = exp_t[key] + break + else: + exposure = 0.05 + print("use default exposure time 0.05 sec") + for key in acq_p.keys(): + if elem == key: + period = acq_p[key] + break + else: + period = 0.05 + print("Use default acquisition period 0.05 sec") + print(f"We are going to do 2D XANES of {elem} with exposure time {exposure} sec and acquisition period {period} sec; filters {flt} will be used.") + eng_list = _mk_eng_list(edge_list[elem], bulk=False) + + if scan_type == "2D": + yield from _multi_pos_xanes_2D_xh( + eng_list, + x_list, + y_list, + z_list, + r_list, + out_x=out_pos[0], + out_y=out_pos[1], + out_z=out_pos[2], + out_r=out_pos[3], + exposure_time=exposure, + chunk_size=5, + simu=simu, + relative_move_flag=rel_out_flag, + note=note, + md=None, + sleep_time=sleep, + repeat_num=repeat, + binning=bin_fac, + flts=flt, + enable_z=True, + ) + elif scan_type == "3D": + yield from _multi_pos_xanes_3D_zebra_xh( + eng_list, + x_list, + y_list, + z_list, + r_list, + exp_t=exposure, + acq_p=period, + ang_s=ang_s, + ang_e=ang_e, + vel=vel, + acc_t=acc_t, + out_x=out_pos[0], + out_y=out_pos[1], + out_z=out_pos[2], + out_r=out_pos[3], + simu=simu, + rel_out_flag=rel_out_flag, + note=note, + sleep_time=sleep, + bin_fac=bin_fac, + flts=flt, + repeat=repeat, + ref_flat_scan=ref_flat_scan, + cam=cam, + flyer=flyer, + ) + + if bulk: + eng_list = _mk_eng_list(elem, bulk=True) + zpx = zp.x.position + apx = aper.x.position + cdx = clens.x.position + yield from mvr(zp.x, -5500, clens.x, 6500, aper.x, -4000) + xxanes_scan(eng_list, delay_time=0.2, intgr=bulk_intgr, note=note) + yield from mv(clens.x, cdx, aper.x, apx, zp.x, zpx) + else: + print(f"Unrecognizeable scan type {scan_type}") + + def multi_edge_xanes( elements=["Ni_wl"], scan_type="3D", @@ -1758,7 +1934,7 @@ def multi_edge_xanes( eng_list[-1], ]: yield from move_zp_ccd(ii, move_flag=1) - my_note = note + f"_ref_flat@energy={ii}_keV" + my_note = note + f"_ref_flat@energy={ii}keV" yield from bps.sleep(1) print(f"current energy: {ii}") @@ -2200,8 +2376,12 @@ def fly_scan2( binning = 0 if int(binning) not in [0, 1, 2, 3, 4]: raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(cam.binning, binning) - yield from mv(cam.cam.image_mode, 0) + if cam.name != "Oryx": + yield from mv(cam.binning, binning) + if cam.name == "Oryx": + yield from mv(cam.cam.image_mode, 1) + else: + yield from mv(cam.cam.image_mode, 0) yield from mv(cam.cam.num_images, 5) yield from mv(cam.cam.acquire, 1) @@ -2226,9 +2406,11 @@ def fly_scan2( motor_r_out = out_r if not (out_r is None) else motor_r_ini if enable_z: - motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] + #motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] + motor = [zps.sx, zps.sz, zps.pi_r] else: - motor = [zps.sx, zps.sy, zps.pi_r] + #motor = [zps.sx, zps.sy, zps.pi_r] + motor = [zps.sx, zps.pi_r] dets = [cam, ic3] taxi_ang = -1 * rs @@ -2275,13 +2457,14 @@ def fly_scan2( else: _md["hints"].setdefault("dimensions", dimensions) - yield from _set_andor_param_xhx( - exposure_time=exposure_time, - period=period, - chunk_size=20, - binning=binning, - cam=cam, - ) + if cam.name != "Oryx": + yield from _set_andor_param_xhx( + exposure_time=exposure_time, + period=period, + chunk_size=20, + binning=binning, + cam=cam, + ) yield from _set_rotation_speed(rs=np.abs(rs)) print("set rotation speed: {} deg/sec".format(rs)) @@ -2347,7 +2530,10 @@ def fly_inner_scan(): ) uid = yield from fly_inner_scan() - yield from mv(cam.cam.image_mode, 1) + if cam.name == "Oryx": + yield from mv(cam.cam.image_mode, 2) + else: + yield from mv(cam.cam.image_mode, 1) # yield from select_filters([]) print("scan finished") return uid @@ -2884,6 +3070,177 @@ def mosaic_fly_scan_xh( yield from select_filters([]) +def mosaic_zfly_scan_xh( + x_ini=None, + y_ini=None, + z_ini=None, + x_num_steps=1, + y_num_steps=1, + z_num_steps=1, + x_step_size=0, + y_step_size=0, + z_step_size=0, + scn_mode=0, + exp_t=0.05, + acq_p=0.05, + ang_s=0, + ang_e=180, + vel=3, + acc_t=1, + num_swing=1, + out_x=None, + out_y=None, + out_z=None, + out_r=None, + binning=None, + flts=[], + rel_out_flag=True, + simu=False, + note="", + enable_z=True, + repeat=1, + sleep=0, +): + yield from select_filters(flts) + yield from _bin_cam(binning) + + if x_ini is None: + x_ini = zps.sx.position + if y_ini is None: + y_ini = zps.sy.position + if z_ini is None: + z_ini = zps.sz.position + r_ini = zps.pi_r.position + + y_list = y_ini + np.arange(y_num_steps) * y_step_size + x_list = x_ini + np.arange(x_num_steps) * x_step_size + z_list = z_ini + np.arange(z_num_steps) * z_step_size + txt1 = "\n###############################################" + txt2 = "\n####### start mosaic tomography scan ######" + txt3 = "\n###############################################" + txt = txt1 + txt2 + txt3 + print(txt) + + yield from bps.sleep(1) + + for ii in range(int(repeat)): + for y in y_list: + for z in z_list: + for x in x_list: + yield from mv(zps.sx, x, zps.sy, y, zps.sz, z) + yield from tomo_zfly( + scn_mode=scn_mode, + exp_t=exp_t, + acq_p=acq_p, + ang_s=ang_s, + ang_e=ang_e, + vel=vel, + acc_t=acc_t, + num_swing=num_swing, + out_x=out_x, + out_y=out_y, + out_z=out_z, + out_r=out_r, + rel_out_flag=rel_out_flag, + flts=flts, + rot_back_velo=30, + bin_fac=binning, + note=note, + md=None, + simu=simu, + sleep=sleep, + cam=Andor, + flyer=tomo_flyer, +) + if ii < int(repeat) - 1: + print(f"sleeping for {sleep} seconds before iteration #{ii+1}") + yield from bps.sleep(sleep) + yield from mv(zps.sx, x_ini, zps.sy, y_ini, zps.sz, z_ini, zps.pi_r, r_ini) + yield from select_filters([]) + + +def cal_calib_pos(): + calib = trans_calib() + cur_eng = XEng.position + new_th2 = dcm.th2.position + + calib_eng = np.array(list(calib.keys())) + above = sorted(list(calib_eng[calib_eng > cur_eng]), reverse=True) + below = sorted(list(calib_eng[calib_eng <= cur_eng]), reverse=True) + if len(above) == 0: + eng1 = below[0] + eng2 = below[1] + elif len(below) == 0: + eng1 = above[-1] + eng2 = above[-2] + else: + eng1 = above[-1] + eng2 = below[0] + + calib_pos = {} + for item in calib[eng1].keys(): + if item != "pos": + calib_pos[cur_eng] = (cur_eng - eng2) * (calib[eng1][item] - calib[eng2][item]) / ( + eng1 - eng2 + ) + calib[eng2][item] + return calib_pos + + + +def update_th2(): + calib = trans_calib() + cur_eng = XEng.position + cur_th2 = dcm.th2.position + + calib_eng = np.array(list(calib.keys())) + above = sorted(list(calib_eng[calib_eng > cur_eng]), reverse=True) + below = sorted(list(calib_eng[calib_eng <= cur_eng]), reverse=True) + if len(above) == 0: + eng1 = below[0] + eng2 = below[1] + elif len(below) == 0: + eng1 = above[-1] + eng2 = above[-2] + else: + eng1 = above[-1] + eng2 = below[0] + old_th2 = (cur_eng - eng2) * (calib[eng1]["th2_motor"] - calib[eng2]["th2_motor"]) / ( + eng1 - eng2 + ) + calib[eng2]["th2_motor"] + delta_th2 = cur_th2 - old_th2 + for key in calib.keys(): + calib[key]["th2_motor"] += delta_th2 + return calib + + +def update_CALIBER_th2(): + global CALIBER + _calib = trans_calib() + _calib_th2 = update_th2() + for key in _calib.keys(): + CALIBER[f"th2_motor_{_calib[key]['pos']}"] = _calib_th2[key]["th2_motor"] + + +def trans_calib(): + global CALIBER + new_key1 = [] + new_key2 = [] + + for key in CALIBER.keys(): + if "XEng" in key: + new_key1.append(key.split("_")[-1] ) + new_key2.append(CALIBER[key]) + + calib_dict = {} + for key1, key2 in zip(new_key1, new_key2): + calib_dict[key2] = {} + for k in CALIBER.keys(): + if key1 in k: + calib_dict[key2][k.strip("_"+ key1)] = CALIBER[k] + calib_dict[key2]["pos"] = key1 + return calib_dict + + def grid_z_scan( zstart=-0.03, zstop=0.03, @@ -3595,7 +3952,7 @@ def radiographic_record( "out_y": out_y, "out_z": out_z, "out_r": out_r, - "filters": [filt.name for filt in flts] if flts else "None", + "filters": ["filter{}".format(t) for t in flts] if flts else "None", "note": note if note else "None", "zone_plate": ZONE_PLATE, }, @@ -3655,6 +4012,52 @@ def rad_record_inner(): yield from rad_record_inner() +def multi_pos_radiography_record( + x_list, + y_list, + z_list, + r_list, + out_x=None, + out_y=None, + out_z=None, + out_r=None, + repeat_num=1, + exposure_time=0.2, + sleep_time=1, + chunk_size=5, + simu=False, + relative_move_flag=False, + note="", + md=None, + binning=0, + flts=[], + enable_z=True,): + + eng_list = [XEng.position] + yield from _multi_pos_xanes_2D_xh( + eng_list, + x_list, + y_list, + z_list, + r_list, + out_x=out_x, + out_y=out_y, + out_z=out_z, + out_r=out_r, + repeat_num=repeat_num, + exposure_time=exposure_time, + sleep_time=sleep_time, + chunk_size=chunk_size, + simu=simu, + relative_move_flag=relative_move_flag, + note=note, + md=md, + binning=binning, + flts=flts, + enable_z=enable_z, + ) + + def multi_pos_2D_and_3D_xanes( elements=["Ni_wl"], flts={"Ni_filters": [1, 2, 3]}, @@ -3861,9 +4264,188 @@ def cal_ccd_zp_xh(eng, mag, zp_cfg=None): p = f * (mag + 1) / mag # object distance from zone plate in mm q = p * mag # detector distance from zone plate in mm det_pos = p + q # ccd detector distance from the sample in mm + print(f"obj-zp dist: {round(p, 4)} mm\ndet-sam dist: {round(det_pos, 4)} mm\nwavelength: {round(wl, 4)} nm\nNumerical aperture: {round(na, 4)} rad\nzp focal length: {round(f,4)} mm") return p, det_pos, wl, na, f +def z_scan_xh( + scan_motor:'zp_z', + start=-0.03, + stop=0.03, + steps=5, + out_x=-100, + out_y=-100, + out_z=0, + chunk_size=10, + exposure_time=0.1, + relative_move_flag=1, + note="", + md=None, +): + """ + + + scan the zone-plate to find best focus + use as: + z_scan(start=-0.03, stop=0.03, steps=5, out_x=-100, out_y=-100, chunk_size=10, exposure_time=0.1, fn='/home/xf18id/Documents/tmp/z_scan.h5', note='', md=None) + + Input: + --------- + start: float, relative starting position of zp_z + + stop: float, relative stop position of zp_z + + steps: int, number of steps between [start, stop] + + out_x: float, relative amount to move sample out for zps.sx + + out_y: float, relative amount to move sample out for zps.sy + + chunk_size: int, number of images per each subscan (for Andor camera) + + exposure_time: float, exposure time for each image + + note: str, experiment notes + + """ + + detectors = [Andor] + motor = [zps.sx, zps.sy, zps.sz, zps.sz, zp.z] + + x_ini = zps.sx.position + y_ini = zps.sy.position + z_ini = zps.sz.position + r_ini = zps.pi_r.position + + if relative_move_flag: + x_out = x_ini + out_x if not (out_x is None) else x_ini + y_out = y_ini + out_y if not (out_y is None) else y_ini + z_out = z_ini + out_z if not (out_z is None) else z_ini + + else: + x_out = out_x if not (out_x is None) else x_ini + y_out = out_y if not (out_y is None) else y_ini + z_out = out_z if not (out_z is None) else z_ini + + + if scan_motor == 'zp_x': + zp_ini = zp.x.position # zp.x intial position + real_motor = zp.x + if scan_motor == 'zp_y': + real_motor = zp.y + zp_ini = zp.y.position # zp.y intial position + else: + zp_ini = zp.z.position # zp.z intial position + real_motor = zp.z + + zp_start = zp_ini + start + zp_stop = zp_ini + stop + ''' + # detectors = [Andor] + y_ini = zps.sy.position # sample y position (initial) + y_out = ( + y_ini + out_y if not (out_y is None) else y_ini + ) # sample y position (out-position) + x_ini = zps.sx.position + x_out = x_ini + out_x if not (out_x is None) else x_ini + z_ini = zps.sz.position + z_out = z_ini if not (out_z is None) else z_ini + ''' + + period = max(exposure_time + 0.01, 0.05) + + yield from _set_andor_param( + exposure_time=exposure_time, period=period, chunk_size=chunk_size + ) + + _md = { + "detectors": [det.name for det in detectors], + "motors": [mot.name for mot in motor], + "XEng": XEng.position, + "plan_args": { + "start": start, + "stop": stop, + "steps": steps, + "out_x": out_x, + "out_y": out_y, + "out_z": out_z, + "chunk_size": chunk_size, + "exposure_time": exposure_time, + "note": note if note else "None", + }, + "plan_name": "z_scan", + "plan_pattern": "linspace", + "plan_pattern_module": "numpy", + "hints": {}, + "operator": "FXI", + #'motor_pos': wh_pos(print_on_screen=0), + } + _md.update(md or {}) + my_var = np.linspace(zp_start, zp_stop, steps) + try: + dimensions = [(motor.hints["fields"], "primary")] + except (AttributeError, KeyError): + pass + else: + _md["hints"].setdefault("dimensions", dimensions) + + @stage_decorator(list(detectors) + motor) + @run_decorator(md=_md) + def z_inner_scan(): + + # take dark image + yield from _take_dark_image(detectors, motor) + yield from _open_shutter() + for pos in my_var: + yield from mv(zps.sx, x_ini, zps.sy, y_ini) + yield from mv(real_motor, pos) + yield from bps.sleep(0.1) + yield from mv(zps.sx, x_ini, zps.sy, y_ini) + yield from mv(real_motor, pos) + yield from bps.sleep(0.1) + yield from _take_image(detectors, motor=motor, num=1) + yield from _take_bkg_image( + out_x=x_out, + out_y=y_out, + out_z=z_out, + out_r=0, + detectors=detectors, + motor=motor, + chunk_size=chunk_size, + ) + + # move back zone_plate and sample y + yield from mv(zps.sx, x_ini, zps.sy, y_ini, zps.sz, z_ini, zp.z, zp_ini) + # yield from abs_set(shutter_open, 1, wait=True) + + yield from z_inner_scan() + yield from mv(Andor.cam.image_mode, 1) + yield from _close_shutter(simu=False) + txt = get_scan_parameter() + insert_text(txt) + print(txt) + +def get_caliber_record(n=1, sh=False): + npnt = [] + for key in CALIBER.keys(): + npnt.append(key[-1]) + npnt = sorted(list(set(npnt))) + if n is None: + for num in npnt: + print(f"XEng_pos{num}: {CALIBER['XEng_pos'+num]}") + if sh: + for key in CALIBER.keys(): + if f"pos{num}" in key: + print(f"{key}: {CALIBER[key]}") + + else: + print(f"XEng_pos{n}: {CALIBER['XEng_pos'+str(n)]}") + if sh: + for key in CALIBER.keys(): + if f"pos{n}" in key: + print(f"{key}: {CALIBER[key]}") + + def zps_motor_scan_with_Andor( motors, starts, diff --git a/startup/99-umacro.py b/startup/99-umacro.py index f3e58fd..e811a0a 100644 --- a/startup/99-umacro.py +++ b/startup/99-umacro.py @@ -1,3 +1,5 @@ +import tqdm +from skimage import io # new_user() # show_global_para() # run_pdf() @@ -5,6 +7,51 @@ # check_latest_scan_id(init_guess=60000, search_size=100) ################################### +global img_handler + +def extract_1st_proj(scan_list=[], fn_save='', clim=[0, 0.5], rot_angle=0): + global img_handler + img_handler = [] + if fn_save == '' and len(scan_list) >=2: + fn_save = f'proj_1st_{scan_list[0]}_{scan_list[-1]}_angle_{rot_angle}.tiff' + n = len(scan_list) + img = [] + scan_failed = [] + h1 = db[int(scan_list[0])] + tmp = list(h1.data("Andor_image", stream_name="primary"))[0] + n_angle = len(tmp) + + ang_idx = int(rot_angle / 180. * n_angle) + for i, sid in tqdm.tqdm(enumerate(scan_list), total=n): + try: + h = db[int(sid)] + tmp = list(h.data("Andor_image", stream_name="primary"))[0] + img_tomo = np.array(tmp[ang_idx]) + if i == 0: + img = np.zeros((n, *img_tomo.shape)) + try: + img_dark = np.array(list(h.data("Andor_image", stream_name="dark")))[0][0:4] + img_dark = np.median(img_dark, axis=0) + except: + img_dark = np.array(list(h.data("Andor_image", stream_name="dark")))[0][0] + try: + img_bkg = np.array(list(h.data("Andor_image", stream_name="flat")))[0][0:4] + img_bkg = np.median(img_bkg, axis=0) + except: + img_bkg = np.array(list(h.data("Andor_image", stream_name="flat")))[0][0] + img_norm = (img_tomo-img_dark)/(img_bkg-img_dark) + img[i] = img_norm + except: + scan_failed.append(sid) + print(f'fail to extract scan {sid}') + img = np.array(img) + + if len(fn_save): + print(f'save to {fn_save}') + io.imsave(fn_save, img) + #tracker = image_scrubber(img, clim) + img_handler.append(plot3D(img)) + return img_handler, img def load_xanes_ref(*arg): """ From 014dfd3ceba444a15a1c90008c54c9479c5f5c6c Mon Sep 17 00:00:00 2001 From: FXI Operator Date: Fri, 23 Aug 2024 08:22:06 -0400 Subject: [PATCH 7/9] before MongoDB 2024_08 --- startup/18-zebra.py | 31 +- startup/18-zebra.py~ | 1157 ------------------------------------ startup/41-scans.py | 16 + startup/46-zebra_flyer.py | 61 +- startup/46-zebra_flyer.py~ | 210 ------- startup/80-load_scan.py | 19 +- startup/91-functions.py | 483 +++++++++------ startup/98-user_scan.py | 350 ++++++++++- 8 files changed, 731 insertions(+), 1596 deletions(-) delete mode 100644 startup/18-zebra.py~ delete mode 100644 startup/46-zebra_flyer.py~ diff --git a/startup/18-zebra.py b/startup/18-zebra.py index bc5be14..8f476f4 100644 --- a/startup/18-zebra.py +++ b/startup/18-zebra.py @@ -1064,24 +1064,24 @@ def compose_scn_cfg( @staticmethod def prime_det(det): - if not list(det.hdf5.array_size.get()): - if det.cam.trigger_mode.get() != 0: - yield from abs_set(det.cam.trigger_mode, 0, wait=True) - if det.cam.image_mode.get() != 0: - yield from abs_set(det.cam.image_mode, 0, wait=True) - yield from abs_set(det.cam.num_images, 5, wait=True) - yield from abs_set(det.cam.acquire, 1, wait=False) + if det.cam.trigger_mode.get() != 0: + yield from abs_set(det.cam.trigger_mode, 0, wait=True) + if det.cam.image_mode.get() != 0: + yield from abs_set(det.cam.image_mode, 0, wait=True) + yield from abs_set(det.cam.num_images, 5, wait=True) + yield from abs_set(det.cam.acquire, 1, wait=False) @staticmethod def stop_det(det): - for _ in range(5): + while det.cam.acquire.value != 0: yield from abs_set(det.cam.acquire, 0, wait=True) - yield from abs_set(det.hdf5.capture, 0, wait=True) + while det.hdf5.capture.value != 0: + yield from abs_set(det.hdf5.capture, 0, wait=True) @staticmethod def bin_det(det, bin_fac): if det.binning.value != bin_fac: - yield from abs_set(det.cam.acquire, 0, wait=False) + yield from FXITomoFlyer.stop_det(det) if bin_fac is None: bin_fac = 0 if int(bin_fac) not in [0, 1, 2, 3, 4]: @@ -1122,14 +1122,9 @@ def get_txm_cur_pos(): def init_mot_r(scn_cfg): cur_pos = zps.pi_r.position yield from abs_set(zps.pi_r.offset_freeze_switch, 1) - ang_max = max(scn_cfg["ang_s"], scn_cfg["ang_e"]) - - if ang_max > 0: - yield from abs_set(zps.pi_r.user_offset, np.ceil(ang_max / 360) * 360) - - if abs(scn_cfg["ang_s"] - cur_pos) > 360: - cur_pos = scn_cfg["ang_s"] // 360 * 360 + cur_pos % 360 - zps.pi_r.set_current_position(cur_pos) + cur_pos = (np.sign((scn_cfg["ang_s"])) * (abs(scn_cfg["ang_s"])//360)) * 360 \ + + np.sign(cur_pos) * (abs(cur_pos)%360) + zps.pi_r.set_current_position(cur_pos) yield from abs_set(zps.pi_r.acceleration, 1, wait=True) yield from abs_set(zps.pi_r.velocity, scn_cfg["mb_vel"], wait=True) yield from abs_set(zps.pi_r, scn_cfg["ang_s"], wait=True) diff --git a/startup/18-zebra.py~ b/startup/18-zebra.py~ deleted file mode 100644 index d83b117..0000000 --- a/startup/18-zebra.py~ +++ /dev/null @@ -1,1157 +0,0 @@ -import os -import h5py -import datetime -import numpy as np -import time as ttime - -from ophyd import Device, EpicsSignal, EpicsSignalRO -from ophyd import Component as Cpt -from ophyd import FormattedComponent as FC -from ophyd.utils import set_and_wait -from ophyd.areadetector.filestore_mixins import ( - new_short_uid, - resource_factory -) -from ophyd.sim import NullStatus -from databroker.assets.handlers import HandlerBase -from nslsii.detectors.zebra import Zebra, EpicsSignalWithRBV -from ophyd.device import Staged, RedundantStaging, OrderedDict -from bluesky.plan_stubs import abs_set - - -# minimum detector acquisition period in second for full frame size -# will move this definition to areaDetector.py -DET_MIN_AP = {"Andor": 0.05, "Marana": 0.01, "Oryx": 0.005} - -# default velocity for rotating rotary stage back to starting position -# will move this definition to motors.py -ROT_BACK_VEL = 30 - -BIN_FACS = {"Andor": {0: 1, 1: 2, 2: 3, 3: 4, 4:8}, "Marana": {}, "Oryx": {}} - -class ZebraPositionCaptureData(Device): - - """ - Data arrays for the Zebra position capture function and their metadata. - - ## Not all variables are needed at FXI - CD - """ - - # Data arrays - div1 = Cpt(EpicsSignal, "PC_DIV1") - div2 = Cpt(EpicsSignal, "PC_DIV2") - div3 = Cpt(EpicsSignal, "PC_DIV3") - div4 = Cpt(EpicsSignal, "PC_DIV4") - enc1 = Cpt(EpicsSignal, "PC_ENC1") - enc2 = Cpt(EpicsSignal, "PC_ENC2") - enc3 = Cpt(EpicsSignal, "PC_ENC3") - enc4 = Cpt(EpicsSignal, "PC_ENC4") - filt1 = Cpt(EpicsSignal, "PC_FILT1") - filt2 = Cpt(EpicsSignal, "PC_FILT2") - filt3 = Cpt(EpicsSignal, "PC_FILT3") - filt4 = Cpt(EpicsSignal, "PC_FILT4") - time = Cpt(EpicsSignal, "PC_TIME") - - # Array sizes - num_cap = Cpt(EpicsSignal, "PC_NUM_CAP") - num_down = Cpt(EpicsSignal, "PC_NUM_DOWN") - - # BOOLs to denote arrays with data - cap_enc1_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B0") - cap_enc2_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B1") - cap_enc3_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B2") - cap_enc4_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B3") - cap_filt1_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B4") - cap_filt2_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B5") - cap_div1_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B6") - cap_div2_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B7") - cap_div3_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B8") - cap_div4_bool = Cpt(EpicsSignal, "PC_BIT_CAP:B9") - - def stage(self): - super().stage() - - def unstage(self): - super().unstage() - - -class ZebraPositionCapture(Device): - - """ - Signals for the position capture function of the Zebra - """ - - # Configuration settings and status PVs - enc = Cpt(EpicsSignalWithRBV, "PC_ENC") - egu = Cpt(EpicsSignalRO, "M1:EGU") - dir = Cpt(EpicsSignalWithRBV, "PC_DIR") - tspre = Cpt(EpicsSignalWithRBV, "PC_TSPRE") - trig_source = Cpt(EpicsSignalWithRBV, "PC_ARM_SEL") - arm = Cpt(EpicsSignal, "PC_ARM") - disarm = Cpt(EpicsSignal, "PC_DISARM") - armed = Cpt(EpicsSignalRO, "PC_ARM_OUT") - gate_source = Cpt(EpicsSignalWithRBV, "PC_GATE_SEL") - gate_start = Cpt(EpicsSignalWithRBV, "PC_GATE_START") - gate_width = Cpt(EpicsSignalWithRBV, "PC_GATE_WID") - gate_step = Cpt(EpicsSignalWithRBV, "PC_GATE_STEP") - gate_num = Cpt(EpicsSignalWithRBV, "PC_GATE_NGATE") - gate_ext = Cpt(EpicsSignalWithRBV, "PC_GATE_INP") - gated = Cpt(EpicsSignalRO, "PC_GATE_OUT") - pulse_source = Cpt(EpicsSignalWithRBV, "PC_PULSE_SEL") - pulse_start = Cpt(EpicsSignalWithRBV, "PC_PULSE_START") - pulse_width = Cpt(EpicsSignalWithRBV, "PC_PULSE_WID") - pulse_step = Cpt(EpicsSignalWithRBV, "PC_PULSE_STEP") - pulse_max = Cpt(EpicsSignalWithRBV, "PC_PULSE_MAX") - pulse = Cpt(EpicsSignalRO, "PC_PULSE_OUT") - enc_pos1_sync = Cpt(EpicsSignal, "M1:SETPOS.PROC") - enc_pos2_sync = Cpt(EpicsSignal, "M2:SETPOS.PROC") - enc_pos3_sync = Cpt(EpicsSignal, "M3:SETPOS.PROC") - enc_pos4_sync = Cpt(EpicsSignal, "M4:SETPOS.PROC") - enc_res1 = Cpt(EpicsSignal, "M1:MRES") - enc_res2 = Cpt(EpicsSignal, "M2:MRES") - enc_res3 = Cpt(EpicsSignal, "M3:MRES") - enc_res4 = Cpt(EpicsSignal, "M4:MRES") - data_in_progress = Cpt(EpicsSignalRO, "ARRAY_ACQ") - block_state_reset = Cpt(EpicsSignal, "SYS_RESET.PROC") - data = Cpt(ZebraPositionCaptureData, "") - - def stage(self): - self.arm.put(1) - super().stage() - - def unstage(self): - self.disarm.put(1) - self.block_state_reset.put(1) - super().unstage() - - -class FXIZebraOR(Device): - # I really appreciate the different indexing for input source - # Thank you for that - - use1 = Cpt(EpicsSignal, "_ENA:B0") - use2 = Cpt(EpicsSignal, "_ENA:B1") - use3 = Cpt(EpicsSignal, "_ENA:B2") - use4 = Cpt(EpicsSignal, "_ENA:B3") - input_source1 = Cpt(EpicsSignal, "_INP1") - input_source2 = Cpt(EpicsSignal, "_INP2") - input_source3 = Cpt(EpicsSignal, "_INP3") - input_source4 = Cpt(EpicsSignal, "_INP4") - invert1 = Cpt(EpicsSignal, "_INV:B0") - invert2 = Cpt(EpicsSignal, "_INV:B1") - invert3 = Cpt(EpicsSignal, "_INV:B2") - invert4 = Cpt(EpicsSignal, "_INV:B3") - - def stage(self): - super().stage() - - def unstage(self): - super().unstage() - - -class ZebraAND(Device): - # I really appreciate the different indexing for input source - # Thank you for that - use1 = Cpt(EpicsSignal, "_ENA:B0") - use2 = Cpt(EpicsSignal, "_ENA:B1") - use3 = Cpt(EpicsSignal, "_ENA:B2") - use4 = Cpt(EpicsSignal, "_ENA:B3") - input_source1 = Cpt(EpicsSignal, "_INP1") - input_source2 = Cpt(EpicsSignal, "_INP2") - input_source3 = Cpt(EpicsSignal, "_INP3") - input_source4 = Cpt(EpicsSignal, "_INP4") - invert1 = Cpt(EpicsSignal, "_INV:B0") - invert2 = Cpt(EpicsSignal, "_INV:B1") - invert3 = Cpt(EpicsSignal, "_INV:B2") - invert4 = Cpt(EpicsSignal, "_INV:B3") - - def stage(self): - super().stage() - - def unstage(self): - super().unstage() - - -class ZebraPulse(Device): - width = Cpt(EpicsSignalWithRBV, "WID") - input_addr = Cpt(EpicsSignalWithRBV, "INP") - input_str = Cpt(EpicsSignalRO, "INP:STR", string=True) - input_status = Cpt(EpicsSignalRO, "INP:STA") - delay = Cpt(EpicsSignalWithRBV, "DLY") - delay_sync = Cpt(EpicsSignal, "DLY:SYNC") - time_units = Cpt(EpicsSignalWithRBV, "PRE", string=True) - output = Cpt(EpicsSignal, "OUT") - - input_edge = FC(EpicsSignal, "{self._zebra_prefix}POLARITY:{self._edge_addr}") - - _edge_addrs = { - 1: "BC", - 2: "BD", - 3: "BE", - 4: "BF", - } - - def __init__( - self, - prefix, - *, - index=None, - parent=None, - configuration_attrs=None, - read_attrs=None, - **kwargs, - ): - if read_attrs is None: - read_attrs = ["input_addr", "input_edge", "delay", "width", "time_units"] - if configuration_attrs is None: - configuration_attrs = [] - - zebra = parent - self.index = index - self._zebra_prefix = zebra.prefix - self._edge_addr = self._edge_addrs[index] - - super().__init__( - prefix, - configuration_attrs=configuration_attrs, - read_attrs=read_attrs, - parent=parent, - **kwargs, - ) - - def stage(self): - super().stage() - - def unstage(self): - super().unstage() - - -class FXIZebra(Zebra): - """ - FXI Zebra device. - """ - - pc = Cpt(ZebraPositionCapture, "") - or1 = Cpt(FXIZebraOR, "OR1") # XF:18ID-ES:1{Dev:Zebra1}:OR1_INV:B0 - or2 = Cpt(FXIZebraOR, "OR2") - or3 = Cpt(FXIZebraOR, "OR3") - or4 = Cpt(FXIZebraOR, "OR4") - and1 = Cpt(ZebraAND, "AND1") # XF:18ID-ES:1{Dev:Zebra2}:AND1_ENA:B0 - and2 = Cpt(ZebraAND, "AND2") - and3 = Cpt(ZebraAND, "AND3") - and4 = Cpt(ZebraAND, "AND4") - pulse1 = Cpt(ZebraPulse, "PULSE1_", index=1) # XF:18ID-ES:1{Dev:Zebra1}:PULSE1_INP - pulse2 = Cpt(ZebraPulse, "PULSE2_", index=2) - pulse3 = Cpt(ZebraPulse, "PULSE3_", index=3) - pulse4 = Cpt(ZebraPulse, "PULSE4_", index=4) - - def stage(self): - super().stage() - - def unstage(self): - super().unstage() - - def __init__(self, prefix, *, read_attrs=None, configuration_attrs=None, **kwargs): - if read_attrs is None: - read_attrs = [] - if configuration_attrs is None: - configuration_attrs = [] - - super().__init__( - prefix, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - **kwargs, - ) - - -## Class below requires FXI specific changes -class FXITomoFlyer(Device): - """ - This is the flyer object for the Zebra. - This is the position based flyer. - """ - root_path = "/nsls2/data/fxi-new/legacy/" - write_path_template = f"zebra/%Y/%m/%d/" - read_path_template = f"zebra/%Y/%m/%d/" - reg_root = f"zebra/" - - KNOWN_DETS = {"Andor", "Marana", "Oryx"} - rot_axis = zps.pi_r - # dummy_axis = ophyd.sim.SynAxis(name="TOMO_DUMMY") - - scn_modes = { - 0: "standard", # a single scan in a given angle range - 1: "snaked: multiple files", # back-forth rocking scan with each swing being saved into a file - 2: "snaked: single file", # back-forth rocking scan being saved into a single file - } - - dft_pulse_wid = {'ms': 0.002, 's': 0.0005, '10s': 0.003} # 0: ms # 1: s # 2: 10s - pc_trig_dir = {1: 0, -1: 1} # 1: positive, -1: negative - scan_cfg = {} - pc_cfg = {} - _staging_delay = 0.010 - tspre = 's' ## ['ms', 's', '10s'] - - def __init__(self, dets, zebra, *, reg=db.reg, scn_mode=0, **kwargs): - super().__init__("", parent=None, **kwargs) - self._state = "idle" - self._dets = dets - self._filestore_resource = None - self._encoder = zebra - self._document_cache = [] # self._document_cache defines resource and datum documents - self._stage_sigs = {} - self._last_bulk = None # self._last_bulk defines event document - - self.reg = reg - self.scn_mode = self.scn_modes[scn_mode] - self.extra_stage_sigs = {} - self.shutter_delay = 0.1 # unit: deg; _shutter_delay/rot_vel > unibliz shutter opening time 1.5ms - self.use_shutter = True - - set_and_wait(self._encoder.pc.block_state_reset, 1) - - ############### Zebra Setup ############### - ## PC Tab - set_and_wait(self._encoder.pc.data.cap_enc1_bool, 1) - set_and_wait(self._encoder.pc.data.cap_enc2_bool, 0) - set_and_wait(self._encoder.pc.data.cap_enc3_bool, 0) - set_and_wait(self._encoder.pc.data.cap_enc4_bool, 0) - set_and_wait(self._encoder.pc.data.cap_filt1_bool, 0) - set_and_wait(self._encoder.pc.data.cap_filt2_bool, 0) - set_and_wait(self._encoder.pc.data.cap_div1_bool, 0) - set_and_wait(self._encoder.pc.data.cap_div2_bool, 0) - set_and_wait(self._encoder.pc.data.cap_div3_bool, 0) - set_and_wait(self._encoder.pc.data.cap_div4_bool, 0) - - set_and_wait(self._encoder.pc.enc, 0) # 0: Enc1, 1: Enc2, 2: Enc3, 3: Enc4, - set_and_wait(self._encoder.pc.dir, 0) # 0: Positive, 1: Negative - set_and_wait(self._encoder.pc.tspre, 1) # 0: ms, 1: s, 2: 10s - - ## AND tab -- can be used for external triggering - set_and_wait(self._encoder.and1.use1, 0) # 0: No, 1: Yes - set_and_wait(self._encoder.and1.use2, 0) - set_and_wait(self._encoder.and1.use3, 0) - set_and_wait(self._encoder.and1.use4, 0) - set_and_wait(self._encoder.and1.input_source1, 0) - set_and_wait(self._encoder.and1.input_source2, 0) - set_and_wait(self._encoder.and1.input_source3, 0) - set_and_wait(self._encoder.and1.input_source4, 0) - set_and_wait(self._encoder.and1.invert1, 0) # 0: No, 1: Yes - set_and_wait(self._encoder.and1.invert2, 0) - set_and_wait(self._encoder.and1.invert3, 0) - set_and_wait(self._encoder.and1.invert4, 0) - - ## OR Tab -- can be used for diagnose - set_and_wait(self._encoder.or1.use1, 0) # 0: No, 1: Yes - set_and_wait(self._encoder.or1.use2, 0) - set_and_wait(self._encoder.or1.use3, 0) - set_and_wait(self._encoder.or1.use4, 0) - set_and_wait(self._encoder.or1.input_source1, 0) - set_and_wait(self._encoder.or1.input_source2, 0) - set_and_wait(self._encoder.or1.input_source3, 0) - set_and_wait(self._encoder.or1.input_source4, 0) - set_and_wait(self._encoder.or1.invert1, 0) # 0 = No, 1 = Yes - set_and_wait(self._encoder.or1.invert2, 0) - set_and_wait(self._encoder.or1.invert3, 0) - set_and_wait(self._encoder.or1.invert4, 0) - - ## PULSE tab -- set for fast shutter - set_and_wait(self._encoder.pulse1.input_addr, 31) - set_and_wait(self._encoder.pulse1.input_edge, 0) # 0 = rising, 1 = falling - set_and_wait(self._encoder.pulse1.delay, 0) - set_and_wait(self._encoder.pulse2.input_addr, 31) - set_and_wait(self._encoder.pulse2.input_edge, 1) # 0 = rising, 1 = falling - set_and_wait(self._encoder.pulse2.delay, 0) - - ## ENC tab - set_and_wait(self._encoder.pc.enc_pos1_sync, 1) - set_and_wait(self._encoder.pc.enc_pos2_sync, 0) - set_and_wait(self._encoder.pc.enc_pos3_sync, 0) - set_and_wait(self._encoder.pc.enc_pos4_sync, 0) - - ## SYS tab - set_and_wait(self._encoder.output1.ttl.addr, 53) # PC_PULSE --> TTL1 --> Camera - set_and_wait(self._encoder.output2.ttl.addr, 52) # PC_PULSE --> TTL2 --> fast shutter - set_and_wait(self._encoder.output3.ttl.addr, 0) - set_and_wait(self._encoder.output4.ttl.addr, 0) - - @property - def encoder(self): - return self._encoder - - @property - def detectors(self): - return tuple(self._dets) - - @detectors.setter - def detectors(self, value): - dets = tuple(value) - if not all(d.name in self.KNOWN_DETS for d in dets): - raise ValueError( - f"One or more of {[d.name for d in dets]}" - f"is not known to the zebra. " - f"The known detectors are {self.KNOWN_DETS})" - ) - self._dets = dets - - def preset_zebra(self, pc_cfg={}): - ############### PC Arm - set_and_wait(self._encoder.pc.trig_source, 0) # 0 = Soft, 1 = External - ############### PC Pulse - # yield from abs_set(self._encoder.pc.pulse_width, self.dft_pulse_wid[self.tspre], wait=True) - - ############### PULSE -- set unibliz trigger to 'external exposure' - if self.use_shutter: - set_and_wait(self._encoder.pulse1.time_units, self.tspre) - self._encoder.pulse1.width.put(self.dft_pulse_wid[self.tspre], timeout=10) - ttime.sleep(0.01) - set_and_wait(self._encoder.pulse2.time_units, self.tspre) - self._encoder.pulse2.width.put(self.dft_pulse_wid[self.tspre], timeout=10) - ttime.sleep(0.01) - set_and_wait(self._encoder.output2.ttl.addr, 52) - else: - set_and_wait(self._encoder.output2.ttl.addr, 29) - if self.scn_mode == "standard": - ############### PC Tab ############### - ## PC Gate - set_and_wait(self._encoder.pc.gate_source, 0) # 0 = Position, 1 = Time, 2 = External - set_and_wait(self._encoder.pc.gate_step, 0) - set_and_wait(self._encoder.pc.gate_num, 1) - - ## PC Pulse - set_and_wait(self._encoder.pc.pulse_source, 0) # 0 = Position, 1 = Time, 2 = External - self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) - ttime.sleep(0.01) - elif self.scn_mode == "snaked: multiple files": - ############### PC Tab ############### - ## PC Gate - set_and_wait(self._encoder.pc.gate_source, 0) # 0 = Position, 1 = Time, 2 = External - set_and_wait(self._encoder.pc.gate_step, 0) - set_and_wait(self._encoder.pc.gate_num, 1) - - ## PC Pulse - set_and_wait(self._encoder.pc.pulse_source, 0) # 0 = Position, 1 = Time, 2 = External - self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) - ttime.sleep(0.01) - elif self.scn_mode == "snaked: single file": - ############### PC Tab ############### - ## PC Gate - set_and_wait(self._encoder.pc.gate_source, 2) # 0 = Position, 1 = Time, 2 = External - set_and_wait(self._encoder.pc.gate_ext, 29) - set_and_wait(self._encoder.pc.gate_num, 0) - - ## PC Pulse - set_and_wait(self._encoder.pc.pulse_source, 1) # 0 = Position, 1 = Time, 2 = External - self._encoder.pc.pulse_width.put(self.dft_pulse_wid[self.tspre], timeout=10) - ttime.sleep(0.01) - - for key, val in pc_cfg[self.scn_mode].items(): - getattr(self._encoder.pc, key).put(val, timeout=10) - ttime.sleep(0.01) - - def make_filename(self): - """Make a filename. - Taken/Modified from ophyd.areadetector.filestore_mixins - This is a hook so that the read and write paths can either be modified - or created on disk prior to configuring the areaDetector plugin. - Returns - ------- - filename : str - The start of the filename - read_path : str - Path that ophyd can read from - write_path : str - Path that the IOC can write to - """ - filename = f"{new_short_uid()}.h5" - formatter = datetime.now().strftime - write_path = formatter(f"{self.root_path}{self.write_path_template}") - read_path = formatter(f"{self.root_path}{self.read_path_template}") - return filename, read_path, write_path - - def stage(self): - # self.set_stage_sigs() - self._stage_with_delay() - super.stage() - - def _stage_with_delay(self): - # Staging taken from https://github.com/bluesky/ophyd/blob/master/ophyd/device.py - # Device - BlueskyInterface - """Stage the device for data collection. - This method is expected to put the device into a state where - repeated calls to :meth:`~BlueskyInterface.trigger` and - :meth:`~BlueskyInterface.read` will 'do the right thing'. - Staging not idempotent and should raise - :obj:`RedundantStaging` if staged twice without an - intermediate :meth:`~BlueskyInterface.unstage`. - This method should be as fast as is feasible as it does not return - a status object. - The return value of this is a list of all of the (sub) devices - stage, including it's self. This is used to ensure devices - are not staged twice by the :obj:`~bluesky.run_engine.RunEngine`. - This is an optional method, if the device does not need - staging behavior it should not implement `stage` (or - `unstage`). - Returns - ------- - devices : list - list including self and all child devices staged - """ - if self._staged == Staged.no: - pass # to short-circuit checking individual cases - elif self._staged == Staged.yes: - raise RedundantStaging( - "Device {!r} is already staged. " "Unstage it first.".format(self) - ) - elif self._staged == Staged.partially: - raise RedundantStaging( - "Device {!r} has been partially staged. " - "Maybe the most recent unstaging " - "encountered an error before finishing. " - "Try unstaging again.".format(self) - ) - self.log.debug("Staging %s", self.name) - self._staged = Staged.partially - - # Resolve any stage_sigs keys given as strings: 'a.b' -> self.a.b - stage_sigs = OrderedDict() - for k, v in self.stage_sigs.items(): - if isinstance(k, str): - # Device.__getattr__ handles nested attr lookup - stage_sigs[getattr(self, k)] = v - else: - stage_sigs[k] = v - - # Read current values, to be restored by unstage() - original_vals = {sig: sig.get() for sig in stage_sigs} - - # We will add signals and values from original_vals to - # self._original_vals one at a time so that - # we can undo our partial work in the event of an error. - - # Apply settings. - devices_staged = [] - try: - for sig, val in stage_sigs.items(): - self.log.debug( - "Setting %s to %r (original value: %r)", - self.name, - val, - original_vals[sig], - ) - sig.set(val, timeout=10).wait() - ttime.sleep(self._staging_delay) - # It worked -- now add it to this list of sigs to unstage. - self._original_vals[sig] = original_vals[sig] - devices_staged.append(self) - - # Call stage() on child devices. - for attr in self._sub_devices: - device = getattr(self, attr) - if hasattr(device, "stage"): - device.stage() - devices_staged.append(device) - except Exception: - self.log.debug( - "An exception was raised while staging %s or " - "one of its children. Attempting to restore " - "original settings before re-raising the " - "exception.", - self.name, - ) - self.unstage() - raise - else: - self._staged = Staged.yes - return devices_staged - - def unstage(self): - self._unstage_with_delay() - - def _unstage_with_delay(self): - # Staging taken from https://github.com/bluesky/ophyd/blob/master/ophyd/device.py - # Device - BlueskyInterface - """Unstage the device. - This method returns the device to the state it was prior to the - last `stage` call. - This method should be as fast as feasible as it does not - return a status object. - This method must be idempotent, multiple calls (without a new - call to 'stage') have no effect. - Returns - ------- - devices : list - list including self and all child devices unstaged - """ - self.log.debug("Unstaging %s", self.name) - self._staged = Staged.partially - devices_unstaged = [] - - # Call unstage() on child devices. - for attr in self._sub_devices[::-1]: - device = getattr(self, attr) - if hasattr(device, "unstage"): - device.unstage() - devices_unstaged.append(device) - - # Restore original values. - for sig, val in reversed(list(self._original_vals.items())): - self.log.debug("Setting %s back to its original value: %r)", self.name, val) - sig.set(val, timeout=10).wait() - ttime.sleep(self._staging_delay) - self._original_vals.pop(sig) - devices_unstaged.append(self) - - self._staged = Staged.no - return devices_unstaged - - def kickoff(self, *, scn_cfg={}): - self._encoder.pc.arm.put(0) - ttime.sleep(self._staging_delay) - self._state = "kicked off" - - if scn_cfg["ang_s"] < scn_cfg["ang_e"]: - self._encoder.pc.dir.put(0) - try: - self.rot_axis.user_setpoint.put(scn_cfg["ang_s"] - scn_cfg["taxi_dist"]) - except Exception as e: - print(e) - print("Cannot move rotary stage to its taxi position.") - return - else: - self._encoder.pc.dir.put(1) - try: - self.rot_axis.user_setpoint.put(scn_cfg["ang_s"] + scn_cfg["taxi_dist"]) - except Exception as e: - print(e) - print("Cannot move rotary stage to its taxi position.") - return - - if scn_cfg["scn_mode"] == "snaked: multiple files": - self._encoder.pc.gate_start.put(scn_cfg["ang_s"]) - - # sync rotary stage encoder - self._encoder.pc.enc_pos1_sync.put(1) - ttime.sleep(self._staging_delay) - - # Do a block reset on the zebra - self._encoder.pc.block_state_reset.put(1) - ttime.sleep(self._staging_delay) - - return NullStatus() - - def complete(self): - """ - Call this when all needed data has been collected. This has no idea - whether that is true, so it will obligingly stop immediately. It is - up to the caller to ensure that the motion is actually complete. - """ - - print("\nin complet: complete starts") - - # Our acquisition complete PV is: XF:05IDD-ES:1{Dev:Zebra1}:ARRAY_ACQ - t0 = ttime.monotonic() - while self._encoder.pc.data_in_progress.get() == 1: - ttime.sleep(self._staging_delay) - if (ttime.monotonic() - t0) > 60: - print(f"{self.name} is behaving badly!") - self._encoder.pc.disarm.put(1) - ttime.sleep(0.100) - if self._encoder.pc.data_in_progress.get() == 1: - raise TimeoutError - - self._state = "complete" - self._encoder.pc.block_state_reset.put(1) - - for d in self._dets: - d.stop() - - # Set filename/path for zebra data - f, rp, wp = self.make_filename() - self.__filename = f - self.__read_filepath = os.path.join(rp, self.__filename) - self.__write_filepath = os.path.join(wp, self.__filename) - - self.__filestore_resource, datum_factory_z = resource_factory( - "ZEBRA_HDF51", - root="/", - resource_path=self.__read_filepath, - resource_kwargs={}, - path_semantics="posix", - ) - - time_datum = datum_factory_z({"column": "zebra_time"}) - enc1_datum = datum_factory_z({"column": "enc1_pi_r"}) - - # self._document_cache defines resource and datum documents - self._document_cache = [("resource", self.__filestore_resource)] - self._document_cache.extend( - ("datum", d) - for d in ( - time_datum, - enc1_datum, - ) - ) - - # grab the asset documents from all of the child detectors - for d in self._dets: - self._document_cache.extend(d.collect_asset_docs()) - - # Write the file. - # @timer_wrapper - def get_zebra_data(): - export_zebra_data(self._encoder, self.__write_filepath) - get_zebra_data() - - # Yield a (partial) Event document. The RunEngine will put this - # into metadatastore, as it does all readings. - self._last_bulk = { - "time": ttime.time(), - "seq_num": 1, - "data": { - "zebra_time": time_datum["datum_id"], - "enc1_pi_r": enc1_datum["datum_id"], - }, - "timestamps": { - "zebra_time": time_datum["datum_id"], # not a typo# - "enc1_pi_r": time_datum["datum_id"], - }, - } - - for d in self._dets: - reading = d.read() - self._last_bulk["data"].update({k: v["value"] for k, v in reading.items()}) - self._last_bulk["timestamps"].update( - {k: v["timestamp"] for k, v in reading.items()} - ) - print(f"\nin complete: {type(self._last_bulk)=}\n{type(self._document_cache)=}\n") - print(f"\nin complete: {(self._last_bulk)=}\n{(self._document_cache)=}\n") - - return NullStatus() - - def describe_collect(self): - ext_spec = "FileStore:" - num_zebra_data = self.encoder.pc.data.time.get().shape[0] - - spec = { - "external": ext_spec, - "dtype": "array", - "shape": [num_zebra_data], - "source": "", # make this the PV of the array the det is writing - } - - desc = OrderedDict() # desc defines data_keys - - desc['zebra_time'] = spec - desc['zebra_time']["source"] = getattr(self._encoder.pc.data, 'enc1').pvname - desc['enc1_pi_r'] = spec - desc['enc1_pi_r']["source"] = getattr(self._encoder.pc.data, 'enc1').pvname - - # Handle the detectors we are going to get - for d in self.detectors: - desc.update(d.describe()) - # print(f"\nin describe_collect {desc=}\n") - - # # Handle the ion chamber that the zebra is collecting - # desc["i0"] = spec - # desc["i0"]["source"] = self._sis.mca2.pvname - # desc["i0_time"] = spec - # desc["i0_time"]["source"] = self._sis.mca1.pvname - # desc["im"] = spec - # desc["im"]["source"] = self._sis.mca3.pvname - # desc["it"] = spec - # desc["it"]["source"] = self._sis.mca4.pvname - - return {"primary": desc} - - def collect(self): - # Create records in the FileStore database. - # move this to stage because I think that describe_collect needs the - # resource id - # TODO use ophyd.areadectector.filestoer_mixins.resllource_factory here - if self._last_bulk is None: - raise Exception( - "the order of complete and collect is brittle and out " - "of sync. This device relies on in-order and 1:1 calls " - "between complete and collect to correctly create and stash " - "the asset registry documents" - ) - # self._last_bulk defines a event document - yield self._last_bulk - self._last_bulk = None - self._state = "idle" - - def collect_asset_docs(self): - yield from iter(list(self._document_cache)) - self._document_cache.clear() - - def stop(self): - self._encoder.pc.block_state_reset.put(1) - pass - - def pause(self): - "Pausing in the middle of a kickoff nukes the partial dataset." - self._encoder.pc.block_state_reset.put(1) - for d in self._dets: - if hasattr(d, "settings"): - d.settings.acquire.put(0) - if hasattr(d, "cam"): - d.cam.acquire.put(0) - self._state = "idle" - self.unstage() - - def resume(self): - self.unstage() - self.stage() - - def preset_flyer(self, scn_cfg): - yield from FXITomoFlyer.bin_det(self.detectors[0], scn_cfg["bin_fac"]) - yield from FXITomoFlyer.init_mot_r(scn_cfg) - yield from FXITomoFlyer.set_cam_mode(self.detectors[0], stage="pre-scan") - scn_cfg = FXITomoFlyer.cal_cam_rot_params(self.detectors[0], scn_cfg) - pc_cfg = FXITomoFlyer.cal_zebra_pc_params(scn_cfg) - self.preset_zebra(pc_cfg) - print("preset_flyer is done") - return scn_cfg, pc_cfg - - def set_pc_step_for_scan(self, scn_cfg, pc_cfg): - yield from abs_set(self.encoder.pc.dir, pc_cfg[self.scn_modes[scn_cfg["scn_mode"]]]["dir"], wait=True) - yield from abs_set(self.encoder.pc.gate_start, pc_cfg[self.scn_modes[scn_cfg["scn_mode"]]]["gate_start"], wait=True) - - @staticmethod - def set_wait(field, val, wait=0.01): - set_and_wait(field, val, poll_time=wait) - # while field.get() != val: - # field.put(val, timeout=10) - # ttime.sleep(0.01) - # ttime.sleep(wait) - - @classmethod - def cal_cam_rot_params(cls, det, scn_cfg): - """_summary_ - - Args: - det (str): choose from the set {"Andor", "Marana", "Oryx"} - scn_cfg (dict): scan configuration parameters composed of - 'scn_mode': choose between { - 0: "standard", # a single scan in a given angle range - 1: "snaked: single file", # back-forth rocking scan being saved into a single file - 2: "snaked: multiple files" # back-forth rocking scan with each swing being saved into a file - } - 'exp_t': detector exposure time in second, - 'acq_p': acquisition period in second, - 'bin_fac': detector binning factor, - 'ang_s': scan starting angle, - 'ang_e': scan end angle, - 'num_swing': number of sub-scans; motion from one side to another side is defined as one swing - 'vel': rotation velocity in deg/sec, - 'tacc': rotation stage acceleration in sec, - "taxi_dist": taxi distance in unit deg - - Returns: - dict: scan_cfg - """ - ############### calculate detector parameters ############### - acq_p = FXITomoFlyer.check_cam_exp(det, scn_cfg["acq_p"], scn_cfg["bin_fac"]) - - if acq_p > scn_cfg["acq_p"]: - print( - "Acquisition period is too small for the camera. Reset acquisition period to minimum allowed exposure time." - ) - scn_cfg["acq_p"] = acq_p - - if scn_cfg["exp_t"] > acq_p - 0.002: - scn_cfg["exp_t"] = acq_p - 0.002 - - ############### calculate rotary stage parameters ############### - if scn_cfg["tacc"] <= 0: - print("Acceleration time cannot be smaller than 0. Reset it to 1 second.") - scn_cfg["tacc"] = 1 - - if scn_cfg["vel"] > cls.rot_axis.max_velo.get(): - print( - "Designed velocity exceeds the maximum allowed velocity. Reset it to the maximum allowed velocity" - ) - scn_cfg["vel"] = cls.rot_axis.max_velo.get() - elif scn_cfg["vel"] < cls.rot_axis.base_velo.get(): - print( - "Designed velocity is smaller than the minimum allowed velocity. Reset it to the minimum allowed velocity" - ) - scn_cfg["vel"] = cls.rot_axis.base_velo.get() - - taxi_dist = np.ceil( - (scn_cfg["vel"] - cls.rot_axis.base_velo.get()) * scn_cfg["tacc"] / 2 - ) - if not (cls.rot_axis.low_limit_switch.get() == 0 and cls.rot_axis.high_limit_switch.get() == 0): - if (scn_cfg["ang_s"] - taxi_dist) < cls.rot_axis.low_limit.get(): - print("Rotation range is beyond the low limit of the rotary stage. Quit!") - return None - elif (scn_cfg["ang_s"] - taxi_dist) > cls.rot_axis.high_limit.get(): - print("Rotation range is beyond the high limit of the rotary stage. Quit!") - return None - - if (scn_cfg["ang_e"] + taxi_dist) < cls.rot_axis.low_limit.get(): - print("Rotation range is beyond the low limit of the rotary stage. Quit!") - return None - elif (scn_cfg["ang_e"] + taxi_dist) > cls.rot_axis.high_limit.get(): - print("Rotation range is beyond the high limit of the rotary stage. Quit!") - return None - - scn_cfg["taxi_dist"] = taxi_dist - scn_cfg["num_images"] = ( - int( - abs( - (scn_cfg["ang_e"] - scn_cfg["ang_s"]) / scn_cfg["vel"] / scn_cfg["acq_p"] - ) - ) - + 1 - ) - scn_cfg["ang_step"] = scn_cfg["vel"] * scn_cfg["acq_p"] - - if scn_cfg["ang_s"] < scn_cfg["ang_e"]: - scn_cfg["rot_dir"] = 1 - else: - scn_cfg["rot_dir"] = -1 - - return scn_cfg - - @staticmethod - def check_cam_exp(det, acq_p, bin_fac): - acq_min = DET_MIN_AP[det.name] / BIN_FACS[det.name][bin_fac] - acq_p = max(acq_min, acq_p) - return acq_p - - @classmethod - def cal_zebra_pc_params(cls, scn_cfg): - """_summary_ - - Args: - scn_cfg (dict): scan configuration parameters composed of - 'scn_mode': choose between { - 0: "standard", # a single scan in a given angle range - 1: "snaked: single file", # back-forth rocking scan being saved into a single file - 2: "snaked: multiple files" # back-forth rocking scan with each swing being saved into a file - } - 'exp_t': detector exposure time in second, - 'acq_p': acquisition period in second, - 'bin_fac': detector binning factor, - 'ang_s': scan starting angle, - 'ang_e': scan end angle, - 'num_swing': number of sub-scans; motion from one side to another side is defined as one swing - 'vel': rotation velocity in deg/sec, - 'tacc': rotation stage acceleration in sec, - "taxi_dist": taxi distance in unit deg - - Returns: - dict: pc_cfg - """ - pc_cfg = { - "standard": {}, - "snaked: multiple files": {}, - "snaked: single file": {}, - } - if cls.scn_modes[scn_cfg["scn_mode"]] == "standard": - pc_cfg["standard"]["gate_start"] = scn_cfg["ang_s"] - pc_cfg["standard"]["gate_width"] = abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) - pc_cfg["standard"]["pulse_start"] = 0 - pc_cfg["standard"]["pulse_width"] = ( - cls.dft_pulse_wid[cls.tspre] * scn_cfg["vel"] - ) - pc_cfg["standard"]["pulse_step"] = round(scn_cfg["ang_step"], 3) - pc_cfg["standard"]["pulse_max"] = int(round( - abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) - / pc_cfg["standard"]["pulse_step"] - + 1 - )) - pc_cfg["standard"]["gate_width"] = ( - abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) - + pc_cfg["standard"]["pulse_step"] - ) - elif cls.scn_modes[scn_cfg["scn_mode"]] == "snaked: multiple files": - pc_cfg["snaked: multiple files"]["pulse_start"] = 0 - pc_cfg["snaked: multiple files"]["pulse_width"] = ( - cls.dft_pulse_wid[cls.tspre] * scn_cfg["vel"] - ) - pc_cfg["snaked: multiple files"]["pulse_step"] = round(scn_cfg["ang_step"], 3) - pc_cfg["snaked: multiple files"]["pulse_max"] = int(round( - abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) - / pc_cfg["snaked: multiple files"]["pulse_step"] - + 1 - )) - pc_cfg["snaked: multiple files"]["gate_width"] = ( - abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) - + pc_cfg["snaked: multiple files"]["pulse_step"] - ) - elif cls.scn_modes[scn_cfg["scn_mode"]] == "snaked: single file": - pc_cfg["snaked: single file"]["pulse_start"] = 0 - pc_cfg["snaked: single file"]["pulse_width"] = cls.dft_pulse_wid[cls.tspre] - pc_cfg["snaked: single file"]["pulse_step"] = round(scn_cfg["acq_p"], 4) - pc_cfg["snaked: single file"]["pulse_max"] = int(int(round( - abs(scn_cfg["ang_e"] - scn_cfg["ang_s"]) - / (scn_cfg["acq_p"] * scn_cfg["vel"]) - + 2 * scn_cfg["tacc"] / scn_cfg["acq_p"] - + 1 - )) * scn_cfg["num_swing"]) - else: - print("Unrecognized scan mode. Quit") - return None - pc_cfg[cls.scn_modes[scn_cfg["scn_mode"]]]["dir"] = cls.pc_trig_dir[scn_cfg["rot_dir"]] - return pc_cfg - - @staticmethod - def compose_scn_cfg( - scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, tacc, num_swing - ): - scn_cfg = {} - scn_cfg["scn_mode"] = scn_mode - scn_cfg["exp_t"] = exp_t - scn_cfg["acq_p"] = acq_p - scn_cfg["bin_fac"] = 0 if bin_fac is None else bin_fac - scn_cfg["ang_s"] = ang_s - scn_cfg["ang_e"] = ang_e - scn_cfg["vel"] = vel - scn_cfg["tacc"] = tacc - scn_cfg["num_swing"] = num_swing - return scn_cfg - - @staticmethod - def prime_det(det): - if not list(det.hdf5.array_size.get()): - if det.cam.trigger_mode.get() != 0: - yield from abs_set(det.cam.trigger_mode, 0, wait=True) - if det.cam.image_mode.get() != 0: - yield from abs_set(det.cam.image_mode, 0, wait=True) - yield from abs_set(det.cam.num_images, 5, wait=True) - yield from abs_set(det.cam.acquire, 1, wait=True) - - @staticmethod - def bin_det(det, bin_fac): - yield from abs_set(det.cam.acquire, 0, wait=True) - if bin_fac is None: - bin_fac = 0 - if int(bin_fac) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from abs_set(det.binning, bin_fac, wait=True) - FXITomoFlyer.prime_det(det) - - @staticmethod - def def_abs_out_pos( - x_out, - y_out, - z_out, - r_out, - rel_out_flag, - ): - (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() - if rel_out_flag: - mot_x_out = x_ini + x_out if not (x_out is None) else x_ini - mot_y_out = y_ini + y_out if not (y_out is None) else y_ini - mot_z_out = z_ini + z_out if not (z_out is None) else z_ini - mot_r_out = r_ini + r_out if not (r_out is None) else r_ini - else: - mot_x_out = x_out if not (x_out is None) else x_ini - mot_y_out = y_out if not (y_out is None) else y_ini - mot_z_out = z_out if not (z_out is None) else z_ini - mot_r_out = r_out if not (r_out is None) else r_ini - return mot_x_out, mot_y_out, mot_z_out, mot_r_out - - @staticmethod - def get_txm_cur_pos(): - x_ini = zps.sx.position - y_ini = zps.sy.position - z_ini = zps.sz.position - r_ini = zps.pi_r.position - return x_ini, y_ini, z_ini, r_ini - - @staticmethod - def init_mot_r(scn_cfg): - cur_pos = zps.pi_r.position - yield from abs_set(zps.pi_r.offset_freeze_switch, 1) - ang_max = max(scn_cfg["ang_s"], scn_cfg["ang_e"]) - - if ang_max > 0: - yield from abs_set(zps.pi_r.user_offset, np.ceil(ang_max / 360) * 360) - - if abs(scn_cfg["ang_s"] - cur_pos) > 360: - cur_pos = scn_cfg["ang_s"] // 360 * 360 + cur_pos % 360 - zps.pi_r.set_current_position(cur_pos) - yield from abs_set(zps.pi_r.acceleration, 1, wait=True) - yield from abs_set(zps.pi_r.velocity, 30, wait=True) - yield from abs_set(zps.pi_r, scn_cfg["ang_s"], wait=True) - - @staticmethod - def set_cam_step_for_scan(det, scn_cfg): - set_and_wait(det.cam.acquire_time, scn_cfg["exp_t"], rtol=0.01) - set_and_wait(det.hdf5.num_capture, scn_cfg["num_images"]) - set_and_wait(det.cam.num_images, scn_cfg["num_images"]) - - @staticmethod - def set_mot_r_step_for_scan(scn_cfg): - yield from abs_set(zps.pi_r.acceleration, scn_cfg["tacc"], wait=True) - yield from abs_set(zps.pi_r.velocity, scn_cfg["vel"], wait=True) - yield from abs_set(zps.pi_r.user_setpoint, scn_cfg["ang_s"] - scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], wait=True) - - @staticmethod - def set_cam_mode(cam, stage="pre-scan"): - if stage == "pre-scan": - yield from abs_set(cam.cam.image_mode, 0) - yield from abs_set(cam.cam.trigger_mode, 4) - elif stage == "ref-scan": - yield from abs_set(cam.cam.image_mode, 0) - yield from abs_set(cam.cam.trigger_mode, 0) - elif stage == "post-scan": - yield from abs_set(cam.cam.image_mode, 1) - yield from abs_set(cam.cam.trigger_mode, 0) - - -Zebra = FXIZebra( - "XF:18ID-ES:1{Dev:Zebra1}:", - name="Zebra", - read_attrs=["pc.data.enc1", "pc.data.time"], -) - -tomo_flyer = FXITomoFlyer( - list((Andor,)), - Zebra, - name="tomo_flyer", -) - - -def export_zebra_data(zebra, filepath): - j = 0 - while zebra.pc.data_in_progress.get() == 1: - print("Waiting for zebra...") - ttime.sleep(0.1) - j += 1 - if j > 10: - print("THE ZEBRA IS BEHAVING BADLY CARRYING ON") - break - - time_d = zebra.pc.data.time.get() - enc1_d = zebra.pc.data.enc1.get() - - size = (len(time_d),) - with h5py.File(filepath, "w") as f: - dset0 = f.create_dataset("zebra_time", size, dtype="f") - dset0[...] = np.array(time_d) - dset1 = f.create_dataset("enc1_pi_r", size, dtype="f") - dset1[...] = np.array(enc1_d) - - -class ZebraHDF5Handler(HandlerBase): - HANDLER_NAME = "ZEBRA_HDF51" - - def __init__(self, resource_fn): - self._handle = h5py.File(resource_fn, "r") - - def __call__(self, *, column): - return self._handle[column][:] - - def close(self): - self._handle.close() - self._handle = None - super().close() - - -db.reg.register_handler("ZEBRA_HDF51", ZebraHDF5Handler, overwrite=True) diff --git a/startup/41-scans.py b/startup/41-scans.py index b49f991..9055115 100644 --- a/startup/41-scans.py +++ b/startup/41-scans.py @@ -3346,6 +3346,22 @@ def tomo_mosaic_scan( if z_ini is None: z_ini = zps.sz.position + if x_step_size is None or x_step_size <1: + print('x_step_size should > 1') + return 0 + if y_step_size is None or y_step_size <1: + print('y_step_size should > 1') + return 0 + if z_step_size is None or z_step_size <1: + print('z_step_size should > 1') + return 0 + if x_num_steps is None: + x_num_steps = 1 + if y_num_steps is None: + y_num_steps = 1 + if z_num_steps is None: + z_num_steps = 1 + y_list = np.arange(y_ini, y_ini + y_step_size * y_num_steps - 1, y_step_size) x_list = np.arange(x_ini, x_ini + x_step_size * x_num_steps - 1, x_step_size) z_list = np.arange(z_ini, z_ini + z_step_size * z_num_steps - 1, z_step_size) diff --git a/startup/46-zebra_flyer.py b/startup/46-zebra_flyer.py index e3f4422..ffe4a64 100644 --- a/startup/46-zebra_flyer.py +++ b/startup/46-zebra_flyer.py @@ -63,7 +63,7 @@ def tomo_zfly( _type_: _description_ """ global ZONE_PLATE - FXITomoFlyer.stop_det(cam) + yield from FXITomoFlyer.stop_det(cam) yield from bps.sleep(2) sleep_plan = _schedule_sleep(sleep, num_swing) if not sleep_plan: @@ -135,7 +135,7 @@ def tomo_zfly( def inner_fly_plan(): yield from select_filters(flts) - if flyer.scn_mode == "snaked: single file": + if flyer.scn_mode == "snaked: single file": # scn_mode = 2 yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") yield from bps.sleep(1) yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) @@ -195,7 +195,7 @@ def inner_fly_plan(): return for mot in mots: mot.unstage() - elif flyer.scn_mode == "standard": + elif flyer.scn_mode == "standard": # scn_mode = 0 print(sleep_plan) for ii in range(scn_cfg["num_swing"]): yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") @@ -300,7 +300,7 @@ def inner_fly_plan(): print("\n") yield from bps.sleep(sleep_plan[ii]) yield from select_filters([]) - elif flyer.scn_mode == "snaked: multiple files": + elif flyer.scn_mode == "snaked: multiple files": # scn_mode = 1 yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") yield from bps.sleep(1) for mot in mots: @@ -420,6 +420,59 @@ def inner_fly_plan(): # return uid +def tomo_zfly_repeat( + scn_mode=0, + exp_t=0.05, + acq_p=0.05, + ang_s=0, + ang_e=180, + vel=3, + acc_t=1, + out_x=None, + out_y=None, + out_z=None, + out_r=None, + rel_out_flag=True, + flts=[], + rot_back_velo=30, + bin_fac=None, + note="", + md=None, + simu=False, + sleep=0, + repeat=1, + cam=Andor, + flyer=tomo_flyer, +): + for ii in range(repeat): + yield from tomo_zfly(scn_mode=0, + exp_t=exp_t, + acq_p=acq_p, + ang_s=ang_s, + ang_e=ang_e, + vel=vel, + acc_t=acc_t, + num_swing=1, + out_x=out_x, + out_y=out_y, + out_z=out_z, + out_r=out_r, + rel_out_flag=rel_out_flag, + flts=flts, + rot_back_velo=rot_back_velo, + bin_fac=bin_fac, + note=note, + md=md, + simu=simu, + sleep=0, + cam=cam, + flyer=flyer,) + if ii != repeat - 1: + print(f" Sleeping {sleep} seconds before {ii+2}th scan ... ".center(100, "#")) + print("\n") + yield from bps.sleep(sleep) + + def tomo_grid_zfly( scn_mode=0, exp_t=0.05, diff --git a/startup/46-zebra_flyer.py~ b/startup/46-zebra_flyer.py~ deleted file mode 100644 index e78b24b..0000000 --- a/startup/46-zebra_flyer.py~ +++ /dev/null @@ -1,210 +0,0 @@ -from bluesky.plan_stubs import kickoff, collect, complete, wait -from bluesky.utils import short_uid - - -def tomo_zfly( - scn_mode=0, - exp_t=0.05, - acq_p=0.05, - ang_s=0, - ang_e=180, - vel=3, - acc_t=1, - num_swing=1, - out_x=None, - out_y=None, - out_z=None, - out_r=None, - rel_out_flag=True, - flts=[], - rot_back_velo=30, - bin_fac=None, - note="", - md=None, - simu=False, - cam=Andor, - flyer=tomo_flyer, -): - """_summary_ - - Args: - scn_mode (int, optional): _description_. Defaults to 0. - exp_t (float, optional): _description_. Defaults to 0.05. - acq_p (float, optional): _description_. Defaults to 0.05. - ang_s (float or None, optional): _description_. Defaults to None. - ang_e (float, optional): _description_. Defaults to 180. - vel (int, optional): _description_. Defaults to 3. - acc_t (float, optional): _description_. Defaults to 1. - num_swing (int, optional): _description_. Defaults to 1. - out_x (float, optional): _description_. Defaults to None. - out_y (float, optional): _description_. Defaults to None. - out_z (float, optional): _description_. Defaults to None. - out_r (float, optional): _description_. Defaults to None. - flts (list, optional): _description_. Defaults to []. - rot_back_velo (int, optional): _description_. Defaults to 30. - binning (int, optional): _description_. Defaults to None. - note (str, optional): _description_. Defaults to "". - md (dict, optional): _description_. Defaults to None. - simu (bool, optional): _description_. Defaults to False. - cam (ophyd.Device, optional): detector; choose between Andor, Marana, and Oryx. - - Raises: - ValueError: _description_ - - Returns: - _type_: _description_ - - Yields: - _type_: _description_ - """ - global ZONE_PLATE - mots = [zps.sx, zps.sy, zps.sz, zps.pi_r] - flyer.detectors = [ - cam, - ] - scn_cfg = FXITomoFlyer.compose_scn_cfg( - scn_mode, exp_t, acq_p, bin_fac, ang_s, ang_e, vel, acc_t, num_swing - ) - yield from flyer.preset_flyer(scn_cfg) - (x_ini, y_ini, z_ini, r_ini) = FXITomoFlyer.get_txm_cur_pos() - (mot_x_out, mot_y_out, mot_z_out, mot_r_out) = FXITomoFlyer.def_abs_out_pos( - out_x, out_y, out_z, out_r, rel_out_flag - ) - - _md = { - "detectors": [flyer.detectors[0].name], - "motors": [mot.name for mot in mots], - "XEng": XEng.position, - "storage_ring_current (mA)": round(sr_current.get(), 1), - "plan_args": { - "exposure_time": scn_cfg["exp_t"], - "start_angle": scn_cfg["ang_s"], - "end_angle": scn_cfg["ang_e"], - "acquisition_period": scn_cfg["acq_p"], - "number_of_swings": scn_cfg["num_swing"], - "out_x": mot_x_out, - "out_y": mot_y_out, - "out_z": mot_z_out, - "out_r": mot_r_out, - "slew_speed": scn_cfg["vel"], - "rel_out_flag": rel_out_flag, - "filters": ["filter{}".format(t) for t in flts] if flts else "None", - "binning": 0 if scn_cfg["bin_fac"] is None else scn_cfg["bin_fac"], - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - }, - "plan_name": "tomo_zfly", - "num_bkg_images": 10, - "num_dark_images": 10, - "plan_pattern": "linspace", - "plan_pattern_module": "numpy", - "hints": {}, - "operator": "FXI", - "note": note if note else "None", - "zone_plate": ZONE_PLATE, - } - _md.update(md or {}) - - print('preset done') - @stage_decorator(list(mots)) - @run_decorator(md=_md) - def inner_fly_plan(): - yield from select_filters(flts) - for ii in range(scn_cfg["num_swing"]): - FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) - FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) - yield from _open_shutter_xhx(simu) - for d in flyer.detectors: - try: - d.stage() - except: - d.unstage() - d.stage() - st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) - st.wait(timeout=10) - yield from abs_set(flyer.encoder.pc.gate_start, scn_cfg["ang_s"], wait=True) - - det_stream = short_uid('dets') - for d in flyer.detectors: - yield from bps.trigger(d, group=det_stream) - wait(det_stream) - - yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) - t0 = ttime.monotonic() - yield from abs_set( - zps.pi_r, - scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], - wait=True - ) - - t1 = ttime.monotonic() - while int(flyer.encoder.pc.gated.get()): - if ttime.monotonic() - t1 > 60: - print("Scan finished abnormally. Quit!") - return - yield from bps.sleep(flyer._staging_delay) - print(ttime.time()) - print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") - st = yield from complete(flyer, wait=True) - st.wait(timeout=10) - yield from collect(flyer) - for d in flyer.detectors: - try: - d.unstage() - except: - print(f"Cannot unstage detector {d.name}") - return None - if (scn_cfg["num_swing"] > 1) and ( - flyer.scn_modes[scn_cfg["scn_mode"]] != "snaked: single file" - ): - (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( - scn_cfg["ang_e"], - scn_cfg["ang_s"], - ) - scn_cfg["rot_dir"] *= -1 - pc_cfg[scn_cfg["scn_mode"]]["dir"] = flyer.pc_trig_dir[int(scn_cfg["rot_dir"])] - yield from flyer.set_pc_step_for_scan(scn_cfg, pc_cfg) - else: - yield from FXITomoFlyer.init_mot_r(scn_cfg) - - yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") - yield from _take_ref_image( - [cam], - mots_pos={ - "x": mot_x_out, - "y": mot_y_out, - "z": mot_z_out, - "r": mot_r_out, - }, - num=1, - chunk_size=10, - stream_name="flat", - simu=simu, - ) - yield from _take_ref_image( - [cam], - mots_pos={}, - num=1, - chunk_size=10, - stream_name="dark", - simu=simu, - ) - for d in flyer.detectors: - try: - d.unstage() - except: - print(f"Cannot unstage detector {d.name}") - return None - yield from _move_sample( - x_ini, - y_ini, - z_ini, - r_ini, - repeat=2, - ) - yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") - yield from select_filters([]) - - uid = yield from inner_fly_plan() - print("scan finished") - return uid diff --git a/startup/80-load_scan.py b/startup/80-load_scan.py index a08f91b..74deff8 100644 --- a/startup/80-load_scan.py +++ b/startup/80-load_scan.py @@ -77,16 +77,18 @@ def export_scan(scan_id, scan_id_end=None, binning=4, date_end_by=None, fpath=No try: custom_export(int(item), binning, date_end_by=date_end_by, fpath=fpath) db.reg.clear_process_cache() - except: + except Exception as err: print(f'fail to export {item}') + print(err) else: for i in range(scan_id, scan_id_end + 1): try: # export_single_scan(int(i), binning) custom_export(int(i), binning, date_end_by=date_end_by, fpath=fpath) db.reg.clear_process_cache() - except: + except Exception as err: print(f'fail to export {i}') + print(err) def custom_export(scan_id, binning=4, date_end_by=None, fpath=None): """ @@ -832,8 +834,9 @@ def export_test_scan(h, fpath=None): try: write_lakeshore_to_file(h, fname) - except: + except Exception as err: print("fails to write lakeshore info into {fname}") + print(str) del ( img_dark, @@ -1253,9 +1256,13 @@ def export_raster_2D(h, binning=4, fpath=None): ) index = index + 1 s = img_patch.shape - img_patch_bin = bin_ndarray( - img_patch, new_shape=(1, int(s[1] / binning), int(s[2] / binning)) - ) + try: + img_patch_bin = bin_ndarray( + img_patch, new_shape=(1, int(s[1] / binning), int(s[2] / binning)) + ) + except: + img_patch_bin = img_patch + binning = 1 fout_h5 = fpath + f"raster2D_scan_{scan_id}_binning_{binning}.h5" fout_tiff = fpath + f"raster2D_scan_{scan_id}_binning_{binning}.tiff" fout_txt = fpath + f"raster2D_scan_{scan_id}_cord.txt" diff --git a/startup/91-functions.py b/startup/91-functions.py index a5b7e55..26b7e31 100644 --- a/startup/91-functions.py +++ b/startup/91-functions.py @@ -75,17 +75,69 @@ def cal_global_mag(x1, y1, x2, y2, nominal_dist=10000): ###################### new record caliber position function ################3 +def update_th2(): + calib = trans_calib() + cur_eng = XEng.position + cur_th2 = dcm.th2.position + + calib_eng = np.array(list(calib.keys())) + above = sorted(list(calib_eng[calib_eng > cur_eng]), reverse=True) + below = sorted(list(calib_eng[calib_eng <= cur_eng]), reverse=True) + if len(above) == 0: + eng1 = below[0] + eng2 = below[1] + elif len(below) == 0: + eng1 = above[-1] + eng2 = above[-2] + else: + eng1 = above[-1] + eng2 = below[0] + old_th2 = (cur_eng - eng2) * (calib[eng1]["th2_motor"] - calib[eng2]["th2_motor"]) / ( + eng1 - eng2 + ) + calib[eng2]["th2_motor"] + delta_th2 = cur_th2 - old_th2 + for key in calib.keys(): + calib[key]["th2_motor"] += delta_th2 + print(f'For all energy calibrition point, offset DCM_th2 by {delta_th2}') + return calib + + +def update_CALIBER_th2(): + global CALIBER + _calib = trans_calib() + _calib_th2 = update_th2() + for key in _calib.keys(): + CALIBER[f"th2_motor_{_calib[key]['pos']}"] = _calib_th2[key]["th2_motor"] + + +def trans_calib(): + global CALIBER + new_key1 = [] + new_key2 = [] + + for key in CALIBER.keys(): + if "XEng" in key: + new_key1.append(key.split("_")[-1] ) + new_key2.append(CALIBER[key]) + + calib_dict = {} + for key1, key2 in zip(new_key1, new_key2): + calib_dict[key2] = {} + for k in CALIBER.keys(): + if key1 in k: + calib_dict[key2][k.strip("_"+ key1)] = CALIBER[k] + calib_dict[key2]["pos"] = key1 + return calib_dict + def record_calib_pos_new(n): global GLOBAL_MAG, CALIBER - # CALIBER[f'chi2_pos{n}'] = pzt_dcm_chi2.pos.value CALIBER[f"chi2_pos{n}"] = dcm.chi2.position CALIBER[f"XEng_pos{n}"] = XEng.position CALIBER[f"zp_x_pos{n}"] = zp.x.position CALIBER[f"zp_y_pos{n}"] = zp.y.position - #CALIBER[f"th2_motor_pos{n}"] = th2_motor.position CALIBER[f"th2_motor_pos{n}"] = dcm.th2.position CALIBER[f"clens_x_pos{n}"] = clens.x.position CALIBER[f"clens_y1_pos{n}"] = clens.y1.position @@ -110,9 +162,7 @@ def record_calib_pos_new(n): df = pd.DataFrame.from_dict(CALIBER, orient="index") df.to_csv("/nsls2/data/fxi-new/legacy/log/calib_new.csv") # df.to_csv("/home/xf18id/.ipython/profile_collection/startup/calib_new.csv", sep="\t") - print( - f'calib_pos{n} recored: current Magnification = GLOBAL_MAG = {CALIBER[f"mag{n}"]}' - ) + print(f'calib_pos{n} recored: current Magnification = GLOBAL_MAG = {CALIBER[f"mag{n}"]}') def remove_caliber_pos(n): @@ -122,7 +172,8 @@ def remove_caliber_pos(n): CALIBER_backup = CALIBER.copy() try: for k in CALIBER_backup.keys(): - if k[-1] == str(n): + if str(n) in k: + #if k[-1] == str(n): del CALIBER[k] df = pd.DataFrame.from_dict(CALIBER, orient="index") # df.to_csv("/home/xf18id/.ipython/profile_collection/startup/calib_new.csv", sep="\t") @@ -183,7 +234,7 @@ def read_calib_file_new(return_flag=0): return CALIBER -def move_zp_ccd(eng_new, move_flag=1, info_flag=1, move_clens_flag=0, move_det_flag=0): +def move_zp_ccd(eng_new, move_flag=1, info_flag=1, move_clens_flag=0, move_det_flag=0, mag=None): """ move the zone_plate and ccd to the user-defined energy with constant magnification use the function as: @@ -210,9 +261,10 @@ def find_nearest(data, value): eng_ini = XEng.position check_eng_range([eng_ini]) zp_ini, det_ini, zp_delta, det_delta, zp_final, det_final = cal_zp_ccd_position( - eng_new, eng_ini, print_flag=0 + eng_new, eng_ini, print_flag=0, mag=mag ) + assert (det_final) > det.z.low_limit.value and (det_final) < det.z.high_limit.value, print( "Trying to move DetU to {0:2.2f}. Movement is out of travel range ({1:2.2f}, {2:2.2f})\nTry to move the bottom stage manually.".format( det_final, det.z.low_limit.value, det.z.high_limit.value @@ -294,12 +346,15 @@ def find_nearest(data, value): chi2_motor_target = (eng_new - eng2) * (dcm_chi2_eng1 - dcm_chi2_eng2) / ( eng1 - eng2 ) + dcm_chi2_eng2 + zp_x_target = (eng_new - eng2) * (zp_x_pos_eng1 - zp_x_pos_eng2) / ( eng1 - eng2 ) + zp_x_pos_eng2 zp_y_target = (eng_new - eng2) * (zp_y_pos_eng1 - zp_y_pos_eng2) / ( eng1 - eng2 ) + zp_y_pos_eng2 + + th2_motor_target = (eng_new - eng2) * (th2_motor_eng1 - th2_motor_eng2) / ( eng1 - eng2 ) + th2_motor_eng2 @@ -368,11 +423,11 @@ def find_nearest(data, value): ) ) # print ('move pzt_dcm_th2: ({0:2.4f} um --> {1:2.4f} um)'.format(pzt_dcm_th2_ini, pzt_dcm_th2_target)) - print( - "move dcm_chi2: ({0:2.4f} um --> {1:2.4f} um)".format( - dcm_chi2_ini, chi2_motor_target - ) - ) + #print( + # "move dcm_chi2: ({0:2.4f} um --> {1:2.4f} um)".format( + # dcm_chi2_ini, chi2_motor_target + # ) + #) print( "move th2_motor: ({0:2.6f} deg --> {1:2.6f} deg)".format( th2_motor_ini, th2_motor_target @@ -421,19 +476,18 @@ def find_nearest(data, value): ) ) - yield from mv(zp.x, zp_x_target, zp.y, zp_y_target) yield from mv(dcm_th2.feedback_enable, 0) yield from mv(dcm_th2.feedback, th2_motor_target) yield from mv(dcm_th2.feedback_enable, 1) - yield from mv(dcm_chi2.feedback_enable, 0) - yield from mv(dcm_chi2.feedback, chi2_motor_target) - yield from mv(dcm_chi2.feedback_enable, 1) + #yield from mv(dcm_chi2.feedback_enable, 0) + #yield from mv(dcm_chi2.feedback, chi2_motor_target) + #yield from mv(dcm_chi2.feedback_enable, 1) yield from mv(zp.z, zp_final, det.z, det_final, XEng, eng_new) + yield from mv(zp.x, zp_x_target, zp.y, zp_y_target) yield from mv(aper.x, aper_x_target, aper.y, aper_y_target) - #yield from mv(DetU.x, DetU_x_ini + (det_final-det_ini)/400*0.15) if move_clens_flag: yield from mv( clens.x, @@ -447,8 +501,6 @@ def find_nearest(data, value): if move_det_flag: yield from mv(DetU.x, DetU_x_target) yield from mv(DetU.y, DetU_y_target) - # yield from mv(pzt_dcm_th2.setpos, pzt_dcm_th2_target, pzt_dcm_chi2.setpos, pzt_dcm_chi2_target) - # yield from mv(pzt_dcm_chi2.setpos, pzt_dcm_chi2_target) yield from bps.sleep(0.5) if abs(eng_new - eng_ini) >= 0.005: @@ -493,12 +545,11 @@ def find_nearest(data, value): aper_y_ini, aper_y_target ) ) - # print ('will move pzt_dcm_th2: ({0:2.4f} um --> {1:2.4f} um)'.format(pzt_dcm_th2_ini, pzt_dcm_th2_target)) - print( - "will move dcm_chi2: ({0:2.4f} um --> {1:2.4f} um)".format( - dcm_chi2_ini, chi2_motor_target - ) - ) + #print( + # "will move dcm_chi2: ({0:2.4f} um --> {1:2.4f} um)".format( + # dcm_chi2_ini, chi2_motor_target + # ) + #) print( "will move th2_motor: ({0:2.6f} deg --> {1:2.6f} deg)".format( th2_motor_ini, th2_motor_target @@ -539,6 +590,212 @@ def find_nearest(data, value): return 1 +############################### +def move_zp_ccd_TEST(eng_new, move_flag=1, info_flag=1, move_clens_flag=0, move_det_flag=0, mag=None): + """ + move the zone_plate and ccd to the user-defined energy with constant magnification + use the function as: + move_zp_ccd_with_const_mag(eng_new=8.0, move_flag=1) + Note: + in the above commend, it will use two energy calibration points to calculate the motor + position of XEng=8.0 keV. + specfically, one of the calibration points is > 8keV, the other one is < 8keV + + Inputs: + ------- + eng_new: float + User defined energy, in unit of keV + flag: int + 0: Do calculation without moving real stages + 1: Will move stages + """ + def find_nearest(data, value): + data = np.array(data) + return np.abs(data - value).argmin() + + eng_new = float(eng_new) # eV, e.g. 9.0 + det = DetU # upstream detector + eng_ini = XEng.position + check_eng_range([eng_ini]) + zp_ini, det_ini, zp_delta, det_delta, zp_final, det_final = cal_zp_ccd_position( + eng_new, eng_ini, print_flag=0, mag=mag + ) + + + assert (det_final) > det.z.low_limit.value and (det_final) < det.z.high_limit.value, print( + "Trying to move DetU to {0:2.2f}. Movement is out of travel range ({1:2.2f}, {2:2.2f})\nTry to move the bottom stage manually.".format( + det_final, det.z.low_limit.value, det.z.high_limit.value + ) + ) + + ENG_val = [] + ENG_idx = [] + for k in CALIBER.keys(): + if "XEng" in k: + ENG_val.append(CALIBER[k]) + ENG_idx.append(int(k[-1])) + t = find_nearest(eng_new, ENG_val) + eng1 = ENG_val.pop(t) + id1 = ENG_idx.pop(t) + + ENG_val_copy = np.array(ENG_val.copy()) + ENG_idx_copy = np.array(ENG_idx.copy()) + if eng_new >= eng1: + idx = ENG_val_copy > eng_new + else: + idx = ENG_val_copy < eng_new + + ENG_val_copy = ENG_val_copy[idx] + ENG_idx_copy = ENG_idx_copy[idx] + if len(ENG_val_copy): + t = find_nearest(eng_new, ENG_val_copy) + eng2 = ENG_val_copy[t] + id2 = ENG_idx_copy[t] + else: + t = find_nearest(eng_new, ENG_val) + eng2 = ENG_val[t] + id2 = ENG_idx[t] + + mag1 = CALIBER[f"mag{id1}"] + mag2 = CALIBER[f"mag{id2}"] + + if mag is None: + mag = mag1 / 10.0 # e.g. mag = 32.5 + + _, _, _, _, zp_z1, _ = cal_zp_ccd_position(eng1, print_flag=0, mag=mag) + _, _, _, _, zp_z2, _ = cal_zp_ccd_position(eng2, print_flag=0, mag=mag) + + if not (np.abs(mag1 / mag2 - 1) < 1e-3): + print("mismatch in magfinication:") + print(f"magnificatoin at {eng1:2.5f} keV = {mag1}") + print(f"magnificatoin at {eng2:2.5f} keV = {mag2}") + print("stage will not move") + return 0 + else: + print(f"using reference at {eng1:2.5f} keV and {eng2:2.5f} kev to interpolate\n") + dcm_chi2_eng1 = CALIBER[f"chi2_pos{id1}"] + zp_x_pos_eng1 = CALIBER[f"zp_x_pos{id1}"] + zp_y_pos_eng1 = CALIBER[f"zp_y_pos{id1}"] + th2_motor_eng1 = CALIBER[f"th2_motor_pos{id1}"] + clens_x_eng1 = CALIBER[f"clens_x_pos{id1}"] + clens_y1_eng1 = CALIBER[f"clens_y1_pos{id1}"] + clens_y2_eng1 = CALIBER[f"clens_y2_pos{id1}"] + clens_p_eng1 = CALIBER[f"clens_p_pos{id1}"] + DetU_x_eng1 = CALIBER[f"DetU_x_pos{id1}"] + DetU_y_eng1 = CALIBER[f"DetU_y_pos{id1}"] + aper_x_eng1 = CALIBER[f"aper_x_pos{id1}"] + aper_y_eng1 = CALIBER[f"aper_y_pos{id1}"] + + dcm_chi2_eng2 = CALIBER[f"chi2_pos{id2}"] + zp_x_pos_eng2 = CALIBER[f"zp_x_pos{id2}"] + zp_y_pos_eng2 = CALIBER[f"zp_y_pos{id2}"] + th2_motor_eng2 = CALIBER[f"th2_motor_pos{id2}"] + clens_x_eng2 = CALIBER[f"clens_x_pos{id2}"] + clens_y1_eng2 = CALIBER[f"clens_y1_pos{id2}"] + clens_y2_eng2 = CALIBER[f"clens_y2_pos{id2}"] + clens_p_eng2 = CALIBER[f"clens_p_pos{id2}"] + DetU_x_eng2 = CALIBER[f"DetU_x_pos{id2}"] + DetU_y_eng2 = CALIBER[f"DetU_y_pos{id2}"] + aper_x_eng2 = CALIBER[f"aper_x_pos{id2}"] + aper_y_eng2 = CALIBER[f"aper_y_pos{id2}"] + + if np.abs(eng1 - eng2) < 1e-5: # difference less than 0.01 eV + print( + f'eng1({eng1:2.5f} keV) and eng2({eng2:2.5f} keV) in "CALIBER" are two close, will not move any motors...' + ) + else: + slope_eng = (eng_new - eng2) / (eng1 - eng2) + slope_zp_z = (zp_final - zp_z2) / (zp_z1 - zp_z2) + + zp_x_target = slope_zp_z * (zp_x_pos_eng1 - zp_x_pos_eng2) + zp_x_pos_eng2 + zp_y_target = slope_zp_z * (zp_y_pos_eng1 - zp_y_pos_eng2) + zp_y_pos_eng2 + aper_x_target = slope_zp_z * (aper_x_eng1 - aper_x_eng2) + aper_x_eng2 + aper_y_target = slope_zp_z * (aper_y_eng1 - aper_y_eng2) + aper_y_eng2 + + chi2_motor_target = slope_eng * (dcm_chi2_eng1 - dcm_chi2_eng2) + dcm_chi2_eng2 + th2_motor_target = slope_eng * (th2_motor_eng1 - th2_motor_eng2) + th2_motor_eng2 + clens_x_target = slope_eng * (clens_x_eng1 - clens_x_eng2) + clens_x_eng2 + clens_y1_target = slope_eng * (clens_y1_eng1 - clens_y1_eng2) + clens_y1_eng2 + clens_y2_target = slope_eng * (clens_y2_eng1 - clens_y2_eng2) + clens_y2_eng2 + clens_p_target = slope_eng * (clens_p_eng1 - clens_p_eng2) + clens_p_eng2 + DetU_x_target = slope_eng * (DetU_x_eng1 - DetU_x_eng2) + DetU_x_eng2 + DetU_y_target = slope_eng * (DetU_y_eng1 - DetU_y_eng2) + DetU_y_eng2 + + + dcm_chi2_ini = dcm.chi2.position + zp_x_ini = zp.x.position + zp_y_ini = zp.y.position + th2_motor_ini = dcm.th2.position + clens_x_ini = clens.x.position + clens_y1_ini = clens.y1.position + clens_y2_ini = clens.y2.position + clens_p_ini = clens.p.position + DetU_x_ini = DetU.x.position + DetU_y_ini = DetU.y.position + aper_x_ini = aper.x.position + aper_y_ini = aper.y.position + + if info_flag or (not move_flag): + if (not move_flag): + print("This is calculation. No stages move\n") + + print(f"At Magnification = {mag * 10}\n") + print(f"Energy: {eng_ini:5.2f} keV --> {eng_new:5.2f} keV") + print(f"zone plate position: {zp_ini:2.4f} mm --> {zp_final:2.4f} mm") + print(f"CCD position: {det_ini:2.4f} mm --> {det_final:2.4f} mm") + print(f"move zp_x: ({zp_x_ini:2.4f} um --> {zp_x_target:2.4f} um)") + print(f"move zp_y: ({zp_y_ini:2.4f} um --> {zp_y_target:2.4f} um)") + print(f"move dcm_chi2: ({dcm_chi2_ini:2.4f} um --> {chi2_motor_target:2.4f} um)") + print(f"move th2_motor: ({th2_motor_ini:2.6f} deg --> {th2_motor_target:2.6f} deg)") + print(f"move aper_x_motor: ({aper_x_ini:2.4f} um --> {aper_x_target:2.4f} um)") + print(f"move aper_y_motor: ({aper_y_ini:2.4f} um --> {aper_y_target:2.4f} um)") + if move_clens_flag: + print(f"move clens_x: ({clens_x_ini:2.4f} um --> {clens_x_target:2.4f} um)") + print(f"move clens_y1: ({clens_y1_ini:2.4f} um --> {clens_y1_target:2.4f} um)") + print(f"move clens_y2: ({clens_y1_ini:2.4f} um --> {clens_y2_target:2.4f} um)") + print(f"move clens_p: ({clens_p_ini:2.4f} um --> {clens_p_target:2.4f} um)") + if move_det_flag: + print(f"move DetU_x: ({DetU_x_ini:2.4f} um --> {DetU_x_target:2.4f} um)") + print(f"move DetU_y: ({DetU_y_ini:2.4f} um --> {DetU_y_target:2.4f} um)") + + if move_flag: # move stages + print(f"Now moving stages at Magnification = {mag * 10}....") + + yield from mv(zp.x, zp_x_target, zp.y, zp_y_target) + yield from mv(dcm_th2.feedback_enable, 0) + yield from mv(dcm_th2.feedback, th2_motor_target) + yield from mv(dcm_th2.feedback_enable, 1) + + yield from mv(dcm_chi2.feedback_enable, 0) + yield from mv(dcm_chi2.feedback, chi2_motor_target) + yield from mv(dcm_chi2.feedback_enable, 1) + + yield from mv(zp.z, zp_final, det.z, det_final, XEng, eng_new) + yield from mv(aper.x, aper_x_target, aper.y, aper_y_target) + + #yield from mv(DetU.x, DetU_x_ini + (det_final-det_ini)/400*0.15) + if move_clens_flag: + yield from mv( + clens.x, + clens_x_target, + clens.y1, + clens_y1_target, + clens.y2, + clens_y2_target, + ) + yield from mv(clens.p, clens_p_target) + if move_det_flag: + yield from mv(DetU.x, DetU_x_target) + yield from mv(DetU.y, DetU_y_target) + yield from bps.sleep(0.5) + if abs(eng_new - eng_ini) >= 0.005: + t = 10 * abs(eng_new - eng_ini) + t = min(t, 2) + print(f"sleep for {t} sec") + yield from bps.sleep(t) + + return 1 + ################################ @@ -564,13 +821,16 @@ def show_global_para(): ################################################################ -def new_user(*, new_pi_name=None, new_proposal_id=None): +def new_user(new_pi_name=None, new_proposal_id=None): """ The function creates directory structure for a new user. If ``new_pi_name`` and/or ``new_proposal_id`` are ``None``, the function asks the user to type PI name and/or Proposal ID. """ - now = datetime.datetime.now() + try: + now = datetime.datetime.now() + except: + now = datetime.now() year = np.str(now.year) # this is really cycle not quarter @@ -717,7 +977,7 @@ def cal_parameter(eng, print_flag=1): return wave_length, focal_length, na, dof -def cal_zp_ccd_position(eng_new, eng_ini=0, print_flag=1): +def cal_zp_ccd_position(eng_new, eng_ini=0, print_flag=1, mag=None): """ calculate the delta amount of movement for zone_plate and CCD whit change energy from ene_ini to eng_new while keeping same magnification @@ -761,11 +1021,15 @@ def cal_zp_ccd_position(eng_new, eng_ini=0, print_flag=1): check_eng_range([eng_new, eng_ini]) h = 6.6261e-34 - c = 3e8 - ec = 1.602e-19 + c = 299792458 + ec = 1.6021766e-19 det = DetU # read current energy and motor position - mag = GLOBAL_MAG / GLOBAL_VLM_MAG + try: + if mag is None or mag > 45 or mag < 20: + mag = GLOBAL_MAG / GLOBAL_VLM_MAG + except: + mag = GLOBAL_MAG / GLOBAL_VLM_MAG zp_ini = zp.z.position # zone plate position in unit of mm zps_ini = zps.sz.position # sample position in unit of mm @@ -803,161 +1067,6 @@ def cal_zp_ccd_position(eng_new, eng_ini=0, print_flag=1): return zp_ini, det_ini, zp_delta, det_delta, zp_final, det_final -''' -def move_zp_ccd(eng_new, move_flag=1, info_flag=1, move_clens_flag=0, move_det_flag=0): - """ - move the zone_plate and ccd to the user-defined energy with constant magnification - use the function as: - move_zp_ccd_with_const_mag(eng_new=8.0, move_flag=1): - - Inputs: - ------- - eng_new: float - User defined energy, in unit of keV - flag: int - 0: Do calculation without moving real stages - 1: Will move stages - """ - global CALIBER_FLAG - if CALIBER_FLAG: - eng_new = float(eng_new) # eV, e.g. 9.0 - det = DetU # upstream detector - eng_ini = XEng.position - check_eng_range([eng_ini]) - zp_ini, det_ini, zp_delta, det_delta, zp_final, det_final = cal_zp_ccd_position(eng_new, eng_ini, print_flag=0) - - assert ((det_final) > det.z.low_limit and (det_final) < det.z.high_limit), print ('Trying to move DetU to {0:2.2f}. Movement is out of travel range ({1:2.2f}, {2:2.2f})\nTry to move the bottom stage manually.'.format(det_final, det.z.low_limit, det.z.high_limit)) - - eng1 = CALIBER['XEng_pos1'] - eng2 = CALIBER['XEng_pos2'] - - #pzt_dcm_th2_eng1 = CALIBER['th2_pos1'] - dcm_chi2_eng1 = CALIBER['chi2_pos1'] - zp_x_pos_eng1 = CALIBER['zp_x_pos1'] - zp_y_pos_eng1 = CALIBER['zp_y_pos1'] - th2_motor_eng1 = CALIBER['th2_motor_pos1'] - clens_x_eng1 = CALIBER['clens_x_pos1'] - clens_y1_eng1 = CALIBER['clens_y1_pos1'] - clens_y2_eng1 = CALIBER['clens_y2_pos1'] - clens_p_eng1 = CALIBER['clens_p_pos1'] - DetU_x_eng1 = CALIBER['DetU_x_pos1'] - DetU_y_eng1 = CALIBER['DetU_y_pos1'] - aper_x_eng1 = CALIBER['aper_x_pos1'] - aper_y_eng1 = CALIBER['aper_y_pos1'] - - #pzt_dcm_th2_eng2 = CALIBER['th2_pos2'] - pzt_dcm_chi2_eng2 = CALIBER['chi2_pos2'] - zp_x_pos_eng2 = CALIBER['zp_x_pos2'] - zp_y_pos_eng2 = CALIBER['zp_y_pos2'] - th2_motor_eng2 = CALIBER['th2_motor_pos2'] - clens_x_eng2 = CALIBER['clens_x_pos2'] - clens_y1_eng2 = CALIBER['clens_y1_pos2'] - clens_y2_eng2 = CALIBER['clens_y2_pos2'] - clens_p_eng2 = CALIBER['clens_p_pos2'] - DetU_x_eng2 = CALIBER['DetU_x_pos2'] - DetU_y_eng2 = CALIBER['DetU_y_pos2'] - aper_x_eng2 = CALIBER['aper_x_pos2'] - aper_y_eng2 = CALIBER['aper_y_pos2'] - - if np.abs(eng1 - eng2) < 1e-5: # difference less than 0.01 eV - print(f'eng1({eng1:2.5f} eV) and eng2({eng2:2.5f} eV) in "CALIBER" are two close, will not move any motors...') - else: - #pzt_dcm_th2_target = (eng_new - eng2) * (pzt_dcm_th2_eng1 - pzt_dcm_th2_eng2) / (eng1-eng2) + pzt_dcm_th2_eng2 - pzt_dcm_chi2_target = (eng_new - eng2) * (dcm_chi2_eng1 - dcm_chi2_eng2) / (eng1-eng2) + pzt_dcm_chi2_eng2 - zp_x_target = (eng_new - eng2)*(zp_x_pos_eng1 - zp_x_pos_eng2)/(eng1 - eng2) + zp_x_pos_eng2 - zp_y_target = (eng_new - eng2)*(zp_y_pos_eng1 - zp_y_pos_eng2)/(eng1 - eng2) + zp_y_pos_eng2 - th2_motor_target = (eng_new - eng2) * (th2_motor_eng1 -th2_motor_eng2) / (eng1-eng2) + th2_motor_eng2 - clens_x_target = (eng_new - eng2)*(clens_x_eng1 - clens_x_eng2)/(eng1 - eng2) + clens_x_eng2 - clens_y1_target = (eng_new - eng2)*(clens_y1_eng1 - clens_y1_eng2)/(eng1 - eng2) + clens_y1_eng2 - clens_y2_target = (eng_new - eng2)*(clens_y2_eng1 - clens_y2_eng2)/(eng1 - eng2) + clens_y2_eng2 - clens_p_target = (eng_new - eng2)*(clens_p_eng1 - clens_p_eng2)/(eng1 - eng2) + clens_p_eng2 - DetU_x_target = (eng_new - eng2)*(DetU_x_eng1 - DetU_x_eng2)/(eng1 - eng2) + DetU_x_eng2 - DetU_y_target = (eng_new - eng2)*(DetU_y_eng1 - DetU_y_eng2)/(eng1 - eng2) + DetU_y_eng2 - aper_x_target = (eng_new - eng2)*(aper_x_eng1 - aper_x_eng2)/(eng1 - eng2) + aper_x_eng2 - aper_y_target = (eng_new - eng2)*(aper_y_eng1 - aper_y_eng2)/(eng1 - eng2) + aper_y_eng2 - #pzt_dcm_th2_ini = (yield from bps.rd(pzt_dcm_th2.pos)) - pzt_dcm_chi2_ini = (yield from bps.rd(pzt_dcm_chi2.pos.value)) - zp_x_ini = zp.x.position - zp_y_ini = zp.y.position - th2_motor_ini = th2_motor.position - clens_x_ini = clens.x.position - clens_y1_ini = clens.y1.position - clens_y2_ini = clens.y2.position - clens_p_ini = clens.p.position - DetU_x_ini = DetU.x.position - DetU_y_ini = DetU.y.position - aper_x_ini = aper.x.position - aper_y_ini = aper.y.position - - if move_flag: # move stages - print ('Now moving stages ....') - if info_flag: - print ('Energy: {0:5.2f} keV --> {1:5.2f} keV'.format(eng_ini, eng_new)) - print ('zone plate position: {0:2.4f} mm --> {1:2.4f} mm'.format(zp_ini, zp_final)) - print ('CCD position: {0:2.4f} mm --> {1:2.4f} mm'.format(det_ini, det_final)) - print ('move zp_x: ({0:2.4f} um --> {1:2.4f} um)'.format(zp_x_ini, zp_x_target)) - print ('move zp_y: ({0:2.4f} um --> {1:2.4f} um)'.format(zp_y_ini, zp_y_target)) - #print ('move pzt_dcm_th2: ({0:2.4f} um --> {1:2.4f} um)'.format(pzt_dcm_th2_ini, pzt_dcm_th2_target)) - print ('move pzt_dcm_chi2: ({0:2.4f} um --> {1:2.4f} um)'.format(pzt_dcm_chi2_ini, pzt_dcm_chi2_target)) - print ('move th2_motor: ({0:2.6f} deg --> {1:2.6f} deg)'.format(th2_motor_ini, th2_motor_target)) - print ('move aper_x_motor: ({0:2.4f} um --> {1:2.4f} um)'.format(aper_x_ini, aper_x_target)) - print ('move aper_y_motor: ({0:2.4f} um --> {1:2.4f} um)'.format(aper_y_ini, aper_y_target)) - if move_clens_flag: - print ('move clens_x: ({0:2.4f} um --> {1:2.4f} um)'.format(clens_x_ini, clens_x_target)) - print ('move clens_y1: ({0:2.4f} um --> {1:2.4f} um)'.format(clens_y1_ini, clens_y1_target)) - print ('move clens_y2: ({0:2.4f} um --> {1:2.4f} um)'.format(clens_y1_ini, clens_y2_target)) - print ('move clens_p: ({0:2.4f} um --> {1:2.4f} um)'.format(clens_p_ini, clens_p_target)) - if move_det_flag: - print ('move DetU_x: ({0:2.4f} um --> {1:2.4f} um)'.format(DetU_x_ini, DetU_x_target)) - print ('move DetU_y: ({0:2.4f} um --> {1:2.4f} um)'.format(DetU_y_ini, DetU_y_target)) - - yield from mv(zp.x, zp_x_target, zp.y, zp_y_target) -# yield from mv(aper.x, aper_x_target, aper.y, aper_y_target) - yield from mv(dcm_th2.feedback_enable, 0) - yield from mv(dcm_th2.feedback, th2_motor_target) - yield from mv(dcm_th2.feedback_enable, 1) - yield from mv(zp.z, zp_final,det.z, det_final, XEng, eng_new) - yield from mv(aper.x, aper_x_target, aper.y, aper_y_target) - if move_clens_flag: - yield from mv(clens.x, clens_x_target, clens.y1, clens_y1_target, clens.y2, clens_y2_target) - yield from mv(clens.p, clens_p_target) - if move_det_flag: - yield from mv(DetU.x, DetU_x_target) - yield from mv(DetU.y, DetU_y_target) - #yield from mv(pzt_dcm_th2.setpos, pzt_dcm_th2_target, pzt_dcm_chi2.setpos, pzt_dcm_chi2_target) - #yield from mv(pzt_dcm_chi2.setpos, pzt_dcm_chi2_target) - - yield from bps.sleep(0.1) - if abs(eng_new - eng_ini) >= 0.005: - t = 10 * abs(eng_new - eng_ini) - t = min(t, 2) - print(f'sleep for {t} sec') - yield from bps.sleep(t) - else: - print ('This is calculation. No stages move') - print ('Will move Energy: {0:5.2f} keV --> {1:5.2f} keV'.format(eng_ini, eng_new)) - print ('will move zone plate down stream by: {0:2.4f} mm ({1:2.4f} mm --> {2:2.4f} mm)'.format(zp_delta, zp_ini, zp_final)) - print ('will move CCD down stream by: {0:2.4f} mm ({1:2.4f} mm --> {2:2.4f} mm)'.format(det_delta, det_ini, det_final)) - print ('will move zp_x: ({0:2.4f} um --> {1:2.4f} um)'.format(zp_x_ini, zp_x_target)) - print ('will move zp_y: ({0:2.4f} um --> {1:2.4f} um)'.format(zp_y_ini, zp_y_target)) - print ('will move aper_x: ({0:2.4f} um --> {1:2.4f} um)'.format(aper_x_ini, aper_x_target)) - print ('will move aper_y: ({0:2.4f} um --> {1:2.4f} um)'.format(aper_y_ini, aper_y_target)) - #print ('will move pzt_dcm_th2: ({0:2.4f} um --> {1:2.4f} um)'.format(pzt_dcm_th2_ini, pzt_dcm_th2_target)) - print ('will move pzt_dcm_chi2: ({0:2.4f} um --> {1:2.4f} um)'.format(pzt_dcm_chi2_ini, pzt_dcm_chi2_target)) - print ('will move th2_motor: ({0:2.6f} deg --> {1:2.6f} deg)'.format(th2_motor_ini, th2_motor_target)) - if move_clens_flag: - print ('will move clens_x: ({0:2.4f} um --> {1:2.4f} um)'.format(clens_x_ini, clens_x_target)) - print ('will move clens_y1: ({0:2.4f} um --> {1:2.4f} um)'.format(clens_y1_ini, clens_y1_target)) - print ('will move clens_y2: ({0:2.4f} um --> {1:2.4f} um)'.format(clens_y1_ini, clens_y2_target)) - print ('will move clens_p: ({0:2.4f} um --> {1:2.4f} um)'.format(clens_p_ini, clens_p_target)) - if move_det_flag: - print ('will move DetU_x: ({0:2.6f} mm --> {1:2.6f} mm)'.format(DetU_x_ini, DetU_x_target)) - print ('will move DetU_y: ({0:2.6f} mm --> {1:2.6f} mm)'.format(DetU_y_ini, DetU_y_target)) - else: - print('record_calib_pos1() or record_calib_pos2() not excuted successfully...\nWill not move anything') - -''' - # def cal_phase_ring_position(eng_new, eng_ini=0, print_flag=1): # ''' # calculate delta amount of phase_ring movement: diff --git a/startup/98-user_scan.py b/startup/98-user_scan.py index 9fe2859..3797a17 100644 --- a/startup/98-user_scan.py +++ b/startup/98-user_scan.py @@ -586,7 +586,7 @@ def _xanes_3D_xh( enable_z=True, ): for eng in eng_list: - yield from move_zp_ccd(eng, move_flag=1) + yield from move_zp_ccd_xh(eng, move_flag=1) my_note = f"{note}@energy={eng}keV" yield from bps.sleep(1) print(f"current energy: {eng}") @@ -718,7 +718,7 @@ def _multi_pos_xanes_3D_xh( eng_list[int(eng_list.shape[0] / 2)], eng_list[0], ]: - yield from move_zp_ccd(ii, move_flag=1) + yield from move_zp_ccd_xh(ii, move_flag=1) my_note = note + f"_ref_flat@energy={ii}keV" yield from bps.sleep(1) print(f"current energy: {ii}") @@ -770,6 +770,7 @@ def _xanes_3D_zebra_xh( out_z=None, out_r=None, simu=False, + settle_time=0, rel_out_flag=1, note="", bin_fac=1, @@ -778,9 +779,10 @@ def _xanes_3D_zebra_xh( flyer=tomo_flyer, ): for eng in eng_list: - yield from move_zp_ccd(eng, move_flag=1) + yield from move_zp_ccd_xh(eng, move_flag=1) my_note = f"{note}@energy={eng}keV" yield from bps.sleep(1) + yield from bps.sleep(settle_time) print(f"current energy: {eng}") yield from tomo_zfly( @@ -828,6 +830,7 @@ def _multi_pos_xanes_3D_zebra_xh( rel_out_flag=1, note="", sleep_time=0, + settle_time=0, bin_fac=1, flts=[], repeat=1, @@ -856,6 +859,7 @@ def _multi_pos_xanes_3D_zebra_xh( trans_first_flag=1, enable_z=True, ) + yield from bps.sleep(settle_time) yield from _xanes_3D_zebra_xh( eng_list, exp_t=exp_t, @@ -869,6 +873,7 @@ def _multi_pos_xanes_3D_zebra_xh( out_z=out_z, out_r=out_r, simu=simu, + settle_time=settle_time, rel_out_flag=rel_out_flag, note=note, bin_fac=bin_fac, @@ -916,7 +921,7 @@ def _multi_pos_xanes_3D_zebra_xh( eng_list[int(eng_list.shape[0] / 2)], eng_list[0], ]: - yield from move_zp_ccd(ii, move_flag=1) + yield from move_zp_ccd_xh(ii, move_flag=1) my_note = f"{note}_ref_flat@energy={ii}keV" yield from bps.sleep(1) print(f"current energy: {ii}") @@ -971,6 +976,7 @@ def _multi_pos_xanes_2D_xh( repeat_num=1, exposure_time=0.2, sleep_time=1, + settle_time=0, chunk_size=5, simu=False, relative_move_flag=True, @@ -1157,7 +1163,7 @@ def inner_scan(): for rep in range(repeat_num): print(f"repeat multi-pos xanes scan #{rep}") for eng in eng_list: - yield from move_zp_ccd(eng, move_flag=1, info_flag=0) + yield from move_zp_ccd_xh(eng, move_flag=1, info_flag=0) yield from _open_shutter_xhx(simu) # take image at multiple positions for i in range(num): @@ -1170,6 +1176,7 @@ def inner_scan(): trans_first_flag=1, enable_z=enable_z, ) + yield from bps.sleep(settle_time) yield from trigger_and_read(list(detectors) + motor) yield from _take_bkg_image_xhx( motor_x_out, @@ -1215,6 +1222,7 @@ def inner_scan(): trans_first_flag=1, enable_z=enable_z, ) + yield from bps.sleep(settle_time) yield from trigger_and_read(list(detectors) + motor) yield from _take_bkg_image_xhx( motor_x_out, @@ -1684,6 +1692,7 @@ def multi_edge_xanes_zebra( bulk_intgr=10, simu=False, sleep=0, + settle_time=0, repeat=None, ref_flat_scan=False, cam=Andor, @@ -1721,7 +1730,6 @@ def multi_edge_xanes_zebra( else: period = 0.05 print("Use default acquisition period 0.05 sec") - print(f"We are going to do 2D XANES of {elem} with exposure time {exposure} sec and acquisition period {period} sec; filters {flt} will be used.") eng_list = _mk_eng_list(edge_list[elem], bulk=False) if scan_type == "2D": @@ -1742,6 +1750,7 @@ def multi_edge_xanes_zebra( note=note, md=None, sleep_time=sleep, + settle_time=settle_time, repeat_num=repeat, binning=bin_fac, flts=flt, @@ -1768,6 +1777,7 @@ def multi_edge_xanes_zebra( rel_out_flag=rel_out_flag, note=note, sleep_time=sleep, + settle_time=settle_time, bin_fac=bin_fac, flts=flt, repeat=repeat, @@ -1933,7 +1943,7 @@ def multi_edge_xanes( eng_list[int(eng_list.shape[0] / 2)], eng_list[-1], ]: - yield from move_zp_ccd(ii, move_flag=1) + yield from move_zp_ccd_xh(ii, move_flag=1) my_note = note + f"_ref_flat@energy={ii}keV" yield from bps.sleep(1) print(f"current energy: {ii}") @@ -3186,8 +3196,7 @@ def cal_calib_pos(): return calib_pos - -def update_th2(): +def update_th2_xh(): calib = trans_calib() cur_eng = XEng.position cur_th2 = dcm.th2.position @@ -3213,7 +3222,7 @@ def update_th2(): return calib -def update_CALIBER_th2(): +def update_CALIBER_th2_xh(): global CALIBER _calib = trans_calib() _calib_th2 = update_th2() @@ -3221,7 +3230,7 @@ def update_CALIBER_th2(): CALIBER[f"th2_motor_{_calib[key]['pos']}"] = _calib_th2[key]["th2_motor"] -def trans_calib(): +def trans_calib_xh(): global CALIBER new_key1 = [] new_key2 = [] @@ -3238,9 +3247,314 @@ def trans_calib(): if key1 in k: calib_dict[key2][k.strip("_"+ key1)] = CALIBER[k] calib_dict[key2]["pos"] = key1 + calib_dict[key2]["mag"] = CALIBER["mag" + key1.strip("pos")] return calib_dict +def lock_th2_xh(): + cur_th2 = dcm.th2.position + yield from mv(dcm_th2.feedback_enable, 0) + yield from mv(dcm_th2.feedback, cur_th2) + yield from mv(dcm_th2.feedback_enable, 1) + + +def move_zp_ccd_xh(eng_new, move_flag=1, info_flag=1, move_clens_flag=0, move_det_flag=0, mag=None, zp_cfg=None): + """ + move the zone_plate and ccd to the user-defined energy with constant magnification + use the function as: + move_zp_ccd_with_const_mag(eng_new=8.0, move_flag=1) + Note: + in the above commend, it will use two energy calibration points to calculate the motor + position of XEng=8.0 keV. + specfically, one of the calibration points is > 8keV, the other one is < 8keV + + Inputs: + ------- + eng_new: float + User defined energy, in unit of keV + flag: int + 0: Do calculation without moving real stages + 1: Will move stages + """ + global GLOBAL_VLM_MAG + + def find_nearest(data, value): + data = np.array(data) + return np.abs(data - value).argmin() + + eng_new = float(eng_new) # keV, e.g. 9.0 + if (eng_new < 5.0) or (eng_new > 15.0): + print("Target energy is out of allowed range [5.0, 15.0] keV.") + return -1 + + det = DetU # upstream detector + eng_ini = XEng.position + + + eng_lst1 = list(trans_calib_xh().keys()) + eng_lst2 = list(trans_calib_xh().keys()) + + idx = find_nearest(eng_new, eng_lst1) + eng1 = eng_lst1[idx] + print(f"{eng1=}") + id1 = trans_calib_xh()[eng1]["pos"].strip("pos") + mag1 = trans_calib_xh()[eng1]["mag"] + + eng_lst2.pop(idx) + eng2 = eng_lst2[find_nearest(eng_new, eng_lst2)] + print(f"{eng2=}") + id2 = trans_calib_xh()[eng2]["pos"].strip("pos") + mag2 = trans_calib_xh()[eng2]["mag"] + + if not (np.abs(mag1 / mag2 - 1) < 1e-3): + print( + f"mismatch in magfinication:\nmagnificatoin at {eng1:5f} keV = {mag1}\nmagnificatoin at {eng2:2.5f} keV = {mag2}\nwill not move" + ) + return -1 + + zp_ini, det_ini, _, _, _ = cal_ccd_zp_xh(eng_ini, mag1/GLOBAL_VLM_MAG, zp_cfg=zp_cfg, show=False) + zp_final, det_final, _, _, _ = cal_ccd_zp_xh(eng_new, mag1/GLOBAL_VLM_MAG, zp_cfg=zp_cfg, show=False) + zp_delta = zp_final - zp_ini + det_delta = det_final - det_ini + + if (det_final < det.z.low_limit.value) or (det_final > det.z.high_limit.value): + print( + "Trying to move DetU to {0:2.2f}. Movement is out of travel range ({1:2.2f}, {2:2.2f})\nTry to move the bottom stage manually.".format( + det_final, det.z.low_limit.value, det.z.high_limit.value + ) + ) + return -1 + + print( + f"using reference at {eng1:2.5f} keV and {eng2:2.5f} kev to interpolate\n" + ) + dcm_chi2_eng1 = CALIBER[f"chi2_pos{id1}"] + zp_x_pos_eng1 = CALIBER[f"zp_x_pos{id1}"] + zp_y_pos_eng1 = CALIBER[f"zp_y_pos{id1}"] + th2_motor_eng1 = CALIBER[f"th2_motor_pos{id1}"] + clens_x_eng1 = CALIBER[f"clens_x_pos{id1}"] + clens_y1_eng1 = CALIBER[f"clens_y1_pos{id1}"] + clens_y2_eng1 = CALIBER[f"clens_y2_pos{id1}"] + clens_p_eng1 = CALIBER[f"clens_p_pos{id1}"] + DetU_x_eng1 = CALIBER[f"DetU_x_pos{id1}"] + DetU_y_eng1 = CALIBER[f"DetU_y_pos{id1}"] + aper_x_eng1 = CALIBER[f"aper_x_pos{id1}"] + aper_y_eng1 = CALIBER[f"aper_y_pos{id1}"] + + dcm_chi2_eng2 = CALIBER[f"chi2_pos{id2}"] + zp_x_pos_eng2 = CALIBER[f"zp_x_pos{id2}"] + zp_y_pos_eng2 = CALIBER[f"zp_y_pos{id2}"] + th2_motor_eng2 = CALIBER[f"th2_motor_pos{id2}"] + clens_x_eng2 = CALIBER[f"clens_x_pos{id2}"] + clens_y1_eng2 = CALIBER[f"clens_y1_pos{id2}"] + clens_y2_eng2 = CALIBER[f"clens_y2_pos{id2}"] + clens_p_eng2 = CALIBER[f"clens_p_pos{id2}"] + DetU_x_eng2 = CALIBER[f"DetU_x_pos{id2}"] + DetU_y_eng2 = CALIBER[f"DetU_y_pos{id2}"] + aper_x_eng2 = CALIBER[f"aper_x_pos{id2}"] + aper_y_eng2 = CALIBER[f"aper_y_pos{id2}"] + + chi2_motor_target = (eng_new - eng2) * (dcm_chi2_eng1 - dcm_chi2_eng2) / ( + eng1 - eng2 + ) + dcm_chi2_eng2 + + zp_x_target = (eng_new - eng2) * (zp_x_pos_eng1 - zp_x_pos_eng2) / ( + eng1 - eng2 + ) + zp_x_pos_eng2 + zp_y_target = (eng_new - eng2) * (zp_y_pos_eng1 - zp_y_pos_eng2) / ( + eng1 - eng2 + ) + zp_y_pos_eng2 + + th2_motor_target = (eng_new - eng2) * (th2_motor_eng1 - th2_motor_eng2) / ( + eng1 - eng2 + ) + th2_motor_eng2 + clens_x_target = (eng_new - eng2) * (clens_x_eng1 - clens_x_eng2) / ( + eng1 - eng2 + ) + clens_x_eng2 + clens_y1_target = (eng_new - eng2) * (clens_y1_eng1 - clens_y1_eng2) / ( + eng1 - eng2 + ) + clens_y1_eng2 + clens_y2_target = (eng_new - eng2) * (clens_y2_eng1 - clens_y2_eng2) / ( + eng1 - eng2 + ) + clens_y2_eng2 + clens_p_target = (eng_new - eng2) * (clens_p_eng1 - clens_p_eng2) / ( + eng1 - eng2 + ) + clens_p_eng2 + DetU_x_target = (eng_new - eng2) * (DetU_x_eng1 - DetU_x_eng2) / ( + eng1 - eng2 + ) + DetU_x_eng2 + DetU_y_target = (eng_new - eng2) * (DetU_y_eng1 - DetU_y_eng2) / ( + eng1 - eng2 + ) + DetU_y_eng2 + aper_x_target = (eng_new - eng2) * (aper_x_eng1 - aper_x_eng2) / ( + eng1 - eng2 + ) + aper_x_eng2 + aper_y_target = (eng_new - eng2) * (aper_y_eng1 - aper_y_eng2) / ( + eng1 - eng2 + ) + aper_y_eng2 + + dcm_chi2_ini = dcm.chi2.position + zp_x_ini = zp.x.position + zp_y_ini = zp.y.position + th2_motor_ini = dcm.th2.position + clens_x_ini = clens.x.position + clens_y1_ini = clens.y1.position + clens_y2_ini = clens.y2.position + clens_p_ini = clens.p.position + DetU_x_ini = DetU.x.position + DetU_y_ini = DetU.y.position + aper_x_ini = aper.x.position + aper_y_ini = aper.y.position + + + if np.abs(eng1 - eng2) < 1e-5: # difference less than 0.01 eV + print( + f'eng1({eng1:2.5f} eV) and eng2({eng2:2.5f} eV) in "CALIBER" are two close, will not move any motors...' + ) + return -1 + else: + if info_flag or (not move_flag): + if not move_flag: + print("This is calculation. No stages move") + + print( + "Energy: {0:5.2f} keV --> {1:5.2f} keV".format(eng_ini, eng_new) + ) + print( + "zone plate position: {0:2.4f} mm --> {1:2.4f} mm".format( + zp_ini, zp_final + ) + ) + print( + "CCD position: {0:2.4f} mm --> {1:2.4f} mm".format( + det_ini, det_final + ) + ) + print( + "move zp_x: ({0:2.4f} um --> {1:2.4f} um)".format( + zp_x_ini, zp_x_target + ) + ) + print( + "move zp_y: ({0:2.4f} um --> {1:2.4f} um)".format( + zp_y_ini, zp_y_target + ) + ) + print( + "move th2_motor: ({0:2.6f} deg --> {1:2.6f} deg)".format( + th2_motor_ini, th2_motor_target + ) + ) + print( + "move aper_x_motor: ({0:2.4f} um --> {1:2.4f} um)".format( + aper_x_ini, aper_x_target + ) + ) + print( + "move aper_y_motor: ({0:2.4f} um --> {1:2.4f} um)".format( + aper_y_ini, aper_y_target + ) + ) + if move_clens_flag: + print( + "move clens_x: ({0:2.4f} um --> {1:2.4f} um)".format( + clens_x_ini, clens_x_target + ) + ) + print( + "move clens_y1: ({0:2.4f} um --> {1:2.4f} um)".format( + clens_y1_ini, clens_y1_target + ) + ) + print( + "move clens_y2: ({0:2.4f} um --> {1:2.4f} um)".format( + clens_y1_ini, clens_y2_target + ) + ) + print( + "move clens_p: ({0:2.4f} um --> {1:2.4f} um)".format( + clens_p_ini, clens_p_target + ) + ) + + if move_det_flag: + print( + "move DetU_x: ({0:2.4f} um --> {1:2.4f} um)".format( + DetU_x_ini, DetU_x_target + ) + ) + print( + "move DetU_y: ({0:2.4f} um --> {1:2.4f} um)".format( + DetU_y_ini, DetU_y_target + ) + ) + if move_flag: # move stages + print("Now moving stages ....") + + yield from mv(dcm_th2.feedback_enable, 0) + yield from mv(dcm_th2.feedback, th2_motor_target) + yield from mv(dcm_th2.feedback_enable, 1) + + yield from mv(zp.x, zp_x_target, zp.y, zp_y_target) + yield from mv(aper.x, aper_x_target, aper.y, aper_y_target) + yield from mv(zp.z, zp_final, det.z, det_final, XEng, eng_new) + + if move_clens_flag: + yield from mv( + clens.x, + clens_x_target, + clens.y1, + clens_y1_target, + clens.y2, + clens_y2_target, + ) + yield from mv(clens.p, clens_p_target) + if move_det_flag: + yield from mv(DetU.x, DetU_x_target) + yield from mv(DetU.y, DetU_y_target) + + yield from bps.sleep(0.5) + if abs(eng_new - eng_ini) >= 0.005: + t = 10 * abs(eng_new - eng_ini) + t = min(t, 2) + print(f"sleep for {t} sec") + yield from bps.sleep(t) + + +def record_calib_pos_new_xh(n): + global GLOBAL_MAG, CALIBER + + CALIBER[f"chi2_pos{n}"] = dcm.chi2.position + CALIBER[f"XEng_pos{n}"] = XEng.position + CALIBER[f"zp_x_pos{n}"] = zp.x.position + CALIBER[f"zp_y_pos{n}"] = zp.y.position + CALIBER[f"th2_motor_pos{n}"] = dcm.th2.position + CALIBER[f"clens_x_pos{n}"] = clens.x.position + CALIBER[f"clens_y1_pos{n}"] = clens.y1.position + CALIBER[f"clens_y2_pos{n}"] = clens.y2.position + CALIBER[f"clens_p_pos{n}"] = clens.p.position + CALIBER[f"DetU_y_pos{n}"] = DetU.y.position + CALIBER[f"DetU_x_pos{n}"] = DetU.x.position + CALIBER[f"aper_x_pos{n}"] = aper.x.position + CALIBER[f"aper_y_pos{n}"] = aper.y.position + #CALIBER[f"txm_x_pos{n}"] = zps.pi_x.position + + mag = (DetU.z.position / zp.z.position - 1) * GLOBAL_VLM_MAG + CALIBER[f"mag{n}"] = np.round(mag * 100) / 100.0 + GLOBAL_MAG = CALIBER[f"mag{n}"] + + tmp = {} + for k in CALIBER.keys(): + if str(n) in k: + tmp[k] = CALIBER[k] + pp = pprint.PrettyPrinter(indent=4) + pp.pprint(tmp) + df = pd.DataFrame.from_dict(CALIBER, orient="index") + df.to_csv("/nsls2/data/fxi-new/legacy/log/calib_new.csv") + # df.to_csv("/home/xf18id/.ipython/profile_collection/startup/calib_new.csv", sep="\t") + print(f'calib_pos{n} recored: current Magnification = GLOBAL_MAG = {CALIBER[f"mag{n}"]}') + + def grid_z_scan( zstart=-0.03, zstop=0.03, @@ -4024,6 +4338,7 @@ def multi_pos_radiography_record( repeat_num=1, exposure_time=0.2, sleep_time=1, + settle_time=0, chunk_size=5, simu=False, relative_move_flag=False, @@ -4047,6 +4362,7 @@ def multi_pos_radiography_record( repeat_num=repeat_num, exposure_time=exposure_time, sleep_time=sleep_time, + settle_time=settle_time, chunk_size=chunk_size, simu=simu, relative_move_flag=relative_move_flag, @@ -4239,7 +4555,7 @@ def multi_pos_2D_xanes_and_3D_tomo( ) -def cal_ccd_zp_xh(eng, mag, zp_cfg=None): +def cal_ccd_zp_xh(eng, mag, zp_cfg=None, show=True): """ INPUT: eng: X-ray energy in keV @@ -4264,10 +4580,16 @@ def cal_ccd_zp_xh(eng, mag, zp_cfg=None): p = f * (mag + 1) / mag # object distance from zone plate in mm q = p * mag # detector distance from zone plate in mm det_pos = p + q # ccd detector distance from the sample in mm - print(f"obj-zp dist: {round(p, 4)} mm\ndet-sam dist: {round(det_pos, 4)} mm\nwavelength: {round(wl, 4)} nm\nNumerical aperture: {round(na, 4)} rad\nzp focal length: {round(f,4)} mm") + if show: + print(f"obj-zp dist: {round(p, 4)} mm\ndet-sam dist: {round(det_pos, 4)} mm\nwavelength: {round(wl, 4)} nm\nNumerical aperture: {round(na, 4)} rad\nzp focal length: {round(f,4)} mm") return p, det_pos, wl, na, f +def set_ccd_zp_xh(eng, mag, zp_cfg=None): + p, det_pos, _, _, _ = cal_ccd_zp_xh(eng, mag, zp_cfg=zp_cfg) + + + def z_scan_xh( scan_motor:'zp_z', start=-0.03, @@ -4689,7 +5011,7 @@ def diff_tomo( for jj in range(sam_in_pos_list.shape[0]): for ii in range(len(eng)): - yield from move_zp_ccd( + yield from move_zp_ccd_xh( eng[ii], move_flag=1, info_flag=1, move_clens_flag=0, move_det_flag=0 ) yield from mv( From e600575a7dcdf6a32349022f7fdf8c7867c3f349 Mon Sep 17 00:00:00 2001 From: FXI Operator Date: Fri, 6 Sep 2024 13:58:44 -0400 Subject: [PATCH 8/9] Add Ophyd objects for Wiener Crate, Scaler and SR570 --- startup/05-ion-chamber.py | 156 ++++++++++++++++++++++++++++++++++++ startup/10-area-detector.py | 21 ++--- 2 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 startup/05-ion-chamber.py diff --git a/startup/05-ion-chamber.py b/startup/05-ion-chamber.py new file mode 100644 index 0000000..7b03f0c --- /dev/null +++ b/startup/05-ion-chamber.py @@ -0,0 +1,156 @@ +from ophyd import Device, Component as Cpt, EpicsScaler, EpicsSignal, EpicsSignalRO +from ophyd.device import DynamicDeviceComponent as DDC +from collections import OrderedDict + + +# The source for the scaler code is github.com/NSLS-II-SRX/profile_collection/startup/30-scaler.py + + +class EpicsSignalROLazyier(EpicsSignalRO): + def get(self, *args, timeout=5, **kwargs): + return super().get(*args, timeout=timeout, **kwargs) + + +def _scaler_fields(attr_base, field_base, range_, **kwargs): + defn = OrderedDict() + for i in range_: + attr = "{attr}{i}".format(attr=attr_base, i=i) + suffix = "{field}{i}".format(field=field_base, i=i) + defn[attr] = (EpicsSignalROLazyier, suffix, kwargs) + + return defn + + +class FXIScaler(EpicsScaler): + acquire_mode = Cpt(EpicsSignal, "AcquireMode") + acquiring = Cpt(EpicsSignal, "Acquiring") + asyn = Cpt(EpicsSignal, "Asyn") + channel1_source = Cpt(EpicsSignal, "Channel1Source") + channel_advance = Cpt(EpicsSignal, "ChannelAdvance", string=True) + channels = DDC(_scaler_fields("chan", ".S", range(1, 33))) + client_wait = Cpt(EpicsSignal, "ClientWait") + count_on_start = Cpt(EpicsSignal, "CountOnStart") + current_channel = Cpt(EpicsSignal, "CurrentChannel") + disable_auto_count = Cpt(EpicsSignal, "DisableAutoCount") + do_read_all = Cpt(EpicsSignal, "DoReadAll") + dwell = Cpt(EpicsSignal, "Dwell") + elapsed_real = Cpt(EpicsSignal, "ElapsedReal") + enable_client_wait = Cpt(EpicsSignal, "EnableClientWait") + erase_all = Cpt(EpicsSignal, "EraseAll") + erase_start = Cpt(EpicsSignal, "EraseStart") + firmware = Cpt(EpicsSignal, "Firmware") + hardware_acquiring = Cpt(EpicsSignal, "HardwareAcquiring") + input_mode = Cpt(EpicsSignal, "InputMode") + max_channels = Cpt(EpicsSignal, "MaxChannels") + model = Cpt(EpicsSignal, "Model") + mux_output = Cpt(EpicsSignal, "MUXOutput") + nuse_all = Cpt(EpicsSignal, "NuseAll") + output_mode = Cpt(EpicsSignal, "OutputMode") + output_polarity = Cpt(EpicsSignal, "OutputPolarity") + prescale = Cpt(EpicsSignal, "Prescale") + preset_real = Cpt(EpicsSignal, "PresetReal") + read_all = Cpt(EpicsSignal, "ReadAll") + read_all_once = Cpt(EpicsSignal, "ReadAllOnce") + set_acquiring = Cpt(EpicsSignal, "SetAcquiring") + set_client_wait = Cpt(EpicsSignal, "SetClientWait") + snl_connected = Cpt(EpicsSignal, "SNL_Connected") + software_channel_advance = Cpt(EpicsSignal, "SoftwareChannelAdvance") + count_mode = Cpt(EpicsSignal, ".CONT") + start_all = Cpt(EpicsSignal, "StartAll") + stop_all = Cpt(EpicsSignal, "StopAll") + user_led = Cpt(EpicsSignal, "UserLED") + wfrm = Cpt(EpicsSignal, "Wfrm") + mca1 = Cpt(EpicsSignalRO, "mca1") + mca2 = Cpt(EpicsSignalRO, "mca2") + mca3 = Cpt(EpicsSignalRO, "mca3") + mca4 = Cpt(EpicsSignalRO, "mca4") + + def __init__(self, prefix, **kwargs): + super().__init__(prefix, **kwargs) + self.stage_sigs[self.count_mode] = "OneShot" + +sclr1 = FXIScaler("XF:18IDB-ES{Sclr:1}", name="sclr1") + + + +class SR570(Device): + # SR570 preamps are controlled via one-way RS232 connection. The IOC can keep track only of the + # settings change via EPICS. It does not know the actual settings if the changes are made + # manually using buttons on the hardware unit. + + init = Cpt(EpicsSignal, "init.PROC") + reset = Cpt(EpicsSignal, "reset.PROC") + + sensitivity_num = Cpt(EpicsSignal, "sens_num", string=True) + sensitivity_unit = Cpt(EpicsSignal, "sens_unit", string=True) + + offset_on = Cpt(EpicsSignal, "offset_on", string=True) + offset_sign = Cpt(EpicsSignal, "offset_sign", string=True) + offset_num = Cpt(EpicsSignal, "offset_num", string=True) + offset_unit = Cpt(EpicsSignal, "offset_unit", string=True) + offset_u_put = Cpt(EpicsSignal, "off_u_put", ) + offset_u_tweak = Cpt(EpicsSignal, "offset_u_tweak") + offset_cal = Cpt(EpicsSignal, "offset_cal", string=True) + + bias_put = Cpt(EpicsSignal, "bias_put") + bias_tweak = Cpt(EpicsSignal, "bias_tweak") + bias_on = Cpt(EpicsSignal, "bias_on", string=True) + + filter_type = Cpt(EpicsSignal, "filter_type", string=True) + filter_reset = Cpt(EpicsSignal, "filter_reset.PROC") + filter_low_freq = Cpt(EpicsSignal, "low_freq", string=True) + filter_high_freq = Cpt(EpicsSignal, "high_freq", string=True) + + gain_mode = Cpt(EpicsSignal, "gain_mode", string=True) + invert_on = Cpt(EpicsSignal, "invert_on", string=True) + blank_on = Cpt(EpicsSignal, "blank_on", string=True) + + +class SR570_PREAMPS(Device): + unit1 = Cpt(SR570, "{SR570:1}") + unit2 = Cpt(SR570, "{SR570:2}") + unit3 = Cpt(SR570, "{SR570:3}") + unit4 = Cpt(SR570, "{SR570:4}") + + +sr570_preamps = SR570_PREAMPS("XF:18IDB-CT", name="sr570_preamps") + + +class WienerHVCrateChannel(Device): + + status_dec = Cpt(EpicsSignalRO, "StatusDec") # Bits 0-7 + + # Meanings of the status bits: + # outputOn (0) output channel is on + # outputInhibit(1) external (hardware-)inhibit of the output channel + # outputFailureMinSenseVoltage (2) Sense voltage is too low + # outputFailureMaxSenseVoltage (3) Sense voltage is too high + # outputFailureMaxTerminalVoltage (4) Terminal voltage is too high + # outputFailureMaxCurrent (5) Current is too high + # outputFailureMaxTemperature (6) Heat sink temperature is too high + # outputFailureMaxPower (7) Output power is too high + + switch_on_off = Cpt(EpicsSignal, "Switch") + V_set = Cpt(EpicsSignal, "V-Set") + V_sense = Cpt(EpicsSignalRO, "V-Sense") + I_set_limit = Cpt(EpicsSignal, "I-SetLimit") + I_sense = Cpt(EpicsSignalRO, "I-Sense") + temperature = Cpt(EpicsSignalRO, "Temperature") + V_fall_rate = Cpt(EpicsSignal, "V-FallRate") + V_rise_rate = Cpt(EpicsSignal, "V-RiseRate") + + +class WienerHVCrate(Device): + + u0 = Cpt(WienerHVCrateChannel, "HV:u0}") + u1 = Cpt(WienerHVCrateChannel, "HV:u1}") + u2 = Cpt(WienerHVCrateChannel, "HV:u2}") + u3 = Cpt(WienerHVCrateChannel, "HV:u3}") + u4 = Cpt(WienerHVCrateChannel, "HV:u4}") + u5 = Cpt(WienerHVCrateChannel, "HV:u5}") + u6 = Cpt(WienerHVCrateChannel, "HV:u6}") + u7 = Cpt(WienerHVCrateChannel, "HV:u7}") + + +hv_crate = WienerHVCrate("XF:18IDB-OP{WPS:01-", name="hv_crate") + diff --git a/startup/10-area-detector.py b/startup/10-area-detector.py index 9d549f7..06b41d1 100644 --- a/startup/10-area-detector.py +++ b/startup/10-area-detector.py @@ -399,15 +399,18 @@ def resume(self): # vlm.hdf5.read_attrs = [] -Oryx = Manta("XF:18IDB-ES{Det:Oryx1}", name="Oryx") -#Oryx.cam.ensure_nonblocking() -Oryx.read_attrs = ["hdf5"] -#Oryx.stats1.read_attrs = ["total"] -Oryx.hdf5.read_attrs = ["time_stamp"] -Oryx.stage_sigs["cam.image_mode"] = 1 -for k in ("image", ): - getattr(Oryx, k).ensure_nonblocking() -Oryx.hdf5.time_stamp.name = "Oryx_timestamps" +############################################# +# turn off Oryx when it is not used +# Oryx = Manta("XF:18IDB-ES{Det:Oryx1}", name="Oryx") +# #Oryx.cam.ensure_nonblocking() +# Oryx.read_attrs = ["hdf5"] +# #Oryx.stats1.read_attrs = ["total"] +# Oryx.hdf5.read_attrs = ["time_stamp"] +# Oryx.stage_sigs["cam.image_mode"] = 1 +# for k in ("image", ): +# getattr(Oryx, k).ensure_nonblocking() +# Oryx.hdf5.time_stamp.name = "Oryx_timestamps" +############################################# #for det in [detA1, Andor]: for det in [detA1]: From fc1c2c509d79db82713cd6838e16a4f927d6d59b Mon Sep 17 00:00:00 2001 From: FXI Operator Date: Fri, 4 Oct 2024 14:52:48 -0400 Subject: [PATCH 9/9] 2024_10_04 --- startup/10-area-detector.py | 69 +++--- startup/18-zebra.py | 74 ++++--- startup/40-scan_pre_define.py | 45 ++-- startup/41-scans.py | 191 +++++++++------- startup/42-scans_legacy.py | 34 +-- startup/43-scans_pzt.py | 26 +-- startup/44-scans_other.py | 40 ++-- startup/46-zebra_flyer.py | 147 ++++++------- startup/80-load_scan.py | 36 ++- startup/91-functions.py | 30 ++- startup/98-user_scan.py | 403 +++++++++++++++++++++------------- startup/99-umacro.py | 2 +- 12 files changed, 652 insertions(+), 445 deletions(-) diff --git a/startup/10-area-detector.py b/startup/10-area-detector.py index 06b41d1..cb8c8de 100644 --- a/startup/10-area-detector.py +++ b/startup/10-area-detector.py @@ -178,23 +178,22 @@ class AndorKlass(SingleTriggerV33, DetectorBase): roi4 = Cpt(ROIPlugin, "ROI4:") proc1 = Cpt(ProcessPlugin, "Proc1:") + def cam_name(self): + print(self.prefix.split("{")[1].strip("}").split(":")[1]) + + root_path = "/nsls2/data/fxi-new/legacy/Andor" hdf5 = Cpt( HDF5PluginWithFileStore, suffix="HDF1:", - write_path_template="/nsls2/data/fxi-new/legacy/Andor/%Y/%m/%d/", - # write_path_template='/tmp/test_2022/%Y/%m/%d/' , - # write_path_template="/nsls2/data/fxi-new/assets/default/%Y/%m/%d/", - # write_path_template="/nsls2/data/fxi-new/legacy/Andor//%Y/%m/%d/", - # root="/nsls2/data/fxi-new/assets/default", - root="/nsls2/data/fxi-new/legacy/Andor", - # root = "/tmp/test_2022", - # root="/nsls2/data/fxi-new/legacy/Andor/", - # write_path_template='/tmp/', - # root='/dev/shm', + write_path_template=f"{root_path}/%Y/%m/%d/", + root=root_path, ) ac_period = Cpt(EpicsSignal, "cam1:AcquirePeriod") binning = Cpt(EpicsSignal, "cam1:A3Binning") + pre_amp = Cpt(EpicsSignal, "cam1:PreAmpGain") + rd_rate = Cpt(EpicsSignal, "cam1:ReadoutRate") + pxl_encoding = Cpt(EpicsSignal, "cam1:PixelEncoding") def stop(self): self.hdf5.capture.put(0) @@ -353,7 +352,9 @@ def resume(self): Andor.hdf5.read_attrs = [] """ - +""" +Comment out this section when Andor Neo2 is not connected +#---- added by xh Andor = AndorKlass("XF:18IDB-BI{Det:Neo2}", name="Andor") Andor.cam.ensure_nonblocking() # Andor.read_attrs = ['hdf5', 'stats1', 'stats5'] @@ -368,27 +369,19 @@ def resume(self): for k in ("image", "trans1", "roi1", "proc1"): getattr(Andor, k).ensure_nonblocking() Andor.hdf5.time_stamp.name = "Andor_timestamps" +""" ######################################### -''' comment away this section when Marana is disconnected - # added by XH -Marana = AndorKlass("XF:18IDB-ES{Det:Marana1}", name="Andor") -Marana.cam.ensure_nonblocking() -# Andor.read_attrs = ['hdf5', 'stats1', 'stats5'] -Marana.read_attrs = ['hdf5'] -#Andor.read_attrs = ["hdf5", "stats1"] -#Andor.stats1.read_attrs = ["total"] -# Andor.stats5.read_attrs = ['total'] -Marana.hdf5.read_attrs = ["time_stamp"] -Marana.stage_sigs["cam.image_mode"] = 0 -#for k in ("image", "stats1", "trans1", "roi1", "proc1"): -# getattr(Andor, k).ensure_nonblocking() +MaranaU = AndorKlass("XF:18IDB-ES{Det:Marana1}", name="Andor") +MaranaU.cam.ensure_nonblocking() +MaranaU.read_attrs = ['hdf5'] +MaranaU.hdf5.read_attrs = ["time_stamp"] +MaranaU.stage_sigs["cam.image_mode"] = 0 for k in ("image", "trans1", "roi1", "proc1"): - getattr(Marana, k).ensure_nonblocking() -Marana.hdf5.time_stamp.name = "Andor_timestamps" + getattr(MaranaU, k).ensure_nonblocking() +MaranaU.hdf5.time_stamp.name = "Andor_timestamps" -''' ############################################# # vlm = Manta("XF:18IDB-BI{VLM:1}", name="vlm") # detA1.read_attrs = ['hdf5', 'stats1', 'stats5'] @@ -418,3 +411,25 @@ def resume(self): # It does not work since it's not defined in the class, commenting out: # det.stats5.total.kind = 'hinted' +############################################# +# added by XH +CAM_RD_CFG = { + "MARANA-4BV6X": { + "rd_time": { + '12-bit (low noise)': 0.011, + '16-bit (high dynamic rang': 0.014, + '11-bit (high speed)': 0.007 + }, + "pxl_encoding": { + '12-bit (low noise)': 'Mono12', + '16-bit (high dynamic rang': 'Mono16', + '11-bit (high speed)': 'Mono12'}, + "image_mode": MaranaU.cam.image_mode.metadata["enum_strs"], + "trigger_mode": MaranaU.cam.trigger_mode.metadata["enum_strs"], + }, +} + +def cfg_cam_encoding(cam): + cam_model = cam.cam.model.value + yield from abs_set(cam.pxl_encoding, CAM_RD_CFG[cam_model]["pxl_encoding"][cam.pre_amp.enum_strs[cam.pre_amp.value]], wait=True) + diff --git a/startup/18-zebra.py b/startup/18-zebra.py index 8f476f4..f060058 100644 --- a/startup/18-zebra.py +++ b/startup/18-zebra.py @@ -18,13 +18,13 @@ # minimum detector acquisition period in second for full frame size # will move this definition to areaDetector.py -DET_MIN_AP = {"Andor": 0.05, "Marana": 0.01, "Oryx": 0.005} +DET_MIN_AP = {"Andor": 0.05, "MaranaU": 0.01, "Oryx": 0.005} # default velocity for rotating rotary stage back to starting position # will move this definition to motors.py ROT_BACK_VEL = 30 -BIN_FACS = {"Andor": {0: 1, 1: 2, 2: 3, 3: 4, 4: 8}, "Marana": {}, "Oryx": {}} +BIN_FACS = {"Andor": {0: 1, 1: 2, 2: 3, 3: 4, 4: 8}, "MaranaU": {}, "Oryx": {}} class ZebraPositionCaptureData(Device): @@ -275,7 +275,7 @@ class FXITomoFlyer(Device): read_path_template = f"zebra/%Y/%m/%d/" reg_root = f"zebra/" - KNOWN_DETS = {"Andor", "Marana", "Oryx"} + KNOWN_DETS = {"Andor", "MaranaU", "Oryx"} rot_axis = zps.pi_r # dummy_axis = ophyd.sim.SynAxis(name="TOMO_DUMMY") @@ -287,6 +287,7 @@ class FXITomoFlyer(Device): dft_pulse_wid = {"ms": 0.002, "s": 0.0005, "10s": 0.003} # 0: ms # 1: s # 2: 10s pc_trig_dir = {1: 0, -1: 1} # 1: positive, -1: negative + rot_var = 0.002 scan_cfg = {} pc_cfg = {} _staging_delay = 0.010 @@ -825,6 +826,7 @@ def resume(self): def preset_flyer(self, scn_cfg): yield from FXITomoFlyer.bin_det(self.detectors[0], scn_cfg["bin_fac"]) + yield from FXITomoFlyer.prime_det(self.detectors[0]) yield from FXITomoFlyer.init_mot_r(scn_cfg) # yield from FXITomoFlyer.set_cam_mode(self.detectors[0], stage="pre-scan") scn_cfg = FXITomoFlyer.cal_cam_rot_params(self.detectors[0], scn_cfg) @@ -844,7 +846,7 @@ def cal_cam_rot_params(cls, det, scn_cfg): """_summary_ Args: - det (str): choose from the set {"Andor", "Marana", "Oryx"} + det (str): choose from the set {"Andor", "MaranaU", "Oryx"} scn_cfg (dict): scan configuration parameters composed of 'scn_mode': choose between { 0: "standard", # a single scan in a given angle range @@ -865,16 +867,17 @@ def cal_cam_rot_params(cls, det, scn_cfg): dict: scan_cfg """ ############### calculate detector parameters ############### - acq_p = FXITomoFlyer.check_cam_exp(det, scn_cfg["acq_p"], scn_cfg["bin_fac"]) + acq_p, acq_min = FXITomoFlyer.check_cam_acq_p(det, scn_cfg["acq_p"], scn_cfg["bin_fac"]) + print(f"acq_p: {acq_p}, acq_min: {acq_min}") if acq_p > scn_cfg["acq_p"]: print( "Acquisition period is too small for the camera. Reset acquisition period to minimum allowed exposure time." ) - scn_cfg["acq_p"] = acq_p + scn_cfg["acq_p"] = acq_p - if scn_cfg["exp_t"] > acq_p - 0.002: - scn_cfg["exp_t"] = acq_p - 0.002 + if scn_cfg["exp_t"] > (acq_p - acq_min): + scn_cfg["exp_t"] = acq_p - acq_min ############### calculate rotary stage parameters ############### if scn_cfg["tacc"] <= 0: @@ -922,7 +925,7 @@ def cal_cam_rot_params(cls, det, scn_cfg): return None scn_cfg["taxi_dist"] = taxi_dist - scn_cfg["ang_step"] = scn_cfg["vel"] * scn_cfg["acq_p"] + scn_cfg["ang_step"] = scn_cfg["vel"] * (scn_cfg["acq_p"] + FXITomoFlyer.rot_var) if cls.scn_modes[scn_cfg["scn_mode"]] == "snaked: single file": scn_cfg["num_images"] = int( int( @@ -947,14 +950,18 @@ def cal_cam_rot_params(cls, det, scn_cfg): scn_cfg["rot_dir"] = 1 else: scn_cfg["rot_dir"] = -1 - + print(f"scn_cfg: {scn_cfg}") return scn_cfg @staticmethod - def check_cam_exp(det, acq_p, bin_fac): + def check_cam_acq_p(det, acq_p, bin_fac): + cam_model = det.cam.model.value acq_min = DET_MIN_AP[det.name] / BIN_FACS[det.name][bin_fac] - acq_p = max(acq_min, acq_p) - return acq_p + if cam_model == "MARANA-4BV6X": + full_acq_min = CAM_RD_CFG[cam_model]["rd_time"][det.pre_amp.enum_strs[det.pre_amp.value]] + acq_min = full_acq_min * (det.cam.size.size_y.value / 2048) + acq_p = max(acq_min + 0.002, acq_p) - FXITomoFlyer.rot_var # accounting vel variation in rot vel + return acq_p, acq_min @classmethod def cal_zebra_pc_params(cls, scn_cfg): @@ -1063,13 +1070,14 @@ def compose_scn_cfg( return scn_cfg @staticmethod - def prime_det(det): - if det.cam.trigger_mode.get() != 0: - yield from abs_set(det.cam.trigger_mode, 0, wait=True) - if det.cam.image_mode.get() != 0: - yield from abs_set(det.cam.image_mode, 0, wait=True) + def do_prime(det): + if CAM_RD_CFG[det.cam.model.value]["trigger_mode"][det.cam.trigger_mode.value] != "Internal": + yield from abs_set(det.cam.trigger_mode, "Internal", timeout=5, settle_time=1, wait=True) + if CAM_RD_CFG[det.cam.model.value]["image_mode"][det.cam.image_mode.value] != "Fixed": + yield from abs_set(det.cam.image_mode, "Fixed", timeout=5, settle_time=1, wait=True) yield from abs_set(det.cam.num_images, 5, wait=True) - yield from abs_set(det.cam.acquire, 1, wait=False) + yield from abs_set(det.cam.acquire, 1, wait=True) + print(f"{det.name} is primed!") @staticmethod def stop_det(det): @@ -1087,7 +1095,13 @@ def bin_det(det, bin_fac): if int(bin_fac) not in [0, 1, 2, 3, 4]: raise ValueError("binnng must be in [0, 1, 2, 3, 4]") yield from abs_set(det.binning, bin_fac, wait=True) - FXITomoFlyer.prime_det(det) + return bin_fac + # FXITomoFlyer.do_prime(det) + + @staticmethod + def prime_det(det): + if (det.cam.array_size.array_size_x.value != det.hdf5.array_size.width.value) or (det.cam.array_size.array_size_y.value != det.hdf5.array_size.height.value): + yield from FXITomoFlyer.do_prime(det) @staticmethod def def_abs_out_pos( @@ -1148,14 +1162,20 @@ def set_mot_r_step_for_scan(scn_cfg): @staticmethod def set_cam_mode(cam, stage="pre-scan"): if stage == "pre-scan": - yield from abs_set(cam.cam.image_mode, 0, wait=True) - yield from abs_set(cam.cam.trigger_mode, 4, wait=True) + while CAM_RD_CFG[cam.cam.model.value]["image_mode"][cam.cam.image_mode.value] != "Fixed": + yield from abs_set(cam.cam.image_mode, "Fixed", timeout=5, settle_time=0.5, wait=True) + while CAM_RD_CFG[cam.cam.model.value]["trigger_mode"][cam.cam.trigger_mode.value] != "External": + yield from abs_set(cam.cam.trigger_mode, "External", timeout=5, settle_time=0.5, wait=True) elif stage == "ref-scan": - yield from abs_set(cam.cam.image_mode, 0, wait=True) - yield from abs_set(cam.cam.trigger_mode, 0, wait=True) + while CAM_RD_CFG[cam.cam.model.value]["image_mode"][cam.cam.image_mode.value] != "Fixed": + yield from abs_set(cam.cam.image_mode, "Fixed", timeout=5, settle_time=0.5, wait=True) + while CAM_RD_CFG[cam.cam.model.value]["trigger_mode"][cam.cam.trigger_mode.value] != "Internal": + yield from abs_set(cam.cam.trigger_mode, "Internal", timeout=5, settle_time=0.5, wait=True) elif stage == "post-scan": - yield from abs_set(cam.cam.image_mode, 1, wait=True) - yield from abs_set(cam.cam.trigger_mode, 0, wait=True) + while CAM_RD_CFG[cam.cam.model.value]["image_mode"][cam.cam.image_mode.value] != "Continuous": + yield from abs_set(cam.cam.image_mode, "Continuous", timeout=5, settle_time=0.5, wait=True) + while CAM_RD_CFG[cam.cam.model.value]["trigger_mode"][cam.cam.trigger_mode.value] != "Internal": + yield from abs_set(cam.cam.trigger_mode, "Internal", timeout=5, settle_time=0.5, wait=True) Zebra = FXIZebra( @@ -1165,7 +1185,7 @@ def set_cam_mode(cam, stage="pre-scan"): ) tomo_flyer = FXITomoFlyer( - list((Andor,)), + list((MaranaU,)), Zebra, name="tomo_flyer", ) diff --git a/startup/40-scan_pre_define.py b/startup/40-scan_pre_define.py index 7398393..fc0dd15 100644 --- a/startup/40-scan_pre_define.py +++ b/startup/40-scan_pre_define.py @@ -15,25 +15,32 @@ def _move_sample_out(out_x, out_y, out_z, out_r, repeat=1, rot_first_flag=1): z_out = out_z r_out = out_r + r_ini = zps.pi_r.position + for i in range(repeat): if rot_first_flag: - yield from mv(zps.pi_r, r_out) + if np.abs(r_ini - r_out) > 0.02: + yield from mv(zps.pi_r, r_out) yield from mv(zps.sx, x_out, zps.sy, y_out, zps.sz, z_out) else: yield from mv(zps.sx, x_out, zps.sy, y_out, zps.sz, z_out) - yield from mv(zps.pi_r, r_out) + if np.abs(r_ini - r_out) > 0.02: + yield from mv(zps.pi_r, r_out) def _move_sample_in(in_x, in_y, in_z, in_r, repeat=1, trans_first_flag=1): """ move in at absolute position """ + r_ini = zps.pi_r.position for i in range(repeat): if trans_first_flag: yield from mv(zps.sx, in_x, zps.sy, in_y, zps.sz, in_z) - yield from mv(zps.pi_r, in_r) + if np.abs(r_ini - in_r) > 0.02: + yield from mv(zps.pi_r, in_r) else: - yield from mv(zps.pi_r, in_r) + if np.abs(r_ini - in_r) > 0.02: + yield from mv(zps.pi_r, in_r) yield from mv(zps.sx, in_x, zps.sy, in_y, zps.sz, in_z) @@ -49,7 +56,7 @@ def _take_image(detectors, motor, num, stream_name="primary"): def _set_Andor_chunk_size(detectors, chunk_size): for detector in detectors: yield from unstage(detector) - yield from abs_set(Andor.cam.num_images, chunk_size, wait=True) + yield from abs_set(MaranaU.cam.num_images, chunk_size, wait=True) for detector in detectors: yield from stage(detector) @@ -58,7 +65,7 @@ def _take_dark_image( detectors, motor, num=1, chunk_size=1, stream_name="dark", simu=False ): yield from _close_shutter(simu) - original_num_images = yield from rd(Andor.cam.num_images) + original_num_images = yield from rd(MaranaU.cam.num_images) yield from _set_Andor_chunk_size(detectors, chunk_size) yield from _take_image(detectors, motor, num, stream_name=stream_name) yield from _set_Andor_chunk_size(detectors, original_num_images) @@ -80,20 +87,23 @@ def _take_bkg_image( yield from _move_sample_out( out_x, out_y, out_z, out_r, repeat=2, rot_first_flag=rot_first_flag ) - original_num_images = yield from rd(Andor.cam.num_images) + original_num_images = yield from rd(MaranaU.cam.num_images) yield from _set_Andor_chunk_size(detectors, chunk_size) yield from _take_image(detectors, motor, num, stream_name=stream_name) yield from _set_Andor_chunk_size(detectors, original_num_images) def _set_andor_param(exposure_time=0.1, period=0.1, chunk_size=1, binning=[1, 1]): - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, chunk_size) - period_cor = period - yield from mv(Andor.cam.acquire_time, exposure_time) - # yield from abs_set(Andor.cam.acquire_period, period_cor) - Andor.cam.acquire_period.put(period_cor) + yield from mv(MaranaU.cam.acquire, 0) + yield from mv(MaranaU.cam.acquire, 0) + yield from mv(MaranaU.cam.image_mode, 0) + yield from mv(MaranaU.cam.num_images, chunk_size) + period_cor = max(period, exposure_time+0.013) + + yield from mv(MaranaU.cam.acquire_time, exposure_time) + yield from bps.sleep(1) + yield from abs_set(MaranaU.cam.acquire_period, period_cor) + yield from bps.sleep(1) def _xanes_per_step( @@ -104,9 +114,10 @@ def _xanes_per_step( move_clens_flag=1, info_flag=0, stream_name="primary", + mag=None, ): - yield from move_zp_ccd( - eng, move_flag=move_flag, move_clens_flag=move_clens_flag, info_flag=info_flag + yield from move_zp_ccd_TEST( + eng, move_flag=move_flag, move_clens_flag=move_clens_flag, info_flag=info_flag, mag=mag, ) yield from bps.sleep(0.1) if not (type(detectors) == list): @@ -211,7 +222,7 @@ def _take_ref_image( yield from _take_image(cams, [], num, stream_name=stream_name) -def _prime_cam(cam=Andor): +def _prime_cam(cam=MaranaU): yield from abs_set(cam.cam.image_mode, 0, wait=True) yield from abs_set(cam.cam.num_images, 5, wait=True) yield from abs_set(cam.cam.acquire, 1, wait=True) diff --git a/startup/41-scans.py b/startup/41-scans.py index 9055115..abdd285 100644 --- a/startup/41-scans.py +++ b/startup/41-scans.py @@ -73,7 +73,7 @@ def tomo_scan( """ global ZONE_PLATE - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] yield from _set_andor_param( exposure_time=exposure_time, period=exposure_time, chunk_size=20 ) @@ -176,7 +176,7 @@ def tomo_inner_scan(): yield from tomo_inner_scan() print("tomo-scan finishes") - +''' def fly_scan( exposure_time=0.05, start_angle=None, @@ -261,13 +261,13 @@ def fly_scan( motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] #offset_angle = -1 * rs offset_angle = 0 current_rot_angle = zps.pi_r.position target_rot_angle = current_rot_angle + relative_rot_angle _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in motor], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -330,7 +330,7 @@ def fly_inner_scan(): ) # open shutter, tomo_images - true_period = yield from rd(Andor.cam.acquire_period) + true_period = yield from rd(MaranaU.cam.acquire_period) rot_time = np.abs(relative_rot_angle) / np.abs(rs) num_img = int(rot_time / true_period) + 2 @@ -382,14 +382,15 @@ def fly_inner_scan(): yield from mv(flt, 0) uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) print("scan finished") txt = get_scan_parameter(print_flag=0) insert_text(txt) print(txt) return uid +''' -def fly_scan_test( +def fly_scan( exposure_time=0.05, start_angle=None, relative_rot_angle=180, @@ -445,6 +446,12 @@ def fly_scan_test( rs: float, default is 1 rotation speed in unit of deg/sec + take_bkg_img: bool, default is True, + check if need to take background image (without sample) + + take_dark_img: bool, default is True,=True, + check if need to take dark image (shutter closed) + note: string adding note to the scan @@ -455,7 +462,7 @@ def fly_scan_test( """ global ZONE_PLATE - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] offset_angle = -1 * rs current_rot_angle = zps.pi_r.position target_rot_angle = current_rot_angle + relative_rot_angle @@ -486,7 +493,7 @@ def fly_scan_test( _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in motor], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -552,7 +559,7 @@ def fly_inner_scan(): ) # open shutter, tomo_images - true_period = yield from rd(Andor.cam.acquire_period) + true_period = yield from rd(MaranaU.cam.acquire_period) rot_time = np.abs(relative_rot_angle) / np.abs(rs) num_img = int(rot_time / true_period) + 2 @@ -601,7 +608,7 @@ def fly_inner_scan(): yield from mv(flt, 0) uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) print("scan finished") txt = get_scan_parameter(print_flag=0) insert_text(txt) @@ -623,6 +630,7 @@ def xanes_scan2( rot_first_flag=1, simu=False, return_ini=True, + mag=None, md=None, ): """ @@ -668,7 +676,7 @@ def xanes_scan2( """ global ZONE_PLATE - detectors = [Andor, ic3, ic4] + detectors = [MaranaU, ic3, ic4] period = exposure_time if exposure_time >= 0.05 else 0.05 yield from _set_andor_param(exposure_time, period, chunk_size) motor_eng = XEng @@ -707,6 +715,7 @@ def xanes_scan2( "exposure_time": exposure_time, "eng_list": eng_list, "XEng": XEng.position, + "mag":mag, "plan_args": { "eng_list": "eng_list", "exposure_time": exposure_time, @@ -769,6 +778,7 @@ def xanes_inner_scan(): move_clens_flag=0, info_flag=0, stream_name="primary", + mag=mag, ) if len(filters): for filt in filters: @@ -811,7 +821,7 @@ def xanes_inner_scan(): yield from bps.sleep(0.5) yield from xanes_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) txt1 = get_scan_parameter() eng_list = np.round(eng_list, 5) if len(eng_list) > 10: @@ -879,7 +889,7 @@ def xanes_scan( """ global ZONE_PLATE - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] period = exposure_time if exposure_time >= 0.05 else 0.05 yield from _set_andor_param(exposure_time, period, chunk_size) motor_eng = XEng @@ -1055,7 +1065,7 @@ def xanes_scan_img_only( """ global ZONE_PLATE - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] period = exposure_time if exposure_time >= 0.05 else 0.05 yield from _set_andor_param(exposure_time, period, chunk_size) motor_eng = XEng @@ -1164,8 +1174,6 @@ def mv_stage(motor, pos): - - @parameter_annotation_decorator( { "parameters": { @@ -1177,7 +1185,7 @@ def mv_stage(motor, pos): } } ) -def eng_scan_basic( +def _eng_scan_basic( start, stop=None, num=1, detectors=[ic3, ic4], delay_time=1, note="", md=None ): """ @@ -1190,7 +1198,7 @@ def eng_scan_basic( num: int, number of energies - detectors: list, detector list, e.g.[ic3, ic4, Andor] + detectors: list, detector list, e.g.[ic3, ic4, MaranaU] delay_time: float, delay time after moving motors, in sec @@ -1225,7 +1233,7 @@ def eng_scan_basic( # "motors": [motor_x.name], "motors": [motor_x.name, motor_y.name], "XEng": XEng.position, - "plan_name": "eng_scan_basic", + "plan_name": "_eng_scan_basic", "plan_args": { "start": start, "stop": stop, @@ -1376,6 +1384,25 @@ def stop(self, doc): def eng_scan( start, stop=None, num=1, detectors=[], delay_time=1, note="", md=None ): + """ + Scan the energy (XEng) and record signals from the assigned "detectors" + + Input: + ---------- + start: float, energy start in keV + + end: float, energy stop in keV + + num: int, number of energies + + detectors: list, detector list, e.g.[ic3, ic4, MaranaU] + + delay_time: float, delay time after moving motors, in sec + + note: string + + """ + if len(detectors) < 2: detectors = [ic3, ic4] load_cell_force = yield from bps.rd(pzt_cm_loadcell) @@ -1386,11 +1413,11 @@ def eng_scan( load_cell_force=load_cell_force, bender_pos=None ) eng_scan_with_plot = subs_wrapper( - eng_scan_basic( + _eng_scan_basic( start, stop, num, - detectors=[ic3, ic4], + detectors=detectors, delay_time=delay_time, ), [engscan_plot], @@ -1427,12 +1454,12 @@ def grid2D_rel( ): # detectors=[ic3, ic4] global ZONE_PLATE - detectors = [Andor, ic3] - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, 1) + detectors = [MaranaU, ic3] + yield from mv(MaranaU.cam.acquire, 0) + yield from mv(MaranaU.cam.image_mode, 0) + yield from mv(MaranaU.cam.num_images, 1) yield from mv(detectors[0].cam.acquire_time, exposure_time) - yield from mv(Andor.cam.acquire_period, exposure_time) + yield from mv(MaranaU.cam.acquire_period, exposure_time) motor1_ini = motor1.position motor2_ini = motor2.position @@ -1589,7 +1616,7 @@ def delay_scan( Inputs: --------- - detectors: list of dectectors, e.g., [Andor, ic3] + detectors: list of dectectors, e.g., [MaranaU, ic3] motor: list of motors, e.g., zps.sx @@ -1611,7 +1638,7 @@ def delay_scan( """ global ZONE_PLATE - if Andor in detectors: + if MaranaU in detectors: yield from _set_andor_param(exposure_time, period=exposure_time, chunk_size=1) # motor = dcm.th2 @@ -1721,8 +1748,8 @@ def raster_2D_scan( out_y=None, out_z=None, out_r=None, - img_sizeX=2560, - img_sizeY=2160, + img_sizeX=2048, + img_sizeY=2040, pxl=20, simu=False, relative_move_flag=1, @@ -1733,7 +1760,12 @@ def raster_2D_scan( md=None, ): """ - scanning large area by moving samples at different 2D block position, defined by x_range and y_range, only work for Andor camera at full resolution (2160 x 2560) + !!! Note: + + Filters will be inserted at all time, including: img, img_bkg, and img_dark + + + scanning large area by moving samples at different 2D block position, defined by x_range and y_range, only work for MaranaU camera at full resolution (2040 x 2048) for example, set x_range=[-1,1] and y_range=[-2, 2] will totally take 3 x 5 = 15 images and stitch them together @@ -1762,9 +1794,9 @@ def raster_2D_scan( relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - img_sizeX: int, default is 2560, it is the pixel number for Andor camera horizontal + img_sizeX: int, default is 2048, it is the pixel number for MaranaU camera horizontal - img_sizeY: int, default is 2160, it is the pixel number for Andor camera vertical + img_sizeY: int, default is 2040, it is the pixel number for MaranaU camera vertical pxl: float, pixel size, default is 17.2, in unit of nm/pix @@ -1781,7 +1813,7 @@ def raster_2D_scan( """ global ZONE_PLATE motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] yield from _set_andor_param( exposure_time=exposure_time, period=exposure_time, chunk_size=1 ) @@ -1924,8 +1956,8 @@ def raster_2D_scan_filter_bkg( out_y=None, out_z=None, out_r=None, - img_sizeX=2560, - img_sizeY=2160, + img_sizeX=2048, + img_sizeY=2040, pxl=20, simu=False, relative_move_flag=1, @@ -1936,7 +1968,13 @@ def raster_2D_scan_filter_bkg( md=None, ): """ - scanning large area by moving samples at different 2D block position, defined by x_range and y_range, only work for Andor camera at full resolution (2160 x 2560) + + !!! Note: + + Filters will ONLY be inserted when taking background image + + + scanning large area by moving samples at different 2D block position, defined by x_range and y_range, only work for MaranaU camera at full resolution (2040 x 2048) for example, set x_range=[-1,1] and y_range=[-2, 2] will totally take 3 x 5 = 15 images and stitch them together Inputs: @@ -1964,9 +2002,9 @@ def raster_2D_scan_filter_bkg( relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - img_sizeX: int, default is 2560, it is the pixel number for Andor camera horizontal + img_sizeX: int, default is 2048, it is the pixel number for MaranaU camera horizontal - img_sizeY: int, default is 2160, it is the pixel number for Andor camera vertical + img_sizeY: int, default is 2040, it is the pixel number for MaranaU camera vertical pxl: float, pixel size, default is 17.2, in unit of nm/pix @@ -1983,7 +2021,7 @@ def raster_2D_scan_filter_bkg( """ global ZONE_PLATE motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] yield from _set_andor_param( exposure_time=exposure_time, period=exposure_time, chunk_size=1 ) @@ -2123,7 +2161,7 @@ def raster_2D_inner(): print(txt) -def raster_2D_scan2( +def raster_2D_scan_individal_bkg( x_range=[-1, 1], y_range=[-1, 1], exposure_time=0.1, @@ -2131,8 +2169,8 @@ def raster_2D_scan2( out_y=0, out_z=0, out_r=0, - img_sizeX=2560, - img_sizeY=2160, + img_sizeX=2048, + img_sizeY=2040, pxl=17.2, num_bkg=1, simu=False, @@ -2143,7 +2181,7 @@ def raster_2D_scan2( md=None, ): """ - scanning large area by moving samples at different 2D block position, defined by x_range and y_range, only work for Andor camera at full resolution (2160 x 2560) + scanning large area by moving samples at different 2D block position, defined by x_range and y_range, only work for MaranaU camera at full resolution (2040 x 2048) for example, set x_range=[-1,1] and y_range=[-2, 2] will totally take 3 x 5 = 15 images and stitch them together Different from raster_2D_scan that this scan will take backgound image for every movement @@ -2173,9 +2211,9 @@ def raster_2D_scan2( relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - img_sizeX: int, default is 2560, it is the pixel number for Andor camera horizontal + img_sizeX: int, default is 2048, it is the pixel number for MaranaU camera horizontal - img_sizeY: int, default is 2160, it is the pixel number for Andor camera vertical + img_sizeY: int, default is 2040, it is the pixel number for MaranaU camera vertical pxl: float, pixel size, default is 17.2, in unit of nm/pix @@ -2192,7 +2230,7 @@ def raster_2D_scan2( """ global ZONE_PLATE motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] yield from _set_andor_param( exposure_time=exposure_time, period=exposure_time, chunk_size=1 ) @@ -2332,7 +2370,7 @@ def raster_2D_inner(): insert_text(txt) print(txt) - +''' def multipos_2D_xanes_scan( eng_list, x_list, @@ -2392,7 +2430,7 @@ def multipos_2D_xanes_scan( zps.sx, x_list[0], zps.sy, y_list[0], zps.sz, z_list[0], zps.pi_r, r_list[0] ) insert_text("Finished the multipos_2D_xanes_scan") - +''' def multipos_2D_xanes_scan2( eng_list, @@ -2484,10 +2522,10 @@ def multipos_2D_xanes_scan2( global ZONE_PLATE txt = "starting multipos_2D_xanes_scan2:" insert_text(txt) - detectors = [Andor, ic3, ic4] + detectors = [MaranaU, ic3, ic4] period = max(0.05, exposure_time) - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.bin_y, binning[0], Andor.cam.bin_x, binning[1]) + yield from mv(MaranaU.cam.acquire, 0) + yield from mv(MaranaU.cam.bin_y, binning[0], MaranaU.cam.bin_x, binning[1]) yield from _set_andor_param(exposure_time, period=period, chunk_size=chunk_size) eng_ini = XEng.position @@ -2719,7 +2757,7 @@ def multipos_2D_xanes_scan3( global ZONE_PLATE txt = "starting multipos_2D_xanes_scan3" insert_text(txt) - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] period = max(0.05, exposure_time) yield from _set_andor_param(exposure_time, period=period, chunk_size=chunk_size) eng_ini = XEng.position @@ -2869,8 +2907,8 @@ def raster_2D_xanes2( out_y=None, out_z=None, out_r=None, - img_sizeX=2560, - img_sizeY=2160, + img_sizeX=2048, + img_sizeY=2040, pxl=20, simu=False, relative_move_flag=1, @@ -2879,6 +2917,10 @@ def raster_2D_xanes2( md=None, ): + ''' + take 2D-xanes at defined grid position, using "multipos_2D_xanes_scan2" + ''' + motor_x_ini = zps.sx.position motor_y_ini = zps.sy.position motor_z_ini = zps.sz.position @@ -2927,7 +2969,7 @@ def raster_2D_xanes2( ) insert_text("finished raster_2D_xanes2") - +''' def raster_2D_xanes3( eng_list, x_range=[-1, 1], @@ -2937,8 +2979,8 @@ def raster_2D_xanes3( out_y=None, out_z=None, out_r=None, - img_sizeX=2560, - img_sizeY=2160, + img_sizeX=2048, + img_sizeY=2040, pxl=20, simu=False, relative_move_flag=1, @@ -2972,7 +3014,7 @@ def raster_2D_xanes3( ) insert_text("finished raster_2D_xanes3") - +''' """ def repeat_multipos_2D_xanes_scan2(eng_list, x_list, y_list, z_list, r_list, out_x=0, out_y=0, out_z=0, out_r=0, exposure_time=0.2, chunk_size=5, repeat_num=1, sleep_time=60, simu=False, relative_move_flag=1, note='', md=None): @@ -3007,11 +3049,11 @@ def multipos_count( md=None, ): global ZONE_PLATE - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": "zps_sx, zps_sy, zps_sz", "XEng": XEng.position, "ion_chamber": ic3.name, @@ -3055,7 +3097,7 @@ def multipos_count( else: _md["hints"].setdefault("dimensions", dimensions) - @stage_decorator(list([Andor, ic3]) + [zps.sx, zps.sy, zps.sz, zps.pi_r]) + @stage_decorator(list([MaranaU, ic3]) + [zps.sx, zps.sy, zps.sz, zps.pi_r]) @run_decorator(md=_md) def inner_scan(): print("\nshutter closed, taking 10 dark images...") @@ -3087,12 +3129,12 @@ def inner_scan(): z_target = out_z if not (out_z is None) else z_ini r_target = out_r if not (out_r is None) else r_ini yield from trigger_and_read( - list([Andor, ic3]) + [zps.sx, zps.sy, zps.sz, zps.pi_r] + list([MaranaU, ic3]) + [zps.sx, zps.sy, zps.sz, zps.pi_r] ) yield from mv(zps.pi_r, r_target) yield from mv(zps.sx, x_target, zps.sy, y_target, zps.sz, z_target) yield from trigger_and_read( - list([Andor, ic3]) + [zps.sx, zps.sy, zps.sz, zps.pi_r] + list([MaranaU, ic3]) + [zps.sx, zps.sy, zps.sz, zps.pi_r] ) yield from mv(zps.sx, x_ini, zps.sy, y_ini, zps.sz, z_ini) yield from mv(zps.pi_r, r_ini) @@ -3150,10 +3192,10 @@ def xanes_3D( binning=binning, ) yield from bps.sleep(1) - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) export_pdf(1) - +''' def fly_scan_repeat( exposure_time=0.03, start_angle=None, @@ -3245,8 +3287,8 @@ def fly_scan_repeat( if i != repeat - 1: yield from bps.sleep(sleep_time) export_pdf(1) - - +''' +''' def multi_pos_xanes_3D( eng_list, x_list, @@ -3313,7 +3355,7 @@ def multi_pos_xanes_3D( ) print(f"sleep for {sleep_time} sec\n\n\n\n") yield from bps.sleep(sleep_time) - +''' def tomo_mosaic_scan( x_ini, @@ -3347,14 +3389,11 @@ def tomo_mosaic_scan( z_ini = zps.sz.position if x_step_size is None or x_step_size <1: - print('x_step_size should > 1') - return 0 + x_step_size = 1e-3 if y_step_size is None or y_step_size <1: - print('y_step_size should > 1') - return 0 + y_step_size = 1e-3 if z_step_size is None or z_step_size <1: - print('z_step_size should > 1') - return 0 + z_step_size = 1e-3 if x_num_steps is None: x_num_steps = 1 if y_num_steps is None: diff --git a/startup/42-scans_legacy.py b/startup/42-scans_legacy.py index d911c88..c0d1777 100644 --- a/startup/42-scans_legacy.py +++ b/startup/42-scans_legacy.py @@ -75,7 +75,7 @@ def tomo_scan_legacy( """ global ZONE_PLATE - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] yield from _set_andor_param( exposure_time=exposure_time, period=exposure_time, chunk_size=chunk_size ) @@ -211,7 +211,7 @@ def fly_scan_legacy( period of taking images, "period" should >= "exposure_time" chunk_size: int, default setting is 20 - number of images taken for each trigger of Andor camera + number of images taken for each trigger of MaranaU camera out_x: float, default is 0 relative movement of sample in "x" direction using zps.sx to move out sample (in unit of um) @@ -263,13 +263,13 @@ def fly_scan_legacy( motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] offset_angle = -1 * rs current_rot_angle = zps.pi_r.position target_rot_angle = current_rot_angle + relative_rot_angle _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in motor], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -310,8 +310,8 @@ def fly_scan_legacy( else: _md["hints"].setdefault("dimensions", dimensions) - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.bin_y, binning[0], Andor.cam.bin_x, binning[1]) + yield from mv(MaranaU.cam.acquire, 0) + yield from mv(MaranaU.cam.bin_y, binning[0], MaranaU.cam.bin_x, binning[1]) yield from _set_andor_param( exposure_time=exposure_time, period=period, chunk_size=chunk_size ) @@ -367,7 +367,7 @@ def fly_inner_scan(): yield from mv(flt, 0) uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) print("scan finished") txt = get_scan_parameter(print_flag=0) insert_text(txt) @@ -430,7 +430,7 @@ def xanes_scan_legacy( """ global ZONE_PLATE - detectors = [Andor, ic3] + detectors = [MaranaU, ic3] period = exposure_time if exposure_time >= 0.05 else 0.05 yield from _set_andor_param(exposure_time, period, chunk_size) motor_eng = XEng @@ -609,7 +609,7 @@ def xanes_scan2_legacy( """ global ZONE_PLATE - detectors = [Andor, ic3, ic4] + detectors = [MaranaU, ic3, ic4] period = exposure_time if exposure_time >= 0.05 else 0.05 yield from _set_andor_param(exposure_time, period, chunk_size) motor_eng = XEng @@ -728,7 +728,7 @@ def xanes_inner_scan(): yield from _close_shutter(simu=simu) yield from xanes_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) txt1 = get_scan_parameter() eng_list = np.round(eng_list, 5) if len(eng_list) > 10: @@ -855,8 +855,8 @@ def xanes_3D_legacy( ): txt = "start 3D xanes scan, containing following fly_scan:\n" insert_text(txt) - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.bin_y, binning[0], Andor.cam.bin_x, binning[1]) + yield from mv(MaranaU.cam.acquire, 0) + yield from mv(MaranaU.cam.bin_y, binning[0], MaranaU.cam.bin_x, binning[1]) for eng in eng_list: yield from move_zp_ccd(eng, move_flag=1) my_note = note + f"_energy={eng}" @@ -880,7 +880,7 @@ def xanes_3D_legacy( rot_first_flag=rot_first_flag, ) yield from bps.sleep(1) - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) export_pdf(1) @@ -959,7 +959,7 @@ def multi_pos_xanes_3D_legacy( # period of taking images, "period" should >= "exposure_time" # # chunk_size: int, default setting is 20 -# number of images taken for each trigger of Andor camera +# number of images taken for each trigger of MaranaU camera # # out_x: float, default is 0 # relative movement of sample in "x" direction using zps.sx to move out sample (in unit of um) @@ -1007,12 +1007,12 @@ def multi_pos_xanes_3D_legacy( # # motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] # -# detectors = [Andor, ic3] +# detectors = [MaranaU, ic3] # offset_angle = -0.5 * rs # current_rot_angle = zps.pi_r.position # # target_rot_angle = current_rot_angle + relative_rot_angle -# _md = {'detectors': ['Andor'], +# _md = {'detectors': ['MaranaU'], # 'motors': [mot.name for mot in motor], # 'XEng': XEng.position, # 'ion_chamber': ic3.name, @@ -1082,7 +1082,7 @@ def multi_pos_xanes_3D_legacy( # for flt in filters: # yield from mv(flt, 0) # uid = yield from fly_inner_scan() -# yield from mv(Andor.cam.image_mode, 1) +# yield from mv(MaranaU.cam.image_mode, 1) # print('scan finished') # txt = get_scan_parameter(print_flag=0) # insert_text(txt) diff --git a/startup/43-scans_pzt.py b/startup/43-scans_pzt.py index ec0b861..ca5583f 100644 --- a/startup/43-scans_pzt.py +++ b/startup/43-scans_pzt.py @@ -11,7 +11,7 @@ ) def pzt_scan(pzt_motor, start, stop, steps, detectors=[Vout2], sleep_time=1, md=None): """ - scan the pzt_motor (e.g., pzt_dcm_th2), detectors can be any signal or motor (e.g., Andor, dcm.th2) + scan the pzt_motor (e.g., pzt_dcm_th2), detectors can be any signal or motor (e.g., MaranaU, dcm.th2) Inputs: --------- @@ -23,17 +23,17 @@ def pzt_scan(pzt_motor, start, stop, steps, detectors=[Vout2], sleep_time=1, md= steps: int, number of steps - detectors: list of detectors, e.g., [Vout2, Andor, ic3] + detectors: list of detectors, e.g., [Vout2, MaranaU, ic3] sleep time: float, in unit of sec """ - if Andor in detectors: - exposure_time = yield from bps.rd(Andor.cam.acquire_time) - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, 1) - Andor.cam.acquire_period.put(exposure_time) + if MaranaU in detectors: + exposure_time = yield from bps.rd(MaranaU.cam.acquire_time) + yield from mv(MaranaU.cam.acquire, 0) + yield from mv(MaranaU.cam.image_mode, 0) + yield from mv(MaranaU.cam.num_images, 1) + MaranaU.cam.acquire_period.put(exposure_time) motor = pzt_motor.setpos motor_readback = pzt_motor.pos @@ -194,7 +194,7 @@ def pzt_scan_multiple( """ Repeat scanning the pzt (e.g. pzt_dcm_ch2, pzt_dcm_th2), and read the detector outputs. Images and .csv data file will be saved Use as: - e.g., pzt_scan_multiple(pzt_dcm_th2,-4, -2, 3, [Vout2, Andor,ic1, dcm.th2], repeat_num=2, save_file_dir='/home/xf18id/Documents/FXI_commision/DCM_scan/') + e.g., pzt_scan_multiple(pzt_dcm_th2,-4, -2, 3, [Vout2, MaranaU,ic1, dcm.th2], repeat_num=2, save_file_dir='/home/xf18id/Documents/FXI_commision/DCM_scan/') Inputs: --------- @@ -208,7 +208,7 @@ def pzt_scan_multiple( steps: int steps of scanning detectors: list of detector device or signals - e.g., [dcm.th2, Andor, ic3, Vout2] + e.g., [dcm.th2, MaranaU, ic3, Vout2] repeat_num: int repeat scanning for "repeat_num" times save_file_dir: str @@ -250,7 +250,7 @@ def pzt_scan_multiple( for i in range(len(detector_signal)): det = detector_signal[i] - if (det == "Andor") or (det == "detA1"): + if (det == "MaranaU") or (det == "detA1"): det = det + "_stats1_total" det_readout = np.array(list(h.data(det))) col_y_prefix = det @@ -332,7 +332,7 @@ def pzt_energy_scan( eng_list: list or array(float) e.g. [8.9, 9.0] detectors: list of detector device or signals - e.g., [dcm.th2, Andor, ic3, Vout2] + e.g., [dcm.th2, MaranaU, ic3, Vout2] repeat_num: int repeat scanning for "repeat_num" times save_file_dir: str @@ -414,7 +414,7 @@ def pzt_overnight_scan( eng_list: list or array(float) e.g. [8.9, 9.0] detectors: list of detector device or signals - e.g., [dcm.th2, Andor, ic3, Vout2] + e.g., [dcm.th2, MaranaU, ic3, Vout2] repeat_num: int single step: repeat scanning for "repeat_num" times sleep_time: float diff --git a/startup/44-scans_other.py b/startup/44-scans_other.py index a1efbf4..14875fb 100644 --- a/startup/44-scans_other.py +++ b/startup/44-scans_other.py @@ -5,7 +5,7 @@ def test_test(): - yield from count([Andor], 2) + yield from count([MaranaU], 2) h = db[-1] print(h.start["scan_id"]) @@ -29,7 +29,7 @@ def test_scan( md=None, ): """ - Take multiple images (Andor camera) + Take multiple images (MaranaU camera) Input: ------------ @@ -50,7 +50,7 @@ def test_scan( yield from _set_andor_param(exposure_time, period, 1) - detectors = [Andor] + detectors = [MaranaU] motors = [zps.sx, zps.sy, zps.sz, zps.pi_r] motor_x_ini = zps.sx.position @@ -80,7 +80,7 @@ def test_scan( ''' _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "XEng": XEng.position, "plan_args": { "exposure_time": exposure_time, @@ -170,7 +170,7 @@ def inner_scan(): # yield from abs_set(shutter_open, 1, wait=True) uid = yield from inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) yield from _close_shutter(simu=simu) txt = get_scan_parameter() insert_text(txt) @@ -195,7 +195,7 @@ def test_scan2( md=None, ): """ - Take multiple images (Andor camera) + Take multiple images (MaranaU camera) Input: ------------ @@ -214,7 +214,7 @@ def test_scan2( yield from _set_andor_param(exposure_time, period_time, 1) - detectors = [Andor] + detectors = [MaranaU] motor_x_ini = zps.sx.position motor_y_ini = zps.sy.position motor_z_ini = zps.sz.position @@ -235,7 +235,7 @@ def test_scan2( motors = [zps.sx, zps.sy, zps.sz, zps.pi_r] _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in motors], "XEng": XEng.position, "plan_args": { @@ -300,7 +300,7 @@ def inner_scan(): ) uid = yield from inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) #yield from _close_shutter(simu=simu) txt = get_scan_parameter() insert_text(txt) @@ -341,7 +341,7 @@ def z_scan( out_y: float, relative amount to move sample out for zps.sy - chunk_size: int, number of images per each subscan (for Andor camera) + chunk_size: int, number of images per each subscan (for MaranaU camera) exposure_time: float, exposure time for each image @@ -349,7 +349,7 @@ def z_scan( """ - detectors = [Andor] + detectors = [MaranaU] motor = [zps.sx, zps.sy, zps.sz, zps.sz, zp.z] x_ini = zps.sx.position @@ -383,7 +383,7 @@ def z_scan( zp_start = zp_ini + start zp_stop = zp_ini + stop ''' - # detectors = [Andor] + # detectors = [MaranaU] y_ini = zps.sy.position # sample y position (initial) y_out = ( y_ini + out_y if not (out_y is None) else y_ini @@ -461,7 +461,7 @@ def z_inner_scan(): # yield from abs_set(shutter_open, 1, wait=True) yield from z_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) yield from _close_shutter(simu=False) txt = get_scan_parameter() insert_text(txt) @@ -497,7 +497,7 @@ def z_scan2( out_y: float, relative amount to move sample out for zps.sy - chunk_size: int, number of images per each subscan (for Andor camera) + chunk_size: int, number of images per each subscan (for MaranaU camera) exposure_time: float, exposure time for each image @@ -505,12 +505,12 @@ def z_scan2( """ - detectors = [Andor] + detectors = [MaranaU] motor = [zps.sx, zps.sy, zps.sz, zps.sz, zp.z] zp_ini = zp.z.position # zp.z intial position zp_start = zp_ini + start zp_stop = zp_ini + stop - # detectors = [Andor] + # detectors = [MaranaU] y_ini = zps.sy.position # sample y position (initial) y_out = ( y_ini + out_y if not (out_y is None) else y_ini @@ -584,7 +584,7 @@ def z_inner_scan(): # yield from abs_set(shutter_open, 1, wait=True) yield from z_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) yield from _close_shutter(simu=False) txt = get_scan_parameter() insert_text(txt) @@ -619,7 +619,7 @@ def z_scan3( out_y: float, relative amount to move sample out for zps.sy - chunk_size: int, number of images per each subscan (for Andor camera) + chunk_size: int, number of images per each subscan (for MaranaU camera) exposure_time: float, exposure time for each image @@ -627,10 +627,10 @@ def z_scan3( """ - detectors = [Andor] + detectors = [MaranaU] motor = [zps.sx, zps.sy, zps.sz, zps.sz, zp.z] - # detectors = [Andor] + # detectors = [MaranaU] y_ini = zps.sy.position # sample y position (initial) y_out = ( y_ini + out_y if not (out_y is None) else y_ini diff --git a/startup/46-zebra_flyer.py b/startup/46-zebra_flyer.py index ffe4a64..efec4bc 100644 --- a/startup/46-zebra_flyer.py +++ b/startup/46-zebra_flyer.py @@ -23,7 +23,7 @@ def tomo_zfly( md=None, simu=False, sleep=0, - cam=Andor, + cam=MaranaU, flyer=tomo_flyer, ): """_summary_ @@ -51,7 +51,7 @@ def tomo_zfly( note (str, optional): _description_. Defaults to "". md (dict, optional): _description_. Defaults to None. simu (bool, optional): _description_. Defaults to False. - cam (ophyd.Device, optional): detector; choose between Andor, Marana, and Oryx. + cam (ophyd.Device, optional): detector; choose between Andor, MaranaU, and Oryx. Raises: ValueError: _description_ @@ -92,7 +92,6 @@ def tomo_zfly( (mot_x_out, mot_y_out, mot_z_out, mot_r_out) = FXITomoFlyer.def_abs_out_pos( out_x, out_y, out_z, out_r, rel_out_flag ) - _md = { "detectors": [flyer.detectors[0].name], "motors": [mot.name for mot in mots], @@ -135,67 +134,7 @@ def tomo_zfly( def inner_fly_plan(): yield from select_filters(flts) - if flyer.scn_mode == "snaked: single file": # scn_mode = 2 - yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") - yield from bps.sleep(1) - yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) - yield from bps.sleep(1) - yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) - yield from _open_shutter_xhx(simu) - for d in flyer.detectors: - try: - d.stage() - except: - d.unstage() - d.stage() - for mot in mots: - mot.stage() - - st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) - st.wait(timeout=10) - - det_stream = short_uid("dets") - for d in flyer.detectors: - yield from bps.trigger(d, group=det_stream) - wait(det_stream) - - yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) - - t0 = ttime.monotonic() - for ii in range(scn_cfg["num_swing"]): - yield from abs_set( - zps.pi_r, - scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], - wait=True, - ) - (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( - scn_cfg["ang_e"], - scn_cfg["ang_s"], - ) - scn_cfg["rot_dir"] *= -1 - if ii == scn_cfg["num_swing"] - 1: - set_and_wait(flyer.encoder.pc.disarm, 1) - - t1 = ttime.monotonic() - while int(flyer.encoder.pc.gated.get()): - if ttime.monotonic() - t1 > 60: - print("Scan finished abnormally. Quit!") - return - yield from bps.sleep(flyer._staging_delay) - # print(ttime.time()) - print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") - st = yield from complete(flyer, wait=True) - st.wait(timeout=10) - yield from collect(flyer) - for d in flyer.detectors: - try: - d.unstage() - except: - print(f"Cannot unstage detector {d.name}") - return - for mot in mots: - mot.unstage() - elif flyer.scn_mode == "standard": # scn_mode = 0 + if flyer.scn_mode == "standard": # scn_mode = 0 print(sleep_plan) for ii in range(scn_cfg["num_swing"]): yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") @@ -316,8 +255,6 @@ def inner_fly_plan(): except: d.unstage() d.stage() - # for mot in mots: - # mot.stage() st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) st.wait(timeout=10) @@ -345,7 +282,6 @@ def inner_fly_plan(): print("Scan finished abnormally. Quit!") return yield from bps.sleep(flyer._staging_delay) - # print(ttime.time()) print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") st = yield from complete(flyer, wait=True) st.wait(timeout=10) @@ -356,8 +292,6 @@ def inner_fly_plan(): except: print(f"Cannot unstage detector {d.name}") return None - # for mot in mots: - # mot.unstage() if scn_cfg["num_swing"] > 1: (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( @@ -413,11 +347,70 @@ def inner_fly_plan(): repeat=2, ) yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") + elif flyer.scn_mode == "snaked: single file": # scn_mode = 2 + yield from FXITomoFlyer.set_cam_mode(flyer.detectors[0], stage="pre-scan") + yield from bps.sleep(1) + yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + yield from bps.sleep(1) + yield from FXITomoFlyer.set_mot_r_step_for_scan(scn_cfg) + yield from _open_shutter_xhx(simu) + for d in flyer.detectors: + try: + d.stage() + except: + d.unstage() + d.stage() + for mot in mots: + mot.stage() + + st = yield from kickoff(flyer, wait=True, scn_cfg=scn_cfg) + st.wait(timeout=10) + + det_stream = short_uid("dets") + for d in flyer.detectors: + yield from bps.trigger(d, group=det_stream) + wait(det_stream) + + yield from abs_set(flyer.encoder.pc.arm, 1, wait=True) + + t0 = ttime.monotonic() + for ii in range(scn_cfg["num_swing"]): + yield from abs_set( + zps.pi_r, + scn_cfg["ang_e"] + scn_cfg["rot_dir"] * scn_cfg["taxi_dist"], + wait=True, + ) + (scn_cfg["ang_s"], scn_cfg["ang_e"]) = ( + scn_cfg["ang_e"], + scn_cfg["ang_s"], + ) + scn_cfg["rot_dir"] *= -1 + if ii == scn_cfg["num_swing"] - 1: + set_and_wait(flyer.encoder.pc.disarm, 1) + + t1 = ttime.monotonic() + while int(flyer.encoder.pc.gated.get()): + if ttime.monotonic() - t1 > 60: + print("Scan finished abnormally. Quit!") + return + yield from bps.sleep(flyer._staging_delay) + # print(ttime.time()) + print(f"Scan # {ii} takes {ttime.monotonic() - t0} seconds.") + st = yield from complete(flyer, wait=True) + st.wait(timeout=10) + yield from collect(flyer) + for d in flyer.detectors: + try: + d.unstage() + except: + print(f"Cannot unstage detector {d.name}") + return + for mot in mots: + mot.unstage() yield from select_filters([]) yield from inner_fly_plan() print("scan finished") - # return uid def tomo_zfly_repeat( @@ -441,11 +434,11 @@ def tomo_zfly_repeat( simu=False, sleep=0, repeat=1, - cam=Andor, + cam=MaranaU, flyer=tomo_flyer, ): for ii in range(repeat): - yield from tomo_zfly(scn_mode=0, + yield from tomo_zfly(scn_mode=scn_mode, exp_t=exp_t, acq_p=acq_p, ang_s=ang_s, @@ -495,7 +488,7 @@ def tomo_grid_zfly( md=None, sleep=0, simu=False, - cam=Andor, + cam=MaranaU, flyer=tomo_flyer, ): """_summary_ @@ -526,7 +519,7 @@ def tomo_grid_zfly( note (str, optional): _description_. Defaults to "". md (dict, optional): _description_. Defaults to None. simu (bool, optional): _description_. Defaults to False. - cam (ophyd.Device, optional): detector; choose between Andor, Marana, and Oryx. + cam (ophyd.Device, optional): detector; choose between Andor, MaranaU, and Oryx. Raises: ValueError: _description_ @@ -880,7 +873,7 @@ def tomo_grid_zfly2( md=None, sleep=0, simu=False, - cam=Andor, + cam=MaranaU, flyer=tomo_flyer, ): """_summary_ @@ -911,7 +904,7 @@ def tomo_grid_zfly2( note (str, optional): _description_. Defaults to "". md (dict, optional): _description_. Defaults to None. simu (bool, optional): _description_. Defaults to False. - cam (ophyd.Device, optional): detector; choose between Andor, Marana, and Oryx. + cam (ophyd.Device, optional): detector; choose between Andor, MaranaU, and Oryx. Raises: ValueError: _description_ @@ -1083,4 +1076,4 @@ def prep_grid_dic(pos, mots=[zps.sx, zps.sy, zps.sz]): for z in range(pos["x"][0], pos["z"][1], pos["z"][2]): pos.append([x, y, z]) """ grid_nodes["pos"] = pos - return grid_nodes \ No newline at end of file + return grid_nodes diff --git a/startup/80-load_scan.py b/startup/80-load_scan.py index 74deff8..0225e5d 100644 --- a/startup/80-load_scan.py +++ b/startup/80-load_scan.py @@ -66,7 +66,7 @@ def write_lakeshore_to_file(h, fname): break -def export_scan(scan_id, scan_id_end=None, binning=4, date_end_by=None, fpath=None): +def export_scan(scan_id, scan_id_end=None, binning=4, date_end_by=None, fpath=None, reverse=False): """ e.g. load_scan([0001, 0002]) """ @@ -75,7 +75,7 @@ def export_scan(scan_id, scan_id_end=None, binning=4, date_end_by=None, fpath=No scan_id = [scan_id] for item in scan_id: try: - custom_export(int(item), binning, date_end_by=date_end_by, fpath=fpath) + custom_export(int(item), binning, date_end_by=date_end_by, fpath=fpath, reverse=reverse) db.reg.clear_process_cache() except Exception as err: print(f'fail to export {item}') @@ -84,20 +84,20 @@ def export_scan(scan_id, scan_id_end=None, binning=4, date_end_by=None, fpath=No for i in range(scan_id, scan_id_end + 1): try: # export_single_scan(int(i), binning) - custom_export(int(i), binning, date_end_by=date_end_by, fpath=fpath) + custom_export(int(i), binning, date_end_by=date_end_by, fpath=fpath, reverse=reverse) db.reg.clear_process_cache() except Exception as err: print(f'fail to export {i}') print(err) -def custom_export(scan_id, binning=4, date_end_by=None, fpath=None): +def custom_export(scan_id, binning=4, date_end_by=None, fpath=None, reverse=False): """ date_end_by: string, e.g., '2020-01-20' """ tmp = list(db(scan_id=scan_id)) n = len(tmp) if date_end_by is None: - export_single_scan(scan_id, binning) + export_single_scan(scan_id, binning, reverse=reverse) else: for sid in tmp: uid = sid.start["uid"] @@ -105,11 +105,11 @@ def custom_export(scan_id, binning=4, date_end_by=None, fpath=None): ts = pd.to_datetime(timestamp, unit="s").tz_localize("US/Eastern") date_end = pd.Timestamp(date_end_by,).tz_localize('US/Eastern') if ts < date_end: - export_single_scan(uid, binning) + export_single_scan(uid, binning, reverse=reverse) break -def export_single_scan(scan_id=-1, binning=4, fpath=None): +def export_single_scan(scan_id=-1, binning=4, fpath=None, reverse=False): import datetime h = db[scan_id] scan_id = h.start["scan_id"] @@ -179,10 +179,10 @@ def export_single_scan(scan_id=-1, binning=4, fpath=None): export_grid2D_rel(h, fpath) elif scan_type == "raster_2D": print("exporting raster_2D: #{}".format(scan_id)) - export_raster_2D(h, binning) + export_raster_2D(h, binning, reverse=reverse) elif scan_type == "raster_2D_2": print("exporting raster_2D_2: #{}".format(scan_id)) - export_raster_2D(h, binning, fpath) + export_raster_2D(h, binning, fpath, reverse=reverse) elif scan_type == "count" or scan_type == "delay_count": print("exporting count: #{}".format(scan_id)) export_count_img(h, fpath) @@ -265,6 +265,7 @@ def export_fly_scan(h, fpath=None): y_pos = h.table("baseline")["zps_sy"][1] z_pos = h.table("baseline")["zps_sz"][1] r_pos = h.table("baseline")["zps_pi_r"][1] + relative_rot_angle = h.start['plan_args']['relative_rot_angle'] zp_z_pos = h.table("baseline")["zp_z"][1] DetU_z_pos = h.table("baseline")["DetU_z"][1] M = (DetU_z_pos / zp_z_pos - 1) * 10.0 @@ -272,6 +273,8 @@ def export_fly_scan(h, fpath=None): x_eng = h.start["XEng"] img_angle = get_fly_scan_angle(uid) + id_stop = find_nearest(img_angle, img_angle[0]+relative_rot_angle-1) + img_tomo = np.array(list(h.data("Andor_image", stream_name="primary")))[0] s = img_tomo.shape @@ -287,6 +290,9 @@ def export_fly_scan(h, fpath=None): img_dark_avg = np.median(img_dark, axis=0, keepdims=True) img_bkg_avg = np.median(img_bkg, axis=0, keepdims=True) + img_tomo = img_tomo[:id_stop] + img_angle = img_angle[:id_stop] + fname = fpath + scan_type + "_id_" + str(scan_id) + ".h5" with h5py.File(fname, "w") as hf: @@ -922,7 +928,7 @@ def export_test_scan2(h, fpath=None): def export_count_img(h, fpath=None): """ - load images (e.g. RE(count([Andor], 10)) ) and save to .h5 file + load images (e.g. RE(count([MaranaU], 10)) ) and save to .h5 file """ if fpath is None: fpath = "./" @@ -976,7 +982,7 @@ def export_delay_scan(h, fpath=None): DetU_z_pos = h.table("baseline")["DetU_z"][1] M = (DetU_z_pos / zp_z_pos - 1) * 10.0 pxl_sz = 6500.0 / M - if det == "detA1" or det == "Andor": + if det == "detA1" or det == "MaranaU": img = get_img(h, det) fname = fpath + scan_type + "_id_" + str(scan_id) + ".h5" with h5py.File(fname, "w") as hf: @@ -1196,7 +1202,7 @@ def export_raster_2D_2(h, binning=4, fpath=None): print(f"fails to write lakeshore info into {fn_h5_save}") -def export_raster_2D(h, binning=4, fpath=None): +def export_raster_2D(h, binning=4, fpath=None, reverse=False): import tifffile if fpath is None: @@ -1226,6 +1232,12 @@ def export_raster_2D(h, binning=4, fpath=None): img_raw = np.squeeze(np.array(list(h.data("Andor_image")))) img_dark_avg = np.mean(img_raw[:num_dark], axis=0, keepdims=True) img_bkg_avg = np.mean(img_raw[-num_bkg:], axis=0, keepdims=True) + + if reverse: + img_raw = img_raw[:, ::-1, ::-1] + img_dark_avg = img_dark_avg[:, ::-1, ::-1] + img_bkg_avg = img_bkg_avg[:, ::-1, ::-1] + img = img_raw[num_dark:-num_bkg] s = img.shape img = (img - img_dark_avg) / (img_bkg_avg - img_dark_avg) diff --git a/startup/91-functions.py b/startup/91-functions.py index 26b7e31..7889f1e 100644 --- a/startup/91-functions.py +++ b/startup/91-functions.py @@ -131,7 +131,7 @@ def trans_calib(): -def record_calib_pos_new(n): +def record_calib_pos_new(n, record_th2_only=False): global GLOBAL_MAG, CALIBER CALIBER[f"chi2_pos{n}"] = dcm.chi2.position @@ -149,9 +149,19 @@ def record_calib_pos_new(n): CALIBER[f"aper_y_pos{n}"] = aper.y.position #CALIBER[f"txm_x_pos{n}"] = zps.pi_x.position - mag = (DetU.z.position / zp.z.position - 1) * GLOBAL_VLM_MAG - CALIBER[f"mag{n}"] = np.round(mag * 100) / 100.0 - GLOBAL_MAG = CALIBER[f"mag{n}"] + if record_th2_only: + calib_mag = GLOBAL_MAG + keys = list(CALIBER.keys()) + if len(keys): + for k in keys: + if 'mag' in k: + calib_mag = CALIBER[k] + break + CALIBER[f"mag{n}"] = calib_mag + else: + mag = (DetU.z.position / zp.z.position - 1) * GLOBAL_VLM_MAG + CALIBER[f"mag{n}"] = np.round(mag * 100) / 100.0 + GLOBAL_MAG = CALIBER[f"mag{n}"] tmp = {} for k in CALIBER.keys(): @@ -807,7 +817,7 @@ def show_global_para(): for k in CALIBER.keys(): if "mag" in k: print(f"{k} = {CALIBER[k]} X") - print(f"\nFor Andor camera, current pixel size = {6500./GLOBAL_MAG:3.1f} nm") + print(f"\nFor MaranaU camera, current pixel size = {6500./GLOBAL_MAG:3.1f} nm") print("\nChange parameters if necessary.\n\n") @@ -1246,12 +1256,12 @@ def plot_ic(scan_id=[-1], ics=[]): def plot2dsum(scan_id=-1, fn="Det_Image", save_flag=0): """ - valid only if the scan using Andor or detA1 camera + valid only if the scan using MaranaU or detA1 camera """ h = db[scan_id] if scan_id == -1: scan_id = h.start["scan_id"] - if "Andor" in h.start["detectors"]: + if "MaranaU" in h.start["detectors"]: det = "Andor_image" find_areaDet = 1 elif "detA1" in h.start["detectors"]: @@ -1316,7 +1326,7 @@ def plot1d(scan_id=-1, detectors=[], plot_time_stamp=0, return_flag=0): y_sig = {} for i in range(n): det_name = detectors[i] - if det_name == "detA1" or det_name == "Andor": + if det_name == "detA1" or det_name == "MaranaU": det_name = det_name + "_stats1_total" y = np.abs(np.array(list(h.data(det_name)))) title_txt = f"scan#{scan_id}: {det_name}" @@ -1423,7 +1433,7 @@ def print_baseline_list(): i += 1 -def get_img(h, det="Andor", sli=[]): +def get_img(h, det="MaranaU", sli=[]): "Take in a Header and return a numpy array of detA1 image(s)." det_name = f"{det}_image" if len(sli) == 2: @@ -1515,7 +1525,7 @@ def get_scan_file_name(scan_id): fpath_root = res_doc["root"] fpath_relative = res_doc["resource_path"] fpath = fpath_root + "/" + fpath_relative - fpath_remote = "/nsls2/xf18id1/backup/DATA/Andor/" + fpath_relative + fpath_remote = "/nsls2/xf18id1/backup/DATA/MaranaU/" + fpath_relative return print(f"local path: {fpath}\nremote path: {fpath_remote}") diff --git a/startup/98-user_scan.py b/startup/98-user_scan.py index 3797a17..273a838 100644 --- a/startup/98-user_scan.py +++ b/startup/98-user_scan.py @@ -270,13 +270,13 @@ def user_fly_scan( motor_r_ini = zps.pi_r.position motor = [zps.sx, zps.sy, zps.sz, zps.pi_r, zps.pi_x] - dets = [Andor, ic3] + dets = [MaranaU, ic3] taxi_ang = -2.0 * rs cur_rot_ang = zps.pi_r.position # tgt_rot_ang = cur_rot_ang + rel_rot_ang _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in motor], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -455,7 +455,7 @@ def mosaic_fly_scan( except: count += 1 RE.abort() - Andor.unstage() + MaranaU.unstage() print("sleeping for 30 sec") RE(bps.sleep(30)) txt = f"Redo scan at x={x_list[i]}, y={y_list[i]}, z={z_list[i]} for {count} times" @@ -538,7 +538,7 @@ def _open_shutter_xhx(simu=False): raise Exception("fails to open shutter") break yield from abs_set(shutter_open, 1, wait=True) - print(f"try opening {i} time(s) ...") + print(f"try opening shutter {i} time(s) ...") yield from bps.sleep(1) i += 1 reading = yield from bps.rd(shutter_status) @@ -562,7 +562,7 @@ def _close_shutter_xhx(simu=False): yield from abs_set(shutter_close, 1, wait=True) yield from bps.sleep(1) i += 1 - print(f"try closing {i} time(s) ...") + print(f"try closing shutter {i} time(s) ...") reading = yield from bps.rd(shutter_status) @@ -610,7 +610,7 @@ def _xanes_3D_xh( enable_z=enable_z, ) yield from bps.sleep(1) - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) def _multi_pos_xanes_3D_xh( @@ -775,7 +775,7 @@ def _xanes_3D_zebra_xh( note="", bin_fac=1, flts=[], - cam=Andor, + cam=MaranaU, flyer=tomo_flyer, ): for eng in eng_list: @@ -835,7 +835,7 @@ def _multi_pos_xanes_3D_zebra_xh( flts=[], repeat=1, ref_flat_scan=False, - cam=Andor, + cam=MaranaU, flyer=tomo_flyer, ): yield from select_filters(flts) @@ -1057,12 +1057,12 @@ def _multi_pos_xanes_2D_xh( yield from select_filters(flts) # yield from _bin_cam(binning) - detectors = [Andor, ic3, ic4] + detectors = [MaranaU, ic3, ic4] # print(f"{exposure_time=}") # period = yield from _exp_t_sanity_check(exposure_time, binning=binning) # print('444:', period) period = exposure_time - yield from mv(Andor.cam.acquire, 0) + yield from mv(MaranaU.cam.acquire, 0) yield from _set_andor_param(exposure_time, period=period, chunk_size=chunk_size) eng_ini = XEng.position @@ -1358,7 +1358,7 @@ def _exp_t_sanity_check(exp_t, binning=None): return period -def _bin_cam(binning, cam=Andor): +def _bin_cam(binning, cam=MaranaU): if cam.binning.value != binning: yield from abs_set(cam.cam.acquire, 0, wait=True) if binning is None: @@ -1463,7 +1463,7 @@ def _move_sample_in_xhx( def _take_dark_image_xhx( - detectors, motor, num=1, chunk_size=1, stream_name="dark", simu=False, cam=Andor + detectors, motor, num=1, chunk_size=1, stream_name="dark", simu=False, cam=MaranaU ): yield from _close_shutter_xhx(simu) original_num_images = yield from rd(cam.cam.num_images) @@ -1485,7 +1485,7 @@ def _take_bkg_image_xhx( stream_name="flat", simu=False, enable_z=False, - cam=Andor, + cam=MaranaU, ): yield from _move_sample_out_xhx( out_x, @@ -1511,7 +1511,7 @@ def _set_Andor_chunk_size_xhx(detectors, chunk_size, cam): def _set_andor_param_xhx( - exposure_time=0.1, period=0.1, chunk_size=1, binning=[1, 1], cam=Andor + exposure_time=0.1, period=0.1, chunk_size=1, binning=[1, 1], cam=MaranaU ): yield from abs_set(cam.cam.acquire, 0, wait=True) yield from abs_set(cam.cam.image_mode, 0, wait=True) @@ -1542,7 +1542,7 @@ def multi_edge_xanes_zebra_legacy( sleep=0, repeat=None, ref_flat_scan=False, - cam=Andor, + cam=MaranaU, flyer=tomo_flyer ): yield from mv(cam.cam.acquire, 0) @@ -1574,7 +1574,7 @@ def multi_edge_xanes_zebra_legacy( print("use default exposure time 0.05s") for key in acq_p.keys(): if elem.split("_")[0] == key.split("_")[0]: - yield from mv(Andor.cam.acquire_time, exposure) + yield from mv(MaranaU.cam.acquire_time, exposure) yield from bps.sleep(2) break eng_list = _mk_eng_list(elem, bulk=False) @@ -1606,9 +1606,9 @@ def multi_edge_xanes_zebra_legacy( bin_fac = 1 bin_fac = yield from _bin_cam(bin_fac) - yield from abs_set(Andor.cam.image_mode, 0, wait=True) - yield from abs_set(Andor.cam.num_images, 5, wait=True) - yield from abs_set(Andor.cam.acquire, 1, wait=False) + yield from abs_set(MaranaU.cam.image_mode, 0, wait=True) + yield from abs_set(MaranaU.cam.num_images, 5, wait=True) + yield from abs_set(MaranaU.cam.acquire, 1, wait=False) x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) for elem in elems: @@ -1695,7 +1695,7 @@ def multi_edge_xanes_zebra( settle_time=0, repeat=None, ref_flat_scan=False, - cam=Andor, + cam=MaranaU, flyer=tomo_flyer ): yield from mv(cam.cam.acquire, 0) @@ -1815,7 +1815,7 @@ def multi_edge_xanes( ref_flat_scan=False, enable_z=True, ): - yield from mv(Andor.cam.acquire, 0) + yield from mv(MaranaU.cam.acquire, 0) x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) for elem in elements: for key in flts.keys(): @@ -1838,7 +1838,7 @@ def multi_edge_xanes( binning = 0 if int(binning) not in [0, 1, 2, 3, 4]: raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) + yield from mv(MaranaU.binning, binning) yield from _multi_pos_xanes_2D_xh( eng_list, @@ -1865,7 +1865,7 @@ def multi_edge_xanes( binning = 1 if int(binning) not in [0, 1, 2, 3, 4]: raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) + yield from mv(MaranaU.binning, binning) yield from _multi_pos_xanes_3D_xh( eng_list, @@ -2005,7 +2005,7 @@ def multi_edge_xanes2( ref_flat_scan=False, enable_z=True, ): - yield from mv(Andor.cam.acquire, 0) + yield from mv(MaranaU.cam.acquire, 0) if repeat is None: repeat = 1 repeat = int(repeat) @@ -2037,10 +2037,10 @@ def multi_edge_xanes2( print("use default exposure time 0.05s") for key in period_time.keys(): if elem.split("_")[0] == key.split("_")[0]: - yield from mv(Andor.cam.acquire_time, exposure) + yield from mv(MaranaU.cam.acquire_time, exposure) yield from bps.sleep(2) - # yield from mv(Andor.cam.acquire_period, period_time[key]) - # period = yield from rd(Andor.cam.acquire_period) + # yield from mv(MaranaU.cam.acquire_period, period_time[key]) + # period = yield from rd(MaranaU.cam.acquire_period) # period = yield from _exp_t_sanity_check(period, binning=binning) # print(elem, f"{period=}") break @@ -2073,9 +2073,9 @@ def multi_edge_xanes2( binning = 1 binning = yield from _bin_cam(binning) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, 5) - yield from mv(Andor.cam.acquire, 1) + yield from mv(MaranaU.cam.image_mode, 0) + yield from mv(MaranaU.cam.num_images, 5) + yield from mv(MaranaU.cam.acquire, 1) x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) for elem in elements: @@ -2097,11 +2097,11 @@ def multi_edge_xanes2( print(f"{period_time=}\n{key=}") if elem.split("_")[0] == key.split("_")[0]: print(f"{exposure=}") - yield from mv(Andor.cam.acquire_time, exposure) + yield from mv(MaranaU.cam.acquire_time, exposure) yield from bps.sleep(2) print(f"{period_time[key]=}") - # yield from mv(Andor.cam.acquire_period, period_time[key]) - # period = yield from rd(Andor.cam.acquire_period) + # yield from mv(MaranaU.cam.acquire_period, period_time[key]) + # period = yield from rd(MaranaU.cam.acquire_period) # period = _exp_t_sanity_check(period, binning) # print(elem, f"{period=}") break @@ -2176,7 +2176,7 @@ def multi_edge_xanes3( ref_flat_scan=False, enable_z=True, ): - yield from abs_set(Andor.cam.acquire, 0) + yield from abs_set(MaranaU.cam.acquire, 0) if repeat is None: repeat = 1 repeat = int(repeat) @@ -2208,10 +2208,10 @@ def multi_edge_xanes3( print("use default exposure time 0.05s") for key in period_time.keys(): if elem.split("_")[0] == key.split("_")[0]: - yield from abs_set(Andor.cam.acquire_time, exposure) + yield from abs_set(MaranaU.cam.acquire_time, exposure) yield from bps.sleep(2) - # yield from mv(Andor.cam.acquire_period, period_time[key]) - # period = yield from rd(Andor.cam.acquire_period) + # yield from mv(MaranaU.cam.acquire_period, period_time[key]) + # period = yield from rd(MaranaU.cam.acquire_period) # period = yield from _exp_t_sanity_check(period, binning=binning) # print(elem, f"{period=}") break @@ -2244,10 +2244,10 @@ def multi_edge_xanes3( binning = 1 binning = yield from _bin_cam(binning) - yield from abs_set(Andor.cam.image_mode, 0) - yield from abs_set(Andor.cam.trigger_mode, 0) - yield from abs_set(Andor.cam.num_images, 5) - yield from abs_set(Andor.cam.acquire, 1) + yield from abs_set(MaranaU.cam.image_mode, 0) + yield from abs_set(MaranaU.cam.trigger_mode, 0) + yield from abs_set(MaranaU.cam.num_images, 5) + yield from abs_set(MaranaU.cam.acquire, 1) x_list, y_list, z_list, r_list = _sort_in_pos(in_pos_list) for elem in elements: @@ -2269,7 +2269,7 @@ def multi_edge_xanes3( print(f"{period_time=}\n{key=}") if elem.split("_")[0] == key.split("_")[0]: print(f"{exposure=}") - yield from abs_set(Andor.cam.acquire_time, exposure) + yield from abs_set(MaranaU.cam.acquire_time, exposure) yield from bps.sleep(2) print(f"{period_time[key]=}") break @@ -2338,7 +2338,7 @@ def fly_scan2( move_to_ini_pos=True, simu=False, enable_z=True, - cam=Andor, + cam=MaranaU, ): """ Inputs: @@ -2427,7 +2427,7 @@ def fly_scan2( cur_rot_ang = zps.pi_r.position tgt_rot_ang = cur_rot_ang + rel_rot_ang _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in motor], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -2571,7 +2571,7 @@ def fly_scan3( noDark=False, noFlat=False, enable_z=True, - cam=Andor, + cam=MaranaU, ): """ Inputs: @@ -2653,7 +2653,7 @@ def fly_scan3( cur_rot_ang = zps.pi_r.position tgt_rot_ang = cur_rot_ang + rel_rot_ang _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in motor], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -2847,14 +2847,14 @@ def rock_scan( False: will really close/open shutter """ - yield from mv(Andor.cam.acquire, 0) + yield from mv(MaranaU.cam.acquire, 0) global ZONE_PLATE if binning is None: binning = 0 if int(binning) not in [0, 1, 2, 3, 4]: raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) + yield from mv(MaranaU.binning, binning) motor_x_ini = zps.sx.position motor_y_ini = zps.sy.position @@ -2880,10 +2880,10 @@ def rock_scan( rev = int(np.ceil(t_span / (2 * rel_rot_ang / rs))) + 1 mots = [zps.pi_r] - dets = [Andor] + dets = [MaranaU] tgt_ang = start_angle + rel_rot_ang _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in mots], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -2927,7 +2927,7 @@ def rock_scan( _md["hints"].setdefault("dimensions", dimensions) yield from _set_andor_param(exposure_time=exp_t, period=period, chunk_size=20) - true_period = yield from rd(Andor.cam.acquire_period) + true_period = yield from rd(MaranaU.cam.acquire_period) num_img = int(t_span / true_period) + 2 yield from _set_rotation_speed(rs=np.abs(rs)) @@ -2955,13 +2955,13 @@ def rock_inner_scan(): # modified based on trigger_and_read tgt, old_tgt = tgt_ang, start_angle - yield from trigger(Andor, group="Andor", wait=False) + yield from trigger(MaranaU, group="Andor", wait=False) for ii in range(rev): yield from mv(zps.pi_r, tgt) old_tgt, tgt = tgt, old_tgt yield from bps.wait(group="Andor") yield from bps.create("primary") - yield from bps.read(Andor) + yield from bps.read(MaranaU) yield from bps.save() # bkg images @@ -2997,7 +2997,7 @@ def rock_inner_scan(): yield from select_filters([]) uid = yield from rock_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) print("scan finished") return uid @@ -3159,7 +3159,7 @@ def mosaic_zfly_scan_xh( md=None, simu=simu, sleep=sleep, - cam=Andor, + cam=MaranaU, flyer=tomo_flyer, ) if ii < int(repeat) - 1: @@ -3585,34 +3585,34 @@ def grid_z_scan( out_y: float, relative amount to move sample out for zps.sy - chunk_size: int, number of images per each subscan (for Andor camera) + chunk_size: int, number of images per each subscan (for MaranaU camera) exposure_time: float, exposure time for each image note: str, experiment notes """ - yield from mv(Andor.cam.acquire, 0) - dets = [Andor] + yield from mv(MaranaU.cam.acquire, 0) + dets = [MaranaU] motor = zp.z z_ini = motor.position # zp.z intial position z_start = z_ini + zstart z_stop = z_ini + zstop zp_x_ini = zp.x.position zp_y_ini = zp.y.position - # dets = [Andor] + # dets = [MaranaU] y_ini = zps.sy.position # sample y position (initial) y_out = ( y_ini + out_y if not (out_y is None) else y_ini ) # sample y position (out-position) x_ini = zps.sx.position x_out = x_ini + out_x if not (out_x is None) else x_ini - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, chunk_size) - yield from mv(Andor.cam.acquire_time, exposure_time) + yield from mv(MaranaU.cam.acquire, 0) + yield from mv(MaranaU.cam.image_mode, 0) + yield from mv(MaranaU.cam.num_images, chunk_size) + yield from mv(MaranaU.cam.acquire_time, exposure_time) period_cor = max(exposure_time + 0.01, 0.05) - # yield from mv(Andor.cam.acquire_period, period_cor) + # yield from mv(MaranaU.cam.acquire_period, period_cor) _md = { "detectors": [det.name for det in dets], @@ -3672,10 +3672,10 @@ def inner_scan(): yield from mv(zps.sy, y_ini) yield from mv(zp.z, z_ini) yield from mv(zp.x, zp_x_ini, zp.y, zp_y_ini, wait=True) - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) uid = yield from inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) yield from _close_shutter_xhx(simu=simu) txt = get_scan_parameter() insert_text(txt) @@ -3879,20 +3879,20 @@ def mosaic_2D_rel_grid_xh( md=None, simu=False, ): - yield from mv(Andor.cam.acquire, 0) - dets = [Andor] + yield from mv(MaranaU.cam.acquire, 0) + dets = [MaranaU] y_ini = zps.sy.position # sample y position (initial) y_out = ( y_ini + out_y if not (out_y is None) else y_ini ) # sample y position (out-position) x_ini = zps.sx.position x_out = x_ini + out_x if not (out_x is None) else x_ini - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, chunk_size) - yield from mv(Andor.cam.acquire_time, exp_time) + yield from mv(MaranaU.cam.acquire, 0) + yield from mv(MaranaU.cam.image_mode, 0) + yield from mv(MaranaU.cam.num_images, chunk_size) + yield from mv(MaranaU.cam.acquire_time, exp_time) period_cor = max(exp_time + 0.01, 0.05) - # yield from mv(Andor.cam.acquire_period, period_cor) + # yield from mv(MaranaU.cam.acquire_period, period_cor) _md = { "detectors": [det.name for det in dets], @@ -3937,20 +3937,20 @@ def mosaic_2D_rel_grid_xh( mot2_snake, ) yield from mv(zps.sx, x_out, zps.sy, y_out, wait=True) - yield from stage(Andor) + yield from stage(MaranaU) yield from bps.sleep(1) yield from trigger_and_read(list(dets) + [mot1, mot2], name="flat") yield from mv(zps.sx, x_ini, zps.sy, y_ini, wait=True) yield from bps.sleep(1) yield from _close_shutter_xhx(simu=simu) - yield from stage(Andor) + yield from stage(MaranaU) yield from bps.sleep(1) yield from trigger_and_read(list(dets) + [mot1, mot2], name="dark") yield from mv(zps.sx, x_ini) yield from mv(zps.sy, y_ini) - yield from unstage(Andor) - yield from mv(Andor.cam.image_mode, 1) + yield from unstage(MaranaU) + yield from mv(MaranaU.cam.image_mode, 1) yield from _close_shutter_xhx(simu=simu) @@ -3974,7 +3974,7 @@ def mosaic_2D_xh( md=None, enable_z=True, ): - yield from mv(Andor.cam.acquire, 0) + yield from mv(MaranaU.cam.acquire, 0) # zp_z_pos = zp.z.position # DetU_z_pos = DetU.z.position # M = (DetU_z_pos / zp_z_pos - 1) * 10.0 @@ -3986,7 +3986,7 @@ def mosaic_2D_xh( motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] else: motor = [zps.sx, zps.sy, zps.pi_r] - dets = [Andor, ic3] + dets = [MaranaU, ic3] yield from _set_andor_param( exposure_time=exposure_time, period=exposure_time, chunk_size=1 ) @@ -4141,7 +4141,7 @@ def dummy_scan( rot_back_velo=30, repeat=1, ): - yield from mv(Andor.cam.acquire, 0) + yield from mv(MaranaU.cam.acquire, 0) motor_x_ini = zps.sx.position motor_y_ini = zps.sy.position motor_z_ini = zps.sz.position @@ -4164,17 +4164,17 @@ def dummy_scan( # motors = [zps.sx, zps.sy, zps.sz, zps.pi_r] motors = [zps.sx, zps.sy, zps.pi_r] - dets = [Andor, ic3] + dets = [MaranaU, ic3] taxi_ang = -2 * rs cur_rot_ang = zps.pi_r.position tgt_rot_ang = cur_rot_ang + rel_rot_ang _md = {"dummy scan": "dummy scan"} - yield from mv(Andor.cam.acquire, 0) + yield from mv(MaranaU.cam.acquire, 0) yield from _set_andor_param(exposure_time=exposure_time, period=period) - yield from mv(Andor.cam.image_mode, 1) - yield from mv(Andor.cam.acquire, 1) + yield from mv(MaranaU.cam.image_mode, 1) + yield from mv(MaranaU.cam.acquire, 1) @stage_decorator(motors) @bpp.monitor_during_decorator([zps.pi_r]) @@ -4226,35 +4226,65 @@ def radiographic_record( simu=False, rot_first_flag=1, relative_move_flag=1, + cam=MaranaU, ): - if binning is None: - binning = 0 - if int(binning) not in [0, 1, 2, 3, 4]: - raise ValueError("binnng must be in [0, 1, 2, 3, 4]") - yield from mv(Andor.binning, binning) - - yield from mv(Andor.cam.acquire, 0) - motor_x_ini = zps.sx.position - motor_y_ini = zps.sy.position - motor_z_ini = zps.sz.position - motor_r_ini = zps.pi_r.position - - if relative_move_flag: - motor_x_out = motor_x_ini + out_x if not (out_x is None) else motor_x_ini - motor_y_out = motor_y_ini + out_y if not (out_y is None) else motor_y_ini - motor_z_out = motor_z_ini + out_z if not (out_z is None) else motor_z_ini - motor_r_out = motor_r_ini + out_r if not (out_r is None) else motor_r_ini - else: - motor_x_out = out_x if not (out_x is None) else motor_x_ini - motor_y_out = out_y if not (out_y is None) else motor_y_ini - motor_z_out = out_z if not (out_z is None) else motor_z_ini - motor_r_out = out_r if not (out_r is None) else motor_r_ini + (motor_x_ini, + motor_y_ini, + motor_z_ini, + motor_r_ini) = FXITomoFlyer.get_txm_cur_pos() + + (motor_x_out, + motor_y_out, + motor_z_out, + motor_r_out) = FXITomoFlyer.def_abs_out_pos(out_x, + out_y, + out_z, + out_r, + relative_move_flag,) + + yield from FXITomoFlyer.stop_det(cam) + binning = yield from FXITomoFlyer.bin_det(cam, binning) + yield from FXITomoFlyer.prime_det(cam) + yield from FXITomoFlyer.set_cam_mode(cam, stage="ref-scan") + + acq_p, acq_min = FXITomoFlyer.check_cam_acq_p(cam, period, binning) + if acq_p > period: + print( + "Acquisition period is too small for the camera. Reset acquisition period to minimum allowed exposure time." + ) + period = acq_p + + if exp_t > (acq_p - acq_min): + exp_t = acq_p - acq_min + + # FXITomoFlyer.get_txm_cur_pos() + # if binning is None: + # binning = 0 + # if int(binning) not in [0, 1, 2, 3, 4]: + # raise ValueError("binnng must be in [0, 1, 2, 3, 4]") + # yield from mv(cam.binning, binning) + + # yield from mv(cam.cam.acquire, 0) + # motor_x_ini = zps.sx.position + # motor_y_ini = zps.sy.position + # motor_z_ini = zps.sz.position + # motor_r_ini = zps.pi_r.position + + # if relative_move_flag: + # motor_x_out = motor_x_ini + out_x if not (out_x is None) else motor_x_ini + # motor_y_out = motor_y_ini + out_y if not (out_y is None) else motor_y_ini + # motor_z_out = motor_z_ini + out_z if not (out_z is None) else motor_z_ini + # motor_r_out = motor_r_ini + out_r if not (out_r is None) else motor_r_ini + # else: + # motor_x_out = out_x if not (out_x is None) else motor_x_ini + # motor_y_out = out_y if not (out_y is None) else motor_y_ini + # motor_z_out = out_z if not (out_z is None) else motor_z_ini + # motor_r_out = out_r if not (out_r is None) else motor_r_ini motors = [zps.sx, zps.sy, zps.sz, zps.pi_r] - - dets = [Andor, ic3] + dets = [cam, ic3] _md = { - "detectors": ["Andor"], + "detectors": [cam.name], # "motors": [mot.name for mot in motors], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -4282,10 +4312,11 @@ def radiographic_record( } _md.update(md or {}) - yield from mv(Andor.cam.acquire, 0) + yield from mv(cam.cam.acquire, 0) yield from _set_andor_param(exposure_time=exp_t, period=period) - yield from mv(Andor.cam.image_mode, 0) + yield from mv(cam.cam.image_mode, 0) + @stage_decorator(list(dets)) @run_decorator(md=_md) def rad_record_inner(): @@ -4293,8 +4324,14 @@ def rad_record_inner(): yield from select_filters(flts) yield from bps.sleep(1) - yield from mv(Andor.cam.num_images, int(t_span / period)) - yield from trigger_and_read([Andor], name="primary") + scn_cfg = {} + scn_cfg["exp_t"] = exp_t + scn_cfg["num_images"] = int(t_span / period) + scn_cfg["num_images"] = int(t_span / period) + yield from FXITomoFlyer.set_cam_step_for_scan(cam, scn_cfg) + + # yield from mv(cam.cam.num_images, int(t_span / period)) + yield from trigger_and_read([cam], name="primary") yield from mv( zps.sx, @@ -4306,8 +4343,8 @@ def rad_record_inner(): zps.pi_r, motor_r_out, ) - yield from mv(Andor.cam.num_images, 20) - yield from trigger_and_read([Andor], name="flat") + yield from mv(cam.cam.num_images, 10) + yield from trigger_and_read([cam], name="flat") yield from _close_shutter_xhx(simu=simu) yield from mv( zps.sx, @@ -4319,11 +4356,12 @@ def rad_record_inner(): zps.pi_r, motor_r_ini, ) - yield from trigger_and_read([Andor], name="dark") - yield from mv(Andor.cam.image_mode, 1) + yield from trigger_and_read([cam], name="dark") + yield from mv(cam.cam.image_mode, 1) yield from select_filters([]) - + yield from rad_record_inner() + yield from FXITomoFlyer.set_cam_mode(cam, stage="post-scan") def multi_pos_radiography_record( @@ -4433,6 +4471,75 @@ def multi_pos_2D_and_3D_xanes( if ii < repeat_num - 1: yield from bps.sleep(sleep_time) +#### function "multi_pos_xanes_3D" is moved from 41-scans.py +def multi_pos_xanes_3D( + eng_list, + x_list, + y_list, + z_list, + r_list, + start_angle=None, + exposure_time=0.05, + relative_rot_angle=185, + period=0.05, + out_x=0, + out_y=0, + out_z=0, + out_r=0, + rs=2, + simu=False, + relative_move_flag=1, + rot_first_flag=1, + note="", + sleep_time=0, + binning=[2, 2], + repeat=1, +): + n = len(x_list) + for rep in range(repeat): + for i in range(n): + if x_list[i] is None: + x_list[i] = zps.sx.position + if y_list[i] is None: + y_list[i] = zps.sy.position + if z_list[i] is None: + z_list[i] = zps.sz.position + if r_list[i] is None: + r_list[i] = zps.pi_r.position + yield from mv( + zps.sx, + x_list[i], + zps.sy, + y_list[i], + zps.sz, + z_list[i], + zps.pi_r, + r_list[i], + ) + txt = f"start xanes_3D at pos1: x={x_list[i]}, y={y_list[i]}, z={z_list[i]}\nrepeat:{rep}" + insert_text(txt) + print(f"{txt}\n##########################\n\n\n\n") + yield from xanes_3D( + eng_list, + exposure_time=exposure_time, + start_angle=start_angle, + relative_rot_angle=relative_rot_angle, + period=period, + out_x=out_x, + out_y=out_y, + out_z=out_z, + out_r=out_r, + rs=rs, + simu=simu, + relative_move_flag=relative_move_flag, + rot_first_flag=rot_first_flag, + note=note, + binning=[2, 2], + ) + print(f"sleep for {sleep_time} sec\n\n\n\n") + yield from bps.sleep(sleep_time) + + def multi_pos_2D_xanes_and_3D_tomo( elements=["Ni"], @@ -4449,7 +4556,7 @@ def multi_pos_2D_xanes_and_3D_tomo( relative_move_flag=0, simu=False, ): - yield from mv(Andor.cam.acquire, 0) + yield from mv(MaranaU.cam.acquire, 0) sam_in_pos_list_2D = np.asarray(sam_in_pos_list_2D) sam_out_pos_list_2D = np.asarray(sam_out_pos_list_2D) sam_in_pos_list_3D = np.asarray(sam_in_pos_list_3D) @@ -4623,7 +4730,7 @@ def z_scan_xh( out_y: float, relative amount to move sample out for zps.sy - chunk_size: int, number of images per each subscan (for Andor camera) + chunk_size: int, number of images per each subscan (for MaranaU camera) exposure_time: float, exposure time for each image @@ -4631,7 +4738,7 @@ def z_scan_xh( """ - detectors = [Andor] + detectors = [MaranaU] motor = [zps.sx, zps.sy, zps.sz, zps.sz, zp.z] x_ini = zps.sx.position @@ -4663,7 +4770,7 @@ def z_scan_xh( zp_start = zp_ini + start zp_stop = zp_ini + stop ''' - # detectors = [Andor] + # detectors = [MaranaU] y_ini = zps.sy.position # sample y position (initial) y_out = ( y_ini + out_y if not (out_y is None) else y_ini @@ -4741,7 +4848,7 @@ def z_inner_scan(): # yield from abs_set(shutter_open, 1, wait=True) yield from z_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) yield from _close_shutter(simu=False) txt = get_scan_parameter() insert_text(txt) @@ -4787,7 +4894,7 @@ def zps_motor_scan_with_Andor( md=None, ): global ZONE_PLATE - dets = [Andor, ic3] + dets = [MaranaU, ic3] # if len(out_x) != len(motors): # out_x = [out_x[0]] * len(motors) @@ -4802,11 +4909,11 @@ def zps_motor_scan_with_Andor( # out_r = [out_r[0]] * len(motors) def _set_andor_param(): - yield from mv(Andor.cam.acquire, 0) - yield from mv(Andor.cam.image_mode, 0) - yield from mv(Andor.cam.num_images, chunk_size) - yield from mv(Andor.cam.acquire_time, exposure_time) - # yield from mv(Andor.cam.acquire_period, exposure_time) + yield from mv(MaranaU.cam.acquire, 0) + yield from mv(MaranaU.cam.image_mode, 0) + yield from mv(MaranaU.cam.num_images, chunk_size) + yield from mv(MaranaU.cam.acquire_time, exposure_time) + # yield from mv(MaranaU.cam.acquire_period, exposure_time) if exposure_time is not None: yield from _set_andor_param() @@ -4972,7 +5079,7 @@ def zps_motor_scan_inner(): yield from _close_shutter(simu) yield from zps_motor_scan_inner() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) print("scan finished") txt = get_scan_parameter() insert_text(txt) @@ -5163,7 +5270,7 @@ def user_fly_scan( period of taking images, "period" should >= "exposure_time" chunk_size: int, default setting is 20 - number of images taken for each trigger of Andor camera + number of images taken for each trigger of MaranaU camera out_x: float, default is 0 relative movement of sample in "x" direction using zps.sx to move out sample (in unit of um) @@ -5211,13 +5318,13 @@ def user_fly_scan( motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - dets = [Andor, ic3] + dets = [MaranaU, ic3] taxi_ang = -0.5 * rs cur_rot_ang = zps.pi_r.position tgt_rot_ang = cur_rot_ang + rel_rot_ang _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in motor], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -5315,7 +5422,7 @@ def fly_inner_scan(): yield from select_filters([]) uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) print("scan finished") txt = get_scan_parameter(print_flag=0) insert_text(txt) @@ -5343,13 +5450,13 @@ def user_fly_only( motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - dets = [Andor, ic3] + dets = [MaranaU, ic3] # taxi_ang = 0 #-0.5 * rs * np.sign(rel_rot_ang) cur_rot_ang = zps.pi_r.position tgt_rot_ang = end_rot_angle _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in motor], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -5398,7 +5505,7 @@ def fly_inner_scan(): yield from trigger_and_read(list(dets) + motor) uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) print("scan finished") # yield from _set_rotation_speed(rs=30) txt = get_scan_parameter(print_flag=0) @@ -5416,7 +5523,7 @@ def user_dark_only(exposure_time=0.1, chunk_size=20, note="", simu=False, md=Non exposure_time: float, in unit of sec chunk_size: int, default setting is 20 - number of images taken for each trigger of Andor camera + number of images taken for each trigger of MaranaU camera note: string adding note to the scan @@ -5428,11 +5535,11 @@ def user_dark_only(exposure_time=0.1, chunk_size=20, note="", simu=False, md=Non """ global ZONE_PLATE period = exposure_time # default to exposure time for backgrounds - dets = [Andor, ic3] + dets = [MaranaU, ic3] motor = [] _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "XEng": XEng.position, "ion_chamber": ic3.name, "plan_args": { @@ -5472,7 +5579,7 @@ def inner_scan(): yield from _take_dark_image(dets, motor, num_dark=1, simu=simu) uid = yield from inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) print("dark finished") txt = get_scan_parameter(print_flag=0) insert_text(txt) @@ -5501,7 +5608,7 @@ def user_bkg_only( exposure_time: float, in unit of sec chunk_size: int, default setting is 20 - number of images taken for each trigger of Andor camera + number of images taken for each trigger of MaranaU camera out_x: float, default is 0 relative movement of sample in "x" direction using zps.sx to move out sample (in unit of um) @@ -5547,11 +5654,11 @@ def user_bkg_only( motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] - dets = [Andor, ic3] + dets = [MaranaU, ic3] cur_rot_ang = zps.pi_r.position _md = { - "detectors": ["Andor"], + "detectors": ["MaranaU"], "motors": [mot.name for mot in motor], "XEng": XEng.position, "ion_chamber": ic3.name, @@ -5615,7 +5722,7 @@ def fly_inner_scan(): ) uid = yield from fly_inner_scan() - yield from mv(Andor.cam.image_mode, 1) + yield from mv(MaranaU.cam.image_mode, 1) print("bkg finished") txt = get_scan_parameter(print_flag=0) insert_text(txt) @@ -5751,7 +5858,7 @@ def trim_points_to_polygon(xyz_list, poly): xyz_list_out.append((x, y, z)) return xyz_list_out - +''' def qingchao_scan( eng_list, x_list1, @@ -5807,7 +5914,7 @@ def qingchao_scan( ) print(f"sleep for {sleep_time} sec ...") yield from bps.sleep(sleep_time) - +''' def scan_change_expo_time( x_range, @@ -5835,7 +5942,7 @@ def scan_change_expo_time( motor_z_ini = zps.sz.position motor_r_ini = zps.pi_r.position - dets = [Andor, ic3] + dets = [MaranaU, ic3] if relative_move_flag: motor_x_out = motor_x_ini + out_x if out_x else motor_x_ini diff --git a/startup/99-umacro.py b/startup/99-umacro.py index e811a0a..1b44264 100644 --- a/startup/99-umacro.py +++ b/startup/99-umacro.py @@ -572,7 +572,7 @@ def test_Andor_stage_unstage(n=500): #Andor.stage() #Andor.unstage() #uid = RE_test(count([Andor], 1))[0] - uid = RE(count([Andor], 5))[0] + uid = RE(count([MaranaU], 5))[0] sid = db[-1].start['scan_id'] uid_list.append(sid) print(f'save {fsave_ts}')