This repository has been archived by the owner on Dec 16, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
1,040 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
|
||
""" | ||
local, custom Bluesky plans (scans) and other functions | ||
""" | ||
|
||
from .alignment import * | ||
from .flux_calculations import * | ||
from .lineup_tweak import * | ||
from .move_diodes import * | ||
from .move_sample import * | ||
from .pv_registers import * | ||
from .shutters import * | ||
from .xpcs_acquire import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
|
||
""" | ||
Command-line functions for alignment - NOT bluesky plans | ||
""" | ||
|
||
__all__ = """ | ||
pre_align | ||
post_align | ||
""".strip() | ||
|
||
from instrument.session_logs import logger | ||
logger.info(__file__) | ||
|
||
from ..devices import actuator_flux, att, default_counter, pind4 | ||
from ..devices import shutter, shutter_mode | ||
|
||
def pre_align(): | ||
""" | ||
This is not a plan and so we should use it in command line, which means no use of RE | ||
""" | ||
global att, default_counter | ||
shutter.close() | ||
shutter_mode.put("1UFXC") | ||
actuator_flux.put("IN") | ||
att.put(0) | ||
default_counter = pind4 | ||
|
||
def post_align(): | ||
""" | ||
This is not a plan and so we should use it in command line, which means no use of RE | ||
""" | ||
global att | ||
shutter.close() | ||
#shutter_mode.put("1UFXC") | ||
actuator_flux.put("OUT") | ||
att.put(0) #att will be defined to att1 or att2 |
106 changes: 106 additions & 0 deletions
106
profile_bluesky/startup/instrument/plans/flux_calculations.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
|
||
""" | ||
Bluesky plans to calculate beam flux | ||
""" | ||
|
||
__all__ = """ | ||
calc_flux | ||
flux | ||
flux_params | ||
print_flux_params | ||
""".strip() | ||
|
||
from instrument.session_logs import logger | ||
logger.info(__file__) | ||
|
||
from ..devices import monochromator, preamps | ||
from .functions import taylor_series | ||
import math | ||
import pyRestTable | ||
|
||
|
||
def calc_flux(cps, params, pin_diode): | ||
""" | ||
calculate the number of photons/s from diode count rate | ||
""" | ||
gain = preamps.gains[pin_diode.name] | ||
amps = (cps/params["CtpV"])*gain | ||
photons = amps/(1.60218e-19*params["N_elec"]*params["Abs_frac"]) | ||
return photons | ||
|
||
|
||
def flux(pin_diode, count_rate): | ||
""" | ||
print the flux on the named photodiode | ||
""" | ||
gain = preamps.gains[pin_diode.name] | ||
params = flux_params(pin_diode) | ||
print_flux_params(params, pin_diode) | ||
|
||
t = pyRestTable.Table() | ||
t.addLabel("term") | ||
t.addLabel("value") | ||
t.addRow(("diode", pin_diode.name)) | ||
t.addRow(("count rate, cps", count_rate)) | ||
t.addRow(("photo current, A", count_rate/params["CtpV"]*gain)) | ||
v = calc_flux(count_rate, params, pin_diode) | ||
t.addRow(("flux, ph/s", f"{v:8.3g}")) | ||
|
||
logger.info(f"\n{t}") | ||
|
||
return v | ||
|
||
|
||
def flux_params(_counter): | ||
""" | ||
dictionary of computed conversion constants | ||
""" | ||
result = {} | ||
result["Amps_per_Volt"] = preamps.gains[_counter.name] # amplifier gain | ||
result["CtpV"] = 1e5 # voltage/frequency conversion | ||
Length = result["fluxLength"] = 0.04 # | ||
result["Element"] = "Si" | ||
keV = monochromator.energy.position | ||
result["Ephot"] = keV * 1000 | ||
|
||
if result["Element"] == "Ar": | ||
N_elec = result["Ephot"]/26.4 | ||
v = taylor_series(keV, [-2.78262, .782515, -.0379763, 1.04293e-3, -1.14407e-5]) | ||
Elength = math.exp(v) | ||
elif result["Element"] == "N2": | ||
N_elec = result["Ephot"]/34.8 | ||
v = taylor_series(keV, [6.17639, -6.90647e-1, 2.44039e-2, -.453977e-3, 0.033084e-4]) | ||
Elength = 1.0/(math.exp(v)*1.165e-3) | ||
elif result["Element"] == "He": | ||
N_elec = result["Ephot"]/41.3 | ||
v = taylor_series(keV, [15.4707, 0.972059, -0.0487191, 0.00134312, -1.46011e-05]) | ||
Elength = math.exp(v) / 1e4 | ||
elif result["Element"] == "Si": | ||
N_elec = result["Ephot"]/3.62 # 3.62: electron-hole pair production energy | ||
Elength = 61e-4*pow((7.65/keV),-3) | ||
Length = 400e-4 | ||
else: | ||
raise KeyError(f"flux params not defined for '{result['Element']}'") | ||
|
||
result["N_elec"] = N_elec | ||
result["Elength"] = Elength | ||
result["Abs_frac"] = (1-math.exp(-Length/Elength)) | ||
|
||
return result | ||
|
||
|
||
def print_flux_params(params, counter): | ||
gain = preamps.gains[counter.name] | ||
element = params['Element'] | ||
t = pyRestTable.Table() | ||
t.addLabel("term") | ||
t.addLabel("value") | ||
t.addLabel("units") | ||
t.addRow(("detector", counter.name, "")) | ||
t.addRow(("Amps/Volt", gain, "A/V")) | ||
t.addRow(("counts/Volt", params["CtpV"], "")) | ||
t.addRow(("Length", params["fluxLength"], "cm")) | ||
t.addRow(("Element", element, "")) | ||
t.addRow(("Ephot", params["Ephot"], "eV")) | ||
t.addRow((f"{element} detector", params["Elength"], "cm")) | ||
logger.info(f"\n{t}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
|
||
""" | ||
various functions | ||
""" | ||
|
||
__all__ = ["taylor_series", ] | ||
|
||
from instrument.session_logs import logger | ||
logger.info(__file__) | ||
|
||
|
||
def taylor_series(x, coefficients): | ||
""" | ||
compute a Taylor series expansion at x | ||
a0 + x*(a1 + x*(a2+x*0) | ||
""" | ||
v = 0 | ||
for a in reversed(coefficients): | ||
v = v*x + a | ||
return v |
145 changes: 145 additions & 0 deletions
145
profile_bluesky/startup/instrument/plans/lineup_tweak.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
|
||
""" | ||
Bluesky plans to move the sample stage and actuators | ||
""" | ||
|
||
__all__ = """ | ||
lineup | ||
tw | ||
""".strip() | ||
|
||
from instrument.session_logs import logger | ||
logger.info(__file__) | ||
|
||
from bluesky import plans as bp | ||
from bluesky import plan_stubs as bps | ||
from ..framework import bec | ||
from ophyd.scaler import ScalerCH, ScalerChannel | ||
import pyRestTable | ||
|
||
|
||
def tw(counter, motor, delta): | ||
""" | ||
maximize a positioner using a motor and reading a scaler channel | ||
In SPEC, the `tw` command initiates an interactive session. | ||
Automate that here, if possible. | ||
""" | ||
# Usage: tw mot [mot2 ...] delta [delta2 ...] [count_time] | ||
raise NotImplementedError("Need to write the Bluesky tw() plan") | ||
|
||
|
||
def lineup(counter, axis, minus, plus, npts, time_s=0.1, peak_factor=4, width_factor=0.8,_md={}): | ||
""" | ||
lineup and center a given axis, relative to current position | ||
PARAMETERS | ||
counter : Signal or scaler channel object | ||
detector or Signal to be maximized | ||
axis : movable | ||
Signal or EpicsMotor to use for alignment, the independent axis | ||
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 | ||
peak_factor : float (default: 4) | ||
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)) | ||
""" | ||
# 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(_md) | ||
md["purpose"] = "alignment" | ||
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 |
Oops, something went wrong.