Ejemplo n.º 1
0
def test_live_grid(fresh_RE):
    RE = fresh_RE
    motor1._fake_sleep = 0
    motor2._fake_sleep = 0
    RE(outer_product_scan([det4], motor1, -3, 3, 6, motor2, -5, 5, 10, False),
       LiveGrid((6, 10), 'det4'))

    # Test the deprecated name.
    RE(outer_product_scan([det4], motor1, -3, 3, 6, motor2, -5, 5, 10, False),
       LiveRaster((6, 10), 'det4'))
Ejemplo n.º 2
0
def test_live_grid(RE, hw):
    hw.motor1.delay = 0
    hw.motor2.delay = 0
    RE(grid_scan([hw.det4], hw.motor1, -3, 3, 6, hw.motor2, -5, 5, 10, False),
       LiveGrid((6, 10), 'det4'))

    # Test the deprecated name.
    with pytest.warns(UserWarning):
        RE(
            grid_scan([hw.det4], hw.motor1, -3, 3, 6, hw.motor2, -5, 5, 10,
                      False), LiveRaster((6, 10), 'det4'))
Ejemplo n.º 3
0
def setup_liveraster(*, motors, gs, shape, extent):
    """Setup a LiveTable by inspecting motors, shape, extent, and gs.
    """
    if len(motors) != 2:
        return None
    ylab, xlab = [first_key_heuristic(m) for m in motors]
    raster = LiveGrid(shape,
                      gs.MASTER_DET_FIELD,
                      xlabel=xlab,
                      ylabel=ylab,
                      extent=extent)
    return raster
Ejemplo n.º 4
0
def nano_xrf(xstart,
             xstop,
             xstep,
             ystart,
             ystop,
             ystep,
             dwell,
             shutter=True,
             extra_dets=None,
             xmotor=nano_stage.sx,
             ymotor=nano_stage.sy,
             flag_snake=True):

    # calculate number of points
    xnum = np.int(np.abs(np.round((xstop - xstart) / xstep)) + 1)
    ynum = np.int(np.abs(np.round((ystop - ystart) / ystep)) + 1)

    # Setup detectors
    if extra_dets is None:
        extra_dets = []
    dets = [sclr1, xs, xbpm2, xmotor, ymotor] + extra_dets

    # Record relevant metadata in the Start document, defined in 90-usersetup.py
    scan_md = {}
    get_stock_md(scan_md)
    # scan_md['scan_input'] = str([xstart, xstop, xstep, ystart, ystop, ystep, dwell])
    # scan_md['scaninfo']  = {'type': 'XRF',
    #                         'raster' : True}
    scan_md['scan']['type'] = 'XRF_STEP'
    scan_md['scan']['scan_input'] = [
        xstart, xstop, xstep, ystart, ystop, ystep, dwell
    ]
    scan_md['scan']['detectors'] = [d.name for d in dets]
    scan_md['scan']['fast_axis'] = {
        'motor_name': xmotor.name,
        'units': xmotor.motor_egu.get()
    }
    scan_md['scan']['slow_axis'] = {
        'motor_name': ymotor.name,
        'units': ymotor.motor_egu.get()
    }
    scan_md['scan']['theta'] = {
        'val': nano_stage.th.user_readback.get(),
        'units': nano_stage.th.motor_egu.get()
    }
    scan_md['scan']['delta'] = {'val': 0, 'units': xmotor.motor_egu.get()}
    scan_md['scan']['snake'] = 1 if flag_snake else 0
    scan_md['scan']['shape'] = (xnum, ynum)

    # Set counting time
    sclr1.preset_time.put(dwell)
    xs.external_trig.put(False)
    xs.settings.acquire_time.put(dwell)
    xs.total_points.put(xnum * ynum)
    if (merlin in dets):
        merlin.cam.acquire_time.put(dwell)
        merlin.cam.acquire_period.put(dwell + 0.005)
        merlin.hdf5.stage_sigs['num_capture'] = xnum * ynum
        scan_md['scan']['merlin'] = {
            'merlin_exp_time': dwell,
            'merlin_exp_period': dwell + 0.005
        }

    # LiveGrid
    livecallbacks = []
    livecallbacks.append(LiveTable([xmotor.name, ymotor.name]))
    roi_name = 'roi{:02}'.format(1)
    roi_key = getattr(xs.channel1.rois, roi_name).value.name
    livecallbacks.append(
        LiveGrid((ynum, xnum),
                 roi_key,
                 clim=None,
                 cmap='viridis',
                 xlabel='x [um]',
                 ylabel='y [um]',
                 extent=[xstart, xstop, ystart, ystop],
                 x_positive='right',
                 y_positive='down'))

    myplan = grid_scan(dets,
                       ymotor,
                       ystart,
                       ystop,
                       ynum,
                       xmotor,
                       xstart,
                       xstop,
                       xnum,
                       flag_snake,
                       md=scan_md)
    myplan = subs_wrapper(myplan, {'all': livecallbacks})

    # Open shutter
    # if (shutter):
    #     yield from mv(shut_b,'Open')
    yield from check_shutters(shutter, 'Open')

    # grid scan
    uid = yield from myplan

    # Close shutter
    # if (shutter):
    #     yield from mv(shut_b,'Close')
    yield from check_shutters(shutter, 'Close')

    return uid
Ejemplo n.º 5
0
def hf2dxrf(*,
            xstart,
            xnumstep,
            xstepsize,
            ystart,
            ynumstep,
            ystepsize,
            acqtime,
            shutter=True,
            align=False,
            xmotor=hf_stage.x,
            ymotor=hf_stage.y,
            numrois=1,
            extra_dets=[],
            setenergy=None,
            u_detune=None,
            echange_waittime=10,
            samplename=None,
            snake=True):
    '''input:
        xstart, xnumstep, xstepsize : float
        ystart, ynumstep, ystepsize : float
        acqtime : float
             acqusition time to be set for both xspress3 and F460
        numrois : integer
            number of ROIs set to display in the live raster scans.
            This is for display ONLY.  The actualy number of ROIs
            saved depend on how many are enabled and set in the
            read_attr However noramlly one cares only the raw XRF
            spectra which are all saved and will be used for fitting.
        energy (float): set energy, use with caution, hdcm might
            become misaligned
        u_detune (float): amount of undulator to
            detune in the unit of keV

    '''
    # Record relevant metadata in the Start document, defined in 90-usersetup.py
    scan_md = {}
    get_stock_md(scan_md)
    scan_md['sample'] = {'name': samplename}
    scan_md['scan_input'] = str(
        [xstart, xnumstep, xstepsize, ystart, ynumstep, ystepsize, acqtime])
    scan_md['scaninfo'] = {'type': 'XRF', 'raster': True}

    # Setup detectors
    dets = [sclr1, xs]
    dets = dets + extra_dets
    dets_by_name = {d.name: d for d in dets}
    # Scaler
    if (acqtime < 0.001):
        acqtime = 0.001
    sclr1.preset_time.put(acqtime)
    # XS3
    xs.external_trig.put(False)
    xs.settings.acquire_time.put(acqtime)
    xs.total_points.put((xnumstep + 1) * (ynumstep + 1))

    if ('merlin' in dets_by_name):
        dpc = dets_by_name['merlin']

        # Setup Merlin
        dpc.cam.trigger_mode.put(0)
        dpc.cam.acquire_time.put(acqtime)
        dpc.cam.acquire_period.put(acqtime + 0.005)
        dpc.cam.num_images.put(1)
        dpc.hdf5.stage_sigs['num_capture'] = (xnumstep + 1) * (ynumstep + 1)
        dpc._mode = SRXMode.step
        dpc.total_points.put((xnumstep + 1) * (ynumstep + 1))

    if ('xs2' in dets_by_name):
        xs2 = dets_by_name['xs2']
        xs2.external_trig.put(False)
        xs2.settings.acquire_time.put(acqtime)
        xs2.total_points.put((xnumstep + 1) * (ynumstep + 1))

    # Setup the live callbacks
    livecallbacks = []

    # Setup scanbroker to update time remaining
    def time_per_point(name, doc, st=ttime.time()):
        if ('seq_num' in doc.keys()):
            scanrecord.scan0.tpp.put((doc['time'] - st) / doc['seq_num'])
            scanrecord.scan0.curpt.put(int(doc['seq_num']))
            scanrecord.time_remaining.put(
                (doc['time'] - st) / doc['seq_num'] *
                ((xnumstep + 1) * (ynumstep + 1) - doc['seq_num']) / 3600)

    livecallbacks.append(time_per_point)

    # Setup LiveTable
    livetableitem = [xmotor.name, ymotor.name, i0.name]
    xstop = xstart + xnumstep * xstepsize
    ystop = ystart + ynumstep * ystepsize

    for roi_idx in range(numrois):
        roi_name = 'roi{:02}'.format(roi_idx + 1)
        roi_key = getattr(xs.channel1.rois, roi_name).value.name
        livetableitem.append(roi_key)
        roimap = LiveGrid((ynumstep + 1, xnumstep + 1),
                          roi_key,
                          clim=None,
                          cmap='viridis',
                          xlabel='x (mm)',
                          ylabel='y (mm)',
                          extent=[xstart, xstop, ystart, ystop],
                          x_positive='right',
                          y_positive='down')
        livecallbacks.append(roimap)

    if ('xs2' in dets_by_name):
        for roi_idx in range(numrois):
            roi_key = getattr(xs2.channel1.rois, roi_name).value.name
            livetableitem.append(roi_key)
            fig = plt.figure('xs2_ROI{:02}'.format(roi_idx + 1))
            fig.clf()
            roimap = LiveGrid((ynumstep + 1, xnumstep + 1),
                              roi_key,
                              clim=None,
                              cmap='viridis',
                              xlabel='x (mm)',
                              ylabel='y (mm)',
                              extent=[xstart, xstop, ystart, ystop],
                              x_positive='right',
                              y_positive='down',
                              ax=fig.gca())
            livecallbacks.append(roimap)

    if ('merlin' in dets_by_name) and (hasattr(dpc, 'stats1')):
        fig = plt.figure('DPC')
        fig.clf()
        dpc_tmap = LiveGrid((ynumstep + 1, xnumstep + 1),
                            dpc.stats1.total.name,
                            clim=None,
                            cmap='viridis',
                            xlabel='x (mm)',
                            ylabel='y (mm)',
                            x_positive='right',
                            y_positive='down',
                            extent=[xstart, xstop, ystart, ystop],
                            ax=fig.gca())
        livecallbacks.append(dpc_tmap)

    # Change energy (if provided)
    if (setenergy is not None):
        if (u_detune is not None):
            energy.detune.put(u_detune)
        print('Changing energy to ', setenergy)
        yield from mv(energy, setenergy)
        print('Waiting time (s) ', echange_waittime)
        yield from bps.sleep(echange_waittime)

    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(scan_md['scaninfo']['type'])
        scanrecord.scanning.put(True)

    def finalize_scan(name, doc):
        scanrecord.scanning.put(False)

    # Setup the scan
    hf2dxrf_scanplan = outer_product_scan(dets,
                                          ymotor,
                                          ystart,
                                          ystop,
                                          ynumstep + 1,
                                          xmotor,
                                          xstart,
                                          xstop,
                                          xnumstep + 1,
                                          snake,
                                          md=scan_md)
    hf2dxrf_scanplan = subs_wrapper(hf2dxrf_scanplan, {
        'all': livecallbacks,
        'start': at_scan,
        'stop': finalize_scan
    })
    # Move to starting position
    yield from mv(xmotor, xstart, ymotor, ystart)

    # Peak up monochromator at this energy
    if (align):
        yield from peakup_fine(shutter=shutter)

    # Open shutter
    if (shutter):
        yield from mv(shut_b, 'Open')

    # Run the scan
    scaninfo = yield from hf2dxrf_scanplan

    #TO-DO: implement fast shutter control (close)
    if (shutter):
        yield from mv(shut_b, 'Close')

    # Write to scan log
    if ('merlin' in dets_by_name):
        logscan_event0info('2dxrf_withdpc')
        # Should this be here?
        merlin.hdf5.stage_sigs['num_capture'] = 0
    else:
        logscan_detailed('2dxrf')

    return scaninfo
