def scan_slit(slp): #if slit_height < 0.5: # yield from mv(slits3.vsize, 0.5) yield from abs_set(quadem1.averaging_time, 0.1, wait=True) yield from abs_set(motor.velocity, 0.4, wait=True) yield from abs_set(motor.kill_cmd, 1, wait=True) uid = yield from rel_scan([quadem1], motor, start, stop, nsteps) 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 = 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 abs_set(motor.kill_cmd, 1, wait=True) 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 abs_set(motor.kill_cmd, 1, wait=True) yield from move_after_scan(motor) yield from abs_set(quadem1.averaging_time, 0.5, wait=True)
def into_control_range(self, p_theta=2, p_y=-15, p_z=90): """ Move the Kohzu motors into range so the energy controls will work. Written as a bluesky plan so that all motors can be moved simultaneously. Return early if the motors are already in range. USAGE:: RE(dcm.into_control_range()) """ args = [] if self.m_theta.position < p_theta: args += [self.m_theta, p_theta] if self.m_y.position > p_y: args += [self.m_y, p_y] if self.m_z.position < p_z: args += [self.m_z, p_z] if (len(args) == 0): # all motors in range, no work to do, MUST yield something yield from bps.null() return yield from bps.sleep(1) # allow IOC to react yield from bps.mv(self.operator_acknowledge, 1, self.mode, "Auto")
def execute_command_list(filename, commands, md=None): """ plan: execute the command list The command list is a tuple described below. * Only recognized commands will be executed. * Unrecognized commands will be reported as comments. See example implementation with APS USAXS instrument: https://github.com/APS-USAXS/ipython-usaxs/blob/5db882c47d935c593968f1e2144d35bec7d0181e/profile_bluesky/startup/50-plans.py#L381-L469 PARAMETERS filename : str Name of input text file. Can be relative or absolute path, such as "actions.txt", "../sample.txt", or "/path/to/overnight.txt". commands : list[command] List of command tuples for use in ``execute_command_list()`` where command : tuple (action, OrderedDict, line_number, raw_command) action: str names a known action to be handled parameters: list List of parameters for the action. The list is empty of there are no values line_number: int line number (1-based) from the input text file raw_command: obj (str or list(str) contents from input file, such as: ``SAXS 0 0 0 blank`` SEE ALSO .. autosummary:: ~execute_command_list ~register_command_handler ~run_command_file ~summarize_command_file ~parse_Excel_command_file ~parse_text_command_file *new in apstools release 1.1.7* """ full_filename = os.path.abspath(filename) if len(commands) == 0: yield from bps.null() return text = f"Command file: {filename}\n" text += str(APS_utils.command_list_as_table(commands)) print(text) for command in commands: action, args, i, raw_command = command print(f"file line {i}: {raw_command}") _md = {} _md["full_filename"] = full_filename _md["filename"] = filename _md["line_number"] = i _md["action"] = action _md["parameters"] = args # args is shorter than parameters, means the same thing here _md.update(md or {}) # overlay with user-supplied metadata action = action.lower() if action == "tune_optics": # example: yield from tune_optics(md=_md) emsg = "This is an example. action={raw_command}." emsg += " Must define your own execute_command_list() function" logger.warn(emsg) yield from bps.null() else: print(f"no handling for line {i}: {raw_command}")
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 per_nd_step(detectors, post_cache, *args, **kwargs): yield from bps.null()
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 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(start, stop, nsteps, move, slp, force): (ok, text) = BMM_clear_to_start() if force is False and ok is False: print(error_msg(text)) yield from null() return RE.msg_hook = None BMMuser.motor = user_ns['dm3_bct'] func = lambda doc: (doc['data'][motor.name], doc['data']['I0']) plot = DerivedPlot(func, xlabel=motor.name, ylabel='I0', title='I0 signal vs. slit height') line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \ (motor.name, 'i0', start, stop, nsteps, motor.user_readback.get()) 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_slit(slp): #if slit_height < 0.5: # yield from mv(slits3.vsize, 0.5) yield from abs_set(quadem1.averaging_time, 0.1, wait=True) yield from abs_set(motor.velocity, 0.4, wait=True) yield from abs_set(motor.kill_cmd, 1, wait=True) uid = yield from rel_scan([quadem1], motor, start, stop, nsteps) 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 = 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 abs_set(motor.kill_cmd, 1, wait=True) 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 abs_set(motor.kill_cmd, 1, wait=True) yield from move_after_scan(motor) yield from abs_set(quadem1.averaging_time, 0.5, wait=True) yield from scan_slit(slp)
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 bailout(): print(whisper('doing nothing')) yield from null()
def do_nothing(self): print(whisper('doing nothing')) yield from null()
def do_SlitHeight(self): print(go_msg('You would like to set the slit height...\n')) print(disconnected_msg('yield from slit_height()')) yield from null()
def do_XRFSpectrum(self): print(go_msg('You would like to see an XRF spectrum...\n')) user_ns['xs'].measure_xrf() yield from null()
def do_RockingCurve(self): print(go_msg('You would like to measure a rocking curve scan...\n')) print(disconnected_msg('yield from rocking_curve()')) yield from null()
def do_Spreadsheet(self): print(go_msg('You would like to import a spreadsheet...\n')) ## prompt for type of spreadsheet: wheel or glancing angle ret = user_ns['wmb'].spreadsheet() yield from null()
def set_filters(thickness=None, state=None): positions = [55, -46.5, -20.5, 5.5, 31] BMMuser = user_ns['BMMuser'] if thickness == 0 or state == 0: yield from mv(dm1_filters1, positions[0], dm1_filters2, positions[0]) BMMuser.filter_state = 0 elif thickness == 100 or state == 1: yield from mv(dm1_filters1, positions[1], dm1_filters2, positions[0]) BMMuser.filter_state = 1 elif thickness == 200 or state == 2: yield from mv(dm1_filters1, positions[2], dm1_filters2, positions[0]) BMMuser.filter_state = 2 elif thickness == 300 or state == 3: yield from mv(dm1_filters1, positions[2], dm1_filters2, positions[1]) BMMuser.filter_state = 3 elif thickness == 400 or state == 4: yield from mv(dm1_filters1, positions[2], dm1_filters2, positions[2]) BMMuser.filter_state = 4 elif thickness == 500 or state == 5: yield from mv(dm1_filters1, positions[3], dm1_filters2, positions[0]) BMMuser.filter_state = 5 elif thickness == 600 or state == 6: yield from mv(dm1_filters1, positions[3], dm1_filters2, positions[1]) BMMuser.filter_state = 6 elif thickness == 700 or state == 7: yield from mv(dm1_filters1, positions[3], dm1_filters2, positions[2]) BMMuser.filter_state = 7 elif thickness == 1000 or state == 8: yield from mv(dm1_filters1, positions[3], dm1_filters2, positions[3]) BMMuser.filter_state = 8 elif thickness == 1700 or state == 9: yield from mv(dm1_filters1, positions[4], dm1_filters2, positions[0]) BMMuser.filter_state = 9 elif thickness == 1800 or state == 10: yield from mv(dm1_filters1, positions[4], dm1_filters2, positions[1]) BMMuser.filter_state = 10 elif thickness == 1900 or state == 11: yield from mv(dm1_filters1, positions[4], dm1_filters2, positions[2]) BMMuser.filter_state = 11 elif thickness == 2200 or state == 12: yield from mv(dm1_filters1, positions[4], dm1_filters2, positions[3]) BMMuser.filter_state = 12 elif thickness == 3400 or state == 13: yield from mv(dm1_filters1, positions[4], dm1_filters2, positions[4]) BMMuser.filter_state = 13 else: print(error_msg('Valid filter thicknesses are: ')) print(error_msg(' state 0: 0 μm')) print(error_msg(' state 1: 100 μm')) print(error_msg(' state 2: 200 μm')) print(error_msg(' state 3: 300 μm')) print(error_msg(' state 4: 400 μm')) print(error_msg(' state 5: 500 μm')) print(error_msg(' state 6: 600 μm')) print(error_msg(' state 7: 700 μm')) print(error_msg(' state 8: 1000 μm')) print(error_msg(' state 9: 1700 μm')) print(error_msg(' state 10: 1800 μm')) print(error_msg(' state 11: 1900 μm')) print(error_msg(' state 12: 2200 μm')) print(error_msg(' state 13: 3400 μm')) yield from null()
def empty_plan(*args, **kwargs): logger.info(f"Doing nothing: args={args}, kwargs={kwargs}") yield from bps.null()
def rocking_curve(start=-0.10, stop=0.10, nsteps=101, detector='I0', choice='peak'): '''Perform a relative scan of the DCM 2nd crystal pitch around the current position to find the peak of the crystal rocking curve. Begin by opening the hutch slits to 3 mm. At the end, move to the position of maximum intensity on I0, then return to the hutch slits to their original height. Parameters ---------- start : (float) starting position relative to current [-0.1] end : (float) ending position relative to current [0.1] nsteps : (int) number of steps [101] detector : (string) 'I0' or 'Bicron' ['I0'] choice : (string) 'peak', fit' or 'com' (center of mass) ['peak'] If choice is fit, the fit is performed using the SkewedGaussianModel from lmfit, which works pretty well for this measurement at BMM. The line shape is a bit skewed due to the convolution with the slightly misaligned entrance slits. ''' def main_plan(start, stop, nsteps, detector): (ok, text) = BMM_clear_to_start() if ok is False: print(error_msg(text)) yield from null() return RE.msg_hook = None BMMuser.motor = motor if detector.lower() == 'bicron': func = lambda doc: (doc['data'][motor.name], doc['data']['Bicron']) dets = [ bicron, ] sgnl = 'Bicron' titl = 'Bicron signal vs. DCM 2nd crystal pitch' else: func = lambda doc: (doc['data'][motor.name], doc['data']['I0']) dets = [ quadem1, ] sgnl = 'I0' titl = 'I0 signal vs. DCM 2nd crystal pitch' plot = DerivedPlot(func, xlabel=motor.name, ylabel=sgnl, title=titl) 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_dcmpitch(sgnl): line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \ (motor.name, sgnl, start, stop, nsteps, motor.user_readback.get()) yield from abs_set(user_ns['_locked_dwell_time'], 0.1, wait=True) yield from dcm.kill_plan() yield from mv(slits3.vsize, 3) if sgnl == 'Bicron': yield from mv(slitsg.vsize, 5) uid = yield from rel_scan(dets, motor, start, stop, nsteps) #yield from rel_adaptive_scan(dets, 'I0', motor, # start=start, # stop=stop, # min_step=0.002, # max_step=0.03, # target_delta=.15, # backstep=True) t = db[-1].table() signal = t[sgnl] if choice.lower() == 'com': position = com(signal) top = t[motor.name][position] elif choice.lower() == 'fit': pitch = t['dcm_pitch'] mod = SkewedGaussianModel() pars = mod.guess(signal, x=pitch) out = mod.fit(signal, pars, x=pitch) print(whisper(out.fit_report(min_correl=0))) out.plot() top = out.params['center'].value else: position = peak(signal) top = t[motor.name][position] yield from sleep(3.0) yield from abs_set(motor.kill_cmd, 1, wait=True) RE.msg_hook = BMM_msg_hook BMM_log_info('rocking curve scan: %s\tuid = %s, scan_id = %d' % (line1, uid, user_ns['db'][-1].start['scan_id'])) yield from mv(motor, top) if sgnl == 'Bicron': yield from mv(slitsg.vsize, gonio_slit_height) yield from scan_dcmpitch(sgnl) def cleanup_plan(): yield from mv(user_ns['slits3'].vsize, slit_height) yield from abs_set(user_ns['_locked_dwell_time'], 0.5, wait=True) yield from sleep(1.0) yield from abs_set(motor.kill_cmd, 1, wait=True) yield from sleep(1.0) yield from user_ns['dcm'].kill_plan() yield from resting_state_plan() RE, BMMuser, db, rkvs = user_ns['RE'], user_ns['BMMuser'], user_ns[ 'db'], user_ns['rkvs'] dcm, slits3, slitsg, quadem1 = user_ns['dcm'], user_ns['slits3'], user_ns[ 'slitsg'], user_ns['quadem1'] ###################################################################### # this is a tool for verifying a macro. this replaces this rocking # # curve 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 running a rocking curve scan.\n' % BMMuser.macro_sleep)) countdown(BMMuser.macro_sleep) return (yield from null()) ###################################################################### motor = user_ns['dcm_pitch'] slit_height = user_ns['slits3'].vsize.readback.get() try: gonio_slit_height = slitsg.vsize.readback.get() except: gonio_slit_height = 1 RE.msg_hook = None yield from finalize_wrapper(main_plan(start, stop, nsteps, detector), cleanup_plan()) RE.msg_hook = BMM_msg_hook
def slit_height(start=-1.5, stop=1.5, nsteps=31, move=False, force=False, slp=1.0, choice='peak'): '''Perform a relative scan of the DM3 BCT motor around the current position to find the optimal position for slits3. Optionally, the motor will moved to the center of mass of the peak at the end of the scan. Parameters ---------- start : float starting position relative to current [-3.0] end : float ending position relative to current [3.0] nsteps : int number of steps [61] move : bool True=move to position of max signal, False=pluck and move [False] slp : float length of sleep before trying to move dm3_bct [3.0] choice : str 'peak' or 'com' (center of mass) ['peak'] ''' def main_plan(start, stop, nsteps, move, slp, force): (ok, text) = BMM_clear_to_start() if force is False and ok is False: print(error_msg(text)) yield from null() return RE.msg_hook = None BMMuser.motor = user_ns['dm3_bct'] func = lambda doc: (doc['data'][motor.name], doc['data']['I0']) plot = DerivedPlot(func, xlabel=motor.name, ylabel='I0', title='I0 signal vs. slit height') line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \ (motor.name, 'i0', start, stop, nsteps, motor.user_readback.get()) 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_slit(slp): #if slit_height < 0.5: # yield from mv(slits3.vsize, 0.5) yield from abs_set(quadem1.averaging_time, 0.1, wait=True) yield from abs_set(motor.velocity, 0.4, wait=True) yield from abs_set(motor.kill_cmd, 1, wait=True) uid = yield from rel_scan([quadem1], motor, start, stop, nsteps) 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 = 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 abs_set(motor.kill_cmd, 1, wait=True) 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 abs_set(motor.kill_cmd, 1, wait=True) yield from move_after_scan(motor) yield from abs_set(quadem1.averaging_time, 0.5, wait=True) yield from scan_slit(slp) def cleanup_plan(slp): yield from mv(slits3.vsize, slit_height) yield from abs_set(user_ns['_locked_dwell_time'], 0.5, wait=True) yield from sleep(slp) yield from abs_set(motor.kill_cmd, 1, wait=True) yield from resting_state_plan() RE, BMMuser, db, slits3, quadem1 = user_ns['RE'], user_ns[ 'BMMuser'], user_ns['db'], user_ns['slits3'], user_ns['quadem1'] rkvs = user_ns['rkvs'] ####################################################################### # this is a tool for verifying a macro. this replaces this slit # # height 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 running a slit height scan.\n' % BMMuser.macro_sleep)) countdown(BMMuser.macro_sleep) return (yield from null()) ####################################################################### motor = user_ns['dm3_bct'] slit_height = slits3.vsize.readback.get() RE.msg_hook = None yield from finalize_wrapper( main_plan(start, stop, nsteps, move, slp, force), cleanup_plan(slp)) RE.msg_hook = BMM_msg_hook
def linescan(detector, axis, start, stop, nsteps, pluck=True, force=False, inttime=0.1, md={}): # integration time? ''' Generic linescan plan. This is a RELATIVE scan, relative to the current position of the selected motor. Examples -------- >>> RE(linescan('it', 'x', -1, 1, 21)) Parameters ---------- detector : str detector to display -- if, it, ir, or i0 axis : str or EpicsMotor motor or nickname start : float starting value for a relative scan stop : float ending value for a relative scan nsteps : int number of steps in scan pluck : bool, optional flag for whether to offer to pluck & move motor force : bool, optional flag for forcing a scan even if not clear to start inttime : float, optional integration time in seconds (default: 0.1) The motor is either the BlueSky name for a motor (e.g. xafs_linx) or a nickname for an XAFS sample motor (e.g. 'x' for xafs_linx). This does not write an ASCII data file, but it does make a log entry. Use the ls2dat() function to extract the linescan from the database and write it to a file. ''' 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 cleanup_plan(): ## BMM_clear_suspenders() ##RE.clear_suspenders() # disable suspenders yield from resting_state_plan() RE, BMMuser, rkvs = user_ns['RE'], user_ns['BMMuser'], user_ns['rkvs'] try: xs = user_ns['xs'] except: pass ###################################################################### # 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 running a line scan.\n' % BMMuser.macro_sleep)) countdown(BMMuser.macro_sleep) return (yield from null()) ###################################################################### RE.msg_hook = None yield from finalize_wrapper( main_plan(detector, axis, start, stop, nsteps, pluck, force), cleanup_plan()) RE.msg_hook = BMM_msg_hook
def do_XAFS(self): print(go_msg('You would like to do an XAFS scan...\n')) howlong() yield from null()
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 main_plan(start, stop, nsteps, detector): (ok, text) = BMM_clear_to_start() if ok is False: print(error_msg(text)) yield from null() return RE.msg_hook = None BMMuser.motor = motor if detector.lower() == 'bicron': func = lambda doc: (doc['data'][motor.name], doc['data']['Bicron']) dets = [ bicron, ] sgnl = 'Bicron' titl = 'Bicron signal vs. DCM 2nd crystal pitch' else: func = lambda doc: (doc['data'][motor.name], doc['data']['I0']) dets = [ quadem1, ] sgnl = 'I0' titl = 'I0 signal vs. DCM 2nd crystal pitch' plot = DerivedPlot(func, xlabel=motor.name, ylabel=sgnl, title=titl) 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_dcmpitch(sgnl): line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \ (motor.name, sgnl, start, stop, nsteps, motor.user_readback.get()) yield from abs_set(user_ns['_locked_dwell_time'], 0.1, wait=True) yield from dcm.kill_plan() yield from mv(slits3.vsize, 3) if sgnl == 'Bicron': yield from mv(slitsg.vsize, 5) uid = yield from rel_scan(dets, motor, start, stop, nsteps) #yield from rel_adaptive_scan(dets, 'I0', motor, # start=start, # stop=stop, # min_step=0.002, # max_step=0.03, # target_delta=.15, # backstep=True) t = db[-1].table() signal = t[sgnl] if choice.lower() == 'com': position = com(signal) top = t[motor.name][position] elif choice.lower() == 'fit': pitch = t['dcm_pitch'] mod = SkewedGaussianModel() pars = mod.guess(signal, x=pitch) out = mod.fit(signal, pars, x=pitch) print(whisper(out.fit_report(min_correl=0))) out.plot() top = out.params['center'].value else: position = peak(signal) top = t[motor.name][position] yield from sleep(3.0) yield from abs_set(motor.kill_cmd, 1, wait=True) RE.msg_hook = BMM_msg_hook BMM_log_info('rocking curve scan: %s\tuid = %s, scan_id = %d' % (line1, uid, user_ns['db'][-1].start['scan_id'])) yield from mv(motor, top) if sgnl == 'Bicron': yield from mv(slitsg.vsize, gonio_slit_height) yield from scan_dcmpitch(sgnl)
def per_step_old(detectors, motor, step): yield from bps.null()
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 per_step_extra(detectors, motor, step, some_kwarg=None): yield from bps.null()
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 per_step_exact(detectors, motor, step, take_reading=None): yield from bps.null()
def _unstage(): for component in hinted_stash: component.kind = Kind.hinted yield from null()
def per_step_kwargs(detectors, motor, step, **kwargs): yield from bps.null()