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 "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",
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"}
54class CalibrationSettings(namedtuple(
'CalSet_Factory', [
"name",
63 "produced_payloads"])):
65 Simple class to hold and
display required information for a
prompt calibration script (process).
68 name (str): The unique calibration name,
not longer than 64 characters.
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
74 subsystem (str): The name of the subsystem that this calibration
is for.
75 e.g.
"cdc",
"ecl",
"klm",
"svd",
"top",
"beam", etc.
77 description (str): Long form description of the calibration
and what it does. Feel free to make this
as long
as you need.
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.
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"]
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)
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.
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``.
110 produced_payloads (list(str)): The names of the payloads that this calibration script produces.
116 allowed_data_formats = frozenset({"raw",
"cdst",
"mdst",
"udst"})
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):
122 The special method to create the tuple instance. Returning the instance
123 calls the __init__ method
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)
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'!")
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)}")
148 input_data_filters = {}
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}'")
158 if not isinstance(expert_config, dict):
159 raise TypeError(
"expert_config must be a dictionary")
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.")
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.")
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)
181 def json_dumps(self):
184 str: A valid JSON format string of the attributes.
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
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}"
214class ValidationSettings(namedtuple(
'ValSet_Factory', [
"name",
"description",
"download_files",
"expert_config"])):
216 Simple class to hold and
display required information for a
validation calibration script (process).
219 name (str): The unique name that must match the corresponding calibration,
not longer than 64 characters.
221 description (str): Long form description of the validation
and what it does. Feel free to make this
as long
as you need.
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.
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``.
235 def __new__(cls, name, description, download_files=None, expert_config=None):
237 The special method to create the tuple instance. Returning the instance
238 calls the __init__ method
241 raise ValueError(
"name cannot be longer than 64 characters!")
245 if not isinstance(expert_config, dict):
246 raise TypeError(
"expert_config must be a dictionary")
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.")
257 return super().__new__(cls, name, description, download_files, expert_config)
259 def json_dumps(self):
262 str: A valid JSON format string of the attributes.
264 return json.dumps({
"name": self.name,
265 "description": self.description,
266 "download_files": self.download_files,
267 "expert_config": self.expert_config
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}"