def test_live_fit_multidim(fresh_RE): RE = fresh_RE try: import lmfit except ImportError: raise pytest.skip('requires lmfit') motor1._fake_sleep = 0 motor2._fake_sleep = 0 det4.exposure_time = 0 def gaussian(x, y, A, sigma, x0, y0): return A * np.exp(-((x - x0)**2 + (y - y0)**2) / (2 * sigma**2)) model = lmfit.Model(gaussian, ['x', 'y']) init_guess = { 'A': 2, 'sigma': lmfit.Parameter('sigma', 3, min=0), 'x0': -0.2, 'y0': 0.3 } cb = LiveFit(model, 'det4', { 'x': 'motor1', 'y': 'motor2' }, init_guess, update_every=50) RE(outer_product_scan([det4], motor1, -1, 1, 10, motor2, -1, 1, 10, False), cb) expected = {'A': 1, 'sigma': 1, 'x0': 0, 'y0': 0} for k, v in expected.items(): assert np.allclose(cb.result.values[k], v, atol=1e-6)
def test_live_fit_plot(fresh_RE): RE = fresh_RE try: import lmfit except ImportError: raise pytest.skip('requires lmfit') def gaussian(x, A, sigma, x0): return A * np.exp(-(x - x0)**2 / (2 * sigma**2)) model = lmfit.Model(gaussian) init_guess = { 'A': 2, 'sigma': lmfit.Parameter('sigma', 3, min=0), 'x0': -0.2 } livefit = LiveFit(model, 'det', {'x': 'motor'}, init_guess, update_every=50) lfplot = LiveFitPlot(livefit, color='r') lplot = LivePlot('det', 'motor', ax=plt.gca(), marker='o', ls='none') RE(scan([det], motor, -1, 1, 50), [lplot, lfplot]) expected = {'A': 1, 'sigma': 1, 'x0': 0} for k, v in expected.items(): assert np.allclose(livefit.result.values[k], v, atol=1e-6)
def test_live_fit(RE, hw): try: import lmfit except ImportError: raise pytest.skip('requires lmfit') def gaussian(x, A, sigma, x0): return A * np.exp(-(x - x0) ** 2 / (2 * sigma ** 2)) model = lmfit.Model(gaussian) init_guess = {'A': 2, 'sigma': lmfit.Parameter('sigma', 3, min=0), 'x0': -0.2} cb = LiveFit(model, 'det', {'x': 'motor'}, init_guess, update_every=50) RE(scan([hw.det], hw.motor, -1, 1, 50), cb) # results are in cb.result.values expected = {'A': 1, 'sigma': 1, 'x0': 0} for k, v in expected.items(): assert np.allclose(cb.result.values[k], v, atol=1e-6)
import numpy as np import lmfit from bluesky.plans import scan from ophyd.sim import motor, noisy_det from bluesky.callbacks import LiveFit from bluesky.callbacks.mpl_plotting import LivePlot, LiveFitPlot from bluesky import RunEngine RE = RunEngine({}) def gaussian(x, A, sigma, x0): return A * np.exp(-(x - x0)**2 / (2 * sigma**2)) model = lmfit.Model(gaussian) init_guess = {'A': 2, 'sigma': lmfit.Parameter('sigma', 3, min=0), 'x0': -0.2} import matplotlib.pyplot as plt fig, ax = plt.subplots() lf = LiveFit(model, 'noisy_det', {'x': 'motor'}, init_guess) lfp = LiveFitPlot(lf, ax=ax, color='r') lp = LivePlot('noisy_det', 'motor', ax=ax, marker='o', linestyle='none') RE(scan([noisy_det], motor, -1, 1, 50), [lfp, lp]) plt.draw()
def xScanAndCenter(iteration, detector, det_name, motor, motor_name, alignRange = [-1, 2], stepRange = [0.01, 0.10], targetDelta = def_xTargetDelta, plotter = [], SimAlign = False, fudge = def_fudge, verbose = False): """ align_x: -scan xMotor through range -monitors detector looking for transition of beam/no-beam Parameters ---------- iteration : overall iteration of full alignment loop, required for plot colors/legend detector : detector object detector_name : detector object's name motor : X motor object motor_name : X motor object's name alignRange : list of two floats, describing range of x-alignment Default value is [-1,2] mm, StepRange : list of two floats, minimum and maximum steps for adaptive scan. Default value is [0.015, 0.075] degrees Minimum thMotor step is 0.01 degrees targetDelta : float, maximum jump in detector value before scan step decreased, default is set to def_xTargetDelta value is 10 in detector units, plotter : axis object for x alignment data, Default value is None which later creates new object SimAlign : boolean flag for simulation/testing Default value is False, fudge : Fudge factor for x-scan data reduction to remove bump from post-scan fit (in mm). Defaults to def_fudge verbose : boolean for showing alignment process using LiveTable callbacks. Default value is False """ if plotter is None: figX, xAx = plt.subplots() else: xAx = plotter cur_color = fit_colors[iteration % len(fit_colors)] cur_linestyle = linestyles[iteration // len(fit_colors)] scanPlot = LivePlot(det_name,x=motor_name, markeredgecolor = cur_color, markerfacecolor = 'none', ax = xAx, linestyle = 'none', marker = '^', label = '{} - data'.format(iteration)) comp_model = lmfit.Model(erfx, prefix="erf_") + lmfit.Model(gaussian, prefix = "gau_") comp_guess = {'erf_low': 0, 'erf_high': 0.03, 'erf_wid': lmfit.Parameter('erf_wid', value = 0.4, min=0), 'erf_x0': -0.1, 'gau_A': 0.01, 'gau_sigma': lmfit.Parameter('gau_sigma', value = .1, min=0), 'gau_x0': 1.0} xLiveFit = LiveFit(comp_model, det_name, {'x': motor_name}, comp_guess, update_every=5) lt = LiveTable([detector, motor]) if verbose: cbs = [scanPlot, xLiveFit, lt] else: cbs = [scanPlot, xLiveFit] @subs_decorator(cbs) def preFitScan(detectors, motor, alignRange, stepRange, targetDelta): yield from adaptive_scan([detectors], det_name, motor, start = alignRange[0], stop = alignRange[1], min_step = stepRange[0], max_step = stepRange[1], target_delta = targetDelta, backstep = True) yield from preFitScan(detector, motor, alignRange, stepRange, targetDelta) x0_rough = xLiveFit.result.params['erf_x0'].value width = xLiveFit.result.params['erf_wid'].value new_x0 = fitAndPlotBumplessData(np.asarray(scanPlot.y_data), np.asarray(scanPlot.x_data), x0_rough, width, ax = xAx, color = cur_color, linestyle = cur_linestyle, fudge = fudge) other_data = [] leg_raw = {'label' : 'Data', 'shape' : '^', 'color' : 'k', 'size' : '10'} other_data = [leg_raw] cleanLegend(iteration, xAx, other_data) asd.x0.append(new_x0) if not SimAlign: if (asd.x0[-1] <= min(alignRange) or asd.x0[-1] >= max(alignRange)): print("Peak estimated to be outside of alignment range") else: motor.move(asd.x0[-1])
@subs_decorator(coarse_cbs) def coarseScan(detector, motor, cRange, pts = 10): #Coarse scan to find region of peak yield from scan([detector], motor, cRange[0], cRange[1], num = pts) yield from coarseScan(detector, motor, alignRange, pts = coarsePTS) finePlot = LivePlot(detector_name,x=motor_name, linestyle = 'none', marker = 'o', markerfacecolor = 'none', markeredgecolor = cur_color, ax = thAx, label = '{} - fine'.format(iteration)) fineThetaModel = lmfit.Model(gaussian) fineThetaInitGuess = {'A': coarsePeak.max[1], 'sigma': lmfit.Parameter('sigma', .03, min=0), 'x0': coarsePeak.max[0]} fineThetaLiveFit = LiveFit(fineThetaModel, detector_name, {'x': motor_name}, fineThetaInitGuess, update_every=1) fineThetaLiveFitPlot = LiveFitPlot(fineThetaLiveFit, ax = thAx, label='fit', color = fit_colors[iteration % 7], linestyle = linestyles[iteration // 7]) fRange = [coarsePeak.max[0]-fineRadius, coarsePeak.max[0]+fineRadius] thTargetDelta = coarsePeak.max[1]/targetDeltaFactor if not SimAlign: if coarsePeak.max[0] > alignRange[1]-1: motor.move(4.00) if verbose: fine_cbs = [finePlot, fineThetaLiveFitPlot, lt] else:
def Ecal_old(detectors, motor, guessed_energy, mode, *, guessed_amplitude=0.5, guessed_sigma=0.004, min_step=0.001, D='LaB6', max_n=3, margin=0.5, md=None): """ Energy calibration scan Parameters ---------- detectors : detectors motor : the motor guessed_energy : number units of keV mode : {'peaks', 'dips'} guessed_amplitude : number, optional detector units, defaults to 1500 guessed_sigma : number, optional in units of degrees, defaults to 0.004 min_step : number, optional step size in degrees, defaults to 0.001 D : string or array, optional Either provide the spacings literally (as an array) or give the name of a known spacing ('LaB6' or 'Si') max_n : integer, optional how many peaks (on one side of 0), default is 3 margin : number, optional how far to scan in two theta beyond the guessed left and right peaks, default 0.5 Example ------- Execute an energy calibration scan with default steps. >>> RE(Ecal(68)) """ if mode == 'peaks': #motor = tth_cal factor = 2 offset = 0 sign = 1 if mode == 'dips': #motor = th_cal factor = 1 # theta_hardware = theta_theorhetical + offset offset = -35.26 # degrees sign = -1 if isinstance(D, str): D = D_SPACINGS[D] # Based on the guessed energy, compute where the peaks should be centered # in theta. This will be used as an initial guess for peak-fitting. guessed_wavelength = 12.398 / guessed_energy # angtroms theta = np.rad2deg(np.arcsin(guessed_wavelength / (2 * D))) guessed_centers = factor * theta # 'factor' puts us in two-theta units if applicable _range = max(guessed_centers) + (factor * margin) # Scan from positive to negative because that is the direction # that the th_cal and tth_cal motors move without backlash. start, stop = _range + offset, -_range + offset print('guessed_wavelength={} [Angstroms]'.format(guessed_wavelength)) print('guessed_centers={} [in {}-theta DEGREES]'.format( guessed_centers, factor)) print('will scan from {} to {} in hardware units'.format(start, stop)) if max_n > 3: raise NotImplementedError("I only work for n up to 3.") # todo : make a model and sum them def peaks(x, c0, wavelength, a1, a2, sigma): # x comes from hardware in [theta or two-theta] degrees x = np.deg2rad(x / factor - offset) # radians assert np.all(wavelength < 2 * D), \ "wavelength would result in illegal arg to arcsin" cs = np.arcsin(wavelength / (2 * D)) c1 = cs[0] c2 = cs[1] #c3 = cs[2] # first peak result = ( voigt(x=x, amplitude=sign * a1, center=c0 - c1, sigma=sigma) + voigt(x=x, amplitude=sign * a1, center=c0 + c1, sigma=sigma)) # second peak result += ( voigt(x=x, amplitude=sign * a2, center=c0 - c2, sigma=sigma) + voigt(x=x, amplitude=sign * a2, center=c0 + c2, sigma=sigma)) # third peak #result += (voigt(x=x, amplitude=sign*a3, center=c0 - c3, sigma=sigma) + #voigt(x=x, amplitude=sign*a3, center=c0 + c3, sigma=sigma)) return result model = Model(peaks) + LinearModel() # Fill out initial guess. init_guess = { 'intercept': Parameter('intercept', value=0, min=-100, max=100), 'slope': Parameter('slope', value=0, min=-100, max=100), 'sigma': Parameter('sigma', value=np.deg2rad(guessed_sigma)), 'c0': Parameter('c0', value=np.deg2rad(0), min=-0.2, max=0.2), 'wavelength': Parameter('wavelength', guessed_wavelength, min=0.8 * guessed_wavelength, max=1.2 * guessed_wavelength) } # min=0, max=np.min(2 * D))} kwargs = {'min': 0.5 * guessed_amplitude, 'max': 2 * guessed_amplitude} for i, center in enumerate(guessed_centers): init_guess.update({ 'a%d' % (1 + i): Parameter('a%d' % (1 + i), guessed_amplitude, **kwargs) }) print(init_guess) lf = LiveFit(model, detectors[0].name, {'x': motor.name}, init_guess, update_every=100) fig, ax = plt.subplots() # explitly create figure, axes to use below plot = LivePlot(detectors[0].name, motor.name, linestyle='none', marker='o', ax=ax) lfp = LiveFitPlot(lf, ax=ax, color='r') subs = [lfp, plot] #detectors = [det]#[sc] # Set up metadata -- based on the sourcecode of bluesky.plans.scan. _md = { 'detectors': [det.name for det in detectors], 'motors': [motor.name], 'hints': {}, } _md.update(md or {}) try: dimensions = [(motor.hints['fields'], 'primary')] except (AttributeError, KeyError): pass else: _md['hints'].setdefault('dimensions', dimensions) initial_steps = np.arange(start, stop, -min_step) assert len(initial_steps), "bad start, stop, min_step parameters" size = factor * 0.05 # region around each predicted peak location @bpp.stage_decorator(list(detectors) + [motor]) @bpp.run_decorator(md=_md) def inner_scan(): wavelength = guessed_wavelength for step in initial_steps: yield from bps.one_1d_step(detectors, motor, step) x_data = lf.independent_vars_data['x'] if x_data and lf.result is not None: # Have we yet scanned past the third peak? wavelength = lf.result.values['wavelength'] # Convert c's to hardware units here for comparison with x_data. c1, c2, c3 = factor * np.rad2deg( np.arcsin(wavelength / (2 * D))) + offset if np.min(x_data) < (c1 - 1): # Stop dense scanning. print('Preliminary result:\n', lf.result.values) print('Becoming adaptive to save time....') break # left of zero peaks c1, c2, c3 = -factor * np.rad2deg(np.arcsin(wavelength / (2 * D))) + offset neighborhoods = [ np.arange(c + size, c - size, -min_step) for c in (c1, c2, c3) ] for neighborhood in neighborhoods: for step in neighborhood: yield from bps.one_1d_step(detectors, motor, step) plan = inner_scan() plan = bpp.subs_wrapper(plan, subs) plan = bpp.reset_positions_wrapper(plan, [motor]) ret = (yield from plan) print(lf.result.values) print('WAVELENGTH: {} [Angstroms]'.format(lf.result.values['wavelength'])) return ret