def test_finalize(hw): det = hw.det def plan(): yield from [Msg('null')] def cleanup_plan(): yield from [Msg('read', det)] # wrapper accepts list processed_plan = list(finalize_wrapper(plan(), [Msg('read', det)])) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # or func that returns list def plan(): yield from [Msg('null')] processed_plan = list(finalize_wrapper(plan(), lambda: [Msg('read', det)])) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # or generator instance def plan(): yield from [Msg('null')] processed_plan = list(finalize_wrapper(plan(), cleanup_plan())) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # or generator func def plan(): yield from [Msg('null')] processed_plan = list(finalize_wrapper(plan(), cleanup_plan)) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # decorator accepts generator func processed_plan = list(finalize_decorator(cleanup_plan)(plan)()) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # or func that returns list processed_plan = list(finalize_decorator( lambda: [Msg('read', det)])(plan)()) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # decorator does NOT accept list with pytest.raises(TypeError): list(finalize_decorator([Msg('read', det)])(plan)()) # nor generator instance with pytest.raises(TypeError): list(finalize_decorator(cleanup_plan())(plan)())
def test_sigint_three_hits(RE, hw): motor = hw.motor motor.delay = 0.3 pid = os.getpid() def sim_kill(n=1): for j in range(n): print('KILL') os.kill(pid, signal.SIGINT) lp = RE.loop motor.loop = lp lp.call_later(.02, sim_kill, 3) lp.call_later(.02, sim_kill, 3) lp.call_later(.02, sim_kill, 3) start_time = ttime.time() with pytest.raises(RunEngineInterrupted): RE( finalize_wrapper(abs_set(motor, 1, wait=True), abs_set(motor, 0, wait=True))) end_time = ttime.time() assert end_time - start_time < 0.2 # not enough time for motor to cleanup RE.abort() # now cleanup done_cleanup_time = ttime.time() assert done_cleanup_time - end_time > 0.3
def wiggle_plan(dets, motor, delta, *, md=None): ''' dets, a list of detectors to read, must have det.cam.acquire_time attribute delta, relative range to wiggle move through range in 5 step ''' original_pos = motor.position def kickoff(): # plan for moving st = StatusBase() # move once every second for 2 min for i in range(24): # 24*5 = 120 for loc in np.linspace(-delta / 2, delta / 2, 5): # 5s, RE.loop.call_later(1, lambda v=loc: motor.set(v)) st._finished() return st def inner_plan(): yield from bp.count(dets) def clean_up(): yield from bps.mv(motor, original_pos) timeout = dets[0].cam.acquire_time.value rp = bp.ramp_plan(kickoff(), motor, inner_plan, timeout=timeout, md=md) return (yield from bpp.finalize_wrapper(rp, clean_up()))
def test_finialize_pause(): fail_cmd = 'fail_next' def erroring_plan(): yield Msg(fail_cmd, None) raise RuntimeError('saw this coming') num = 5 cmd = 'echo' plan = finalize_wrapper(erroring_plan(), echo_plan(command=cmd, num=num), pause_for_debug=True) msgs = list() try: EchoRE(plan, msg_list=msgs) except RuntimeError: pass total = num + 2 _verify_msg_seq(msgs, m_len=total, cmd_sq=[fail_cmd, 'pause'] + [cmd] * num, args_sq=[()] * total, kwargs_sq=[{}, { 'defer': False }] + [{}] * num)
def extra_devices_wrapper(plan, extras): hinted_stash = [] def _stage(): for device in extras: for _, component in device._get_components_of_kind(Kind.normal): if component.kind == Kind.hinted: component.kind = Kind.normal hinted_stash.append(component) yield from null() def _unstage(): for component in hinted_stash: component.kind = Kind.hinted yield from null() def _inner_plan(): yield from _stage() return (yield from plan) if len(extras) != 0: return (yield from finalize_wrapper(_inner_plan(), _unstage())) else: return (yield from plan)
def multi_exafs_scan_with_cleanup(detectors, motor, E0, energy_list, time_list, delay_time, waitTime, device_dict, parent): """ Repeat multiple or batch exafs_scan with clean-up""" yield from bpp.finalize_wrapper( multi_exafs_scan(detectors, motor, E0, energy_list, time_list, delay_time, waitTime, device_dict, parent), finalize(parent, device_dict, E0))
def exafs_scan(detectors, motor, E0, energy_list, time_list, delay_time, per_step=delay_per_step, md={}, waitTime=0): """ EXAFS scan with waitTime sleep for autoCounter """ yield from bps.sleep(waitTime) yield from bpp.finalize_wrapper( energy_list_scan(detectors, motor, E0, energy_list, time_list, delay_time, per_step=delay_per_step, md=md), cleanup_energy_scan(motor, E0))
def test_sigint_three_hits(RE, hw): import time motor = hw.motor motor.delay = .5 pid = os.getpid() def sim_kill(n): for j in range(n): time.sleep(.05) os.kill(pid, signal.SIGINT) lp = RE.loop motor.loop = lp def self_sig_int_plan(): threading.Timer(.05, sim_kill, (3, )).start() yield from abs_set(motor, 1, wait=True) start_time = ttime.time() with pytest.raises(RunEngineInterrupted): RE(finalize_wrapper(self_sig_int_plan(), abs_set(motor, 0, wait=True))) end_time = ttime.time() # not enough time for motor to cleanup, but long enough to start assert 0.05 < end_time - start_time < 0.2 RE.abort() # now cleanup done_cleanup_time = ttime.time() # this should be 0.5 (the motor.delay) above, leave sloppy for CI assert 0.3 < done_cleanup_time - end_time < 0.6
def test_sigint_three_hits(RE, hw): motor = hw.motor motor.delay = 0.3 pid = os.getpid() def sim_kill(n=1): for j in range(n): print('KILL') os.kill(pid, signal.SIGINT) lp = RE.loop motor.loop = lp lp.call_later(.02, sim_kill, 3) lp.call_later(.02, sim_kill, 3) lp.call_later(.02, sim_kill, 3) start_time = ttime.time() with pytest.raises(RunEngineInterrupted): RE(finalize_wrapper(abs_set(motor, 1, wait=True), abs_set(motor, 0, wait=True))) end_time = ttime.time() assert end_time - start_time < 0.2 # not enough time for motor to cleanup RE.abort() # now cleanup done_cleanup_time = ttime.time() assert done_cleanup_time - end_time > 0.3
def test_finalizer_closeable(): pre = (j for j in range(18)) post = (j for j in range(18)) plan = finalize_wrapper(pre, post) for j in range(3): next(plan) plan.close()
def configure_counts_wrapper(plan, detectors, count_time): """ Set all devices with a `preset_monitor` to the same value. The original setting is stashed and restored at the end. Parameters ---------- plan : iterable or iterator a generator, list, or similar containing `Msg` objects monitor : float or None If None, the plan passes through unchanged. Yields ------ msg : Msg messages from plan, with 'set' messages inserted """ original_times = {} original_monitor = [] def setup(): if count_time < 0: if detectors != [scalerd]: raise ValueError('count_time can be < 0 only if the scalerd ' 'is only detector used.') else: if scalerd.monitor == 'Time': raise ValueError('count_time can be < 0 only if ' 'scalerd.monitor is not "Time".') original_times[scalerd] = yield from rd(scalerd.preset_monitor) yield from mv(scalerd.preset_monitor, abs(count_time)) elif count_time > 0: for det in detectors: if det == scalerd: original_monitor.append(scalerd.monitor) det.monitor = 'Time' original_times[det] = yield from rd(det.preset_monitor) yield from mv(det.preset_monitor, count_time) else: raise ValueError('count_time cannot be zero.') def reset(): for det, time in original_times.items(): yield from mv(det.preset_monitor, time) if det == scalerd and len(original_monitor) == 1: det.monitor = original_monitor[0] def _inner_plan(): yield from setup() return (yield from plan) if count_time is None: return (yield from plan) else: return (yield from finalize_wrapper(_inner_plan(), reset()))
def count_dets(_dets, _full_md): _count_plan = bp.count(_dets, md=_full_md) _count_plan = bpp.subs_wrapper(_count_plan, LiveTable(_dets)) _count_plan = bpp.finalize_wrapper( _count_plan, bps.abs_set(xpd_configuration['shutter'], XPD_SHUTTER_CONF['close'], wait=True)) yield from bps.abs_set(xpd_configuration['shutter'], XPD_SHUTTER_CONF['open'], wait=True) yield from _count_plan
def interpolation_mono_vernier_duration_scan( mono_grating, energy_req, config, *, ev_bounds=None, urad_bounds=None, duration ): """ Move the mono between two urad points for some duration in seconds. As the mono moves, continually put the interpolated eV position to the energy_req signal. This plan is not safe to inspect because it uses an ophyd subscription with a standalone put. This could be fixed by including the ophyd subscription as a custom run engine message, or by refactoring to include the energy_req subscription as part of the mono grating's stage and unstage. """ # Get the PV values at the start from the config config.recalc() # Interpolate to pick the bounds that were not given if ev_bounds is None and urad_bounds is None: raise ValueError('Either ev_bounds or urad_bounds must be provided') elif urad_bounds is None: urad_bounds = [config.ev_to_urad(ev, recalc=False) for ev in ev_bounds] def update_vernier(value, **kwargs): energy_req.put(config.urad_to_ev(value, recalc=False)) cbid = 0 def sub_and_move(): nonlocal cbid cbid = mono_grating.subscribe(update_vernier) return ( yield from nbp.duration_scan( [], mono_grating, urad_bounds, duration=duration ) ) def cleanup_sub(): yield from bps.null() mono_grating.unsubscribe(cbid) return ( yield from bpp.finalize_wrapper( sub_and_move(), cleanup_sub(), ) )
def calibrate(focus=False): "Run a sequence of 10 foil XANES scans, Fe to Mo, to calibrate the DCM." def main_plan(): dcm = user_ns['dcm'] report(f'Calibrating the {dcm._crystal} monochrmoator', 'bold') yield from calibrate_low_end(mono=dcm._crystal, focus=focus) yield from calibrate_high_end(mono=dcm._crystal, focus=focus) yield from resting_state_plan() def cleanup_plan(): yield from resting_state_plan() yield from finalize_wrapper(main_plan(), cleanup_plan())
def shutter_flash_scan(*args, **kwargs): def cleanup_plan(): yield from bps.mov(shutter, 0) yield from bps.sleep(.2) def collect_plan(detectors, step, pos_cache): motors = step.keys() yield from move_per_step(step, pos_cache) yield from bps.mov(shutter, 1) yield from bps.sleep(.2) yield from trigger_and_read(list(detectors) + list(motors)) yield from bpp.finalize_wrapper(collect_plan(*args, **kwargs), cleanup_plan())
def fast_shutter_wrapper(plan): update_metadata() if USE_FAST_SHUTTER: plan = bpp.pchain( bps.abs_set(fast_shutter.output, FastShutter.OPEN_SHUTTER, settle_time=FastShutter.SETTLE_TIME), plan) plan = bpp.finalize_wrapper( plan, bps.abs_set(fast_shutter.output, FastShutter.CLOSE_SHUTTER, settle_time=FastShutter.SETTLE_TIME)) return (yield from plan)
def daq_interpolation_mono_vernier_duration_scan( mono_grating, energy_req, config, *, ev_bounds=None, urad_bounds=None, duration ): """ Warning: this plan CANNOT be inspected! This should definitely be re-done to use a more bluesky-like interface so that we can inspect the plan! """ daq_control = DaqControl( host='drp-neh-ctl001', platform=2, timeout=1000, ) def daq_and_scan(): yield from bps.null() state = daq_control.getState() if state != 'configured': raise RuntimeError( 'DAQ must be in configured state to run vernier scan! ' f'Currently in {state} state.' ) print('Starting the DAQ') daq_control.setState('running') return (yield from interpolation_mono_vernier_duration_scan( mono_grating, energy_req, config, ev_bounds=ev_bounds, urad_bounds=urad_bounds, duration=duration, ) ) def stop_daq(): yield from bps.null() if daq_control.getState() in ('starting', 'paused', 'running'): print('Stopping the DAQ') daq_control.setState('configured') return ( yield from bpp.finalize_wrapper( daq_and_scan(), stop_daq(), ) )
def delay_scan_with_cleanup(detectors, motor, E0, start, stop, step_size, delay_time, per_step=delay_per_step, md={}): yield from bpp.finalize_wrapper( delay_scan(detectors, motor, E0, start, stop, step_size, delay_time, per_step=delay_per_step, md=md), cleanup_energy_scan(motor, E0))
def test_finialize_success(): suc_cmd = 'it_works' num = 5 cmd = 'echo' plan = finalize_wrapper(single_message_gen(Msg(suc_cmd, None)), echo_plan(command=cmd, num=num)) msgs = list() try: EchoRE(plan, msg_list=msgs) except RuntimeError: pass total = num + 1 _verify_msg_seq(msgs, m_len=total, cmd_sq=[suc_cmd] + [cmd]*num, args_sq=[()]*total, kwargs_sq=[{}]*total)
def test_finialize_success(): suc_cmd = 'it_works' num = 5 cmd = 'echo' plan = finalize_wrapper(single_message_gen(Msg(suc_cmd, None)), echo_plan(command=cmd, num=num)) msgs = list() try: EchoRE(plan, msg_list=msgs) except RuntimeError: pass total = num + 1 _verify_msg_seq(msgs, m_len=total, cmd_sq=[suc_cmd] + [cmd] * num, args_sq=[()] * total, kwargs_sq=[{}] * total)
def test_finalize_runs_after_error(RE, hw): det = hw.det def plan(): yield from [Msg('null')] raise Exception msgs = [] def accumulator(msg): msgs.append(msg) RE.msg_hook = accumulator try: RE(finalize_wrapper(plan(), [Msg('read', det)])) except Exception: pass # swallow the Exception; we are interested in msgs below expected = [Msg('null'), Msg('read', det)] assert msgs == expected
def test_finialize_fail(): fail_cmd = 'fail_next' def erroring_plan(): yield Msg(fail_cmd, None) raise RuntimeError('saw this coming') num = 5 cmd = 'echo' plan = finalize_wrapper(erroring_plan(), echo_plan(command=cmd, num=num)) msgs = list() try: EchoRE(plan, msg_list=msgs) except RuntimeError: pass total = num + 1 _verify_msg_seq(msgs, m_len=total, cmd_sq=[fail_cmd] + [cmd]*num, args_sq=[()]*total, kwargs_sq=[{}]*total)
def __1DScan(self, motor, start, end, npoints, ndaq, record): # Setup the event sequencer for the scan logging.debug("Setting up the sequencer for %s daq points", ndaq) self.__setup_sequencer(ndaq) # Setup the pulse picker if pp.mode.get() == 3: logging.debug("The pulse picker is already in burst mode") else: logging.debug("Setting up the pulse picker for burst mode") pp.burst(wait=True) # Setup the DAQ daq.record = record daq.configure(events=ndaq) bps.configure(daq, events=ndaq) # For plan introspection # Add sequencer, DAQ to detectors for scan dets = [daq, seq] # Log stuff logging.debug("Returning __1DScan with the following parameters:") logging.debug("motor: {}".format(motor)) logging.debug("start: {}".format(start)) logging.debug("end: {}".format(end)) logging.debug("npoints: {}".format(npoints)) logging.debug("ndaq: {}".format(ndaq)) logging.debug("record: {}".format(record)) logging.debug("detectors: {}".format(dets)) # Return the plan scan_plan = scan(dets, motor, start, end, npoints) final_plan = bpp.finalize_wrapper(scan_plan, self.__cleanup_plan()) return final_plan
def calibrate_low_end(mono='111', focus=False): '''Step through the lower 5 elements of the mono calibration procedure.''' BMMuser, shb, dcm_pitch = user_ns['BMMuser'], user_ns['shb'], user_ns[ 'dcm_pitch'] (ok, text) = BMM_clear_to_start() if ok is False: print(error_msg('\n' + text) + bold_msg('Quitting macro....\n')) return (yield from null()) BMM_log_info('Beginning low end calibration macro') def main_plan(): BMMuser.prompt = False datafile = os.path.join(BMMuser.DATA, 'edges%s.ini' % mono) handle = open(datafile, 'w') handle.write('[config]\n') handle.write("mono = %s\n" % mono) if mono == '111': handle.write('DSPACING = 3.13597211\n') else: handle.write('DSPACING = 1.63762644\n') handle.write('thistitle = Si(%s) calibration curve\n' % mono) handle.write( 'reference = Kraft et al, Review of Scientific Instruments 67, 681 (1996)\n' ) handle.write('doi = https://doi.org/10.1063/1.1146657\n\n') handle.write('## found, tabulated, found_angle, dcm_pitch\n') handle.write('[edges]\n') handle.flush() yield from change_edge('Fe', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='fecal', edge='Fe', e0=7112, sample='Fe foil') close_last_plot() handle.write('fe = 11111.11, 7110.75, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Co', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='cocal', edge='Co', e0=7709, sample='Co foil') close_last_plot() handle.write('co = 11111.11, 7708.78, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Ni', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='nical', edge='Ni', e0=8333, sample='Ni foil') close_last_plot() handle.write('ni = 11111.11, 8331.49, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Cu', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='cucal', edge='Cu', e0=8979, sample='Cu foil') close_last_plot() handle.write('cu = 11111.11, 8980.48, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Zn', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='zncal', edge='Zn', e0=9659, sample='Zn foil') close_last_plot() handle.write('zn = 11111.11, 9660.76, 22222.22, %.5f\n' % pitch) handle.flush() handle.close() #yield from shb.close_plan() def cleanup_plan(): yield from resting_state_plan() yield from finalize_wrapper(main_plan(), cleanup_plan()) yield from resting_state_plan() BMM_log_info('Low end calibration macro finished!')
def calibrate_high_end(mono='111', focus=False): '''Step through the upper 5 elements of the mono calibration procedure.''' BMMuser, shb, dcm_pitch = user_ns['BMMuser'], user_ns['shb'], user_ns[ 'dcm_pitch'] (ok, text) = BMM_clear_to_start() if ok is False: print(error_msg('\n' + text) + bold_msg('Quitting macro....\n')) return (yield from null()) BMM_log_info('Beginning high end calibration macro') def main_plan(): BMMuser.prompt = False datafile = os.path.join(BMMuser.DATA, 'edges%s.ini' % mono) handle = open(datafile, 'a') #yield from shb.open_plan() yield from change_edge('Pt', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='ptcal', edge='Pt', e0=11563, sample='Pt foil') close_last_plot() handle.write('pt = 11111.11, 11562.76, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Au', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='aucal', edge='Au', e0=11919, sample='Au foil') close_last_plot() handle.write('au = 11111.11, 11919.70, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Pb', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='pbcal', edge='Pb', e0=13035, sample='Pb foil') close_last_plot() handle.write('pb = 11111.11, 13035.07, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Nb', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='nbcal', edge='Nb', e0=18986, sample='Nb foil') close_last_plot() handle.write('nb = 11111.11, 18982.97, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Mo', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='mocal', edge='Mo', e0=20000, sample='Mo foil') close_last_plot() handle.write('mo = 11111.11, 20000.36, 22222.22, %.5f\n' % pitch) handle.flush() handle.close() #yield from shb.close_plan() def cleanup_plan(): yield from resting_state_plan() yield from finalize_wrapper(main_plan(), cleanup_plan()) yield from resting_state_plan() BMM_log_info('High end calibration macro finished!')
def xanes_plan(erange=[], estep=[], harmonic=1, correct_c2_x=True, correct_c1_r=False, detune=None, acqtime=1., roinum=1, delaytime=0.00, struck=True, fluor=True, samplename='', filename='', shutter=True, align=False, align_at=None, per_step=None): ''' erange (list of floats): energy ranges for XANES in eV, e.g. erange = [7112-50, 7112-20, 7112+50, 7112+120] estep (list of floats): energy step size for each energy range in eV, e.g. estep = [2, 1, 5] harmonic (odd integer): when set to 1, use the highest harmonic achievable automatically. when set to an odd integer, force the XANES scan to use that harmonic correct_c2_x (boolean or float): when True, automatically correct the c2x when False, c2x will not be moved during the XANES scan correct_c1_r (False or float): when False, c1r will not be moved during a XANES scan when set to a float, c1r will be set to that value before a XANES scan but will remain the same during the whole scan detune: add this value to the gap of the undulator to reduce flux [mm] acqtime (float): acqusition time to be set for both xspress3 and preamplifier roinum: select the roi to be used to calculate the XANES spectrum delaytime: reduce acquisition time of F460 by this value [sec] struck: Use the SRS and Struck scaler for the ion chamber and diode. Set to False to use the F460. fluorescence: indicate the presence of fluorescence data [bool] samplename (string): sample name to be saved in the scan metadata filename (string): filename to be added to the scan id as the text output filename shutter: instruct the scan to control the B shutter [bool] align: control the tuning of the DCM pointing before each XANES scan [bool] align_at: energy at which to align, default is the first energy point ''' ept = numpy.array([]) det = [] filename = filename last_time_pt = time.time() ringbuf = collections.deque(maxlen=10) c2pitch_kill = EpicsSignal("XF:05IDA-OP:1{Mono:HDCM-Ax:P2}Cmd:Kill-Cmd") xs.external_trig.put(False) #make sure user provided correct input if erange is []: raise AttributeError( "An energy range must be provided in a list by means of the 'erange' keyword." ) if estep is []: raise AttributeError( "A list of energy steps must be provided by means of the 'esteps' keyword." ) if (not isinstance(erange, list)) or (not isinstance(estep, list)): raise TypeError("The keywords 'estep' and 'erange' must be lists.") if len(erange) - len(estep) is not 1: raise ValueError("The 'erange' and 'estep' lists are inconsistent;"\ +'c.f., erange = [7000, 7100, 7150, 7500], estep = [2, 0.5, 5] ') if type(roinum) is not list: roinum = [roinum] if detune is not None: yield from abs_set(energy.detune, detune) #record relevant meta data in the Start document, defined in 90-usersetup.py metadata_record() #add user meta data RE.md['sample'] = {'name': samplename} RE.md['scaninfo'] = { 'type': 'XANES', 'ROI': roinum, 'raster': False, 'dwell': acqtime } RE.md['scan_input'] = str(np.around(erange, 2)) + ', ' + str( np.around(estep, 2)) #convert erange and estep to numpy array erange = numpy.array(erange) estep = numpy.array(estep) #calculation for the energy points for i in range(len(estep)): ept = numpy.append(ept, numpy.arange(erange[i], erange[i + 1], estep[i])) ept = numpy.append(ept, numpy.array(erange[-1])) # Debugging # Convert energy to bragg angle egap = np.array(()) ebragg = np.array(()) exgap = np.array(()) for i in ept: # Convert from eV to keV # if (i > 4500): # i = i / 1000 # Convert keV to bragg angle #b, _, _ = energy.energy_to_positions(i, 5, 0) eg, eb, ex = energy.forward(i) egap = np.append(egap, eg) ebragg = np.append(ebragg, eb) exgap = np.append(exgap, ex) # print(ebragg) #register the detectors det = [ring_current] if struck == True: det.append(sclr1) else: det.append(current_preamp) if fluor == True: det.append(xs) #setup xspress3 yield from abs_set(xs.settings.acquire_time, acqtime) yield from abs_set(xs.total_points, len(ept)) #setup the preamp if struck == True: yield from abs_set(sclr1.preset_time, acqtime) else: yield from abs_set(current_preamp.exp_time, acqtime - delaytime) #setup dcm/energy options if correct_c2_x is False: yield from abs_set(energy.move_c2_x, False) if correct_c1_r is not False: yield from abs_set(dcm.c1_roll, correct_c1_r) if harmonic != 1: yield from abs_set(energy.harmonic, harmonic) #prepare to peak up DCM at first scan point if align_at is not None: align = True if align is True: if align_at == None: yield from abs_set(energy, ept[0], wait=True) else: print("aligning at ", align_at) yield from abs_set(energy, float(align_at), wait=True) # energy.u_gap.corrfunc_dis.put(1) #open b shutter if shutter is True: #shut_b.open() yield from mv(shut_b, 'Open') #yield from abs_set(shut_b,1,wait=True) #peak up DCM at first scan point if align is True: ps = PeakStats(dcm.c2_pitch.name, 'sclr_i0') e_value = energy.energy.get()[1] # if e_value < 10.: # yield from abs_set(sclr1.preset_time,0.1, wait = True) # peakup = scan([sclr1], dcm.c2_pitch, -19.335, -19.305, 31) # else: # yield from abs_set(sclr1.preset_time,1., wait = True) # peakup = scan([sclr1], dcm.c2_pitch, -19.355, -19.320, 36) if e_value < 14.: sclr1.preset_time.put(0.1) else: sclr1.preset_time.put(1.) peakup = scan([sclr1], dcm.c2_pitch, -19.320, -19.360, 41) peakup = subs_wrapper(peakup, ps) yield from peakup yield from abs_set(dcm.c2_pitch, ps.cen, wait=True) #ttime.sleep(10) #yield from abs_set(c2pitch_kill, 1) #setup the live callbacks myscan = list_scan(det, energy, list(ept), per_step=per_step) livecallbacks = [] livetableitem = ['energy_energy'] if struck == True: livetableitem = livetableitem + ['sclr_i0', 'sclr_it'] else: livetableitem = livetableitem + [ 'current_preamp_ch0', 'current_preamp_ch2' ] if fluor == True: roi_name = 'roi{:02}'.format(roinum[0]) roi_key = [] roi_key.append(getattr(xs.channel1.rois, roi_name).value.name) roi_key.append(getattr(xs.channel2.rois, roi_name).value.name) roi_key.append(getattr(xs.channel3.rois, roi_name).value.name) livetableitem.append(roi_key[0]) livecallbacks.append(LiveTable(livetableitem)) liveploty = roi_key[0] liveplotx = energy.energy.name liveplotfig = plt.figure('raw xanes') elif struck == True: liveploty = 'sclr_it' liveplotx = energy.energy.name liveplotfig = plt.figure('raw xanes') # livecallbacks.append(LiveTable([sclr1, xs, energy])) livecallbacks.append(LivePlot(liveploty, x=liveplotx, fig=liveplotfig)) #livecallbacks.append(LivePlot(liveploty, x=liveplotx, ax=plt.gca(title='raw xanes'))) if struck == True: liveploty = 'sclr_i0' i0 = 'sclr_i0' else: liveploty = 'current_preamp_ch2' i0 = 'current_preamp_ch2' liveplotfig2 = plt.figure('i0') livecallbacks.append(LivePlot(liveploty, x=liveplotx, fig=liveplotfig2)) #livecallbacks.append(LivePlot(liveploty, x=liveplotx, ax=plt.gca(title='incident intensity'))) livenormfig = plt.figure('normalized xanes') if fluor == True: livecallbacks.append( NormalizeLivePlot(roi_key[0], x=liveplotx, norm_key=i0, fig=livenormfig)) #livecallbacks.append(NormalizeLivePlot(roi_key[0], x=liveplotx, norm_key = i0, ax=plt.gca(title='normalized xanes'))) else: livecallbacks.append( NormalizeLivePlot('sclr_it', x=liveplotx, norm_key=i0, fig=livenormfig)) #livecallbacks.append(NormalizeLivePlot(roi_key[0], x=liveplotx, norm_key = i0, ax=plt.gca(title='normalized xanes'))) def after_scan(name, doc): if name != 'stop': print( "You must export this scan data manually: xanes_afterscan_plan(doc[-1], <filename>, <roinum>)" ) return xanes_afterscan_plan(doc['run_start'], filename, roinum) logscan_detailed('xanes') def at_scan(name, doc): scanrecord.current_scan.put(doc['uid'][:6]) scanrecord.current_scan_id.put(str(doc['scan_id'])) scanrecord.current_type.put(RE.md['scaninfo']['type']) scanrecord.scanning.put(True) def finalize_scan(): # yield from abs_set(energy.u_gap.corrfunc_en,1) # disabled to test if # undulator gets stuck -AMK yield from abs_set(energy.move_c2_x, True) yield from abs_set(energy.harmonic, 1) scanrecord.scanning.put(False) if shutter == True: yield from mv(shut_b, 'Close') if detune is not None: energy.detune.put(0) del RE.md['sample']['name'] del RE.md['scaninfo'] myscan = list_scan(det, energy, list(ept), per_step=per_step) # myscan = list_scan(det, energy, list(ept), per_step=per_step(detectors, motor, step)) # myscan = list_scan(det, energy.bragg, list(ebragg), energy.u_gap, list(egap), energy.c2_x, list(exgap)) # myscan = scan_nd(det, energy.bragg, list(ebragg), energy.u_gap, list(egap), energy.c2_x, list(exgap)) myscan = finalize_wrapper(myscan, finalize_scan) return (yield from subs_wrapper(myscan, { 'all': livecallbacks, 'stop': after_scan, 'start': at_scan }))
def linescan(detector, axis, start, stop, nsteps, pluck=True, force=False, inttime=0.1, md={}): # integration time? ''' Generic linescan plan. This is a RELATIVE scan, relative to the current position of the selected motor. Examples -------- >>> RE(linescan('it', 'x', -1, 1, 21)) Parameters ---------- detector : str detector to display -- if, it, ir, or i0 axis : str or EpicsMotor motor or nickname start : float starting value for a relative scan stop : float ending value for a relative scan nsteps : int number of steps in scan pluck : bool, optional flag for whether to offer to pluck & move motor force : bool, optional flag for forcing a scan even if not clear to start inttime : float, optional integration time in seconds (default: 0.1) The motor is either the BlueSky name for a motor (e.g. xafs_linx) or a nickname for an XAFS sample motor (e.g. 'x' for xafs_linx). This does not write an ASCII data file, but it does make a log entry. Use the ls2dat() function to extract the linescan from the database and write it to a file. ''' def main_plan(detector, axis, start, stop, nsteps, pluck, force): (ok, text) = BMM_clear_to_start() if force is False and ok is False: print(error_msg(text)) yield from null() return detector, axis = ls_backwards_compatibility(detector, axis) # print('detector is: ' + str(detector)) # print('axis is: ' + str(axis)) # return(yield from null()) RE.msg_hook = None ## sanitize input and set thismotor to an actual motor if type(axis) is str: axis = axis.lower() detector = detector.capitalize() ## sanity checks on axis if axis not in motor_nicknames.keys() and 'EpicsMotor' not in str(type(axis)) \ and 'PseudoSingle' not in str(type(axis)) and 'WheelMotor' not in str(type(axis)): print( error_msg('\n*** %s is not a linescan motor (%s)\n' % (axis, str.join(', ', motor_nicknames.keys())))) yield from null() return if 'EpicsMotor' in str(type(axis)): thismotor = axis elif 'PseudoSingle' in str(type(axis)): thismotor = axis elif 'WheelMotor' in str(type(axis)): thismotor = axis else: # presume it's an xafs_XXXX motor thismotor = motor_nicknames[axis] current = thismotor.position if current + start < thismotor.limits[0]: print( error_msg( f'These scan parameters will take {thismotor.name} outside it\'s lower limit of {thismotor.limits[0]}' )) print(whisper(f'(starting position = {thismotor.position})')) return (yield from null()) if current + stop > thismotor.limits[1]: print( error_msg( f'These scan parameters will take {thismotor.name} outside it\'s upper limit of {thismotor.limits[1]}' )) print(whisper(f'(starting position = {thismotor.position})')) return (yield from null()) BMMuser.motor = thismotor ## sanity checks on detector if detector not in ('It', 'If', 'I0', 'Iy', 'Ir', 'Both', 'Bicron', 'Ia', 'Ib', 'Dualio', 'Xs', 'Xs1'): print( error_msg( '\n*** %s is not a linescan measurement (%s)\n' % (detector, 'it, if, i0, iy, ir, both, bicron, dualio, xs, xs1'))) yield from null() return yield from abs_set(user_ns['_locked_dwell_time'], inttime, wait=True) if detector == 'Xs': yield from mv(xs.settings.acquire_time, inttime) yield from mv(xs.total_points, nsteps) dets = [ user_ns['quadem1'], ] if user_ns['with_dualem']: dualio = user_ns['dualio'] denominator = '' detname = '' ## func is an anonymous function, built on the fly, for feeding to DerivedPlot if detector == 'It': denominator = ' / I0' detname = 'transmission' func = lambda doc: (doc['data'][thismotor.name], doc['data']['It'] / doc['data']['I0']) elif detector == 'Ia' and dualio is not None: dets.append(dualio) detname = 'Ia' func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ia']) elif detector == 'Ib' and dualio is not None: dets.append(dualio) detname = 'Ib' func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ib']) elif detector == 'Ir': #denominator = ' / It' detname = 'reference' #func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ir']/doc['data']['It']) func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ir']) elif detector == 'I0': detname = 'I0' func = lambda doc: (doc['data'][thismotor.name], doc['data']['I0']) elif detector == 'Bicron': dets.append(user_ns['vor']) detname = 'Bicron' func = lambda doc: (doc['data'][thismotor.name], doc['data'][ 'Bicron']) elif detector == 'Iy': denominator = ' / I0' detname = 'electron yield' func = lambda doc: (doc['data'][thismotor.name], doc['data']['Iy'] / doc['data']['I0']) elif detector == 'If': dets.append(user_ns['vor']) denominator = ' / I0' detname = 'fluorescence' func = lambda doc: (doc['data'][thismotor.name], (doc['data'][ BMMuser.dtc1] + doc['data'][BMMuser.dtc2] + doc['data'][ BMMuser.dtc3] + doc['data'][BMMuser.dtc4]) / doc['data'][ 'I0']) elif detector == 'Xs': dets.append(user_ns['xs']) denominator = ' / I0' detname = 'fluorescence' func = lambda doc: (doc['data'][thismotor.name], (doc['data'][BMMuser.xs1] + doc['data'][ BMMuser.xs2] + doc['data'][BMMuser.xs3] + doc['data'][BMMuser.xs4]) / doc['data']['I0']) yield from mv(xs.total_points, nsteps) # Xspress3 demands that this be set up front elif detector == 'Xs1': dets.append(user_ns['xs']) denominator = ' / I0' detname = 'fluorescence' func = lambda doc: (doc['data'][thismotor.name], doc['data'][ BMMuser.xs8] / doc['data']['I0']) yield from mv(xs1.total_points, nsteps) # Xspress3 demands that this be set up front elif detector == 'Dualio': dets.append(dualio) funcia = lambda doc: (doc['data'][thismotor.name], doc['data']['Ia' ]) funcib = lambda doc: (doc['data'][thismotor.name], doc['data']['Ib' ]) ## need a "Both" for trans + xs !!!!!!!!!! elif detector == 'Both': dets.append(user_ns['vor']) functr = lambda doc: (doc['data'][thismotor.name], doc['data'][ 'It'] / doc['data']['I0']) funcfl = lambda doc: (doc['data'][thismotor.name], (doc['data'][ BMMuser.dtc1] + doc['data'][BMMuser.dtc2] + doc['data'][ BMMuser.dtc3] + doc['data'][BMMuser.dtc4]) / doc['data'][ 'I0']) ## and this is the appropriate way to plot this linescan #abs_set(_locked_dwell_time, 0.5) if detector == 'Both': plot = [ DerivedPlot(funcfl, xlabel=thismotor.name, ylabel='If/I0', title='fluorescence vs. %s' % thismotor.name), DerivedPlot(functr, xlabel=thismotor.name, ylabel='It/I0', title='transmission vs. %s' % thismotor.name) ] elif detector == 'Dualio': plot = [ DerivedPlot(funcia, xlabel=thismotor.name, ylabel='Ia/I0', title='Ia vs. %s' % thismotor.name), DerivedPlot(funcib, xlabel=thismotor.name, ylabel='Ib/I0', title='Ib vs. %s' % thismotor.name) ] else: plot = DerivedPlot(func, xlabel=thismotor.name, ylabel=detector + denominator, title='%s vs. %s' % (detname, thismotor.name)) if 'PseudoSingle' in str(type(axis)): value = thismotor.readback.get() else: value = thismotor.user_readback.get() line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \ (thismotor.name, detector, start, stop, nsteps, value) ##BMM_suspenders() # engage suspenders thismd = dict() thismd['XDI'] = dict() thismd['XDI']['Facility'] = dict() thismd['XDI']['Facility']['GUP'] = BMMuser.gup thismd['XDI']['Facility']['SAF'] = BMMuser.saf rkvs.set('BMM:scan:type', 'line') rkvs.set('BMM:scan:starttime', str(datetime.datetime.timestamp(datetime.datetime.now()))) rkvs.set('BMM:scan:estimated', 0) @subs_decorator(plot) #@subs_decorator(src.callback) def scan_xafs_motor(dets, motor, start, stop, nsteps): uid = yield from rel_scan(dets, motor, start, stop, nsteps, md={ **thismd, **md }) return uid uid = yield from scan_xafs_motor(dets, thismotor, start, stop, nsteps) #global mytable #run = src.retrieve() #mytable = run.primary.read().to_dataframe() BMM_log_info('linescan: %s\tuid = %s, scan_id = %d' % (line1, uid, user_ns['db'][-1].start['scan_id'])) if pluck is True: action = input('\n' + bold_msg( 'Pluck motor position from the plot? [Y/n then Enter] ')) if action.lower() == 'n' or action.lower() == 'q': return (yield from null()) yield from move_after_scan(thismotor) def cleanup_plan(): ## BMM_clear_suspenders() ##RE.clear_suspenders() # disable suspenders yield from resting_state_plan() RE, BMMuser, rkvs = user_ns['RE'], user_ns['BMMuser'], user_ns['rkvs'] try: xs = user_ns['xs'] except: pass ###################################################################### # this is a tool for verifying a macro. this replaces an xafs scan # # with a sleep, allowing the user to easily map out motor motions in # # a macro # if BMMuser.macro_dryrun: print( info_msg( '\nBMMuser.macro_dryrun is True. Sleeping for %.1f seconds rather than running a line scan.\n' % BMMuser.macro_sleep)) countdown(BMMuser.macro_sleep) return (yield from null()) ###################################################################### RE.msg_hook = None yield from finalize_wrapper( main_plan(detector, axis, start, stop, nsteps, pluck, force), cleanup_plan()) RE.msg_hook = BMM_msg_hook
def rocking_curve(start=-0.10, stop=0.10, nsteps=101, detector='I0', choice='peak'): '''Perform a relative scan of the DCM 2nd crystal pitch around the current position to find the peak of the crystal rocking curve. Begin by opening the hutch slits to 3 mm. At the end, move to the position of maximum intensity on I0, then return to the hutch slits to their original height. Parameters ---------- start : (float) starting position relative to current [-0.1] end : (float) ending position relative to current [0.1] nsteps : (int) number of steps [101] detector : (string) 'I0' or 'Bicron' ['I0'] choice : (string) 'peak', fit' or 'com' (center of mass) ['peak'] If choice is fit, the fit is performed using the SkewedGaussianModel from lmfit, which works pretty well for this measurement at BMM. The line shape is a bit skewed due to the convolution with the slightly misaligned entrance slits. ''' def main_plan(start, stop, nsteps, detector): (ok, text) = BMM_clear_to_start() if ok is False: print(error_msg(text)) yield from null() return RE.msg_hook = None BMMuser.motor = motor if detector.lower() == 'bicron': func = lambda doc: (doc['data'][motor.name], doc['data']['Bicron']) dets = [ bicron, ] sgnl = 'Bicron' titl = 'Bicron signal vs. DCM 2nd crystal pitch' else: func = lambda doc: (doc['data'][motor.name], doc['data']['I0']) dets = [ quadem1, ] sgnl = 'I0' titl = 'I0 signal vs. DCM 2nd crystal pitch' plot = DerivedPlot(func, xlabel=motor.name, ylabel=sgnl, title=titl) rkvs.set('BMM:scan:type', 'line') rkvs.set('BMM:scan:starttime', str(datetime.datetime.timestamp(datetime.datetime.now()))) rkvs.set('BMM:scan:estimated', 0) @subs_decorator(plot) #@subs_decorator(src.callback) def scan_dcmpitch(sgnl): line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \ (motor.name, sgnl, start, stop, nsteps, motor.user_readback.get()) yield from abs_set(user_ns['_locked_dwell_time'], 0.1, wait=True) yield from dcm.kill_plan() yield from mv(slits3.vsize, 3) if sgnl == 'Bicron': yield from mv(slitsg.vsize, 5) uid = yield from rel_scan(dets, motor, start, stop, nsteps) #yield from rel_adaptive_scan(dets, 'I0', motor, # start=start, # stop=stop, # min_step=0.002, # max_step=0.03, # target_delta=.15, # backstep=True) t = db[-1].table() signal = t[sgnl] if choice.lower() == 'com': position = com(signal) top = t[motor.name][position] elif choice.lower() == 'fit': pitch = t['dcm_pitch'] mod = SkewedGaussianModel() pars = mod.guess(signal, x=pitch) out = mod.fit(signal, pars, x=pitch) print(whisper(out.fit_report(min_correl=0))) out.plot() top = out.params['center'].value else: position = peak(signal) top = t[motor.name][position] yield from sleep(3.0) yield from abs_set(motor.kill_cmd, 1, wait=True) RE.msg_hook = BMM_msg_hook BMM_log_info('rocking curve scan: %s\tuid = %s, scan_id = %d' % (line1, uid, user_ns['db'][-1].start['scan_id'])) yield from mv(motor, top) if sgnl == 'Bicron': yield from mv(slitsg.vsize, gonio_slit_height) yield from scan_dcmpitch(sgnl) def cleanup_plan(): yield from mv(user_ns['slits3'].vsize, slit_height) yield from abs_set(user_ns['_locked_dwell_time'], 0.5, wait=True) yield from sleep(1.0) yield from abs_set(motor.kill_cmd, 1, wait=True) yield from sleep(1.0) yield from user_ns['dcm'].kill_plan() yield from resting_state_plan() RE, BMMuser, db, rkvs = user_ns['RE'], user_ns['BMMuser'], user_ns[ 'db'], user_ns['rkvs'] dcm, slits3, slitsg, quadem1 = user_ns['dcm'], user_ns['slits3'], user_ns[ 'slitsg'], user_ns['quadem1'] ###################################################################### # this is a tool for verifying a macro. this replaces this rocking # # curve scan with a sleep, allowing the user to easily map out motor # # motions in a macro # if BMMuser.macro_dryrun: print( info_msg( '\nBMMuser.macro_dryrun is True. Sleeping for %.1f seconds rather than running a rocking curve scan.\n' % BMMuser.macro_sleep)) countdown(BMMuser.macro_sleep) return (yield from null()) ###################################################################### motor = user_ns['dcm_pitch'] slit_height = user_ns['slits3'].vsize.readback.get() try: gonio_slit_height = slitsg.vsize.readback.get() except: gonio_slit_height = 1 RE.msg_hook = None yield from finalize_wrapper(main_plan(start, stop, nsteps, detector), cleanup_plan()) RE.msg_hook = BMM_msg_hook
def slit_height(start=-1.5, stop=1.5, nsteps=31, move=False, force=False, slp=1.0, choice='peak'): '''Perform a relative scan of the DM3 BCT motor around the current position to find the optimal position for slits3. Optionally, the motor will moved to the center of mass of the peak at the end of the scan. Parameters ---------- start : float starting position relative to current [-3.0] end : float ending position relative to current [3.0] nsteps : int number of steps [61] move : bool True=move to position of max signal, False=pluck and move [False] slp : float length of sleep before trying to move dm3_bct [3.0] choice : str 'peak' or 'com' (center of mass) ['peak'] ''' def main_plan(start, stop, nsteps, move, slp, force): (ok, text) = BMM_clear_to_start() if force is False and ok is False: print(error_msg(text)) yield from null() return RE.msg_hook = None BMMuser.motor = user_ns['dm3_bct'] func = lambda doc: (doc['data'][motor.name], doc['data']['I0']) plot = DerivedPlot(func, xlabel=motor.name, ylabel='I0', title='I0 signal vs. slit height') line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \ (motor.name, 'i0', start, stop, nsteps, motor.user_readback.get()) rkvs.set('BMM:scan:type', 'line') rkvs.set('BMM:scan:starttime', str(datetime.datetime.timestamp(datetime.datetime.now()))) rkvs.set('BMM:scan:estimated', 0) @subs_decorator(plot) #@subs_decorator(src.callback) def scan_slit(slp): #if slit_height < 0.5: # yield from mv(slits3.vsize, 0.5) yield from abs_set(quadem1.averaging_time, 0.1, wait=True) yield from abs_set(motor.velocity, 0.4, wait=True) yield from abs_set(motor.kill_cmd, 1, wait=True) uid = yield from rel_scan([quadem1], motor, start, stop, nsteps) RE.msg_hook = BMM_msg_hook BMM_log_info('slit height scan: %s\tuid = %s, scan_id = %d' % (line1, uid, user_ns['db'][-1].start['scan_id'])) if move: t = db[-1].table() signal = t['I0'] #if get_mode() in ('A', 'B', 'C'): # position = com(signal) #else: position = peak(signal) top = t[motor.name][position] yield from sleep(slp) yield from abs_set(motor.kill_cmd, 1, wait=True) yield from mv(motor, top) else: action = input('\n' + bold_msg( 'Pluck motor position from the plot? [Y/n then Enter] ')) if action.lower() == 'n' or action.lower() == 'q': return (yield from null()) yield from sleep(slp) yield from abs_set(motor.kill_cmd, 1, wait=True) yield from move_after_scan(motor) yield from abs_set(quadem1.averaging_time, 0.5, wait=True) yield from scan_slit(slp) def cleanup_plan(slp): yield from mv(slits3.vsize, slit_height) yield from abs_set(user_ns['_locked_dwell_time'], 0.5, wait=True) yield from sleep(slp) yield from abs_set(motor.kill_cmd, 1, wait=True) yield from resting_state_plan() RE, BMMuser, db, slits3, quadem1 = user_ns['RE'], user_ns[ 'BMMuser'], user_ns['db'], user_ns['slits3'], user_ns['quadem1'] rkvs = user_ns['rkvs'] ####################################################################### # this is a tool for verifying a macro. this replaces this slit # # height scan with a sleep, allowing the user to easily map out motor # # motions in a macro # if BMMuser.macro_dryrun: print( info_msg( '\nBMMuser.macro_dryrun is True. Sleeping for %.1f seconds rather than running a slit height scan.\n' % BMMuser.macro_sleep)) countdown(BMMuser.macro_sleep) return (yield from null()) ####################################################################### motor = user_ns['dm3_bct'] slit_height = slits3.vsize.readback.get() RE.msg_hook = None yield from finalize_wrapper( main_plan(start, stop, nsteps, move, slp, force), cleanup_plan(slp)) RE.msg_hook = BMM_msg_hook
def stage_ami_wrapper(plan, magnet): """ Stage the AMI magnet. Turns on/off the persistence switch heater before/after the magnetic field scan or move. Parameters ---------- plan : iterable or iterator a generator, list, or similar containing `Msg` objects magnet : boolean Flag that triggers the stage/unstage. Yields ------ msg : Msg messages from plan, with 'subscribe' and 'unsubscribe' messages inserted and appended """ # This just needs to be set once here. signal = SetSignal(name='tmp') def _stage(): _heater_status = yield from rd(mag6t.field.switch_heater) if _heater_status != 'On': # Click current ramp button yield from mv(mag6t.field.ramp_button, 1) # Wait for the supply current to match the magnet. target = yield from rd(mag6t.field.current) function = _difference_check(target, tolerance=0.01) yield from abs_set(signal, mag6t.field.supply_current, function, wait=True) # Turn on persistance switch heater. yield from mv(mag6t.field.switch_heater, 'On') yield from sleep(2) # Wait for the heater to be on. function = _status_check(target=[3]) yield from abs_set(signal, mag6t.field.magnet_status, function, wait=True) # Click current ramp button yield from mv(mag6t.field.ramp_button, 1) def _unstage(): # Wait for the voltage to be zero. function = _difference_check(target=0.0, tolerance=0.02) yield from abs_set(signal, mag6t.field.voltage, function, wait=True) # Turn off persistance switch heater yield from mv(mag6t.field.switch_heater, 'Off') yield from sleep(2) # Wait for the heater to be off. function = _status_check(target=[2, 3]) yield from abs_set(signal, mag6t.field.magnet_status, function, wait=True) yield from mv(mag6t.field.zero_button, 1) def _inner_plan(): yield from _stage() return (yield from plan) if magnet: return (yield from finalize_wrapper(_inner_plan(), _unstage())) else: return (yield from plan)
def stage_dichro_wrapper(plan, dichro, lockin): """ Stage dichoic scans. Parameters ---------- plan : iterable or iterator a generator, list, or similar containing `Msg` objects dichro : boolean Flag that triggers the stage/unstage process of dichro scans. lockin : boolean Flag that triggers the stage/unstage process of lockin scans. Yields ------ msg : Msg messages from plan, with 'subscribe' and 'unsubscribe' messages inserted and appended """ _current_scaler_plot = [] def _stage(): if dichro and lockin: raise ValueError('Cannot have both dichro and lockin = True.') if lockin: for chan in scalerd.channels.component_names: scaler_channel = getattr(scalerd.channels, chan) if scaler_channel.kind.value >= 5: _current_scaler_plot.append(scaler_channel.s.name) scalerd.select_plot_channels(['Lock DC', 'Lock AC']) if pr_setup.positioner is None: raise ValueError('Phase retarder was not selected.') if 'th' in pr_setup.positioner.name: raise TypeError('Theta motor cannot be used in lock in! \ Please run pr_setup.config() and choose \ pzt.') yield from mv(pr_setup.positioner.parent.selectAC, 1) if dichro: # move PZT to center. if 'pzt' in pr_setup.positioner.name: yield from mv(pr_setup.positioner, pr_setup.positioner.parent.center.get()) def _unstage(): if lockin: scalerd.select_plot_channels(_current_scaler_plot) yield from mv(pr_setup.positioner.parent.selectDC, 1) if dichro: # move PZT to off center. if 'pzt' in pr_setup.positioner.name: yield from mv( pr_setup.positioner, pr_setup.positioner.parent.center.get() + pr_setup.offset.get()) def _inner_plan(): yield from _stage() return (yield from plan) return (yield from finalize_wrapper(_inner_plan(), _unstage()))
def _run_E_ramp(dets, start, stop, velocity, deadband, *, md=None): if md is None: md = {} md = ChainMap(md, {'plan_args': {'dets': list(map(repr, dets)), 'start': start, 'stop': stop, 'velocity': velocity, 'deadband': deadband}, 'plan_name': 'E_ramp', 'motors': [pgm.energy.name]}) # put the energy at the starting value yield from bps.abs_set(pgm.energy, start, wait=True) yield from bps.abs_set(pgm.fly.start_sig, start, wait=True) yield from bps.abs_set(pgm.fly.stop_sig, stop, wait=True) yield from bps.abs_set(pgm.fly.velocity, velocity, wait=True) # TODO do this with stage old_db = epu1.flt.output_deadband.get() yield from bps.abs_set(epu1.flt.output_deadband, deadband) # get the old vlaue v = (yield from bps.read(epu1.flt.input_pv)) if v is None: old_link = '' else: n = epu1.flt.input_pv.name old_link = v[n]['value'] # define a clean up plan def clean_up(): # move the energy setpoint to where the energy really is yield from bps.abs_set(pgm.energy, pgm.energy.position, wait=True) # set the interpolator to look at what it was looking at before # the scan. This should be the energy set point. yield from bps.abs_set(epu1.flt.input_pv, old_link, wait=True) yield from bps.abs_set(epu1.flt.output_deadband, old_db, wait=True) # change to track the readout energy yield from change_epu_flt_link(pgm_energy.readback.pvname) def go_plan(): ret = (yield from bps.abs_set(pgm.fly.fly_start, 1)) st = StatusBase() enum_map = pgm.fly.scan_status.describe()[pgm.fly.scan_status.name]['enum_strs'] def _done_cb(value, old_value, **kwargs): old_value = enum_map[int(old_value)] value = enum_map[int(value)] if old_value != value and value == 'Ready': st._finished() pgm.fly.scan_status.clear_sub(_done_cb) if ret is not None: pgm.fly.scan_status.subscribe(_done_cb, run=False) else: st._finished() print('SIM MODE') return st def inner_plan(): yield from trigger_and_read(dets) print(md) rp = ramp_plan(go_plan(), pgm.energy, inner_plan, period=None, md=md) return (yield from bpp.finalize_wrapper(rp, clean_up()))
def _run_E_ramp(dets, start, stop, velocity, deadband, *, streamname='primary', md=None): if md is None: md = {} md = ChainMap( md, { 'plan_args': { 'dets': list(map(repr, dets)), 'start': start, 'stop': stop, 'velocity': velocity, 'deadband': deadband }, 'plan_name': 'E_ramp', 'motors': [pgm.energy.name] }) # put the energy at the starting value yield from bps.abs_set(pgm.energy, start, wait=True) yield from bps.abs_set(pgm.fly.start_sig, start, wait=True) yield from bps.abs_set(pgm.fly.stop_sig, stop, wait=True) yield from bps.abs_set(pgm.fly.velocity, velocity, wait=True) if specs in dets: specs.stage() # TODO do this with stage old_db = epu1.flt.output_deadband.get() yield from bps.abs_set(epu1.flt.output_deadband, deadband) # get the old vlaue v = (yield from bps.read(epu1.flt.input_pv)) if v is None: old_link = '' else: n = epu1.flt.input_pv.name old_link = v[n]['value'] # define a clean up plan def clean_up(): # move the energy setpoint to where the energy really is yield from bps.abs_set(pgm.energy, pgm.energy.position, wait=True) # set the interpolator to look at what it was looking at before # the scan. This should be the energy set point. yield from bps.abs_set(epu1.flt.input_pv, old_link, wait=True) yield from bps.abs_set(epu1.flt.output_deadband, old_db, wait=True) if specs in dets: specs.unstage() # change to track the readout energy yield from change_epu_flt_link(pgm_energy.readback.pvname) def go_plan(): ret = (yield from bps.abs_set(pgm.fly.fly_start, 1)) st = StatusBase() enum_map = pgm.fly.scan_status.describe()[ pgm.fly.scan_status.name]['enum_strs'] def _done_cb(value, old_value, **kwargs): print(f'Old value {old_value} -> new value {value}') print( f'Old value type {type(old_value)} -> new value {type(value)}') try: old_value = enum_map[int(old_value)] except (TypeError, ValueError): ... try: value = enum_map[int(value)] except (TypeError, ValueError): ... if old_value != value and value == 'Ready': st._finished() pgm.fly.scan_status.clear_sub(_done_cb) if ret is not None: pgm.fly.scan_status.subscribe(_done_cb, run=False) else: st._finished() print('SIM MODE') return st def inner_plan(): yield from trigger_and_read(dets, name=streamname) print(md) rp = ramp_plan(go_plan(), pgm.energy, inner_plan, period=None, md=md) return (yield from bpp.finalize_wrapper(rp, clean_up()))
def xanes_plan(erange=[], estep=[], acqtime=1., samplename='', filename='', det_xs=xs, harmonic=1, detune=0, align=False, align_at=None, roinum=1, shutter=True, per_step=None): ''' erange (list of floats): energy ranges for XANES in eV, e.g. erange = [7112-50, 7112-20, 7112+50, 7112+120] estep (list of floats): energy step size for each energy range in eV, e.g. estep = [2, 1, 5] acqtime (float): acqusition time to be set for both xspress3 and preamplifier samplename (string): sample name to be saved in the scan metadata filename (string): filename to be added to the scan id as the text output filename det_xs (xs3 detector): the xs3 detector used for the measurement harmonic (odd integer): when set to 1, use the highest harmonic achievable automatically. when set to an odd integer, force the XANES scan to use that harmonic detune: add this value to the gap of the undulator to reduce flux [keV] align: perform peakup_fine before scan [bool] align_at: energy at which to align, default is average of the first and last energy points roinum: select the roi to be used to calculate the XANES spectrum shutter: instruct the scan to control the B shutter [bool] per_step: use a custom function for each energy point ''' # Make sure user provided correct input if (erange is []): raise AttributeError( "An energy range must be provided in a list by means of the 'erange' keyword." ) if (estep is []): raise AttributeError( "A list of energy steps must be provided by means of the 'esteps' keyword." ) if (not isinstance(erange, list)) or (not isinstance(estep, list)): raise TypeError("The keywords 'estep' and 'erange' must be lists.") if (len(erange) - len(estep) is not 1): raise ValueError("The 'erange' and 'estep' lists are inconsistent; " \ + 'c.f., erange = [7000, 7100, 7150, 7500], estep = [2, 0.5, 5] ') if (type(roinum) is not list): roinum = [roinum] if (detune is not 0): yield from abs_set(energy.detune, detune) # Record relevant meta data in the Start document, defined in 90-usersetup.py # Add user meta data scan_md = {} get_stock_md(scan_md) scan_md['sample'] = {'name': samplename} scan_md['scaninfo'] = { 'type': 'XANES', 'ROI': roinum, 'raster': False, 'dwell': acqtime } scan_md['scan_input'] = str(np.around(erange, 2)) + ', ' + str( np.around(estep, 2)) # Convert erange and estep to numpy array ept = np.array([]) erange = np.array(erange) estep = np.array(estep) # Calculation for the energy points for i in range(len(estep)): ept = np.append(ept, np.arange(erange[i], erange[i + 1], estep[i])) ept = np.append(ept, np.array(erange[-1])) # Debugging, is this needed? is this recorded in scanoutput? # Convert energy to bragg angle egap = np.array(()) ebragg = np.array(()) exgap = np.array(()) for i in ept: eg, eb, ex = energy.forward(i) egap = np.append(egap, eg) ebragg = np.append(ebragg, eb) exgap = np.append(exgap, ex) # Register the detectors det = [ring_current, sclr1, det_xs] # Setup xspress3 yield from abs_set(det_xs.external_trig, False) yield from abs_set(det_xs.settings.acquire_time, acqtime) yield from abs_set(det_xs.total_points, len(ept)) # Setup the scaler yield from abs_set(sclr1.preset_time, acqtime) # Setup DCM/energy options if (harmonic != 1): yield from abs_set(energy.harmonic, harmonic) # Prepare to peak up DCM at middle scan point if (align_at is not None): align = True if (align is True): if (align_at is None): align_at = 0.5 * (ept[0] + ept[-1]) print("Aligning at ", align_at) yield from abs_set(energy, align_at, wait=True) else: print("Aligning at ", align_at) yield from abs_set(energy, float(align_at), wait=True) # Peak up DCM at first scan point if (align is True): yield from peakup_fine(shutter=shutter) # Setup the live callbacks livecallbacks = [] # Setup Raw data livetableitem = ['energy_energy', 'sclr_i0', 'sclr_it'] roi_name = 'roi{:02}'.format(roinum[0]) roi_key = [] roi_key.append(getattr(det_xs.channel1.rois, roi_name).value.name) try: roi_key.append(getattr(xs.channel2.rois, roi_name).value.name) roi_key.append(getattr(xs.channel3.rois, roi_name).value.name) except NameError: pass livetableitem.append(roi_key[0]) livecallbacks.append(LiveTable(livetableitem)) liveploty = roi_key[0] liveplotx = energy.energy.name def my_factory(name): fig = plt.figure(num=name) ax = fig.gca() return fig, ax # liveplotfig = plt.figure('Raw XANES') # livecallbacks.append(LivePlot(liveploty, x=liveplotx, fig=liveplotfig)) livecallbacks.append( HackLivePlot(liveploty, x=liveplotx, fig_factory=partial(my_factory, name='Raw XANES'))) # Setup normalization liveploty = 'sclr_i0' i0 = 'sclr_i0' # liveplotfig2 = plt.figure('I0') # livecallbacks.append(LivePlot(liveploty, x=liveplotx, fig=liveplotfig2)) livecallbacks.append( HackLivePlot(liveploty, x=liveplotx, fig_factory=partial(my_factory, name='I0'))) # Setup normalized XANES # livenormfig = plt.figure('Normalized XANES') # livecallbacks.append(NormalizeLivePlot(roi_key[0], x=liveplotx, norm_key = i0, fig=livenormfig)) livecallbacks.append( NormalizeLivePlot(roi_key[0], x=liveplotx, norm_key=i0, fig_factory=partial(my_factory, name='Normalized XANES'))) def after_scan(name, doc): if (name != 'stop'): print( "You must export this scan data manually: xanes_afterscan_plan(doc[-1], <filename>, <roinum>)" ) return xanes_afterscan_plan(doc['run_start'], filename, roinum) logscan_detailed('xanes') def at_scan(name, doc): scanrecord.current_scan.put(doc['uid'][:6]) scanrecord.current_scan_id.put(str(doc['scan_id'])) # Not sure if RE should be here, but not sure what to make it # scanrecord.current_type.put(RE.md['scaninfo']['type']) scanrecord.current_type.put(scan_md['scaninfo']['type']) scanrecord.scanning.put(True) def finalize_scan(): yield from abs_set(energy.move_c2_x, True) yield from abs_set(energy.harmonic, 1) if (shutter is True): yield from mv(shut_b, 'Close') if (detune is not None): yield from abs_set(energy.detune, 0) scanrecord.scanning.put(False) energy.move(ept[0]) myscan = list_scan(det, energy, list(ept), per_step=per_step, md=scan_md) myscan = finalize_wrapper(myscan, finalize_scan) # Open b shutter if (shutter is True): yield from mv(shut_b, 'Open') return (yield from subs_wrapper(myscan, { 'all': livecallbacks, 'stop': after_scan, 'start': at_scan }))