Skip to content
Snippets Groups Projects
Geant4Kernel.cpp 14.7 KiB
Newer Older
Markus Frank's avatar
Markus Frank committed
// $Id: $
//==========================================================================
//  AIDA Detector description implementation for LCD
//--------------------------------------------------------------------------
Markus Frank's avatar
Markus Frank committed
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
// Author     : M.Frank
//
//==========================================================================

// Framework include files
#include "DD4hep/LCDD.h"
#include "DD4hep/Memory.h"
#include "DD4hep/Printout.h"
#include "DD4hep/Primitives.h"
#include "DD4hep/InstanceCount.h"

#include "DDG4/Geant4Kernel.h"
#include "DDG4/Geant4ActionPhase.h"

// Geant4 include files
#ifdef G4MULTITHREADED
#include "G4MTRunManager.hh"
#else
#include "G4RunManager.hh"
#include "G4UIdirectory.hh"
#include "G4Threading.hh"
#include "G4AutoLock.hh"

// C/C++ include files
#include <stdexcept>
#include <algorithm>
#include <memory>

using namespace std;
using namespace DD4hep::Simulation;

namespace {
  G4Mutex kernel_mutex=G4MUTEX_INITIALIZER;
  DD4hep::dd4hep_ptr<Geant4Kernel> s_main_instance(0);
}

/// Standard constructor
Geant4Kernel::PhaseSelector::PhaseSelector(Geant4Kernel* kernel)
}

/// Copy constructor
Geant4Kernel::PhaseSelector::PhaseSelector(const PhaseSelector& c)
Markus Frank's avatar
Markus Frank committed
/// Assignment operator
Geant4Kernel::PhaseSelector& Geant4Kernel::PhaseSelector::operator=(const PhaseSelector& c) {
  if ( this != &c )  {
    m_kernel = c.m_kernel;
  }
  return *this;
}

/// Phase access to the map
Geant4ActionPhase& Geant4Kernel::PhaseSelector::operator[](const std::string& nam) const {
  Geant4ActionPhase* action_phase = m_kernel->getPhase(nam);
  if ( action_phase ) {
    return *action_phase;
  throw runtime_error(format("Geant4Kernel", "Attempt to access the nonexisting phase '%s'", nam.c_str()));
}

/// Standard constructor
Geant4Kernel::Geant4Kernel(LCDD& lcdd_ref)
  : Geant4ActionContainer(), m_runManager(0), m_control(0), m_trackMgr(0), m_lcdd(&lcdd_ref), 
    m_numThreads(0), m_id(Geant4Kernel::thread_self()), m_master(this), m_shared(0), phase(this)  
  m_lcdd->addExtension < Geant4Kernel > (this);
Markus Frank's avatar
Markus Frank committed
  declareProperty("UI",m_uiName);
  declareProperty("OutputLevel",    m_outputLevel = DEBUG);
  declareProperty("NumEvents",      m_numEvent = 10);
  declareProperty("OutputLevels",   m_clientLevels);
  declareProperty("NumberOfThreads",m_numThreads);
  m_controlName = "/ddg4/";
  m_control = new G4UIdirectory(m_controlName.c_str());
  m_control->SetGuidance("Control for named Geant4 actions");
  setContext(new Geant4Context(this));
  //m_shared = new Geant4Kernel(lcdd_ref, this, -2);
  InstanceCount::increment(this);
}

/// Standard constructor
Geant4Kernel::Geant4Kernel(Geant4Kernel* m, unsigned long ident)
  : Geant4ActionContainer(), m_runManager(0), m_control(0), m_trackMgr(0), m_lcdd(0),
    m_numThreads(1), m_id(ident), m_master(m), m_shared(0), phase(this)
  m_lcdd           = m_master->m_lcdd;
  m_ident          = m_master->m_workers.size();
  declareProperty("UI",m_uiName = m_master->m_uiName);
  declareProperty("OutputLevel", m_outputLevel = m_master->m_outputLevel);
  declareProperty("OutputLevels",m_clientLevels = m_master->m_clientLevels);
  ::snprintf(text,sizeof(text),"/ddg4.%d/",(int)(m_master->m_workers.size()));
  m_controlName = text;
  m_control = new G4UIdirectory(m_controlName.c_str());
  m_control->SetGuidance("Control for thread specific Geant4 actions");
  setContext(new Geant4Context(this));
  InstanceCount::increment(this);
}

