예제 #1
0
    def alignment_plot(self, yt, pitch, yf):
        '''Make a pretty, three-panel plot at the end of an auto-alignment'''
        BMMuser = user_ns['BMMuser']
        close_all_plots()
        fig = plt.figure(tight_layout=True) #, figsize=(9,6))
        gs = gridspec.GridSpec(1,3)

        if self.orientation == 'parallel':
            motor = 'xafs_y'
        else:
            motor =  'xafs_x'

        t  = fig.add_subplot(gs[0, 0])
        tt = user_ns['db'][yt].table()
        yy = tt[motor]
        signal = tt['It']/tt['I0']
        if float(signal[2]) > list(signal)[-2] :
            ss     = -(signal - signal[2])
            self.inverted = 'inverted '
        else:
            ss     = signal - signal[2]
            self.inverted    = ''
        mod    = StepModel(form='erf')
        pars   = mod.guess(ss, x=numpy.array(yy))
        out    = mod.fit(ss, pars, x=numpy.array(yy))
        t.scatter(yy, out.data)
        t.plot(yy, out.best_fit, color='red')
        t.scatter(out.params['center'].value, out.params['amplitude'].value/2, s=120, marker='x', color='green')
        t.set_xlabel(f'{motor} (mm)')
        t.set_ylabel(f'{self.inverted}data and error function')

        p  = fig.add_subplot(gs[0, 1])
        tp = user_ns['db'][pitch].table()
        xp = tp['xafs_pitch']
        signal = tp['It']/tp['I0']
        target = signal.idxmax()
        p.plot(xp, signal)
        p.scatter(xp[target], signal.max(), s=120, marker='x', color='green')
        p.set_xlabel('xafs_pitch (deg)')
        p.set_ylabel('It/I0')
        p.set_title(f'alignment of spinner {self.current()}')

        f = fig.add_subplot(gs[0, 2])
        tf = user_ns['db'][yf].table()
        yy = tf[motor]
        signal = (tf[BMMuser.xs1] + tf[BMMuser.xs2] + tf[BMMuser.xs3] + tf[BMMuser.xs4]) / tf['I0']
        #if BMMuser.element in ('Zr', 'Sc', 'Nb'):
        #    com = signal.idxmax()
        #    centroid = yy[com]
        #else:
        com = int(center_of_mass(signal)[0])+1
        centroid = yy[com]
        f.plot(yy, signal)
        f.scatter(centroid, signal[com], s=120, marker='x', color='green')
        f.set_xlabel(f'{motor} (mm)')
        f.set_ylabel('If/I0')

        fig.canvas.draw()
        fig.canvas.flush_events()
        plt.show()
예제 #2
0
 def pitch_plot(self, pitch, signal, filename=None):
     target = signal.idxmax()
     close_all_plots()
     #plt.cla()
     plt.plot(pitch, signal)
     plt.scatter(pitch[target], signal.max(), s=160, marker='x', color='green')
     plt.xlabel('xafs_pitch (deg)')
     plt.ylabel('It/I0')
     plt.title(f'pitch scan, spinner {self.current()}')
     #plt.draw()
     plt.show()
예제 #3
0
 def y_plot(self, yy, out, filename=None):
     #plt.cla()
     close_all_plots()
     plt.scatter(yy, out.data)
     plt.plot(yy, out.best_fit, color='red')
     plt.scatter(out.params['center'].value, out.params['amplitude'].value/2, s=160, marker='x', color='green')
     if self.orientation == 'parallel':
         plt.xlabel('xafs_y (mm)')
         direction = 'Y'
     else:
         plt.xlabel('xafs_x (mm)')
         direction = 'X'
     plt.ylabel(f'{self.inverted}data and error function')
     plt.title(f'fit to {direction} scan, spinner {self.current()}')
     #plt.draw()
     plt.show()
