Newer
Older
Markus Frank
committed
//==========================================================================
Markus Frank
committed
//--------------------------------------------------------------------------
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
Markus Frank
committed
// All rights reserved.
Markus Frank
committed
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
Markus Frank
committed
// Author : M.Frank
//
//==========================================================================
Markus Frank
committed
// Framework include files
#include "DD4hep/Printout.h"
#include "DD4hep/Primitives.h"
#include "DD4hep/InstanceCount.h"
#include "DD4hep/Handle.h"
#include "DDG4/Geant4RunAction.h"
#include "DDG4/Geant4EventAction.h"
#include "DDG4/Geant4SteppingAction.h"
#include "DDG4/Geant4TrackingAction.h"
#include "DDG4/Geant4StackingAction.h"
#include "DDG4/Geant4GeneratorAction.h"
Markus Frank
committed
#include "DDG4/Geant4UserInitialization.h"
#include "DDG4/Geant4DetectorConstruction.h"
#include "DDG4/Geant4Kernel.h"
#include "DDG4/Geant4Random.h"
Markus Frank
committed
// Geant4 include files
Markus Frank
committed
#include "G4Version.hh"
Markus Frank
committed
#include "G4UserRunAction.hh"
#include "G4UserEventAction.hh"
#include "G4UserTrackingAction.hh"
#include "G4UserStackingAction.hh"
#include "G4UserSteppingAction.hh"
#include "G4VUserPhysicsList.hh"
#include "G4VModularPhysicsList.hh"
#include "G4VUserPrimaryGeneratorAction.hh"
Markus Frank
committed
#include "G4VUserActionInitialization.hh"
#include "G4VUserDetectorConstruction.hh"
Markus Frank
committed
// C/C++ include files
Markus Frank
committed
namespace {
G4Mutex action_mutex=G4MUTEX_INITIALIZER;
}
/// Namespace for the AIDA detector description toolkit
/// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
/// Sequence handler implementing common actions to all sequences.
/** @class SequenceHdl
*
* @author M.Frank
* @version 1.0
*/
Markus Frank
committed
template <typename T> class SequenceHdl {
public:
Markus Frank
committed
mutable Geant4Context* m_activeContext;
Markus Frank
committed
: m_sequence(0), m_activeContext(0) {
Markus Frank
committed
SequenceHdl(Geant4Context* ctxt, T* seq) : m_sequence(0), m_activeContext(ctxt) {
Markus Frank
committed
_aquire(seq);
Markus Frank
committed
_release();
Markus Frank
committed
InstanceCount::increment(this);
m_sequence = s;
/// Release object
void _release() {
detail::releasePtr(m_sequence);
InstanceCount::decrement(this);
}
/// Update Geant4Context for current call
Markus Frank
committed
void updateContext(Geant4Context* ctxt) {
m_activeContext = ctxt;
if ( m_sequence ) {
m_sequence->updateContext(ctxt);
}
}
/// Access reference to the current active Geant4Context structure
Markus Frank
committed
Geant4Context* context() const {
Markus Frank
committed
}
/// Access reference to the current active Geant4Kernel structure
Markus Frank
committed
Geant4Kernel& kernel() const {
Markus Frank
committed
}
/// G4 callback in multi threaded mode to configure thread fiber
Markus Frank
committed
void configureFiber(Geant4Context* ctxt) {
if ( m_sequence ) {
m_sequence->configureFiber(ctxt);
}
Markus Frank
committed
Geant4Run* r = new Geant4Run(run);
m_activeContext->setRun(r);
Markus Frank
committed
Geant4Run* r = m_activeContext->runPtr();
if ( r ) {
m_activeContext->setRun(0);
Markus Frank
committed
}
Geant4Event* e = new Geant4Event(evt,Geant4Random::instance());
Markus Frank
committed
m_activeContext->setEvent(e);
Markus Frank
committed
Geant4Event* e = m_activeContext->eventPtr();
if ( e ) {
m_activeContext->setEvent(0);
Markus Frank
committed
}
class Geant4UserRunAction;
class Geant4UserEventAction;
/// Concrete implementation of the Geant4 run action
/** @class Geant4UserRunAction
*
* @author M.Frank
* @version 1.0
*/
Markus Frank
committed
class Geant4UserRunAction :
public G4UserRunAction,
public SequenceHdl<Geant4RunActionSequence>
{
Markus Frank
committed
public:
Markus Frank
committed
Geant4UserRunAction(Geant4Context* ctxt, Geant4RunActionSequence* seq)
: Base(ctxt, seq), eventAction(0) {
Markus Frank
committed
updateContext(ctxt);
configureFiber(ctxt);
virtual ~Geant4UserRunAction() {
}
virtual void BeginOfRunAction(const G4Run* run) final;
virtual void EndOfRunAction(const G4Run* run) final;
/// Concrete implementation of the Geant4 event action
/** @class Geant4UserEventAction
*
* @author M.Frank
* @version 1.0
*/
Markus Frank
committed
class Geant4UserEventAction :
public G4UserEventAction,
public SequenceHdl<Geant4EventActionSequence>
{
Markus Frank
committed
public:
Markus Frank
committed
Geant4UserEventAction(Geant4Context* ctxt, Geant4EventActionSequence* seq)
: Base(ctxt, seq), runAction(0) {
Markus Frank
committed
updateContext(ctxt);
configureFiber(ctxt);
virtual ~Geant4UserEventAction() {
}
virtual void BeginOfEventAction(const G4Event* evt) final;
virtual void EndOfEventAction(const G4Event* evt) final;
/// Concrete implementation of the Geant4 tracking action
/** @class Geant4UserTrackingAction
*
* @author M.Frank
* @version 1.0
*/
Markus Frank
committed
class Geant4UserTrackingAction :
public G4UserTrackingAction,
public SequenceHdl<Geant4TrackingActionSequence>
{
Markus Frank
committed
public:
Markus Frank
committed
Geant4UserTrackingAction(Geant4Context* ctxt, Geant4TrackingActionSequence* seq)
: Base(ctxt, seq) {
Markus Frank
committed
updateContext(ctxt);
configureFiber(ctxt);
virtual ~Geant4UserTrackingAction() {
}
virtual void PreUserTrackingAction(const G4Track* trk) final {
Markus Frank
committed
m_sequence->context()->kernel().setTrackMgr(fpTrackingManager);
virtual void PostUserTrackingAction(const G4Track* trk) final {
Markus Frank
committed
m_sequence->context()->kernel().setTrackMgr(0);
/// Concrete implementation of the Geant4 stacking action sequence
/** @class Geant4UserStackingAction
*
* @author M.Frank
* @version 1.0
*/
Markus Frank
committed
class Geant4UserStackingAction :
public G4UserStackingAction,
public SequenceHdl<Geant4StackingActionSequence>
{
Markus Frank
committed
public:
Markus Frank
committed
Geant4UserStackingAction(Geant4Context* ctxt, Geant4StackingActionSequence* seq)
: Base(ctxt, seq) {
Markus Frank
committed
updateContext(ctxt);
configureFiber(ctxt);
virtual ~Geant4UserStackingAction() {
}
virtual void NewStage() override final {
m_sequence->newStage(stackManager);
virtual void PrepareNewEvent() override final {
m_sequence->prepare(stackManager);
}
virtual G4ClassificationOfNewTrack ClassifyNewTrack(const G4Track* track) override final {
auto ret = m_sequence->classifyNewTrack(stackManager, track);
if ( ret.type != NoTrackClassification )
return ret.value;
return this->G4UserStackingAction::ClassifyNewTrack(track);
Markus Frank
committed
/// Concrete implementation of the Geant4 generator action
/** @class Geant4UserGeneratorAction
*
* @author M.Frank
* @version 1.0
*/
Markus Frank
committed
class Geant4UserGeneratorAction :
public G4VUserPrimaryGeneratorAction,
public SequenceHdl<Geant4GeneratorActionSequence>
{
Markus Frank
committed
public:
Markus Frank
committed
Geant4UserGeneratorAction(Geant4Context* ctxt, Geant4GeneratorActionSequence* seq)
: G4VUserPrimaryGeneratorAction(), Base(ctxt, seq) {
Markus Frank
committed
updateContext(ctxt);
configureFiber(ctxt);
virtual ~Geant4UserGeneratorAction() {
}
virtual void GeneratePrimaries(G4Event* event) final;
/// Concrete implementation of the Geant4 stepping action
/** @class Geant4UserSteppingAction
*
* @author M.Frank
* @version 1.0
*/
Markus Frank
committed
class Geant4UserSteppingAction :
public G4UserSteppingAction,
public SequenceHdl<Geant4SteppingActionSequence>
{
Markus Frank
committed
public:
Markus Frank
committed
Geant4UserSteppingAction(Geant4Context* ctxt, Geant4SteppingActionSequence* seq)
: Base(ctxt, seq) {
Markus Frank
committed
updateContext(ctxt);
configureFiber(ctxt);
virtual ~Geant4UserSteppingAction() {
virtual void UserSteppingAction(const G4Step* s) final {
(*m_sequence)(s, fpSteppingManager);
Markus Frank
committed
/// Concrete implementation of the Geant4 user detector construction action sequence
/** @class Geant4UserDetectorConstruction
*
* @author M.Frank
* @version 1.0
*/
class Geant4UserDetectorConstruction :
public G4VUserDetectorConstruction,
public SequenceHdl<Geant4DetectorConstructionSequence>
{
protected:
Geant4DetectorConstructionContext m_ctxt;
public:
/// Standard constructor
Geant4UserDetectorConstruction(Geant4Context* ctxt, Geant4DetectorConstructionSequence* seq)
: G4VUserDetectorConstruction(), Base(ctxt, seq),
m_ctxt(ctxt->kernel().detectorDescription(), this)
Markus Frank
committed
{
}
/// Default destructor
virtual ~Geant4UserDetectorConstruction() {
}
/// Call the actions for the construction of the sensitive detectors and the field
virtual void ConstructSDandField() final;
Markus Frank
committed
/// Call the actions to construct the detector geometry
virtual G4VPhysicalVolume* Construct() final;
Markus Frank
committed
};
/// Concrete implementation of the Geant4 user initialization action sequence
/** @class Geant4UserActionInitialization
*
* @author M.Frank
* @version 1.0
*/
class Geant4UserActionInitialization :
public G4VUserActionInitialization,
public SequenceHdl<Geant4UserInitializationSequence>
{
public:
/// Standard constructor
Geant4UserActionInitialization(Geant4Context* ctxt, Geant4UserInitializationSequence* seq)
: G4VUserActionInitialization(), Base(ctxt, seq) {
}
/// Default destructor
virtual ~Geant4UserActionInitialization() {
Markus Frank
committed
}
/// Build the actions for the worker thread
virtual void Build() const final;
Markus Frank
committed
/// Build the action sequences for the master thread
virtual void BuildForMaster() const final;
Markus Frank
committed
};
/// Begin-of-run callback
void Geant4UserRunAction::BeginOfRunAction(const G4Run* run) {
createClientContext(run);
Markus Frank
committed
kernel().executePhase("begin-run",(const void**)&run);
Markus Frank
committed
if ( m_sequence ) m_sequence->begin(run); // Action not mandatory
}
/// End-of-run callback
void Geant4UserRunAction::EndOfRunAction(const G4Run* run) {
Markus Frank
committed
if ( m_sequence ) m_sequence->end(run); // Action not mandatory
Markus Frank
committed
kernel().executePhase("end-run",(const void**)&run);
destroyClientContext(run);
}
/// Begin-of-event callback
void Geant4UserEventAction::BeginOfEventAction(const G4Event* evt) {
Markus Frank
committed
kernel().executePhase("begin-event",(const void**)&evt);
Markus Frank
committed
if ( m_sequence ) m_sequence->begin(evt); // Action not mandatory
}
/// End-of-event callback
void Geant4UserEventAction::EndOfEventAction(const G4Event* evt) {
Markus Frank
committed
if ( m_sequence ) m_sequence->end(evt); // Action not mandatory
Markus Frank
committed
kernel().executePhase("end-event",(const void**)&evt);
Markus Frank
committed
/// Generate primary particles
void Geant4UserGeneratorAction::GeneratePrimaries(G4Event* event) {
createClientContext(event);
if ( m_sequence ) {
(*m_sequence)(event);
return;
}
throw std::runtime_error("GeneratePrimaries: Panic! No action sequencer defined. "
"No primary particles can be produced.");
}
/// Instantiate Geant4 sensitive detectors and electro-magnetic field
void Geant4UserDetectorConstruction::ConstructSDandField() {
G4AutoLock protection_lock(&action_mutex);
Geant4Context* ctx = m_sequence->context();
Geant4Kernel& krnl = kernel().worker(Geant4Kernel::thread_self(),true);
updateContext(krnl.workerContext());
Markus Frank
committed
m_sequence->constructField(&m_ctxt);
m_sequence->constructSensitives(&m_ctxt);
updateContext(ctx);
}
/// Construct electro magnetic field entity from the dd4hep field
Markus Frank
committed
G4VPhysicalVolume* Geant4UserDetectorConstruction::Construct() {
Markus Frank
committed
/// The G4TransportationManager is thread-local.
/// Thus, regardless of whether the field class object is global or local
/// to a certain volume, a field object must be assigned to G4FieldManager.
Markus Frank
committed
G4AutoLock protection_lock(&action_mutex);
updateContext(m_sequence->context());
m_sequence->constructGeo(&m_ctxt);
if ( nullptr == m_ctxt.world ) {
Markus Frank
committed
m_sequence->except("+++ Executing G4 detector construction did not result in a valid world volume!");
}
Markus Frank
committed
m_sequence->context()->kernel().setWorld(m_ctxt.world);
Markus Frank
committed
return m_ctxt.world;
}
/// Build the actions for the worker thread
void Geant4UserActionInitialization::Build() const {
G4AutoLock protection_lock(&action_mutex);
Geant4Kernel& krnl = kernel().worker(Geant4Kernel::thread_self(),true);
Geant4Context* ctx = krnl.workerContext();
Markus Frank
committed
if ( m_sequence ) {
Geant4Context* old = m_sequence->context();
m_sequence->info("+++ Executing Geant4UserActionInitialization::Build. "
"Context:%p Kernel:%p [%ld]", (void*)ctx, (void*)&krnl, krnl.id());
Markus Frank
committed
m_sequence->updateContext(ctx);
m_sequence->build();
m_sequence->updateContext(old);
}
Markus Frank
committed
/// Set user generator action sequence. Not optional, since event context is defined inside
Geant4UserGeneratorAction* gen_action = new Geant4UserGeneratorAction(ctx,krnl.generatorAction(false));
Markus Frank
committed
SetUserAction(gen_action);
Markus Frank
committed
/// Set the run action sequence. Not optional, since run context is defined/destroyed inside
Geant4UserRunAction* run_action = new Geant4UserRunAction(ctx,krnl.runAction(false));
Markus Frank
committed
SetUserAction(run_action);
Markus Frank
committed
/// Set the event action sequence. Not optional, since event context is destroyed inside
Geant4UserEventAction* evt_action = new Geant4UserEventAction(ctx,krnl.eventAction(false));
Markus Frank
committed
run_action->eventAction = evt_action;
evt_action->runAction = run_action;
SetUserAction(evt_action);
Markus Frank
committed
/// Set the tracking action sequence
Geant4TrackingActionSequence* trk_action = krnl.trackingAction(false);
Markus Frank
committed
if ( trk_action ) {
Geant4UserTrackingAction* action = new Geant4UserTrackingAction(ctx, trk_action);
SetUserAction(action);
}
Markus Frank
committed
/// Set the stepping action sequence
Geant4SteppingActionSequence* stp_action = krnl.steppingAction(false);
Markus Frank
committed
if ( stp_action ) {
Geant4UserSteppingAction* action = new Geant4UserSteppingAction(ctx, stp_action);
SetUserAction(action);
}
Markus Frank
committed
/// Set the stacking action sequence
Geant4StackingActionSequence* stk_action = krnl.stackingAction(false);
Markus Frank
committed
if ( stk_action ) {
Geant4UserStackingAction* action = new Geant4UserStackingAction(ctx, stk_action);
SetUserAction(action);
}
}
/// Build the action sequences for the master thread
void Geant4UserActionInitialization::BuildForMaster() const {
if ( m_sequence ) {
m_sequence->info("+++ Executing Geant4UserActionInitialization::BuildForMaster....");
m_sequence->buildMaster();
}
}
#include "DDG4/Geant4DetectorConstruction.h"
#include "DDG4/Geant4Kernel.h"
using namespace dd4hep;
using namespace dd4hep::sim;
// Geant4 include files
#include "G4RunManager.hh"
/// Compatibility actions for running Geant4 in single threaded mode
/** @class Geant4Compatibility
*
* @author M.Frank
* @version 1.0
*/
Markus Frank
committed
class Geant4Compatibility {
public:
/// Default constructor
Geant4Compatibility() = default;
/// Default destructor
virtual ~Geant4Compatibility() = default;
/// Detector construction invocation in compatibility mode
Markus Frank
committed
Geant4DetectorConstructionSequence* buildDefaultDetectorConstruction(Geant4Kernel& kernel);
};
/// Detector construction invocation in compatibility mode
Markus Frank
committed
Geant4DetectorConstructionSequence* Geant4Compatibility::buildDefaultDetectorConstruction(Geant4Kernel& kernel) {
Geant4Action* cr;
Geant4DetectorConstruction* det_cr;
Geant4Context* ctx = kernel.workerContext();
Geant4DetectorConstructionSequence* seq = kernel.detectorConstruction(true);
printout(WARNING, "Geant4Exec", "+++ Building default Geant4DetectorConstruction for single threaded compatibility.");
Markus Frank
committed
/// Attach first the geometry converter from dd4hep to Geant4
Markus Frank
committed
cr = PluginService::Create<Geant4Action*>("Geant4DetectorGeometryConstruction",ctx,string("ConstructGeometry"));
det_cr = dynamic_cast<Geant4DetectorConstruction*>(cr);
if ( det_cr )
seq->adopt(det_cr);
else
throw runtime_error("Panic! Failed to build Geant4DetectorGeometryConstruction.");
Markus Frank
committed
/// Attach the sensitive detector manipulator:
Markus Frank
committed
cr = PluginService::Create<Geant4Action*>("Geant4DetectorSensitivesConstruction",ctx,string("ConstructSensitives"));
det_cr = dynamic_cast<Geant4DetectorConstruction*>(cr);
if ( det_cr )
seq->adopt(det_cr);
else
throw runtime_error("Panic! Failed to build Geant4DetectorSensitivesConstruction.");
return seq;
}
int Geant4Exec::configure(Geant4Kernel& kernel) {
Detector& description = kernel.detectorDescription();
Markus Frank
committed
Geant4Context* ctx = kernel.workerContext();
Geant4Random* rndm = Geant4Random::instance(false);
if ( !rndm ) {
rndm = new Geant4Random(ctx, "Geant4Random");
/// Initialize the engine etc.
rndm->initialize();
}
Geant4Random::setMainInstance(rndm);
Markus Frank
committed
kernel.executePhase("configure",0);
Markus Frank
committed
/// Construct the default run manager
G4RunManager& runManager = kernel.runManager();
Markus Frank
committed
/// Check if the geometry was loaded
if (description.sensitiveDetectors().size() <= 1) {
printout(WARNING, "Geant4Exec", "+++ Only %d subdetectors present. "
Markus Frank
committed
"You sure you loaded the geometry properly?",
Markus Frank
committed
/// Get the detector constructed
Markus Frank
committed
Geant4DetectorConstructionSequence* user_det = kernel.detectorConstruction(false);
if ( nullptr == user_det && kernel.isMultiThreaded() ) {
Markus Frank
committed
throw runtime_error("Panic! No valid detector construction sequencer present. [Mandatory MT]");
}
if ( nullptr == user_det && !kernel.isMultiThreaded() ) {
Markus Frank
committed
user_det = Geant4Compatibility().buildDefaultDetectorConstruction(kernel);
Markus Frank
committed
Geant4UserDetectorConstruction* det_seq = new Geant4UserDetectorConstruction(ctx,user_det);
runManager.SetUserInitialization(det_seq);
Markus Frank
committed
/// Get the physics list constructed
Markus Frank
committed
Geant4PhysicsListActionSequence* phys_seq = kernel.physicsList(false);
if ( nullptr == phys_seq ) {
Markus Frank
committed
string phys_model = "QGSP_BERT";
phys_seq = kernel.physicsList(true);
phys_seq->property("extends").set(phys_model);
Markus Frank
committed
}
G4VUserPhysicsList* physics = phys_seq->extensionList();
if (nullptr == physics) {
throw runtime_error("Panic! No valid user physics list present!");
#if 0
/// Not here: Use seperate object to do this!
printout(INFO, "Geant4Exec", "+++ PhysicsList RangeCut: %f", phys_seq->m_rangecut );
physics->SetDefaultCutValue(phys_seq->m_rangecut);
physics->SetCuts();
if( DEBUG == printLevel() ) physics->DumpCutValuesTable();
phys_seq->enable(physics);
Markus Frank
committed
Markus Frank
committed
/// Construct the remaining user initialization in multi-threaded mode
Markus Frank
committed
Geant4UserInitializationSequence* user_init = kernel.userInitialization(false);
if ( nullptr == user_init && kernel.isMultiThreaded() ) {
Markus Frank
committed
throw runtime_error("Panic! No valid user initialization sequencer present. [Mandatory MT]");
else if ( nullptr == user_init && !kernel.isMultiThreaded() ) {
Markus Frank
committed
/// Use default actions registered to the default kernel. Will do the right thing...
Markus Frank
committed
user_init = kernel.userInitialization(true);
Markus Frank
committed
Geant4UserActionInitialization* init = new Geant4UserActionInitialization(ctx,user_init);
runManager.SetUserInitialization(init);
return 1;
}
/// Initialize the simulation
int Geant4Exec::initialize(Geant4Kernel& kernel) {
Markus Frank
committed
/// Construct the default run manager
G4RunManager& runManager = kernel.runManager();
Markus Frank
committed
///
/// Initialize G4 engine
///
Markus Frank
committed
kernel.executePhase("initialize",0);
runManager.Initialize();
return 1;
}
/// Run the simulation
int Geant4Exec::run(Geant4Kernel& kernel) {
Property& p = kernel.property("UI");
string value = p.value<string>();
Markus Frank
committed
kernel.executePhase("start",0);
if ( !value.empty() ) {
Geant4Action* ui = kernel.globalAction(value);
if ( ui ) {
Geant4Call* c = dynamic_cast<Geant4Call*>(ui);
if ( c ) {
Markus Frank
committed
(*c)(nullptr);
Markus Frank
committed
return 1;
ui->except("++ Geant4Exec: Failed to start UI interface.");
throw runtime_error(format("Geant4Exec","++ Failed to locate UI interface %s.",value.c_str()));
long nevt = kernel.property("NumEvents").value<long>();
Markus Frank
committed
kernel.executePhase("stop",0);
return 1;
}
/// Run the simulation
Markus Frank
committed
int Geant4Exec::terminate(Geant4Kernel& kernel) {
kernel.executePhase("terminate",0);