Ejemplo n.º 6
0
def nano_xrf(xstart,
             xstop,
             xstep,
             ystart,
             ystop,
             ystep,
             acqtime,
             shutter=True,
             extra_dets=None,
             xmotor=nano_stage.sx,
             ymotor=nano_stage.sy):

    # define motors
    # xmotor = nano_stage.x
    # ymotor = nano_stage.y

    # Record relevant metadata in the Start document, defined in 90-usersetup.py
    scan_md = {}
    get_stock_md(scan_md)
    scan_md['scan_input'] = str(
        [xstart, xstop, xstep, ystart, ystop, ystep, acqtime])
    scan_md['scaninfo'] = {'type': 'XRF', 'raster': True}

    # calculate number of points
    xnum = np.int(np.abs(np.round((xstop - xstart) / xstep)) + 1)
    ynum = np.int(np.abs(np.round((ystop - ystart) / ystep)) + 1)

    # Setup detectors
    if extra_dets is None:
        extra_dets = []
    dets = [sclr1, xs, xbpm2, xmotor, ymotor] + extra_dets

    # Set counting time
    sclr1.preset_time.put(acqtime)
    xs.external_trig.put(False)
    xs.settings.acquire_time.put(acqtime)
    xs.total_points.put(xnum * ynum)
    if (merlin in dets):
        merlin.cam.acquire_time.put(acqtime)
        merlin.cam.acquire_period.put(acqtime + 0.005)
        merlin.hdf5.stage_sigs['num_capture'] = xnum * ynum
        scan_md['merlin'] = {
            'merlin_exp_time': acqtime,
            'merlin_exp_period': acqtime + 0.005
        }

    # LiveGrid
    livecallbacks = []
    livecallbacks.append(LiveTable([xmotor.name, ymotor.name]))
    roi_name = 'roi{:02}'.format(1)
    roi_key = getattr(xs.channel1.rois, roi_name).value.name
    livecallbacks.append(
        LiveGrid((ynum, xnum),
                 roi_key,
                 clim=None,
                 cmap='viridis',
                 xlabel='x [um]',
                 ylabel='y [um]',
                 extent=[xstart, xstop, ystart, ystop],
                 x_positive='right',
                 y_positive='down'))

    myplan = grid_scan(dets,
                       ymotor,
                       ystart,
                       ystop,
                       ynum,
                       xmotor,
                       xstart,
                       xstop,
                       xnum,
                       True,
                       md=scan_md)
    myplan = subs_wrapper(myplan, {'all': livecallbacks})

    # Open shutter
    if (shutter):
        yield from mv(shut_b, 'Open')

    # grid scan
    uid = yield from myplan

    # Open shutter
    if (shutter):
        yield from mv(shut_b, 'Close')

    return uid
Ejemplo n.º 7
0
def scan_and_fly_base(detectors,
                      xstart,
                      xstop,
                      xnum,
                      ystart,
                      ystop,
                      ynum,
                      dwell,
                      *,
                      flying_zebra,
                      xmotor,
                      ymotor,
                      delta=None,
                      shutter=True,
                      align=False,
                      plot=True,
                      md=None,
                      snake=False,
                      verbose=False):
    """Read IO from SIS3820.
    Zebra buffers x(t) points as a flyer.
    Xpress3 is our detector.
    The aerotech has the x and y positioners.
    delta should be chosen so that it takes about 0.5 sec to reach the gate??
    ymotor  slow axis
    xmotor  fast axis

    Parameters
    ----------
    Detectors : List[Device]
       These detectors must be known to the zebra

    xstart, xstop : float
    xnum : int
    ystart, ystop : float
    ynum : int
    dwell : float
       Dwell time in seconds

    flying_zebra : SRXFlyer1Axis

    xmotor, ymotor : EpicsMotor, kwarg only
        These should be known to the zebra
        # TODO sort out how to check this

    delta : float, optional, kwarg only
       offset on the ystage start position.  If not given, derive from
       dwell + pixel size
    align : bool, optional, kwarg only
       If True, try to align the beamline
    shutter : bool, optional, kwarg only
       If True, try to open the shutter
    """

    # t_setup = tic()

    # Check for negative number of points
    if (xnum < 1 or ynum < 1):
        print('Error: Number of points is negative.')
        return

    # Set metadata
    if md is None:
        md = {}
    md = get_stock_md(md)

    # Assign detectors to flying_zebra, this may fail
    flying_zebra.detectors = detectors
    # Setup detectors, combine the zebra, sclr, and the just set detector list
    detectors = (flying_zebra.encoder,
                 flying_zebra.sclr) + flying_zebra.detectors

    dets_by_name = {d.name: d for d in detectors}

    # Set up the merlin
    if 'merlin' in dets_by_name:
        dpc = dets_by_name['merlin']
        # TODO use stage sigs
        # Set trigger mode
        # dpc.cam.trigger_mode.put(2)
        # Make sure we respect whatever the exposure time is set to
        if (dwell < 0.0066392):
            print('The Merlin should not operate faster than 7 ms.')
            print('Changing the scan dwell time to 7 ms.')
            dwell = 0.007
        # According to Ken's comments in hxntools, this is a de-bounce time
        # when in external trigger mode
        dpc.cam.stage_sigs['acquire_time'] = 0.50 * dwell - 0.0016392
        dpc.cam.stage_sigs['acquire_period'] = 0.75 * dwell
        dpc.cam.stage_sigs['num_images'] = 1
        dpc.stage_sigs['total_points'] = xnum
        dpc.hdf5.stage_sigs['num_capture'] = xnum
        del dpc

    # Setup dexela
    if ('dexela' in dets_by_name):
        xrd = dets_by_name['dexela']
        xrd.cam.stage_sigs['acquire_time'] = dwell
        xrd.cam.stage_sigs['acquire_period'] = dwell
        del xrd

    # If delta is None, set delta based on time for acceleration
    if (delta is None):
        MIN_DELTA = 0.100  # old default value
        v = ((xstop - xstart) / (xnum - 1)) / dwell  # compute "stage speed"
        t_acc = xmotor.acceleration.get()  # acceleration time
        delta = 0.5 * t_acc * v  # distance the stage will travel in t_acc
        delta = np.amax((delta, MIN_DELTA))
        # delta = 0.500 #was 2.5 when npoint scanner drifted

    # Move to start scanning location
    # Calculate move to scan start
    pxsize = (xstop - xstart) / (xnum - 1)
    row_start = xstart - delta - (pxsize / 2)
    row_stop = xstop + delta + (pxsize / 2)
    # yield from mv(xmotor, row_start,
    #               ymotor, ystart)

    # Run a peakup before the map?
    if (align):
        yield from peakup_fine(shutter=shutter)

    # Scan metadata
    md['scan']['type'] = 'XRF_FLY'
    md['scan']['scan_input'] = [
        xstart, xstop, xnum, ystart, ystop, ynum, dwell
    ]
    md['scan']['sample_name'] = ''
    md['scan']['detectors'] = [d.name for d in detectors]
    md['scan']['dwell'] = dwell
    md['scan']['fast_axis'] = {
        'motor_name': xmotor.name,
        'units': xmotor.motor_egu.get()
    }
    md['scan']['slow_axis'] = {
        'motor_name': ymotor.name,
        'units': ymotor.motor_egu.get()
    }
    md['scan']['theta'] = {
        'val': nano_stage.th.user_readback.get(),
        'units': nano_stage.th.motor_egu.get()
    }
    md['scan']['delta'] = {'val': delta, 'units': xmotor.motor_egu.get()}
    md['scan']['snake'] = snake
    md['scan']['shape'] = (xnum, ynum)

    @stage_decorator(flying_zebra.detectors)
    def fly_each_step(motor, step, row_start, row_stop):
        def move_to_start_fly():
            "See http://nsls-ii.github.io/bluesky/plans.html#the-per-step-hook"
            # row_str = short_uid('row')
            # yield from abs_set(xmotor, row_start, group=row_str)
            # yield from one_1d_step([temp_nanoKB], motor, step)
            # yield from bps.wait(group=row_str)

            row_str = short_uid('row')
            yield from bps.checkpoint()
            yield from bps.abs_set(xmotor, row_start, group=row_str)
            yield from bps.abs_set(motor, step, group=row_str)
            yield from bps.wait(group=row_str)
            yield from bps.trigger_and_read([temp_nanoKB, motor])

        if verbose:
            t_mvstartfly = tic()
        yield from move_to_start_fly()

        # TODO  Why are we re-trying the move?  This should be fixed at
        # a lower level
        # yield from bps.sleep(1.0)  # wait for the "x motor" to move
        x_set = row_start
        x_dial = xmotor.user_readback.get()
        # Get retry deadband value and check against that
        i = 0
        DEADBAND = 0.050  # retry deadband of nPoint scanner
        while (np.abs(x_set - x_dial) > DEADBAND):
            if (i == 0):
                print('Waiting for motor to reach starting position...',
                      end='',
                      flush=True)
            i = i + 1
            yield from mv(xmotor, row_start)
            yield from bps.sleep(0.1)
            x_dial = xmotor.user_readback.get()
        if (i != 0):
            print('done')

        if verbose:
            toc(t_mvstartfly, str='Move to start fly each')

        # Set the scan speed
        # Is abs_set(wait=True) or mv() faster?
        v = ((xstop - xstart) / (xnum - 1)) / dwell  # compute "stage speed"
        # yield from abs_set(xmotor.velocity, v, wait=True)  # set the "stage speed"
        if (v > xmotor.velocity.high_limit):
            raise ValueError(
                f'Desired motor velocity too high\nMax velocity: {xmotor.velocity.high_limit}'
            )
        elif (v < xmotor.velocity.low_limit):
            raise ValueError(
                f'Desired motor velocity too low\nMin velocity: {xmotor.velocity.low_limit}'
            )
        else:
            yield from mv(xmotor.velocity, v)

        # set up all of the detectors
        # TODO we should be able to move this out of the per-line call?!
        if ('xs' in dets_by_name):
            xs = dets_by_name['xs']
            yield from abs_set(xs.hdf5.num_capture, xnum, group='set')
            yield from abs_set(xs.settings.num_images, xnum, group='set')
            yield from bps.wait(group='set')
            # yield from mv(xs.hdf5.num_capture, xnum,
            #               xs.settings.num_images, xnum)
            # xs.hdf5.num_capture.put(xnum)
            # xs.settings.num_images.put(xnum)

        if ('xs2' in dets_by_name):
            xs2 = dets_by_name['xs2']
            # yield from abs_set(xs2.hdf5.num_capture, xnum, wait=True)
            # yield from abs_set(xs2.settings.num_images, xnum, wait=True)
            yield from mv(xs2.hdf5.num_capture, xnum, xs2.settings.num_images,
                          xnum)

        if ('merlin' in dets_by_name):
            merlin = dets_by_name['merlin']
            yield from abs_set(merlin.hdf5.num_capture, xnum, wait=True)
            yield from abs_set(merlin.cam.num_images, xnum, wait=True)

        if ('dexela' in dets_by_name):
            dexela = dets_by_name['dexela']
            yield from abs_set(dexela.hdf5.num_capture, xnum, wait=True)
            # yield from abs_set(dexela.hdf5.num_frames_chunks, xnum, wait=True)
            yield from abs_set(dexela.cam.num_images, xnum, wait=True)

        ion = flying_zebra.sclr
        yield from abs_set(ion.nuse_all, 2 * xnum)

        # arm the Zebra (start caching x positions)
        # @timer_wrapper
        def zebra_kickoff():
            if row_start < row_stop:
                yield from kickoff(flying_zebra,
                                   xstart=xstart,
                                   xstop=xstop,
                                   xnum=xnum,
                                   dwell=dwell,
                                   wait=True)
            else:
                yield from kickoff(flying_zebra,
                                   xstart=xstop,
                                   xstop=xstart,
                                   xnum=xnum,
                                   dwell=dwell,
                                   wait=True)

        if verbose:
            t_zebkickoff = tic()
        yield from zebra_kickoff()
        if verbose:
            toc(t_zebkickoff, str='Zebra kickoff')

        if verbose:
            t_datacollect = tic()
        # arm SIS3820, note that there is a 1 sec delay in setting X
        # into motion so the first point *in each row* won't
        # normalize...
        yield from abs_set(ion.erase_start, 1)
        if verbose:
            toc(t_datacollect, str='  reset scaler')

        # trigger all of the detectors
        row_str = short_uid('row')
        if verbose:
            print('Data collection:')
        for d in flying_zebra.detectors:
            if verbose:
                print(f'  triggering {d.name}')
            st = yield from bps.trigger(d, group=row_str)
            st.add_callback(lambda x: toc(
                t_datacollect,
                str=
                f"  status object  {datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S.%f')}"
            ))
            if (d.name == 'dexela'):
                yield from bps.sleep(1)
        if verbose:
            toc(t_datacollect, str='  trigger detectors')

        # yield from bps.sleep(1.5)
        if verbose:
            toc(t_datacollect, str='  sleep')

        # start the 'fly'
        def print_watch(*args, **kwargs):
            with open('/home/xf05id1/bluesky_output.txt', 'a') as f:
                f.write(
                    datetime.datetime.strftime(datetime.datetime.now(),
                                               '%Y-%m-%d %H:%M:%S.%f\n'))
                # print(args)
                f.write(json.dumps(kwargs))
                f.write('\n')

        st = yield from abs_set(xmotor, row_stop, group=row_str)
        # st.watch(print_watch)

        if verbose:
            toc(t_datacollect, str='  move start')

        if verbose and False:
            ttime.sleep(1)
            while (xmotor.motor_is_moving.get()):
                ttime.sleep(0.001)
            toc(t_datacollect, str='  move end')
            while (xs.settings.detector_state.get()):
                ttime.sleep(0.001)
            toc(t_datacollect, str='  xs done')
            while (sclr1.acquiring.get()):
                ttime.sleep(0.001)
            toc(t_datacollect, str='  sclr1 done')
        # wait for the motor and detectors to all agree they are done
        yield from bps.wait(group=row_str)
        st.wait()

        if verbose:
            toc(t_datacollect, str='Total time')

        # we still know about ion from above
        yield from abs_set(ion.stop_all, 1)  # stop acquiring scaler

        # set speed back
        reset_scanner_velocity()

        # @timer_wrapper
        def zebra_complete():
            yield from complete(flying_zebra)  # tell the Zebra we are done

        if verbose:
            t_zebcomplete = tic()
        yield from zebra_complete()
        if verbose:
            toc(t_zebcomplete, str='Zebra complete')

        # @timer_wrapper
        def zebra_collect():
            yield from collect(flying_zebra)  # extract data from Zebra

        if verbose:
            t_zebcollect = tic()
        yield from zebra_collect()
        if verbose:
            toc(t_zebcollect, str='Zebra collect')

    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(md['scan']['type'])
        scanrecord.scanning.put(True)
        scanrecord.time_remaining.put((dwell * xnum + 3.8) / 3600)

    def finalize_scan(name, doc):
        logscan_detailed('XRF_FLY')
        scanrecord.scanning.put(False)
        scanrecord.time_remaining.put(0)

    # TODO remove this eventually?
    # xs = dets_by_name['xs']
    # xs = dets_by_name['xs2']
    # Not sure if this is always true
    xs = dets_by_name[flying_zebra.detectors[0].name]

    yield from mv(xs.erase, 0)

    # Setup LivePlot
    if plot:
        if (ynum == 1):
            livepopup = [
                SRX1DFlyerPlot(xs.channel1.rois.roi01.value.name,
                               xstart=xstart,
                               xstep=(xstop - xstart) / (xnum - 1),
                               xlabel=xmotor.name)
            ]
        else:
            livepopup = [
                LiveGrid((ynum, xnum + 1),
                         xs.channel1.rois.roi01.value.name,
                         extent=(xstart, xstop, ystart, ystop),
                         x_positive='right',
                         y_positive='down')
            ]
    else:
        livepopup = []

    @subs_decorator(livepopup)
    @subs_decorator({'start': at_scan})
    @subs_decorator({'stop': finalize_scan})
    # monitor values from xs
    @monitor_during_decorator([xs.channel1.rois.roi01.value])
    # @monitor_during_decorator([xs.channel1.rois.roi01.value, xs.array_counter])
    @stage_decorator([flying_zebra])  # Below, 'scan' stage ymotor.
    @run_decorator(md=md)
    def plan():
        # TODO move this to stage sigs
        for d in flying_zebra.detectors:
            yield from bps.mov(d.total_points, xnum)

        # TODO move this to stage sigs
        yield from bps.mov(xs.external_trig, True)

        ystep = 0
        for step in np.linspace(ystart, ystop, ynum):
            yield from abs_set(scanrecord.time_remaining,
                               (ynum - ystep) * (dwell * xnum + 3.8) / 3600.)
            # 'arm' the all of the detectors for outputting fly data
            for d in flying_zebra.detectors:
                yield from bps.mov(d.fly_next, True)
            # print('h5 armed\t',time.time())
            if (snake is False):
                direction = 0
                start = row_start
                stop = row_stop
            else:
                if ystep % 2 == 0:
                    direction = 0
                    start = row_start
                    stop = row_stop
                else:
                    direction = 1
                    start = row_stop
                    stop = row_start
            # Do work
            if verbose:
                print(f'Direction = {direction}')
                print(f'Start = {start}')
                print(f'Stop  = {stop}')
            flying_zebra._encoder.pc.dir.set(direction)
            yield from fly_each_step(ymotor, step, start, stop)
            # print('return from step\t',time.time())
            ystep = ystep + 1

        # TODO this should be taken care of by stage sigs
        ion = flying_zebra.sclr
        yield from bps.mov(xs.external_trig, False, ion.count_mode, 1)
        yield from mv(nano_stage.sx, 0, nano_stage.sy, 0, nano_stage.sz, 0)
        yield from bps.sleep(2)

    # toc(t_setup, str='Setup time')

    # Setup the final scan plan
    if shutter:
        final_plan = finalize_wrapper(plan(), check_shutters(shutter, 'Close'))
    else:
        final_plan = plan()

    # Open the shutter
    if verbose:
        t_open = tic()
    yield from check_shutters(shutter, 'Open')
    if verbose:
        toc(t_open, str='Open shutter')

    # Run the scan
    uid = yield from final_plan

    return uid