예제 #4
0
def align_ga(ymargin=0.5, force=False):
    '''Thin wrapper around repeated calls to linescan in order to align 
    the glancing angle stage in the beam. This will perform a linescan in
    xafs_pitch over the range of -2 to 2 or in xafs_y from -1 to 1
    (which are suitable ranges for the size of the slot).  User is
    prompted to scan in pitch or Y, or to quit the loop.  The loop will
    return after 6 scans -- 3 iterations is certainly enough for this
    task.

    In bsui terms, this is a wrapper around these command:

       linescan(xafs_y, 'it', -1, 1, 31)

    and 

       linescan(xafs_pitch, 'it', -2, 2, 31)

    which are suitable linescans for aligning the glancing angle stage
    once it has been aligned roughly by hand.

    The high limit in xafs_y will be set to 0.5 mm (the default) above
    the selected position.  The low limit in xafs_y will be set to 0.5
    mm (the default) below the selected position.  The margin cannot
    be less than 50 microns.

    Parameters
    ==========

    ymargin : float
        margin for setting the high limit of the xafs_y axis, 
        default=2, minimum=0.05
    force : bool
        passed along to linescan(), flag for forcing a scan even if 
        not clear to start

    '''
    xafs_pitch, xafs_y = user_ns['xafs_pitch'], user_ns['xafs_y']
    db = user_ns['db']
    count = 1
    if ymargin < 0.05:
        ymargin = 0.05
    while True:
        action = input(
            bold_msg(
                "\nScan axis? [p=xafs_pitch / y=xafs_y / q=quit] (then Enter) "
            ))
        if action[:1].lower() == 'p':
            yield from linescan(xafs_pitch, 'it', -3, 3, 31, force=force)
        elif action[:1].lower() == 'y':
            yield from mv(xafs_y.hlm, xafs_y.default_hlm, xafs_y.llm,
                          xafs_y.default_llm)
            yield from linescan(xafs_y, 'it', -2, 2, 31, force=force)
            #yield from mv(xafs_y.hlm, xafs_y.position+ymargin, xafs_y.llm, xafs_y.position-ymargin)
        elif action[:1].lower() in ('q', 'n', 'c'):  # quit/no/cancel
            yield from null()
            close_all_plots()
            return
        else:
            continue
        count += 1
        if count > 6:
            print('Three iterations is plenty....')
            close_all_plots()
            return
예제 #5
0
def find_slot(xmargin=0.5, ymargin=2, force=False):
    '''Thin wrapper around repeated calls to linescan in order to align a
    sample wheel slot in the beam. This will perform a linescan in
    xafs_x over the range of -10 to 10 or in xafs_y from -3 to 3
    (which are suitable ranges for the size of the slot).  User is
    prompted to scan in X or Y, or to quit the loop.  The loop will
    return after 6 scans -- 3 iterations is certainly enough for this
    task.

    In bsui terms, this is a wrapper around these command:

       linescan(xafs_x, 'it', -10, 10, 31)

    and 

       linescan(xafs_y, 'it', -3, 3, 31)

    which are suitable linescans for aligning the sample wheel once it
    has been aligned roughly by hand.

    The high limit in xafs_x will be set to 500 microns (the default)
    above the selected position.  The margin cannot be less than 50
    microns.  The low limit in xafs_x is presumed not to be important.

    The high limit in xafs_y will be set to 2 mm (the default) above
    the selected position.  The low limit in xafs_y will be set to 2
    mm (the default) below the selected position.  The margin cannot
    be less than 50 microns.

    Parameters
    ==========

    xmargin : float
        margin for setting the high limit of the xafs_x axis, 
        default=0.5, minimum=0.05
    ymargin : float
        margin for setting the high limit of the xafs_y axis, 
        default=2, minimum=0.05
    force : bool
        passed along to linescan(), flag for forcing a scan even if 
        not clear to start

    '''
    xafs_x, xafs_y = user_ns['xafs_x'], user_ns['xafs_y']
    count = 1
    if xmargin < 0.05:
        xmargin = 0.05
    if ymargin < 0.05:
        ymargin = 0.05
    while True:
        action = input(
            bold_msg(
                "\nScan axis? [x=xafs_x / y=xafs_y / q=quit] (then Enter) "))
        if action[:1].lower() == 'x':
            yield from mv(xafs_x.hlm, xafs_x.default_hlm)
            yield from linescan(xafs_x, 'it', -10, 10, 31, force=force)
            yield from mv(xafs_x.hlm, xafs_x.position + xmargin)
        elif action[:1].lower() == 'y':
            yield from mv(xafs_y.hlm, xafs_y.default_hlm, xafs_y.llm,
                          xafs_y.default_llm)
            yield from linescan(xafs_y, 'it', -3, 3, 31, force=force)
            yield from mv(xafs_y.hlm, xafs_y.position + ymargin, xafs_y.llm,
                          xafs_y.position - ymargin)
        elif action[:1].lower() in ('q', 'n', 'c'):  # quit/no/cancel
            yield from null()
            close_all_plots()
            return
        else:
            continue
        count += 1
        if count > 6:
            print('Three iterations is plenty....')
            close_all_plots()
            return
예제 #6
0
def ca(arg):
    '''close all plots'''
    close_all_plots()
    return None
예제 #7
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)