Skip to content

Commit

Permalink
calibration: expose diode calibration
Browse files Browse the repository at this point in the history
This gives users an option to interrogate the parameter values and results of the diode calibration
  • Loading branch information
JoepVanlier committed Sep 25, 2024
1 parent 2b9dbef commit c240bcb
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 9 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* Added `applied_at` property to a [calibration item](https://lumicks-pylake.readthedocs.io/en/latest/_api/lumicks.pylake.calibration.ForceCalibrationItem.html) obtained from a force slice. This property returns the timestamp in nanoseconds at which the force calibration was applied.
* Added improved printing of calibration items under `channel.calibration` providing a more convenient overview of the items associated with a `Slice`.
* Added improved printing of calibrations performed with `Pylake`.
* Added property `diode_calibration` to access diode calibration model in [calibration item](https://lumicks-pylake.readthedocs.io/en/latest/_api/lumicks.pylake.calibration.ForceCalibrationItem.html).
* Added parameter `titles` to customize title of each subplot in [`Kymo.plot_with_channels()`](https://lumicks-pylake.readthedocs.io/en/latest/_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.plot_with_channels).
* Added [`KymoTrack.sample_from_channel()`](https://lumicks-pylake.readthedocs.io/en/latest/_api/lumicks.pylake.kymotracker.kymotrack.KymoTrack.html#lumicks.pylake.kymotracker.kymotrack.KymoTrack.sample_from_channel) to downsample channel data to the time points of a kymotrack.

Expand Down
3 changes: 2 additions & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ If you are just looking to get started, read the :doc:`/tutorial/index` first.
File
channel.Slice
calibration.ForceCalibrationItem
calibration.DiodeCalibrationModel
fdcurve.FdCurve
fdensemble.FdEnsemble
kymo.Kymo
Expand Down Expand Up @@ -157,4 +158,4 @@ Simulation
:toctree: _api
:template: function.rst

simulation.simulate_diffusive_tracks
simulation.simulate_diffusive_tracks
5 changes: 5 additions & 0 deletions lumicks/pylake/force_calibration/calibration_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from functools import wraps
from collections import UserDict

from lumicks.pylake.force_calibration.calibration_models import DiodeCalibrationModel
from lumicks.pylake.force_calibration.detail.calibration_properties import (
CalibrationPropertiesMixin,
)
Expand Down Expand Up @@ -37,6 +38,10 @@ def _fitted_diode(self):
"""Diode parameters were fitted"""
return "f_diode (Hz)" in self or "alpha" in self

@property
def diode_calibration(self):
return DiodeCalibrationModel.from_calibration_dict(self.data)

@property
def _sensor_type(self):
if self.fast_sensor:
Expand Down
44 changes: 44 additions & 0 deletions lumicks/pylake/force_calibration/calibration_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,50 @@ def diode_params_from_voltage(
)


class DiodeCalibrationModel:
def __init__(self, model_fun, params):
"""Diode calibration model
Parameters
----------
model_fun : callable
Function that takes trap voltage data as input and returns the diode parameters.
params : dict
Dictionary with raw diode calibration parameters.
"""
self._model_fun = model_fun
self._params = params

@property
def params(self):
"""Raw diode model calibration parameters"""
return self._params

@staticmethod
def from_calibration_dict(calibration_dict):
try:
params = {
"delta_f_diode": calibration_dict["Diode frequency delta"],
"rate_f_diode": calibration_dict["Diode frequency rate"],
"max_f_diode": calibration_dict["Diode frequency max"],
"delta_alpha": calibration_dict["Diode alpha delta"],
"rate_alpha": calibration_dict["Diode alpha rate"],
"max_alpha": calibration_dict["Diode alpha max"],
}
except KeyError:
return None

def diode_fun(trap_voltage):
f_diode, alpha, _ = diode_params_from_voltage(trap_voltage, **params)
return {"fixed_diode": f_diode, "fixed_alpha": alpha}

return DiodeCalibrationModel(diode_fun, params)

def __call__(self, trap_voltage):
"""Function to look up the diode parameters at a given trap power."""
return self._model_fun(trap_voltage)


def density_of_water(temperature, molarity, pressure=0.101325):
"""Determine the density of water with NaCl.
Expand Down
52 changes: 44 additions & 8 deletions lumicks/pylake/force_calibration/tests/test_calibration_item.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import pickle

import pytest

from lumicks.pylake.calibration import ForceCalibrationList
Expand Down Expand Up @@ -143,8 +145,8 @@ def test_passive_item(compare_to_reference_dict, reference_data, calibration_dat
assert item.number_of_samples == 781250
assert not item.active_calibration
assert not item.fast_sensor
assert item.start is 1696171376701856700
assert item.stop is 1696171386701856700
assert item.start == 1696171376701856700
assert item.stop == 1696171386701856700
assert item.stiffness is ref_passive_fixed_diode_with_height["kappa (pN/nm)"]
assert item.force_sensitivity is ref_passive_fixed_diode_with_height["Rf (pN/V)"]
assert item.displacement_sensitivity is ref_passive_fixed_diode_with_height["Rd (um/V)"]
Expand All @@ -170,7 +172,7 @@ def test_passive_item(compare_to_reference_dict, reference_data, calibration_dat
assert item.diffusion_volts_std_err == ref_passive_fixed_diode_with_height["err_D (V^2/s)"]
assert not item.diode_frequency_std_err
assert not item.diode_relaxation_factor_std_err
assert item.applied_at is 1696171386701856700
assert item.applied_at == 1696171386701856700

compare_to_reference_dict(item.power_spectrum_params(), test_name="power")
compare_to_reference_dict(item._model_params(), test_name="model")
Expand All @@ -194,8 +196,8 @@ def test_active_item_fixed_diode(compare_to_reference_dict, calibration_data):
assert item.sample_rate == 78125
assert item.active_calibration # It is an active item!
assert not item.fast_sensor
assert item.start is 1713785826919398000
assert item.stop is 1713785836900152600
assert item.start == 1713785826919398000
assert item.stop == 1713785836900152600
assert item.stiffness is ref_active["kappa (pN/nm)"]
assert item.force_sensitivity is ref_active["Rf (pN/V)"]
assert item.displacement_sensitivity is ref_active["Rd (um/V)"]
Expand Down Expand Up @@ -264,9 +266,9 @@ def test_non_full(compare_to_reference_dict):
)
assert not item.stiffness
assert not item.displacement_sensitivity
assert item.force_sensitivity is 1.0
assert item.start is 1714391268938540100
assert item.stop is 1714391268938540200
assert item.force_sensitivity == 1.0
assert item.start == 1714391268938540100
assert item.stop == 1714391268938540200

for func in ("calibration_params", "power_spectrum_params"):
with pytest.raises(
Expand Down Expand Up @@ -327,3 +329,37 @@ def create_item(t_start, t_stop, **kwargs):
assert it == fc

assert list(items) == items._src


def test_item_pickle():
pickled_str = pickle.dumps(ForceCalibrationItem(ref_passive_fixed_diode_with_height))
loaded_object = pickle.loads(pickled_str)
assert loaded_object == ForceCalibrationItem(ref_passive_fixed_diode_with_height)


@pytest.mark.parametrize(
"trap_power, reference_values",
[
(0, {"fixed_alpha": 0.45, "fixed_diode": 10000.0}),
(1000000, {"fixed_alpha": 0.5, "fixed_diode": 14000.0}),
(1.0, {"fixed_alpha": 0.48160602794142787, "fixed_diode": 12853.980812559239}),
([0.5, 1.5], {"fixed_alpha": 0.48160602794142787, "fixed_diode": 12853.980812559239}),
],
)
def test_diode_model(trap_power, reference_values):
item = ForceCalibrationItem(ref_passive_fixed_diode_with_height)
diode_calibration = item.diode_calibration
assert diode_calibration(trap_power) == reference_values
assert diode_calibration.params == {
"delta_f_diode": ref_passive_fixed_diode_with_height["Diode frequency delta"],
"rate_f_diode": ref_passive_fixed_diode_with_height["Diode frequency rate"],
"max_f_diode": ref_passive_fixed_diode_with_height["Diode frequency max"],
"delta_alpha": ref_passive_fixed_diode_with_height["Diode alpha delta"],
"rate_alpha": ref_passive_fixed_diode_with_height["Diode alpha rate"],
"max_alpha": ref_passive_fixed_diode_with_height["Diode alpha max"],
}


def test_no_diode_model():
item = ForceCalibrationItem(ref_active)
assert item.diode_calibration is None

0 comments on commit c240bcb

Please sign in to comment.