Ejemplo n.º 8
0
def xy_fly(
    scan_title,
    *,
    beamline_operator,
    dwell_time,
    xstart,
    xstop,
    xstep_size,
    ystart,
    ystop,
    ystep_size=None,
    xspress3=None,
):
    """Do a x-y fly scan.

    The x-motor is the 'fast' direction.

    Parameters
    ----------
    scan_title : str
       A name for the scan.

    beamline_operator : str
       The individual responsible for this scan. Appears in output directory path.

    dwell_time : float
       Target time is s on each pixel

    xstart, xstop : float
       The start and stop values in the fast direction in mm

    xstep_size :
        xstep_size is step of x movement

    ystart, ystop : float
       The start and stop values in the slow direction in mm

    ystep_size :
        ystep_size use xstep_size if it isn't passed in

    scan_title : str
       Title of scan, required.
    """
    xy_fly_stage = xy_stage
    _validate_motor_limits(xy_fly_stage.x, xstart, xstop, "x")
    _validate_motor_limits(xy_fly_stage.y, ystart, ystop, "y")
    ystep_size = ystep_size if ystep_size is not None else xstep_size
    assert dwell_time > 0, f"dwell_time ({dwell_time}) must be more than 0"
    assert xstep_size > 0, f"xstep_size ({xstep_size}) must be more than 0"
    assert ystep_size > 0, f"ystep_size ({ystep_size}) must be more than 0"
    ret = yield from bps.read(xy_fly_stage.x.mres)  # (in mm)
    xmres = ret[
        xy_fly_stage.x.mres.name]["value"] if ret is not None else 0.0003125

    ret = yield from bps.read(xy_fly_stage.y.mres)  # (in mm)
    ymres = ret[
        xy_fly_stage.y.mres.name]["value"] if ret is not None else 0.0003125

    prescale = int(np.floor((xstep_size / (5 * xmres))))
    a_xstep_size = prescale * (5 * xmres)

    a_ystep_size = int(np.floor((ystep_size / (ymres)))) * ymres

    num_xpixels = int(np.floor((xstop - xstart) / a_xstep_size))
    num_ypixels = int(np.floor((ystop - ystart) / a_ystep_size))

    yield from bps.mv(
        x_centers,
        a_xstep_size / 2 + xstart + np.arange(num_xpixels) * a_xstep_size)

    # SRX original roi_key = getattr(xs.channel1.rois, roi_name).value.name
    roi_livegrid_key = xs.channel1.rois.roi01.value.name
    fig = plt.figure("xs")
    fig.clf()
    roi_livegrid = LiveGrid(
        (num_ypixels + 1, num_xpixels + 1),
        roi_livegrid_key,
        clim=None,
        cmap="inferno",
        xlabel="x (mm)",
        ylabel="y (mm)",
        extent=[xstart, xstop, ystart, ystop],
        x_positive="right",
        y_positive="down",
        ax=fig.gca(),
    )

    flyspeed = a_xstep_size / dwell_time  # this is in mm/s

    try:
        xy_fly_stage.x.velocity.check_value(flyspeed)
    except LimitError as e:
        raise LimitError(f"You requested a range of {xstop - xstart} with "
                         f"{num_xpixels} pixels and a dwell time of "
                         f"{dwell_time}.  This requires a "
                         f"motor velocity of {flyspeed} which "
                         "is out of range.") from e

    # set up delta-tau trigger to fast motor
    for v in ["p1600=0", "p1607=1", "p1600=1"]:
        yield from bps.mv(dtt, v)
        yield from bps.sleep(0.1)

    # TODO make this a message?
    sclr.set_mode("flying")
    # poke the struck settings
    yield from bps.mv(sclr.mcas.prescale, prescale)
    yield from bps.mv(sclr.mcas.nuse, num_xpixels)
    if xspress3 is not None:
        yield from bps.mv(xs.external_trig, True)
        yield from bps.mv(xspress3.total_points, num_xpixels)
        yield from bps.mv(xspress3.hdf5.num_capture, num_xpixels)
        yield from bps.mv(xspress3.settings.num_images, num_xpixels)

    @bpp.reset_positions_decorator([xy_fly_stage.x, xy_fly_stage.y])
    @bpp.subs_decorator({"all": [roi_livegrid]})
    @bpp.monitor_during_decorator([xs.channel1.rois.roi01.value])
    @bpp.stage_decorator([sclr])
    @bpp.baseline_decorator([mono, xy_fly_stage])
    # TODO put is other meta data
    @bpp.run_decorator(
        md={
            "scan_title": scan_title,
            "operator": beamline_operator,
            "user_input": {
                "dwell_time": dwell_time,
                "xstart": xstart,
                "xstop": xstop,
                "xstep_size": xstep_size,
                "ystart": ystart,
                "ystep_size": ystep_size,
            },
            "derived_input": {
                "actual_ystep_size": a_ystep_size,
                "actual_xstep_size": a_xstep_size,
                "fly_velocity": flyspeed,
                "xpixels": num_xpixels,
                "ypixels": num_ypixels,
                "prescale": prescale,
            },
        })
    def fly_body():

        yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, ystart)

        @bpp.stage_decorator([x for x in [xspress3] if x is not None])
        def fly_row():
            # go to start of row
            target_y = ystart + y * a_ystep_size
            yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, target_y)
            yield from bps.mv(y_centers,
                              np.ones(num_xpixels) *
                              target_y)  # set the fly speed

            ret = yield from bps.read(xy_fly_stage.z.user_readback)  # (in mm)
            zpos = (ret[xy_fly_stage.z.user_readback.name]["value"]
                    if ret is not None else 0)
            yield from bps.mov(z_centers, np.ones(num_xpixels) * zpos)

            yield from bps.mv(xy_fly_stage.x.velocity, flyspeed)

            yield from bps.trigger_and_read([xy_fly_stage], name="row_ends")

            for v in ["p1600=0", "p1600=1"]:
                yield from bps.mv(dtt, v)
                yield from bps.sleep(0.1)

            # arm the struck
            yield from bps.trigger(sclr, group=f"fly_row_{y}")
            # maybe start the xspress3
            if xspress3 is not None:
                yield from bps.trigger(xspress3, group=f"fly_row_{y}")
            yield from bps.sleep(0.1)
            # fly the motor
            yield from bps.abs_set(xy_fly_stage.x,
                                   xstop + a_xstep_size,
                                   group=f"fly_row_{y}")
            yield from bps.wait(group=f"fly_row_{y}")

            yield from bps.trigger_and_read([xy_fly_stage], name="row_ends")
            yield from bps.mv(xy_fly_stage.x.velocity, 5.0)
            yield from bps.sleep(0.1)
            # read and save the struck
            yield from bps.create(name="primary")
            #
            yield from bps.read(sclr)
            yield from bps.read(mono)
            yield from bps.read(x_centers)
            yield from bps.read(y_centers)
            yield from bps.read(z_centers)
            yield from bps.read(xy_fly_stage.y)
            yield from bps.read(xy_fly_stage.z)
            # and maybe the xspress3
            if xspress3 is not None:
                yield from bps.read(xspress3)
            yield from bps.save()

        for y in range(num_ypixels):
            if xspress3 is not None:
                yield from bps.mv(xspress3.fly_next, True)

            yield from fly_row()

    yield from fly_body()

    # save the start document to a file for the benefit of the user
    start = db[-1].start
    dt = datetime.datetime.fromtimestamp(start["time"])
    filepath = os.path.expanduser(
        f"~/Users/Data/{start['operator']}/{dt.date().isoformat()}/xy_fly/"
        f"{start['scan_title']}-{start['scan_id']}-{start['operator']}-{dt.time().isoformat()}.log"
    )
    os.makedirs(os.path.dirname(filepath), exist_ok=True)
    with open(filepath, "wt") as output_file:
        output_file.write(pprint.pformat(start))