/// Default destructor
Geant4Kernel::~Geant4Kernel() {
  if ( this == s_main_instance.get() )   {
    s_main_instance.release();
  }
  destroyObjects(m_workers);
    releaseObjects(m_globalFilters);
    releaseObjects(m_globalActions);
  destroyPhases();
  deletePtr(m_runManager);
  Geant4ActionContainer::terminate();
  if ( m_lcdd && isMaster() )  {
    try  {
      m_lcdd->removeExtension < Geant4Kernel > (false);
      m_lcdd->destroyInstance();
      m_lcdd = 0;
    }
    catch(...)  {
    }
  InstanceCount::decrement(this);
}

/// Instance accessor
Geant4Kernel& Geant4Kernel::instance(LCDD& lcdd) {
  if ( 0 == s_main_instance.get() )   {
    G4AutoLock protection_lock(&kernel_mutex);    {
      if ( 0 == s_main_instance.get() )   { // Need to check again!
        s_main_instance.adopt(new Geant4Kernel(lcdd));
      }
    }
  }
  return *(s_main_instance.get());
/// Access thread identifier
unsigned long int Geant4Kernel::thread_self()    {
  unsigned long int thr_id = (unsigned long int)::pthread_self();
  return thr_id;
}

/// Create identified worker instance
Geant4Kernel& Geant4Kernel::createWorker()   {
  if ( isMaster() )   {
    unsigned long identifier = thread_self();
    Geant4Kernel* w = new Geant4Kernel(this, identifier);
    m_workers[identifier] = w;
    printout(INFO,"Geant4Kernel","+++ Created worker instance id=%ul",identifier);
    return *w;
  }
  throw runtime_error(format("Geant4Kernel", "DDG4: Only the master instance may create workers."));
}

/// Access worker instance by it's identifier
Geant4Kernel& Geant4Kernel::worker(unsigned long identifier, bool create_if)    {
  Workers::iterator i = m_workers.find(identifier);
  if ( i != m_workers.end() )   {
    return *((*i).second);
  }
  else if ( identifier == m_id )  {
    return *this;
  }
    unsigned long self = thread_self();
  else if ( create_if )  {
    return createWorker();
  }
  throw runtime_error(format("Geant4Kernel", "DDG4: The Kernel object 0x%p does not exists!",(void*)identifier));
}

/// Access number of workers
int Geant4Kernel::numWorkers() const   {
  return m_workers.size();
}

Markus Frank's avatar
Markus Frank committed
void Geant4Kernel::printProperties()  const  {
  printout(ALWAYS,"Geant4Kernel","OutputLevel:  %d", m_outputLevel);
  printout(ALWAYS,"Geant4Kernel","UI:           %s", m_uiName.c_str());
Markus Frank's avatar
Markus Frank committed
  printout(ALWAYS,"Geant4Kernel","NumEvents:    %ld",m_numEvent);
  printout(ALWAYS,"Geant4Kernel","NumThreads:   %d", m_numThreads);
Markus Frank's avatar
Markus Frank committed
  for(ClientOutputLevels::const_iterator i=m_clientLevels.begin(); i!=m_clientLevels.end();++i)  {
    printout(ALWAYS,"Geant4Kernel","OutputLevel[%s]:  %d",(*i).first.c_str(),(*i).second);
  }
}

Markus Frank's avatar
Markus Frank committed
/// Check property for existence
bool Geant4Kernel::hasProperty(const std::string& name) const    {
  return m_properties.exists(name);
}

/// Access single property
DD4hep::Property& Geant4Kernel::property(const std::string& name)   {
  return properties()[name];
}

Markus Frank's avatar
Markus Frank committed
/// Fill cache with the global output level of a named object. Must be set before instantiation
void Geant4Kernel::setOutputLevel(const std::string object, PrintLevel new_level)   {
  m_clientLevels[object] = new_level;
}

/// Retrieve the global output level of a named object.
DD4hep::PrintLevel Geant4Kernel::getOutputLevel(const std::string object) const   {
  ClientOutputLevels::const_iterator i=m_clientLevels.find(object);
  if ( i != m_clientLevels.end() ) return (PrintLevel)(*i).second;
  return DD4hep::PrintLevel(DD4hep::printLevel()-1);
}

/// Set the output level; returns previous value
DD4hep::PrintLevel Geant4Kernel::setOutputLevel(PrintLevel new_level)  {
  int old = m_outputLevel;
  m_outputLevel = new_level;
  return (PrintLevel)old;
}

/// Access to the Geant4 run manager
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;
    }
#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;
    }
    return *(m_runManager = new G4RunManager);
  }
  throw runtime_error(format("Geant4Kernel", 
                             "DDG4: Only the master thread may instantiate "
                             "a G4RunManager object!"));
}

/// Construct detector geometry using lcdd plugin
void Geant4Kernel::loadGeometry(const std::string& compact_file) {
  char* arg = (char*) compact_file.c_str();
  m_lcdd->apply("DD4hepXMLLoader", 1, &arg);
  //return *this;
}

