12from collections
import namedtuple
15prompt_script_package =
"prompt.calibrations."
16prompt_script_dir =
"calibration/scripts/prompt/calibrations"
18prompt_validation_script_package =
"prompt.validations."
19prompt_validation_script_dir =
"calibration/scripts/prompt/validations"
21INPUT_DATA_FILTERS = {
"Magnet": {
"On":
"On",
24 "Beam Energy": {
"No Beam":
"No Beam",
26 "Continuum":
"Continuum",
29 "Run Type": {
"beam":
"beam",
31 "debug":
"debug",
"null":
"null",
32 "physics":
"physics"},
33 "Data Tag": {
"bhabha_all_calib":
"bhabha_all_calib",
34 "bhabha_combined_calib":
"bhabha_combined_calib",
35 "cosmic_calib":
"cosmic_calib",
36 "gamma_gamma_calib":
"gamma_gamma_calib",
37 "hadron_calib":
"hadron_calib",
38 "btocharm_calib":
"btocharm_calib",
39 "mumu_tight_or_highm_calib":
"mumu_tight_or_highm_calib",
40 "offip_calib":
"offip_calib",
41 "radmumu_calib":
"radmumu_calib",
42 "random_calib":
"random_calib",
43 "delayedbhabha_calib":
"delayedbhabha_calib",
44 "single_gamma_mc":
"single_gamma_mc"},
45 "Data Quality Tag": {
">=30 Minute Run":
">=30 Minute Run",
46 "Bad For Alignment":
"Bad For Alignment",
48 "Good Shifter":
"Good Shifter",
49 "Good For PXD":
"Good For PXD",
50 "Good Or Recoverable":
"Good Or Recoverable",
51 "Good Or Recoverable Shifter":
"Good Or Recoverable Shifter"}
55class CalibrationSettings(namedtuple(
'CalSet_Factory',
65 "produced_payloads"])):
67 Simple class to hold and display required information for a prompt calibration script (process).
70 name (str): The unique calibration name, not longer than 64 characters.
72 expert_username (str): The GitLab username of the expert to contact about this script.
73 This username will be used to assign the default responsible person for submitting and checking prompt
76 subsystem (str): The name of the subsystem that this calibration is for.
77 e.g. "cdc", "ecl", "klm", "svd", "top", "beam", etc.
79 description (str): Long form description of the calibration and what it does. Feel free to make this as long as you need.
81 input_data_formats (frozenset(str)): The data formats {'raw', 'cdst', 'mdst', 'udst'} of the input files
82 that should be used as input to the process. Used to figure out if this calibration should occur
83 before the relevant data production e.g. before cDST files are created.
85 input_data_names (frozenset(str)): The names that you will use when accessing the input data given to the
86 prompt calibration process i.e. Use these in the ``get_calibrations`` function to access the correct input
87 data files. e.g. input_data_names=["all_events", "offres_photon_events"]
89 input_data_filters (dict): The data selection for the data input names, used for automated calibration.
90 The keys should correspond to one of the ``input_data_names`` with the values being a list of the various data
91 filters, e.g. Data Tag, Beam Energy, Run Type, Run Quality Tag and Magnet. All available filters can be found in the
92 input_data_filters dictionary e.g. from prompt import input_data_filters with details about data tags and run quality
93 tags found at: https://calibration.belle2.org/belle2/data_tags/list/.
94 To exclude specific filters, pre-append with *NOT* e.g.
95 {"all_events": ["mumu_tight_or_highm_calib", "hadron_calib", "Good", "On"],
96 "offres_photon_events": ["gamma_gamma_calib", "Good", "NOT On"]}.
97 Not selecting a specific filters (e.g. Magnet) is equivalent to not having any requirements, e.g. (Either)
99 depends_on (list(CalibrationSettings)): The settings variables of the other prompt calibrations that you want
100 want to depend on. This will allow the external automatic system to understand the overall ordering of
101 scripts to run. If you encounter an import error when trying to run your prompt calibration script, it is
102 likely that you have introduced a circular dependency.
104 expert_config (dict): Default expert configuration for this calibration script. This is an optional dictionary
105 (which must be JSON compliant) of configuration options for your get_calibrations(...) function.
106 This is supposed to be used as a catch-all place to send in options for your calibration setup. For example,
107 you may want to have an optional list of IoV boundaries so that your prompt script knows that it should split the
108 input data between different IoV ranges. Or you might want to send if options like the maximum events per
109 input file to process. The value in your settings object will be the *default*, but you can override the value via
110 the caf_config.json sent into ``b2caf-prompt-run``.
112 produced_payloads (list(str)): The names of the payloads that this calibration script produces.
118 allowed_data_formats = frozenset({
"raw",
"cdst",
"mdst",
"udst"})
120 def __new__(cls, name, expert_username, subsystem, description,
121 input_data_formats=None, input_data_names=None, input_data_filters=None,
122 depends_on=None, expert_config=None, produced_payloads=None):
124 The special method to create the tuple instance. Returning the instance
125 calls the __init__ method
128 raise ValueError(
"name cannot be longer than 64 characters!")
129 if not input_data_formats:
130 raise ValueError(
"You must specify at least one input data format")
131 input_data_formats = frozenset(
map(
lambda x: x.lower(), input_data_formats))
132 if input_data_formats.difference(cls.allowed_data_formats):
133 raise ValueError(
"There was a data format that is not in the allowed_data_formats attribute.")
134 if not input_data_names:
135 raise ValueError(
"You must specify at least one input data name")
136 input_data_names = frozenset(input_data_names)
139 if input_data_filters:
140 if set(input_data_filters.keys()) != input_data_names:
141 raise ValueError(
"The 'input_data_filters' keys don't match the 'input_data_names'!")
143 allowed_filters = {filter_name
for category
in INPUT_DATA_FILTERS.values()
for filter_name
in category}
144 requested_filters = {filter_name.replace(
"NOT",
"", 1).lstrip()
for filters
in input_data_filters.values()
145 for filter_name
in filters}
146 if not allowed_filters.issuperset(requested_filters):
147 raise ValueError(
"The 'input_data_filters' contains unknown filter names:"
148 f
"{requested_filters.difference(allowed_filters)}")
150 input_data_filters = {}
153 allowed_subsystems = {
"pxd",
"svd",
"cdc",
"ecl",
"top",
"arich",
154 "klm",
"trigger",
"tracking",
"alignment",
"beam",
"example"}
155 if subsystem
not in allowed_subsystems:
156 raise ValueError(f
"subsystem must be one of {allowed_subsystems}, but got '{subsystem}'")
160 if not isinstance(expert_config, dict):
161 raise TypeError(
"expert_config must be a dictionary")
164 json.dumps(expert_config)
165 except TypeError
as e:
166 basf2.B2ERROR(
"expert_config could not be serialised to JSON. "
167 "Most likely you used a non-supported type e.g. datetime.")
173 for calibration_settings
in depends_on:
174 if not isinstance(calibration_settings, cls):
175 raise TypeError(f
"A list of {str(cls)} object is required when setting the 'depends_on' keyword.")
179 return super().__new__(cls, name, expert_username, subsystem, description,
180 input_data_formats, input_data_names, input_data_filters,
181 depends_on, expert_config, produced_payloads)
183 def json_dumps(self):
186 str: A valid JSON format string of the attributes.
188 depends_on_names = [calibration_settings.name
for calibration_settings
in self.depends_on]
189 return json.dumps({
"name": self.name,
190 "expert_username": self.expert_username,
191 "subsystem": self.subsystem,
192 "input_data_formats": list(self.input_data_formats),
193 "input_data_names": list(self.input_data_names),
194 "input_data_filters": self.input_data_filters,
195 "depends_on": list(depends_on_names),
196 "description": self.description,
197 "expert_config": self.expert_config,
198 "produced_payloads": self.produced_payloads
202 depends_on_names = [calibration_settings.name
for calibration_settings
in self.depends_on]
203 output_str = str(self.__class__.__name__) + f
"(name='{self.name}'):\n"
204 output_str += f
" expert_username='{self.expert_username}'\n"
205 output_str += f
" subsystem='{self.subsystem}'\n"
206 output_str += f
" input_data_formats={list(self.input_data_formats)}\n"
207 output_str += f
" input_data_names={list(self.input_data_names)}\n"
208 output_str += f
" input_data_filters={list(self.input_data_filters)}\n"
209 output_str += f
" depends_on={list(depends_on_names)}\n"
210 output_str += f
" description='{self.description}'\n"
211 output_str += f
" expert_config={self.expert_config}\n"
212 output_str += f
" produced_payloads={self.produced_payloads}"
216class ValidationSettings(namedtuple(
'ValSet_Factory', [
"name",
"description",
"download_files",
"expert_config"])):
218 Simple class to hold and display required information for a validation calibration script (process).
221 name (str): The unique name that must match the corresponding calibration, not longer than 64 characters.
223 description (str): Long form description of the validation and what it does. Feel free to make this as long as you need.
225 download_files (list): The names of the files you want downloaded, e.g. mycalibration_stdout. If multiple files of
226 the same name are found, all files are downloaded and appended with the folder they were in.
228 expert_config (dict): Default expert configuration for this validation script. This is an optional dictionary
229 (which must be JSON compliant) of configuration options for validation script.
230 This is supposed to be used as a catch-all place to send in options for your calibration setup. For example,
231 you may want to have an optional list of IoV boundaries so that your validation script knows that it should split the
232 input data between different IoV ranges. Or you might want to send if options like the maximum events per
233 input file to process. The value in your settings object will be the *default*, but you can override the value via
234 the caf_config.json sent into ``b2caf-prompt-run``.
237 def __new__(cls, name, description, download_files=None, expert_config=None):
239 The special method to create the tuple instance. Returning the instance
240 calls the __init__ method
243 raise ValueError(
"name cannot be longer than 64 characters!")
247 if not isinstance(expert_config, dict):
248 raise TypeError(
"expert_config must be a dictionary")
251 json.dumps(expert_config)
252 except TypeError
as e:
253 basf2.B2ERROR(
"expert_config could not be serialised to JSON. "
254 "Most likely you used a non-supported type e.g. datetime.")
259 return super().__new__(cls, name, description, download_files, expert_config)
261 def json_dumps(self):
264 str: A valid JSON format string of the attributes.
266 return json.dumps({
"name": self.name,
267 "description": self.description,
268 "download_files": self.download_files,
269 "expert_config": self.expert_config
273 output_str = str(self.__class__.__name__) + f
"(name='{self.name}'):\n"
274 output_str += f
" description='{self.description}'\n"
275 output_str += f
" download_files='{self.download_files}'\n"
276 output_str += f
" expert_config={self.expert_config}"