def scan_and_fly_base(detectors, xstart, xstop, xnum, ystart, ystop, ynum, dwell, *,
                      flying_zebra, xmotor, ymotor,
                      delta=None, shutter=True, align=False,
                      md=None):
    """Read IO from SIS3820.
    Zebra buffers x(t) points as a flyer.
    Xpress3 is our detector.
    The aerotech has the x and y positioners.
    delta should be chosen so that it takes about 0.5 sec to reach the gate??
    ymotor  slow axis
    xmotor  fast axis

    Parameters
    ----------
    Detectors : List[Device]
       These detectors must be known to the zebra

    xstart, xstop : float
    xnum : int
    ystart, ystop : float
    ynum : int
    dwell : float
       Dwell time in seconds

    flying_zebra : SRXFlyer1Axis

    xmotor, ymotor : EpicsMotor, kwarg only
        These should be known to the zebra
        # TODO sort out how to check this

    delta : float, optional, kwarg only
       offset on the ystage start position.  If not given, derive from
       dwell + pixel size
    align : bool, optional, kwarg only
       If True, try to align the beamline
    shutter : bool, optional, kwarg only
       If True, try to open the shutter
    """

    # t_setup = tic()

    # Check for negative number of points
    if (xnum < 1 or ynum < 1):
        print('Error: Number of points is negative.')
        return

    # Set metadata
    if md is None:
        md = {}

    # Change retry deadband for hf_stage.x and hf_stage.y
    if (hf_stage.x in (xmotor, ymotor)):
        OLD_RDBD_X = hf_stage.RETRY_DEADBAND_X.get()
        NEW_RDBD_X = 2e-4
        hf_stage.RETRY_DEADBAND_X.put(NEW_RDBD_X)
    if (hf_stage.y in (xmotor, ymotor)):
        OLD_RDBD_Y = hf_stage.RETRY_DEADBAND_Y.get()
        NEW_RDBD_Y = 2e-4
        hf_stage.RETRY_DEADBAND_Y.put(NEW_RDBD_Y)

    # assign detectors to flying_zebra, this may fail
    flying_zebra.detectors = detectors
    # Setup detectors, combine the zebra, sclr, and the just set detector list
    detectors = (flying_zebra.encoder, flying_zebra.sclr) + flying_zebra.detectors

    dets_by_name = {d.name : d
                    for d in detectors}

    # set up the merlin
    if 'merlin' in dets_by_name:
        dpc = dets_by_name['merlin']
        # TODO use stage sigs
        # Set trigger mode
        # dpc.cam.trigger_mode.put(2)
        # Make sure we respect whatever the exposure time is set to
        if (dwell < 0.0066392):
            print('The Merlin should not operate faster than 7 ms.')
            print('Changing the scan dwell time to 7 ms.')
            dwell = 0.007
        # According to Ken's comments in hxntools, this is a de-bounce time
        # when in external trigger mode
        dpc.cam.stage_sigs['acquire_time'] = 0.50 * dwell - 0.0016392
        dpc.cam.stage_sigs['acquire_period'] = 0.75 * dwell
        dpc.cam.stage_sigs['num_images'] = 1
        dpc.stage_sigs['total_points'] = xnum
        dpc.hdf5.stage_sigs['num_capture'] = xnum
        del dpc

    # Setup dexela
    if ('dexela' in dets_by_name):
        xrd = dets_by_name['dexela']
        xrd.cam.stage_sigs['acquire_time'] = 0.50 * dwell - 0.050
        xrd.cam.stage_sigs['acquire_period'] = 0.50 * dwell - 0.020
        del xrd

    # If delta is None, set delta based on time for acceleration
    if (delta is None):
        if (flying_zebra.encoder.pc.egu.get() == 'mm'):
            MIN_DELTA = 0.002  # old default value
            v = ((xstop - xstart) / (xnum-1)) / dwell  # compute "stage speed"
            t_acc = 1.0  # acceleration time, default 1.0 s
            delta = t_acc * v  # distance the stage will travel in t_acc
            delta = np.amax((delta, MIN_DELTA))
        else:
            delta = 0.500 #was 2.5 when npoint scanner drifted

    # Move to start scanning location
    #if ('nano_stage' in xmotor.name):
    #    yield from mv(xmotor.velocity, 30)
    #    yield from mv(xmotor.velocity, 30)
    yield from mv(xmotor, xstart - delta,
                  ymotor, ystart)

    # Run a peakup before the map?
    if (align):
        yield from peakup_fine(shutter=shutter)

    md = ChainMap(md, {
        'plan_name': 'scan_and_fly',
        'detectors': [d.name for d in detectors],
        'dwell': dwell,
        'shape': (xnum, ynum),
        'scaninfo': {'type': 'XRF_fly',
                     'raster': False,
                     'fast_axis': flying_zebra.fast_axis.get()},
                     # 'slow_axis': flying_zebra.slow_axis.get(),
                     # 'theta': hf_stage.th.position}
        'scan_params': [xstart, xstop, xnum, ystart, ystop, ynum, dwell],
        'scan_input': [xstart, xstop, xnum, ystart, ystop, ynum, dwell],
        'delta': delta,
        'beamline_status' : {'energy' : energy.position.energy}
        }
    )

    if ('xs2' in dets_by_name):
        md['scaninfo']['type'] = 'XRF_E_tomo_fly'

    @stage_decorator(flying_zebra.detectors)
    def fly_each_step(motor, step):
        def move_to_start_fly():
            "See http://nsls-ii.github.io/bluesky/plans.html#the-per-step-hook"
            yield from abs_set(xmotor, xstart-delta, group='row')
            yield from one_1d_step([temp_nanoKB], motor, step)
            yield from bps.wait(group='row')

        # t_mvstartfly = tic()
        yield from move_to_start_fly()
        # toc(t_mvstartfly, str='Move to start fly each')

        # TODO  Why are we re-trying the move?  This should be fixed at
        # a lower level
        # yield from bps.sleep(1.0)  # wait for the "x motor" to move
        x_set = xstart - delta
        x_dial = xmotor.user_readback.get()
        # Get retry deadband value and check against that
        i = 0
        if (xmotor.egu == 'mm'):
            DEADBAND = 0.0005
        else:
            DEADBAND = 0.02
        while (np.abs(x_set - x_dial) > DEADBAND):
            if (i == 0):
                print('Waiting for motor to reach starting position...',
                      end='', flush=True)
            i = i + 1
            yield from mv(xmotor, xstart - delta)
            yield from bps.sleep(0.1)
            x_dial = xmotor.user_readback.get()
        if (i != 0):
            print('done')

        # Set the scan speed
        # Is abs_set(wait=True) or mv() faster?
        v = ((xstop - xstart) / (xnum-1)) / dwell  # compute "stage speed"
        # yield from abs_set(xmotor.velocity, v, wait=True)  # set the "stage speed"
        yield from mv(xmotor.velocity, v)
        # Change backlash speed for hf_stage.x and hf_stage.y
        if (hf_stage.x is xmotor):
            yield from mv(hf_stage.BACKLASH_SPEED_X, v)
        if (hf_stage.y is xmotor):
            yield from mv(hf_stage.BACKLASH_SPEED_Y, v)

        # set up all of the detectors
        # TODO we should be able to move this out of the per-line call?!
        if ('xs' in dets_by_name):
            xs = dets_by_name['xs']
            # yield from abs_set(xs.hdf5.num_capture, xnum, wait=True)
            # yield from abs_set(xs.settings.num_images, xnum, wait=True)
            yield from mv(xs.hdf5.num_capture, xnum,
                          xs.settings.num_images, xnum)

        if ('xs2' in dets_by_name):
            xs2 = dets_by_name['xs2']
            # yield from abs_set(xs2.hdf5.num_capture, xnum, wait=True)
            # yield from abs_set(xs2.settings.num_images, xnum, wait=True)
            yield from mv(xs2.hdf5.num_capture, xnum,
                          xs2.settings.num_images, xnum)

        if ('merlin' in dets_by_name):
            merlin = dets_by_name['merlin']
            yield from abs_set(merlin.hdf5.num_capture, xnum, wait=True)
            yield from abs_set(merlin.cam.num_images, xnum, wait=True)

        if ('dexela' in dets_by_name):
            dexela = dets_by_name['dexela']
            yield from abs_set(dexela.hdf5.num_capture, xnum, wait=True)
            yield from abs_set(dexela.cam.num_images, xnum, wait=True)

        ion = flying_zebra.sclr
        yield from abs_set(ion.nuse_all,xnum)

        # arm the Zebra (start caching x positions)
        # @timer_wrapper
        def zebra_kickoff():
            yield from kickoff(flying_zebra,
                               xstart=xstart, xstop=xstop, xnum=xnum, dwell=dwell,
                               wait=True)
        # t_zebkickoff = tic()
        yield from zebra_kickoff()
        # toc(t_zebkickoff, str='Zebra kickoff')

        # arm SIS3820, note that there is a 1 sec delay in setting X
        # into motion so the first point *in each row* won't
        # normalize...
        yield from abs_set(ion.erase_start, 1)

        # trigger all of the detectors
        for d in flying_zebra.detectors:
            yield from bps.trigger(d, group='row')
            if (d.name == 'dexela'):
                yield from bps.sleep(1)

        yield from bps.sleep(1.5)
        # start the 'fly'
        yield from abs_set(xmotor, xstop + 1*delta, group='row')  # move in x
        # wait for the motor and detectors to all agree they are done
        yield from bps.wait(group='row')

        # we still know about ion from above
        yield from abs_set(ion.stop_all, 1)  # stop acquiring scaler

        # @timer_wrapper
        def zebra_complete():
            yield from complete(flying_zebra)  # tell the Zebra we are done
        # t_zebcomplete = tic()
        yield from zebra_complete()
        # toc(t_zebcomplete, str='Zebra complete')


        # @timer_wrapper
        def zebra_collect():
            yield from collect(flying_zebra)  # extract data from Zebra
        # t_zebcollect = tic()
        yield from zebra_collect()
        # toc(t_zebcollect, str='Zebra collect')

        # TODO what?
        if ('e_tomo' in xmotor.name):
            v_return = min(4, xmotor.velocity.high_limit)
            yield from mv(xmotor.velocity, v_return)
        if ('nano_stage' in xmotor.name):
            yield from mv(xmotor.velocity, 30)
        else:
            # set the "stage speed"
            yield from mv(xmotor.velocity, 1.0)

    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(md['scaninfo']['type'])
        scanrecord.scanning.put(True)
        scanrecord.time_remaining.put((dwell*xnum + 3.8)/3600)

    def finalize_scan(name, doc):
        logscan_detailed('xrf_fly')
        scanrecord.scanning.put(False)
        scanrecord.time_remaining.put(0)

        if (hf_stage.x in (xmotor, ymotor)):
            hf_stage.RETRY_DEADBAND_X.put(OLD_RDBD_X)
        if (hf_stage.y in (xmotor, ymotor)):
            hf_stage.RETRY_DEADBAND_Y.put(OLD_RDBD_Y)

    # TODO remove this eventually?
    # xs = dets_by_name['xs']
    # xs = dets_by_name['xs2']
    # Not sure if this is always true
    xs = dets_by_name[flying_zebra.detectors[0].name]

    yield from mv(xs.erase, 0)

    # Setup LivePlot
    if (ynum == 1):
        # livepopup = LivePlot(xs.channel1.rois.roi01.value.name)
        livepopup = SRX1DFlyerPlot(xs.channel1.rois.roi01.value.name,
                                   xstart=xstart,
                                   xstep=(xstop-xstart)/(xnum-1),
                                   xlabel=xmotor.name)
    else:
        livepopup = LiveGrid((ynum, xnum+1),
                             xs.channel1.rois.roi01.value.name,
                             extent=(xstart, xstop, ystart, ystop),
                             x_positive='right', y_positive='down')
        # livepopup = ArrayCounterLiveGrid(
        #     (ynum, xnum + 1),
        #     xs.channel1.rois.roi01.value.name,
        #     array_counter_key=xs.array_counter.name,
        #     extent=(xstart, xstop, ystart, ystop),
        #     x_positive='right', y_positive='down'
        # )
    @subs_decorator([livepopup])
    @subs_decorator({'start': at_scan})
    @subs_decorator({'stop': finalize_scan})
    # monitor values from xs
    # @monitor_during_decorator([xs.channel1.rois.roi01.value])
    @monitor_during_decorator([xs.channel1.rois.roi01.value, xs.array_counter])
    @stage_decorator([flying_zebra])  # Below, 'scan' stage ymotor.
    @run_decorator(md=md)
    def plan():
        # TODO move this to stage sigs
        for d in flying_zebra.detectors:
            yield from bps.mov(d.total_points, xnum)

        # TODO move this to stage sigs
        yield from bps.mov(xs.external_trig, True)
        ystep = 0

        for step in np.linspace(ystart, ystop, ynum):
            yield from abs_set(scanrecord.time_remaining,
                               (ynum - ystep) * ( dwell * xnum + 3.8 ) / 3600.)
            ystep = ystep + 1
            # 'arm' the all of the detectors for outputting fly data
            for d in flying_zebra.detectors:
                yield from bps.mov(d.fly_next, True)
            # print('h5 armed\t',time.time())
            yield from fly_each_step(ymotor, step)
            # print('return from step\t',time.time())

        # TODO this should be taken care of by stage sigs
        ion = flying_zebra.sclr
        yield from bps.mov(xs.external_trig, False,
                           ion.count_mode, 1)
    # toc(t_setup, str='Setup time')

    # Setup the final scan plan
    if shutter:
        final_plan = finalize_wrapper(plan(),
                                      bps.mov(shut_b, 'Close'))
    else:
        final_plan = plan()

    # Open the shutter
    if shutter:
        # t_open = tic()
        yield from mv(shut_b, 'Open')
        # toc(t_open, str='Open shutter')

    # Run the scan
    uid = yield from final_plan

    # Close the shutter
    # if shutter:
    #     # t_close = tic()
    #     yield from mv(shut_b, 'Close')
    #     # toc(t_close, str='Close shutter')

    # Return sample stage defaults
    if ('hf_stage' in xmotor.name):
        hf_stage.reset_stage_defaults()

    return uid
