Code Listing#
Here is the full listing of the rocking curve scan code.
An up-to-date version can be found here at BMM’s GitHub site. This code listing is not identical to what is found in the BMM repository. The version in use at the beamline has a few options which fall outside the scope of this tutorial.
The whisper()
function at line 55 is one of the convenience
functions discussed in Colored text in bsui.
1 from bluesky.plan_stubs import mv
2 from bluesky.plans import rel_scan
3 from bluesky.preprocessors import finalize_wrapper
4
5 import numpy, pandas
6 from lmfit.models import SkewedGaussianModel
7 from scipy.ndimage import center_of_mass
8
9 def rocking_curve(start=-0.10, stop=0.10, nsteps=101, choice='peak'):
10 '''Perform a relative scan of the DCM 2nd crystal pitch around
11 the current position to find the peak of the crystal rocking
12 curve. Begin by opening the hutch slits to 3 mm. At the end,
13 move to the position of maximum intensity on I0, then return
14 to the hutch slits to their original height.
15
16 Parameters
17 ----------
18 start : (float)
19 starting position relative to current [-0.1]
20 end : (float)
21 ending position relative to current [0.1]
22 nsteps : (int)
23 number of steps [101]
24 choice : (string)
25 'peak', fit' or 'com' (center of mass) ['peak']
26
27 If choice is fit, the fit is performed using the
28 SkewedGaussianModel from lmfit, which works pretty well for
29 this measurement at BMM. The line shape is a bit skewed due
30 to the convolution with the slightly misaligned entrance
31 slits.
32 '''
33 def main_plan(start, stop, nsteps, choice):
34 func = lambda doc: (doc['data'][motor.name], doc['data']['I0'])
35 dets = [quadem1,]
36 sgnl = 'I0'
37 titl = 'I0 signal vs. DCM 2nd crystal pitch'
38 plot = DerivedPlot(func, xlabel=motor.name, ylabel=sgnl, title=titl)
39
40 @subs_decorator(plot)
41 def scan_dcmpitch(sgnl):
42 line1 = f'{motor.name}, {sgnl}, {start:.3f}, {stop:.3f}, {nsteps} -- starting at {motor.user_readback.get():.3f}\n'
43
44 uid = yield from rel_scan(dets, motor, start, stop, nsteps)
45 t = db[-1].table()
46 signal = t[sgnl]
47 if choice.lower() == 'com':
48 position = int(center_of_mass(signal)[0])
49 top = t[motor.name][position]
50 elif choice.lower() == 'fit':
51 pitch = t['dcm_pitch']
52 mod = SkewedGaussianModel()
53 pars = mod.guess(signal, x=pitch)
54 out = mod.fit(signal, pars, x=pitch)
55 print(whisper(out.fit_report(min_correl=0)))
56 out.plot()
57 top = out.params['center'].value
58 else:
59 position = pandas.Series.idxmax(signal)
60 top = t[motor.name][position]
61
62 print(f'rocking curve scan: {line1}\tuid = {uid}, scan_id = {user_ns['db'][-1].start['scan_id']}')
63 yield from mv(motor, top)
64 print(f'found optimal picth position at {motor.user_readback.get():.3f}')
65 yield from scan_dcmpitch(sgnl)
66
67 def cleanup_plan():
68 yield from mv(slits3.vsize, slit_height)
69 yield from mv(_locked_dwell_time, 0.5)
70
71 motor = dcm_pitch
72 yield from mv(_locked_dwell_time, 0.1)
73 yield from mv(slits3.vsize, 3)
74 slit_height = slits3.vsize.readback.get()
75 yield from finalize_wrapper(main_plan(start, stop, nsteps, choice), cleanup_plan())