Skip to content

Commit

Permalink
Merge 89792bd into 5301e19
Browse files Browse the repository at this point in the history
  • Loading branch information
MDecarabas committed Apr 12, 2024
2 parents 5301e19 + 89792bd commit 3c8bab8
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 3 deletions.
81 changes: 81 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
]

# Same as Black.
line-length = 115
indent-width = 4

# Assume Python 3.8
target-version = "py38"

[lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F"]
ignore = ["E402", "E741", "F405"]

# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[lint.per-file-ignores]
"__init__.py" = ["F401", "F403"]
"**/{tests,docs,tools}/*" = ["E402"]

[format]
# Like Black, use double quotes for strings.
quote-style = "double"

# Like Black, indent with spaces, rather than tabs.
indent-style = "space"

# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false

# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"

# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false

# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"
7 changes: 4 additions & 3 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ describe future plans.
release expected by 2024-04-12

New Features
------------
------------

* Add new plan for edge alignment called edge_align
* Added a mesh grid scan plan that will collect until number of collection points is met

* Added a mesh grid scan plan that will collect until number of collection points is met

Fixes
-----

Expand Down
1 change: 1 addition & 0 deletions apstools/plans/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .alignment import TuneResults
from .alignment import lineup
from .alignment import lineup2
from .alignment import edge_align
from .alignment import tune_axes
from .command_list import CommandFileReadError
from .command_list import command_list_as_table
Expand Down
112 changes: 112 additions & 0 deletions apstools/plans/alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

import numpy as np
import pyRestTable
from scipy.optimize import curve_fit
from scipy.special import erf

from bluesky import plan_stubs as bps
from bluesky import plans as bp
from bluesky import preprocessors as bpp
Expand Down Expand Up @@ -213,6 +216,115 @@ def peak_analysis():
scaler.stage_sigs = old_sigs


def edge_align(detectors, mover, start, end, points, cat=None, md={}):
"""
Align to the edge given mover & detector data, relative to absolute position.
This plan can be used in the queueserver, Jupyter notebooks, and IPython
consoles.
PARAMETERS
----------
detectors *Readable* or [*Readable*]:
Detector object or list of detector objects (each is a Device or
Signal).
mover *Movable*:
Mover object, such as motor or other positioner.
start *float*:
Starting point for the scan. This is an absolute motor location.
end *float*:
Ending point for the scan. This is an absolute motor location.
points *int*:
Number of points in the scan.
cat *databroker.temp().v2*:
Catalog where bluesky data is saved and can be retrieved from.
md *dict*:
User-supplied metadata for this scan.
"""

def guess_erf_params(x_data, y_data):
"""
Provide an initial guess for the parameters of an error function.
Parameters
----------
x_data : A numpy array of the values on the x_axis
y_data : A numpy array of the values on the y_axis
Returns
-------
guess : dict
A dictionary containing the guessed parameters 'low_y_data', 'high_y_data', 'width', and 'midpoint'.
"""

# Sort data to make finding the mid-point easier and to assist in other estimations
y_data_sorted = np.sort(y_data)
x_data_sorted = np.sort(x_data)

# Estimate low and high as the first and last elements (assuming sorted data)
low_y_data = np.min(y_data_sorted)
high_y_data = np.max(y_data_sorted)

low_x_data = np.min(x_data_sorted)
high_x_data = np.max(x_data_sorted)

# Estimate wid as a fraction of the range. This is very arbitrary and might need tuning!
width = (
high_x_data - low_x_data
) / 10 # This is a guess and might need adjustment based on your data's characteristics

# Estimate the midpoint of the x values
midpoint = x_data[int(len(x_data) / 2)]

return [low_y_data, high_y_data, width, midpoint]

def erf_model(x, low, high, width, midpoint):
"""
Create error function for fitting and simulation
Parameters
----------
x : input upon which error function is evaluated
low : min value of error function
high : max value of error function
width : "spread" of error function transition region
midpoint: location of error function's "center"
"""
return (high - low) * 0.5 * (1 - erf((x - midpoint) / width)) + low

if not isinstance(detectors, (tuple, list)):
detectors = [detectors]

_md = dict(purpose="edge_align")
_md.update(md or {})

uid = yield from bp.scan(detectors, mover, start, end, points, md=_md)
cat = cat or utils.getCatalog()
run = cat[uid] # return uids
ds = run.primary.read()

x = ds["mover"]
y = ds["noisy"]

try:
initial_guess = guess_erf_params(x, y)
popt, pcov = curve_fit(erf_model, x, y, p0=initial_guess)
if pcov[3, 3] != np.inf:
print("Significant signal change detected; motor moving to detected edge.")
yield from bps.mv(mover, popt[3])
else:
raise Exception
except Exception as reason:
print(f"reason: {reason}")
print("No significant signal change detected; motor movement skipped.")


def lineup2(
# fmt: off
detectors, mover, rel_start, rel_end, points,
Expand Down
2 changes: 2 additions & 0 deletions docs/source/api/_plans.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Custom Scans

.. autosummary::

~apstools.plans.alignment.edge_align

~apstools.plans.alignment.lineup
~apstools.plans.alignment.lineup2
~apstools.plans.alignment.tune_axes
Expand Down
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies:
- pysumreg
- qt =5
- readline !=8.1.2
- scipy
- setuptools-scm
- spec2nexus
- sphinx >=5
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ dependencies = [
"pyepics>=3.4.2",
"pyRestTable>=2020.0.8",
"pysumreg",
"scipy",
"spec2nexus>=2021.1.7",
"toolz>=0.12.1",
"xlrd",
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ psutil
pyepics>=3.4.2
pyRestTable
pysumreg
scipy
spec2nexus>=2021.1.7
xlrd

0 comments on commit 3c8bab8

Please sign in to comment.