Ejemplo n.º 10
0
    def main_plan(detector, slow, startslow, stopslow, nslow, fast, startfast,
                  stopfast, nfast, pluck, force, dwell, md):
        (ok, text) = BMM_clear_to_start()
        if force is False and ok is False:
            print(error_msg(text))
            BMMuser.final_log_entry = False
            yield from null()
            return

        RE.msg_hook = None

        ## sanity checks on slow axis
        if type(slow) is str: slow = slow.lower()
        if slow not in motor_nicknames.keys() and 'EpicsMotor' not in str(
                type(slow)) and 'PseudoSingle' not in str(type(slow)):
            print(
                error_msg('\n*** %s is not an areascan motor (%s)\n' %
                          (slow, str.join(', ', motor_nicknames.keys()))))
            BMMuser.final_log_entry = False
            yield from null()
            return
        if slow in motor_nicknames.keys():
            slow = motor_nicknames[slow]

        ## sanity checks on fast axis
        if type(fast) is str: fast = fast.lower()
        if fast not in motor_nicknames.keys() and 'EpicsMotor' not in str(
                type(fast)) and 'PseudoSingle' not in str(type(fast)):
            print(
                error_msg('\n*** %s is not an areascan motor (%s)\n' %
                          (fast, str.join(', ', motor_nicknames.keys()))))
            BMMuser.final_log_entry = False
            yield from null()
            return
        if fast in motor_nicknames.keys():
            fast = motor_nicknames[fast]

        detector = detector.capitalize()
        yield from abs_set(_locked_dwell_time, dwell, wait=True)
        dets = [
            quadem1,
        ]
        if detector == 'If':
            dets.append(vor)
            detector = 'ROI1'
        if detector.lower() == 'xs':
            dets.append(xs)
            detector = BMMuser.xs1

        if 'PseudoSingle' in str(type(slow)):
            valueslow = slow.readback.get()
        else:
            valueslow = slow.user_readback.get()
            line1 = 'slow motor: %s, %.3f, %.3f, %d -- starting at %.3f\n' % \
                    (slow.name, startslow, stopslow, nslow, valueslow)

        if 'PseudoSingle' in str(type(fast)):
            valuefast = fast.readback.get()
        else:
            valuefast = fast.user_readback.get()
        line2 = 'fast motor: %s, %.3f, %.3f, %d -- starting at %.3f\n' % \
                (fast.name, startfast, stopfast, nfast, valuefast)

        npoints = nfast * nslow
        estimate = int(npoints * (dwell + 0.7))

        # extent = (
        #     valuefast + startfast,
        #     valueslow + startslow,
        #     valuefast + stopfast,
        #     valueslow + stopslow,
        # )
        # extent = (
        #     0,
        #     nfast-1,
        #     0,
        #     nslow-1
        # )
        # print(extent)
        # return(yield from null())

        # areaplot = LiveScatter(fast.name, slow.name, detector,
        #                        xlim=(startfast, stopfast), ylim=(startslow, stopslow))

        areaplot = LiveGrid(
            (nslow, nfast),
            detector,  #aspect='equal', #aspect=float(nslow/nfast), extent=extent,
            xlabel='fast motor: %s' % fast.name,
            ylabel='slow motor: %s' % slow.name)
        #BMMuser.ax     = areaplot.ax
        #BMMuser.fig    = areaplot.ax.figure
        BMMuser.motor = fast
        BMMuser.motor2 = slow
        #BMMuser.fig.canvas.mpl_connect('close_event', handle_close)

        thismd = dict()
        thismd['XDI'] = dict()
        thismd['XDI']['Facility'] = dict()
        thismd['XDI']['Facility']['GUP'] = BMMuser.gup
        thismd['XDI']['Facility']['SAF'] = BMMuser.saf
        thismd['slow_motor'] = slow.name
        thismd['fast_motor'] = fast.name

        ## engage suspenders right before starting scan sequence
        if force is False: BMM_suspenders()

        @subs_decorator(areaplot)
        #@subs_decorator(src.callback)
        def make_areascan(dets,
                          slow,
                          startslow,
                          stopslow,
                          nslow,
                          fast,
                          startfast,
                          stopfast,
                          nfast,
                          snake=False):
            BMMuser.final_log_entry = False
            uid = yield from grid_scan(dets, slow, startslow, stopslow, nslow,
                                       fast, startfast, stopfast, nfast, snake)
            BMMuser.final_log_entry = True
            return uid

        rkvs.set('BMM:scan:type', 'area')
        rkvs.set('BMM:scan:starttime',
                 str(datetime.datetime.timestamp(datetime.datetime.now())))
        rkvs.set('BMM:scan:estimated', estimate)

        BMM_log_info('begin areascan observing: %s\n%s%s' %
                     (detector, line1, line2))
        uid = yield from make_areascan(dets, slow, valueslow + startslow,
                                       valueslow + stopslow, nslow, fast,
                                       valuefast + startfast,
                                       valuefast + stopfast, nfast, False)

        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())
            print(
                'Single click the left mouse button on the plot to pluck a point...'
            )
            cid = BMMuser.fig.canvas.mpl_connect(
                'button_press_event',
                interpret_click)  # see 65-derivedplot.py and
            while BMMuser.x is None:  #  https://matplotlib.org/users/event_handling.html
                yield from sleep(0.5)

            print('Converting plot coordinates to real coordinates...')
            begin = valuefast + startfast
            stepsize = (stopfast - startfast) / (nfast - 1)
            pointfast = begin + stepsize * BMMuser.x
            #print(BMMuser.x, pointfast)

            begin = valueslow + startslow
            stepsize = (stopslow - startslow) / (nslow - 1)
            pointslow = begin + stepsize * BMMuser.y
            #print(BMMuser.y, pointslow)

            print('That translates to x=%.3f, y=%.3f' % (pointfast, pointslow))
            yield from mv(fast, pointfast, slow, pointslow)
