diff --git a/docs/api.calibration.rst b/docs/api.calibration.rst index 962e0cc3..ff653cb6 100644 --- a/docs/api.calibration.rst +++ b/docs/api.calibration.rst @@ -1,10 +1,123 @@ -api.calibration +Calibration ------------- +Calibration Module +#################### + +The calibration module of this library is used to generate the metadata necessary configure and run calibration workflows for different assets/devices. + +The metadata follows the general logic of the library by implementing the three core classes: + - :py:class:`~aind_behavior_services.session.AindBehaviorSessionModel` + - :py:class:`~aind_behavior_services.rig.AindBehaviorRigModel` + - :py:class:`~aind_behavior_services.task_logic.AindBehaviorTaskLogicModel` + +A fourth class :py:class:`~aind_behavior_services.calibration.Calibration`, +specific to the Calibration module, is also implemented to keep track of the calibration metrics. +This class was written to be aligned to the Calibration class in aind-data-schemas +(https://github.com/AllenNeuralDynamics/aind-data-schema/blob/2fd0e403bf46f0f1a47e5922c4228517e68376a3/src/aind_data_schema/components/devices.py#L274). +An application example will be provided below. + +While we use the base :py:class:`~aind_behavior_services.session.AindBehaviorSessionModel` class to keep track of the session metadata, +both :py:class:`~aind_behavior_services.rig.AindBehaviorRigModel` and :py:class:`~aind_behavior_services.task_logic.AindBehaviorTaskLogicModel` are +expected to be sub-classed to specify the necessary dependencies of the calibration workflow. + +Sub-classing :py:class:`~aind_behavior_services.calibration.Calibration` +########################################################################## + +Sub-classing :py:class:`~aind_behavior_services.calibration.Calibration` boils down to providing a subtype of the `input` and `output` fields. +These fields are expected to be of a sub-type of `~pydantic.BaseModel` and define the structure of the calibration outcome. +Conceptually, `input` is the pre-process data that resulted from the calibration workflow (i.e. the weight of delivered water), +whereas `output` is used to represent a post-processed version of the calibration outcome (e.g. a linear model that relates valve-opening times to water volume). + +An example of a sub-class of `Calibration` is provided below: + +.. code-block:: python + + from pydantic import BaseModel, Field + from typing import List, Literal + from aind_behavior_services.calibration import Calibration + + + class BarContainer(BaseModel): + baz: string = Field(..., description="Baz value") + bar: float = Field(..., description="Bar value") + + + class DeviceCalibrationInput(BaseModel): + measured_foo: List[int] = Field(..., description="Measurements of Foo") + bar_container: List[BarContainer] = Field(..., description="Bar container") + + + class DeviceCalibrationOutput(BaseModel): + param_a = float = Field(default=1, description="Parameter A") + param_b = float = Field(default=0, description="Parameter B") + + + class DeviceCalibration(Calibration): + device_name: Literal["MyDevice"] = "MyDevice" + description: Literal["Stores the calibration of a device"] = "Stores the calibration of a device" + input: DeviceCalibrationInput = Field(..., title="Input of the calibration") + output: DeviceCalibrationOutput = Field(..., title="Output of the calibration") + +Sub-classing :py:class:`~aind_behavior_services.rig.AindBehaviorRigModel` +########################################################################## + +We adopt the following pattern to sub-class the :py:class:`~aind_behavior_services.rig.AindBehaviorRigModel` class: + +.. code-block:: python + + from aind_behavior_services.rig import AindBehaviorRigModel, Device + + RIG_VERSION = "1.0.0" # Use SemVer + + class FooDevice(Device): + calibration: DeviceCalibration = Field(..., title="Calibration of the device foo") + + + class CalibrationRig(AindBehaviorRigModel): + version: Literal[RIG_VERSION] = RIG_VERSION + device_foo: FooDevice = Field(..., title="Device Foo") + device_bar: Device = Field(..., title="Device Bar") + + +For an example see :py:class:`aind_behavior_services.calibration.olfactometer.CalibrationRig`. + + + +Sub-classing :py:class:`~aind_behavior_services.task_logic.AindBehaviorTaskLogicModel` +################################################################################ + +The same way a :py:class:`~aind_behavior_services.task_logic.AindBehaviorTaskLogicModel` is used to define +the settings to run a behavior task, it is also used to define the settings to run a calibration workflow. +It will thus fallow an identical sub-classing pattern: + + +.. code-block:: python + + from aind_behavior_services.task_logic import AindBehaviorTaskLogicModel, TaskParameters + + TASK_LOGIC_VERSION = "0.1.0" + + class CalibrationParameters(TaskParameters): + n_iterations: int = Field(default=10, description="Number of iterations to run the calibration") + channels_to_calibrate: List[Literal[1,2,3]] = Field(default=[1], description="List of channels to calibrate") + + class CalibrationLogic(AindBehaviorTaskLogicModel): + name: Literal["CalibrationLogic"] = "CalibrationLogic + version: Literal[TASK_LOGIC_VERSION] = TASK_LOGIC_VERSION + task_parameters: CalibrationParameters = Field(default=CalibrationParameters(), title="Task parameters", validate_default=True) + + +For an example see :py:class:`aind_behavior_services.calibration.olfactometer.CalibrationLogic`. + + + .. toctree:: - :maxdepth: 3 + :maxdepth: 4 api.calibration/aind_manipulator api.calibration/load_cells api.calibration/olfactometer api.calibration/water_valve + + diff --git a/docs/conf.py b/docs/conf.py index 7e59209c..529da1e0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -32,7 +32,7 @@ rst_target_path = os.path.abspath("json-schemas") root_template = """ -json-schemas +JsonSchema ------------- .. toctree:: :maxdepth: 4 @@ -75,6 +75,7 @@ "sphinx.ext.linkcode", "myst_parser", "sphinxcontrib.autodoc_pydantic", + 'sphinx_copybutton' ] templates_path = ["_templates"] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]