From 9e05869b75c27b36a5485bce34a714dbc60be23a Mon Sep 17 00:00:00 2001
From: Markus Frank <Markus.Frank@cern.ch>
Date: Wed, 29 Nov 2017 20:21:04 +0100
Subject: [PATCH] Add user defined framework context to DDG4and make the
 G4RunManager instantiation a plugin this should consequently allow for user
 defined specializations.

---
 DDG4/include/DDG4/Geant4Context.h  |  50 +++++++++++++++
 DDG4/include/DDG4/Geant4Kernel.h   |  19 ++++--
 DDG4/plugins/Geant4RunManagers.cpp | 100 +++++++++++++++++++++++++++++
 DDG4/plugins/Geant4SDActions.cpp   |  31 ++++++++-
 DDG4/src/Geant4Context.cpp         |   5 ++
 DDG4/src/Geant4Kernel.cpp          |  88 ++++++++++++-------------
 6 files changed, 245 insertions(+), 48 deletions(-)
 create mode 100644 DDG4/plugins/Geant4RunManagers.cpp

diff --git a/DDG4/include/DDG4/Geant4Context.h b/DDG4/include/DDG4/Geant4Context.h
index 2500bc211..20b130b89 100644
--- a/DDG4/include/DDG4/Geant4Context.h
+++ b/DDG4/include/DDG4/Geant4Context.h
@@ -154,16 +154,62 @@ namespace dd4hep {
 
     /// Generic context to extend user, run and event information
     /**
+     *  A valid instance of the Geant4Context is passed to every instance of a Geant4Action at 
+     *  creation time.
+     *
+     *  The Geant4Context is the main thread specific accessor to the dd4hep, DDG4 and
+     *  the user framework.
+     *  - The access to the dd4hep objects is via the Geant4Context::detectorDescription() call,
+     *  - the access to DDG4 as a whole is supported via Geant4Context::kernel() and
+     *  - the access to the user gframework using a specialized implementation of:
+     *  template <typename T> T& userFramework()  const;
+     *
+     *  A user defined implementations must be specialized somewhere in a compilation unit
+     *  of the user framework, not in a header file. The framework object could host
+     *  e.g. references for histogramming, logging, data access etc.
+     *
+     *  This way any experiment/user related data processing framework can exhibit
+     *  it's essential tools to DDG4 actions.
+     *
+     *  A possible specialized implementations would look like the following:
+     *
+     *  struct Gaudi  {
+     *    IMessageSvc*   msg;
+     *    IHistogramSvc* histos;
+     *    ....
+     *  };
+     *
+     *  template<> Gaudi& Geant4Context::userFramework<Gaudi>()  const  {
+     *    UserFramework& fw = m_kernel->userFramework();
+     *    if ( fw.first && &typeid(T) == fw.second ) return *(T*)fw.first;
+     *    throw std::runtime_error("No user specified framework context present!");
+     *  }
+     *
+     *  To access the user framework then use the following call:
+     *  Gaudi* fw = context->userFramework<Gaudi>();
+     *
+     *  of course after having initialized it:
+     *  Gaudi * fw = ...;
+     *  GaudiKernel& kernel = ...;
+     *  kernel.setUserFramework(fw);
+     *
      *  \author  M.Frank
      *  \version 1.0
      *  \ingroup DD4HEP_SIMULATION
      */
     class Geant4Context  {
+    public:
       friend class Geant4Kernel;
+      typedef std::pair<void*, const std::type_info*>   UserFramework;
+
     protected:
+      /// Reference to the kernel object
       Geant4Kernel* m_kernel;
+      /// Transient context variable - depending on the thread context: run reference
       Geant4Run*    m_run;
+      /// Transient context variable - depending on the thread context: event reference
       Geant4Event*  m_event;
+
       /// Default constructor
       Geant4Context(Geant4Kernel* kernel);
     public:
@@ -183,6 +229,10 @@ namespace dd4hep {
       Geant4Event* eventPtr()  const  { return m_event; }
       /// Access to the kernel object
       Geant4Kernel& kernel()  const   { return *m_kernel;   }
+      /// Access to the user framework. Specialized function to be implemented by the client
+      template <typename T> T& framework()  const;
+      /// Generic framework access
+      UserFramework& userFramework() const;
       /// Access to detector description
       Detector& detectorDescription() const;
       /// Access the tracking manager
diff --git a/DDG4/include/DDG4/Geant4Kernel.h b/DDG4/include/DDG4/Geant4Kernel.h
index 2648acd0b..09a342ecd 100644
--- a/DDG4/include/DDG4/Geant4Kernel.h
+++ b/DDG4/include/DDG4/Geant4Kernel.h
@@ -35,6 +35,8 @@ namespace dd4hep {
 
     /// Class, which allows all Geant4Action derivatives to access the DDG4 kernel structures.
     /**
+     *  To implement access to a user specified framework please see class Geant4Context.
+     *
      *  \author  M.Frank
      *  \version 1.0
      *  \ingroup DD4HEP_SIMULATION
@@ -45,6 +47,7 @@ namespace dd4hep {
       typedef std::map<std::string, Geant4ActionPhase*> Phases;
       typedef std::map<std::string, Geant4Action*>      GlobalActions;
       typedef std::map<std::string,int>                 ClientOutputLevels;
+      typedef std::pair<void*, const std::type_info*>   UserFramework;
 
     protected:
       /// Reference to the run manager
@@ -57,7 +60,8 @@ namespace dd4hep {
       Detector*          m_detDesc;
       /// Property pool
       PropertyManager    m_properties;
-
+      /// Reference to the user framework
+      UserFramework      m_userFramework;
 
       /// Action phases
       Phases        m_phases;
@@ -67,16 +71,18 @@ namespace dd4hep {
       GlobalActions m_globalActions;
       /// Globally registered filters of sensitive detectors
       GlobalActions m_globalFilters;
+      /// Property: Client output levels
+      ClientOutputLevels m_clientLevels;
       /// Property: Name of the G4UI command tree
       std::string m_controlName;
       /// Property: Name of the UI action. Must be member of the global actions
       std::string m_uiName;
+      /// Property: Name of the G4 run manager factory to be used. Default: Geant4RunManager
+      std::string m_runManagerType;
       /// Property: Number of events to be executed in batch mode
       long        m_numEvent;
       /// Property: Output level
       int         m_outputLevel;
-      /// Property: Client output levels
-      ClientOutputLevels m_clientLevels;
 
       /// Property: Running in multi threaded context
       //bool        m_multiThreaded;
@@ -159,7 +165,12 @@ namespace dd4hep {
       unsigned long id()  const                 {        return m_ident;           }
       /// Access to the Geant4 run manager
       G4RunManager& runManager();
-
+      /// Generic framework access
+      UserFramework& userFramework()            {        return m_userFramework;   }
+      /// Set the framework context to the kernel object
+      template <typename T> void setUserFramework(T* object)   {
+        m_userFramework = UserFramework(object,&typeid(T));
+      }
       /** Property access                            */
       /// Access to the properties of the object
       PropertyManager& properties()             {        return m_properties;      }
diff --git a/DDG4/plugins/Geant4RunManagers.cpp b/DDG4/plugins/Geant4RunManagers.cpp
new file mode 100644
index 000000000..818ae7362
--- /dev/null
+++ b/DDG4/plugins/Geant4RunManagers.cpp
@@ -0,0 +1,100 @@
+//==========================================================================
+//  AIDA Detector description implementation 
+//--------------------------------------------------------------------------
+// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
+// All rights reserved.
+//
+// For the licensing terms see $DD4hepINSTALL/LICENSE.
+// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
+//
+// Author     : M.Frank
+//
+//==========================================================================
+#ifndef DDG4_GEANT4RUNMANAGER_H
+#define DDG4_GEANT4RUNMANAGER_H 1
+
+/// Framework include files
+#include "DDG4/Geant4Action.h"
+
+/// Geant4 include files
+#include "G4RunManager.hh"
+
+typedef G4RunManager   G4__RunManager;
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
+  namespace sim {
+
+    /// Geant4 run manager plugin class. 
+    /**
+     *  The plugin acts as a normal Geant4Action. However, it must support a 
+     *  dynamic_cast to G4RunManager.
+     *  The templated class may be specialized by any user defined class
+     *  which has G4RunManager as a super class.
+     *
+     *  Current specializations are:
+     *  - G4RunManager for single threaded applications
+     *  - G4MTRunManager for multi threaded applications
+     *
+     *  For convenience we name the factory instances according to the G4 classes.
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_SIMULATION
+     */
+    template <typename RUNMANAGER>
+    class Geant4RunManager : public Geant4Action, public RUNMANAGER    {
+    public:
+      Geant4RunManager(Geant4Context* ctxt, const std::string& nam)
+        : Geant4Action(ctxt, nam), RUNMANAGER()
+      {
+        declareProperty("NumberOfThreads", m_numThreads);
+      }
+      virtual ~Geant4RunManager()   { }
+      /// Enable and install UI messenger
+      virtual void enableUI();
+    private:
+      /// global range cut for secondary productions
+      int m_numThreads;
+    };
+    template <> void Geant4RunManager<G4RunManager>::enableUI()  {
+      Geant4Action::enableUI();
+      printout(WARNING,"Geant4RunManager","+++ Configured run manager of type: %s.",
+               typeName(typeid(G4RunManager)).c_str());
+      printout(WARNING,"Geant4Kernel","+++ Multi-threaded mode requested, "
+               "but not supported by this compilation of Geant4.");
+      printout(WARNING,"Geant4Kernel","+++ Falling back to single threaded mode.");
+      m_numThreads = 0;
+    }
+    typedef Geant4RunManager<G4__RunManager>   G4RunManager;
+  }
+}
+#endif   // DDG4_GEANT4RUNMANAGER_H
+
+#include "DDG4/Factories.h"
+using namespace dd4hep::sim;
+DECLARE_GEANT4ACTION(G4RunManager)
+
+#ifdef G4MULTITHREADED
+#include "G4MTRunManager.hh"
+typedef  G4MTRunManager G4__MTRunManager;
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+  /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
+  namespace sim {
+    template <> void Geant4RunManager<G4MTRunManager>::enableUI()  {
+      Geant4Action::enableUI();
+      this->RUNMANAGER::SetNumberOfThreads(m_numThreads);
+      printout(WARNING,"Geant4RunManager","+++ Configured run manager of type: %s with %d threads.",
+               typeName(typeid(G4MTRunManager)).c_str(), m_numThreads);
+    }
+    typedef Geant4RunManager<G4__MTRunManager> G4MTRunManager;
+  }
+}
+DECLARE_GEANT4ACTION(G4MTRunManager)
+#endif
+
+
diff --git a/DDG4/plugins/Geant4SDActions.cpp b/DDG4/plugins/Geant4SDActions.cpp
index 2bc1c0883..ef5f0d6e1 100644
--- a/DDG4/plugins/Geant4SDActions.cpp
+++ b/DDG4/plugins/Geant4SDActions.cpp
@@ -25,6 +25,33 @@ namespace dd4hep {
   /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
   namespace sim   {
 
+    namespace {
+      struct Geant4VoidSensitive {};
+    }
+    // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+    //               Geant4SensitiveAction<Geant4VoidSensitive>
+    // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+    /** \addtogroup Geant4SDActionPlugin
+     *
+     * @{
+     * \package Geant4VoidSensitiveAction
+     * \brief Void Sensitive detector action to skip the processing of a detector
+     *        without changing the entire DDG4 setup.
+     *
+     * @}
+     */
+
+    /// Define collections created by this sensitivie action object
+    template <> void Geant4SensitiveAction<Geant4VoidSensitive>::defineCollections()    {
+      m_collectionID = -1;
+    }
+
+    /// Method for generating hit(s) using the information of G4Step object.
+    template <> bool Geant4SensitiveAction<Geant4VoidSensitive>::process(G4Step* /*step*/,G4TouchableHistory* /*hist*/ ) {
+      return true;
+    }
+    typedef Geant4SensitiveAction<Geant4VoidSensitive> Geant4VoidSensitiveAction;
+
     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     //               Geant4SensitiveAction<Geant4Tracker>
     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -32,7 +59,6 @@ namespace dd4hep {
      *
      * @{
      * \package Geant4TrackerAction
-     *
      * \brief Sensitive detector meant for tracking detectors, will produce one hit per step
      *
      * @}
@@ -480,6 +506,9 @@ namespace dd4hep {
 using namespace dd4hep::sim;
 
 #include "DDG4/Factories.h"
+// Special void entry point
+DECLARE_GEANT4SENSITIVE(Geant4VoidSensitiveAction)
+// Standard factories used for simulation
 DECLARE_GEANT4SENSITIVE(Geant4TrackerAction)
 DECLARE_GEANT4SENSITIVE(Geant4TrackerCombineAction)
 DECLARE_GEANT4SENSITIVE(Geant4CalorimeterAction)
diff --git a/DDG4/src/Geant4Context.cpp b/DDG4/src/Geant4Context.cpp
index 01c444f5b..ff303f2c8 100644
--- a/DDG4/src/Geant4Context.cpp
+++ b/DDG4/src/Geant4Context.cpp
@@ -89,6 +89,11 @@ Detector& Geant4Context::detectorDescription() const {
   return m_kernel->detectorDescription();
 }
 
+/// Generic framework access
+Geant4Context::UserFramework& Geant4Context::userFramework() const  {
+  return m_kernel->userFramework();
+}
+
 /// Create a user trajectory
 G4VTrajectory* Geant4Context::createTrajectory(const G4Track* /* track */) const {
   string err = dd4hep::format("Geant4Kernel", "createTrajectory: Purely virtual method. requires overloading!");
diff --git a/DDG4/src/Geant4Kernel.cpp b/DDG4/src/Geant4Kernel.cpp
index 3408d9a7a..103f99613 100644
--- a/DDG4/src/Geant4Kernel.cpp
+++ b/DDG4/src/Geant4Kernel.cpp
@@ -14,6 +14,7 @@
 // Framework include files
 #include "DD4hep/Detector.h"
 #include "DD4hep/Memory.h"
+#include "DD4hep/Plugins.h"
 #include "DD4hep/Printout.h"
 #include "DD4hep/Primitives.h"
 #include "DD4hep/InstanceCount.h"
@@ -23,11 +24,7 @@
 #include "DDG4/Geant4ActionPhase.h"
 
 // Geant4 include files
-#ifdef G4MULTITHREADED
-#include "G4MTRunManager.hh"
-#else
 #include "G4RunManager.hh"
-#endif
 #include "G4UIdirectory.hh"
 #include "G4Threading.hh"
 #include "G4AutoLock.hh"
@@ -82,10 +79,11 @@ Geant4Kernel::Geant4Kernel(Detector& description_ref)
   m_detDesc->addExtension < Geant4Kernel > (this);
   m_ident = -1;
   declareProperty("UI",m_uiName);
-  declareProperty("OutputLevel",    m_outputLevel = DEBUG);
-  declareProperty("NumEvents",      m_numEvent = 10);
-  declareProperty("OutputLevels",   m_clientLevels);
-  declareProperty("NumberOfThreads",m_numThreads);
+  declareProperty("OutputLevel",      m_outputLevel = DEBUG);
+  declareProperty("NumEvents",        m_numEvent = 10);
+  declareProperty("OutputLevels",     m_clientLevels);
+  declareProperty("NumberOfThreads",  m_numThreads);
+  declareProperty("RunManagerType",   m_runManagerType = "G4RunManager");
   m_controlName = "/ddg4/";
   m_control = new G4UIdirectory(m_controlName.c_str());
   m_control->SetGuidance("Control for named Geant4 actions");
@@ -101,9 +99,10 @@ Geant4Kernel::Geant4Kernel(Geant4Kernel* m, unsigned long ident)
     m_threadContext(0), phase(this)
 {
   char text[64];
-  m_detDesc           = m_master->m_detDesc;
+  m_detDesc        = m_master->m_detDesc;
   m_ident          = m_master->m_workers.size();
   m_numEvent       = m_master->m_numEvent;
+  m_runManagerType = m_master->m_runManagerType;
   declareProperty("UI",m_uiName = m_master->m_uiName);
   declareProperty("OutputLevel", m_outputLevel = m_master->m_outputLevel);
   declareProperty("OutputLevels",m_clientLevels = m_master->m_clientLevels);
@@ -241,26 +240,28 @@ G4RunManager& Geant4Kernel::runManager() {
     return *m_runManager;
   }
   else if ( isMaster() )   {
-#ifdef G4MULTITHREADED
-    if ( m_numThreads > 0 )   {
-      printout(WARNING,"Geant4Kernel","+++ Multi-threaded mode requested with %d worker threads.",m_numThreads);
-      G4MTRunManager* run_mgr = new G4MTRunManager;
-      run_mgr->SetNumberOfThreads(m_numThreads);
-      m_runManager = run_mgr;
-      return *m_runManager;
+    Geant4Action* mgr =
+      PluginService::Create<Geant4Action*>(m_runManagerType,
+                                           m_context,
+                                           string("Geant4RunManager"));
+    if ( !mgr )   {
+      except("Geant4Kernel",
+             "+++ Invalid Geant4RunManager class: %s. Aborting.",
+             m_runManagerType.c_str());
     }
-#endif
-    if ( m_numThreads > 0 )   {
-      printout(WARNING,"Geant4Kernel","+++ Multi-threaded mode requested, "
-               "but not supported by this compilation of Geant4.");
-      printout(WARNING,"Geant4Kernel","+++ Falling back to single threaded mode.");
-      m_numThreads = 0;
+    mgr->property("NumberOfThreads").set(m_numThreads);
+    mgr->enableUI();
+    m_runManager = dynamic_cast<G4RunManager*>(mgr);
+    if ( m_runManager )  {
+      return *m_runManager;
     }
-    return *(m_runManager = new G4RunManager);
+    except("Geant4Kernel",
+           "+++ Invalid Geant4RunManager action: %s. Invalid inheritance.",
+           m_runManagerType.c_str());
   }
-  throw runtime_error(format("Geant4Kernel", 
-                             "DDG4: Only the master thread may instantiate "
-                             "a G4RunManager object!"));
+  except("Geant4Kernel", 
+         "+++ Only the master thread may instantiate a G4RunManager object!");
+  throw runtime_error("Is never called -- just to satisfy compiler!");
 }
 
 /// Construct detector geometry using description plugin
@@ -312,7 +313,7 @@ int Geant4Kernel::terminate() {
   detail::releaseObjects(m_globalFilters);
   detail::releaseObjects(m_globalActions);
   if ( ptr == this )  {
-    detail::deletePtr  (m_runManager);
+    detail::deletePtr(m_runManager);
   }
   Geant4ActionContainer::terminate();
   if ( ptr == this && m_detDesc )  {
@@ -339,11 +340,12 @@ Geant4Kernel& Geant4Kernel::registerGlobalAction(Geant4Action* action) {
                nam.c_str(),typeName(typeid(*action)).c_str());
       return *this;
     }
-    throw runtime_error(format("Geant4Kernel", "DDG4: The action '%s' is already globally "
-                               "registered. [Action-Already-Registered]", nam.c_str()));
+    except("Geant4Kernel", "DDG4: The action '%s' is already globally "
+           "registered. [Action-Already-Registered]", nam.c_str());
   }
-  throw runtime_error(format("Geant4Kernel", "DDG4: Attempt to globally register an invalid "
-                             "action. [Action-Invalid]"));
+  except("Geant4Kernel",
+         "DDG4: Attempt to globally register an invalid action. [Action-Invalid]");
+  return *this;
 }
 
 /// Retrieve action from repository
@@ -351,8 +353,8 @@ Geant4Action* Geant4Kernel::globalAction(const std::string& action_name, bool th
   GlobalActions::iterator i = m_globalActions.find(action_name);
   if (i == m_globalActions.end()) {
     if (throw_if_not_present) {
-      throw runtime_error(format("Geant4Kernel", "DDG4: The action '%s' is not globally "
-                                 "registered. [Action-Missing]", action_name.c_str()));
+       except("Geant4Kernel", "DDG4: The action '%s' is not globally "
+              "registered. [Action-Missing]", action_name.c_str());
     }
     return 0;
   }
@@ -373,11 +375,12 @@ Geant4Kernel& Geant4Kernel::registerGlobalFilter(Geant4Action* filter) {
       m_globalFilters[nam] = filter;
       return *this;
     }
-    throw runtime_error(format("Geant4Kernel", "DDG4: The filter '%s' is already globally "
-                               "registered. [Filter-Already-Registered]", nam.c_str()));
+    except("Geant4Kernel", "DDG4: The filter '%s' is already globally "
+           "registered. [Filter-Already-Registered]", nam.c_str());
   }
-  throw runtime_error(format("Geant4Kernel", "DDG4: Attempt to globally register an invalid "
-                             "filter. [Filter-Invalid]"));
+  except("Geant4Kernel",
+         "DDG4: Attempt to globally register an invalid filter. [Filter-Invalid]");
+  return *this;
 }
 
 /// Retrieve filter from repository
@@ -385,8 +388,8 @@ Geant4Action* Geant4Kernel::globalFilter(const std::string& filter_name, bool th
   GlobalActions::iterator i = m_globalFilters.find(filter_name);
   if (i == m_globalFilters.end()) {
     if (throw_if_not_present) {
-      throw runtime_error(format("Geant4Kernel", "DDG4: The filter '%s' is not already globally "
-                                 "registered. [Filter-Missing]", filter_name.c_str()));
+      except("Geant4Kernel", "DDG4: The filter '%s' is not already globally "
+             "registered. [Filter-Missing]", filter_name.c_str());
     }
     return 0;
   }
@@ -409,8 +412,8 @@ Geant4ActionPhase* Geant4Kernel::getPhase(const std::string& nam) {
   if (i != m_phases.end()) {
     return (*i).second;
   }
-  throw runtime_error(format("Geant4Kernel", "DDG4: The Geant4 action phase '%s' "
-                             "does not exist. [No-Entry]", nam.c_str()));
+  except("Geant4Kernel", "DDG4: The Geant4 action phase '%s' does not exist. [No-Entry]", nam.c_str());
+  return 0;
 }
 
 /// Add a new phase to the phase
@@ -428,8 +431,7 @@ Geant4ActionPhase* Geant4Kernel::addPhase(const std::string& nam, const type_inf
     return p;
   }
   else if (throw_on_exist) {
-    throw runtime_error(format("Geant4Kernel", "DDG4: The Geant4 action phase %s "
-                               "already exists. [Already-Exists]", nam.c_str()));
+    except("Geant4Kernel", "DDG4: The Geant4 action phase %s already exists. [Already-Exists]", nam.c_str());
   }
   return (*i).second;
 }
-- 
GitLab