Skip to content

Commit

Permalink
calibration: add convenience api
Browse files Browse the repository at this point in the history
for items that have the calibration inside, you can make it even easier for users
  • Loading branch information
JoepVanlier committed Jan 23, 2025
1 parent c2a9a23 commit b90d8e6
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## v1.7.0 | t.b.d.

* Added [voltage](https://lumicks-pylake.readthedocs.io/en/latest/_api/lumicks.pylake.calibration.ForceCalibrationItem.html#lumicks.pylake.calibration.ForceCalibrationItem.voltage), [sum_voltage](https://lumicks-pylake.readthedocs.io/en/latest/_api/lumicks.pylake.calibration.ForceCalibrationItem.html#lumicks.pylake.calibration.ForceCalibrationItem.sum_voltage) and [driving](https://lumicks-pylake.readthedocs.io/en/latest/_api/lumicks.pylake.calibration.ForceCalibrationItem.html#lumicks.pylake.calibration.ForceCalibrationItem.driving) properties which return raw calibration data. Note that these are only available when the option to export the raw data has explicitly been selected in Bluelake. When unavailable, these properties will return empty slices.
* Added [ForceCalibrationItem.recalibrate_with()](https://lumicks-pylake.readthedocs.io/en/latest/_api/lumicks.pylake.calibration.ForceCalibrationItem.html#lumicks.pylake.calibration.ForceCalibrationItem.recalibrate_with) and [ForceCalibrationItem.plot()](https://lumicks-pylake.readthedocs.io/en/latest/_api/lumicks.pylake.calibration.ForceCalibrationItem.html#lumicks.pylake.calibration.ForceCalibrationItem.plot) to recalibrate and/or plot calibrations performed in Bluelake. Note that the raw data needs to have been exported inside the item (Bluelake 2.7.x feature) for this feature to work.

## v1.6.0 | t.b.d.

Expand Down
67 changes: 67 additions & 0 deletions docs/tutorial/force_calibration/calibration_items.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,78 @@ These parameters are properties and can be extracted as such::
Redoing a Bluelake calibration
------------------------------

Starting from Bluelake `2.7.0`, it is possible to export the raw data used for calibration with the
calibration item. You can enable this in the settings panel in Bluelake.
With this feature, recalibrating your data differently becomes a lot easier.
If you don't have this setting enabled, you can still recalibrate your data, but you will have to
ensure that you manually export the raw calibration data and slice the appropriate calibration data.
To find out how to do this, please refer to the section :ref:`Recalibrating using timeline data<recalibrating_manually>`.

.. _recalibrating_simple:

Recalibrating using calibration items with raw data
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Let's start by loading our calibration item::

f = lk.File("test_data/raw_data_in_item.h5")
calibration = f.force1x.calibration[0]

We can quickly plot this calibration::

calibration.plot()

.. image:: figures/in_item_plot.png

If you wish to obtain the raw calibration data, you can simply access the properties :meth:`~lumicks.pylake.force_calibration.calibration_item.ForceCalibrationItem.calibration.voltage` and for active calibration :meth:`~lumicks.pylake.force_calibration.calibration_item.ForceCalibrationItem.calibration.driving`.
These return slices you can plot and interact with::

calibration.voltage.plot()

.. image:: figures/in_item_raw_data.png

We can easily re-perform this calibration by invoking :meth:`~lumicks.pylake.force_calibration.calibration_item.ForceCalibrationItem.recalibrate_with()`.
Let's see what this spectrum would have looked like with less blocking::

recalibrated = calibration.recalibrate_with(num_points_per_block=200)
recalibrated.plot()

.. image:: figures/in_item_less_blocking.png

Note that any of the calibration parameters can easily be changed this way.
To investigate the effect of the hydrodynamically correct model for example, we can try turning it off::

recalibrated_no_hyco = calibration.recalibrate_with(hydrodynamically_correct=False)
recalibrated_no_hyco.plot()

.. image:: figures/in_item_no_hyco.png

This clearly fits the data poorly.
To see what it would do to our timeline data, we can simply recalibrate that by applying the calibration item to :meth:`~lumicks.pylake.channel.Slice.recalibrate_force`::

recalibrated_force = f.force1x.recalibrate_force(recalibrated_no_hyco)

f.force1x.plot()
recalibrated_force.plot()

.. image:: figures/in_item_no_hyco_trace.png

That's all there is to it.

Recalibrating using timeline data
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. _recalibrating_manually:

.. important::
In order to redo a Bluelake calibration, the force data that was used for the calibration has to
be included in the `.h5` file. *Note that this force data is not exported nor marked by default*;
it has to be explicitly added to the exported file.

We start by loading the calibration item::

f = lk.File("test_data/passive_calibration.h5")

We can directly slice the channel by the calibration item we want to reproduce to extract the relevant data::

force1x_slice = f.force1x[f.force1x.calibration[1]]
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/tutorial/force_calibration/figures/in_item_no_hyco.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/tutorial/force_calibration/figures/in_item_plot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/tutorial/force_calibration/figures/in_item_raw_data.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions lumicks/pylake/force_calibration/calibration_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,26 @@ def __repr__(self):
)
return f"{self.__class__.__name__}({properties})"

def plot(self):
if not self.voltage:
raise ValueError(
"This calibration item does not contain the raw data. If you still have the "
"timeline force data, you can de-calibrate that and perform the re-calibration"
"manually. See the pylake tutorial on force calibration for more information."
)

self.recalibrate_with().plot()

def recalibrate_with(self, **params):
"""Returns a calibration structure with some parameters overridden.
For a full list of parameters to override, please see
:func:`~lumicks.pylake.calibrate_force()`"""
active_data = {"driving_data": self.driving.data} if self.active_calibration else {}
return calibrate_force(
self.voltage.data, **(self.calibration_params() | active_data | params)
)

@_verify_full
def _model_params(self):
"""Returns parameters with which to create an active or passive calibration model"""
Expand Down
36 changes: 36 additions & 0 deletions lumicks/pylake/force_calibration/tests/test_calibration_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,42 @@ def test_non_full(compare_to_reference_dict):
)


def test_plot_item(active_ref_data):
item = ForceCalibrationItem(ref_active)
with pytest.raises(ValueError, match="This calibration item does not contain the raw data"):
item.plot()

voltage, driving = active_ref_data

item = ForceCalibrationItem(ref_active, voltage=voltage)
with pytest.raises(
ValueError, match="Active calibration requires the driving_data to be defined"
):
item.plot()

item = ForceCalibrationItem(ref_active, voltage=voltage, driving=driving)
item.plot()


def test_recalibrate_item(active_ref_data):
voltage, driving = active_ref_data
item = ForceCalibrationItem(ref_active, voltage=voltage, driving=driving)
np.testing.assert_allclose(item.stiffness, 0.50338, rtol=1e-4)

same_calibration = item.recalibrate_with()
np.testing.assert_allclose(same_calibration.stiffness, 0.521374, rtol=1e-4)
assert same_calibration.fitted_diode

recalibrated = item.recalibrate_with(
hydrodynamically_correct=False,
fixed_diode=17498.229,
fixed_alpha=0.135,
distance_to_surface=1.5,
)
np.testing.assert_allclose(recalibrated.stiffness, 0.498051, rtol=1e-4)
assert not recalibrated.fitted_diode


def test_force_calibration_handling():
def timestamp(time):
return 1714391268938540100 + int(1e9) * time
Expand Down

0 comments on commit b90d8e6

Please sign in to comment.