-
Notifications
You must be signed in to change notification settings - Fork 0
Centre of mass callback #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9272689
f4a46a2
fbd884d
edd30de
42dd6c9
4ce4107
d625763
37f46f1
e87b883
729f1c9
41ace63
516d5ab
40559dd
31ad418
9d0ceea
c329bad
13f773a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Using our own Centre of Mass Callback | ||
|
|
||
| ## Status | ||
|
|
||
| Current | ||
|
|
||
| ## Context | ||
|
|
||
| A decision needs to be made about whether to make changes to upstream Bluesky so that their `CoM` callback works for us, or we make our own. | ||
|
|
||
| ## Decision | ||
|
|
||
| We will be making our own `CoM` callback. | ||
|
|
||
rerpha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ## Justification & Consequences | ||
|
|
||
| We attempted to make changes to upstream Bluesky which were rejected, as it adds limits to the functionality of the callback. We also found other limitations with using their callback, such as not being able to have disordered and non-continuous data sent to it without it skewing the calculated value- we need it to work with disordered and non-continuous data as we need to be able to run continuous scans. | ||
|
|
||
| This will mean that... | ||
| - Our version of the callback will not be supported by Bluesky and may need changes as Bluesky updates. | ||
| - We can have a version of the callback that is made bespokely for our use cases. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -214,6 +214,40 @@ lf = LiveFit(fit_method, ...) | |
| ``` | ||
| See the [standard fits](#models) list above for standard fits which require parameters. It gets more complicated if you want to define your own custom model or guess which you want to pass parameters to. You will have to define a function that takes these parameters and returns the model / guess function with the subsituted values. | ||
|
|
||
| # Centre of Mass | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as above i don't think this wants to be in fitting, but i think that the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll refactor
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will fix in documentation mega-pr |
||
|
|
||
| [`CentreOfMass`](ibex_bluesky_core.callbacks.CentreOfMass) is a callback that provides functionality for calculating our definition of Centre of Mass. We calculate centre of mass from the 2D region bounded by min(y), min(x), max(x), and straight-line segments joining (x, y) data points with their nearest neighbours along the x axis. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should link to the ADR here to justify why it exists
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will fix in documentation mega-pr |
||
|
|
||
| [`CentreOfMass`](ibex_bluesky_core.callbacks.CentreOfMass) has a property, `result` which stores the centre of mass value once the callback has finished. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can link to the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will fix in documentation mega-pr |
||
|
|
||
| In order to use the callback, import `CentreOfMass` from `ibex_bluesky_core.callbacks`. | ||
| ```py | ||
| from ibex_bluesky_core.callbacks import CentreOfMass | ||
| ``` | ||
|
|
||
| ## Our CoM Algorithm | ||
|
|
||
| Given non-continuous arrays of collected data `x` and `y`, ({py:obj}`ibex_bluesky_core.callbacks.CentreOfMass`) returns the `x` value of the centre of mass. | ||
|
|
||
| Our use cases require that our algorithm abides to the following rules: | ||
| - Any background on data does not skew the centre of mass | ||
| - The order in which data is received does not skew the centre of mass | ||
| - Should support non-constant point spacing without skewing the centre of mass | ||
|
|
||
| *Note that this is designed for only **positive** peaks.* | ||
|
|
||
| ### Step-by-step | ||
|
|
||
| 1) Sort `x` and `y` arrays in respect of `x` ascending. This is so that data can be received in any order. | ||
| 2) From each `y` element, subtract `min(y)`. This means that any constant background over data is ignored. (Does not work for negative peaks) | ||
| 3) Calculate weight/widths for each point; based on it's `x` distances from neighbouring points. This ensures non-constant point spacing is accounted for in our calculation. | ||
| 4) For each decomposed shape that makes up the total area under the curve, `CoM` is calculated as the following: | ||
| ```{math} | ||
| com_x = \frac{\sum_{}^{}x * y * \text{weight}}{\sum_{}^{}y * \text{weight}} | ||
| ``` | ||
|
|
||
| [`CentreOfMass`](ibex_bluesky_core.callbacks.CentreOfMass) can be used from our callbacks collection. See [ISISCallbacks](ibex_bluesky_core.callbacks.ISISCallbacks). | ||
|
|
||
| ## Chained Fitting | ||
|
|
||
| [`ChainedLiveFit`](ibex_bluesky_core.callbacks.ChainedLiveFit) is a specialised callback that manages multiple LiveFit instances in a chain, where each fit's results inform the next fit's initial parameters. This is particularly useful when dealing with complex data sets where subsequent fits depend on the parameters obtained from previous fits. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import logging | ||
|
|
||
| import numpy as np | ||
| from bluesky.callbacks import CollectThenCompute | ||
|
|
||
| from ibex_bluesky_core.utils import center_of_mass_of_area_under_curve | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| __all__ = ["CentreOfMass"] | ||
|
|
||
|
|
||
| class CentreOfMass(CollectThenCompute): | ||
| """Compute centre of mass after a run finishes. | ||
|
|
||
| Calculates the CoM of the 2D region bounded by min(y), min(x), max(x), | ||
| and straight-line segments joining (x, y) data points with their nearest | ||
| neighbours along the x axis. | ||
| """ | ||
|
|
||
| def __init__(self, x: str, y: str) -> None: | ||
| """Initialise the callback. | ||
|
|
||
| Args: | ||
| x: Name of independent variable in event data | ||
| y: Name of dependent variable in event data | ||
|
|
||
| """ | ||
| super().__init__() | ||
| self.x: str = x | ||
| self.y: str = y | ||
| self._result: float | None = None | ||
|
|
||
| @property | ||
| def result(self) -> float | None: | ||
| """The centre-of-mass calculated by this callback.""" | ||
| return self._result | ||
|
|
||
| def compute(self) -> None: | ||
| """Calculate statistics at the end of the run.""" | ||
| x_values = [] | ||
| y_values = [] | ||
|
|
||
| for event in self._events: | ||
| if self.x not in event["data"]: | ||
| raise ValueError(f"{self.x} is not in event document.") | ||
|
|
||
| if self.y not in event["data"]: | ||
| raise ValueError(f"{self.y} is not in event document.") | ||
|
|
||
| x_values.append(event["data"][self.x]) | ||
| y_values.append(event["data"][self.y]) | ||
|
|
||
| if not x_values: | ||
| return | ||
|
|
||
| x_data = np.array(x_values, dtype=np.float64) | ||
| y_data = np.array(y_values, dtype=np.float64) | ||
| (self._result, _) = center_of_mass_of_area_under_curve(x_data, y_data) |
Uh oh!
There was an error while loading. Please reload this page.