Ejemplo n.º 11
0
    def descriptor(self, doc):
        self._descriptors[doc['uid']] = doc
        stream_name = doc.get('name', 'primary')  # fall back for old docs

        if stream_name not in self._stream_names_seen:
            self._stream_names_seen.add(stream_name)
            if self._table_enabled:
                print("New stream: {!r}".format(stream_name))

        columns = hinted_fields(doc)

        # ## This deals with old documents. ## #

        if stream_name == 'primary' and self._cleanup_motor_heuristic:
            # We stashed object names in self.dim_fields, which we now need to
            # look up the actual fields for.
            self._cleanup_motor_heuristic = False
            fixed_dim_fields = []
            for obj_name in self.dim_fields:
                try:
                    fields = doc.get('hints', {}).get(obj_name, {})['fields']
                except KeyError:
                    fields = doc['object_keys'][obj_name]
                fixed_dim_fields.extend(fields)
            self.dim_fields = fixed_dim_fields

        # ## TABLE ## #

        if stream_name == self.dim_stream:
            # Ensure that no independent variables ('dimensions') are
            # duplicated here.
            columns = [c for c in columns if c not in self.dim_fields]

            if self._table_enabled:
                self._table = LiveTable(list(self.dim_fields) + columns)
                self._table('start', self._start_doc)
                self._table('descriptor', doc)

        # ## DECIDE WHICH KIND OF PLOT CAN BE USED ## #

        if not self._plots_enabled:
            return
        if stream_name in self.noplot_streams:
            return
        if ((self._start_doc.get('num_points') == 1) and
                (stream_name == self.dim_stream) and
                self.omit_single_point_plot):
            return

        # This is a heuristic approach until we think of how to hint this in a
        # generalizable way.
        if stream_name == self.dim_stream:
            dim_fields = self.dim_fields
        else:
            dim_fields = ['time']  # 'time' once LivePlot can do that

        # Create a figure or reuse an existing one.

        fig_name = '{} vs {}'.format(' '.join(sorted(columns)),
                                     ' '.join(sorted(dim_fields)))
        if self.overplot and len(dim_fields) == 1:
            # If any open figure matches 'figname {number}', use it. If there
            # are multiple, the most recently touched one will be used.
            pat1 = re.compile('^' + fig_name + '$')
            pat2 = re.compile('^' + fig_name + ' \d+$')
            for label in plt.get_figlabels():
                if pat1.match(label) or pat2.match(label):
                    fig_name = label
                    break
        else:
            if plt.fignum_exists(fig_name):
                # Generate a unique name by appending a number.
                for number in itertools.count(2):
                    new_name = '{} {}'.format(fig_name, number)
                    if not plt.fignum_exists(new_name):
                        fig_name = new_name
                        break
        ndims = len(dim_fields)
        if not 0 < ndims < 3:
            # we need 1 or 2 dims to do anything, do not make empty figures
            return

        fig = plt.figure(fig_name)
        if not fig.axes:
            # This is apparently a fresh figure. Make axes.
            # The complexity here is due to making a shared x axis. This can be
            # simplified when Figure supports the `subplots` method in a future
            # release of matplotlib.
            fig.set_size_inches(6.4, min(950, len(columns) * 400) / fig.dpi)
            for i in range(len(columns)):
                if i == 0:
                    ax = fig.add_subplot(len(columns), 1, 1 + i)
                    if ndims == 1:
                        share_kwargs = {'sharex': ax}
                    elif ndims == 2:
                        share_kwargs = {'sharex': ax, 'sharey': ax}
                    else:
                        raise NotImplementedError("we now support 3D?!")
                else:
                    ax = fig.add_subplot(len(columns), 1, 1 + i,
                                         **share_kwargs)
        axes = fig.axes

        # ## LIVE PLOT AND PEAK ANALYSIS ## #

        if ndims == 1:
            self._live_plots[doc['uid']] = {}
            self._peak_stats[doc['uid']] = {}
            x_key, = dim_fields
            for y_key, ax in zip(columns, axes):
                dtype = doc['data_keys'][y_key]['dtype']
                if dtype not in ('number',):
                    warn("Omitting {} from plot because dtype is {}"
                         "".format(y_key, dtype))
                    continue
                # Create an instance of LivePlot and an instance of PeakStats.
                live_plot = LivePlotPlusPeaks(y=y_key, x=x_key, ax=ax,
                                              peak_results=self.peaks)
                live_plot('start', self._start_doc)
                live_plot('descriptor', doc)
                peak_stats = PeakStats(x=x_key, y=y_key)
                peak_stats('start', self._start_doc)
                peak_stats('descriptor', doc)

                # Stash them in state.
                self._live_plots[doc['uid']][y_key] = live_plot
                self._peak_stats[doc['uid']][y_key] = peak_stats

            for ax in axes[:-1]:
                ax.set_xlabel('')
        elif ndims == 2:
            # Decide whether to use LiveGrid or LiveScatter. LiveScatter is the
            # safer one to use, so it is the fallback..
            gridding = self._start_doc.get('hints', {}).get('gridding')
            if gridding == 'rectilinear':
                self._live_grids[doc['uid']] = {}
                x_key, y_key = dim_fields
                try:
                    extents = self._start_doc['extents']
                    shape = self._start_doc['shape']
                except KeyError:
                    warn("Need both 'shape' and 'extents' in plan metadata to "
                         "create LiveGrid.")
                else:
                    data_range = np.array([float(np.diff(e)) for e in extents])
                    y_step, x_step = data_range / [s - 1 for s in shape]
                    adjusted_extent = [extents[1][0] - x_step / 2,
                                       extents[1][1] + x_step / 2,
                                       extents[0][0] - y_step / 2,
                                       extents[0][1] + y_step / 2]
                    for I_key, ax in zip(columns, axes):
                        # MAGIC NUMBERS based on what tacaswell thinks looks OK
                        data_aspect_ratio = np.abs(data_range[1]/data_range[0])
                        MAR = 2
                        if (1/MAR < data_aspect_ratio < MAR):
                            aspect = 'equal'
                            ax.set_aspect(aspect, adjustable='box-forced')
                        else:
                            aspect = 'auto'
                            ax.set_aspect(aspect, adjustable='datalim')

                        live_grid = LiveGrid(shape, I_key,
                                             xlabel=x_key, ylabel=y_key,
                                             extent=adjusted_extent,
                                             aspect=aspect,
                                             ax=ax)

                        live_grid('start', self._start_doc)
                        live_grid('descriptor', doc)
                        self._live_grids[doc['uid']][I_key] = live_grid
            else:
                self._live_scatters[doc['uid']] = {}
                x_key, y_key = dim_fields
                for I_key, ax in zip(columns, axes):
                    try:
                        extents = self._start_doc['extents']
                    except KeyError:
                        xlim = ylim = None
                    else:
                        xlim, ylim = extents
                    live_scatter = LiveScatter(x_key, y_key, I_key,
                                               xlim=xlim, ylim=ylim,
                                               # Let clim autoscale.
                                               ax=ax)
                    live_scatter('start', self._start_doc)
                    live_scatter('descriptor', doc)
                    self._live_scatters[doc['uid']][I_key] = live_scatter
        else:
            raise NotImplementedError("we do not support 3D+ in BEC yet "
                                      "(and it should have bailed above)")

        fig.tight_layout()
Ejemplo n.º 12
0
def scan_and_fly(xstart, xstop, xnum, ystart, ystop, ynum, dwell, *,
                 delta=None, shutter=True,
                 xmotor=hf_stage.x, ymotor=hf_stage.y,
                 xs=xs, ion=sclr1, align=False,
                 flying_zebra=flying_zebra, md=None):
    """

    Read IO from SIS3820.
    Zebra buffers x(t) points as a flyer.
    Xpress3 is our detector.
    The aerotech has the x and y positioners.
    delta should be chosen so that it takes about 0.5 sec to reach the gate??
    ymotor  slow axis
    xmotor  fast axis
    """
    c2pitch_kill = EpicsSignal("XF:05IDA-OP:1{Mono:HDCM-Ax:P2}Cmd:Kill-Cmd")
    if md is None:
        md = {}

    # If delta is None, set delta based on time for acceleration
    if delta is None:
        # delta = 0.002  # old default value
        v = (xstop - xstart) / (xnum-1) / dwell  # compute "stage speed"
        t_acc = 1.0  # acceleration time, default 1.0 s
        delta = t_acc * v  # distance the stage will travel in t_acc
    
    yield from abs_set(ymotor, ystart, wait=True) # ready to move
    yield from abs_set(xmotor, xstart - delta, wait=True) # ready to move

    if shutter is True:
        yield from mv(shut_b, 'Open')

    if align:
        fly_ps = PeakStats(dcm.c2_pitch.name, i0.name)
        align_scan = scan([sclr1], dcm.c2_pitch, -19.320, -19.360, 41)
        align_scan = bp.subs_wrapper(align_scan, fly_ps)
        yield from align_scan
        yield from abs_set(dcm.c2_pitch, fly_ps.max[0], wait=True)
        #ttime.sleep(10)
        #yield from abs_set(c2pitch_kill, 1)

    md = ChainMap(md, {
        'plan_name': 'scan_and_fly',
        'detectors': [zebra.name, xs.name, ion.name],
        'dwell': dwell,
        'shape': (xnum, ynum),
        'scaninfo': {'type': 'XRF_fly',
                     'raster': False,
                     'fast_axis': flying_zebra._fast_axis},
        # 'scaninfo': {'type': 'E_tomo',
        #              'raster': False,
        #              'fast_axis': flying_zebra._fast_axis},
        'scan_params': [xstart, xstop, xnum, ystart, ystop, ynum, dwell],
        'scan_input': [xstart, xstop, xnum, ystart, ystop, ynum, dwell],
        'delta': delta
        }
    )

    if (xs.name == 'xs2'):
        md['scaninfo']['type'] = 'XRF_E_tomo_fly'

    @stage_decorator([xs])
    def fly_each_step(detectors, motor, step, firststep):
        "See http://nsls-ii.github.io/bluesky/plans.html#the-per-step-hook"
        # First, let 'scan' handle the normal y step, including a checkpoint.
        yield from one_1d_step(detectors, motor, step)

        # Now do the x steps.
        v = (xstop - xstart) / (xnum-1) / dwell  # compute "stage speed"
        yield from abs_set(xmotor, xstart - delta, wait=True) # ready to move
        yield from abs_set(xmotor.velocity, v, wait=True)  # set the "stage speed"

        yield from abs_set(xs.hdf5.num_capture, xnum, wait=True)
        yield from abs_set(xs.settings.num_images, xnum, wait=True)
        yield from abs_set(ion.nuse_all,xnum)
        # arm the Zebra (start caching x positions)


        yield from kickoff(flying_zebra, xstart=xstart, xstop=xstop, xnum=xnum, dwell=dwell, wait=True)
        yield from abs_set(ion.erase_start, 1) # arm SIS3820, note that there is a 1 sec delay in setting X into motion
                                               # so the first point *in each row* won't normalize...
        yield from bps.trigger(xs, group='row')
        #if firststep == True:
        #    ttime.sleep(0.)
        yield from bps.sleep(1.5)
        yield from abs_set(xmotor, xstop+1*delta, group='row')  # move in x
        yield from bps.wait(group='row')
        # yield from abs_set(xs.settings.acquire, 0)  # stop acquiring images
        yield from abs_set(ion.stop_all, 1)  # stop acquiring scaler
        yield from complete(flying_zebra)  # tell the Zebra we are done
        yield from collect(flying_zebra)  # extract data from Zebra
        if ('e_tomo' in xmotor.name):
            v_return = 4
            v_max = xmotor.velocity.high_limit
            if (v_return > v_max):
                xmotor.velocity.set(v_max)
            else:
                xmotor.velocity.set(v_return)
        else:
            yield from abs_set(xmotor.velocity, 1.0, wait=True)  # set the "stage speed"

    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(md['scaninfo']['type'])
        scanrecord.scanning.put(True)
        scanrecord.time_remaining.put((dwell*xnum + 3.8)/3600)

    def finalize_scan(name, doc):
        logscan_detailed('xrf_fly')
        scanrecord.scanning.put(False)
        scanrecord.time_remaining.put(0)

    #@subs_decorator([LiveTable([ymotor]), RowBasedLiveGrid((ynum, xnum), ion.name, row_key=ymotor.name), LiveZebraPlot()])
    #@subs_decorator([LiveTable([ymotor]), LiveGrid((ynum, xnum), sclr1.mca1.name)])
    @subs_decorator([LiveGrid((ynum, xnum+1),
                              xs.channel1.rois.roi01.value.name,
                              extent=(xstart, xstop, ystop, ystart))])
    @subs_decorator({'start': at_scan})
    @subs_decorator({'stop': finalize_scan})
    # monitor values from xs
    @monitor_during_decorator([xs.channel1.rois.roi01.value])
    #@monitor_during_decorator([xs], run=False)  # monitor values from xs
    @stage_decorator([flying_zebra])  # Below, 'scan' stage ymotor.
    @run_decorator(md=md)
    def plan():
        yield from bps.mov(xs.total_points, xnum)
        # added to "prime" the detector
        #yield from abs_set(xs.settings.trigger_mode, 'TTL Veto Only')

        yield from bps.mov(xs.external_trig, True)
        ystep = 0

        for step in np.linspace(ystart, ystop, ynum):
            yield from abs_set(scanrecord.time_remaining,
                               (ynum - ystep) * ( dwell * xnum + 3.8 ) / 3600.)
            ystep = ystep + 1
            # 'arm' the xs for outputting fly data
            yield from bps.mov(xs.fly_next, True)
