def show_reference_wheel(): xafs_ref = user_ns['xafs_ref'] wheel = xafs_ref.content.copy() this = xafs_ref.current_slot() - 1 #wheel[this] = go_msg(wheel[this]) text = 'Foil wheel:\n' text += bold_msg( ' 1 2 3 4 5 6 7 8 9 10 11 12\n' ) text += ' ' for i in range(12): if i == this: text += go_msg('%4.4s' % str(wheel[i])) + ' ' else: text += '%4.4s' % str(wheel[i]) + ' ' text += '\n' text += bold_msg( ' 13 14 15 16 17 18 19 20 21 22 23 24\n' ) text += ' ' for i in range(12, 24): if i == this: text += go_msg('%4.4s' % str(wheel[i])) + ' ' else: text += '%4.4s' % str(wheel[i]) + ' ' text += '\n' return (text)
def xlsx(): '''Prompt for a macro building spreadsheet for any instrument. Use the content of cell B1 to direct this spreadsheet to the correct builder. if cell B1 is "Glancing angle" --> build a glancing angle macro if cell B1 is "Sample wheel" --> build a sample wheel macro if cell B1 is empty --> build a sample wheel macro ''' spreadsheet = present_options('xlsx') if spreadsheet is None: print(error_msg('No spreadsheet specified!')) return None #spreadsheet = os.path.join(BMMuser.folder, spreadsheet) wb = load_workbook(os.path.join(BMMuser.folder, spreadsheet), read_only=True) ws = wb.active instrument = str(ws['B1'].value).lower() if instrument == 'glancing angle': print(bold_msg('This is a glancing angle spreadsheet')) pinwheel.spreadsheet(spreadsheet) else: print(bold_msg('This is a sample wheel spreadsheet')) wmb.spreadsheet(spreadsheet)
def do_ChangeXtals(self): if user_ns['dcm']._crystal == '111': print( go_msg('You would like to change from the ') + whisper('Si(111)') + go_msg(' to the ') + bold_msg('Si(311)') + go_msg(' crystals...\n')) print(disconnected_msg('yield from change_xtals("311")')) else: print( go_msg('You would like to change from the ') + whisper('Si(311)') + go_msg(' to the ') + bold_msg('Si(111)') + go_msg(' crystals...\n')) print(disconnected_msg('yield from change_xtals("111")')) yield from null()
def finish_macro(self): ####################################### # explain to the user what to do next # ####################################### print(f'\nYour new {BMMuser.instrument} plan is called: ' + bold_msg('%s_macro' % self.basename)) print('\nVerify: ' + bold_msg('%s_macro??' % self.basename)) if 'glancing angle' in self.instrument: print('Run: ' + bold_msg('RE(%s_macro())' % self.basename)) print('Add ref: ' + bold_msg('RE(%s_macro(ref=True))' % self.basename)) else: print('Run: ' + bold_msg('RE(%s_macro())' % self.basename)) #print('Dryrun: ' + bold_msg('RE(%s_macro(dryrun=True))' % self.basename)) hours = int(self.totaltime/60) minutes = int(self.totaltime - hours*60) self.deltatime = numpy.sqrt(self.deltatime) print(f'\nApproximate time: {hours} hours, {minutes} minutes +/- {self.deltatime:.1f} minutes')
def verify_roi(self, xs, el, edge, tab=''): print(bold_msg(f'{tab}Attempting to set ROIs for {el} {edge} edge')) try: ## if el is not one of the "standard" 12 ROI sets, insert it into xs.slots[12]/index 13 if xs.check_element(el, edge): forceit = False if el.capitalize() in ('Pb', 'Pt') and edge.capitalize() in ('L2', 'L1'): forceit = True # Pb and Pt L3 edges are "standard" ROIs if el not in xs.slots or forceit: with open(os.path.join(startup_dir, 'rois.json'), 'r') as fl: js = fl.read() allrois = json.loads(js) xs.slots[14] = el for channel in xs.iterate_channels(): xs.set_roi_channel( channel, index=15, name=f'{el.capitalize()}', low=allrois[el.capitalize()][edge.lower()]['low'], high=allrois[el.capitalize()][ edge.lower()]['high']) xs.set_rois() xs.measure_roi() else: report( f'{tab}No tabulated ROIs for the {el.capitalize()} {edge.capitalize()} edge. Not setting ROIs for mesaurement.', level='bold', slack=True) xs.reset_rois(tab=tab) except Exception as E: print(error_msg(E))
def cycle(self, mc=None): '''Cycle power to the amplifiers on a motor controller, then reenable the motors on that controller. Identify the motor controller by these strings: 'dcm', 'slits2', 'm2', 'm3', 'dm3' ''' if self.check(mc) is False: return print(bold_msg(f'Cycling amplifiers on {mc} motor controller')) print(whisper('killing amplifiers')) self.kill(mc) countdown(5) print(whisper('reactivating amplifiers')) self.enable(mc) print(whisper('enabling motors')) if mc == 'm2': user_ns['m2'].ena() elif mc == 'm3': user_ns['m3'].ena() elif mc == 'slits2': user_ns['slits2'].enable() elif mc == 'dm3': user_ns['slits3'].enable() for axis in ('dm3_bct', 'dm3_bpm', 'dm3_foils', 'dm3_fs'): user_ns[axis].enable() time.sleep(0.5) user_ns[axis].kill() elif mc == 'dcm': user_ns['dcm'].ena() time.sleep(0.5) user_ns['dcm'].kill()
def find_slot(close=False): yield from rectangle_scan(motor=xafs_y, start=-3, stop=3, nsteps=31, detector='It') yield from rectangle_scan(motor=xafs_x, start=-10, stop=10, nsteps=31, detector='It') user_ns['xafs_wheel'].in_place() print(bold_msg(f'Found slot at (X,Y) = ({xafs_x.position}, {xafs_y.position})')) if close: close_all_plots()
def doscan(): line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \ (motor.name, sgnl, start, stop, nsteps, motor.user_readback.get()) uid = yield from rel_scan(dets, motor, start, stop, nsteps, md={'plan_name' : f'rel_scan linescan {motor.name} I0'}) t = user_ns['db'][-1].table() if detector.lower() == 'if': signal = numpy.array((t[BMMuser.xs1]+t[BMMuser.xs2]+t[BMMuser.xs3]+t[BMMuser.xs4])/t['I0']) elif detector.lower() == 'it': signal = numpy.array(t['It']/t['I0']) elif detector.lower() == 'ir': signal = numpy.array(t['Ir']/t['It']) signal = signal - signal[0] if negate is True: signal = -1 * signal pos = numpy.array(t[motor.name]) mod = RectangleModel(form='erf') pars = mod.guess(signal, x=pos) out = mod.fit(signal, pars, x=pos) print(whisper(out.fit_report(min_correl=0))) out.plot() if filename is not None: plt.savefig(filename) #middle = out.params['center1'].value + (out.params['center2'].value - out.params['center1'].value)/2 yield from mv(motor, out.params['midpoint'].value) print(bold_msg(f'Found center at {motor.name} = {motor.position}')) rkvs.set('BMM:lmfit:center1', out.params['center1'].value) rkvs.set('BMM:lmfit:center2', out.params['center2'].value) rkvs.set('BMM:lmfit:sigma1', out.params['sigma1'].value) rkvs.set('BMM:lmfit:sigma2', out.params['sigma2'].value) rkvs.set('BMM:lmfit:amplitude', out.params['amplitude'].value) rkvs.set('BMM:lmfit:midpoint', out.params['midpoint'].value)
def center_sample_y(): yield from linescan('it', xafs_liny, -1.5, 1.5, 61, pluck=False) table = db[-1].table() diff = -1 * table['It'].diff() inflection = table['xafs_liny'][diff.idxmax()] yield from mv(xafs_liny, inflection) print(bold_msg('Optimal position in y at %.3f' % inflection))
def to_xdi(self, filename=None): '''Write an XDI-style file with bin energy in the first column and the waveform of each of the 4 channels in the other columns. ''' dcm, BMMuser, ring = user_ns['dcm'], user_ns['BMMuser'], user_ns[ 'ring'] column_list = ['MCA1', 'MCA2', 'MCA3', 'MCA4'] column_list = [ f'MCA{channel_number}' for channel_number in self.channel_numbers ] m2state, m3state = mirror_state() handle = open(filename, 'w') handle.write('# XDI/1.0 BlueSky/%s\n' % bluesky_version) #handle.write('# Scan.uid: %s\n' % dataframe['start']['uid']) #handle.write('# Scan.transient_id: %d\n' % dataframe['start']['scan_id']) handle.write( '# Beamline.name: BMM (06BM) -- Beamline for Materials Measurement' ) handle.write('# Beamline.xray_source: NSLS-II three-pole wiggler\n') handle.write( '# Beamline.collimation: paraboloid mirror, 5 nm Rh on 30 nm Pt\n') handle.write('# Beamline.focusing: %s\n' % m2state) handle.write('# Beamline.harmonic_rejection: %s\n' % m3state) handle.write('# Beamline.energy: %.3f\n' % dcm.energy.position) handle.write( '# Detector.fluorescence: SII Vortex ME4 (4-element silicon drift)\n' ) handle.write('# Scan.end_time: %s\n' % now()) handle.write('# Scan.dwell_time: %.2f\n' % self.cam.acquire_time.value) handle.write('# Facility.name: NSLS-II\n') handle.write('# Facility.current: %.1f mA\n' % ring.current.value) handle.write('# Facility.mode: %s\n' % ring.mode.value) handle.write('# Facility.cycle: %s\n' % BMMuser.cycle) handle.write('# Facility.GUP: %d\n' % BMMuser.gup) handle.write('# Facility.SAF: %d\n' % BMMuser.saf) handle.write('# Column.1: energy (eV)\n') for c, mca_number in enumerate(column_list): handle.write(f'# Column.{c+2}: MCA{mca_number} (counts)\n') handle.write( '# ==========================================================\n') handle.write('# energy ') ## data table e = numpy.arange(0, len( self.channels.channel01.mca.array_data.get())) * 10 mca_data_array_list = [ channel.mca.array_data.get() for channel in self.iterate_channels() ] a = numpy.vstack(mca_data_array_list) b = pandas.DataFrame(a.transpose(), index=e, columns=column_list) handle.write(b.to_csv(sep=' ')) handle.flush() handle.close() print(bold_msg('wrote XRF spectra to %s' % filename))
def ls2dat(datafile, key): ''' Export a linescan database entry to a simple column data file. ls2dat('/path/to/myfile.dat', '0783ac3a-658b-44b0-bba5-ed4e0c4e7216') or ls2dat('/path/to/myfile.dat', 1533) The arguments are a data file name and the database key. ''' #BMMuser, db = user_ns['BMMuser'], user_ns['db'] if os.path.isfile(datafile): print(error_msg('%s already exists! Bailing out....' % datafile)) return handle = open(datafile, 'w') dataframe = user_ns['db'][key] devices = dataframe.devices() # note: this is a _set_ (this is helpful: https://snakify.org/en/lessons/sets/) if 'vor' in devices: abscissa = dataframe['start']['motors'][0] column_list = [abscissa, 'I0', 'It', 'Ir', BMMuser.dtc1, BMMuser.dtc2, BMMuser.dtc3, BMMuser.dtc4, BMMuser.roi1, 'ICR1', 'OCR1', BMMuser.roi2, 'ICR2', 'OCR2', BMMuser.roi3, 'ICR3', 'OCR3', BMMuser.roi4, 'ICR4', 'OCR4'] template = " %.3f %.6f %.6f %.6f %.6f %.6f %.6f %.6f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f\n" elif 'DualI0' in devices: abscissa = dataframe['start']['motors'][0] column_list = [abscissa, 'Ia', 'Ib',] template = " %.3f %.6f %.6f\n" else: abscissa = dataframe['start']['motors'][0] template = " %.3f %.6f %.6f %.6f\n" column_list = [abscissa, 'I0', 'It', 'Ir'] #print(column_list) table = dataframe.table() this = table.loc[:,column_list] handle.write('# XDI/1.0 BlueSky/%s\n' % bluesky_version) handle.write('# Scan.uid: %s\n' % dataframe['start']['uid']) handle.write('# Scan.transient_id: %d\n' % dataframe['start']['scan_id']) try: handle.write('# Facility.GUP: %s\n' % dataframe['start']['XDI']['Facility']['GUP']) except: pass try: handle.write('# Facility.SAF: %s\n' % dataframe['start']['XDI']['Facility']['SAF']) except: pass handle.write('# ==========================================================\n') handle.write('# ' + ' '.join(column_list) + '\n') for i in range(0,len(this)): handle.write(template % tuple(this.iloc[i])) handle.flush() handle.close() print(bold_msg('wrote linescan to %s' % datafile))
def to_xdi(self, filename=None): '''Write an XDI-style file with bin energy in the first column and the waveform of the measurement channel in the second column. ''' dcm, BMMuser, ring = user_ns['dcm'], user_ns['BMMuser'], user_ns[ 'ring'] column_list = ['MCA8'] #template = " %.3f %.6f %.6f %.6f %.6f\n" m2state, m3state = mirror_state() handle = open(filename, 'w') handle.write('# XDI/1.0 BlueSky/%s\n' % bluesky_version) #handle.write('# Scan.uid: %s\n' % dataframe['start']['uid']) #handle.write('# Scan.transient_id: %d\n' % dataframe['start']['scan_id']) handle.write( '# Beamline.name: BMM (06BM) -- Beamline for Materials Measurement' ) handle.write('# Beamline.xray_source: NSLS-II three-pole wiggler\n') handle.write( '# Beamline.collimation: paraboloid mirror, 5 nm Rh on 30 nm Pt\n') handle.write('# Beamline.focusing: %s\n' % m2state) handle.write('# Beamline.harmonic_rejection: %s\n' % m3state) handle.write('# Beamline.energy: %.3f\n' % dcm.energy.position) handle.write( '# Detector.fluorescence: SII Vortex ME4 (4-element silicon drift)\n' ) handle.write('# Scan.end_time: %s\n' % now()) handle.write('# Scan.dwell_time: %.2f\n' % self.settings.acquire_time.value) handle.write('# Facility.name: NSLS-II\n') handle.write('# Facility.current: %.1f mA\n' % ring.current.value) handle.write('# Facility.mode: %s\n' % ring.mode.value) handle.write('# Facility.cycle: %s\n' % BMMuser.cycle) handle.write('# Facility.GUP: %d\n' % BMMuser.gup) handle.write('# Facility.SAF: %d\n' % BMMuser.saf) handle.write('# Column.1: energy (eV)\n') handle.write('# Column.2: MCA1 (counts)\n') handle.write( '# ==========================================================\n') handle.write('# energy ') ## data table e = numpy.arange(0, len(self.mca1.value)) * 10 a = numpy.vstack([ self.mca1.value, self.mca2.value, self.mca3.value, self.mca4.value ]) b = pd.DataFrame(a.transpose(), index=e, columns=column_list) handle.write(b.to_csv(sep=' ')) handle.flush() handle.close() print(bold_msg('wrote XRF spectra to %s' % filename))
def show_rois(self): BMMuser = user_ns['BMMuser'] text = 'Xspress3 ROIs:\n' text += bold_msg(' 1 2 3 4 5 6 7 8\n') text += ' ' for i in range(8): if self.slots[i] == BMMuser.element: text += go_msg('%4.4s' % self.slots[i]) + ' ' else: text += '%4.4s' % self.slots[i] + ' ' text += '\n' text += bold_msg(' 9 10 11 12 13 14 15 16\n') text += ' ' for i in range(8, 16): if self.slots[i] == BMMuser.element: text += go_msg('%4.4s' % self.slots[i]) + ' ' else: text += '%4.4s' % self.slots[i] + ' ' text += '\n' return(text)
def amfe(): print(bold_msg("%-12s : %s / %s" % ('motor', 'AMFE', 'AMFAE'))) for m in mcs8_motors: if m.amfe.get(): fe = warning_msg(m.amfe.enum_strs[m.amfe.get()]) else: fe = m.amfe.enum_strs[m.amfe.get()] if m.amfae.get(): fae = warning_msg(m.amfae.enum_strs[m.amfae.get()]) else: fae = m.amfae.enum_strs[m.amfae.get()] print("%-12s : %s / %s" % (m.name, fe, fae))
def status(self): text = '\n %s is %s\n\n' % (self.name, self.prefix) for signal in list(self.configuration_attrs): if signal.upper() not in status_list.keys(): continue suffix = getattr(self, signal).pvname.replace(self.prefix, '') string = getattr(self, signal).enum_strs[getattr(self, signal).get()] if signal != 'asscs': if getattr(self, signal).get() != status_list[signal.upper()]: string = error_msg('%-19s' % string) text += ' %-26s : %-19s %s %s \n' % ( getattr(self, signal + '_desc').get(), string, bold_msg(getattr(self, signal).get()), whisper(suffix)) boxedtext('%s status signals' % self.name, text, 'green')
def report(text, level=None, slack=False): '''Print a string to: * the log file * the screen * the BMM beamtime slack channel Report level decorations on screen: * 'error' (red) * 'warning' (yellow) * 'info' (brown) * 'url' (undecorated) * 'bold' (bright white) * 'verbosebold' (bright cyan) * 'list' (cyan) * 'disconnected' (purple) * 'whisper' (gray) not matching a report level will be undecorated ''' BMMuser = user_ns['BMMuser'] BMM_log_info(text) if color: # test that color is sensible... if level == 'error': print(error_msg(text)) elif level == 'warning': print(warning_msg(text)) elif level == 'info': print(info_msg(text)) elif level == 'url': print(url_msg(text)) elif level == 'bold': print(bold_msg(text)) elif level == 'verbosebold': print(verbosebold_msg(text)) elif level == 'disconnected': print(disconnected_msg(text)) elif level == 'list': print(list_msg(text)) elif level == 'whisper': print(whisper(text)) else: print(text) else: print(text) if BMMuser.use_slack and slack: post_to_slack(text)
def status(self): text = '\n %s is %s\n\n' % (self.name, self.prefix) for signal in status_list.keys(): sig = signal.lower() try: suffix = getattr(self, sig).pvname.replace(self.prefix, '') string = getattr(self, sig).enum_strs[getattr(self, sig).get()] if signal != 'asscs': if getattr(self, sig).get() != status_list[signal]: string = verbosebold_msg('%-19s' % string) #text += ' %-26s : %-19s %s %s \n' % (getattr(self, sig+'_desc').get(), # string, # bold_msg(getattr(self, sig).get()), # whisper(suffix)) text += ' %-19s %s %s \n' % ( string, bold_msg(getattr(self, sig).get()), whisper(suffix)) except: pass boxedtext('%s status signals' % self.name, text, 'green')
def read_spreadsheet(self): '''Slurp up the content of the spreadsheet and write the default control file ''' print('Reading spreadsheet: %s' % self.source) count = 0 self.offset = 0 isok, explanation = True, '' if self.has_e0_column: # deal with older xlsx that have e0 in column H self.offset = 1 for row in self.ws.rows: count += 1 if count < 6: continue defaultline = False if count == 6: defaultline = True if count > 200: break self.measurements.append(self.get_keywords(row, defaultline)) # check that scan parameters make sense if type(self.measurements[-1]['bounds']) is str: b = re.split('[ ,]+', self.measurements[-1]['bounds'].strip()) else: b = re.split('[ ,]+', self.measurements[0]['bounds'].strip()) if type(self.measurements[-1]['steps']) is str: s = re.split('[ ,]+', self.measurements[-1]['steps'].strip()) else: s = re.split('[ ,]+', self.measurements[0]['steps'].strip()) if type(self.measurements[-1]['times']) is str: t = re.split('[ ,]+', self.measurements[-1]['times'].strip()) else: t = re.split('[ ,]+', self.measurements[0]['times'].strip()) (problem, text, reference) = sanitize_step_scan_parameters(b, s, t) if problem is True: isok = False explanation += bold_msg(f'\nrow {count}, sample {self.measurements[-1]["filename"]}:\n') + text return(isok, explanation, reference)
def scan_slit(slp): #if slit_height < 0.5: # yield from mv(slits3.vsize, 0.5) yield from mv(quadem1.averaging_time, 0.1) yield from mv(motor.velocity, 0.4) yield from mv(motor.kill_cmd, 1) uid = yield from rel_scan([quadem1], motor, start, stop, nsteps, md={'plan_name' : f'rel_scan linescan {motor.name} I0'}) user_ns['RE'].msg_hook = BMM_msg_hook BMM_log_info('slit height scan: %s\tuid = %s, scan_id = %d' % (line1, uid, user_ns['db'][-1].start['scan_id'])) if move: t = user_ns['db'][-1].table() signal = t['I0'] #if get_mode() in ('A', 'B', 'C'): # position = com(signal) #else: position = peak(signal) top = t[motor.name][position] yield from sleep(slp) yield from mv(motor.kill_cmd, 1) yield from sleep(slp) yield from mv(motor, top) else: 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()) yield from sleep(slp) yield from mv(motor.kill_cmd, 1) #yield from mv(motor.inpos, 1) yield from sleep(slp) yield from move_after_scan(motor) yield from mv(quadem1.averaging_time, 0.5)
def cycle(self, mc=None): '''Cycle power to the amplifiers on a motor controller, then reenable the motors on that controller. Identify the motor controller by these strings: 'dcm', 'slits2', 'm2', 'm3', 'dm3' ''' if self.check(mc) is False: return print(bold_msg(f'Cycling amplifiers on {mc} motor controller')) print(whisper('killing amplifiers')) self.kill(mc) countdown(5) print(whisper('reactivating amplifiers')) self.enable(mc) print(whisper('enabling motors')) if mc == 'm2': m2.ena() elif mc == 'm3': m3.ena() elif mc == 'slits2': slits2.enable() elif mc == 'dm3': slits3.enable() for axis in (dm3_bct, dm3_bpm, dm3_foils, dm3_fs): try: axis.enable() time.sleep(0.5) axis.kill() except: pass elif mc == dcm: dcm.ena() time.sleep(0.5) dcm.kill()
def set_instrument(instrument=None): if instrument.lower() == 'glancing angle': print(bold_msg('Setting instrument as glancing angle stage')) BMMuser.instrument = 'glancing angle stage' elif instrument.lower() == 'double wheel': print(bold_msg('Setting instrument as double sample wheel')) BMMuser.instrument = 'double wheel' elif instrument.lower() == 'linkam': print(bold_msg('Setting instrument as Linkam stage')) BMMuser.instrument = 'Linkam stage' elif instrument.lower() == 'lakeshore': print(bold_msg('Setting instrument as LakeShore 331')) BMMuser.instrument = 'LakeShore' elif instrument.lower() == 'grid': print(bold_msg('Setting instrument as sample grid')) BMMuser.instrument = 'motor grid' else: print(bold_msg('Default instrument choice: sample wheel')) BMMuser.instrument = 'sample wheel' rkvs.set('BMM:automation:type', instrument)
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 change_edge(el, focus=False, edge='K', energy=None, slits=True, target=300., xrd=False, bender=True): '''Change edge energy by: 1. Moving the DCM above the edge energy 2. Moving the photon delivery system to the correct mode 3. Running a rocking curve scan 4. Running a slits_height scan Parameters ---------- el : str one- or two-letter symbol focus : bool, optional T=focused or F=unfocused beam [False, unfocused] edge : str, optional edge symbol ['K'] energy : float, optional e0 value [None, determined from el/edge] slits : bool, optional perform slit_height() scan [False] target : float, optional energy where rocking curve is measured [300] xrd : boolean, optional force photon delivery system to XRD [False] Examples -------- Normal use, unfocused beam: >>> RE(change_edge('Fe')) Normal use, focused beam: >>> RE(change_edge('Fe', focus=True)) L2 or L1 edge: >>> RE(change_edge('Re', edge='L2')) Measure rocking curve at edge energy: >>> RE(change_edge('Fe', target=0)) XRD, new energy: >>> RE(change_edge('Fe', xrd=True, energy=8600)) note that you must specify an element, but it doesn't matter which one the energy will be moved to the specified energy xrd=True implies focus=True and target=0 ''' BMMuser, RE, dcm, dm3_bct = user_ns['BMMuser'], user_ns['RE'], user_ns[ 'dcm'], user_ns['dm3_bct'] dcm_pitch, dcm_bragg = user_ns['dcm_pitch'], user_ns['dcm_bragg'] rkvs = user_ns['rkvs'] try: xs = user_ns['xs'] except: pass #BMMuser.prompt = True el = el.capitalize() ###################################################################### # this is a tool for verifying a macro. this replaces an xafsmod scan # # with a sleep, allowing the user to easily map out motor motions in # # a macro # if BMMuser.macro_dryrun: print( info_msg( '\nBMMuser.macro_dryrun is True. Sleeping for %.1f seconds rather than changing to the %s edge.\n' % (BMMuser.macro_sleep, el))) countdown(BMMuser.macro_sleep) return (yield from null()) ###################################################################### if pds_motors_ready() is False: print( error_msg( '\nOne or more motors are showing amplifier faults.\nToggle the correct kill switch, then re-enable the faulted motor.' )) return (yield from null()) (ok, text) = BMM_clear_to_start() if ok is False: print( error_msg('\n' + text) + bold_msg('Quitting change_edge() macro....\n')) return (yield from null()) if energy is None: energy = edge_energy(el, edge) if energy is None: print( error_msg('\nEither %s or %s is not a valid symbol\n' % (el, edge))) return (yield from null()) if energy > 23500: edge = 'L3' energy = edge_energy(el, 'L3') if energy < 4000: print(warning_msg('The %s edge energy is below 4950 eV' % el)) print(warning_msg('You have to change energy by hand.')) return (yield from null()) if energy > 23500: print( warning_msg( 'The %s edge energy is outside the range of this beamline!' % el)) return (yield from null()) BMMuser.edge = edge BMMuser.element = el BMMuser.edge_energy = energy rkvs.set('BMM:pds:edge', edge) rkvs.set('BMM:pds:element', el) rkvs.set('BMM:pds:edge_energy', energy) if energy > 8000: mode = 'A' if focus else 'D' elif energy < 6000: #mode = 'B' if focus else 'F' ## mode B currently is inaccessible :( mode = 'C' if focus else 'F' else: mode = 'C' if focus else 'E' if xrd: mode = 'XRD' focus = True target = 0.0 current_mode = get_mode() if mode in ('D', 'E', 'F') and current_mode in ('D', 'E', 'F'): with_m2 = False elif mode in ('A', 'B', 'C') and current_mode in ('A', 'B', 'C'): # no need to move M2 with_m2 = False else: with_m2 = True if all_connected(with_m2) is False: print(warning_msg('Ophyd connection failure' % el)) return (yield from null()) ################################ # confirm configuration change # ################################ print(bold_msg('\nEnergy change:')) print(' %s: %s %s' % (list_msg('edge'), el.capitalize(), edge.capitalize())) print(' %s: %.1f' % (list_msg('edge energy'), energy)) print(' %s: %.1f' % (list_msg('target energy'), energy + target)) print(' %s: %s' % (list_msg('focus'), str(focus))) print(' %s: %s' % (list_msg('photon delivery mode'), mode)) print(' %s: %s' % (list_msg('optimizing slits height'), str(slits))) if BMMuser.prompt: action = input("\nBegin energy change? [Y/n then Enter] ") if action.lower() == 'q' or action.lower() == 'n': return (yield from null()) if mode == 'C' and energy < 6000: print( warning_msg( '\nMoving to mode C for focused beam and an edge energy below 6 keV.' )) action = input( "You will not get optimal harmonic rejection. Continue anyway? [Y/n then Enter] " ) if action.lower() == 'q' or action.lower() == 'n': return (yield from null()) start = time.time() if mode == 'XRD': report('Configuring beamline for XRD', level='bold', slack=True) else: report( f'Configuring beamline for {el.capitalize()} {edge.capitalize()} edge', level='bold', slack=True) yield from dcm.kill_plan() ################################################ # change to the correct photon delivery mode # # + move mono to correct energy # # + move reference holder to correct slot # ################################################ # if not calibrating and mode != current_mode: # print('Moving to photon delivery mode %s...' % mode) yield from mv(dcm_bragg.acceleration, BMMuser.acc_slow) yield from change_mode(mode=mode, prompt=False, edge=energy + target, reference=el, bender=bender) yield from mv(dcm_bragg.acceleration, BMMuser.acc_fast) if arrived_in_mode(mode=mode) is False: print(error_msg(f'\nFailed to arrive in Mode {mode}')) print( 'Fixing this is often as simple as re-running the change_mode() command.' ) print('Or try dm3_bct.kill() the re-run the change_mode() command.') print('If that doesn\'t work, call for help') return (yield from null()) yield from user_ns['kill_mirror_jacks']() yield from sleep(1) if BMMuser.motor_fault is not None: print( error_msg('\nSome motors are reporting amplifier faults: %s' % BMMuser.motor_fault)) print( 'Clear the faults and try running the same change_edge() command again.' ) print('Troubleshooting: ' + url_msg( 'https://nsls-ii-bmm.github.io/BeamlineManual/trouble.html#amplifier-fault' )) BMMuser.motor_fault = None return (yield from null()) BMMuser.motor_fault = None ############################ # run a rocking curve scan # ############################ print('Optimizing rocking curve...') yield from abs_set(dcm_pitch.kill_cmd, 1, wait=True) yield from mv(dcm_pitch, approximate_pitch(energy + target)) yield from sleep(1) yield from abs_set(dcm_pitch.kill_cmd, 1, wait=True) yield from rocking_curve() close_last_plot() ########################## # run a slit height scan # ########################## if slits: print('Optimizing slits height...') yield from slit_height(move=True) close_last_plot() ## redo rocking curve? ################################## # set reference and roi channels # ################################## if not xrd: ## reference channel rois = user_ns['rois'] print('Moving reference foil...') yield from rois.select_plan(el) ## Xspress3 BMMuser.verify_roi(xs, el, edge) ## feedback show_edges() if mode == 'XRD': report('Finished configuring for XRD', level='bold', slack=True) else: report( f'Finished configuring for {el.capitalize()} {edge.capitalize()} edge', level='bold', slack=True) if slits is False: print( ' * You may need to verify the slit position: RE(slit_height())') self.to_json(os.path.join(self.DATA, '.BMMuser')) yield from dcm.kill_plan() end = time.time() print('\n\nThat took %.1f min' % ((end - start) / 60)) return ()
def calibrate_low_end(mono='111', focus=False): '''Step through the lower 5 elements of the mono calibration procedure.''' BMMuser, shb, dcm_pitch = user_ns['BMMuser'], user_ns['shb'], user_ns[ 'dcm_pitch'] (ok, text) = BMM_clear_to_start() if ok is False: print(error_msg('\n' + text) + bold_msg('Quitting macro....\n')) return (yield from null()) BMM_log_info('Beginning low end calibration macro') def main_plan(): BMMuser.prompt = False datafile = os.path.join(BMMuser.DATA, 'edges%s.ini' % mono) handle = open(datafile, 'w') handle.write('[config]\n') handle.write("mono = %s\n" % mono) if mono == '111': handle.write('DSPACING = 3.13597211\n') else: handle.write('DSPACING = 1.63762644\n') handle.write('thistitle = Si(%s) calibration curve\n' % mono) handle.write( 'reference = Kraft et al, Review of Scientific Instruments 67, 681 (1996)\n' ) handle.write('doi = https://doi.org/10.1063/1.1146657\n\n') handle.write('## found, tabulated, found_angle, dcm_pitch\n') handle.write('[edges]\n') handle.flush() yield from change_edge('Fe', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='fecal', edge='Fe', e0=7112, sample='Fe foil') close_last_plot() handle.write('fe = 11111.11, 7110.75, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Co', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='cocal', edge='Co', e0=7709, sample='Co foil') close_last_plot() handle.write('co = 11111.11, 7708.78, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Ni', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='nical', edge='Ni', e0=8333, sample='Ni foil') close_last_plot() handle.write('ni = 11111.11, 8331.49, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Cu', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='cucal', edge='Cu', e0=8979, sample='Cu foil') close_last_plot() handle.write('cu = 11111.11, 8980.48, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Zn', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='zncal', edge='Zn', e0=9659, sample='Zn foil') close_last_plot() handle.write('zn = 11111.11, 9660.76, 22222.22, %.5f\n' % pitch) handle.flush() handle.close() #yield from shb.close_plan() def cleanup_plan(): yield from resting_state_plan() yield from finalize_wrapper(main_plan(), cleanup_plan()) yield from resting_state_plan() BMM_log_info('Low end calibration macro finished!')
def calibrate_high_end(mono='111', focus=False): '''Step through the upper 5 elements of the mono calibration procedure.''' BMMuser, shb, dcm_pitch = user_ns['BMMuser'], user_ns['shb'], user_ns[ 'dcm_pitch'] (ok, text) = BMM_clear_to_start() if ok is False: print(error_msg('\n' + text) + bold_msg('Quitting macro....\n')) return (yield from null()) BMM_log_info('Beginning high end calibration macro') def main_plan(): BMMuser.prompt = False datafile = os.path.join(BMMuser.DATA, 'edges%s.ini' % mono) handle = open(datafile, 'a') #yield from shb.open_plan() yield from change_edge('Pt', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='ptcal', edge='Pt', e0=11563, sample='Pt foil') close_last_plot() handle.write('pt = 11111.11, 11562.76, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Au', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='aucal', edge='Au', e0=11919, sample='Au foil') close_last_plot() handle.write('au = 11111.11, 11919.70, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Pb', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='pbcal', edge='Pb', e0=13035, sample='Pb foil') close_last_plot() handle.write('pb = 11111.11, 13035.07, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Nb', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='nbcal', edge='Nb', e0=18986, sample='Nb foil') close_last_plot() handle.write('nb = 11111.11, 18982.97, 22222.22, %.5f\n' % pitch) handle.flush() yield from change_edge('Mo', target=0, focus=focus) pitch = dcm_pitch.user_readback.get() yield from xafs('/home/xf06bm/Data/Staff/mono_calibration/cal.ini', folder=BMMuser.DATA, filename='mocal', edge='Mo', e0=20000, sample='Mo foil') close_last_plot() handle.write('mo = 11111.11, 20000.36, 22222.22, %.5f\n' % pitch) handle.flush() handle.close() #yield from shb.close_plan() def cleanup_plan(): yield from resting_state_plan() yield from finalize_wrapper(main_plan(), cleanup_plan()) yield from resting_state_plan() BMM_log_info('High end calibration macro finished!')
def as2dat(datafile, key): ''' Export an areascan database entry to a simple column data file. as2dat('/path/to/myfile.dat', '42447313-46a5-42ef-bf8a-46fedc2c2bd1') or as2dat('/path/to/myfile.dat', 2948) The arguments are a data file name and the database key. ''' BMMuser, db = user_ns['BMMuser'], user_ns['db'] if os.path.isfile(datafile): print(error_msg('%s already exists! Bailing out....' % datafile)) return dataframe = db[key] if 'slow_motor' not in dataframe['start']: print( error_msg( 'That database entry does not seem to be a an areascan (missing slow_motor)' )) return if 'fast_motor' not in dataframe['start']: print( error_msg( 'That database entry does not seem to be a an areascan (missing fast_motor)' )) return devices = dataframe.devices( ) # note: this is a _set_ (this is helpful: https://snakify.org/en/lessons/sets/) if 'vor' in devices: column_list = [ dataframe['start']['slow_motor'], dataframe['start']['fast_motor'], 'I0', 'It', 'Ir', BMMuser.dtc1, BMMuser.dtc2, BMMuser.dtc3, BMMuser.dtc4, BMMuser.roi1, 'ICR1', 'OCR1', BMMuser.roi2, 'ICR2', 'OCR2', BMMuser.roi3, 'ICR3', 'OCR3', BMMuser.roi4, 'ICR4', 'OCR4' ] template = " %.3f %.3f %.6f %.6f %.6f %.6f %.6f %.6f %.6f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f\n" else: column_list = [ dataframe['start']['slow_motor'], dataframe['start']['fast_motor'], 'I0', 'It', 'Ir' ] template = " %.3f %.3f %.6f %.6f %.6f\n" table = dataframe.table() this = table.loc[:, column_list] handle = open(datafile, 'w') handle.write('# Scan.uid: %s\n' % dataframe['start']['uid']) handle.write('# Scan.transient_id: %d\n' % dataframe['start']['scan_id']) try: handle.write('# Facility.GUP: %d\n' % dataframe['start']['XDI']['Facility']['GUP']) except: pass try: handle.write('# Facility.SAF: %d\n' % dataframe['start']['XDI']['Facility']['SAF']) except: pass handle.write( '# ==========================================================\n') handle.write('# ' + ' '.join(column_list) + '\n') slowval = None for i in range(0, len(this)): if i > 0 and this.iloc[i, 0] != slowval: handle.write('\n') handle.write(template % tuple(this.iloc[i])) slowval = this.iloc[i, 0] handle.flush() handle.close() print(bold_msg('wrote areascan to %s' % datafile))
def center_sample_roll(): yield from linescan('it', xafs_roll, -3, 3, 61, pluck=False) table = db[-1].table() peak = table['xafs_roll'][table['It'].idxmax()] yield from mv(xafs_roll, peak) print(bold_msg('Optimal position in roll at %.3f' % peak))
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)
def main_plan(detector, axis, start, stop, nsteps, pluck, force): (ok, text) = BMM_clear_to_start() if force is False and ok is False: print(error_msg(text)) yield from null() return detector, axis = ls_backwards_compatibility(detector, axis) # print('detector is: ' + str(detector)) # print('axis is: ' + str(axis)) # return(yield from null()) RE.msg_hook = None ## sanitize input and set thismotor to an actual motor if type(axis) is str: axis = axis.lower() detector = detector.capitalize() ## sanity checks on axis if axis not in motor_nicknames.keys() and 'EpicsMotor' not in str(type(axis)) \ and 'PseudoSingle' not in str(type(axis)) and 'WheelMotor' not in str(type(axis)): print( error_msg('\n*** %s is not a linescan motor (%s)\n' % (axis, str.join(', ', motor_nicknames.keys())))) yield from null() return if 'EpicsMotor' in str(type(axis)): thismotor = axis elif 'PseudoSingle' in str(type(axis)): thismotor = axis elif 'WheelMotor' in str(type(axis)): thismotor = axis else: # presume it's an xafs_XXXX motor thismotor = motor_nicknames[axis] current = thismotor.position if current + start < thismotor.limits[0]: print( error_msg( f'These scan parameters will take {thismotor.name} outside it\'s lower limit of {thismotor.limits[0]}' )) print(whisper(f'(starting position = {thismotor.position})')) return (yield from null()) if current + stop > thismotor.limits[1]: print( error_msg( f'These scan parameters will take {thismotor.name} outside it\'s upper limit of {thismotor.limits[1]}' )) print(whisper(f'(starting position = {thismotor.position})')) return (yield from null()) BMMuser.motor = thismotor ## sanity checks on detector if detector not in ('It', 'If', 'I0', 'Iy', 'Ir', 'Both', 'Bicron', 'Ia', 'Ib', 'Dualio', 'Xs', 'Xs1'): print( error_msg( '\n*** %s is not a linescan measurement (%s)\n' % (detector, 'it, if, i0, iy, ir, both, bicron, dualio, xs, xs1'))) yield from null() return yield from abs_set(user_ns['_locked_dwell_time'], inttime, wait=True) if detector == 'Xs': yield from mv(xs.settings.acquire_time, inttime) yield from mv(xs.total_points, nsteps) dets = [ user_ns['quadem1'], ] if user_ns['with_dualem']: dualio = user_ns['dualio'] denominator = '' detname = '' ## func is an anonymous function, built on the fly, for feeding to DerivedPlot if detector == 'It': denominator = ' / I0' detname = 'transmission' func = lambda doc: (doc['data'][thismotor.name], doc['data']['It'] / doc['data']['I0']) elif detector == 'Ia' and dualio is not None: dets.append(dualio) detname = 'Ia' func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ia']) elif detector == 'Ib' and dualio is not None: dets.append(dualio) detname = 'Ib' func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ib']) elif detector == 'Ir': #denominator = ' / It' detname = 'reference' #func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ir']/doc['data']['It']) func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ir']) elif detector == 'I0': detname = 'I0' func = lambda doc: (doc['data'][thismotor.name], doc['data']['I0']) elif detector == 'Bicron': dets.append(user_ns['vor']) detname = 'Bicron' func = lambda doc: (doc['data'][thismotor.name], doc['data'][ 'Bicron']) elif detector == 'Iy': denominator = ' / I0' detname = 'electron yield' func = lambda doc: (doc['data'][thismotor.name], doc['data']['Iy'] / doc['data']['I0']) elif detector == 'If': dets.append(user_ns['vor']) denominator = ' / I0' detname = 'fluorescence' func = lambda doc: (doc['data'][thismotor.name], (doc['data'][ BMMuser.dtc1] + doc['data'][BMMuser.dtc2] + doc['data'][ BMMuser.dtc3] + doc['data'][BMMuser.dtc4]) / doc['data'][ 'I0']) elif detector == 'Xs': dets.append(user_ns['xs']) denominator = ' / I0' detname = 'fluorescence' func = lambda doc: (doc['data'][thismotor.name], (doc['data'][BMMuser.xs1] + doc['data'][ BMMuser.xs2] + doc['data'][BMMuser.xs3] + doc['data'][BMMuser.xs4]) / doc['data']['I0']) yield from mv(xs.total_points, nsteps) # Xspress3 demands that this be set up front elif detector == 'Xs1': dets.append(user_ns['xs']) denominator = ' / I0' detname = 'fluorescence' func = lambda doc: (doc['data'][thismotor.name], doc['data'][ BMMuser.xs8] / doc['data']['I0']) yield from mv(xs1.total_points, nsteps) # Xspress3 demands that this be set up front elif detector == 'Dualio': dets.append(dualio) funcia = lambda doc: (doc['data'][thismotor.name], doc['data']['Ia' ]) funcib = lambda doc: (doc['data'][thismotor.name], doc['data']['Ib' ]) ## need a "Both" for trans + xs !!!!!!!!!! elif detector == 'Both': dets.append(user_ns['vor']) functr = lambda doc: (doc['data'][thismotor.name], doc['data'][ 'It'] / doc['data']['I0']) funcfl = lambda doc: (doc['data'][thismotor.name], (doc['data'][ BMMuser.dtc1] + doc['data'][BMMuser.dtc2] + doc['data'][ BMMuser.dtc3] + doc['data'][BMMuser.dtc4]) / doc['data'][ 'I0']) ## and this is the appropriate way to plot this linescan #abs_set(_locked_dwell_time, 0.5) if detector == 'Both': plot = [ DerivedPlot(funcfl, xlabel=thismotor.name, ylabel='If/I0', title='fluorescence vs. %s' % thismotor.name), DerivedPlot(functr, xlabel=thismotor.name, ylabel='It/I0', title='transmission vs. %s' % thismotor.name) ] elif detector == 'Dualio': plot = [ DerivedPlot(funcia, xlabel=thismotor.name, ylabel='Ia/I0', title='Ia vs. %s' % thismotor.name), DerivedPlot(funcib, xlabel=thismotor.name, ylabel='Ib/I0', title='Ib vs. %s' % thismotor.name) ] else: plot = DerivedPlot(func, xlabel=thismotor.name, ylabel=detector + denominator, title='%s vs. %s' % (detname, thismotor.name)) if 'PseudoSingle' in str(type(axis)): value = thismotor.readback.get() else: value = thismotor.user_readback.get() line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \ (thismotor.name, detector, start, stop, nsteps, value) ##BMM_suspenders() # engage suspenders thismd = dict() thismd['XDI'] = dict() thismd['XDI']['Facility'] = dict() thismd['XDI']['Facility']['GUP'] = BMMuser.gup thismd['XDI']['Facility']['SAF'] = BMMuser.saf rkvs.set('BMM:scan:type', 'line') rkvs.set('BMM:scan:starttime', str(datetime.datetime.timestamp(datetime.datetime.now()))) rkvs.set('BMM:scan:estimated', 0) @subs_decorator(plot) #@subs_decorator(src.callback) def scan_xafs_motor(dets, motor, start, stop, nsteps): uid = yield from rel_scan(dets, motor, start, stop, nsteps, md={ **thismd, **md }) return uid uid = yield from scan_xafs_motor(dets, thismotor, start, stop, nsteps) #global mytable #run = src.retrieve() #mytable = run.primary.read().to_dataframe() BMM_log_info('linescan: %s\tuid = %s, scan_id = %d' % (line1, uid, user_ns['db'][-1].start['scan_id'])) 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()) yield from move_after_scan(thismotor)
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