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()
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()
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()
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
def ca(arg): '''close all plots''' close_all_plots() return None
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)