103 unique_tag_info_values = True, ignoreNonExistingLocalFiles=False, normalize_type_names = False):
104 """
105 This tool is independent of Athena framework and returns the metadata from a given file.
106 :param filenames: the input file from which metadata needs to be extracted.
107 :param file_type: the type of file. POOL or BS (bytestream: RAW, DRAW) files.
108 :param mode: if true, will return all metadata associated with the filename. By default, is false and this will
109 return a "tiny" version which have only the following keys: 'file_guid', 'file_size', 'file_type', 'nentries'.
110 :param normalize_type_names: report the RNTuple item types using the class names of the equivalent TTree branches,
111 and the TTree item names using the names of the equivalent RNTuple fields, with the items sorted, e.g. to compare
112 the content of RNTuple and TTree files.
113 :return: a dictionary of metadata for the given input file.
114 """
115
116
117 global _gbl_mode, _gbl_run
118 _gbl_mode = mode
119 _gbl_run = None
120
121 from RootUtils import PyROOTFixes
122
123
124 if isinstance(filenames, str):
125 filenames = [filenames]
126
127
128 if file_type is not None:
129 if file_type not in ('POOL', 'BS'):
130 raise NameError('Allowed values for \'file_type\' parameter are: "POOL" or "BS": you provided "' + file_type + '"')
131 else:
132 msg.info('Forced file_type: {0}'.format(file_type))
133
134
135 if mode not in ('tiny', 'lite', 'full', 'peeker', 'iov'):
136 raise NameError('Allowed values for "mode" parameter are: "tiny", "lite", "peeker", "iov" or "full"')
137
138 if meta_key_filter is None:
139 meta_key_filter = []
140
141
142 if not isGaudiEnv():
143 if mode in ('full', 'iov'):
144 raise NameError('The following modes are not available in AnalysisBase: "iov" and "full"')
145
146 msg.info('Current mode used: {0}'.format(mode))
147 msg.info('Current filenames: {0}'.format(filenames))
148
149 if mode != 'full' and mode !='iov' and len(meta_key_filter) > 0:
150 raise NameError('It is possible to use the meta_key_filter option only for full mode')
151 if meta_key_filter:
152 msg.info('Filter used: {0}'.format(meta_key_filter))
153
154
155 meta_dict = {}
156
157
158 for filename in filenames:
159 meta_dict[filename] = {}
160 current_file_type = None
161
162 _gbl_run = None
163
164 if not file_type:
165 if os.path.isfile(filename):
166
167 if ignoreNonExistingLocalFiles and not regex_URI_scheme.match(filename) and gSystem.AccessPathName(filename):
168 msg.warning('Ignoring not accessible file: {}'.format(filename))
169 continue
170
171 with open(filename, 'rb') as binary_file:
172 magic_file = binary_file.read(4)
173
174 if magic_file == 'root' or magic_file == b'root':
175 current_file_type = 'POOL'
176 meta_dict[filename]['file_type'] = 'POOL'
177
178 elif Project.determine() in (
179 Project.AnalysisBase, Project.AthAnalysis):
180 raise RuntimeError(
181 f"{filename} is not a ROOT file, assumed bytestream"
182 ", this is not supported in Analysis releases")
183 else:
184 current_file_type = 'BS'
185 meta_dict[filename]['file_type'] = 'BS'
186
187
188 meta_dict[filename]['file_size'] = os.path.getsize(filename)
189
190
191 else:
192 if regex_BS_files.match(filename):
193 current_file_type = 'BS'
194 meta_dict[filename]['file_type'] = 'BS'
195 else:
196 current_file_type = 'POOL'
197 meta_dict[filename]['file_type'] = 'POOL'
198
199
200 meta_dict[filename]['file_size'] = None
201
202 else:
203 current_file_type = file_type
204
205
206 if current_file_type == 'POOL':
207
208 if ignoreNonExistingLocalFiles and not regex_URI_scheme.match(filename) and gSystem.AccessPathName(filename):
209 msg.warning('Ignoring not accessible file: {}'.format(filename))
210 continue
211
212 import ROOT
213
214 current_file = ROOT.TFile.Open( _get_pfn(filename) )
215
216
217 from PyUtils.PoolFile import PoolOpts
218 collectionTree = current_file.Get(PoolOpts.TTreeNames.EventData)
219 if isinstance(collectionTree, ROOT.TTree):
220 meta_dict[filename]['auto_flush'] = collectionTree.GetAutoFlush()
221
222
223 meta_dict[filename]['file_guid'] = _read_guid(filename)
224
225
226 meta_dict[filename]['file_comp_alg'] = current_file.GetCompressionAlgorithm()
227 meta_dict[filename]['file_comp_level'] = current_file.GetCompressionLevel()
228
229 if (
230 isRNTuple(md:=current_file.Get(PoolOpts.RNTupleNames.MetaData))
231 and mode != "tiny"
232 ):
233 meta_dict[filename]["metadata_items"] = {}
234
235
236
237 descriptor_reader = ROOT.RNTupleReader.Open(md)
238 descriptor = descriptor_reader.GetDescriptor()
239 auxes = {}
240 classes_with_aux = {
241 "xAOD::FileMetaData_v1",
242 "xAOD::FileMetaDataAuxInfo_v1",
243 "xAOD::TriggerMenuAuxContainer_v1",
244 "DataVector<xAOD::TriggerMenu_v1>",
245 "xAOD::TriggerMenuJsonAuxContainer_v1",
246 "DataVector<xAOD::TriggerMenuJson_v1>",
247 "xAOD::TruthMetaDataAuxContainer_v1",
248 "DataVector<xAOD::TruthMetaData_v1>",
249 "xAOD::CutBookkeeperContainer_v1",
250 "xAOD::CutBookkeeperAuxContainer_v1",
251 "xAOD::LumiBlockRangeAuxContainer_v1",
252 "DataVector<xAOD::LumiBlockRange_v1>",
253 }
254
255
256 dynamic_fmd_fields = {}
257
258 selected_fields = {}
259
260 meta_filter = get_meta_filter(mode, meta_key_filter)
261
262 for field in descriptor.GetTopLevelFields():
263 normalizedName = field.GetFieldName()
264 if "index_ref" in normalizedName:
265 continue
266 if regexIOVMetaDataContainer.match(field.GetTypeName()):
267
268
269 normalizedName = (
270 field.GetFieldName()
271 .
replace(
"IOVMetaDataContainer_p1_",
"")
273 )
274 meta_dict[filename]["metadata_items"][normalizedName] = (
275 "IOVMetaDataContainer"
276 )
277 elif regexByteStreamMetadataContainer.match(field.GetTypeName()):
278 meta_dict[filename]["metadata_items"][field.GetFieldName()] = (
279 "ByteStreamMetadataContainer"
280 )
281 elif regexEventStreamInfo.match(field.GetTypeName()):
282 meta_dict[filename]["metadata_items"][field.GetFieldName()] = (
283 "EventStreamInfo"
284 )
285 elif regexXAODFileMetaData.match(field.GetTypeName()):
286 meta_dict[filename]["metadata_items"][
287 field.GetFieldName().
replace(
"xAOD__",
"xAOD::")
288 ] = field.GetTypeName()
289 elif regexXAODFileMetaDataAuxDyn.match(
290 normalizedName := field.GetFieldName()
293 ):
294 dynamic_fmd_fields[normalizedName.split(".")[1]] = (
295 field.GetFieldName()
296 )
297 meta_dict[filename]["metadata_items"][normalizedName] = (
298 field.GetTypeName()
299 )
300 continue
301 elif regexXAODFileMetaDataAux.match(field.GetTypeName()):
302 meta_dict[filename]["metadata_items"][
303 field.GetFieldName()
306 ] = field.GetTypeName()
307 elif regexXAODTruthMetaData.match(field.GetTypeName()):
308 meta_dict[filename]["metadata_items"][
309 field.GetFieldName()
311 .
replace(
"DataVector_",
"DataVector<")
313 ] = "TruthMetaData"
314 elif regexXAODTruthMetaDataAux.match(field.GetTypeName()):
315 meta_dict[filename]["metadata_items"][
316 field.GetFieldName()
319 ] = field.GetTypeName()
320 elif regexXAODEventFormat.match(field.GetTypeName()):
321 meta_dict[filename]["metadata_items"][
322 field.GetFieldName().
replace(
"xAOD__",
"xAOD::")
323 ] = field.GetTypeName()
324 elif regexXAODTriggerMenuJson.match(field.GetTypeName()):
325 meta_dict[filename]["metadata_items"][
326 field.GetFieldName()
328 .
replace(
"DataVector_",
"DataVector<")
329 .
replace(
"__Trigger",
">_Trigger")
330 ] = field.GetTypeName()
331 elif regexXAODTriggerMenuJsonAux.match(field.GetTypeName()):
332 meta_dict[filename]["metadata_items"][
333 field.GetFieldName()
336 ] = field.GetTypeName()
337 elif regexXAODCutBookkeeperContainer.match(field.GetTypeName()):
338 meta_dict[filename]["metadata_items"][
339 field.GetFieldName()
341 .
replace(
"DataVector_",
"DataVector<")
342 .
replace(
"__CutBookkeeper",
">_CutBookkeeper")
343 ] = field.GetTypeName()
344 elif regexXAODCutBookkeeperContainerAux.match(field.GetTypeName()):
345 meta_dict[filename]["metadata_items"][
346 field.GetFieldName()
349 ] = field.GetTypeName()
350 else:
351 meta_dict[filename]["metadata_items"][
352 field.GetFieldName().
replace(
"Aux:",
"Aux.")
353 ] = field.GetTypeName()
354
355 if field.GetTypeName() in classes_with_aux:
356
357 auxes[field.GetFieldName()] = field.GetTypeName()
358 continue
359
360 if not should_keep_meta(
361 normalizedName, field.GetTypeName(), meta_filter
362 ):
363 continue
364
365 selected_fields[normalizedName] = (
366 field.GetFieldName(),
367 field.GetTypeName(),
368 )
369
370 meta_dict[filename]["metadata_items"] = denormalize_metadata_types(
371 meta_dict[filename]["metadata_items"]
372 )
373
374 def _get_aux_base(aux_key: str) -> str:
375
376 key = aux_key
377 key = key.replace("xAOD__TriggerMenuJsonAuxContainer_v1_", "")
378 key = key.replace("xAOD__TriggerMenuAuxContainer_v1_", "")
379 key = key.replace("xAOD__FileMetaDataAuxInfo_v1_", "")
380 key = key.replace("xAOD__TruthMetaDataAuxContainer_v1_", "")
381
382 if key.endswith("Aux:"):
383 key = key[:-4]
384 elif key.endswith("Aux"):
385 key = key[:-3]
386
387 key = key.strip("_:")
388 return key
389
390 def _get_main_base(main_key: str) -> str:
391 main_base = main_key
392
393 if main_key.startswith("DataVector_xAOD__TriggerMenuJson_v1__"):
394 main_base = main_key.replace(
395 "DataVector_xAOD__TriggerMenuJson_v1__", ""
396 )
397 elif main_key.startswith("DataVector_xAOD__TriggerMenu_v1__"):
398 main_base = main_key.replace(
399 "DataVector_xAOD__TriggerMenu_v1__", ""
400 )
401
402 elif main_key.startswith("xAOD__FileMetaData_v1_"):
403 main_base = main_key.replace("xAOD__FileMetaData_v1_", "")
404
405 elif main_key.startswith("DataVector_xAOD__TruthMetaData_v1__"):
406 main_base = main_key.replace(
407 "DataVector_xAOD__TruthMetaData_v1__", ""
408 )
409 return main_base
410
411 def _find_associated_pairs(auxes: dict) -> list[tuple[str, str]]:
412
413 aux_map = {}
414 for k in auxes:
415 if "Aux" in k:
416 aux_map[_get_aux_base(k)] = k
417
418 main_map = {}
419 for k in auxes:
420 base = _get_main_base(k)
421 if base:
422 main_map[base] = k
423
424
425 pairs = []
426 for base, aux_key in aux_map.items():
427 if base in main_map:
428 pairs.append((aux_key, main_map[base]))
429 return pairs
430
431 def _keep_pair(
432 pair: tuple[str, str], auxes: dict, meta_filter: dict
433 ) -> bool:
434 key = next(
435 (
436 k
437 for k, v in trigger_menu_json_map.items()
438 if v
439 == pair[1]
441 .
replace(
"DataVector_",
"DataVector<")
442 .
replace(
"__Trigger",
">_Trigger")
443 ),
444 auxes[pair[0]],
445 )
446
447 try:
448 key = (
449 key.replace("xAOD__", "xAOD::")
450 if key.count("_") <= 1
451 else key.replace("xAOD__", "xAOD::").rsplit("_", 2)[0]
452 )
453 except IndexError:
454 pass
455
456 return should_keep_meta(
457 pair[0]
459 .
replace(
"DataVector_",
"DataVector<")
460 .
replace(
"__Trigger",
">_Trigger")
462 key,
463 meta_filter,
464 )
465
466 selected_pairs = [
467 pair
468 for pair in _find_associated_pairs(auxes)
469 if _keep_pair(pair, auxes, meta_filter)
470 ]
471
472
473 if any(
474 regexXAODTriggerMenuJsonAux.match(auxes[pair[0]])
475 for pair in selected_pairs
476 ):
477 selected_pairs = [
478 pair
479 for pair in selected_pairs
480 if not regexXAODTriggerMenuAux.match(auxes[pair[0]])
481 ]
482
483 read_dynamic_fmd = any(
484 "FileMetaData" in pair[0] for pair in selected_pairs
485 )
486
487 fields_to_read = {name for name, _ in selected_fields.values()}
488 fields_to_read.update(name for pair in selected_pairs for name in pair)
489 if read_dynamic_fmd:
490 fields_to_read.update(dynamic_fmd_fields.values())
491
492
493 model = ROOT.RNTupleModel.Create()
494 for field in descriptor.GetTopLevelFields():
495 if field.GetFieldName() in fields_to_read:
496 model.AddField(ROOT.std.move(field.CreateField(descriptor)))
497
498 reader = ROOT.RNTupleReader.Open(ROOT.std.move(model), md)
499 entry = reader.CreateEntry()
500 reader.LoadEntry(0, entry)
501
502
503 for normalizedName, (field_name, type_name) in sorted(
504 selected_fields.items(),
505 key=lambda item: regexEventStreamInfo.match(item[1][1]) is None,
506 ):
507 try:
508 value = _convert_value(
509 _get_entry_value(entry, field_name, type_name)
510 )
511 except KeyError:
512 msg.warning(f"missing type {type_name}")
513 continue
514 meta_dict[filename][normalizedName] = value
515 if (
516 regexEventStreamInfo.match(type_name)
517 and value
518 and len(value.get("runNumbers", [])) == 1
519 ):
520 _gbl_run = value["runNumbers"][0]
521 msg.debug("Found RunNumber in EventStreamInfo: %d", _gbl_run)
522
523 dynamic_fmd_items = {}
524 if read_dynamic_fmd:
525 for name, field_name in dynamic_fmd_fields.items():
526 value = _convert_value(entry[field_name])
527 if name.startswith("is"):
528
529 value = value != "\x00"
530 dynamic_fmd_items[name] = value
531
532 for pair in selected_pairs:
533 return_obj = _convert_value(
534 _get_entry_value(entry, pair[1], auxes[pair[1]]),
535 _get_entry_value(entry, pair[0], auxes[pair[0]]),
536 )
537
538
539 if "TriggerMenu" in pair[0]:
540 if "RAWTriggerMenuJson" in return_obj:
541 key = (
542 pair[1]
543 if pair[0].startswith("Trigger")
544 else trigger_menu_json_map[pair[0]]
545 )
546 meta_dict[filename][key] = return_obj["RAWTriggerMenuJson"]
547 del return_obj["RAWTriggerMenuJson"]
548 if "TriggerConfigInfo" not in meta_dict[filename]:
549 meta_dict[filename]["TriggerConfigInfo"] = {}
550 if "dbkey" in return_obj:
551 meta_dict[filename]["TriggerConfigInfo"][
553 ] = {"key": return_obj["dbkey"], "name": return_obj["name"]}
554 del return_obj["dbkey"]
555 del return_obj["name"]
556 if "TriggerMenu" not in meta_dict[filename]:
557 meta_dict[filename]["TriggerMenu"] = {}
558 meta_dict[filename]["TriggerMenu"].update(return_obj)
559 elif "FileMetaData" in pair[0]:
560 if "FileMetaData" not in meta_dict[filename]:
561 meta_dict[filename]["FileMetaData"] = dynamic_fmd_items
562 meta_dict[filename]["FileMetaData"].update(return_obj)
563 elif "TruthMetaData" in pair[0]:
564 if pair == ("TruthMetaDataAux:", "TruthMetaData"):
565 if "TruthMetaData" not in meta_dict[filename]:
566 meta_dict[filename]["TruthMetaData"] = {}
567 meta_dict[filename]["TruthMetaData"].update(return_obj)
568 else:
569
570 meta_dict[filename][
571 pair[1]
573 .
replace(
"DataVector_",
"DataVector<")
575 ] = {}
576 meta_dict[filename][
577 pair[0]
580 ] = {}
581 elif pair == ("CutBookkeepersAux:", "CutBookkeepers"):
582 meta_dict[filename]["CutBookkeepers"] = return_obj
583
584 msg.debug(f"Read metadata from RNTuple: {meta_dict[filename]}")
585
586 else:
587
588 if mode != 'tiny':
589
590 metadata_tree = current_file.Get('MetaData')
591
592 metadata_branches = metadata_tree.GetListOfBranches()
593 nr_of_branches = metadata_branches.GetEntriesFast()
594
595
596 meta_dict[filename]['metadata_items'] = {}
597
598 meta_filter = get_meta_filter(mode, meta_key_filter)
599
600
601 persistent_instances = {}
602 dynamic_fmd_items = {}
603
604
605 if not isGaudiEnv():
606 metadata_tree.SetBranchStatus("*", False)
607
608 for i in range(0, nr_of_branches):
609 branch = metadata_branches.At(i)
610 name = branch.GetName()
611 if name == 'index_ref':
612
613 continue
614
615 class_name = branch.GetClassName()
616
617 if regexIOVMetaDataContainer.match(class_name):
618 name = name.replace(
'IOVMetaDataContainer_p1_',
'').
replace(
'_',
'/')
619
620 if regexIOVMetaDataContainer.match(class_name):
621 meta_dict[filename]['metadata_items'][name] = 'IOVMetaDataContainer'
622 elif regexByteStreamMetadataContainer.match(class_name):
623 meta_dict[filename]['metadata_items'][name] = 'ByteStreamMetadataContainer'
624 elif regexEventStreamInfo.match(class_name):
625 meta_dict[filename]['metadata_items'][name] = 'EventStreamInfo'
626 elif regexXAODFileMetaData.match(class_name):
627 meta_dict[filename]['metadata_items'][name] = 'FileMetaData'
628 elif regexXAODTruthMetaData.match(class_name):
629 meta_dict[filename]['metadata_items'][name] = 'TruthMetaData'
630 else:
631 type_name = class_name
632 if not type_name:
633 try:
634 type_name = branch.GetListOfLeaves()[0].GetTypeName()
635 except IndexError:
636 pass
637 meta_dict[filename]['metadata_items'][name] = type_name
638
639 if len(meta_filter) > 0:
640 keep = False
641 for filter_key, filter_class in meta_filter.items():
642 if (filter_key.replace('/', '_') in name.replace('/', '_') or filter_key == '*') and fnmatchcase(class_name, filter_class):
643 if 'CutBookkeepers' in filter_key:
644 keep = filter_key == name
645 if keep:
646 break
647 else:
648 keep = True
649 break
650
651 if not keep:
652 continue
653 else:
654
655 if 'CutBookkeepers' in name and name not in ['CutBookkeepers', 'CutBookkeepersAux.']:
656 continue
657
658 if not isGaudiEnv():
659 metadata_tree.SetBranchStatus(f"{name}*", True)
660
661
662 if regexEventStreamInfo.match(class_name):
663 if class_name.endswith('_p1'):
664 persistent_instances[name] = ROOT.EventStreamInfo_p1()
665 elif class_name.endswith('_p2'):
666 persistent_instances[name] = ROOT.EventStreamInfo_p2()
667 else:
668 persistent_instances[name] = ROOT.EventStreamInfo_p3()
669 elif regexIOVMetaDataContainer.match(class_name):
670 persistent_instances[name] = ROOT.IOVMetaDataContainer_p1()
671 elif regexByteStreamMetadataContainer.match(class_name):
672 persistent_instances[name] = ROOT.ByteStreamMetadataContainer_p1()
673 elif regexXAODEventFormat.match(class_name):
674 persistent_instances[name] = ROOT.xAOD.EventFormat_v1()
675 elif regexXAODTriggerMenu.match(class_name) and _check_project() not in ['AthGeneration']:
676 persistent_instances[name] = ROOT.xAOD.TriggerMenuContainer_v1()
677 elif regexXAODTriggerMenuAux.match(class_name) and _check_project() not in ['AthGeneration']:
678 persistent_instances[name] = ROOT.xAOD.TriggerMenuAuxContainer_v1()
679 elif regexXAODTriggerMenuJson.match(class_name) and _check_project() not in ['AthGeneration']:
680 persistent_instances[name] = ROOT.xAOD.TriggerMenuJsonContainer_v1()
681 elif regexXAODTriggerMenuJsonAux.match(class_name) and _check_project() not in ['AthGeneration']:
682 persistent_instances[name] = ROOT.xAOD.TriggerMenuJsonAuxContainer_v1()
683 elif regexXAODCutBookkeeperContainer.match(class_name):
684 persistent_instances[name] = ROOT.xAOD.CutBookkeeperContainer_v1()
685 elif regexXAODCutBookkeeperContainerAux.match(class_name):
686 persistent_instances[name] = ROOT.xAOD.CutBookkeeperAuxContainer_v1()
687 elif regexXAODFileMetaData.match(class_name):
688 persistent_instances[name] = ROOT.xAOD.FileMetaData_v1()
689 elif regexXAODFileMetaDataAux.match(class_name):
690 persistent_instances[name] = ROOT.xAOD.FileMetaDataAuxInfo_v1()
691 elif regexXAODTruthMetaData.match(class_name):
692 persistent_instances[name] = ROOT.xAOD.TruthMetaDataContainer_v1()
693 elif regexXAODTruthMetaDataAux.match(class_name):
694 persistent_instances[name] = ROOT.xAOD.TruthMetaDataAuxContainer_v1()
695
696 if name in persistent_instances:
697 branch.SetAddress(ROOT.AddressOf(persistent_instances[name]))
698
699
700 dynamicFMD = regexXAODFileMetaDataAuxDyn.match(name)
701 if dynamicFMD:
702 dynamicName = dynamicFMD.group().
split(
'.')[-1]
703 dynamicType = regex_cppname.match(class_name)
704 if dynamicType:
705
706 dynamic_fmd_items[dynamicName] = ROOT.std.string()
707 branch.SetAddress(ROOT.AddressOf(dynamic_fmd_items[dynamicName]))
708 else:
709 dynamic_fmd_items[dynamicName] = None
710
711
712 metadata_tree.GetEntry(0)
713
714
715 for key in dynamic_fmd_items:
716 if dynamic_fmd_items[key] is None:
717 try:
718 if key.startswith("is"):
719
720 dynamic_fmd_items[key] = getattr(metadata_tree, key) != '\x00'
721 else:
722
723 dynamic_fmd_items[key] = getattr(metadata_tree, key)
724 except AttributeError:
725
726 pass
727 else:
728
729 dynamic_fmd_items[key] = str(dynamic_fmd_items[key])
730
731
732 if meta_key_filter:
733 meta_dict[filename] = {}
734
735
736 for key, obj in persistent_instances.items():
737 if key.startswith('EventStreamInfo'):
738 data = _convert_value(obj, None)
739 if data and 'runNumbers' in data and len(data['runNumbers']) == 1:
740 _gbl_run = data['runNumbers'][0]
741 msg.debug("Found RunNumber in EventStreamInfo: %d", _gbl_run)
742
743
744 for name, content in persistent_instances.items():
745 key = name
746 if hasattr(content, 'm_folderName'):
747 key = content.m_folderName
748
749
750 has_r3_trig_meta = ('TriggerMenuJson_HLT' in persistent_instances or 'DataVector<xAOD::TriggerMenuJson_v1>_TriggerMenuJson_HLT' in persistent_instances)
751 aux = None
752 if key.startswith('TriggerMenuJson_') and not key.endswith('Aux.'):
753 aux = persistent_instances[key+'Aux.']
754 elif key.startswith('DataVector<xAOD::TriggerMenuJson_v1>_TriggerMenuJson_') and not key.endswith('Aux.'):
755 menuPart = key.split('_')[-1]
756 aux = persistent_instances['xAOD::TriggerMenuJsonAuxContainer_v1_TriggerMenuJson_'+menuPart+'Aux.']
757 elif key == 'TriggerMenu' and 'TriggerMenuAux.' in persistent_instances and not has_r3_trig_meta:
758 aux = persistent_instances['TriggerMenuAux.']
759 elif key == 'DataVector<xAOD::TriggerMenu_v1>_TriggerMenu' and 'xAOD::TriggerMenuAuxContainer_v1_TriggerMenuAux.' in persistent_instances and not has_r3_trig_meta:
760 aux = persistent_instances['xAOD::TriggerMenuAuxContainer_v1_TriggerMenuAux.']
761 elif (key == 'CutBookkeepers'
762 and 'CutBookkeepersAux.' in persistent_instances):
763 aux = persistent_instances['CutBookkeepersAux.']
764 elif key == 'CutBookkeepersAux.':
765 continue
766 elif (key == 'FileMetaData'
767 and 'FileMetaDataAux.' in persistent_instances):
768 aux = persistent_instances['FileMetaDataAux.']
769 elif (key == 'xAOD::FileMetaData_v1_FileMetaData'
770 and 'xAOD::FileMetaDataAuxInfo_v1_FileMetaDataAux.' in persistent_instances):
771 aux = persistent_instances['xAOD::FileMetaDataAuxInfo_v1_FileMetaDataAux.']
772 elif (key == 'TruthMetaData'
773 and 'TruthMetaDataAux.' in persistent_instances):
774 aux = persistent_instances['TruthMetaDataAux.']
775 elif key == 'TruthMetaDataAux.':
776 continue
777 elif 'Menu' in key and key.endswith('Aux.'):
778 continue
779
780 return_obj = _convert_value(content, aux)
781
782 if 'TriggerMenuJson' in key or ('TriggerMenu' in key and not has_r3_trig_meta):
783 if 'RAWTriggerMenuJson' in return_obj:
784 meta_dict[filename][key] = return_obj['RAWTriggerMenuJson']
785 del return_obj['RAWTriggerMenuJson']
786 if 'TriggerConfigInfo' not in meta_dict[filename]:
787 meta_dict[filename]['TriggerConfigInfo'] = {}
788 if 'dbkey' in return_obj:
789 meta_dict[filename]['TriggerConfigInfo'][key.split('_')[-1]] = {
790 'key' : return_obj['dbkey'],
791 'name': return_obj['name']
792 }
793 del return_obj['dbkey']
794 del return_obj['name']
795 if 'TriggerMenu' not in meta_dict[filename]:
796 meta_dict[filename]['TriggerMenu'] = {}
797 meta_dict[filename]['TriggerMenu'].update(return_obj)
798 elif "FileMetaData" in key:
799 if "FileMetaData" not in meta_dict[filename]:
800 meta_dict[filename]["FileMetaData"] = dynamic_fmd_items
801 meta_dict[filename]["FileMetaData"].update(return_obj)
802 else:
803 meta_dict[filename][key] = return_obj
804
805 try:
806
807 esi_dict = next(key for key, value in meta_dict[filename].items()
808 if isinstance(value, dict) and "numberOfEvents" in value and
809 meta_dict[filename]["metadata_items"][key] == "EventStreamInfo")
810 msg.debug(f"{esi_dict=}")
811 meta_dict[filename]["nentries"] = meta_dict[filename][esi_dict]["numberOfEvents"]
812 except StopIteration as err:
813 msg.debug(f"Caught {err=}, {type(err)=}, falling back on opening the DataHeader"
814 " Container to read the number of entries")
815 meta_dict[filename]['nentries'] = dataheader_nentries(current_file)
816 msg.debug(f"{meta_dict[filename]['nentries']=}")
817
818 if unique_tag_info_values and mode=='iov':
819 unique_tag_info_values = False
820 msg.info('disabling "unique_tag_info_values" option for "iov" mode')
821
822
823
824
825
826
827 if unique_tag_info_values:
828 msg.info('MetaReader is called with the parameter "unique_tag_info_values" set to True. '
829 'This is a workaround to remove all duplicate values from "/TagInfo" key')
830 if '/TagInfo' in meta_dict[filename]:
831 for key, value in meta_dict[filename]['/TagInfo'].items():
832 if isinstance(value, list) and value:
833 if len(unique_values :=
set(value)) > 1:
834 msg.warning(
835 f"Found multiple values for {key}: {value}. "
836 "Looking for possible duplicates."
837 )
838 maybe_ok = False
839 if key == "AMITag":
840
841 unique_amitags =
set()
842 for amitags in unique_values:
843 unique_amitags.add(
844 "_".join([tag for tag in amitags.split("_") if tag])
845 )
846
847
848 parent_tags = []
849 for atag in unique_amitags:
850 if any(atag+'_' in x for x in unique_amitags if x != atag):
851 parent_tags += [atag]
852 for atag in parent_tags:
853
854 if len(unique_amitags)>1:
855 msg.warning(f"Removing parent AMI tag {atag}")
856 unique_amitags.remove(atag)
857 if len(unique_amitags) == 1:
858 maybe_ok = True
859
860 value.insert(0,list(unique_amitags)[0])
861 elif key == "beam_energy":
862
863 unique_energies =
set()
864 for energy in unique_values:
865 try:
866 energy = int(energy)
867 except ValueError:
868 try:
869 energy = float(energy)
870 except ValueError:
871 pass
872 unique_energies.add(energy)
873 if len(unique_energies) == 1:
874 maybe_ok = True
875
876 elif key in ["AtlasRelease", "IOVDbGlobalTag", "AODFixVersion", "randomSeed"]:
877 maybe_ok = True
878 if maybe_ok:
879 msg.warning(
880 f"Multiple values for {key} may mean the same, or "
881 "the input file was produced in multi-step job. "
882 f"Ignoring all but the first entry: {key} = {value[0]}"
883 )
884 else:
885 raise ValueError(
886 f"{key} from /TagInfo contains more than 1 unique value: {value}"
887 )
888
889 meta_dict[filename]['/TagInfo'][key] = value[0]
890
891 if promote is None:
892 promote = mode == 'lite' or mode == 'peeker'
893
894
895 if mode == 'lite':
896 meta_dict = make_lite(meta_dict)
897
898 if mode == 'peeker':
899 meta_dict = make_peeker(meta_dict)
900
901 if promote:
902 meta_dict = promote_keys(meta_dict, mode)
903
904
905 if not isGaudiEnv():
906 if isinstance(collectionTree, ROOT.TTree):
907 item_list = [
908 (str(b.GetClassName()), str(b.GetName())) for b in collectionTree.GetListOfBranches()
909 ]
910 if normalize_type_names:
911 item_list = [
912 (type_name, _branch_name_to_field_name(name)) for type_name, name in item_list
913 ]
914 elif isRNTuple(eventData := current_file.Get(PoolOpts.RNTupleNames.EventData)):
915
916 eventDataReader = ROOT.RNTupleReader.Open(eventData)
917
918
919 item_list = [
920 (str(f.GetTypeName()), re.sub(r'Aux(Dyn)?:', r'Aux\1.', str(f.GetFieldName()), count=1))
921 for f in eventDataReader.GetDescriptor().GetTopLevelFields()
922 if not str(f.GetTypeName()).startswith('DataHeader')
923 ]
924 if normalize_type_names:
925 item_list = [
926 (_rntuple_type_to_class_name(type_name), name) for type_name, name in item_list
927 ]
928 if normalize_type_names:
929
930 item_list.sort()
931 meta_dict[filename]['itemList'] = item_list
932
933
934 elif current_file_type == 'BS':
935
936 if ignoreNonExistingLocalFiles and not regex_URI_scheme.match(filename) and not os.path.isfile(filename):
937 msg.warning('Ignoring not accessible file: {}'.format(filename))
938 continue
939
940 import eformat
941
942
943 bs = eformat.istream(filename)
944 meta_dict[filename]['nentries'] = bs.total_events
945
946
947 data_reader = eformat.EventStorage.pickDataReader(filename)
948 assert data_reader, 'problem picking a data reader for file [%s]' % filename
949
950
951 meta_dict[filename]['auto_flush'] = 1
952
953 if hasattr(data_reader, 'GUID'):
954 meta_dict[filename]['file_guid'] = data_reader.GUID()
955
956
957 meta_dict[filename]['file_comp_alg'] = 1
958 meta_dict[filename]['file_comp_level'] = 1
959
960
961
962
963 if mode != "tiny":
964 bs_metadata = {}
965
966 for md in data_reader.freeMetaDataStrings():
967 if md.startswith('Event type:'):
968 k = 'eventTypes'
969 v = []
970 if 'is sim' in md:
971 v.append('IS_SIMULATION')
972 else:
973 v.append('IS_DATA')
974
975 if 'is atlas' in md:
976 v.append('IS_ATLAS')
977 else:
978 v.append('IS_TESTBEAM')
979
980 if 'is physics' in md:
981 v.append('IS_PHYSICS')
982 else:
983 v.append('IS_CALIBRATION')
984
985 bs_metadata[k] = tuple(v)
986
987 elif md.startswith('GeoAtlas:'):
988 k = 'geometry'
989 v = md.split(
'GeoAtlas:')[1].
strip()
990 bs_metadata[k] = v
991
992 elif md.startswith('IOVDbGlobalTag:'):
993 k = 'conditions_tag'
994 v = md.split(
'IOVDbGlobalTag:')[1].
strip()
995 bs_metadata[k] = v
996
997 elif '=' in md:
998 k, v = md.split('=', 1)
999 bs_metadata[k] = v
1000
1001 bs_metadata['detectorMask'] = data_reader.detectorMask()
1002 bs_metadata['runNumbers'] = data_reader.runNumber()
1003 bs_metadata['lumiBlockNumbers'] = data_reader.lumiblockNumber()
1004 bs_metadata['projectTag'] = data_reader.projectTag()
1005 bs_metadata['stream'] = data_reader.stream()
1006
1007 beamTypeNbr= data_reader.beamType()
1008
1009
1010
1011
1012 if (beamTypeNbr==0): bs_metadata['beamType'] = 'cosmics'
1013 elif (beamTypeNbr==1 or beamTypeNbr==2): bs_metadata['beamType'] = 'collisions'
1014 else: bs_metadata['beamType'] = 'unknown'
1015
1016 bs_metadata['beamEnergy'] = data_reader.beamEnergy()
1017
1018 meta_dict[filename]['eventTypes'] = bs_metadata.get('eventTypes', [])
1019 meta_dict[filename]['GeoAtlas'] = bs_metadata.get('geometry', None)
1020 meta_dict[filename]['conditions_tag'] = bs_metadata.get('conditions_tag', None)
1021 meta_dict[filename]['project_name'] = bs_metadata.get('projectTag', None)
1022
1023
1024 meta_dict[filename]['detectorMask'] = [bs_metadata.get('detectorMask', None)]
1025 meta_dict[filename]['runNumbers'] = [bs_metadata.get('runNumbers', None)]
1026 meta_dict[filename]['lumiBlockNumbers'] = [bs_metadata.get('lumiBlockNumbers', None)]
1027 meta_dict[filename]['beam_type'] = bs_metadata.get('beamType', None)
1028 meta_dict[filename]['beam_energy'] = bs_metadata.get('beamEnergy', None)
1029 meta_dict[filename]['stream'] = bs_metadata.get('stream', None)
1030
1031 if not data_reader.good():
1032
1033 meta_dict[filename]['runNumbers'].append(bs_metadata.get('run_number', 0))
1034 meta_dict[filename]['lumiBlockNumbers'].append(bs_metadata.get('LumiBlock', 0))
1035
1036 msg.debug(f"{meta_dict[filename]=}")
1037 msg.debug(f"{len(bs)=}")
1038 if len(bs):
1039 evt = bs[0]
1040 try:
1041 evt.check()
1042 meta_dict[filename]['processingTags'] = [tag.name for tag in evt.stream_tag()]
1043 meta_dict[filename]['evt_number'] = [evt.global_id()]
1044 meta_dict[filename]['run_type'] = [eformat.helper.run_type2string(evt.run_type())]
1045
1046
1047 if meta_dict[filename]['lumiBlockNumbers'] == [0]:
1048 msg.debug('Taking the luminosity block info from the first event (%i)', evt.lumi_block())
1049 meta_dict[filename]['lumiBlockNumbers'] = [evt.lumi_block()]
1050
1051
1052 if meta_dict[filename]['runNumbers'] == [0]:
1053 msg.debug('Taking the run number info from the first event (%i)', evt.run_no())
1054 meta_dict[filename]['runNumbers'] = [evt.run_no()]
1055 except RuntimeError as err:
1056 msg.error("Issue while reading the first event of BS file %r: %r", filename, err)
1057 else:
1058 msg.debug(f"{meta_dict[filename]=}")
1059 else:
1060 msg.warning(f"Event-less BS {filename=}, will not read metadata information from the first event")
1061
1062
1063 if len(bs_metadata.get('eventTypes', '')) == 0:
1064 evt_type = ['IS_DATA', 'IS_ATLAS']
1065 if bs_metadata.get('stream', '').startswith('physics_'):
1066 evt_type.append('IS_PHYSICS')
1067 elif bs_metadata.get('stream', '').startswith('calibration_'):
1068 evt_type.append('IS_CALIBRATION')
1069 elif bs_metadata.get('projectTag', '').endswith('_calib'):
1070 evt_type.append('IS_CALIBRATION')
1071 else:
1072 evt_type.append('Unknown')
1073
1074 meta_dict[filename]['eventTypes'] = evt_type
1075
1076 if mode == 'full':
1077 meta_dict[filename]['bs_metadata'] = bs_metadata
1078
1079
1080 else:
1081 msg.error('Unknown filetype for {0} - there is no metadata interface for type {1}'.format(filename, current_file_type))
1082 return None
1083
1084 return meta_dict
1085
1086
std::string replace(std::string s, const std::string &s2, const std::string &s3)