def align_linear(self, force=False, drop=None): '''Fit an error function to the linear scan against It. Plot the result. Move to the centroid of the error function.''' if self.orientation == 'parallel': motor = user_ns['xafs_liny'] else: motor = user_ns['xafs_linx'] yield from linescan(motor, 'it', -2.3, 2.3, 51, pluck=False) close_last_plot() table = user_ns['db'][-1].table() yy = table[motor.name] signal = table['It']/table['I0'] if drop is not None: yy = yy[:-drop] signal = signal[:-drop] 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)) print(whisper(out.fit_report(min_correl=0))) target = out.params['center'].value yield from mv(motor, target) self.y_plot(yy, out)
def align_y(self, force=False, drop=None): '''Fit an error function to the xafs_y scan against It. Plot the result. Move to the centroid of the error function.''' xafs_y = user_ns['xafs_y'] db = user_ns['db'] yield from linescan(xafs_y, 'it', -1, 1, 31, pluck=False) close_last_plot() table = db[-1].table() yy = table['xafs_y'] signal = table['It'] / table['I0'] if drop is not None: yy = yy[:-drop] signal = signal[:-drop] 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)) print(whisper(out.fit_report(min_correl=0))) self.y_plot(yy, out) target = out.params['center'].value yield from mv(xafs_y, target)
def wafer_edge(motor='x'): '''Fit an error function to the linear scan against It. Plot the result. Move to the centroid of the error function.''' if motor == 'x': motor = user_ns['xafs_linx'] else: motor = user_ns['xafs_liny'] yield from linescan(motor, 'it', -2, 2, 41, pluck=False) close_last_plot() table = user_ns['db'][-1].table() yy = table[motor.name] signal = table['It'] / table['I0'] if float(signal[2]) > list(signal)[-2]: ss = -(signal - signal[2]) else: ss = signal - signal[2] mod = StepModel(form='erf') pars = mod.guess(ss, x=numpy.array(yy)) out = mod.fit(ss, pars, x=numpy.array(yy)) print(whisper(out.fit_report(min_correl=0))) out.plot() target = out.params['center'].value yield from mv(motor, target) yield from resting_state_plan() print( f'Edge found at X={user_ns["xafs_x"].position} and Y={user_ns["xafs_y"].position}' )
def align_pitch(self, force=False): '''Find the peak of xafs_pitch scan against It. Plot the result. Move to the peak.''' xafs_pitch = user_ns['xafs_pitch'] yield from linescan(xafs_pitch, 'it', -2.5, 2.5, 51, pluck=False, force=force) close_last_plot() table = user_ns['db'][-1].table() pitch = table['xafs_pitch'] signal = table['It']/table['I0'] target = signal.idxmax() yield from mv(xafs_pitch, pitch[target]) self.pitch_plot(pitch, signal)
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 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
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