#            print('h5 armed\t',time.time())
            if step == ystart:
                firststep = True
            else:
                firststep = False
            yield from fly_each_step([], ymotor, step, firststep)
#            print('return from step\t',time.time())
        yield from bps.mov(xs.external_trig, False,
                          ion.count_mode, 1)
        if shutter is True:
            yield from mv(shut_b, 'Close')

    return (yield from plan())
Ejemplo n.º 13
0
    def main_plan(detector, slow, startslow, stopslow, nslow, fast, startfast,
                  stopfast, nfast, pluck, force, dwell, md):
        (ok, text) = BMM_clear_to_start()
        if force is False and ok is False:
            print(error_msg(text))
            BMMuser.final_log_entry = False
            yield from null()
            return

        user_ns['RE'].msg_hook = None

        ## sanity checks on slow axis
        if type(slow) is str: slow = slow.lower()
        if slow not in motor_nicknames.keys() and 'EpicsMotor' not in str(
                type(slow)) and 'PseudoSingle' not in str(type(slow)):
            print(
                error_msg('\n*** %s is not an areascan motor (%s)\n' %
                          (slow, str.join(', ', motor_nicknames.keys()))))
            BMMuser.final_log_entry = False
            yield from null()
            return
        if slow in motor_nicknames.keys():
            slow = motor_nicknames[slow]

        ## sanity checks on fast axis
        if type(fast) is str: fast = fast.lower()
        if fast not in motor_nicknames.keys() and 'EpicsMotor' not in str(
                type(fast)) and 'PseudoSingle' not in str(type(fast)):
            print(
                error_msg('\n*** %s is not an areascan motor (%s)\n' %
                          (fast, str.join(', ', motor_nicknames.keys()))))
            BMMuser.final_log_entry = False
            yield from null()
            return
        if fast in motor_nicknames.keys():
            fast = motor_nicknames[fast]

        detector = detector.capitalize()
        yield from mv(_locked_dwell_time, dwell)
        dets = [
            quadem1,
        ]

        if with_xspress3 and detector == 'If':
            detector = 'Xs'

        if detector == 'If':
            dets.append(vor)
            detector = 'ROI1'
        if detector.lower() == 'xs':
            dets.append(xs)
            detector = BMMuser.xs1
            yield from mv(xs.total_points, nslow * nfast)

        if 'PseudoSingle' in str(type(slow)):
            valueslow = slow.readback.get()
        else:
            valueslow = slow.user_readback.get()
        line1 = 'slow motor: %s, %.3f, %.3f, %d -- starting at %.3f\n' % \
            (slow.name, startslow, stopslow, nslow, valueslow)

        if 'PseudoSingle' in str(type(fast)):
            valuefast = fast.readback.get()
        else:
            valuefast = fast.user_readback.get()
        line2 = 'fast motor: %s, %.3f, %.3f, %d -- starting at %.3f\n' % \
            (fast.name, startfast, stopfast, nfast, valuefast)

        npoints = nfast * nslow
        estimate = int(npoints * (dwell + 0.7))

        # extent = (
        #     valuefast + startfast,
        #     valueslow + startslow,
        #     valuefast + stopfast,
        #     valueslow + stopslow,
        # )
        # extent = (
        #     0,
        #     nfast-1,
        #     0,
        #     nslow-1
        # )
        # print(extent)
        # return(yield from null())

        # areaplot = LiveScatter(fast.name, slow.name, detector,
        #                        xlim=(startfast, stopfast), ylim=(startslow, stopslow))

        close_all_plots()

        areaplot = LiveGrid(
            (nslow, nfast),
            detector,  #aspect='equal', #aspect=float(nslow/nfast), extent=extent,
            xlabel='fast motor: %s' % fast.name,
            ylabel='slow motor: %s' % slow.name)
        #BMMuser.ax     = areaplot.ax
        #BMMuser.fig    = areaplot.ax.figure
        BMMuser.motor = fast
        BMMuser.motor2 = slow
        #BMMuser.fig.canvas.mpl_connect('close_event', handle_close)

        thismd = dict()
        thismd['XDI'] = dict()
        thismd['XDI']['Facility'] = dict()
        thismd['XDI']['Facility']['GUP'] = BMMuser.gup
        thismd['XDI']['Facility']['SAF'] = BMMuser.saf
        thismd['slow_motor'] = slow.name
        thismd['fast_motor'] = fast.name

        report(
            f'Starting areascan at x,y = {fast.position:.3f}, {slow.position:.3f}',
            level='bold',
            slack=True)

        ## engage suspenders right before starting scan sequence
        if force is False: BMM_suspenders()

        @subs_decorator(areaplot)
        #@subs_decorator(src.callback)
        def make_areascan(dets,
                          slow,
                          startslow,
                          stopslow,
                          nslow,
                          fast,
                          startfast,
                          stopfast,
                          nfast,
                          snake=False):
            BMMuser.final_log_entry = False
            uid = yield from grid_scan(
                dets,
                slow,
                startslow,
                stopslow,
                nslow,
                fast,
                startfast,
                stopfast,
                nfast,
                snake,
                md={
                    'plan_name':
                    f'grid_scan measurement {slow.name} {fast.name} {detector}'
                })
            BMMuser.final_log_entry = True
            return uid

        rkvs.set('BMM:scan:type', 'area')
        rkvs.set('BMM:scan:starttime',
                 str(datetime.datetime.timestamp(datetime.datetime.now())))
        rkvs.set('BMM:scan:estimated', estimate)

        BMM_log_info('begin areascan observing: %s\n%s%s' %
                     (detector, line1, line2))
        uid = yield from make_areascan(dets,
                                       slow,
                                       valueslow + startslow,
                                       valueslow + stopslow,
                                       nslow,
                                       fast,
                                       valuefast + startfast,
                                       valuefast + stopfast,
                                       nfast,
                                       snake=False)

        if pluck is True:

            close_all_plots()
            thismap = user_ns['db'].v2[uid]
            x = numpy.array(thismap.primary.read()[fast.name])
            y = numpy.array(thismap.primary.read()[slow.name])
            z=numpy.array(thismap.primary.read()[BMMuser.xs1]) +\
                numpy.array(thismap.primary.read()[BMMuser.xs2]) +\
                numpy.array(thismap.primary.read()[BMMuser.xs3]) +\
                numpy.array(thismap.primary.read()[BMMuser.xs4])
            z = z.reshape(nfast, nslow)

            # grabbing the first nfast elements of x and every
            # nslow-th element of y is more reliable than
            # numpy.unique due to float &/or motor precision issues

            #plt.title(f'Energy = {energies["below"]}')
            plt.xlabel(f'fast axis ({fast.name}) position (mm)')
            plt.ylabel(f'slow axis ({slow.name}) position (mm)')
            plt.gca().invert_yaxis()  # plot an xafs_x/xafs_y plot upright
            plt.contourf(x[:nfast], y[::nslow], z, cmap=plt.cm.viridis)
            plt.colorbar()
            plt.show()
            fname = os.path.join(BMMuser.folder, 'map-' + now() + '.png')
            plt.savefig(fname)
            try:
                img_to_slack(fname)
            except:
                post_to_slack('failed to post image: {fname}')
                pass

            BMMuser.x = None
            figs = list(map(plt.figure, plt.get_fignums()))
            canvas = figs[0].canvas
            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())
            print(
                'Single click the left mouse button on the plot to pluck a point...'
            )
            cid = canvas.mpl_connect(
                'button_press_event',
                interpret_click)  # see 65-derivedplot.py and
            while BMMuser.x is None:  #  https://matplotlib.org/users/event_handling.html
                yield from sleep(0.5)

            # print('Converting plot coordinates to real coordinates...')
            # begin = valuefast + startfast
            # stepsize = (stopfast - startfast) / (nfast - 1)
            # pointfast = begin + stepsize * BMMuser.x
            # #print(BMMuser.x, pointfast)

            # begin = valueslow + startslow
            # stepsize = (stopslow - startslow) / (nslow - 1)
            # pointslow = begin + stepsize * BMMuser.y
            # #print(BMMuser.y, pointslow)

            # print('That translates to x=%.3f, y=%.3f' % (pointfast, pointslow))
            yield from mv(fast, BMMuser.x, slow, BMMuser.y)
            report(
                f'Moved to position x,y = {fast.position:.3f}, {slow.position:.3f}',
                level='bold',
                slack=True)
