Writing a new Calibration ========================= Writing a new calibration is relatively simple - start by adding a new python module in the ``./module_testing/calibrations/`` directory, with name matching the command name you want the new calibration to have. The calibration will then be called using:: dirigent Imports in this file will need to include at minimum the ``click`` and ``logging`` libraries, and likely some decorators from the ``module_testing.dirigent.dirigent_cli`` package:: import click import logging from module_testing.dirigent.dirigent_cli import request_calibration_context, with_calibration_context # Initialise local logger - use logger.info(...), logger.warning(...) etc. for log/debug messages logger = logging.getLogger(__name__) Next, you need to add the main click command for the calibration:: @click.command('', help='') # Extra arguments/options go here @request_calibration_context @with_calibration_context def (context, cli_ctx, ): # Calibration body goes here... If you do not need a CalibrationContext object (i.e. for instrument control, etc) you can leave away the `@request_calibration_context` and `@with_calibration_context` decorators. Adding Configuration Parameters ------------------------------- Add additional configuration/command line arguments/options as click arguments and options to this command. For example, to add a mandatory module name and an optional numeric argument:: @click.command('', help='') @click.option('-n', '--numeric-arg', metavar='NUMBER', type=float, default=1.0, show_default=True, help='A numeric (floating-point) argument.') @request_calibration_context @with_calibration_context def (context, cli_ctx, module, numeric_arg): # Calibration body goes here... Default values for these arguments and options should be provided. Values can then be modified either using the relevant command line flags, or by setting values for these in the ``[]`` section of the ``dirigent.toml`` or config file. You can add the following lines to the start of your calibration function to print all calibration-specific parameter values on start of calibration:: logger.info('===== parameter values as configured: =====') for param, value in cli_ctx.params.items(): logger.info(f'{param} = {value}') logger.info('===========================================') Accessing Instruments --------------------- The ``InstrumentCluster`` associated with your ``CalibrationContext`` can be accessed via ``context.instruments``. Some examples for turning on/off instruments:: # Turn only LV on/off context.instruments.lv_on() context.instruments.lv_off() # Turn both LV and HV on/off in correct order context.instruments.on() context.instruments.off() # Setting the voltage of the HV supply to certain level: context.instruments.hv_set(voltage=) Further possibilities can be either seen in the `icicle.instrument_cluster` class or in other calibrations. Finally, don't forget to turn off everything on completion of the calibration:: context.instruments.off() Calling ``Ph2ACF/CMSITminiDAQ`` ------------------------------- The ``context.run_calibration(...)`` and ``context.reset_ddr_reset(...)`` calls are the main methods to start a ``CMSITminiDAQ`` instance - in both cases a new XTerm window will open for the calibration or DDR reset to run in:: # Example - reset DDR as first step (with all auxiliary commands too) logger.info('-1: Resetting board') context.instruments.lv_on() context.run_ddr_reset() time.sleep(1) context.instruments.lv_off() logger.info('0: Turning on Instruments') context.instruments.on() # Example - run SCurve logger.info('1: SCurve') context.run_calibration('scurve') context.instruments.off() Alternatively to :any:`calibration_context.CalibrationContext.run_calibration`, there are functions prepared that change the important registers for a scan (e.g. ``InjType = 0`` for noise scan) and execute the scan when the configurations are set. This way, not all the configurations have to be defined in the scan itself. To import these functions use:: from module_testing/config/performable_scans import * A list of available functions can be seen in the :any:`performable_scans` api With ``count``, a number can be printed indicating the step at which the program is currently. Note, that :any:`performable_scans.InjectionDelay()` is only fully supported by a Ph2_ACF version NEWER than v4-13. This, because the ``TriggerConfig`` and ``CAL_EDGE_FINE_DELAY`` are not updated to the ``CMSIT_RD53A/B.txt`` file in older versions (this is where dirigent then copies the values from). Another option to run scans is the :any:`performable_scans.Scan` function also located in :any:`config.performable_scans`. This function will accept dictionaries for 3 different configuration sections: `RD53.Settings`, `Global` and `Settings` (the last section being the scan settings). It will then update the config file with these settings and run a scan. Depending on a setting `write_xml`, the config file will then be updated with the findings from this scan. Usage of the Scan() function can be found in :any:`calibrations.calibrate_module`:: Scan(logger, context, 'pixelalive', True, FESettings_DictB['PixelAlive'], globalSettings_DictB['PixelAlive'], HWSettings_DictB['PixelAlive_Mask'], count='00') **Note** This function is especially designed to be used with dictionaries from the submodule :any:`deps.inner_tracker_tests`. Modifying ``CMSIT.xml`` or ``CMSIT_RD53_ROCn.txt`` config parameters -------------------------------------------------------------------- The current active config may be accessed via ``context.calibration_set.config``, but must be opened using a ``with ...:`` statement to load the current config file states. The files are saved and closed on end of the ``with`` statement, and are then ready for ``Ph2ACF/CMSITminiDAQ`` to be called. For example, config changes for a noise scan:: with context.calibration_set.config as detector: detector.Settings.Setting('nEvents').set('1e6') detector.Settings.Setting('nEvtsBurst').set('1e3') detector.Settings.Setting('nTRIGxEvent').set(10) detector.Settings.Setting('INJtype').set(0) detector.Settings.Setting('nClkDelays').set(50) detector.Settings.Setting('TargetOcc').set(target_occupancy_noise) logger.info('1: Noise') context.run_calibration('noise') Global settings are accessed under ``detector.Settings.Setting('')``, with `get()` and `set(value)` methods available. Individual ``BeBoards``, ``OpticalGroups`` and ``Hybrids`` may be accessed either collectively (addressing all), or individually by ID (examples only for RD53A. RD53B has similar commands without functions to enable specific front-end types (linear, differential, synchronous...):: # Enable linear frontends on all Hybrids detector.BeBoard().OpticalGroup().Hybrid().enable_linear() # Set Vthreshold_LIN only on all chips on all hybrids on BeBoard 0, OpticalGroup 1 detector.BeBoard(0).OpticalGroup(1).Hybrid().RD53A().Settings.set('Vthreshold_LIN', str(starting_threshold)) # Read latency on Chip with ID 6 of Hybrid 2 of all BeBoards and OpticalGroups detector.BeBoard().OpticalGroup().Hybrid(2).RD53A(6).Settings.get('LATENCY_CONFIG') Register overrides are accessed via the ``Settings`` property on the ``RD53A``/``RD53B`` chip config object, as shown above. Register values and masks in the text config file are accessed and updated either using the :any:`RD53BConfig.patch_from_text`/:any:`RD53BConfig.patch_from_xml` methods to copy values, or directly using the `RD53B.text_file_object` of the :any:`RD53AConfig`/:any:`RD53BConfig` chip config object (The same functions exist for RD53A and RD53B):: # Update latency config from results of last latency calibration on all chips detector.BeBoard().OpticalGroup().Hybrid().RD53A().patch_from_text('LATENCY_CONFIG') # Reset TDAC mask for all chips from module_testing.config.calibration_config import RD53TextConfig detector.BeBoard().OpticalGroup().Hybrid().RD53A().rd53_text_config.set('TDAC', RD53TextConfig.DEFAULT_TDAC()) Default masks, mask generators, and mask operators are provided as static methods on the ``RD53TextConfig`` class, which can be imported from ``module_testing.config.calibration_config``. This feature is currently fully supported for RD53A. For RD53B the options for mask manipulation are limited. Due to the single front-end, it is also less important. Queueing saves ---------------- Results form Ph2_ACF scans and monitoring files are saved automatically in the save folder. By default, this folder is called ``Results_dirigent/``. To prevent older runs to be overwritten, runs with the same name are moved by adding a timestamp to their folder name ``Results_dirigent/.``. Additional files, such as ``.csv`` files may be added through the command:: context.calibration_set.queue_script_result(,,
,) There is also a support for generating and saving plots automatically. For this, use :any:`CalibrationSet.queue_plot`:: context.calibration_set.queue_plot(folder, name, x_data, y_data, axes, labels, errors=[]) Uploading using Felis --------------------- If the `felis` option in `dirigent.toml` is activated, the modules are automatically registered and the `Ph2_ACF` scans automatically call felis to make a summary. The only maual thing that is required, is to upload the results in the end. This can be done using the following command:: context.felis.do_upload() This will, when executed ask you for your `panthera` credentials.