Пример #1
0
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)())
Пример #2
0
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)())
Пример #3
0
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
Пример #4
0
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()))
Пример #5
0
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)
Пример #6
0
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)
Пример #7
0
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))
Пример #8
0
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))
Пример #9
0
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
Пример #10
0
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
Пример #11
0
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()
Пример #12
0
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()
Пример #13
0
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()))
Пример #14
0
 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
Пример #15
0
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(),
        )
    )
Пример #16
0
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())
Пример #18
0
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)
Пример #19
0
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(),
        )
    )
Пример #20
0
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))
Пример #21
0
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)
Пример #22
0
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)
Пример #23
0
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
Пример #24
0
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
Пример #25
0
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)
Пример #26
0
    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
Пример #27
0
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!')
Пример #28
0
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!')
Пример #29
0
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
    }))
Пример #30
0
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
Пример #31
0
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
Пример #32
0
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
Пример #33
0
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)
Пример #34
0
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()))
Пример #35
0
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()))
Пример #36
0
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()))
Пример #37
0
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
    }))