Skip to content
Snippets Groups Projects
Commit 90048da8 authored by Hadrien Benjamin Grasland's avatar Hadrien Benjamin Grasland
Browse files

First runnable condition usage example

parent 91808eea
No related branches found
No related tags found
No related merge requests found
Showing
with 545 additions and 0 deletions
...@@ -35,6 +35,7 @@ set(GaudiExamples_srcs ...@@ -35,6 +35,7 @@ set(GaudiExamples_srcs
src/AlgSequencer/*.cpp src/AlgSequencer/*.cpp
src/AlgTools/*.cpp src/AlgTools/*.cpp
src/AnyData/*.cpp src/AnyData/*.cpp
src/Conditions/*.cpp
src/DataOnDemand/*.cpp src/DataOnDemand/*.cpp
src/AlgErrAud/*.cpp src/AlgErrAud/*.cpp
src/NTuples/*.cpp src/NTuples/*.cpp
......
from Gaudi.Configuration import *
from Configurables import AvalancheSchedulerSvc, DemoConditionAlg
from Configurables import HiveWhiteBoard, HiveSlimEventLoopMgr
from Configurables import DemoBackend__ConditionContextProducer as ConditionContextProducer
from Configurables import DemoBackend__detail__ConditionBackendSvc as ConditionBackendSvc
# Application setup
condBackend = ConditionBackendSvc("ConditionBackendSvc")
scheduler = AvalancheSchedulerSvc(ThreadPoolSize=1)
slimEventLoopMgr = HiveSlimEventLoopMgr(OutputLevel=INFO)
whiteBoard = HiveWhiteBoard("EventDataSvc", EventSlots=2)
# - Algorithms
topAlgs = [ConditionContextProducer("ConditionContextProducer"),
DemoConditionAlg("DemoConditionAlg")]
# Application manager
app = ApplicationMgr(EvtMax=10,
ExtSvc=[whiteBoard, condBackend],
EventLoop=slimEventLoopMgr,
EvtSel='NONE',
HistogramPersistency='NONE',
TopAlg=topAlgs)
#pragma once
#include <string>
#include "GaudiKernel/Property.h"
#include "ConditionContext.h"
#include "ConditionKey.h"
namespace DemoBackend
{
// A condition accessor is to condition data what an EventReadHandle is to
// event data : it notifies the framework that an Algorithm or AlgTool depends
// on a certain condition, enables configuring the target condition from
// Python, and allows accessing that condition during event processing.
//
// ConditionAccessors are meant to be declared as members of the Algorithm or
// AlgTool that owns them. Declaring them in any other place (on the stack,
// on a separate heap location...) is likely to result in undefined behaviour.
//
template <typename T>
class ConditionAccessor {
public:
// Constructor takes the "this" pointer of the owner and the usual triplet
// of values needed to declare a property (in this case the condition's key)
template <typename Owner>
ConditionAccessor( Owner* owner,
const std::string& keyName,
const ConditionKey& keyDefault = {},
const std::string& keyDoc = "" )
: m_key{ owner, keyName, keyDefault, keyDoc }
{
owner->registerConditionAccessor( *this );
}
// Condition accessors can neither be moved nor copied
ConditionAccessor( const ConditionAccessor& ) = delete;
ConditionAccessor( ConditionAccessor&& ) = delete;
ConditionAccessor& operator=( const ConditionAccessor& ) = delete;
ConditionAccessor& operator=( ConditionAccessor&& ) = delete;
// Access the key which this ConditionAccessor points to.
// The key may change during configuration of the owner.
const ConditionKey& key() const {
return m_key;
}
// Access the value of the condition, for a given condition context
const T& get( const ConditionContext& ctx ) const {
return ctx.getCondition<T>( m_key );
}
private:
// Configurable key which this ConditoinAccessor points to.
Gaudi::Property<ConditionKey> m_key;
};
}
\ No newline at end of file
#pragma once
#include "GaudiKernel/Algorithm.h"
#include "ConditionAccessorHolder.h"
namespace DemoBackend
{
// Base class for Algorithms that can use ConditionAccessors
using ConditionAccessorAlg = ConditionAccessorHolder<Gaudi::Algorithm>;
}
\ No newline at end of file
#pragma once
#include <typeinfo>
#include <utility>
#include <vector>
#include "GaudiKernel/EventDataHandle.h"
#include "GaudiKernel/StatusCode.h"
#include "ConditionAccessor.h"
#include "ConditionBackendSvc.h"
#include "ConditionContext.h"
#include "ConditionContextPath.h"
#include "ConditionKey.h"
namespace DemoBackend
{
// Wrapper around Algorithm or AlgTool that enables it to depend on
// conditions via ConditionAccessors.
//
// Together with ConditionAccessor, this is the main interface through which
// Algorithm and Tool writers interact with the condition management backend.
// Everything else is, in a sense, an implementation detail of them.
//
template <typename Base>
class ConditionAccessorHolder : public Base {
public:
// Forward the constructor from the Algorithm / AlgTool base class
using Base::Base;
// The base class exposes to the user all the other components of the
// chosen back-end's interface: accessors, contexts and keys.
template <typename T>
using ConditionAccessor = DemoBackend::ConditionAccessor<T>;
using ConditionContext = DemoBackend::ConditionContext;
using ConditionKey = DemoBackend::ConditionKey;
// At initialize() time, we must declare our condition inputs (whose key
// has been configured by now) to the condition management backend.
StatusCode initialize() override {
// Initialize the base (this will configure condition accessor keys)
auto status = Base::initialize();
if ( status.isFailure() ) return status;
// Get access to the condition backend service
auto backendSvcIF = Base::service( "ConditionBackendSvc" );
if ( !backendSvcIF ) return StatusCode::FAILURE;
auto& backendSvc = dynamic_cast<detail::ConditionBackendSvc&>( *backendSvcIF );
// Declare the condition keys and types that we are going to use
for ( auto& info : m_accessorInfo ) {
backendSvc.registerCondition( *info.first, *info.second );
}
return status;
}
// We must also provide a way for a reentrant algorithm to access the
// condition handling context associated with this event.
const ConditionContext& getConditionContext( const EventContext& ctx ) const {
return m_ctxHandle.get( ctx );
}
private:
// This block of private declarations is an implementation detail of accessors
template <typename T>
friend class ConditionAccessor;
// Track that a new ConditionAccessor has been declared
template <typename T>
void registerConditionAccessor( ConditionAccessor<T>& accessor ) {
m_accessorInfo.emplace_back( &accessor.key(), &typeid( T ) );
}
private:
// We must declare a dependency on the condition context
// (which will be produced by the ConditionContextProducer alg).
Gaudi::EventReadHandle<ConditionContext> m_ctxHandle{
detail::CONDITION_CONTEXT_PATH, this };
// We must track some properties of our condition accessors to declare them
// during initialize(). An alternative would be some ConditionAccessorBase*.
std::vector<std::pair<const ConditionKey*, const std::type_info*>> m_accessorInfo;
};
}
\ No newline at end of file
#include <stdexcept>
#include <unordered_map>
#include "ConditionBackendSvc.h"
namespace DemoBackend
{
namespace detail
{
void ConditionBackendSvc::registerCondition( const ConditionKey& key, const std::type_info& type )
{
if ( m_keysFrozen ) {
throw std::runtime_error( "Attempted to register a condition input after start()" );
}
if ( type != typeid( float ) ) {
throw std::runtime_error( "This dummy condition back-end only supports float conditions" );
}
m_keys.insert( key );
}
StatusCode ConditionBackendSvc::start()
{
auto status = Service::start();
if ( status.isFailure() ) return status;
m_keysFrozen = true;
return status;
}
ConditionContext ConditionBackendSvc::setupEvent( const EventTimestamp& eventTime ) {
if ( !m_keysFrozen ) {
throw std::runtime_error( "Attempted to access a condition before start()" );
}
std::unordered_map<ConditionKey, float> conditions;
for ( const auto& key : m_keys ) {
conditions.emplace( key, 4.2 * eventTime * key.size() );
}
return ConditionContext{ std::move( conditions ) };
}
DECLARE_COMPONENT( ConditionBackendSvc )
}
}
#pragma once
#include <typeinfo>
#include <unordered_set>
#include "GaudiKernel/Service.h"
#include "GaudiKernel/StatusCode.h"
#include "ConditionContext.h"
#include "ConditionKey.h"
#include "EventTimestamp.h"
namespace DemoBackend
{
namespace detail
{
// The actual condition handling back-end implementation (which should be
// a service as it is configurable and shared mutable global state) is not
// part of the propose API, and ConditionAccessorAlgs should never interact
// with it directly.
//
// Therefore, the API surface that is presented here is only a rough example
// of what is needed. A more reasonable back-end implementation would also
// feature performance shortcuts to access declared conditions more quickly,
// to start condition IO and derivation before conditions are actually
// needed, and a way for the ConditionContext destructor to notify the
// back-end that an event is done processing, so that usage of condition
// data can be tracked and unused conditions can be garbage-collected.
//
class ConditionBackendSvc final : public Service {
public:
// Inherited Service constructor
using Service::Service;
// Notify the storage back-end that we will be accessing a condition
// Type info is provided to be able to type-check the condition dataflow
void registerCondition( const ConditionKey&, const std::type_info& );
// When event processing starts, the set of conditions to be used is
// considered complete, and new conditions cannot be registered anymore.
StatusCode start() override;
// Given an event timestamp, this function carries our condition IO and
// derivation or gives access to previously prepared conditions.
ConditionContext setupEvent( const EventTimestamp& );
private:
// Set of condition keys that were registered so far
std::unordered_set<ConditionKey> m_keys;
// Flag indicating when the above set can be considered complete
bool m_keysFrozen = false;
};
}
}
\ No newline at end of file
#include "ConditionContext.h"
namespace DemoBackend
{
ConditionContext::ConditionContext( std::unordered_map<ConditionKey, float>&& data )
: m_data{ std::move( data ) }
{}
}
\ No newline at end of file
#pragma once
#include <stdexcept>
#include <typeinfo>
#include <unordered_map>
#include "ConditionKey.h"
namespace DemoBackend
{
template <typename T>
class ConditionAccessor;
namespace detail
{
class ConditionBackendSvc;
}
// Much like the EventContext gives access to event data, a ConditionContext
// gives access to condition data. For technical reasons, the ConditionContext
// may be produced during event processing and is stored in the TES.
class ConditionContext {
public:
// Normally, we would tell the backend that we're done processing a given
// event via RAII, but this is not necessary for this dummy implementation.
~ConditionContext() = default;
private:
// This block of private declarations is an implementation detail of accessors
template <typename T>
friend class ConditionAccessor;
// Query the condition slot for a given condition
template <typename ConditionType>
const ConditionType& getCondition( const ConditionKey& key ) const {
if ( typeid( ConditionType ) != typeid( float ) ) {
throw std::runtime_error( "This silly backend only supports float conditions" );
}
return m_data.at( key );
}
private:
// This block of private declarations is an implementation detail of the back-end
friend class detail::ConditionBackendSvc;
// Construct a ConditionContext from backend-internal data
ConditionContext( std::unordered_map<ConditionKey, float>&& );
private:
std::unordered_map<ConditionKey, float> m_data;
};
}
\ No newline at end of file
#include "ConditionContextPath.h"
namespace DemoBackend
{
namespace detail
{
DataObjID CONDITION_CONTEXT_PATH{ "/Event/DemoBackendCondCtx" };
}
}
\ No newline at end of file
#pragma once
#include "GaudiKernel/DataObjID.h"
namespace DemoBackend
{
namespace detail
{
// Right now, the path to the condition context in the TES is hardcoded.
// Later on, we'll want to make it configurable.
//
// FIXME: Could just be a Property of the ConditionBackendSvc?
//
extern DataObjID CONDITION_CONTEXT_PATH;
}
}
\ No newline at end of file
#include "ConditionBackendSvc.h"
#include "ConditionContextProducer.h"
#include "EventTimestamp.h"
namespace DemoBackend
{
StatusCode ConditionContextProducer::initialize() {
auto status = Gaudi::Algorithm::initialize();
if ( status.isFailure() ) return status;
m_backendSvcIF = service( "ConditionBackendSvc" );
if ( !m_backendSvcIF ) return StatusCode::FAILURE;
m_backendSvc = &dynamic_cast<detail::ConditionBackendSvc&>( *m_backendSvcIF );
return StatusCode::SUCCESS;
}
StatusCode ConditionContextProducer::execute( const EventContext& ctx ) const {
// For this test, we just use the event number as an event timestamp
detail::EventTimestamp eventTime = ctx.evt();
m_ctxHandle.put( ctx, m_backendSvc->setupEvent( eventTime ) );
return StatusCode::SUCCESS;
}
DECLARE_COMPONENT( ConditionContextProducer )
}
#pragma once
#include "GaudiKernel/Algorithm.h"
#include "GaudiKernel/EventContext.h"
#include "GaudiKernel/EventDataHandle.h"
#include "GaudiKernel/IService.h"
#include "GaudiKernel/SmartIF.h"
#include "GaudiKernel/StatusCode.h"
#include "ConditionContext.h"
#include "ConditionContextPath.h"
namespace DemoBackend
{
namespace detail
{
class ConditionBackendSvc;
}
// Every condition handling back-end must define an Algorithm that is executed
// whenever the EventTimestamp is known, reads it from the TES, asks the
// back-end to prepare a ConditionContext for this event, and inserts that in
// a commonly agreed location of the TES.
//
// Whether the user need to insert this Algorithm in their data processing
// graph depends on how clever the Scheduler implementation is. Some
// Scheduler implementations can just insert it automatically.
//
class ConditionContextProducer final : public Gaudi::Algorithm {
public:
// Inherit Algorithm constructor
using Gaudi::Algorithm::Algorithm;
// At initialization time, we get in touch with the back-end
StatusCode initialize() override;
// Whenever the event timestamp is ready, we produce the ConditionContext
StatusCode execute( const EventContext& ctx ) const override;
private:
// This DataHandle allows us to write down the ConditionContext in the TES
Gaudi::EventWriteHandle<ConditionContext> m_ctxHandle{
detail::CONDITION_CONTEXT_PATH, this };
// These members allow us to contact the condition storage back-end
SmartIF<IService> m_backendSvcIF;
detail::ConditionBackendSvc* m_backendSvc = nullptr;
};
}
\ No newline at end of file
#pragma once
#include <string>
namespace DemoBackend
{
// Keys are the way conditions are referred to during configuration
using ConditionKey = std::string;
}
\ No newline at end of file
#include "ConditionAccessorAlg.h"
// This is a demonstration of what it could feel like to write an Algorithm that
// uses conditions, until Gaudi::Functional fully hides it away from the user.
//
// We expect multiple condition management backends to coexist for a while. In
// the proposed infrastructure, a single algorithm may only use a single
// condition management backend, but multiple algorithms may each use a
// different condition management backend.
//
// The selection of a condition management backend is done by the choice of
// "ConditionAccessorAlg" base class, which transparently makes available all
// the other components of the condition access interface: ConditionContexts,
// ConditionAccessors, and ConditionKeys.
//
class DemoConditionAlg final : public DemoBackend::ConditionAccessorAlg {
public:
// Inherit constructor from base class
using DemoBackend::ConditionAccessorAlg::ConditionAccessorAlg;
// In this demonstrator, the condition context is fetched explicitly
StatusCode execute( const EventContext& evCtx ) const override {
const ConditionContext& condCtx = this->getConditionContext( evCtx );
const float& condValue = m_myCondition.get( condCtx );
info() << "Value of condition @ " << m_myCondition.key()
<< " for event #" << evCtx.evt()
<< " is " << condValue << endmsg;
return StatusCode::SUCCESS;
}
private:
// ConditionAccessors feel like EventReadHandles for all intents and purposes
ConditionAccessor<float> m_myCondition {
this, "MyCondition", ConditionKey { "/Path/To/Condition" }, "Location of test condition" };
};
DECLARE_COMPONENT( DemoConditionAlg )
\ No newline at end of file
#pragma once
#include <cstdint>
namespace DemoBackend
{
namespace detail
{
// Timestamps are used to locate events in an experiment run and define
// the notion of a condition's interval of validity.
using EventTimestamp = uint64_t;
}
}
\ No newline at end of file
<?xml version="1.0" ?><!DOCTYPE extension PUBLIC '-//QM/2.3/Extension//EN' 'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'>
<extension class="GaudiTest.GaudiExeTest" kind="test">
<argument name="program"><text>gaudirun.py</text></argument>
<argument name="args"><set><text>$GAUDIEXAMPLESROOT/options/Conditions.py</text></set></argument>
<argument name="use_temp_dir"><enumeral>true</enumeral></argument>
<argument name="reference"><text>refs/Conditions.ref</text></argument>
</extension>
# --> Including file '/root/Gaudi/GaudiExamples/options/Conditions.py'
# <-- End of file '/root/Gaudi/GaudiExamples/options/Conditions.py'
ApplicationMgr SUCCESS
====================================================================================================================================
Welcome to ApplicationMgr (GaudiCoreSvc v30r5)
running on 367e0b4f1840 on Wed Jan 23 22:34:24 2019
====================================================================================================================================
ApplicationMgr INFO Application Manager Configured successfully
ThreadPoolSvc INFO no thread init tools attached
AvalancheSchedu... INFO Activating scheduler in a separate thread
AlgResourcePool INFO TopAlg list empty. Recovering the one of Application Manager
AvalancheSchedu... INFO Found 2 algorithms
AvalancheSchedu... INFO Data Dependencies for Algorithms:
ConditionContextProducer
o OUTPUT '/Event/DemoBackendCondCtx'
DemoConditionAlg
o INPUT '/Event/DemoBackendCondCtx'
PrecedenceSvc INFO Assembling CF and DF task precedence rules
PrecedenceSvc INFO PrecedenceSvc initialized successfully
AvalancheSchedu... INFO Concurrency level information:
AvalancheSchedu... INFO o Number of events in flight: 2
AvalancheSchedu... INFO o TBB thread pool size: 'ThreadPoolSize':1
HiveSlimEventLo...WARNING Unable to locate service "EventSelector"
HiveSlimEventLo...WARNING No events will be processed from external input.
HistogramPersis...WARNING Histograms saving not required.
HiveSlimEventLo... INFO Found 0 events in black list
ApplicationMgr INFO Application Manager Initialized successfully
ApplicationMgr INFO Application Manager Started successfully
HiveSlimEventLo... INFO Starting loop on events
DemoConditionAlg INFO Value of condition @ /Path/To/Condition for event #0 is 0
DemoConditionAlg INFO Value of condition @ /Path/To/Condition for event #1 is 75.6
DemoConditionAlg INFO Value of condition @ /Path/To/Condition for event #2 is 151.2
DemoConditionAlg INFO Value of condition @ /Path/To/Condition for event #3 is 226.8
DemoConditionAlg INFO Value of condition @ /Path/To/Condition for event #4 is 302.4
DemoConditionAlg INFO Value of condition @ /Path/To/Condition for event #5 is 378
DemoConditionAlg INFO Value of condition @ /Path/To/Condition for event #6 is 453.6
DemoConditionAlg INFO Value of condition @ /Path/To/Condition for event #7 is 529.2
DemoConditionAlg INFO Value of condition @ /Path/To/Condition for event #8 is 604.8
DemoConditionAlg INFO Value of condition @ /Path/To/Condition for event #9 is 680.4
HiveSlimEventLo... INFO ---> Loop Finished (skipping 1st evt) - WSS 69.2461 total time 1223367
HiveSlimEventLo... INFO 0 events were SKIPed
ApplicationMgr INFO Application Manager Stopped successfully
HiveSlimEventLo... INFO Histograms converted successfully according to request.
AvalancheSchedu... INFO Joining Scheduler thread
ApplicationMgr INFO Application Manager Finalized successfully
ApplicationMgr INFO Application Manager Terminated successfully
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment