Project

General

Profile

Guide to writing and using art tools

Before reading this, please consult the guide here.

There are two kinds of tools:

  • Function tools, which are equivalent to dynamically loaded free functions. These should be considered before using class tools.
  • Class tools, which are used whenever it is necessary to retain state between function calls.

art tools can be created anywhere: inside of modules, services, sources, and other plugins, even including inside of other tools. Any plugin that can be configured through a FHiCL file can use any number of tools, each of which are configured via the FHiCL file.

Each tool is local only to the scope in which it is created--i.e. there is no global registry of tools like there is for services.

In contrast to art sources and modules, each tool should have a header file. The reason for this is that whereas the user never explicitly calls member functions of a source or module, a user will explicitly create and call tool functionalities in their own source code.

Configuration validation

It is possible to use configuration validation and description with art tools. If you are interested in doing so, please contact . To glean the list of available tools, type:

art --print-available=tool

For more details on a particular tool, type

art --print-description MyTool

All function tools automatically enable configuration description.


art function tools

Defining a function tool

In the header file (e.g. makeSoftTracks.hh), declare the free function. For example, suppose you would like to create a collection of tracks from products in the art::Event. The free-function declration might look like:

#include "art/Framework/Principal/Event.h" 
#include "test/HitCollection.hh" 
#include "test/Track.hh" 

#include <vector>

namespace test {

  std::vector<test::Track> makeSoftTracks(test::HitCollection const&);

  // Or you can create a type alias to a function whose argument is a const reference
  // to a test::HitCollection and that returns a vector of tracks;

  using makeTracks_ft = std::vector<test::Track>(test::HitCollection const&);
  makeTracks_ft makeSoftTracks;

}

The function name can be any name. Now, create a separate .cc file that has the tool suffix (e.g. makeSoftTracks_tool.cc):

#include "art/Utilities/ToolMacros.h" 
#include "test/makeSoftTracks.hh" 

namespace test {
  std::vector<test::Track> makeSoftTracks(test::HitCollection const& hits)
  {
    std::vector<test::Track> tracks;
    // ... use hits to make tracks
    return tracks;
  }
}

DEFINE_ART_FUNCTION_TOOL(test::makeSoftTracks, "TrackAlgorithm")

Macro invocation

The DEFINE_ART_FUNCTION_TOOL macro invocation inserts the code art needs to load the appropriate function whenever a user requests it. The macro takes two arguments:

  • The namespace-qualified name of the function, and
  • An identifier that describes what the purpose of the function is for.

The identifier is an extra safety measure imposed so that the correct function tool is loaded when requested by the user.

Configuring your job to use a function tool

If you would like your tool to be configurable through FHiCL, your module configuration needs to have a nested table, that contains the assignment:

tool_type: <base_name_of_tool>
For example, in the above function tool definition, the shared object library generated has the form libdir1_dir2_makeSoftTracks_tool.so. The base name of the tool is therefore makeSoftTracks [1]. The following is FHiCL table is therefore an allowed configuration.

physics.producers.trackProducer: {
  module_type: TrackProducer
  trackAlgo: {
    tool_type: makeSoftTracks
  }
}

Note that an instance of a tool is attributed to one and only one plugin (module, in this case). This is encapsulation is by design--keeping tool instances local to a given plugin allows for flexible run-time behavior while still retaining the safety inherent in independent, factorized libraries.

1 As is the case for modules, one can also specify the long form to disambiguate between potential tools (i.e. tool_type: "dir1/dir2/makeSoftTracks").
 

Creating a function-tool instance

Let's assume you have a producer module that creates a collection of tracks and puts them onto the event. An example of how a tool can be used to fill this collection is:

#include "art/Framework/Core/EDProducer.h" 
#include "art/Utilities/make_tool.h" 
#include "test/makeSoftTracks.hh" 

namespace test {
  class TrackProducer : public art::EDProducer {
  public:

