def _inner_lup(): yield from rel_scan( detectors + extras, *args, per_step=per_step, md=_md, )
def two_pass_scan(md=None): """ Find the peak of noisy v. m1 in the range of +/- 2. We know the peak center of the simulated noisy detector is positioned randomly between -1 to +1. Overscan that range to find both sides of the peak. This is a 2 scan procedure. First scan passes through the full range. Second scan is centered on the peak and width of the first scan. """ md = md or {} change_noisy_parameters() sig = 2 expansion_factor = 1.2 # expand search by FWHM*expansion_factor m1.move(0) for i in range(1, 3): md["scan_sequence"] = i uid = yield from bp.rel_scan([noisy], m1, -sig, +sig, 23, md=md) stats = _get_peak_stats(uid, noisy.name, m1.name) if len(stats) > 0: sig = stats["fwhm"] * expansion_factor m1.move(stats["centroid_position"]) else: logger.warning("Catalog object not found. No peak statistics.") break
def doscan(): line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \ (motor.name, sgnl, start, stop, nsteps, motor.user_readback.get()) uid = yield from rel_scan(dets, motor, start, stop, nsteps, md={'plan_name' : f'rel_scan linescan {motor.name} I0'}) t = user_ns['db'][-1].table() if detector.lower() == 'if': signal = numpy.array((t[BMMuser.xs1]+t[BMMuser.xs2]+t[BMMuser.xs3]+t[BMMuser.xs4])/t['I0']) elif detector.lower() == 'it': signal = numpy.array(t['It']/t['I0']) elif detector.lower() == 'ir': signal = numpy.array(t['Ir']/t['It']) signal = signal - signal[0] if negate is True: signal = -1 * signal pos = numpy.array(t[motor.name]) mod = RectangleModel(form='erf') pars = mod.guess(signal, x=pos) out = mod.fit(signal, pars, x=pos) print(whisper(out.fit_report(min_correl=0))) out.plot() if filename is not None: plt.savefig(filename) #middle = out.params['center1'].value + (out.params['center2'].value - out.params['center1'].value)/2 yield from mv(motor, out.params['midpoint'].value) print(bold_msg(f'Found center at {motor.name} = {motor.position}')) rkvs.set('BMM:lmfit:center1', out.params['center1'].value) rkvs.set('BMM:lmfit:center2', out.params['center2'].value) rkvs.set('BMM:lmfit:sigma1', out.params['sigma1'].value) rkvs.set('BMM:lmfit:sigma2', out.params['sigma2'].value) rkvs.set('BMM:lmfit:amplitude', out.params['amplitude'].value) rkvs.set('BMM:lmfit:midpoint', out.params['midpoint'].value)
def find_peak(det, mot, start, stop, steps): print(f"Scanning {mot.name} vs {det.name}...") uid = yield from bp.rel_scan([det], mot, start, stop, steps) sp = '_setpoint' if mot is ivu_gap else '_user_setpoint' if det.name == "bpm3": detector_response_name = det.name + '_sum_all' else: detector_response_name = det.name data = np.array(db[uid].table()[[detector_response_name, mot.name + sp]])[1:] peak_idx = np.argmax(data[:, 0]) peak_x = data[peak_idx, 1] peak_y = data[peak_idx, 0] if mot is ivu_gap: m = ivu_gap.gap else: m = mot print( f"Found peak for {m.name} at {peak_x} {m.egu} [BPM reading {peak_y}]") return peak_x, peak_y
def center(signal_key='xsp3_channel1_rois_roi01_value', motor_key='s_stage_pz'): # first move the stage to the copper yield from copper() # do a z-scan while reading the xps3 num = 10 xsp3.total_points.put(num) yield from bp.rel_scan([xsp3], pz, -3, 3, num=num) # grab the data to find the signal peak, no need to fill data, ROI's are scalars df = db[-1].table() ds = df[signal_key] dz = df[motor_key] # find the location of the signal maximum # need to check out the data type of the xps3 deriv = ds.diff() / dz.diff() left_z = dz[deriv == deriv.max()].item() right_z = dz[deriv == deriv.min()].item() nz = (right_z - left_z) / 2 + left_z print(f'({left_z}, {right_z})--> {nz}') # set the z offset so that the stage is zeroed at the signal max # user_readback=dial+offset curr_offset = pz.user_offset.get() pz.user_offset.set(curr_offset - nz) # move to the home location #yield from home() yield from bps.mv(px, 0, py, 0, pz, 0)
def _gen(self): return rel_scan(self.detectors, self.motor, self.start, self.stop, self.num, md=self.md)
def scan_xafs_motor(dets, motor, start, stop, nsteps): uid = yield from rel_scan(dets, motor, start, stop, nsteps, md={ **thismd, **md }) return uid
def test_per_step(RE, hw): # Check default behavior, using one motor and then two. RE(scan([hw.det], hw.motor, -1, 1, 3, per_step=one_nd_step)) RE( scan([hw.det], hw.motor, -1, 1, hw.motor2, -1, 1, 3, per_step=one_nd_step)) RE(inner_product_scan([hw.det], 3, hw.motor, -1, 1, per_step=one_nd_step)) RE( inner_product_scan([hw.det], 3, hw.motor, -1, 1, hw.motor2, -1, 1, per_step=one_nd_step)) # Check that scan still accepts old one_1d_step signature: RE(scan([hw.det], hw.motor, -1, 1, 3, per_step=one_1d_step)) RE(rel_scan([hw.det], hw.motor, -1, 1, 3, per_step=one_1d_step)) # Test that various error paths include a useful error message identifying # that the problem is with 'per_step': # You can't usage one_1d_step signature with more than one motor. with pytest.raises(TypeError) as excinfo: RE( scan([hw.det], hw.motor, -1, 1, hw.motor2, -1, 1, 3, per_step=one_1d_step)) assert excinfo.match("Signature of per_step assumes 1D trajectory") # The signature must be either like one_1d_step or one_nd_step: def bad_sig(detectors, mtr, step): ... with pytest.raises(TypeError) as excinfo: RE(scan([hw.det], hw.motor, -1, 1, 3, per_step=bad_sig)) assert excinfo.match("per_step must be a callable with the signature")
def lineupScans(scaler, motor, start, end, numPts=23, numScans=3): """ show simple peak finding by repeated scans with refinement basically:: RE(bp.scan([scaler], motor, start, end, numPts)) %mov motor peaks["cen"]["scaler"] fwhm=peaks["fwhm"]["scaler"] RE(bp.rel_scan([scaler], motor, -fwhm, fwhm, numPts)) %mov motor peaks["cen"]["scaler"] fwhm=peaks["fwhm"]["scaler"] RE(bp.rel_scan([scaler], motor, -fwhm, fwhm, numPts)) """ yield from bps.mv(motor, start) yield from bp.rel_scan([scaler], motor, 0, end - start, numPts) fwhm = bec.peaks["fwhm"][scaler.name] cen = bec.peaks["cen"][scaler.name] results = [(RE.md["scan_id"], cen, fwhm)] for _again in range(numScans - 1): logger.info("starting rescan %d", (_again + 1)) yield from bps.mv(motor, cen) yield from bp.rel_scan([scaler], motor, -fwhm, fwhm, numPts) if scaler.name not in bec.peaks["fwhm"]: logger.error("no data in `bec.peaks`, end of these scans") break fwhm = bec.peaks["fwhm"][scaler.name] cen = bec.peaks["cen"][scaler.name] results.append((RE.md["scan_id"], cen, fwhm)) tbl = pyRestTable.Table() tbl.addLabel("scan_id") tbl.addLabel("center") tbl.addLabel("FWHM") for row in results: tbl.addRow(row) logger.info("summary results:\n%s", str(tbl))
def findpeak_multipass(number_of_scans=4, number_of_points=23, magnify=1.2, md=None): """ find peak of noisy v. m1 by repeated scans with refinement basically:: sig = 2.1 m1.move(0.0) for _ in range(3): RE(bp.rel_scan([noisy], m1, -sig, sig, 23)) stats = _get_peak_stats(uid, noisy.name, m1.name) sig = stats["fwhm"] m1.move(stats["centroid_position"]) """ md = md or {} md["number_of_scans"] = number_of_scans sig = 2.1 / magnify cen = 0 results = [] for _again in range(number_of_scans): md["scan_sequence_number"] = _again + 1 m1.move(cen) uid = yield from bp.rel_scan([noisy], m1, -magnify * sig, magnify * sig, number_of_points, md=md) stats = _get_peak_stats(uid, noisy.name, m1.name) if len(stats) > 0: scan_id = cat[uid].metadata["start"]["scan_id"] sig = stats["fwhm"] cen = stats["centroid_position"] results.append((scan_id, cen, sig)) else: break m1.move(cen) tbl = pyRestTable.Table() tbl.labels = "scan_id center FWHM".split() for row in results: tbl.addRow(row) logger.info("iterative results:\n%s", str(tbl))
def _scan(md=None): yield from bp.rel_scan([signal], motor, -width/2, width/2, num) # Grab final position from subscribed peak_stats valid_peak = peak_stats.max[-1] >= (4 * peak_stats.min[-1]) if peak_stats.max is None: valid_peak = False final_position = initial_position if valid_peak: # Condition for finding a peak if peak_choice == 'cen': final_position = peak_stats.cen elif peak_choice == 'com': final_position = peak_stats.com yield from bps.mv(motor, final_position)
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)
def scan_dcm_pitch(): line1 = "%s, %s, %.3f, %.3f, %d -- starting at %.3f\n" % ( pitch.name, "I0", start, stop, nsteps, pitch.readback.get(), ) uid = yield from rel_scan([I0], pitch, start, stop, nsteps) # The data that we just acquired has been cached in memory by src. # Access it as a pandas DataFrame so that we can conveniently do some # math on it. run = src.retrieve() t = run.primary.read().to_dataframe() if choice.lower() == "com": signal = numpy.array(t["I0"]) position = com(signal) top = t['pitch'].iloc[position] elif choice.lower() == "fit": signal = numpy.array(t["I0"]) pitch_ = numpy.array(t["pitch"]) mod = SkewedGaussianModel() pars = mod.guess(signal, x=pitch_) out = mod.fit(signal, pars, x=pitch_) print(out.fit_report(min_correl=0)) out.plot() top = out.params["center"].value else: signal = t['I0'] position = peak(signal) top = t[pitch.name][position] print( "rocking curve scan: %s\tuid = %s, scan_id = %d" % (line1, uid, run.metadata["start"]["scan_id"]) ) print(f"Found and moved to peak at {top:.3} via method {choice}") yield from mv(pitch, top)
def scan_slit(slp): #if slit_height < 0.5: # yield from mv(slits3.vsize, 0.5) yield from mv(quadem1.averaging_time, 0.1) yield from mv(motor.velocity, 0.4) yield from mv(motor.kill_cmd, 1) uid = yield from rel_scan([quadem1], motor, start, stop, nsteps, md={'plan_name' : f'rel_scan linescan {motor.name} I0'}) user_ns['RE'].msg_hook = BMM_msg_hook BMM_log_info('slit height scan: %s\tuid = %s, scan_id = %d' % (line1, uid, user_ns['db'][-1].start['scan_id'])) if move: t = user_ns['db'][-1].table() signal = t['I0'] #if get_mode() in ('A', 'B', 'C'): # position = com(signal) #else: position = peak(signal) top = t[motor.name][position] yield from sleep(slp) yield from mv(motor.kill_cmd, 1) yield from sleep(slp) yield from mv(motor, top) else: action = input('\n' + bold_msg('Pluck motor position from the plot? [Y/n then Enter] ')) if action.lower() == 'n' or action.lower() == 'q': return(yield from null()) yield from sleep(slp) yield from mv(motor.kill_cmd, 1) #yield from mv(motor.inpos, 1) yield from sleep(slp) yield from move_after_scan(motor) yield from mv(quadem1.averaging_time, 0.5)
def test_per_step(RE, hw): # Check default behavior, using one motor and then two. RE(scan([hw.det], hw.motor, -1, 1, 3, per_step=one_nd_step)) RE(scan([hw.det], hw.motor, -1, 1, hw.motor2, -1, 1, 3, per_step=one_nd_step)) RE(inner_product_scan([hw.det], 3, hw.motor, -1, 1, per_step=one_nd_step)) RE(inner_product_scan([hw.det], 3, hw.motor, -1, 1, hw.motor2, -1, 1, per_step=one_nd_step)) # Check that scan still accepts old one_1d_step signature: RE(scan([hw.det], hw.motor, -1, 1, 3, per_step=one_1d_step)) RE(rel_scan([hw.det], hw.motor, -1, 1, 3, per_step=one_1d_step)) # Test that various error paths include a useful error message identifying # that the problem is with 'per_step': # You can't usage one_1d_step signature with more than one motor. with pytest.raises(TypeError) as exc: RE(scan([hw.det], hw.motor, -1, 1, hw.motor2, -1, 1, 3, per_step=one_1d_step)) assert "Signature of per_step assumes 1D trajectory" in str(exc) # The signature must be either like one_1d_step or one_nd_step: def bad_sig(detectors, mtr, step): ... with pytest.raises(TypeError) as exc: RE(scan([hw.det], hw.motor, -1, 1, 3, per_step=bad_sig)) assert "per_step must be a callable with the signature" in str(exc)
def lineup( counter, axis, minus, plus, npts, time_s=0.1, peak_factor=4, width_factor=0.8, md=None): """ lineup and center a given axis, relative to current position PARAMETERS counter : object instance of ophyd.Signal (or subclass such as ophyd.scaler.ScalerChannel) dependent measurement to be maximized axis : movable object instance of ophyd.Signal (or subclass such as EpicsMotor) independent axis to use for alignment minus : float first point of scan at this offset from starting position plus : float last point of scan at this offset from starting position npts : int number of data points in the scan time_s : float (default: 0.1) count time per step (if counter is ScalerChannel object) peak_factor : float (default: 4) peak maximum must be greater than ``peak_factor*minimum`` width_factor : float (default: 0.8) fwhm must be less than ``width_factor*plot_range`` EXAMPLE:: RE(lineup(diode, foemirror.theta, -30, 30, 30, 1.0)) """ bec = APS_utils.ipython_shell_namespace().get("bec") if bec is None: raise ValueError("Cannot find BestEffortCallback() instance.") # first, determine if counter is part of a ScalerCH device scaler = None obj = counter.parent if isinstance(counter.parent, ScalerChannel): if hasattr(obj, "parent") and obj.parent is not None: obj = obj.parent if hasattr(obj, "parent") and isinstance(obj.parent, ScalerCH): scaler = obj.parent if scaler is not None: old_sigs = scaler.stage_sigs scaler.stage_sigs["preset_time"] = time_s scaler.select_channels([counter.name]) if hasattr(axis, "position"): old_position = axis.position else: old_position = axis.get() def peak_analysis(): aligned = False if counter.name in bec.peaks["cen"]: table = pyRestTable.Table() table.labels = ("key", "value") table.addRow(("axis", axis.name)) table.addRow(("detector", counter.name)) table.addRow(("starting position", old_position)) for key in bec.peaks.ATTRS: table.addRow((key, bec.peaks[key][counter.name])) logger.info(f"alignment scan results:\n{table}") lo = bec.peaks["min"][counter.name][-1] # [-1] means detector hi = bec.peaks["max"][counter.name][-1] # [0] means axis fwhm = bec.peaks["fwhm"][counter.name] final = bec.peaks["cen"][counter.name] ps = list(bec._peak_stats.values())[0][counter.name] # PeakStats object # get the X data range as received by PeakStats x_range = abs(max(ps.x_data) - min(ps.x_data)) if final is None: logger.error(f"centroid is None") final = old_position elif fwhm is None: logger.error(f"FWHM is None") final = old_position elif hi < peak_factor*lo: logger.error(f"no clear peak: {hi} < {peak_factor}*{lo}") final = old_position elif fwhm > width_factor*x_range: logger.error(f"FWHM too large: {fwhm} > {width_factor}*{x_range}") final = old_position else: aligned = True logger.info(f"moving {axis.name} to {final} (aligned: {aligned})") yield from bps.mv(axis, final) else: logger.error("no statistical analysis of scan peak!") yield from bps.null() # too sneaky? We're modifying this structure locally bec.peaks.aligned = aligned bec.peaks.ATTRS = ('com', 'cen', 'max', 'min', 'fwhm') _md = dict(purpose="alignment") _md.update(md or {}) yield from bp.rel_scan([counter], axis, minus, plus, npts, md=_md) yield from peak_analysis() if bec.peaks.aligned: # again, tweak axis to maximize _md["purpose"] = "alignment - fine" fwhm = bec.peaks["fwhm"][counter.name] yield from bp.rel_scan([counter], axis, -fwhm, fwhm, npts, md=_md) yield from peak_analysis() if scaler is not None: scaler.select_channels() scaler.stage_sigs = old_sigs
from bluesky.plans import rel_scan from bluesky.callbacks import LiveTable, LivePlot #dscan diff.yh subs = [LiveTable(['diff_yh', 'xray_eye3_stats1_total', 'xray_eye3_stats2_total']), LivePlot('xray_eye3_stats1_total', 'diff_yh')] print('A rel_scan of diff_yh with xray_eye3 as camera') RE(rel_scan([xray_eye3], diff.yh, -.1, .1, 3), subs)
from bluesky.plans import rel_scan from bluesky.callbacks import LiveTable, LivePlot subs = [ LiveTable(['dcm_b', 'xray_eye3_stats1_total', 'xray_eye3_stats2_total']), LivePlot('xray_eye3_stats1_total', 'dcm_b') ] print(dcm.b.read()) RE(rel_scan([xray_eye3], dcm.b, -.1, .1, 3), subs) print(dcm.b.read())
from bluesky.plans import rel_scan from bluesky.callbacks import LiveTable, LivePlot subs = [ LiveTable(['mbs_xc', 'xray_eye3_stats1_total', 'xray_eye3_stats2_total']), LivePlot('xray_eye3_stats1_total', 'mbs_xc') ] print(mbs.xc.read()) RE(rel_scan([xray_eye3], mbs.xc, -.1, .1, 3), subs) print(mbs.xc.read())
from bluesky.plans import rel_scan from bluesky.callbacks import LiveTable, LivePlot subs = [ LiveTable(['gsl_xc', 'xray_eye3_stats4_total', 'xray_eye3_stats4_total']), LivePlot('xray_eye3_stats4_total', 'gsl_xc') ] print(gsl.xc.read()) RE(rel_scan([xray_eye3], gsl.xc, -.1, .1, 3), subs) print(gsl.xc.read())
from bluesky.plans import rel_scan from bluesky.callbacks import LiveTable, LivePlot subs = [ LiveTable([ 'diff_xh', 'eiger1m_single_stats1_total', 'eiger1m_single_stats2_total' ]), LivePlot('eiger1m_single_stats1_total', 'diff_xh') ] print( 'The fast shutter will open/close three times, motor is diff.xh, camera is eiger1m_single' ) RE(rel_scan([eiger1m_single], diff.xh, -.1, .1, 3), subs) img = get_images(db[-1], 'eiger1m_single_image') print('show the first image') plt.imshow(img[0][0]) #can we change only one mater file for the same dscan?
def plan_sequential_runs(npts): # Single-run plans may be called consecutively. No special handling is required # as long as the previous scan is closed before the next one is opened yield from scan([hw.det1], hw.motor1, -1, 1, npts) yield from rel_scan([hw.det1, hw.det2], hw.motor1, -1, 1, npts)
def rel_escan(*args, **kwargs): return (yield from bp.rel_scan(*args, per_step=one_1d_step_pseudo_shutter, **kwargs))
def test_simple_rel_scan(): RE = RunEngine() hardware = yaqc_bluesky.Device(39424) sensor = yaqc_bluesky.Device(39425) RE(rel_scan([sensor], hardware, -1, 1, 15))
def test_lin_dscan(RE, hw): traj = np.linspace(0, 10, 5) + 6 hw.motor.set(6) scan = bp.rel_scan([hw.det], hw.motor, 0, 10, 5) traj_checker(RE, scan, traj)
from bluesky.plans import rel_scan from bluesky.callbacks import LiveTable, LivePlot subs = [ LiveTable(['ivu_gap', 'xray_eye3_stats1_total', 'xray_eye3_stats1_total']), LivePlot('xray_eye3_stats1_total', 'ivu_gap') ] print(ivu_gap.read()) RE(rel_scan([xray_eye3], ivu_gap, -.1, .1, 3), subs) print(ivu_gap.read())
def scan_xafs_motor(dets, motor, start, stop, nsteps): uid = yield from rel_scan(dets, motor, start, stop, nsteps, md={**thismd, **md, 'plan_name' : f'rel_scan linescan {motor.name} {detector}'}) return uid
from bluesky.plans import rel_scan #from bluesky.callbacks import LiveTable, LivePlot assert epu2.connected RE(rel_scan([epu2], epu2.gap, 0, 0.2, 5))
from bluesky.plans import rel_scan from bluesky.callbacks import LiveTable, LivePlot subs = [ LiveTable(['diff_xh', 'xray_eye1_stats1_total', 'xray_eye1_stats2_total']), LivePlot('xray_eye1_stats1_total', 'diff_xh') ] RE(rel_scan([xray_eye1], diff.xh, -.1, .1, 3), subs)
from bluesky.plans import rel_scan from bluesky.callbacks import LiveTable, LivePlot subs = [LiveTable(['diff_xh', 'xray_eye3_stats1_total', 'xray_eye3_stats2_total']), LivePlot('xray_eye3_stats1_total', 'diff_xh')] print ( 'Motor is diff.xh, camera is xray_eye3 with saving images') RE(rel_scan([xray_eye3_writing], diff.xh, -.1, .1, 3), subs) img = get_images(db[-1], 'xray_eye3_image') print('show the first image') plt.imshow( img[0] )
from bluesky.plans import rel_scan #from bluesky.callbacks import LiveTable, LivePlot assert sclr.connected #subs = [LiveTable(['eta', 'sclr_ch3']), LivePlot('sclr_ch3', 'eta')] RE(rel_scan([sclr], theta, -.1, .1, 3))