Here we will show you how to modify or create new xAOD containers/objects, and then how to write these to another xAOD using EventLoop.
Note that if you do it this way you will not be able to read the output xAOD back with POOL in Athena. We will make use of some methods in the xAODRootAccess package to create our new xAOD.
These instructions are only meant for EventLoop/AnalysisBase. With Athena/AthAnalysis you have to use the native Athena I/O system for writing mini-xAOD files.
IMPORTANT NOTE: If you are creating a new smaller xAOD file that is used by your analysis group, consider setting up a derivation in the Derivation Reduction Framework, which will automatically generate this smaller xAOD in the production system for you. See the Derivation Framework for more information.
Before you do this part of the tutorial, make sure that you worked through the section on modifying xAOD objects in its entirety. While it can be meaningful simply to copy input objects, you will almost always want to modify them in some way.
First of all we need to set up a new “output stream” for writing the xAOD file. This happens in two separate steps.
xAOD::TEvent
.In your submission script you need to use
EL::Job::outputAdd
to define a new output stream. Assuming that your EL::Job
object is
called job
(as it was shown in the
EventLoop configuration section), you can declare
the new output stream like the following in Python:
# Add an output stream called 'ANALYSIS'.
job.outputAdd( ROOT.EL.OutputStream( 'ANALYSIS' ) )
To make your algorithm the most robust, it’s a very good idea to set up a string property on it that allows setting the output stream name from the submission script. See the Using Properties section for more details on setting up such a property.
Assuming that you called the output stream’s name
m_outputStreamName
, you can connect the job’s xAOD::TEvent
object
to the output stream/file by putting the following into the
initialize()
function of your algorithm:
TFile* ofile = wk()->getOutputFile( m_outputStreamName );
if( ! ofile ) {
// Handle the error...
}
ANA_CHECK( evtStore()->event()->writeTo( ofile ) );
If you have not done
that yet, you may need to include "EventLoop/Worker.h"
at this
point to make this code compile successfully. That may also require
adding EventLoop
as a dependency in your CMakeLists.txt
file.
Finally, in finalize()
let’s tell the job to close up the output
xAOD by adding:
// Finalize and close our output xAOD file.
TFile* ofile = wk()->getOutputFile( m_outputStreamName );
ANA_CHECK( evtStore()->event()->finishWritingTo( ofile ) );
With all of these set up, you are ready to start adding content to your output xAOD.
Here we will show you how to copy the contents of a full container, unmodified, for every event. We assume you have followed the instructions above to define a new output xAOD in EventLoop.
We will create this copy in the event loop, so in
MyAnalysis/Root/MyxAODAnalysis.cxx
in the execute()
method add the
following line to copy the full container for "AntiKt4EMTopoJets"
:
// copy full container(s) to new xAOD
// without modifying the contents of it:
ANA_CHECK( evtStore()->event()->copy("AntiKt4EMTopoJets") );
At the end of execute()
add this line to fill the xAOD with the
content we have specified in the event loop:
// Save the event:
evtStore()->event()->fill();
Compile like usual and test your code as explained in the EventLoop configuration section.
If you have followed the instructions above you will find your output
xAOD in submitDir/data-ANALYSIS/
. (Assuming that you set
"ANALYSIS"
as the name of your output stream.)
Note that you can only copy xAOD objects and/or containers. You can
determine if the container is of xAOD type by running checkxAOD.py
on the xAOD and seeing which objects are of type xAOD::x
(where x
is the container of interest).
There are two options here, dictated by how you set the flag setShallowIO
:
Save a real shallow copy only writing to the xAOD container the
variables you have overwritten while still pointing to the original
for all other variables. In this case you must also write the
original container (setShallowIO
is true) as
previously
described.
Save an actually deep copy; in this case you do not need to also
write the original container to the xAOD (setShallowIO
is false).
For either option add these lines below our iterator over the shallow copied jets:
const xAOD::JetContainer *jets = nullptr;
ANA_CHECK (evtStore()->retrieve (jets, "AntiKt4EMTopoJets"));
std::pair< xAOD::JetContainer*, xAOD::ShallowAuxContainer* > jets_shallowCopy = xAOD::shallowCopyContainer( *jets );
xAOD::TEvent* event = wk()->xaodEvent();
// true = shallow copy, false = deep copy
// if true should have something like this line somewhere:
// event->copy("AntiKt4EMTopoJets");
jets_shallowCopy.second->setShallowIO( false );
ANA_CHECK (event->record (jets_shallowCopy.first, "ShallowCopiedJets"));
ANA_CHECK (event->record (jets_shallowCopy.second, "ShallowCopiedJetsAux."));
You can record the shallow copy to TStore, in a very similar way we
did above by storing it to TEvent. First in the source code in
execute()
you will need to define an instance of a TStore object
(making use of the EventLoop worker object):
xAOD::TStore* store = wk()->xaodStore();
Then simply record your shallowed jet container (and aux container) to the store:
ANA_CHECK (store->record (jets_shallowCopy.first, "ShallowCopiedJets"));
ANA_CHECK (store->record (jets_shallowCopy.second, "ShallowCopiedJetsAux."));
EventLoop takes care of clearing the memory for you.
At any point you can see
what is stored to your xAOD::TStore
object by doing
store->print()
.
Compile like usual and test your code. Depending on how you set
setShallowIO
you will have more or less variables in your new xAOD
associated to the "ShallowCopiedJets"
container. You can try
changing the flag, recompiling and checking the alternative content of
the xAOD.
As a final ingredient to writing out modified objects, you can select which of their properties should be written to the output file. The xAOD design was based around the idea that objects/containers may be slimmed during the analysis easily.
As you should know, all the data payload of xAOD objects/containers is
in their auxiliary store objects. Because of this the way to specify
which variables should be written out, is to set a property for the
auxiliary store in question. This is done using the
xAOD::TEvent::setAuxItemList
function. By putting something like this into your algorithm’s
initialize()
function:
// Set which variables not to write out:
evtStore()->event()->setAuxItemList ("AntiKt4EMTopoJetsAux.",
"-NumTrkPt1000.-NumTrkPt500");
// Set which variable to do write out:
evtStore()->event()->setAuxItemList ("GoodJetsAux.",
"JetGhostArea.TrackCount");
Unfortunately at the time of writing this still has some issues when using multiple input files (code crashing on a few files into the job), but the formalism is going to be this once the code works as intended…