    MyProducer(fhicl::ParameterSet const& ps)
      : makeTracks_{art::make_tool<decltype(test::makeSoftTracks)>(ps.get<ParameterSet>("trackAlgo"), "TrackAlgorithm")}
      , hitsTag_{ps.get<art::InputTag>("hitsTag")}
    {}

    void produce(art::Event& e) override
    {
      auto const& hits = e.getValidHandle<test::HitCollection>(hitsTag_);
      auto tracks = makeTracks_(hits);
      e.put(std::make_unique<decltype(tracks)>(std::move(tracks));
    }
  };
}

art::make_tool invocation

Creating an instance of a tool is done by calling the art::make_tool<FT> function template. The return type of art::make_tool<FT> is an std::function<FT> object, where FT is the template argument corresponding to a function type, typically of the form ReturnType(FunctionArgs...). The following template instantiations are equivalent:

auto const& toolPSet = ps.get<ParameterSet>("trackAlgo");

// Encouraged
auto fn1 = art::make_tool<test::makeTracks_ft>(toolPSet, "TrackAlgorithm")}
auto fn2 = art::make_tool<decltype(test::makeSoftTracks)>(toolPSet, "TrackAlgorithm")}

// Acceptable, but discouraged
auto fn3 = art::make_tool<std::vector<test::Track>(test::HitCollection const&)>(toolPSet, "TrackAlgorithm")}

In the calls above, the only differences are in the provided template argument. For the first encouraged form, the type alias introduced above in the header file is used. For the second encouraged form, the compiler deduces what the function type should be based on the name of the function you provide. The third option makes the function-type explicit--the return type of the function is an std::vector<test::Track>, and the function receives an const reference to an test::HitCollection object. The last option is discouraged because it requires a synchronization between how the function tool is actually declared on what is specified as the template argument to art::make_tool<FT>.

Invoking the tool

As shown above, the makeTracks_ object is the result of having called art::make_tool<FT>. It can now be used as a function object:

auto tracks = makeTracks_(hits);

where tracks is of type std::vector<test::Track>, and hits is of type test::HitCollection.

Link-time dependencies

For compiling a tool (i.e. the .cc file that contains the DEFINE_ART_FUNCTION_TOOL macro invocation), one must link against the cetlib, cetlib_except, fhiclcpp, and Boost system and file-system libraries, plus any other libraries on which the tool itself depends. For function tools that call other function tools, an additional link-time dependency against art_Utilities is required. For users of cetbuildtools or mrb, the listed libraries are automatically included as link-time dependencies whenever the simple_plugin CMake macro is used (e.g.):

# The following line implicitly provides link-time dependencies on
# cetlib, cetlib_except, fhiclcpp, and Boost (file-)system libraries.
# Additional library dependencies should be listed after "tool".
simple_plugin(makeSoftTracks "tool")  

When invoking a tool, which is done through the call art::make_tool<some_function_type>(...), one must link against the art_Utilities library.


art class tools

In contrast to function tools, class tools are, by nature of their class type, very flexible and highly configurable. A primary benefit of class tools is that users can define a base class interface, include the appropriate header (and possibly link-time) dependencies for that base class, and not be constrained to linking against a particular derived-class library. In what follows, we will give an example of an EventCounter base class.

Defining a class tool

The header for a class tool should just be the header of the interface class you which to use. For example [2]:

2 In this example, we do not use the non-virtual public interface approach since we do not expect the base class to be changed frequently. See coding standard 39 of C++ Coding Standards: 101 Rules, Guidelines, and Best Practices by Sutter and Alexandrescu (2005).

// test/Counter.hh
namespace test {
  class Counter {
  public:
    virtual ~Counter() noexcept = default; 
    virtual void update(unsigned n) = 0;
    virtual unsigned count() const = 0;
  };
}

Since the update function is pure virtual, the derived class, which will be loaded whenever the art::make_tool is called, will supply the correct definition.

