diff --git a/AGENTS.md b/AGENTS.md index f0e82c3..e36f049 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,9 @@ Prefer the first option that fits: instruments** (data viewer, analysis, h5 browsing, configuration tools). 4. A plain script, for a one-off sequence that does not need a GUI. +A **model** (section 6) is not a separate option: it tailors an existing extension (PID, optimizers, data mixer, +sequencer) to your experiment. + ## 2. Workflow ```bash @@ -167,7 +170,30 @@ and the deprecated `CustomApp` / `CustomExt` hooks `setup_docks` and `setup_menu Only when no instrument is needed. Same (non-deprecated) lifecycle as above, `parent` is a `DockArea` or `QMainWindow`, and `main()` creates the app with `mkQApp`. If instruments become necessary, move the logic to a `CustomExt`. -## 6. Done checklist +## 6. Models of the extensions (`models/`) + +Four Dashboard extensions delegate the part that depends on your experiment to a model class that your package +provides: PID (`PIDModelGeneric`), Bayesian and Adaptive optimizers (`OptimizerModelGeneric` or `OptimizerModelDefault`), +DataMixer (`DataMixerModel`) and the Sequencer (`ChoiceModelBase`). Write a model, not an extension, when one of these +extensions already does the job and only the conversion of the measurements or of the outputs is specific. + +- Set `models = true` in `[features]`; entry points are generated only then (`pymodaq.models`). +- Create the `models/` folder at the root of the package, one model per module; the module name is the name shown in + the extension. The model is the class defined in the module that derives from the base class of the extension. It may + derive from your own intermediate class, but PID and DataMixer only accepted a direct subclass of the generic base + before the release that follows 5.3.3, so derive directly from it if you must support older releases. The Sequencer + is the exception: it does not look at base classes, you register each choice model with + `@ChoiceModelFactory.register_choice()`. +- The template ships no model on purpose. Start from the working examples of + [`pymodaq_plugins_mockexamples`](https://github.com/PyMoDAQ/pymodaq_plugins_mockexamples) (`PIDModelBeamSteering`, + `PIDModelMockHeater`, `BayesianModelMock`, `DataMixerModelFit`; for the Sequencer, the choice models of + `pymodaq/extensions/sequencer/models`) and from the page "Models of the extensions" of the PyMoDAQ + documentation, which lists the methods to implement for each kind. Test a model with the mock actuators and + detectors of that package. +- Do not use the old `pid_models` flag nor the word "preset" in new code: a model script that opens the Dashboard loads + an experiment file. + +## 7. Done checklist - [ ] Names (package, files, classes) match and the `template` placeholders are gone. - [ ] The `[features]` flags at the top of `pyproject.toml` match the folders you use (`instruments`, `extensions`, diff --git a/pyproject.toml b/pyproject.toml index b30c619..2db3617 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [features] # defines the plugin features contained into this plugin instruments = true # true if plugin contains instrument classes (else false, notice the lowercase for toml files) extensions = false # true if plugins contains dashboard extensions -models = false # true if plugins contains pid models +models = false # true if plugins contains models of the extensions (PID, optimizers, data mixer, sequencer, ...) h5exporters = false # true if plugin contains custom h5 file exporters scanners = false # true if plugin contains custom scan layout (daq_scan extensions) diff --git a/src/pymodaq_plugins_template/models/PIDModelTemplate.py b/src/pymodaq_plugins_template/models/PIDModelTemplate.py deleted file mode 100644 index 2b2dce8..0000000 --- a/src/pymodaq_plugins_template/models/PIDModelTemplate.py +++ /dev/null @@ -1,100 +0,0 @@ -import numpy as np - -from pymodaq.extensions.pid.utils import PIDModelGeneric, DataToActuatorPID, main -from pymodaq_data.data import DataToExport, DataCalculated - -from pymodaq.utils.data import DataActuator - -from typing import List - - -def some_function_to_convert_the_pid_outputs(outputs: List[float], dt: float, stab=True): - """ Should be replaced here or in the model class to process the outputs """ - return outputs - - -def some_function_to_convert_the_data(measurements: DataToExport): - """ Should be replaced here or in the model class to process the measurement """ - a = 0 - b = 1 - return [a, b] - - -class PIDModelTemplate(PIDModelGeneric): - limits = dict(max=dict(state=False, value=100), - min=dict(state=False, value=-100),) - konstants = dict(kp=0.1, ki=0.000, kd=0.0000) - - Nsetpoints = 2 # number of setpoints - setpoint_ini = [128, 128] # number and values of initial setpoints - setpoints_names = ['Xaxis', 'Yaxis'] # number and names of setpoints - - actuators_name = ["Xpiezo", "Ypiezo"] # names of actuator's control modules involved in the PID - detectors_name = ['Camera'] # names of detector's control modules involved in the PID - - params = [] # list of dict to initialize specific Parameters - - def __init__(self, pid_controller): - super().__init__(pid_controller) - - def update_settings(self, param): - """ - Get a parameter instance whose value has been modified by a user on the UI - Parameters - ---------- - param: (Parameter) instance of Parameter object - """ - if param.name() == '': - pass - - def ini_model(self): - super().ini_model() - - # add here other specifics initialization if needed - - def convert_input(self, measurements: DataToExport): - """ - Convert the measurements in the units to be fed to the PID (same dimensionality as the setpoint) - Parameters - ---------- - measurements: DataToExport - Data from the declared detectors from which the model extract a value of the same units as the setpoint - - Returns - ------- - InputFromDetector: the converted input in the setpoints units - - """ - - x, y = some_function_to_convert_the_data(measurements) - return DataToExport('pid inputs', - data=[DataCalculated('pid calculated', - data=[np.array([x]), - np.array([y])])]) - - def convert_output(self, outputs: List[float], dt: float, stab=True) -> DataToActuatorPID: - """ - Convert the output of the PID in units to be fed into the actuator - Parameters - ---------- - outputs: List of float - output value from the PID from which the model extract a value of the same units as the actuator - dt: float - Ellapsed time since the last call to this function - stab: bool - - Returns - ------- - OutputToActuator: the converted output - - """ - outputs = some_function_to_convert_the_pid_outputs(outputs, dt, stab) - return DataToActuatorPID('pid output', mode='rel', - data=[DataActuator(self.actuators_name[ind], data=outputs[ind]) - for ind in range(len(outputs))]) - - -if __name__ == '__main__': - main("BeamSteeringMockNoModel.xml") # some preset configured with the right actuators and detectors - - diff --git a/src/pymodaq_plugins_template/models/__init__.py b/src/pymodaq_plugins_template/models/__init__.py deleted file mode 100644 index 180d4dd..0000000 --- a/src/pymodaq_plugins_template/models/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created the 01/06/2023 - -@author: Sebastien Weber -"""