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