In this notebook you will:
Recommend Prerequisites:
#pip install -U --pre databroker[all]
Hosted by Josh
Run the next cells to make the ipython kernel match the previous session
%run -i gm_user/user_profile.py
%run -i gm_user/user_startup.py
%run -i gm_user/user_startup_afternoon.py
Is the RE persistent metadata correct for the next scan which should be analyzed?
RE.md
md_info()
RE.md["purpose"]
Well, we don't want all the scans to be labeled with the "purpose"
of "setup".
This is a standard python object, so how does one delete a dictionary element without starting from scratch?
There are two solutions that are not equivalent.
The code below lets you try both. Try the different options to remove the dictionary entry in seperate notebook cells to see the difference.
del RE.md["purpose"]
OR
RE.md.pop('purpose')
md_info()
### USE THIS LINE RESET in order to try the ALTERATIVE SOLUTION
RE.md["purpose"] = "setup"
We have a new device, temperature
.
temperature
.RE(mv())
:temperature
to 40motor
to 0 (the peak position)temperature.readback.get()
OR
temperature.setpoint.get()
temperature.readback.read()['motor3']['value']
Explanation:
temperature.readback
Returns:
_ReadbackSignal(name='motor3', parent='temperature', value=0, timestamp=1682079021.6718569)
You will see that the beamline staff played a python trick and that temperature
is really an alias for motor3
. The code used to to this is at the bottom of gm_user/user_profile.py.
RE(mv(temperature, 40))
OR somewhat equivalently depending on the details of a real device
RE(mv(temperature.setpoint, 40))
AND
RE(mv(motor, 0))
## Use to start item #1
temperature
Notice that temperature
is complex and using .get()
and .read()
are complicated. Below illustrates why .read()
is preferred for low level coding.
temperature.get()
temperature.read()
temperature.readback.read()["motor3"]["value"]
RE(mv(motor, 0, temperature, 40))
Hosted by Andi
Earlier in Good Morning Bluesky, we discussed the reasons to not use:
RE(mv(noisy_det.noise_multiplier, 0.1)); RE(scan([noisy_det], motor, -10, 10, 21)); RE(mv(motor, 0))
There are two basic methods to perform these steps in a single line:
#bpp.pchain?
RE(bpp.pchain( mv(noisy_det.noise_multiplier, 0.1), scan([noisy_det], motor, -5, 5, 11) , mv(motor, 0) ) )
simple to assemble
use Cntl R
terminal search to re-use
good way to test plans
hard to read/navigate, especially if many arguments are in bpp.pchain()
even harder to manage multiple unique versions of bpp.pchain()
Let's make a custom plan to perform the last RE call. We do this by making a python function.
But what did we just do? Don't scroll up.
%history -n
%history -n 6-22
From the above, we can easily create a simple plan that will take FULL advantage of bluesky
def align_peak():
yield from scan([noisy_det], motor, -5, 5, 11)
yield from mv(motor, 0)
RE(align_peak())
The peak will move as a function of temperature. In the next cells, we will:
Summarize the plan
It's easy. Just replace RE
with summarize_plan
.
summarize_plan(align_peak())
summarize_plan
is more for syntax checking and object verification. It does not check if the plan is actually feasilble.
Check the device software limits
To do this, replace summarize_plan
with check_limits
check_limits(align_peak())
No errors and a returned prompt are a great sign!
Bluesky has a built in feature called bec.peaks
(best effort callbacks).
Try bec.peaks
below and see what it is. What python data structure does this resemble?
bec.peaks
Try to extract motor
position at the scan's maximum value for noisy_det
:
print(bec.peaks["max"]["noisy_det"][0])
RE(mv(motor, bec.peaks["max"]["noisy_det"][0]) )
For more details on Peak Stats (i.e., bec.peaks
) or the alternative LiveFit.
Let's finish our function
def align_peak():
yield from scan([noisy_det], motor, -5, 5, 11)
yield from bps.sleep(3) #sometimes need this on actual hardware
my_max = bec.peaks["max"]["noisy_det"][0]
yield from mv(motor, my_max)
summarize_plan(align_peak())
Not all beamline specific plans are compatible with the bluesky.simulation module.
Others have more automated alignment plans and those should be used as per the staff instructions.
Our experiment's design is to:
RE(count([noisy_det], num = 1))
def one_temperature():
yield from align_peak()
yield from count([noisy_det], 5)
Let's make sure this is correct by using summarize_plan
.
Note: We don't expect it to work. Can you figure out what is wrong?
If the traceback isn't all that useful. Which functions align_peak()
or count()
sets a python variable?
align_peak??
Type bec.peaks
after RE(count([noisy_det], 5))
RE(count([noisy_det], 5))
bec.peaks
summarize_plan(one_temperature())
summarize_plan
¶Not all plans are appropriate for bec.peaks
. count()
is one of them.
Any time the RE
is used and a "scan" is called, bec.peaks
resets to wait for population at the end of the "scan".
Here, we could get around this problem by just doing this:
RE(scan([noisy_det], motor, -.5, .5, 3) )
summarize_plan(one_temperature())
summarize_plan
¶Performing a scan with a different detector before using summarize_plan()
RE(scan([det], motor, -.5, .5, 3) )
print(bec.peaks)
summarize_plan(one_temperature())
bec.peaks
¶def align_peak():
uid = yield from scan([noisy_det], motor, -5, 5, 11)
if uid is not None:
#print('doing')
yield from bps.sleep(3) #sometimes need this on actual hardware
my_max = bec.peaks["max"]["noisy_det"][0]
yield from mv(motor, my_max)
#else:
#print('passing')
#pass
RE(scan([det], motor, -.5, .5, 3) )
print(bec.peaks)
summarize_plan(one_temperature())
Hosted by Josh
Let's iterate over some temperatures.
for myT in [43, 45, 47, 50, 51, 52, 53, 54]:
print(myT)
- range()
- "list comprehension"
Try to modify the function below.
This represents my ideal solution. There are variations of this that are also acceptable.
Copy and Paste the below in one cell to see the difference
def my_experiment(myT_list):
for myT in myT_list:
print(f'Changing temperature to {myT}')
yield from mv(temperature, myT)
yield from one_temperature()
print(f'\tFinished scans for temperature {myT}')
summarize_plan(my_experiment([31,]))
check_limits(my_experiment([31,]))
## FIX THE FUNCTION
def my_experiment():
yield from mv(temperature, ?)
yield from one_temperature()
summarize_plan(my_experiment())
check_limits(my_experiment())