GaudiTool

From Daya Bay
Jump to navigation Jump to search

How to write a GaudiTool.

What is a Tool?

First, a Tool is a piece of code that other GaudiAlgorithms or other Tools may find useful. By packaging this code into a Tool object it is possible to configure Algorithms or other Tools to use it, or maybe to swap to a different, competing tool. A Tool is similar to a GaudiService except there may be multiple instances of a given Tool and they public (shared by all) or private (shared by a few) client codes.

Basics

All Tools may implement these two methods:

// called when tool first used
StatusCode initialize();

// called with tool is no longer needed
StatusCode finalize();

A Tool's Interface

A tool may not need any extra methods if it is programmed to respond to external events. However, most tools provide functionality through additional methods beyond those above. It is best practice to define these methods in a Gaudi Interface class. This way multiple Tool classes can be written that provide the additional functionality in different ways.

For example, in GenTools, there are several some kinematic generator tools all which have the same Interface. This allows the user to configure the job to use a particular generator without having to recompile code.

How to write a Tool Interface class

The Interface class definition

Write a class that inherits from IAlgTool:

class IMyTool : virtual public IAlgTool
{
public:
    static const InterfaceID& interfaceID();

    virtual StatusCode newMethod1()=0;
    virtual void newMethod2(bool which_one)=0;

protected:
    virtual ~IMyTool();
};

Since other packages may want to write tools to this interface, this header file should be public and go in:

MyPackage/MyPackage/IMyTool.h

Interface implementation file

The new methods added are pure-virtual and so do not have implementation. But, the boiler plate methods need to be defined. This is done in:

MyPackage/src/IMyTool.cc

which should hold something like:

#inculde "MyPackage/IMyTool.h"

namespace { const InterfaceID IID_IMyTool("IMyTool",0,0); }

const Interface ID& IMyTool::interfaceID()
{
    return IID_IMyTool;
}

IMyTool::~IMyTool() {}

Writing a concrete Tool

After writing the (or finding an already existing) interface you can now write your own implementation.

A concrete Tool class definition

A minimal class definition is:

#include "MyPackage/IMyTool.h"
#include "GaudiAlg/GaudiTool.h"

class MyTool : public GaudiTool,
               virtual public IMyTool
{
public:

    MyTool(const std::string& type,
           const std::string& name,
           const IInterface* parent);
    virtual ~MyTool();

    // GaudiTool interface
    virtual StatusCode initialize();
    virtual StatusCode finalize();

    // IMyTool interface
    virtual StatusCode newMethod1();
    virtual void newMethod2(bool which_one);
};

Some things to note:

  • You could just as well inherited from AlgTool instead of GaudiTool but the latter base class provides a lot of useful methods that will make your code simpler.
  • The class must have a constructor as shown.
  • This is a minimal class, you can also add additional methods, however they will not be visible when a user uses your class. Users only see the IMyTool interface methods.

Unless this class is meant to be a base class for others, this header should be private and be placed in:

MyPackage/src/MyTool.h

A Tool's implementation file

A minimal implementation looks like:

#include "MyTool.h"

MyTool::MyTool(const std::string& type,
               const std::string& name, 
               const IInterface* parent)
    : GaudiTool(type,name,parent)
{
    declareInterface<IMyTool>(this);
}

MyTool::MyTool() {}

// GaudiTool methods:

StatusCode MyTool::initialize() 
{
    this->GaudiTool::initialize();

    ... your code goes here ...

    StatusCode sc = ...;
    return sc;
}

StatusCode MyTool::finalize()
{
    ... your code goes here ...

    return this->GaudiTool::finalize();
}

// IMyTool methods:

StatusCode newMethod1()
{
    ... your code goes here ...
    StatusCode sc = ...;
    return sc;
}

void newMethod2(bool which_one)
{
    ... your code goes here ...
}

Note:

  • The class must initialize the GaudiTool parent class constructor.
  • The class must call declareInterface for each Interface class from which it directly inherits.
  • The class must call GaudiTool::initialize() before any of its initialization code.
  • The class must call GaudiTool::finalize() after any of your finalization code.

Plugging your Tool into Gaudi

For your tool to be found by gaudi you must do a few special things:

The cmt/requirements file

You tool needs to be build as a "component". CMT will do that for you by applying this pattern in the cmt/requirements file:

apply_pattern component_library library=GenTools

This is in addition to all the usual CMT requirements.

Declaring the Tool and its Factory

Gaudi needs some boilerplate code to create an instance of your tool. This is done by creating a file:

MyPackage/src/MyPackage_entries.cc

(actually any filename will do). A minimal example of such a file is:

#include "MyTool.h"
#include "GaudiKernel/DeclareFactoryEntries.h"

DECLARE_TOOL_FACTORY(MyTool);
DECLARE_TOOL_FACTORY(MyOtherTool);
DECLARE_ALGORITHM_FACTORY(MyAlgorithm);
DECLARE_FACTORY_ENTRIES(MyPackage) {
    DECLARE_TOOL(MyTool);
    DECLARE_TOOL(MyOtherTool);
    DECLARE_ALGORITHM(MyAlgorithm);
}

For each tool you need to declare it and its factory using the appropriate macro. Also shown is how an example algorithm called MyAlgorithm is declared.

Loading the factory

Some final boilerplate code is needed to tell Gaudi to load the factories defined above. This is done with a file:

MyPackage/src/MyPackage_load.cc

(again, any file name will do). For this example the complete file is:

#include "GaudiKernel/LoadFactoryEntries.h"
LOAD_FACTORY_ENTRIES(MyPackage);

Exposing the Tool's Interface to Python

First follow the general GaudiPython#Exposing Interfaces.

You can then access your tool from python like:

import GaudiPython as gp
app = gp.AppMgr()
mytool = app.toolsvc().create("MyToolName",interface="IMyToolInterface")

You may get errors about failing to load libraries. If so you my have to load them by hand like:

gp.loaddict("libMyTool")
gp.loaddict("libMyToolDict")

User Configuration of Public Tools

A Tool, like an algorithm, will likely have properties so that a user can configure it. For details on properties see Gaudi Properties.

The user must configure a tool differently than an algorithm. This is because tools will be created and initialized when they are first used and so must be configured before that.

To do this, you configure the IProperty that the Tool inherits from. For example, assume MyTool has a "MyProperty" property. To configure it from Python the user must do:

# Get the Python interface to Gaudi
import gaudimodule as gm

# Get Gaudi application manager
app = gm.AppMgr()

# Get the Tool as a property object.  By default it is owned by the
# tool svc so we must name it as shown:
tool = app.property("ToolSvc.MyTool")

# Set the tool's MyProperty:
tool.MyProperty = "Fast, cheap and reliable.  Choose 2"

Later, if some algorithm can be configured with this tool, it is done by giving the class name. For example:

alg = app.algorithm("MyAlgorithm")
alg.ToolToUse = "MyTool"

Create and Config Multiple (Private) Instances of the Same Tool

In c++ code:

toolSvc()->retrieveTool(ToolType,ToolName,pITool);

ToolType is the class name.

ToolName is the name of one instance. ToolName will be used to identify one instance.

pITool is a pointer to ITool, the abstract class, which will has the return value.


In python code:

app.property('ToolSvc.ToolName').property = value

ToolName should be the same as in your code.

property will be replaced by a real property declared in tool.

valve is what is assigned to the property.