Knowing what information is in the xAOD

Last update: 05 Jul 2023 [History] [Edit]

One question everyone will have is: how do I know what information/variables are actually stored in my xAOD for each container type? You can be sure for “particles” (inheriting from IParticle) you will have things like pt, eta, and phi. But what other variables are associated to the different containers? We’ll try to answer that question…

Containers and key names

In order to “retrieve” the information stored in the xAOD containers we need to know the container type and the container key name. We will use a handy script called checkxAOD.py. If you have an xAOD file, say xAOD.pool.root and want to know the containers and associated key names do the following:

checkxAOD.py xAOD.pool.root

Note: You need to replace the fake xAOD.pool.root with the full path to an xAOD sample, for example $ALRB_TutorialData//mc21_13p6TeV.601229.PhPy8EG_A14_ttbar_hdamp258p75_SingleLep.deriv.DAOD_PHYS.e8357_s3802_r13508_p5057/DAOD_PHYS.28625583._000007.pool.root.1.

Alternatively you can make a symbolic link using ln -s $ALRB_TutorialData//mc21_13p6TeV.601229.PhPy8EG_A14_ttbar_hdamp258p75_SingleLep.deriv.DAOD_PHYS.e8357_s3802_r13508_p5057/DAOD_PHYS.28625583._000007.pool.root.1 xAOD.pool.root.

The last column will show you the xAOD container names and types. When you are retrieving information you usually need to know the container type (for example xAOD::CaloCluster) and the key name for the particular instance of that container you are interested in (for example "egammaClusters"). In your analysis you can ignore the Aux containers (for Auxiliary store), these hold some behind-the-scenes magic. You can also “mostly” ignore the versions like _v1. Most information in the xAOD is stored and retrieved via the Auxiliary store. The user doesn’t need to worry about this Auxiliary store, and only retrieves the interface from the event store (e.g. TEvent for ROOT standalone analysis). So now you should know the container type and key name. If you use the wrong key name the code will compile, but it will fail at run-time.

Variables inside the containers

Now to know what variables are associated to this container, the trick I use at the moment (again maybe something official will come along…) is to use interactive ROOT. So back into your Analysis Release shell (with ROOT automatically setup), you can simply do this:

root -l xAOD.pool.root
root [1] CollectionTree->Print("egammaClusters*")

You will get a printout of all variables you can access from that container (aka collection). Note that the variable name you will use in your code is the one that comes after the “.”, so for example you might see:

egammaClusters.rawEta : vector<float>

So in your analysis code (after setting up the TEvent and interface magic), you can access this variable from the xAOD::CaloCluster object by calling rawEta.

If you try to request a variable associated to a particular xAOD object that does not exist the code will fail at compile-time, complaining the xAOD object has no member named 'whatever'. This will mean either that the variable does not exist at all or that it needs to be accessed in a different way, e.g. to access a cluster moment, you would do something like:

#include <xAODCaloEvent/CaloClusterContainer.h>
...
const xAOD::CaloClusterContainer* cls = nullptr;
ANA_CHECK (evtStore()->retrieve ( cls, "egammaClusters" )); 
for (const xAOD::CaloCluster* cl : *cls) {
  double moment = 0.0;
  cl->retrieveMoment( xAOD::CaloCluster::PHICALOFRAME, moment );
}

Add to the CMake file:

   LINK_LIBRARIES [...] xAODCaloEvent

And this would retrieve the FIRST_PHI variable from the input file.

Accessing object variables

To access variables associated to objects in your analysis code there are often special accessor functions available to help. These are associated to the objects of the containers. At the moment the best place to find these accessors is by browsing the code. All of the xAOD EDM classes live in atlasoff/Event/xAOD, and the naming should be obvious to find the object type you are interested in. Alternatively you can access the variables directly without using the accessors, but this is slow as it depends on string comparisons.

Here is one example that might clarify these points (you don’t have to copy and paste this anywhere, it’s just a qualitative description). Let’s say you have a pointer to an xAOD::Muon object for a particular event, called (*muon_itr) (we’ll actually do this later on in complete detail), and now we want to access the ptcone20 isolation variable for this muon.

To access the variable with the help of the muon accessor you can do:

// your variable that will be filled after calling the isolation function
float muPtCone20 = 0.;
// second arg is an enum defined in xAODPrimitives/IsolationType.h
(*muon_itr)->isolation(muPtCone20, xAOD::Iso::ptcone20);

Alternatively you can access that same variable by defining an accessor:

SG::AuxElement::ConstAccessor<float> ptcone20("ptcone20");

and then calling it later

float muPtCone20 = ptcone20(**muon_itr);

For convenience there’s an alternative:

(*muon_itr)->auxdata< float >("ptcone20");

but note that this is slower and should be avoided in production code.

For the muons you can find the complete list of accessors in the xAOD Muon class (version 1)

Accessing Containers

Most of our data is organized into containers of objects of a specific type, jets, muons, electrons, etc. As a simple exercise, let’s retrieve the jet container "AntiKt4EMPFlowJets" and print the pt for each jet.

First, let’s add the jet xAOD EDM package to MyAnalysis/CMakeLists.txt:

   LINK_LIBRARIES [...] xAODJet

where [...] is any other package dependencies you may already have included (you may have already added this earlier in the tutorial).

Now let’s add the relevant code to MyxAODAnalysis.cxx:

#include <xAODJet/JetContainer.h>
...
execute () {
  ...
  // get jet container of interest
  const xAOD::JetContainer* jets = nullptr;
  ANA_CHECK (evtStore()->retrieve (jets, "AntiKt4EMPFlowJets"));
  ANA_MSG_INFO ("execute(): number of jets = " << jets->size());

  // loop over the jets in the container
  for (const xAOD::Jet *jet : *jets) {
    ANA_MSG_INFO ("execute(): jet pt = " << (jet->pt() * 0.001) << " GeV"); // just to print out something
  } // end for loop over jets
  ...
}
...

If you are not sure of the container type and/or container name see above. Note that we are taking advantage here of C++11 for loops to simplify our code.

tip You are liable to see two ways to do the above code, both in the tutorial and in ATLAS in general. The other way to do it is using the C++11 keyword auto:

  for (auto jet : *jets) [...]

Which one is better is a matter of debate, but both methods work and do exactly the same. Using auto is somewhat more compact, but using the exact type is more explicit in what you are doing and can avoid certain kinds of mistakes.

Now compile and run the code and check for the new print-out. Note that there is a lot of print-out, so you may want to disable it once you are done with this part of the exercise.


⭐️ Bonus Exercise

  • Retrieve and print out the number of tracks in the InDetTrackParticle collection.
  • Try printing the number of tracks with pT > 10 GeV.