Belle II Software release-09-00-02
__init__.py
1# disable doxygen check for this file
2# @cond
3
4
11import basf2
12from collections import namedtuple
13import json
14
15prompt_script_package = "prompt.calibrations."
16prompt_script_dir = "calibration/scripts/prompt/calibrations"
17
18prompt_validation_script_package = "prompt.validations."
19prompt_validation_script_dir = "calibration/scripts/prompt/validations"
20
21INPUT_DATA_FILTERS = {"Magnet": {"On": "On",
22 "Off": "Off",
23 "Either": "Either"},
24 "Beam Energy": {"No Beam": "No Beam",
25 "4S": "4S",
26 "Continuum": "Continuum",
27 "": "",
28 "Scan": "Scan"},
29 "Run Type": {"beam": "beam",
30 "cosmic": "cosmic",
31 "debug": "debug", "null": "null",
32 "physics": "physics"},
33 "Data Tag": {"bhabha_all_calib": "bhabha_all_calib",
34 "cosmic_calib": "cosmic_calib",
35 "gamma_gamma_calib": "gamma_gamma_calib",
36 "hadron_calib": "hadron_calib",
37 "btocharm_calib": "btocharm_calib",
38 "mumu_tight_or_highm_calib": "mumu_tight_or_highm_calib",
39 "offip_calib": "offip_calib",
40 "radmumu_calib": "radmumu_calib",
41 "random_calib": "random_calib",
42 "delayedbhabha_calib": "delayedbhabha_calib",
43 "single_gamma_mc": "single_gamma_mc"},
44 "Data Quality Tag": {">=30 Minute Run": ">=30 Minute Run",
45 "Bad For Alignment": "Bad For Alignment",
46 "Good": "Good",
47 "Good Shifter": "Good Shifter",
48 "Good For PXD": "Good For PXD",
49 "Good Or Recoverable": "Good Or Recoverable",
50 "Good Or Recoverable Shifter": "Good Or Recoverable Shifter"}
51 }
52
53
54class CalibrationSettings(namedtuple('CalSet_Factory', ["name",
55 "expert_username",
56 "subsystem",
57 "description",
58 "input_data_formats",
59 "input_data_names",
60 "input_data_filters",
61 "depends_on",
62 "expert_config",
63 "produced_payloads"])):
64 """
65 Simple class to hold and display required information for a prompt calibration script (process).
66
67 Parameters:
68 name (str): The unique calibration name, not longer than 64 characters.
69
70 expert_username (str): The GitLab username of the expert to contact about this script.
71 This username will be used to assign the default responsible person for submitting and checking prompt
72 calibration jobs.
73
74 subsystem (str): The name of the subsystem that this calibration is for.
75 e.g. "cdc", "ecl", "klm", "svd", "top", "beam", etc.
76
77 description (str): Long form description of the calibration and what it does. Feel free to make this as long as you need.
78
79 input_data_formats (frozenset(str)): The data formats {'raw', 'cdst', 'mdst', 'udst'} of the input files
80 that should be used as input to the process. Used to figure out if this calibration should occur
81 before the relevant data production e.g. before cDST files are created.
82
83 input_data_names (frozenset(str)): The names that you will use when accessing the input data given to the
84 prompt calibration process i.e. Use these in the ``get_calibrations`` function to access the correct input
85 data files. e.g. input_data_names=["all_events", "offres_photon_events"]
86
87 input_data_filters (dict): The data selection for the data input names, used for automated calibration.
88 The keys should correspond to one of the ``input_data_names`` with the values being a list of the various data
89 filters, e.g. Data Tag, Beam Energy, Run Type, Run Quality Tag and Magnet. All available filters can be found in the
90 input_data_filters dictionary e.g. from prompt import input_data_filters with details about data tags and run quality
91 tags found at: https://calibration.belle2.org/belle2/data_tags/list/.
92 To exclude specific filters, pre-append with *NOT* e.g.
93 {"all_events": ["mumu_tight_or_highm_calib", "hadron_calib", "Good", "On"],
94 "offres_photon_events": ["gamma_gamma_calib", "Good", "NOT On"]}.
95 Not selecting a specfic filters (e.g. Magnet) is equivalent to not having any requirements, e.g. (Either)
96
97 depends_on (list(CalibrationSettings)): The settings variables of the other prompt calibrations that you want
98 want to depend on. This will allow the external automatic system to understand the overall ordering of
99 scripts to run. If you encounter an import error when trying to run your prompt calibration script, it is
100 likely that you have introduced a circular dependency.
101
102 expert_config (dict): Default expert configuration for this calibration script. This is an optional dictionary
103 (which must be JSON compliant) of configuration options for your get_calibrations(...) function.
104 This is supposed to be used as a catch-all place to send in options for your calibration setup. For example,
105 you may want to have an optional list of IoV boundaries so that your prompt script knows that it should split the
106 input data between different IoV ranges. Or you might want to send if options like the maximum events per
107 input file to process. The value in your settings object will be the *default*, but you can override the value via
108 the caf_config.json sent into ``b2caf-prompt-run``.
109
110 produced_payloads (list(str)): The names of the payloads that this calibration script produces.
111 """
112
113
116 allowed_data_formats = frozenset({"raw", "cdst", "mdst", "udst"})
117
118 def __new__(cls, name, expert_username, subsystem, description,
119 input_data_formats=None, input_data_names=None, input_data_filters=None,
120 depends_on=None, expert_config=None, produced_payloads=None):
121 """
122 The special method to create the tuple instance. Returning the instance
123 calls the __init__ method
124 """
125 if len(name) > 64:
126 raise ValueError("name cannot be longer than 64 characters!")
127 if not input_data_formats:
128 raise ValueError("You must specify at least one input data format")
129 input_data_formats = frozenset(map(lambda x: x.lower(), input_data_formats))
130 if input_data_formats.difference(cls.allowed_data_formats):
131 raise ValueError("There was a data format that is not in the allowed_data_formats attribute.")
132 if not input_data_names:
133 raise ValueError("You must specify at least one input data name")
134 input_data_names = frozenset(input_data_names)
135
136 # The input data names in the filters MUST correspond to the input data names for the calibration.
137 if input_data_filters:
138 if set(input_data_filters.keys()) != input_data_names:
139 raise ValueError("The 'input_data_filters' keys don't match the 'input_data_names'!")
140 # Requested input data filters MUST exist in the ones we defined in the global dictionary.
141 allowed_filters = {filter_name for category in INPUT_DATA_FILTERS.values() for filter_name in category}
142 requested_filters = {filter_name.replace("NOT", "", 1).lstrip() for filters in input_data_filters.values()
143 for filter_name in filters}
144 if not allowed_filters.issuperset(requested_filters):
145 raise ValueError("The 'input_data_filters' contains unknown filter names:"
146 f"{requested_filters.difference(allowed_filters)}")
147 else:
148 input_data_filters = {}
149
150 # Check that the subsystem is among the allowed ones
151 allowed_subsystems = {"pxd", "svd", "cdc", "ecl", "top", "arich",
152 "klm", "trigger", "tracking", "alignment", "beam", "example"}
153 if subsystem not in allowed_subsystems:
154 raise ValueError(f"subsystem must be one of {allowed_subsystems}, but got '{subsystem}'")
155
156 if expert_config:
157 # Check that it's a dictionary and not some other valid JSON object
158 if not isinstance(expert_config, dict):
159 raise TypeError("expert_config must be a dictionary")
160 # Check if it is JSONable since people might put objects in there by mistake
161 try:
162 json.dumps(expert_config)
163 except TypeError as e:
164 basf2.B2ERROR("expert_config could not be serialised to JSON. "
165 "Most likely you used a non-supported type e.g. datetime.")
166 raise e
167 else:
168 expert_config = {}
169
170 if depends_on:
171 for calibration_settings in depends_on:
172 if not isinstance(calibration_settings, cls):
173 raise TypeError(f"A list of {str(cls)} object is required when setting the 'depends_on' keyword.")
174 else:
175 depends_on = []
176
177 return super().__new__(cls, name, expert_username, subsystem, description,
178 input_data_formats, input_data_names, input_data_filters,
179 depends_on, expert_config, produced_payloads)
180
181 def json_dumps(self):
182 """
183 Returns:
184 str: A valid JSON format string of the attributes.
185 """
186 depends_on_names = [calibration_settings.name for calibration_settings in self.depends_on]
187 return json.dumps({"name": self.name,
188 "expert_username": self.expert_username,
189 "subsystem": self.subsystem,
190 "input_data_formats": list(self.input_data_formats),
191 "input_data_names": list(self.input_data_names),
192 "input_data_filters": self.input_data_filters,
193 "depends_on": list(depends_on_names),
194 "description": self.description,
195 "expert_config": self.expert_config,
196 "produced_payloads": self.produced_payloads
197 })
198
199 def __str__(self):
200 depends_on_names = [calibration_settings.name for calibration_settings in self.depends_on]
201 output_str = str(self.__class__.__name__) + f"(name='{self.name}'):\n"
202 output_str += f" expert_username='{self.expert_username}'\n"
203 output_str += f" subsystem='{self.subsystem}'\n"
204 output_str += f" input_data_formats={list(self.input_data_formats)}\n"
205 output_str += f" input_data_names={list(self.input_data_names)}\n"
206 output_str += f" input_data_filters={list(self.input_data_filters)}\n"
207 output_str += f" depends_on={list(depends_on_names)}\n"
208 output_str += f" description='{self.description}'\n"
209 output_str += f" expert_config={self.expert_config}\n"
210 output_str += f" produced_payloads={self.produced_payloads}"
211 return output_str
212
213
214class ValidationSettings(namedtuple('ValSet_Factory', ["name", "description", "download_files", "expert_config"])):
215 """
216 Simple class to hold and display required information for a validation calibration script (process).
217
218 Parameters:
219 name (str): The unique name that must match the corresponding calibration, not longer than 64 characters.
220
221 description (str): Long form description of the validation and what it does. Feel free to make this as long as you need.
222
223 download_files (list): The names of the files you want downloaded, e.g. mycalibration_stdout. If multiple files of
224 the same name are found, all files are downloaded and appended with the folder they were in.
225
226 expert_config (dict): Default expert configuration for this validation script. This is an optional dictionary
227 (which must be JSON compliant) of configuration options for validation script.
228 This is supposed to be used as a catch-all place to send in options for your calibration setup. For example,
229 you may want to have an optional list of IoV boundaries so that your validation script knows that it should split the
230 input data between different IoV ranges. Or you might want to send if options like the maximum events per
231 input file to process. The value in your settings object will be the *default*, but you can override the value via
232 the caf_config.json sent into ``b2caf-prompt-run``.
233 """
234
235 def __new__(cls, name, description, download_files=None, expert_config=None):
236 """
237 The special method to create the tuple instance. Returning the instance
238 calls the __init__ method
239 """
240 if len(name) > 64:
241 raise ValueError("name cannot be longer than 64 characters!")
242
243 if expert_config:
244 # Check that it's a dictionary and not some other valid JSON object
245 if not isinstance(expert_config, dict):
246 raise TypeError("expert_config must be a dictionary")
247 # Check if it is JSONable since people might put objects in there by mistake
248 try:
249 json.dumps(expert_config)
250 except TypeError as e:
251 basf2.B2ERROR("expert_config could not be serialised to JSON. "
252 "Most likely you used a non-supported type e.g. datetime.")
253 raise e
254 else:
255 expert_config = {}
256
257 return super().__new__(cls, name, description, download_files, expert_config)
258
259 def json_dumps(self):
260 """
261 Returns:
262 str: A valid JSON format string of the attributes.
263 """
264 return json.dumps({"name": self.name,
265 "description": self.description,
266 "download_files": self.download_files,
267 "expert_config": self.expert_config
268 })
269
270 def __str__(self):
271 output_str = str(self.__class__.__name__) + f"(name='{self.name}'):\n"
272 output_str += f" description='{self.description}'\n"
273 output_str += f" download_files='{self.download_files}'\n"
274 output_str += f" expert_config={self.expert_config}"
275 return output_str
276
277# @endcond
278