changed criteria for judging whether a fit is correct or not
This commit is contained in:
1 parent
5fbb696eb2
commit
016e9b1394
3 files changed
+244
-68
No files matched your search
@@ -151,7 +151,7 @@ def fit(data, fit_params):
|
||||
}
|
||||
|
||||
|
||||
def check_peak_fit(x_data, result):
|
||||
def check_peak_fit(x_data, result, min_fwhm_steps=3):
|
||||
"""Check whether a fitted peak is reliable
|
||||
Parameters: X data, results from the fit step
|
||||
Returns: success or problems"""
|
||||
@@ -166,13 +166,19 @@ def check_peak_fit(x_data, result):
|
||||
centre = result["centre"]
|
||||
fwhm = result["fwhm"]
|
||||
|
||||
step_size = np.mean(np.diff(x_data))
|
||||
fwhm_steps = result["fwhm"] / abs(step_size)
|
||||
|
||||
problems = []
|
||||
|
||||
if not x_min + 0.1 * scan_range < centre < x_max - 0.1 * scan_range:
|
||||
problems.append("peak centre is too close to scan edge")
|
||||
|
||||
if fwhm < 3 * step:
|
||||
problems.append("peak is narrower than 3 scan steps")
|
||||
if fwhm < min_fwhm_steps * step:
|
||||
problems.append(
|
||||
f"Peak FWHM spans only {fwhm_steps:.2f} scan steps "
|
||||
f"(minimum {min_fwhm_steps})"
|
||||
)
|
||||
|
||||
if fwhm > 0.7 * scan_range:
|
||||
problems.append("peak is too broad for scan range")
|
||||
|
||||
@@ -76,7 +76,7 @@ def plot_fitted_data(data, fit_result, window_name="Fitting", plot_widget=None,
|
||||
f"FWHM = {fit_result['fwhm']:.5f}\n"
|
||||
f"Height = {fit_result['height']:.4g}\n"
|
||||
f"Model = {fit_result['model']}\n"
|
||||
f"Chi sq = {fit_result['chi_sq']:.3g}\n"
|
||||
f"Centre_StdErr = {fit_result['centre_stderr']:.3g}\n"
|
||||
f"SNR = {fit_result['snr']:.3g}"
|
||||
)
|
||||
|
||||
|
||||
+234
-64
@@ -117,87 +117,161 @@ def go_to_peak(
|
||||
gomax: bool = False,
|
||||
negative: bool = False,
|
||||
gap: bool = False,
|
||||
min_fwhm_steps: float = 3,
|
||||
window_name: str = "Fitting",
|
||||
plot_widget=None,
|
||||
):
|
||||
"""
|
||||
Go to the peak of a signal by scanning a motor within a specified range and
|
||||
identifying the optimal position based on signal peak data.
|
||||
Scan a motor and move either to the fitted peak centre or the
|
||||
highest measured data point.
|
||||
|
||||
Parameters:
|
||||
motor_device: The motor device to be scanned.
|
||||
signal_device: The signal device to monitor during the scan.
|
||||
start (float): The starting position of the scan. Ignored if `relative` is True.
|
||||
stop (float): The ending position of the scan. Ignored if `relative` is True.
|
||||
steps (int): The number of steps to divide the scan range into.
|
||||
relative (bool, optional): If True, interpret `start` and `stop` as relative to
|
||||
the current motor position. Defaults to RELATIVE_MODE constant.
|
||||
plot (bool, optional): If True, plot the scan data and the fitted results.
|
||||
Defaults to True.
|
||||
settle (float, optional): The time in seconds to wait after each step for the
|
||||
signal to stabilize. Defaults to DEFAULT_SETTLE_TIME constant.
|
||||
confirm (bool, optional): If True, ask for user confirmation before starting
|
||||
the scan. Defaults to True.
|
||||
If gomax=False:
|
||||
Fit the scan, validate the fit using check_peak_fit(),
|
||||
and move to the fitted centre.
|
||||
|
||||
Raises:
|
||||
Exception: Raises exceptions potentially raised by dependent functions or
|
||||
operations such as plotting, fitting, or motor movement.
|
||||
If gomax=True:
|
||||
Skip peak-fit validation and move directly to the highest
|
||||
measured signal value. Information about whether the maximum
|
||||
occurred at a scan boundary is returned.
|
||||
|
||||
Returns:
|
||||
fit parameters
|
||||
Parameters
|
||||
----------
|
||||
motor_device
|
||||
Motor device to scan.
|
||||
signal_device
|
||||
Signal device to monitor.
|
||||
start : float
|
||||
Scan start position.
|
||||
stop : float
|
||||
Scan end position.
|
||||
steps : int
|
||||
Number of scan steps.
|
||||
relative : bool
|
||||
If True, start and stop are relative to current motor position.
|
||||
plot : bool
|
||||
Plot scan data/results.
|
||||
settle : float
|
||||
Settling time at each scan point.
|
||||
confirm : bool
|
||||
Ask for confirmation before starting scan.
|
||||
gomax : bool
|
||||
If True, move to highest measured point rather than fitted centre.
|
||||
negative : bool
|
||||
Fit a negative-going peak.
|
||||
gap : bool
|
||||
Apply minimum-gap protection when using fitted peak position.
|
||||
min_fwhm_steps : float
|
||||
Minimum allowed fitted FWHM in scan steps. Only used when
|
||||
gomax=False.
|
||||
window_name : str
|
||||
BEC GUI window name.
|
||||
plot_widget
|
||||
Optional explicit plot widget.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
Result information.
|
||||
|
||||
For a successful fitted peak:
|
||||
success
|
||||
problems
|
||||
centre
|
||||
fwhm
|
||||
height
|
||||
chi_sq
|
||||
centre_stderr
|
||||
snr
|
||||
|
||||
For gomax=True:
|
||||
success
|
||||
problems
|
||||
x_max
|
||||
max_index
|
||||
at_lower_boundary
|
||||
at_upper_boundary
|
||||
|
||||
For a rejected fit:
|
||||
success=False
|
||||
problems=list of fit rejection reasons
|
||||
"""
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Get motor and signal names
|
||||
# ------------------------------------------------------------
|
||||
|
||||
mres = get_value(motor_device, True)
|
||||
motor_name = mres["root"]
|
||||
|
||||
sres = get_value(signal_device, True)
|
||||
signal_name = sres["root"]
|
||||
|
||||
print(f"Signal name is {signal_name}")
|
||||
|
||||
# if plot:
|
||||
# if plot_widget is not None:
|
||||
# plot_live_data(plot_widget, motor_name, signal_name, title=f"{motor_name} scan")
|
||||
# else:
|
||||
# plot_live_data_bec(motor_name, signal_name, window_name=window_name)
|
||||
# ------------------------------------------------------------
|
||||
# Set up live plotting
|
||||
# ------------------------------------------------------------
|
||||
|
||||
if plot:
|
||||
plot_live_data(
|
||||
motor_name,
|
||||
motor_name,
|
||||
signal_name,
|
||||
window_name=window_name,
|
||||
plot_widget=plot_widget,
|
||||
title=f"{motor_name} scan")
|
||||
plot_widget=plot_widget,
|
||||
title=f"{motor_name} scan",
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Validate scan range and get starting position
|
||||
# ------------------------------------------------------------
|
||||
|
||||
# Validate and calculate step size
|
||||
step_size = calculate_step_size(start, stop, steps)
|
||||
|
||||
# Confirm the scan range
|
||||
starting_position = mres["value"]
|
||||
|
||||
print(f"Starting motor position = {starting_position}")
|
||||
|
||||
if confirm:
|
||||
if relative:
|
||||
scan_start = starting_position + start
|
||||
scan_end = starting_position + stop
|
||||
|
||||
print(
|
||||
f"\nScanning from {scan_start: .6g} to {scan_end: .6g} in "
|
||||
f"{steps} steps of size {step_size}"
|
||||
f"\nScanning from {scan_start:.6g} to {scan_end:.6g} "
|
||||
f"in {steps} steps of size {step_size}"
|
||||
)
|
||||
print(f"Relative mode = {relative}")
|
||||
|
||||
else:
|
||||
print(
|
||||
f"\nScanning from {start: .5g} to {stop: .5g} in {steps} steps of size {step_size}"
|
||||
f"\nScanning from {start:.5g} to {stop:.5g} "
|
||||
f"in {steps} steps of size {step_size}"
|
||||
)
|
||||
print(f"Relative mode = {relative}")
|
||||
|
||||
input("Press Enter to continue...")
|
||||
|
||||
# Perform the scan
|
||||
# ------------------------------------------------------------
|
||||
# Perform scan
|
||||
# ------------------------------------------------------------
|
||||
|
||||
scan_result = scans.line_scan(
|
||||
motor_device, start, stop, steps=steps, relative=relative, settling_time=settle
|
||||
motor_device,
|
||||
start,
|
||||
stop,
|
||||
steps=steps,
|
||||
relative=relative,
|
||||
settling_time=settle,
|
||||
)
|
||||
motor_data = scan_result.scan.live_data[mres["live_root"]][mres["name"]].val
|
||||
# # motor_data = scan_result.scan.live_data[motor_name][motor_name].val
|
||||
# sres = get_value(signal_name, True)
|
||||
signal_data = scan_result.scan.live_data[sres["root"]][sres["name"]].val
|
||||
|
||||
motor_data = (
|
||||
scan_result.scan.live_data[mres["live_root"]][mres["name"]].val
|
||||
)
|
||||
|
||||
signal_data = (
|
||||
scan_result.scan.live_data[sres["root"]][sres["name"]].val
|
||||
)
|
||||
|
||||
scan_number = "Current"
|
||||
|
||||
data = {
|
||||
@@ -209,16 +283,87 @@ def go_to_peak(
|
||||
"scan_number": scan_number,
|
||||
}
|
||||
|
||||
# Define and fit model to scan data
|
||||
# ============================================================
|
||||
# GOMAX MODE
|
||||
# ============================================================
|
||||
#
|
||||
# Do not fit or apply peak-shape validation.
|
||||
# Simply use the highest measured point.
|
||||
#
|
||||
|
||||
if gomax:
|
||||
|
||||
if negative:
|
||||
max_index = int(np.argmin(data["y_data"]))
|
||||
else:
|
||||
max_index = int(np.argmax(data["y_data"]))
|
||||
|
||||
x_max = float(data["x_data"][max_index])
|
||||
|
||||
at_lower_boundary = max_index == 0
|
||||
at_upper_boundary = max_index == len(data["x_data"]) - 1
|
||||
|
||||
print(f"Maximum measured signal at {x_max:.6g}")
|
||||
|
||||
if at_lower_boundary:
|
||||
print("Maximum is at the lower scan boundary")
|
||||
|
||||
if at_upper_boundary:
|
||||
print("Maximum is at the upper scan boundary")
|
||||
|
||||
# Plot raw scan rather than trying to assess a fitted peak
|
||||
if plot:
|
||||
plot_scan_data(
|
||||
data,
|
||||
window_name=window_name,
|
||||
plot_widget=plot_widget,
|
||||
title=f"{motor_name} scan - maximum at {x_max:.6g}",
|
||||
)
|
||||
|
||||
# Move to measured maximum
|
||||
move_to_scan_position(
|
||||
data["motor_device"],
|
||||
data["motor_name"],
|
||||
x_max,
|
||||
data,
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"problems": [],
|
||||
"x_max": x_max,
|
||||
"max_index": max_index,
|
||||
"at_lower_boundary": at_lower_boundary,
|
||||
"at_upper_boundary": at_upper_boundary,
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# FITTED PEAK MODE
|
||||
# ============================================================
|
||||
|
||||
fit_params = create_fit_parameters(
|
||||
deriv=False, negative=False, model=FitDefaults.MODEL, baseline=FitDefaults.BASELINE
|
||||
deriv=False,
|
||||
negative=negative,
|
||||
model=FitDefaults.MODEL,
|
||||
baseline=FitDefaults.BASELINE,
|
||||
)
|
||||
|
||||
fit_result = fit(data, fit_params)
|
||||
|
||||
check = check_peak_fit(data["x_data"], fit_result)
|
||||
# ------------------------------------------------------------
|
||||
# Validate fitted peak
|
||||
# ------------------------------------------------------------
|
||||
|
||||
check = check_peak_fit(
|
||||
data["x_data"],
|
||||
fit_result,
|
||||
min_fwhm_steps=min_fwhm_steps,
|
||||
)
|
||||
|
||||
if not check["success"]:
|
||||
|
||||
print("Peak fit rejected:")
|
||||
|
||||
for problem in check["problems"]:
|
||||
print(f" - {problem}")
|
||||
|
||||
@@ -229,42 +374,67 @@ def go_to_peak(
|
||||
plot_widget=plot_widget,
|
||||
title=f"{motor_name} scan - FIT REJECTED",
|
||||
)
|
||||
print(f"Returning motor to start position, {starting_position:.5g}")
|
||||
|
||||
print(
|
||||
f"Returning motor to start position, "
|
||||
f"{starting_position:.5g}"
|
||||
)
|
||||
|
||||
restore_position(
|
||||
motor_device,
|
||||
starting_position,
|
||||
)
|
||||
|
||||
return None
|
||||
return {
|
||||
"success": False,
|
||||
"problems": check["problems"],
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Plot accepted fit
|
||||
# ------------------------------------------------------------
|
||||
|
||||
|
||||
# Plot the fitted data if plot = True
|
||||
if plot:
|
||||
plot_fitted_data(data, fit_result, window_name=window_name, plot_widget=plot_widget)
|
||||
plot_fitted_data(
|
||||
data,
|
||||
fit_result,
|
||||
window_name=window_name,
|
||||
plot_widget=plot_widget,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Move to fitted peak centre
|
||||
# ------------------------------------------------------------
|
||||
|
||||
centre = fit_result["centre"]
|
||||
|
||||
if gap and centre < 4.5:
|
||||
print(
|
||||
f"Fitted gap position {centre:.5g} mm is below "
|
||||
"minimum gap of 4.5 mm"
|
||||
)
|
||||
umv(dev.id_gap, 4.5)
|
||||
|
||||
# If gomax is set then move to the maximum value, rather than the fit centre
|
||||
if gomax:
|
||||
value = fit_result["x_max"]
|
||||
print(f"Max position is at {value}")
|
||||
move_to_scan_position(data["motor_device"], data["motor_name"], fit_result["x_max"], data)
|
||||
else:
|
||||
if gap:
|
||||
if fit_result["centre"] < 4.5:
|
||||
umv(dev.id_gap, 4.5)
|
||||
else:
|
||||
move_to_scan_position(
|
||||
data["motor_device"], data["motor_name"], fit_result["centre"], data
|
||||
)
|
||||
else:
|
||||
# Safely move the motor to the peak position
|
||||
move_to_scan_position(data["motor_device"], data["motor_name"], fit_result["centre"], data)
|
||||
move_to_scan_position(
|
||||
data["motor_device"],
|
||||
data["motor_name"],
|
||||
centre,
|
||||
data,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Return successful fit information
|
||||
# ------------------------------------------------------------
|
||||
|
||||
return {
|
||||
"centre": fit_result["centre"],
|
||||
"success": True,
|
||||
"problems": [],
|
||||
"centre": centre,
|
||||
"fwhm": float(fit_result["fwhm"]),
|
||||
"height": float(fit_result["height"]),
|
||||
"chi_sq": float(fit_result["chi_sq"]),
|
||||
"centre_stderr": float(fit_result["centre_stderr"]),
|
||||
"snr": float(fit_result["snr"]),
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user