Ejemplo n.º 14
0
def scan_and_fly_base(detectors,
                      xstart,
                      xstop,
                      xnum,
                      ystart,
                      ystop,
                      ynum,
                      dwell,
                      *,
                      flying_zebra,
                      xmotor,
                      ymotor,
                      delta=None,
                      shutter=True,
                      align=False,
                      md=None):
    """Read IO from SIS3820.
    Zebra buffers x(t) points as a flyer.
    Xpress3 is our detector.
    The aerotech has the x and y positioners.
    delta should be chosen so that it takes about 0.5 sec to reach the gate??
    ymotor  slow axis
    xmotor  fast axis

    Parameters
    ----------
    Detectors : List[Device]
       These detectors must be known to the zebra

    xstart, xstop : float
    xnum : int
    ystart, ystop : float
    ynum : int
    dwell : float
       Dwell time in seconds

    flying_zebra : SRXFlyer1Axis

    xmotor, ymotor : EpicsMotor, kwarg only
        These should be known to the zebra
        # TODO sort out how to check this

    delta : float, optional, kwarg only
       offset on the ystage start position.  If not given, derive from
       dwell + pixel size
    align : bool, optional, kwarg only
       If True, try to align the beamline
    shutter : bool, optional, kwarg only
       If True, try to open the shutter
    """
    c2pitch_kill = EpicsSignal("XF:05IDA-OP:1{Mono:HDCM-Ax:P2}Cmd:Kill-Cmd")
    if md is None:
        md = {}

    # assign detectors to flying_zebra, this may fail
    flying_zebra.detectors = detectors
    # Setup detectors, combine the zebra, sclr, and the just set detector list
    detectors = (flying_zebra.encoder,
                 flying_zebra.sclr) + flying_zebra.detectors

    dets_by_name = {d.name: d for d in detectors}

    # set up the merlin
    if 'merlin' in dets_by_name:
        dpc = dets_by_name['merlin']
        # TODO use stage sigs
        # Set trigger mode
        # dpc.cam.trigger_mode.put(2)
        # Make sure we respect whatever the exposure time is set to
        if (dwell < 0.0066392):
            print('The Merlin should not operate faster than 7 ms.')
            print('Changing the scan dwell time to 7 ms.')
            dwell = 0.007
        # According to Ken's comments in hxntools, this is a de-bounce time
        # when in external trigger mode
        dpc.cam.stage_sigs['acquire_time'] = .25 * dwell - 0.0016392
        dpc.cam.stage_sigs['acquire_period'] = .5 * dwell
        dpc.cam.stage_sigs['num_images'] = 1
        dpc.stage_sigs['total_points'] = xnum
        dpc.hdf5.stage_sigs['num_capture'] = xnum
        del dpc

    # setup dexela
    if ('dexela' in dets_by_name):
        xrd = dets_by_name['dexela']
        xrd.cam.stage_sigs['acquire_time'] = 1.00 * dwell - 0.050
        xrd.cam.stage_sigs['acquire_period'] = 1.00 * dwell - 0.020
        del xrd

    # If delta is None, set delta based on time for acceleration
    if delta is None:
        # delta = 0.002  # old default value
        v = ((xstop - xstart) / (xnum - 1)) / dwell  # compute "stage speed"
        t_acc = 1.0  # acceleration time, default 1.0 s
        delta = t_acc * v  # distance the stage will travel in t_acc

    # TODO can we do this move in parallel?
    yield from abs_set(ymotor, ystart, wait=True)  # ready to move
    yield from abs_set(xmotor, xstart - delta, wait=True)  # ready to move

    if shutter:
        yield from mv(shut_b, 'Open')

    if align:
        fly_ps = PeakStats(dcm.c2_pitch.name, i0.name)
        align_scan = bp.subs_wrapper(
            scan([flying_zebra.sclr], dcm.c2_pitch, -19.320, -19.360, 41),
            fly_ps)
        ret = yield from align_scan
        if ret is not None:
            yield from abs_set(dcm.c2_pitch, fly_ps.max[0], wait=True)
        #ttime.sleep(10)
        #yield from abs_set(c2pitch_kill, 1)

    md = ChainMap(
        md,
        {
            'plan_name': 'scan_and_fly',
            'detectors': [d.name for d in detectors],
            'dwell': dwell,
            'shape': (xnum, ynum),
            # 'scaninfo': {'type': 'XRF_fly',
            #              'raster': False,
            #              'fast_axis': flying_zebra._fast_axis},
            #              'theta': hf_stage.th.position,
            'scaninfo': {
                'type': 'E_tomo',
                'raster': False,
                'fast_axis': flying_zebra._fast_axis
            },
            'scan_params': [xstart, xstop, xnum, ystart, ystop, ynum, dwell],
            'scan_input': [xstart, xstop, xnum, ystart, ystop, ynum, dwell],
            'delta': delta
        })

    # if (xs == 'xs2'):
    #     md['scaninfo']['type'] = 'XRF_E_tomo_fly'
    if ('xs2' in dets_by_name):
        md['scaninfo']['type'] = 'XRF_E_tomo_fly'

    @stage_decorator(flying_zebra.detectors)
    def fly_each_step(motor, step):
        "See http://nsls-ii.github.io/bluesky/plans.html#the-per-step-hook"
        # First, let 'scan' handle the normal y step, including a checkpoint.
        yield from one_1d_step([], motor, step)
        # yield from bps.sleep(1.0)  # wait for the "x motor" to move

        # Now do the x steps.
        v = ((xstop - xstart) / (xnum - 1)) / dwell  # compute "stage speed"
        yield from abs_set(xmotor, xstart - delta, wait=True)  # ready to move

        # TODO  Why are we re-trying the move?  This should be fixed at
        # a lower level
        # yield from bps.sleep(1.0)  # wait for the "x motor" to move
        x_set = xstart - delta
        x_dial = xmotor.user_readback.get()
        i = 0
        while (np.abs(x_set - x_dial) > 0.0001):
            if (i == 0):
                print('Waiting for motor to reach starting position...',
                      end='')
            i = i + 1
            yield from abs_set(xmotor, xstart - delta, wait=True)
            yield from bps.sleep(1.0)
            x_dial = xmotor.user_readback.get()
        if (i != 0):
            print('done')

        yield from abs_set(xmotor.velocity, v,
                           wait=True)  # set the "stage speed"

        # set up all of the detectors
        # TODO we should be able to move this out of the per-line call?!
        if 'xs' in dets_by_name:
            xs = dets_by_name['xs']
            yield from abs_set(xs.hdf5.num_capture, xnum, wait=True)
            yield from abs_set(xs.settings.num_images, xnum, wait=True)

        if 'xs2' in dets_by_name:
            xs2 = dets_by_name['xs2']
            yield from abs_set(xs2.hdf5.num_capture, xnum, wait=True)
            yield from abs_set(xs2.settings.num_images, xnum, wait=True)

        if 'merlin' in dets_by_name:
            merlin = dets_by_name['merlin']
            yield from abs_set(merlin.hdf5.num_capture, xnum, wait=True)
            yield from abs_set(merlin.cam.num_images, xnum, wait=True)

        if 'dexela' in dets_by_name:
            dexela = dets_by_name['dexela']
            yield from abs_set(dexela.hdf5.num_capture, xnum, wait=True)
            yield from abs_set(dexela.cam.num_images, xnum, wait=True)

        ion = flying_zebra.sclr
        yield from abs_set(ion.nuse_all, xnum)

        # arm the Zebra (start caching x positions)
        yield from kickoff(flying_zebra,
                           xstart=xstart,
                           xstop=xstop,
                           xnum=xnum,
                           dwell=dwell,
                           wait=True)

        # arm SIS3820, note that there is a 1 sec delay in setting X
        # into motion so the first point *in each row* won't
        # normalize...
        yield from abs_set(ion.erase_start, 1)

        # trigger all of the detectors
        for d in flying_zebra.detectors:
            yield from bps.trigger(d, group='row')

        yield from bps.sleep(1.5)
        # start the 'fly'
        yield from abs_set(xmotor, xstop + 1 * delta, group='row')  # move in x
        # wait for the motor and detectors to all agree they are done
        yield from bps.wait(group='row')

        # yield from abs_set(xs.settings.acquire, 0)  # stop acquiring images

        # we still know about ion from above
        yield from abs_set(ion.stop_all, 1)  # stop acquiring scaler

        yield from complete(flying_zebra)  # tell the Zebra we are done
        yield from collect(flying_zebra)  # extract data from Zebra
        # TODO what?
        if ('e_tomo' in xmotor.name):
            v_return = min(4, xmotor.velocity.high_limit)
            yield from bps.mov(xmotor.velocity, v_return)
        else:
            # set the "stage speed"
            yield from bps.mov(xmotor.velocity, 1.0)
            # TODO wat
            # set the "stage speed" twice just in case
            yield from abs_set(xmotor.velocity, 1.0, wait=True)

    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(md['scaninfo']['type'])
        scanrecord.scanning.put(True)
        scanrecord.time_remaining.put((dwell * xnum + 3.8) / 3600)

    def finalize_scan(name, doc):
        logscan_detailed('xrf_fly')
        scanrecord.scanning.put(False)
        scanrecord.time_remaining.put(0)

    # TODO remove this eventually?
    xs = dets_by_name['xs']
    # xs = dets_by_name['xs2']

    # @subs_decorator([LiveTable([ymotor]),
    #                  RowBasedLiveGrid((ynum, xnum), ion.name, row_key=ymotor.name),
    #                  LiveZebraPlot()])
    # @subs_decorator([LiveTable([ymotor]), LiveGrid((ynum, xnum), sclr1.mca1.name)])
    if (ynum == 1):
        livepopup = LivePlot(xs.channel1.rois.roi01.value.name,
                             xlim=(xstart, xstop))
    else:
        livepopup = LiveGrid((ynum, xnum + 1),
                             xs.channel1.rois.roi01.value.name,
                             extent=(xstart, xstop, ystart, ystop),
                             x_positive='right',
                             y_positive='down')

    @subs_decorator([livepopup])
    # @subs_decorator([LiveGrid((ynum, xnum+1),
    #                           xs.channel1.rois.roi01.value.name,
    #                           extent=(xstart, xstop, ystart, ystop),
    #                           x_positive='right', y_positive='down')])
    @subs_decorator({'start': at_scan})
    @subs_decorator({'stop': finalize_scan})
    # monitor values from xs
    @monitor_during_decorator([xs.channel1.rois.roi01.value])
    @stage_decorator([flying_zebra])  # Below, 'scan' stage ymotor.
    @run_decorator(md=md)
    def plan():
        # TODO move this to stage sigs
        for d in flying_zebra.detectors:
            yield from bps.mov(d.total_points, xnum)
        # added to "prime" the detector
        #yield from abs_set(xs.settings.trigger_mode, 'TTL Veto Only')

        # TODO move this to stage sigs
        yield from bps.mov(xs.external_trig, True)
        ystep = 0

        for step in np.linspace(ystart, ystop, ynum):
            yield from abs_set(scanrecord.time_remaining,
                               (ynum - ystep) * (dwell * xnum + 3.8) / 3600.)
            ystep = ystep + 1
            # 'arm' the all of the detectors for outputting fly data
            for d in flying_zebra.detectors:
                yield from bps.mov(d.fly_next, True)
            # print('h5 armed\t',time.time())
            yield from fly_each_step(ymotor, step)
            # print('return from step\t',time.time())

        # TODO this should be taken care of by stage sigs
        ion = flying_zebra.sclr
        yield from bps.mov(xs.external_trig, False, ion.count_mode, 1)

    if shutter:
        final_plan = finalize_wrapper(plan(), bps.mov(shut_b, 'Close'))
    else:
        final_plan = plan()

    return (yield from final_plan)