2 Generic Concepts and Design

The DD4hep conditions mechanism was designed to be very flexible concerning back-end implementations. Most of the conditions and alignment utilities offered by DD4hep are available if a minimal interface is respected. This minimal interface includes a container called ConditionsMap (See section 2.2) and the layout of the conditions objects (See section 2.1). The conditions objects contain a flexible user defined payload and a generic, interface used to interact with tools and the generic container object or conditions slices as described in section 1.2.

2.1 Condition Objects and Conditions Data

A conditions objects serves two purposes:

  • Firstly, it supports the basic functionality which is generic to any condition – independent of the actual user payload. This information includes access to the interval of validity and the key to uniquely identify the condition.
  • Secondly, the objects hosts and manages a user payload, the actual conditions data. These data are freely user defined. An automatic parsing mechanism from a string representation is supported if the payload-class can properly described using a boost::spirit parsing structure. Default type implementations are defined for
    • all primitive data types,
    • ROOT::Math::XYZVector, ROOT::Math::XYZPoint, ROOT::Math::PxPyPzEVector.
    • a number of STL containers of the above basic data types:
      std::vector<TYPE>, std::list<TYPE>, std::set<TYPE>,
      std::map<int,TYPE>, std::map<string,TYPE>,
      std::pair<int,TYPE>, std::pair<string,TYPE>.

    Additional types can easily be implemented using boost::spirit if the basic representation is in the form of a string. Dummy boost::spirit parsers may be implemented if the conversion to and from strings is not required.

  • Thirdly, it supports the basic functionality required by a conditions management framework, which implements the ConditionsMap interface.

For completeness we include here the basic data access methods of the conditions class
(see DD4hep/Conditions.h ):

  class Condition: public Handle<detail::ConditionObject> {  
    /** Interval of validity                                   */  
    /// Access the IOV type  
    const IOVType& iovType()  const;  
    /// Access the IOV block  
    const IOV& iov()  const;  
 
    /** Conditions identification using integer keys.          */  
    /// Hash identifier  
    key_type key()  const;  
    /// DetElement part of the identifier  
    detkey_type  detector_key()  const;  
    /// Item part of the identifier  
    itemkey_type item_key()  const;  
 
    /** Conditions meta-data and handling of the data binding  */  
    /// Access the opaque data block  
    OpaqueData& data()  const;  
    /// Access to the type information  
    const std::type_info& typeInfo() const;  
    /// Access to the grammar type  
    const BasicGrammar& descriptor() const;  
    /// Check if object is already bound....  
    bool is_bound()  const  {  return isValid() ? data().is_bound() : false;  }  
    /// Bind the data of the conditions object to a given format.  
    template <typename T> T& bind();  
    /// Set and bind the data of the conditions object to a given format.  
    template <typename T> T& bind(const std::string& val);  
    /// Generic getter. Specify the exact type, not a polymorph type  
    template <typename T> T& get();  
    /// Generic getter (const version). Specify the exact type, not a polymorph type  
    template <typename T> const T& get() const;  
    ...  
  };

Please be aware that the access to the IOV and the IOVType is only possible if supported by the caching mechanism.

Using the OpaqueData data structure and its concrete implementation, the user can map any data item to the conditions object using the bind() method and retrieve the data back using get(). Clearly, the left should know what the right does and the types to be retrieved back must match be bound data types.

The following code-snippet shows how to bind conditions data:

  Condition cond = ...;  
  // Fill conditions data by hand:  
  std::vector<int>& data = cond.bind<std::vector<int> >();  
  data.push_back(0);  
  data.push_back(1);    .....  
 
  // Fill conditions data from the string representation using boost::spirit:  
  std::string str = "[0,1,2]";  
  std::vector<int>& data = cond.bind<std::vector<int> >(str);  
  int i = data[0]; ....

This is an example how to access already bound data:

  Condition cond = ...;  
 
  // Fill conditions data by hand:  
  std::vector<int>& data = cond.get<std::vector<int> >();

2.2 The ConditionsMap Interface

The ConditionsMap interface (see defines the lowest common denominator to allow tools or clients to interact with conditions of a given slice. This interface defines the interaction of clients with a conditions slice. These interactions cover both the data access and the data management within a slice. The interface allows to

  • access individual conditions by the detector element and a given item key. The interface allows
  • to scan conditions according to the detector element or
  • to scan all conditions contained. Further it allows
  • insert conditions to the mapping and
  • to clear the content.

The provision of these basic interaction mechanisms allows us to build very generic tools firstly for conditions, but also later for the management and th computation of alignment data as described in the DDAlign manual [2].

The ConditionsMap interface class, which supports this basic functionality has the following entry points:

  class ConditionsMap   {  
  public:  
    /// Insert a new entry to the map. The detector element key and  
    /// the item key make a unique global conditions key  
    virtual bool insert(DetElement         detector,  
                        Condition::itemkey_type key,  
                        Condition          condition) = 0;  
    /// Interface to access conditions by hash value. The detector element key  
    /// and the item key make a unique global conditions key  
    virtual Condition get(DetElement              detector,  
                          Condition::itemkey_type key) const = 0;  
    /// Interface to scan data content of the conditions mapping  
    virtual void scan(const Condition::Processor& processor) const = 0;  
 
    /// No ConditionsMap overload: Access all conditions within  
    /// a key range in the interval [lower,upper]  
    virtual std::vector<Condition> get(DetElement              detector,  
                                       Condition::itemkey_type lower,  
                                       Condition::itemkey_type upper)  const;  
 
    /// Interface to partially scan data content of the conditions mapping  
    virtual void scan(DetElement                  detector,  
                      Condition::itemkey_type     lower,  
                      Condition::itemkey_type     upper,  
                      const Condition::Processor& processor) const;  
  };

Such ConditionsMap implementations can easily be constructed using standard STL maps. The lookup key is constructed out of two elements:

  • The detector element this condition belongs to and
  • an identifier of condition within this detector element.

An efficient implementation of a longword key would consist of the tuple: [hash32(conditionsname),hash32(det elementpath)],

which resembles to an ordered sequence of conditions according to their detector element. A special implementation, which implements this user interface is the ConditionsSlice implemented in the DDCond package (See section 3 for details).

2.3 Common Conditions Tools

  • ConditionsPrinter A tool to print conditions by scanning conditions for a single DetElement or the entire sub-tree. See DDCore/ConditionsPrinter.h for details).
  • ConditionsProcessor A wrapper to support condition functors implementing the default callback: intoperator()(Conditionconsition);

    The return value may be used to e.g. collect counters.