// Utility function to load XML files
void Geant4Kernel::loadXML(const char* fname) {
  const char* args[] = { fname, 0 };
  m_lcdd->apply("DD4hepXMLLoader", 1, (char**) args);
int Geant4Kernel::configure() {
  return Geant4Exec::configure(*this);
int Geant4Kernel::initialize() {
  return Geant4Exec::initialize(*this);
  }
  catch(const exception& e)   {
    printout(FATAL,"Geant4Kernel","+++ Exception while simulating:%s",e.what());
  }
  catch(...)   {
    printout(FATAL,"Geant4Kernel","+++ UNKNOWN exception while simulating.");
  }
int Geant4Kernel::runEvents(int num_events) {
  m_numEvent = num_events;
  const Geant4Kernel* ptr = s_main_instance.get();
  printout(INFO,"Geant4Kernel","++ Terminate Geant4 and delete associated actions.");
  if ( ptr == this )  {
    Geant4Exec::terminate(*this);
  }
  destroyPhases();
  releaseObjects(m_globalFilters);
  releaseObjects(m_globalActions);
  if ( ptr == this )  {
    deletePtr  (m_runManager);
  }
  Geant4ActionContainer::terminate();
  if ( ptr == this && m_lcdd )  {
    m_lcdd->removeExtension < Geant4Kernel > (false);
    m_lcdd->destroyInstance();
    m_lcdd = 0;
  }
}

/// Register action by name to be retrieved when setting up and connecting action objects
/** Note: registered actions MUST be unique.
 *  However, not all actions need to registered....
 *  Only register those, you later need to retrieve by name.
 */
Geant4Kernel& Geant4Kernel::registerGlobalAction(Geant4Action* action) {
  if (action) {
    string nam = action->name();
    GlobalActions::const_iterator i = m_globalActions.find(nam);
    if (i == m_globalActions.end()) {
      action->addRef();
      m_globalActions[nam] = action;
Markus Frank's avatar
Markus Frank committed
      printout(INFO,"Geant4Kernel","++ Registered global action %s of type %s",
               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()));
  throw runtime_error(format("Geant4Kernel", "DDG4: Attempt to globally register an invalid "
}

/// Retrieve action from repository
Geant4Action* Geant4Kernel::globalAction(const std::string& action_name, bool throw_if_not_present) {
  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()));
    }
    return 0;
  }
  return (*i).second;
}

/// Register filter by name to be retrieved when setting up and connecting filter objects
/** Note: registered filters MUST be unique.
 *  However, not all filters need to registered....
 *  Only register those, you later need to retrieve by name.
 */
Geant4Kernel& Geant4Kernel::registerGlobalFilter(Geant4Action* filter) {
  if (filter) {
    string nam = filter->name();
    GlobalActions::const_iterator i = m_globalFilters.find(nam);
    if (i == m_globalFilters.end()) {
      filter->addRef();
      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()));
  throw runtime_error(format("Geant4Kernel", "DDG4: Attempt to globally register an invalid "
}

/// Retrieve filter from repository
Geant4Action* Geant4Kernel::globalFilter(const std::string& filter_name, bool throw_if_not_present) {
  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()));
    }
    return 0;
  }
  return (*i).second;
}

/// Execute phase action if it exists
bool Geant4Kernel::executePhase(const std::string& nam, const void** arguments)  const   {
  Phases::const_iterator i = m_phases.find(nam);
  if (i != m_phases.end())   {
    (*i).second->execute(arguments);
    return true;
  }
  return false;
}

/// Access phase by name
Geant4ActionPhase* Geant4Kernel::getPhase(const std::string& nam) {
  Phases::const_iterator i = m_phases.find(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()));
/// Add a new phase to the phase
Geant4ActionPhase* Geant4Kernel::addSimplePhase(const std::string& name, bool throw_on_exist)   {
  return addPhase(name,typeid(void),typeid(void),typeid(void),throw_on_exist);
}

/// Add a new phase
Geant4ActionPhase* Geant4Kernel::addPhase(const std::string& nam, const type_info& arg0, const type_info& arg1,
                                          const type_info& arg2, bool throw_on_exist) {
  Phases::const_iterator i = m_phases.find(nam);
  if (i == m_phases.end()) {
    Geant4ActionPhase* p = new Geant4ActionPhase(workerContext(), nam, arg0, arg1, arg2);
    m_phases.insert(make_pair(nam, p));
    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()));
  }
  return (*i).second;
}

/// Remove an existing phase from the phase. If not existing returns false
bool Geant4Kernel::removePhase(const std::string& nam) {
  Phases::iterator i = m_phases.find(nam);
  if (i != m_phases.end()) {
    delete (*i).second;
    m_phases.erase(i);
    return true;
  }
  return false;
}

/// Destroy all phases. To be called only at shutdown
void Geant4Kernel::destroyPhases() {
  destroyObjects(m_phases);