ProditionsCode

From Mu2eWiki
Revision as of 16:16, 17 February 2020 by Rlc (talk | contribs)
Jump to navigation Jump to search

Introduction

ProditionsService is the principle user interface for accessing conditions data. ProditionsService provides conditions data "entities", logical groups of conditions data. Each entity should be able to be filled from fcl or from the database. The fcl version is useful for creating a nominal content, for simulation or for testing. It is also useful if the database contents are not available for any reason. Some entities will contain data that is constant and could be loaded from fcl (such as configuration for the code), and also run-dependent data from the conditions database. A logical approach is to always create the fcl version, then overwrite values or add values from the database, if requested. Every entity has a switch to turn off database access.

Proditions is mostly a layer of code over the DbService. It allows the creating and caching quantities derived from DbService database tables and other, lower-level Proditions entities, creating an unlimited stack of database-derived cached quantities. It will sense if a derived quantity is out of date, because the run/subrun changed and the underlying input data changed, and update itself as the user accesses the entity.

This page explains how to add a Proditions entity. Since the structure of Proditions has wide-ranging effects, and includes complexities such as thread safety, any changes should be presented to, and designed in consultation with the software group.


Adding an Entity

The easiest approach is to copy an existing entity, for example StrawPhysics. There will be several new files in two directories. This example is in TrackerConditions and TrackerConfig, but obviously calorimeter entities should be in their respective directories, etc. It is crucial to maintain directory and code name patterns, or it will be very much harder to maintain. For each Proditions entity, there is one identifying name string, in this case "StrawElectronics" which will be used everywhere.

  • TrackerConditions/inc/StrawElectronics.hh The definition of the container that holds the useful data. It must
    • inherit from ProditionsEntity
    • initialize, hold and access its name, the name function must override the base class definition
    • optionally override a base class print function
namespace mu2e {
  class StrawElectronics : public ProditionsEntity {
  public:
    accessors ...
    void print(std::ostream& os) const;
    std::string const& name() const { return _name; }
  private:
    std::string _name;
    data ...
}
  • TrackerConditions/src/StrawElectronics.cc implementation of methods of the class. This should only manipulate the data of the class, it must not access any run-dependent information other than than the class data. The run dependence is always explicit in the Proditions service, which creates new entities as needed. Ideally, this class is a simple container of data and does not even link in utility classes, unless necessary.
  • TrackerConfig/inc/StrawElectronicsConfig.hh This is definition of a fcl configuration that contains all the data required to initialize a nominal fcl-driven version of the entity.
    • It must have the verbose and useDb flags.
namespace mu2e {
  struct StrawElectronicsConfig {
    using Name=fhicl::Name;
    using Comment=fhicl::Comment;
    fhicl::Atom<int> verbose{
      Name("verbose"), Comment("verbosity: 0 or 1")}; 
    fhicl::Atom<bool> useDb{
     Name("useDb"), Comment("use database or fcl")}; 
   data initialization values ....
  }
}
  • TrackerConditions/inc/StrawElectronicsMaker.hh definition of a utility class that takes the fcl config, database tables or other proditions entities as input and fills this entity.
    • it must have one constructor which takes the config as an arugment
    • it must have a fromFcl() method
    • it must have a fromDb() methos which take as arguments database tables and other Proditions entities
    • it must cache the fcl config
namespace mu2e {
  class StrawElectronicsMaker {
  public:
    StrawElectronicsMaker(StrawElectronicsConfig const& config):_config(config) {}
    StrawElectronics::ptr_t fromFcl();
    StrawElectronics::ptr_t fromDb(TrkDelayPanel::cptr_t tdp,
                                  TrkPreampRStraw::cptr_t tprs,
                                  TrkPreampStraw::cptr_t tps,
                                  TrkThresholdRStraw::cptr_t ttrs );
  private:
    // this object needs to be thread safe, 
    // _config should only be initialized once
    const StrawElectronicsConfig _config;
  };
}
  • TrackerConditions/src/StrawElectronicsMaker.cc implementation of how to fill the entity from the inputs. The contents here can be complex and call other utility routines. Any run dependency must come explicitly from the fromDb() arguments - db tables or other Proditions entities.
  • TrackerConditions/inc/StrawElectronicsCache.hh This contains the guts of the run dependence. As runs pass and updated entities are needed this class creates them and caches them (in the base class).
    • it must inherit from ProditionsCache
    • it must implement on constructor that takes the fcl config as an argument
    • it must implement method initialize() which creates handles to the database tables
    • it must implement method set_t makeSet(art::EventID const& eid) to create a set of CID's that determine the complete database contents it was created from. This set is uniquely identifies the contents of a copy of this entity.
    • it must implement method DbIoV makeIov(art::EventID const& eid) to create one interval of validity from its constituent input intervals.
    • it must implement method ProditionsEntity::ptr makeEntity(art::EventID const& eid) This is where the "maker" is called to actually make the entity.
    • it must contain pointers to handles for the database object and Proditions entities it depends on. This is how it gains its run dependence.


  • TrackerConditions/fcl/prolog.fcl - this should contain the fcl for the fcl initialization of your entity.
StrawElectronics : {
   verbose : 0
   useDb : false
   value : data  ...
}

Now install this entity in the service

  • ProditionsService/inc/ProditionsService.hh
#include "TrackerConfig/inc/StrawElectronicsConfig.hh"
     fhicl::Table<StrawElectronicsConfig> strawElectronics{
         Name("strawElectronics"), 
         Comment("Straw electronics model") };
  • ProditionsService/src/ProditionsService_service.cc:
#include "TrackerConditions/inc/StrawElectronicsCache.hh"
// create the cache
auto sec = std::make_shared<mu2e::StrawElectronicsCache>(_config.strawElectronics());
  • ProditionsService/fcl/prolog.fcl
  strawElectronics : @local::StrawElectronics

Design Considerations

  • the Cache class holds the database and Proditions dependencies as pointers to handles, with the actual handles to be initialized later, when needed, for two reasons.
    • If you create proditions handle when the proditions cache is created then effectively you are trying to intialize Proditions inside Proditions, creating a race condition of initialization
    • if you create a database handle, you force the initialization of the database service. This force the creation of the DbService even if all of your "useDb" switches are off. Lazy initialization allows the db to be truly off if not needed. This is important when it is an external dependence.
    • TrackerConfig is split from TrackerConditions because TrackerConditions must use Proditions and Proditions needs to include the config. Putting TrackerConfig in TrackerConditions woudl create a header file dependency loop.


When adding Proditions entities, there are logical steps.

  1. create all classes, leave fromDb empty, leave useDb off. You don't need to figure out the run dependence details. You can see in StrawDriftCache how to write empty cache classes. In this state you can debug the fcl creation of the entity as well as any code that depends on your entity.
  2. create the code for any new database class, and fill those classes using the text file method. This allows you to test the database tables, and the full cache run-dependent functionality.
  3. create the actual database tables. We recommend leaving this to as late as possible because there is a significant overhead to creating and maintaining the actual database contents. it is also then much harder to change anything. As long as you all local code and text files, changes to tables are relatively trivial.