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 ampen(): for m in mcs8_motors: if m.ampen.get(): print("%-12s : %s" % (m.name, warning_msg(m.ampen.enum_strs[m.ampen.get()]))) else: print("%-12s : %s" % (m.name, m.ampen.enum_strs[m.ampen.get()]))
def _pressure(self): if self.pressure.get() == 'OFF': return (disconnected_msg(-1.1e-15)) if float(self.pressure.get()) > 1e-1: return warning_msg(self.pressure.get()) if float(self.pressure.get()) > 6e-3: return error_msg(self.pressure.get()) return (self.pressure.get())
def _state(self, info=False): t = "%.1f" % self.temperature.get() if self.temperature.get() > self.alarm.get(): return(error_msg(t)) if self.temperature.get() > self.warning.get(): return(warning_msg(t)) if info is True and self.temperature.get() > (0.5 * self.warning.get()): return(info_msg(t)) return(t)
def on(self, number): '''Turn on the specified spinner''' if self.spin is False: print(warning_msg('The spinners are currently disabled. do "ga.spin = True" to re-enable.')) return if not self.valid(number): print(error_msg('The fans are numbered from 1 to 8')) return this = getattr(self, f'spinner{number}') this.put(1)
def _current(self): curr = float(self.current.get()) if curr > 2e-3: out = '%.1f' % (1e3 * curr) return (error_msg(out)) if curr > 5e-4: out = '%.1f' % (1e3 * curr) return (warning_msg(out)) out = '%.1f' % (1e6 * curr) return (out)
def _pressure(self): #print(self.pressure.get()) #print(type(self.pressure.get())) if self.pressure.get() == 'OFF': return (disconnected_msg(-1.1E-15)) if float(self.pressure.get()) > 1e-6: return error_msg(self.pressure.get()) if float(self.pressure.get()) > 1e-8: return warning_msg(self.pressure.get()) return (self.pressure.get())
def _current(self): if self.connected is False: return(disconnected_msg('?????')) curr = float(self.current.get()) if curr > 2e-3: out = '%.1f' % (1e3*curr) return(error_msg(out)) if curr > 5e-4: out = '%.1f' % (1e3*curr) return(warning_msg(out)) out = '%.1f' % (1e6*curr) return(out)
def _pressure(self): #print(self.pressure.get()) #print(type(self.pressure.get())) if self.connected is False: return(disconnected_msg('?????')) if self.pressure.get() == 'OFF': return(disconnected_msg(-1.1E-15)) if type(self.pressure.get()) is str and self.pressure.get() == 'LO<E-11': return whisper('1.00e-11') if float(self.pressure.get()) > 1e-6: return error_msg(self.pressure.get()) if float(self.pressure.get()) > 1e-8: return warning_msg(self.pressure.get()) return(self.pressure.get())
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 select(self, el): '''Choose the ROI configured for element el''' if type(el) is int: if el < 1 or el > 3: print(error_msg('\n%d is not a valid ROI channel\n' % el)) return (yield from null()) el = self.slots[el - 1] if el is None: print(error_msg('\nThat ROI is not configured\n')) return (yield from null()) if Z_number(el) is None: print(error_msg('\n%s is not an element\n' % el)) return (yield from null()) selected = False vor = user_ns['vor'] BMMuser = user_ns['BMMuser'] for i in range(3): if element_symbol(el) == self.slots[i]: BMMuser.roi_channel = i + 1 if i == 0: # help out the best effort callback (BMMuser.roi1, BMMuser.roi2, BMMuser.roi3, BMMuser.roi4) = ('ROI1', 'ROI2', 'ROI3', 'ROI4') (BMMuser.dtc1, BMMuser.dtc2, BMMuser.dtc3, BMMuser.dtc4) = ('DTC1', 'DTC2', 'DTC3', 'DTC4') vor.set_hints(1) elif i == 1: (BMMuser.roi1, BMMuser.roi2, BMMuser.roi3, BMMuser.roi4) = ('ROI2_1', 'ROI2_2', 'ROI2_3', 'ROI2_4') (BMMuser.dtc1, BMMuser.dtc2, BMMuser.dtc3, BMMuser.dtc4) = ('DTC2_1', 'DTC2_2', 'DTC2_3', 'DTC2_4') vor.set_hints(2) elif i == 2: (BMMuser.roi1, BMMuser.roi2, BMMuser.roi3, BMMuser.roi4) = ('ROI3_1', 'ROI3_2', 'ROI3_3', 'ROI3_4') (BMMuser.dtc1, BMMuser.dtc2, BMMuser.dtc3, BMMuser.dtc4) = ('DTC3_1', 'DTC3_2', 'DTC3_3', 'DTC3_4') vor.set_hints(3) report('Set ROI channel to %s at channel %d' % (el.capitalize(), i + 1)) selected = True if not selected: print( warning_msg( '%s is not in a configured channel, not changing BMMuser.roi_channel' % el.capitalize())) yield from null()
def _current(self, num=None): if num is None: num = 1 if num < 1: num = 1 if num > 6: num = 6 sgnl = getattr(self, 'c' + str(num)) curr = float(sgnl.get()) if curr > 2e-3: out = '%.1f' % (1e3 * curr) return (error_msg(out)) if curr > 5e-4: out = '%.1f' % (1e3 * curr) return (warning_msg(out)) out = '%.1f' % (1e6 * curr) return (out)
def _pressure(self, num=None): if num is None: num = 1 if num < 1: num = 1 if num > 6: num = 6 sgnl = getattr(self, 'p' + str(num)) #print(self.pressure.get()) #print(type(self.pressure.get())) if sgnl.get() == 'OFF': return (disconnected_msg(-1.1E-15)) if float(sgnl.get()) > 1e-6: return error_msg(self.pressure.get()) if float(sgnl.get()) > 1e-8: return warning_msg(self.pressure.get()) return (sgnl.get())
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 sanitize_step_scan_parameters(bounds, steps, times): '''Attempt to identify and flag/correct some common scan parameter mistakes.''' problem = False text = '' ############################################################################ # bounds is one longer than steps/times, length of steps = length of times # ############################################################################ if (len(bounds) - len(steps)) != 1: text += error_msg('\nbounds must have one more item than steps\n') text += error_msg('\tbounds = %s\n' % ' '.join(map(str, bounds))) text += error_msg('\tsteps = %s\n' % ' '.join(map(str, steps))) problem = True if (len(bounds) - len(times)) != 1: text += error_msg('\nbounds must have one more item than times\n') text += error_msg('\tbounds = %s\n' % ' '.join(map(str, bounds))) text += error_msg('\ttimes = %s\n' % ' '.join(map(str, times))) problem = True ############################ # tests of boundary values # ############################ for b in bounds: if not isfloat(b) and b[-1:].lower() == 'k': if not isfloat(b[:-1]): text += error_msg('\n%s is not a valid scan boundary value\n' % b) problem = True elif not isfloat(b): text += error_msg('\n%s is not a valid scan boundary value\n' % b) problem = True if not isfloat(b) and b[:1] == '-' and b[-1:].lower() == 'k': text += error_msg('\nNegative bounds must be energy-valued, not k-valued (%s)\n' % b) problem = True ############################# # tests of step size values # ############################# for s in steps: if not isfloat(s) and s[-1:].lower() == 'k': if not isfloat(s[:-1]): text += error_msg('\n%s is not a valid scan step size value\n' % s) problem = True elif float(s[:-1]) < 0: text += error_msg('\nStep sizes cannot be negative (%s)\n' % s) problem = True elif not isfloat(s): text += error_msg('\n%s is not a valid scan step size value\n' % s) problem = True if isfloat(s) and float(s) < 0: text += error_msg('\nStep sizes cannot be negative (%s)\n' % s) problem = True elif isfloat(s) and float(s) <= 0.09: text += warning_msg('\n%s is a very small step size!\n' % s) elif not isfloat(s) and s[-1:].lower() == 'k' and isfloat(s[-1:]) and float(s[:-1]) < 0.01: text += warning_msg('\n%s is a very small step size!\n' % s) #################################### # tests of integration time values # #################################### for t in times: if not isfloat(t) and t[-1:].lower() == 'k': if not isfloat(t[:-1]): text += error_msg('\n%s is not a valid integration time value\n' % t) problem = True elif float(t[:-1]) < 0: text += error_msg('\nIntegration times cannot be negative (%s)\n' % t) problem = True elif not isfloat(t): text += error_msg('\n%s is not a valid integration time value\n' % t) problem = True if isfloat(t) and float(t) < 0: text += error_msg('\nIntegration times cannot be negative (%s)\n' % t) problem = True elif isfloat(t) and float(t) <= 0.1: text += warning_msg('\n%s is a very short integration time!\n' % t) elif not isfloat(t) and t[-1:].lower() == 'k' and isfloat(t[-1:]) and float(t[:-1]) < 0.05: text += warning_msg('\n%s is a very short integration time!\n' % t) reference = 'https://nsls-ii-bmm.github.io/BeamlineManual/xafs.html#scan-regions\n' return problem, text, reference
def main_plan(inifile, force, **kwargs): ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-- ## read and check INI content orig = inifile if not os.path.isfile(inifile): inifile = DATA + inifile if not os.path.isfile(inifile): print( warning_msg('\n%s does not exist! Bailing out....\n' % orig)) return (orig, -1) print(bold_msg('reading ini file: %s' % inifile)) (p, f) = scan_metadata(inifile=inifile, **kwargs) if not any( p): # scan_metadata returned having printed an error message return (yield from null()) #if not os.path.isdir(p['folder']): # print(error_msg('\n%s is not a folder\n' % p['folder'])) # return(yield from null()) detector = 'It' if 'trans' in p['mode']: detector = 'It' elif 'fluo' in p['mode']: detector = 'If' ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-- ## verify output file name won't be overwritten outfile = '%s.%3.3d' % (os.path.join(p['folder'], p['filename']), p['start']) if os.path.isfile(outfile): print(error_msg('%s already exists! Bailing out....' % outfile)) return (yield from null()) ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-- ## prompt user and verify that we are clear to start text = '\n' for k in ('folder', 'filename', 'experimenters', 'e0', 'npoints', 'dwell', 'delay', 'sample', 'prep', 'comment', 'mode', 'snapshots'): text = text + ' %-13s : %-50s\n' % (k, p[k]) if BMMuser.prompt: boxedtext('How does this look?', text + '\n %-13s : %-50s\n' % ('output file', outfile), 'green', width=len(outfile) + 25) # see 05-functions action = input("\nBegin time scan? [Y/n then Enter] ") if action.lower() == 'q' or action.lower() == 'n': return (yield from null()) (ok, ctstext) = BMM_clear_to_start() if force is False and ok is False: print(error_msg(ctstext)) yield from null() return ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-- # organize metadata for injection into database and XDI output print(bold_msg('gathering metadata')) md = bmm_metadata( measurement=p['mode'], experimenters=p['experimenters'], edge=p['edge'], element=p['element'], edge_energy=p['e0'], direction=0, scantype='fixed', channelcut=p['channelcut'], mono='Si(%s)' % dcm._crystal, i0_gas='N2', #\ it_gas='N2', # > these three need to go into INI file ir_gas='N2', #/ sample=p['sample'], prep=p['prep'], stoichiometry=None, mode=p['mode'], comment=p['comment'], ) del (md['XDI']['Element']['edge']) del (md['XDI']['Element']['symbol']) md['XDI']['Column']['01'] = 'time seconds' md['XDI']['Column']['02'] = md.copy()['XDI']['Column']['03'] md['XDI']['Column']['03'] = md.copy()['XDI']['Column']['04'] md['XDI']['Column']['04'] = md['XDI']['Column']['05'] del (md['XDI']['Column']['05']) md['_kind'] = 'sead' rightnow = metadata_at_this_moment() # see 62-metadata.py for family in rightnow.keys(): # transfer rightnow to md if type(rightnow[family]) is dict: if family not in md: md[family] = dict() for k in rightnow[family].keys(): md[family][k] = rightnow[family][k] xdi = {'XDI': md} BMM_log_info( 'Starting single-energy absorption detection time scan using\n%s:\n%s\nCommand line arguments = %s\nMoving to measurement energy: %.1f eV' % (inifile, text, str(kwargs), p['e0'])) ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-- ## move to the energy specified in the INI file print(bold_msg('Moving to measurement energy: %.1f eV' % p['e0'])) dcm.mode = 'fixed' yield from mv(dcm.energy, p['e0']) ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-- ## snap photos if p['snapshots']: image = os.path.join( p['folder'], 'snapshots', "%s_XASwebcam_%s.jpg" % (p['filename'], now())) snap('XAS', filename=image) image = os.path.join(p['folder'], 'snapshots', "%s_analog_%s.jpg" % (p['filename'], now())) snap('analog', filename=image) ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-- ## engage suspenders right before starting measurement if not force: BMM_suspenders() ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-- ## perform the actual time scan uid = yield from timescan(detector, p['npoints'], p['dwell'], p['delay'], force=force, md={**xdi}) ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-- ## write the output file header = db[uid] write_XDI(outfile, header) # yield from ? report('wrote time scan to %s' % outfile)