Tools are possibly the most common way we distribute software components in ATLAS. In many respects it acts like a regular C++ object, but it fits in with the ATLAS component model and is configured alongside other components, though we are not going to discuss the component model here.
As an example, let’s add the GoodRunsLists
tool to our analysis.
This tool will check whether a given data event can be used or was in
a Luminosity blocks with “problems” in it. The exact details are not
that important here, we mostly use it because it makes for a simple
example. The general outline will be the same for all tools.
If this example seems rather complicated for something that ought to be rather simple, it’s because it is. This mechanism is meant for much more complicated scenarios, and for something so simple you could come up with a simpler mechanism. We tried that and the problem is that the simpler mechanisms can cause you various problems further down the road when your code gets more complicated. So we concluded it is better for you to start out with the “proper” way of using tools, and save yourself some headache in the future. And after you used this 2-3 times it will probably come quite natural to you.
One important thing about using tools is that you don’t create (or
destroy) them directly inside your algorithm class. Instead the
user/framework creates and configures the tools for you and then makes
them available to your algorithm. For that to work properly your
algorithm needs to refer to the tool using a ToolHandle
. In many
respects a ToolHandle
works just like a (smart) pointer to the tool,
except for some extra features related to configuration.
To use this tool we need the tool interface class
IGoodRunsListSelectionTool
and the xAOD::EventInfo
class that
holds the data we operate on. For that add AsgAnalysisInterfaces
and xAODEventInfo
to the LINK_LIBRARIES
fields in your
CMakeLists.txt
file.
To use the tools with a ToolHandle
you have to add the proper
includes to the header file (MyxAODAnalysis.h
near the top):
// GRL
#include <AsgAnalysisInterfaces/IGoodRunsListSelectionTool.h>
#include <AsgTools/ToolHandle.h>
You should not try to forward declare the tool interface, but always include the proper header file. While forward declarations will work in EventLoop, they will create a compilation error in Athena.
And then add the ToolHandle
inside the class itself:
ToolHandle<IGoodRunsListSelectionTool> m_grl;
Inside the constructor you then have to setup the ToolHandle
so that
the framework can find and configure it:
MyxAODAnalysis :: MyxAODAnalysis (const std::string& name,
ISvcLocator *pSvcLocator)
: EL::AnaAlgorithm (name, pSvcLocator),
m_grl ("GoodRunsListSelectionTool/grl", this)
{
// declare the tool handle as a property on the algorithm
declareProperty ("grlTool", m_grl, "the GRL tool");
The ToolHandle
constructor takes two arguments: The first is the
type and the name of the tool to be created, separated by a /
. If
you omit the /
the assumption is that you want both of them to be
the same. The second argument is a pointer to the parent, which
indicates that you want to crate a private tool, i.e. a tool that is
only owned/known by the parent. By and large you should make all your
tool private tools (with some noteable exceptions). If you omit the
last parameter your tool becomes a public tool, meaning it is shared
by all tool handles that specify the same name.
The declareProperty
call declares the ToolHandle
as a property on
the algorithm, which allows to configure it from python. It work
pretty much the same as regular property
declaration, though the configuration works
somewhat differently (see below).
Within the initialize
method you should then retrieve the tool,
which is not strictly necessary, but it is recommended to make sure
the tool is actually there before the job starts:
ANA_CHECK (m_grl.retrieve());
Most tools will also have declared an
initialize
method. It is important that you do not call this method yourself. It will already have been called and some tools can break if it gets called twice. Unfortunately there are essentially no checks against you doing that, so you just have to make sure not to do that.
Next let’s actually use the tool. This part is very specific to the tool itself, whereas the above is essentially just boiler-plate code that will look more or less the same for all tools.
In the execute()
method, let’s retrieve the `EventInfo object,
check if the event is in fact a data event, and if it is, then check
the GRL for this event:
//----------------------------
// Event information
//---------------------------
const xAOD::EventInfo* eventInfo = 0;
ANA_CHECK(evtStore()->retrieve( eventInfo, "EventInfo"));
// check if the event is data or MC
// (many tools are applied either to data or MC)
bool isMC = false;
// check if the event is MC
if (eventInfo->eventType (xAOD::EventInfo::IS_SIMULATION)) {
isMC = true; // can do something with this later
}
// if data check if event passes GRL
if (!isMC) { // it's data!
if (!m_grl->passRunLB(*eventInfo)) {
ANA_MSG_INFO ("drop event: GRL");
return StatusCode::SUCCESS; // go to next event
}
} // end if not MC
ANA_MSG_INFO ("keep event: GRL");
The message statement is mostly so that we can see what is happening.
For actual analysis you would either remove that statement or set it
to DEBUG
or even VERBOSE
message level.
You may notice that sometimes we say
m_grl.
and sometimesm_grl->
. The rule is that until the tool is initialized we are talking to the tool handle and usem_grl.
after the tool is initialized we mostly talk to the tool itself and usem_grl->
instead.
You can now compile your code to make sure you didn’t make any mistakes in the code itself, but it won’t run (correctly) yet as the tool needs to be configured.
As noted above, your tool actually gets configured inside the python configuration file and then EventLoop/Athena will create it for you. Let’s do that now. There are actually multiple ways of doing this, we prefer the way below because it works the same for Athena and EventLoop.
Just add the following to your configuration file after you create
your algorithm and before you add it to the sequence/job (if you add
multiple tools, you only need the import
statement once):
from AnaAlgorithm.DualUseConfig import addPrivateTool
# add the GRL tool to the algorithm
addPrivateTool( alg, 'grlTool', 'GoodRunsListSelectionTool' )
# configure the properties of the GRL tool
fullGRLFilePath = os.getenv ("ALRB_TutorialData") + "/data16_13TeV.periodAllYear_DetStatus-v89-pro21-01_DQDefects-00-02-04_PHYS_StandardGRL_All_Good_25ns.xml"
alg.grlTool.GoodRunsListVec = [ fullGRLFilePath ]
alg.grlTool.PassThrough = 0 # if true (default) will ignore result of GRL and will just pass all events
We won’t discuss the actual property values here, but a couple of
words on the addPrivateTool
call: This takes three arguments, the
algorithm the tool belongs to, and the name of the tool class. If you
have keen eyes you may have noticed that this is different from the
class you used in the C++ section. Essentially (almost) every tool
has two classes, an interface class (usually starting with I
) and an
implementation class (usually the same name without the I
in the
beginning). In your C++ code you should only be using the interface
class which contains all the functions the user can call and nothing
else. In the configuration you will however have to specify the
actual implementation class which contains the actual tool code.
Now compile and run your code and see if it does what you expect.