To create a specific class tool, create a derived type of test::Counter (e.g.):

#include "art/Utilities/ToolMacros.h" 
#include "fhiclcpp/ParameterSet.h" 
#include "test/Counter.hh" 

namespace test {

  class SimpleCounter : public Counter {
  public:

    explicit SimpleCounter(fhicl::ParameterSet const& ps) :
      count_{ps.get<unsigned>("offset")}
    {}

    void update(unsigned const n) override
    {
      count_ += n;
    }

    unsigned count() const override { return count_; }

  private:
    unsigned count_;
  };

}

DEFINE_ART_CLASS_TOOL(test::SimpleCounter)

Note that unlike art modules, the test::SimpleCounter does not inherit from an art-provided base class.

Class tool constructor

The class tool constructor can have only one argument: either a const reference to a fhicl::ParameterSet, or a const reference to a type that supports implicit conversion from a fhicl::ParameterSet. The ParameterSet provided to the user is the ParameterSet that is configured for that particular tool for a given module, service, source, or other plugin (see below).

Macro invocation

Unlike the function tools, only the namespace-qualified name of the class is required when calling DEFINE_ART_CLASS_TOOL.

Configuring your job to use a class tool

As in configuring a function tool, a class tool is configured by providing a nested table in the configuration for the plugin in which it is to be used. For example, an analyzer that intends to use the class tool defined above would specify the following configuration:

physics.analyzers.trackAnalyzer: {
  module_type: TrackAnalyzer
  trackCounter: {
    tool_type: SimpleCounter
    offset: 0
  }
}

Creating a class-tool instance

The below code is an example of how a tool could be used to count the number of tracks in a given job.

#include "art/Framework/Core/EDAnalyzer.h" 
#include "art/Utilities/make_tool.h" 
#include "messagefacility/MessageLogger/MessageLogger.h" 
#include "test/Counter.hh" 

namespace test {

  class TrackAnalyzer : public art::EDAnalyzer {
  public:

    explicit TrackAnalyzer(fhicl::ParameterSet const& ps)
      : trackCounter_{art::make_tool<Counter>(ps.get<ParameterSet>("trackCounter"))}
      , tracksTag_{ps.get<art::InputTag>("tracksTag")}
    {}

    void analyze(art::Event const& e) override
    {
      auto const& tracks = e.getValidHandle<std::vector<Track>>(tracksTag_);
      tracksCounter_->update(tracks.size());
    }

    void endJob() override
    {
      mf::LogInfo("TrackAnalyzer") << "Number of tracks seen in job: " << tracksCounter_->count() << '\n';
    }

  private:
    std::unique_ptr<Counter> trackCounter_;
    art::InputTag tracksTag_;
  };

}

art::make_tool invocation

For class tools, the art::make_tool function template:

  • Returns an std::unique_ptr of the (base) class type provided as the template parameter (test::Counter in this case).
  • Receives the ParameterSet whose corresponding FHiCL table contains the tool_type assignment.
  • Does not have an extra argument as in the function tool.

Whenever the art::make_tool template is invoked, it passes the specified ParameterSet to the constructor of the tool, whose type is specified by tool_type. For this example, since the configuration file specified a tool_type of test::SimpleCounter, an object of type test::SimpleCounter will be constructed, receiving the ParameterSet corresponding to the FHiCL table trackCounter. The class tool then has access to the configuration parameters contained by trackCounter.

Using the class tool interface

After a class tool object has been created, the user can call any of the accessible interface of the class through the std::unique_ptr returned by the art::make_tool invocation.

Link-time dependencies

As in function tools, the required dependencies for compiling the tool are cetlib, cetlib_except, fhiclcpp, the Boost system and file-system libraries, and any other link-time dependencies required by using the base-class interface. When invoking the tool (through calling art::make_tool<some_base_class>(...)), one must link against the art_Utilities library.