ATLAS Offline Software
CSV_InDetExporter.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
4 
5 from ROOT import xAOD, TFile
6 import os
7 import csv
8 from argparse import ArgumentParser
9 import numpy as np
10 from numpy import array
11 from InDetMeasurementUtilities.CSV_InDetImporter import getCSVFilename
12 class CSVDumper:
13 
14  def __init__(self, inputAOD, outputDir, dict_variables_types, treename="CollectionTree", nEvents=-1):
15 
16  xAOD.Init() # Setting up ROOT tools
17  self.tree = xAOD.MakeTransientTree( TFile(inputAOD), treename )
18  self.n_entries = self.tree.GetEntriesFast()
19  self.outputDir = outputDir
20 
21  if nEvents < 0 or nEvents > self.n_entries: # Getting number of events to run
22  self.nEvents = self.n_entries
23  else:
24  self.nEvents = nEvents
25  print(f"Running on {self.nEvents} events")
26 
27  os.system("mkdir -p "+self.outputDir) # Creating directory to store files
28 
29  # Container and Variables with their types that should be stored in csv format. Should be something like:
30  #{
31  # "ContainerName": { "var1": "int",
32  # "var2": "float",
33  # }
34  #}
35  self.dict_variables_types = dict_variables_types
36 
37  def WriteCSV(self, filename, dictionary):
38 
39  with open(filename, "w") as out:
40  writer = csv.writer(out)
41  writer.writerow(dictionary.keys())
42  writer.writerows( zip(*dictionary.values()) )
43  print(f"New file saved: {filename}")
44 
45  def ArrayFloat3_to_CppArray(self, ArrayFloat3): # Check ArrayFloat3 in https://gitlab.cern.ch/atlas/athena/-/blob/master/Event/xAOD/xAODInDetMeasurement/xAODInDetMeasurement/versions/SpacePoint_v1.h
46  try:
47  arr = ArrayFloat3.data()
48  except Exception:
49  arr = ArrayFloat3
50  arr.reshape((3,))
51  return list(arr)
52 
53 
54  def ProcessEvent(self, evt):
55 
56  # Getting aux variables based on the implementation of https://gitlab.cern.ch/atlas/athena/-/commit/fc5baf9fd2bb28c56f115fc2107a3ff159f1945d
57  print("--> Event "+str(evt))
58  self.tree.GetEntry(evt)
59  EventNumber = self.tree.EventInfo.mcEventNumber()
60 
61  # Check whether the container exists
62  for container in self.dict_variables_types.keys():
63  dict_lists = {}
64  dict_container = self.dict_variables_types[container]
65  try:
66  tp = getattr(self.tree, container)
67  except Exception:
68  print(".. Missing ", container)
69  continue
70 
71  if tp.size() == 0:
72  print(".. Empty ", container)
73  continue
74 
75  for var,fmt in dict_container.items():
76  try:
77  sp = tp.getConstDataSpan[ fmt ]( var)
78  except Exception:
79  # This is for the case arrays are either empty or have some trouble when accessed via getConstDataSpan. Used in excepction as makes the code slower
80  print("getConstDataSpan failed for variable ",var,fmt, container)
81  sp = [ getattr(element, var)() for element in tp ]
82 
83  try:
84  # Convert list of std::vector<double> to the list of lists
85  list_of_lists = [list(std_vector) for std_vector in sp]
86  # Find the length of the longest vector
87  max_len = max(len(ll) for ll in list_of_lists)
88  # extend lists to the length of max_len
89  for ll in list_of_lists:
90  ll.extend([np.nan] * (max_len - len(ll)))
91  except Exception:
92  list_of_lists = sp # the sp is not iterable
93 
94  # Convert the list of lists to a NumPy array
95  sp = np.array(list_of_lists)
96 
97  if "ArrayFloat3" in fmt and len(sp) > 0: # Needs extra coding when dealing with xAOD::ArrayFloat3 instead of standard C++ array
98  sp = array( list( map(self.ArrayFloat3_to_CppArray, sp) ) )
99 
100  if ("unsigned char" in fmt or "uint8" in fmt) and len(sp) > 0:
101  sp = sp.view(np.int32)
102 
103  if sp.ndim == 1:
104  dict_lists[var] = sp
105  else:
106  # Then we have an array. Each element of the array will be a column in the csv file. [We want things flat]
107  for column in range(sp.shape[1]):
108  dict_lists[var+f"_at{column}"] = sp.T[column]
109 
110  self.WriteCSV( filename=getCSVFilename(self.outputDir, container, EventNumber), dictionary=dict_lists )
111 
112 
113  def Run(self):
114 
115  for evt in range(self.nEvents):
116  self.ProcessEvent(evt)
117 
118 
119 if __name__ == "__main__":
120 
121 
122  parser = ArgumentParser()
123  parser.add_argument('--inputAOD', type=str, default="", help="Input AOD.root file")
124  parser.add_argument('--outputDir', type=str, default="", help="Output directory")
125  parser.add_argument('--treename', type=str, default="CollectionTree")
126  parser.add_argument('--nEvents', type=int, default=-1, help="Number of events")
127  parser.add_argument('--CSV_DictFormats', type=str, default="InDetMeasurementUtilities.CSV_DictFormats",
128  help="Name of the python file (ex. local CSV_DictFormats) with variable list. Default: InDetMeasurementUtilities.CSV_DictFormats" )
129  parser.add_argument('--renames', type=str, default="", help="Names of collections other than default eg. InDetTrackParticles=NewColl,ITkPixelClusters=NewClusters,...")
130 
131  args = parser.parse_args()
132 
133  import importlib
134  module = importlib.import_module(args.CSV_DictFormats)
135  CSV_DictFormats = module.CSV_DictFormats
136 
137  if args.inputAOD == "":
138  raise Exception("No inputAOD was provided!")
139 
140  if args.outputDir == "":
141  raise Exception("No outputDir was provided!")
142 
143  if args.renames != "":
144  for n in args.renames.split(","):
145  old,new = n.split("=")
146  CSV_DictFormats[new] = CSV_DictFormats[old]
147  del CSV_DictFormats[old]
148  print("Instead of", old, "will use", new)
149 
150  Dumper = CSVDumper(inputAOD=args.inputAOD,
151  outputDir=args.outputDir,
152  dict_variables_types=CSV_DictFormats,
153  treename=args.treename,
154  nEvents=args.nEvents)
155  Dumper.Run()
xAOD::Init
StatusCode Init(const char *appname, int *argc, char **argv)
Function initialising an application for using the ATLAS EDM.
Definition: Init.cxx:36
max
#define max(a, b)
Definition: cfImp.cxx:41
CSV_InDetExporter.CSVDumper.outputDir
outputDir
Definition: CSV_InDetExporter.py:19
CSV_InDetImporter.getCSVFilename
def getCSVFilename(outputDir, container, eventNumber)
Definition: CSV_InDetImporter.py:10
CSV_InDetExporter.CSVDumper.ArrayFloat3_to_CppArray
def ArrayFloat3_to_CppArray(self, ArrayFloat3)
Definition: CSV_InDetExporter.py:45
CSV_InDetExporter.CSVDumper.nEvents
nEvents
Definition: CSV_InDetExporter.py:22
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
CSV_InDetExporter.CSVDumper.WriteCSV
def WriteCSV(self, filename, dictionary)
Definition: CSV_InDetExporter.py:37
array
CSV_InDetExporter.CSVDumper.tree
tree
Definition: CSV_InDetExporter.py:17
CSV_InDetExporter.CSVDumper.dict_variables_types
dict_variables_types
Definition: CSV_InDetExporter.py:35
CSV_InDetExporter.CSVDumper.__init__
def __init__(self, inputAOD, outputDir, dict_variables_types, treename="CollectionTree", nEvents=-1)
Definition: CSV_InDetExporter.py:14
Trk::open
@ open
Definition: BinningType.h:40
python.KeyStore.list
def list(self, key=None)
Definition: KeyStore.py:318
CSV_InDetExporter.CSVDumper.Run
def Run(self)
Definition: CSV_InDetExporter.py:113
Muon::print
std::string print(const MuPatSegment &)
Definition: MuonTrackSteering.cxx:28
str
Definition: BTagTrackIpAccessor.cxx:11
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:790
CSV_InDetExporter.CSVDumper.n_entries
n_entries
Definition: CSV_InDetExporter.py:18
CSV_InDetExporter.CSVDumper.ProcessEvent
def ProcessEvent(self, evt)
Definition: CSV_InDetExporter.py:54
CSV_InDetExporter.CSVDumper
Definition: CSV_InDetExporter.py:12