Project

General

Profile

Services that produce data products (as of art 2.10)

There are cases when thread-safe access to external information is necessary for an art process, but fetching the information through a module is either awkward or inappropriate. In these cases, art allows users to create a producing service, where products can be inserted immediately after the event, subrun, or run has been read (or created) by the input source.

art provides post-read functions that are called in a thread-safe context. In order for a service to produce a data product via one of these functions, it must publicly inherit from the art::ProducingService base class.

art::ProducingService base class

Suppose an experiment would like to create a producing-service that provides an EField product. An example class definition would be:

namespace expt {
  class EFieldService : public art::ProducingService {
  public:
    struct Config {...};
    using Parameters = art::ServiceTable<Config>;
    explicit EFieldService(Parameters const&);

    // If not using configuration validation and description, the
    // following is also allowed:
    //   explicit EFieldService(fhicl::ParameterSet const&); 

  private:
    void postReadRun(art::Run& r) override;
    void postReadSubRun(art::SubRun& sr) override;
    void postReadEvent(art::Event& e) override;
  };
}

The constructor signature

As shown in the above definition, the constructor of a class deriving from art::ProducingService must be able to accept an art::ServiceTable<Config> object (using in configuration description/validation), or a fhicl::ParameterSet object. The constructor cannot additionally accept an art::ActivityRegistry object--an attempt to do so will yield a compile-time failure. All ActivityRegistry-interaction is handled implicitly by the art::ProducingService base class.

The postRead* virtual functions

None of the postRead* virtual functions are required to be overridden. However, it is the overriding of the virtual functions that allows users to insert products into the corresponding passed-in objects:

void
expt::EFieldService::postReadRun(art::Run& r)
{
  auto e_field = std::make_unique<EField>(...);
  // prepare 'some_product'
  r.put(move(e_field), art::fullRun());
}

Header and link-time dependencies

The necessary header for inheriting from art::ProducingService is art/Framework/Core/ProducingService.h. The service plugin library must link against the art_Framework_Core library.

Product declarations

Products to be produced by a producing service are declared in the same way as in art modules: in the constructor of the class (see the documentation). For example:

expt::EFieldService::EFieldService(Parameters const& p)
  : // initialize members here
{
  // Declare that a Run product of type 'EField' and 
  // instance empty name ("") will be produced by this service
  // in the postReadRun function.
  produces<EField, art::InRun>();  
}

The example shown above for the postReadRun override could then be used to create the correct product.

Failing to put products

For a producing service, it is an error to not insert a product in the postRead* function when a corresponding 'produces' call has been made in the constructor. This behavior is not configurable.

Producing-service definitions

Classes that inherit from art::ProducingService should be defined entirely in a source file (e.g. EFieldService_service.cc) ; the file must invoke the DEFINE_ART_PRODUCING_SERVICE macro at the bottom (e.g.):

// EFieldService_service.cc
#include "art/Framework/Core/ProducingService.h" 

// Other header dependencies...

namespace expt {
  class EFieldService : public art::ProducingService {
  // ...
  };
}

DEFINE_ART_PRODUCING_SERVICE(expt::EFieldService)

Product signatures

A data product is fully specified by five pieces of metadata:

  • The ("friendly") C++ type name
  • The label for the module that produced the product
  • The instance name
  • The process in which the product was produced
  • The data-containment level in which it was produced (i.e. Event, SubRun, Run, or Results)

For products created by producing services, the label corresponds to the user-specified configuration. For example, in order to enable the EFieldService, a user would include the following in his/her FHiCL file1:

services: {
  EFieldService: {...}
}

An (e.g.) analyzer module downstream would thus look up the product in the following way:

void
expt::MyAnalyzer::analyze(art::Run const& r)
{
  art::InputTag const tag{"EFieldService"};
  auto const& h = r.getValidHandle<EField>(tag); // ==> art::ValidHandle<EField>
}

1 The product signature for the product produced by EFieldService would thus be 'EField_EFieldService__<the current process> InRun'. In an art/ROOT file, this would correspond to a TBranch with the name 'EField_EFieldService__<the current process>' in the Runs TTree.
 

Product availability

There may be times when a product must be retrieved in a producing service before a product is inserted. Looking up products provided by the input source is fully supported for any of the postRead* functions; however, any attempts to look up products to be produced by the current process will fail, resulting in an art::ProductNotFound exception throw. For example:

void
expt::EFieldService::postReadRun(art::Run& r)
{
  auto const& h1 = r.getValidHandle<Product1>("product_from_source"); // Ok
  auto const& h2 = r.getValidHandle<Product2>("product_from_this_process"); // ! Error ==> exception throw
  // ...
  r.put(...);
}

Additional restrictions

There are a few more restrictions for an art::ProducingService:

  • Since an art::ProducingService is intended only for inserting products, an art::ServiceHandle cannot be created for any service that inherits from it. Any attempt to do so will result in a compile-time failure.
  • Because a ServiceHandle cannot be created to a derived type of a ProducingService, it is not possible to create a service interface/implementation. Attempting to do so will also result in a compile-time failure.