Beispiel #1
0
    def auto_align(self, pitch=2, drop=None):
        '''Align a sample on a spinner automatically.  This performs 5 scans.
        The first four iterate twice between linear and pitch
        against the signal in It.  This find the flat position.

        Then the sample is pitched to the requested angle and a fifth
        scan is done to optimize the linear motor position against the
        fluorescence signal.

        The linear scans against It look like a step-down function.
        The center of this step is found as the centroid of a fitted
        error function.

        The xafs_pitch scan should be peaked.  Move to the max of the
        signal.

        The linear scan against fluorescence ideally looks like a
        flat-topped peak.  Move to the center of mass.

        At the end, a three-panel figure is drawn showing the last
        three scans.  This is posted to Slack.  It also finds its way
        into the dossier as a record of the quality of the alignment.

        Arguments
        =========
        pitch : int
          The angle at which to make the glancing angle measurements.
        drop : int or None
          If not None, then this many points will be dropped from the
          end of linear scan against transmission when fitting the error
          function. This is an attempt to deal gracefully with leakage 
          through the adhesive at very high energy.

        '''
        BMMuser = user_ns['BMMuser']
        if BMMuser.macro_dryrun:
            report(f'Auto-aligning glancing angle stage, spinner {self.current()}', level='bold', slack=False)
            print(info_msg(f'\nBMMuser.macro_dryrun is True.  Sleeping for %.1f seconds at spinner %d.\n' %
                           (BMMuser.macro_sleep, self.current())))
            countdown(BMMuser.macro_sleep)
            return(yield from null())

        report(f'Auto-aligning glancing angle stage, spinner {self.current()}', level='bold', slack=True)
            
        BMM_suspenders()

        ## first pass in transmission
        yield from self.align_linear(drop=drop)
        yield from self.align_pitch()

        ## for realsies X or Y in transmission
        yield from self.align_linear(drop=drop)
        self.y_uid = user_ns['db'].v2[-1].metadata['start']['uid'] 

        ## for realsies Y in pitch
        yield from self.align_pitch()
        self.pitch_uid = user_ns['db'].v2[-1].metadata['start']['uid'] 

        ## record the flat position
        if self.orientation == 'parallel':
            motor = user_ns['xafs_y']
        else:
            motor = user_ns['xafs_x']
        self.flat = [motor.position, user_ns['xafs_pitch'].position]

        ## move to measurement angle and align
        yield from mvr(user_ns['xafs_pitch'], pitch)
        yield from linescan(motor, 'xs', -2.3, 2.3, 51, pluck=False)
        self.f_uid = user_ns['db'].v2[-1].metadata['start']['uid'] 
        tf = user_ns['db'][-1].table()
        yy = tf[motor.name]
        signal = (tf[BMMuser.xs1] + tf[BMMuser.xs2] + tf[BMMuser.xs3] + tf[BMMuser.xs4]) / tf['I0']
        if BMMuser.element == 'Zr':
            centroid = yy[signal.idxmax()]
        else:
            com = int(center_of_mass(signal)[0])+1
            centroid = yy[com]
        yield from mv(motor, centroid)
        
        ## make a pretty picture, post it to slack
        self.alignment_plot(self.y_uid, self.pitch_uid, self.f_uid)
        self.alignment_filename = os.path.join(BMMuser.folder, 'snapshots', f'spinner{self.current()}-alignment-{now()}.png')
        plt.savefig(self.alignment_filename)
        try:
            img_to_slack(self.alignment_filename)
        except:
            post_to_slack('failed to post image: {self.alignment_filename}')
            pass
        BMM_clear_suspenders()
    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)
    def write_dossier(self):
        BMMuser, dcm, ga = user_ns['BMMuser'], user_ns['dcm'], user_ns['ga']
        if self.filename is None or self.start is None:
            print(error_msg('Filename and/or start number not given.'))
            return None
        firstfile = f'{self.filename}.{self.start:03d}'
        if not os.path.isfile(os.path.join(BMMuser.DATA, firstfile)):
            print(
                error_msg(
                    f'Could not find {os.path.join(BMMuser.DATA, firstfile)}'))
            return None

        # figure out various filenames
        basename = self.filename
        htmlfilename = os.path.join(BMMuser.DATA, 'dossier/',
                                    self.filename + '-01.html')
        seqnumber = 1
        if os.path.isfile(htmlfilename):
            seqnumber = 2
            while os.path.isfile(
                    os.path.join(BMMuser.DATA, 'dossier', "%s-%2.2d.html" %
                                 (self.filename, seqnumber))):
                seqnumber += 1
            basename = "%s-%2.2d" % (self.filename, seqnumber)
            htmlfilename = os.path.join(
                BMMuser.DATA, 'dossier',
                "%s-%2.2d.html" % (self.filename, seqnumber))

        ## generate triplot as a png image (or fail gracefully)
        prjfilename, pngfilename = None, None
        try:
            if self.uidlist is not None:
                pngfilename = os.path.join(BMMuser.DATA, 'snapshots',
                                           f"{basename}.png")
                #prjfilename = os.path.join(BMMuser.DATA, 'prj', f"{basename}.prj")
                self.make_merged_triplot(self.uidlist, pngfilename, self.mode)
        except Exception as e:
            print(error_msg('failure to make triplot'))
            print(e)

        # slurp in the INI file contents
        if self.initext is None:
            with open(os.path.join(BMMuser.DATA, self.inifile)) as f:
                self.initext = ''.join(f.readlines())

        # gather some information about the photon delivery system
        pdstext = f'{get_mode()} ({describe_mode()})'
        mono = 'Si(%s)' % dcm._crystal
        if self.ththth:
            mono = 'Si(333)'

        try:
            # dossier header
            with open(os.path.join(startup_dir, 'tmpl',
                                   'dossier_top.tmpl')) as f:
                content = f.readlines()
            thiscontent = ''.join(content).format(
                filename=self.filename,
                date=BMMuser.date,
                seqnumber=seqnumber,
            )

            # left sidebar, entry for XRF file in the case of fluorescence data
            thismode = plotting_mode(self.mode)
            if thismode == 'xs' or thismode == 'xs1':
                with open(
                        os.path.join(startup_dir, 'tmpl',
                                     'dossier_xrf_file.tmpl')) as f:
                    content = f.readlines()
                thiscontent += ''.join(content).format(
                    basename=basename,
                    xrffile=quote('../XRF/' + str(self.xrffile)),
                    xrfuid=self.xrfuid,
                )

            # middle part of dossier
            with open(os.path.join(startup_dir, 'tmpl',
                                   'dossier_middle.tmpl')) as f:
                content = f.readlines()
            thiscontent += ''.join(content).format(
                basename=basename,
                scanlist=self.scanlist,
                motors=self.motors,
                sample=self.sample,
                prep=self.prep,
                comment=self.comment,
                temperature=self.temperature,
                websnap=quote('../snapshots/' + self.websnap),
                webuid=self.webuid,
                anasnap=quote('../snapshots/' + self.anasnap),
                anauid=self.anauid,
                usb1snap=quote('../snapshots/' + self.usb1snap),
                usb1uid=self.usb1uid,
                usb2snap=quote('../snapshots/' + self.usb2snap),
                usb2uid=self.usb2uid,
            )

            # middle part, XRF and glancing angle alignment images
            if thismode == 'xs' or thismode == 'xs1':
                with open(
                        os.path.join(startup_dir, 'tmpl',
                                     'dossier_xrf_image.tmpl')) as f:
                    content = f.readlines()
                thiscontent += ''.join(content).format(
                    xrfsnap=quote('../XRF/' + str(self.xrfsnap)),
                    pccenergy='%.1f' % self.pccenergy,
                    ocrs=self.ocrs,
                    rois=self.rois,
                    symbol=self.element,
                )
                if 'glancing' in BMMuser.instrument:
                    with open(
                            os.path.join(startup_dir, 'tmpl',
                                         'dossier_ga.tmpl')) as f:
                        content = f.readlines()
                    thiscontent += ''.join(content).format(
                        ga_align=ga.alignment_filename,
                        ga_yuid=ga.y_uid,
                        ga_puid=ga.pitch_uid,
                        ga_fuid=ga.f_uid,
                    )

            # end of dossier
            with open(os.path.join(startup_dir, 'tmpl',
                                   'dossier_bottom.tmpl')) as f:
                content = f.readlines()
            thiscontent += ''.join(content).format(
                e0='%.1f' % self.e0,
                edge=self.edge,
                element=self.element_text(),
                mode=self.mode,
                bounds=self.bounds,
                steps=self.steps,
                times=self.times,
                seqstart=self.seqstart,
                seqend=self.seqend,
                mono=mono,
                pdsmode=pdstext,
                pccenergy='%.1f' % self.pccenergy,
                experimenters=self.experimenters,
                gup=BMMuser.gup,
                saf=BMMuser.saf,
                url=self.url,
                doi=self.doi,
                cif=self.cif,
                initext=highlight(self.initext, IniLexer(), HtmlFormatter()),
                clargs=highlight(self.clargs, PythonLexer(), HtmlFormatter()),
                filename=self.filename,
            )

            with open(htmlfilename, 'a') as o:
                o.write(thiscontent)

            print(f'wrote {htmlfilename}')
        except Exception as E:
            print(E)

        manifest = open(self.manifest_file, 'a')
        manifest.write(htmlfilename + '\n')
        manifest.close()
        self.write_manifest()

        if pngfilename is not None and os.path.isfile(pngfilename):
            try:
                img_to_slack(pngfilename)
            except:
                post_to_slack('failed to post image: {pngfilename}')
                pass

        return htmlfilename
Beispiel #4
0
def tell_slack_shb_opened():
    print('triggering opened message')
    post_to_slack('B shutter opened')
    return (yield from null())