From 34bf9781bacf0999ee4274dc9d3fc6bdeffaa6e8 Mon Sep 17 00:00:00 2001 From: Jibril Mammeri Date: Tue, 23 Mar 2021 16:10:56 +0100 Subject: [PATCH] Add trough() and goto_min() --- bliss/scanning/scan.py | 9 ++++++++ bliss/scanning/scan_math.py | 42 ++++++++++++++++++++++++++++++++++++ bliss/scanning/scan_tools.py | 33 ++++++++++++++++++++++++++++ bliss/shell/standard.py | 4 ++++ doc/docs/scan_alignment.md | 14 +++++++++++- tests/scans/test_cen_com.py | 29 ++++++++++++++++++++++++- 6 files changed, 129 insertions(+), 2 deletions(-) diff --git a/bliss/scanning/scan.py b/bliss/scanning/scan.py index 629b0a3cef..03454db92c 100755 --- a/bliss/scanning/scan.py +++ b/bliss/scanning/scan.py @@ -1032,6 +1032,12 @@ class Scan: def _peak(self, counter, axis): return scan_math.peak(*self._get_x_y_data(counter, axis)) + def trough(self, counter, axis=None, return_axes=False): + return self._multimotors(self._trough, counter, axis, return_axes=return_axes) + + def _trough(self, counter, axis): + return scan_math.trough(*self._get_x_y_data(counter, axis)) + def com(self, counter, axis=None, return_axes=False): return self._multimotors(self._com, counter, axis, return_axes=return_axes) @@ -1123,6 +1129,9 @@ class Scan: def goto_peak(self, counter, axis=None): return self._goto_multimotors(self.peak(counter, axis, return_axes=True)) + def goto_min(self, counter, axis=None): + return self._goto_multimotors(self.trough(counter, axis, return_axes=True)) + def goto_com(self, counter, axis=None, return_axes=False): return self._goto_multimotors(self.com(counter, axis, return_axes=True)) diff --git a/bliss/scanning/scan_math.py b/bliss/scanning/scan_math.py index 35f7b7623a..04e7b2492c 100644 --- a/bliss/scanning/scan_math.py +++ b/bliss/scanning/scan_math.py @@ -18,6 +18,8 @@ Cen = namedtuple("center", ["position", "fwhm"]) Peak = namedtuple("peak", ["position", "value"]) +Trough = namedtuple("trough", ["position", "value"]) + def peak(x: numpy.ndarray, y: numpy.ndarray) -> float: """Returns the x location of the peak. @@ -59,6 +61,46 @@ def peak2(x: numpy.ndarray, y: numpy.ndarray) -> typing.Tuple[float, float]: return Peak(numpy.nan, numpy.nan) +def trough(x: numpy.ndarray, y: numpy.ndarray) -> float: + """Returns the x location of the trough. + + On the current implementation the trough is defined as the min of the y. + + The algorithm was designed to be fast. It is not using any fit function. + + Args: + x (ndarray): X locations + y (ndarray): Y locations + + Returns: + x location of the min + """ + return trough2(x, y)[0] + + +def trough2(x: numpy.ndarray, y: numpy.ndarray) -> typing.Tuple[float, float]: + """Returns the location of the trough. + + On the current implementation the trough is defined as the min of the y. + + The algorithm was designed to be fast. It is not using any fit function. + + Args: + x (ndarray): X locations + y (ndarray): Y locations + + Returns: + A tuple containing the x location and the y location of the min + """ + if not _check_arrays(x, y): + return Trough(numpy.nan, numpy.nan) + with _capture_exceptions(): + x, y = _extract_finite(x, y) + index = numpy.argmin(y) + return Trough(x[index], y[index]) + return Trough(numpy.nan, numpy.nan) + + def com(x: numpy.ndarray, y: numpy.ndarray, visual=True) -> float: """Returns the location of the center of the mass. diff --git a/bliss/scanning/scan_tools.py b/bliss/scanning/scan_tools.py index 24b8834fe1..fe0e4f5c41 100644 --- a/bliss/scanning/scan_tools.py +++ b/bliss/scanning/scan_tools.py @@ -272,6 +272,39 @@ def goto_peak( return _scan_calc("peak", counter=counter, axis=axis, scan=scan, goto=True) +@typeguard.typechecked +@shorten_signature(hidden_kwargs=[]) +def trough( + counter: Optional[_countable] = None, + axis: Optional[_scannable] = None, + scan: Optional[Scan] = None, +): + """ + Return position of scanned motor at minimum of of last scan. + If is not specified, use selected counter. + + Example: min_of_scan = min() + """ + return _scan_calc("trough", counter=counter, axis=axis, scan=scan) + + +@typeguard.typechecked +@shorten_signature(hidden_kwargs=[]) +def goto_min( + counter: Optional[_countable] = None, + axis: Optional[_scannable] = None, + scan: Optional[Scan] = None, +): + """ + Return position of scanned motor at minimum of of last scan. + Move scanned motor to this value. + If is not specified, use selected counter. + + Example: goto_min() + """ + return _scan_calc("trough", counter=counter, axis=axis, scan=scan, goto=True) + + @typeguard.typechecked @typeguardTypeError_to_hint def goto_custom( diff --git a/bliss/shell/standard.py b/bliss/shell/standard.py index 380a2f7e96..62eb21e03f 100644 --- a/bliss/shell/standard.py +++ b/bliss/shell/standard.py @@ -90,6 +90,8 @@ from bliss.scanning.scan_tools import ( goto_com, peak, goto_peak, + trough, + goto_min, where, find_position, goto_custom, @@ -180,6 +182,8 @@ __all__ = ( "goto_cen", "peak", "goto_peak", + "trough", + "goto_min", "com", "goto_com", "where", diff --git a/doc/docs/scan_alignment.md b/doc/docs/scan_alignment.md index 9426d20fc2..23a4368dae 100644 --- a/doc/docs/scan_alignment.md +++ b/doc/docs/scan_alignment.md @@ -21,7 +21,7 @@ goto_cen(axis=robz) peak(axis=robz) ``` -If the axis is not specified, `cen`, `com`, `peak` functions will return value for +If the axis is not specified, `cen`, `com`, `peak`, `trough` functions will return value for all axes and the `goto_` functions will move all motors. ## Counters selection @@ -97,6 +97,13 @@ This function returns the motor position at the counter maximum value. max_pos = peak() ``` +## trough() + +This function returns the motor position at the counter minimum value. +``` +min_pos = trough() +``` + ## goto_ functions * all the previous functions have a corresponding `goto_XXX()` function @@ -104,6 +111,7 @@ to go directly to the calculated position: * `goto_cen()` * `goto_com()` * `goto_peak()` + * `goto_min()` * Before the movement, the `goto_XXX` functions will print the **previous position** and the **future position** of the motor with a `WARNING` message. * In case of motion abortion, the motor returns to its previous @@ -126,6 +134,10 @@ WARNING bliss.scans: Motor mm1 will move from 4.337243 to 10.000000 DEMO [14]: goto_com() WARNING bliss.scans: Motor mm1 will move from 10.000000 to 4.805529 + +DEMO [15]: goto_min() +WARNING bliss.scans: Motor mm1 will move from 4.805529 to 0.000000 + ``` ## where() diff --git a/tests/scans/test_cen_com.py b/tests/scans/test_cen_com.py index d63ddf02ea..a8c14d2725 100644 --- a/tests/scans/test_cen_com.py +++ b/tests/scans/test_cen_com.py @@ -49,6 +49,7 @@ def test_pkcom_ascan_gauss(session): s = scans.ascan(roby, 0, 10, 10, 0, simul_counter, save=False, return_scan=True) peak = s.peak(simul_counter) + trough = s.trough(simul_counter) cen = s.cen(simul_counter) fwhm = s.fwhm(simul_counter) com = s.com(simul_counter) @@ -71,6 +72,8 @@ def test_pkcom_ascan_gauss(session): assert pytest.approx(roby.position) == peak s.goto_com(simul_counter) assert pytest.approx(roby.position) == com + s.goto_min(simul_counter) + assert pytest.approx(roby.position) == trough def test_pkcom_a2scan_gauss(session): @@ -250,10 +253,11 @@ def test_plotselect_and_global_cen(session): scan_tools.goto_cen() scan_tools.goto_com() scan_tools.goto_peak() + scan_tools.goto_min() def test_goto(session): - from bliss.scanning.scan_tools import goto_cen, goto_com, goto_peak + from bliss.scanning.scan_tools import goto_cen, goto_com, goto_peak, goto_min roby = session.config.get("roby") m0 = session.config.get("m0") @@ -280,6 +284,13 @@ def test_goto(session): assert pytest.approx(2, abs=1e-3) == roby.position assert pytest.approx(-80, abs=1) == m0.position + roby.move(3) + m0.move(-78) + + goto_min(simul_counter) # center of simul_counter + assert pytest.approx(0, abs=1e-3) == roby.position + assert pytest.approx(-100, abs=1) == m0.position + ## use scan attached functions as well s.goto_cen(simul_counter) # center of simul_counter assert pytest.approx(2.5, abs=1e-3) == roby.position @@ -299,6 +310,13 @@ def test_goto(session): assert pytest.approx(2, abs=1e-3) == roby.position assert pytest.approx(-80, abs=1) == m0.position + roby.move(3) + m0.move(-78) + + s.goto_min(simul_counter) # center of simul_counter + assert pytest.approx(0, abs=1e-3) == roby.position + assert pytest.approx(-100, abs=1) == m0.position + goto_cen(diode) roby_center = s.cen(diode, roby) m0_center = s.cen(diode, m0) @@ -323,6 +341,15 @@ def test_goto(session): assert pytest.approx(roby_peak, abs=1e-3) == roby.position assert pytest.approx(m0_peak, abs=1) == m0.position + roby.move(3) + m0.move(-78) + + goto_min(diode) + roby_min = s.trough(diode, roby) + m0_min = s.trough(diode, m0) + assert pytest.approx(roby_min, abs=1e-3) == roby.position + assert pytest.approx(m0_min, abs=1) == m0.position + # wrong arguments check with pytest.raises(TypeError): goto_cen(roby) # first arg should be a counter -- GitLab