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 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 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()
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()
def change_xtals(xtal=None): '''Move between the Si(111) and Si(311) monochromators, also moving 2nd crystal pitch and roll to approximate positions. Then do a rocking curve scan. ''' if xtal is None: print('No crystal set specified') return (yield from null()) (ok, text) = BMM_clear_to_start() if ok == 0: print(error_msg(text)) yield from null() return BMMuser, RE, dcm, dm3_bct = user_ns['BMMuser'], user_ns['RE'], user_ns[ 'dcm'], user_ns['dm3_bct'] dcm_pitch, dcm_roll, dcm_x = user_ns['dcm_pitch'], user_ns[ 'dcm_roll'], user_ns['dcm_x'] if '111' in xtal: xtal = 'Si(111)' if '311' in xtal: xtal = 'Si(311)' if xtal not in ('Si(111)', 'Si(311)'): print('%s is not a crytsal set' % xtal) return (yield from null()) ###################################################################### # this is a tool for verifying a macro. this replaces an xafs 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 crystal.\n' % (BMMuser.macro_sleep, xtal))) countdown(BMMuser.macro_sleep) return (yield from null()) ###################################################################### print('Moving to %s crystals' % xtal) action = input('Begin moving motors? [Y/n then Enter] ') if action.lower() == 'q' or action.lower() == 'n': yield from null() return current_energy = dcm.energy.readback.get() start = time.time() RE.msg_hook = None BMM_log_info('Moving to the %s crystals' % xtal) yield from mv(dcm_pitch.kill_cmd, 1) yield from mv(dcm_roll.kill_cmd, 1) if xtal is 'Si(111)': yield from mv(dcm_pitch, 4.1, dcm_roll, -6.26, dcm_x, 0.5) #dcm._crystal = '111' dcm.set_crystal('111') # set d-spacing and bragg offset elif xtal is 'Si(311)': yield from mv(dcm_pitch, 2.28, dcm_roll, -23.86, dcm_x, 65.3) #dcm._crystal = '311' dcm.set_crystal('311') # set d-spacing and bragg offset yield from sleep(2.0) yield from mv(dcm_roll.kill_cmd, 1) print('Returning to %.1f eV' % current_energy) yield from mv(dcm.energy, current_energy) print('Performing a rocking curve scan') yield from mv(dcm_pitch.kill_cmd, 1) yield from mv(dcm_pitch, approximate_pitch(current_energy)) yield from sleep(1) yield from mv(dcm_pitch.kill_cmd, 1) yield from rocking_curve() yield from sleep(2.0) yield from mv(dcm_pitch.kill_cmd, 1) RE.msg_hook = BMM_msg_hook BMM_log_info(motor_status()) close_last_plot() end = time.time() print('\n\nTime elapsed: %.1f min' % ((end - start) / 60))