From 64071e76bce389ad290737baf0f48f75b5b1a23a Mon Sep 17 00:00:00 2001 From: Markus Frank <Markus.Frank@cern.ch> Date: Tue, 9 Apr 2019 18:33:23 +0200 Subject: [PATCH] Give access to Geant4 world volume to Geant4Actions. Add plugin action to write Geant4 GDML files --- DDCore/src/DetectorImp.cpp | 6 + DDG4/include/DDG4/Geant4Context.h | 15 ++- DDG4/include/DDG4/Geant4Kernel.h | 10 +- .../Geant4DetectorGeometryConstruction.cpp | 2 + DDG4/plugins/Geant4GDMLWriteAction.cpp | 126 ++++++++++++++++++ DDG4/src/Geant4Context.cpp | 5 + DDG4/src/Geant4Exec.cpp | 1 + DDG4/src/Geant4Kernel.cpp | 13 ++ examples/CLICSiD/CMakeLists.txt | 8 ++ examples/CLICSiD/scripts/CLIC_GDML.py | 41 ++++++ 10 files changed, 221 insertions(+), 6 deletions(-) create mode 100644 DDG4/plugins/Geant4GDMLWriteAction.cpp create mode 100644 examples/CLICSiD/scripts/CLIC_GDML.py diff --git a/DDCore/src/DetectorImp.cpp b/DDCore/src/DetectorImp.cpp index 5015d7240..2416ed3dd 100644 --- a/DDCore/src/DetectorImp.cpp +++ b/DDCore/src/DetectorImp.cpp @@ -57,6 +57,7 @@ using namespace std; ClassImp(DetectorImp) namespace { + recursive_mutex s_detector_apply_lock; struct TypePreserve { DetectorBuildType& m_t; TypePreserve(DetectorBuildType& t) @@ -594,6 +595,7 @@ namespace { /// Finalize/close the geometry void DetectorImp::endDocument(bool close_geometry) { TGeoManager* mgr = m_manager; + lock_guard<recursive_mutex> lock(s_detector_apply_lock); if ( close_geometry && !mgr->IsClosed() ) { #if 0 Region trackingRegion("TrackingRegion"); @@ -621,6 +623,7 @@ void DetectorImp::endDocument(bool close_geometry) { void DetectorImp::init() { if (!m_world.isValid()) { TGeoManager* mgr = m_manager; + lock_guard<recursive_mutex> lock(s_detector_apply_lock); Constant air_const = getRefChild(m_define, "Air", false); Constant vac_const = getRefChild(m_define, "Vacuum", false); Box worldSolid; @@ -681,12 +684,14 @@ void DetectorImp::init() { /// Read any geometry description or alignment file void DetectorImp::fromXML(const string& xmlfile, DetectorBuildType build_type) { TypePreserve build_type_preserve(m_buildType = build_type); + lock_guard<recursive_mutex> lock(s_detector_apply_lock); processXML(xmlfile,0); } /// Read any geometry description or alignment file with external XML entity resolution void DetectorImp::fromXML(const string& fname, xml::UriReader* entity_resolver, DetectorBuildType build_type) { TypePreserve build_type_preserve(m_buildType = build_type); + lock_guard<recursive_mutex> lock(s_detector_apply_lock); processXML(fname,entity_resolver); } @@ -699,6 +704,7 @@ void DetectorImp::dump() const { /// Manipulate geometry using facroy converter long DetectorImp::apply(const char* factory_type, int argc, char** argv) const { + lock_guard<recursive_mutex> lock(s_detector_apply_lock); string fac = factory_type; try { Detector* thisPtr = const_cast<DetectorImp*>(this); diff --git a/DDG4/include/DDG4/Geant4Context.h b/DDG4/include/DDG4/Geant4Context.h index 20b130b89..d921faba2 100644 --- a/DDG4/include/DDG4/Geant4Context.h +++ b/DDG4/include/DDG4/Geant4Context.h @@ -23,6 +23,7 @@ class G4Run; class G4Track; class G4Event; class G4VTrajectory; +class G4VPhysicalVolume; class G4TrackingManager; /// Namespace for the AIDA detector description toolkit @@ -204,14 +205,15 @@ namespace dd4hep { protected: /// Reference to the kernel object - Geant4Kernel* m_kernel; + Geant4Kernel* m_kernel = 0; /// Transient context variable - depending on the thread context: run reference - Geant4Run* m_run; + Geant4Run* m_run = 0; /// Transient context variable - depending on the thread context: event reference - Geant4Event* m_event; + Geant4Event* m_event = 0; /// Default constructor Geant4Context(Geant4Kernel* kernel); + public: /// Default destructor virtual ~Geant4Context(); @@ -226,9 +228,12 @@ namespace dd4hep { /// Access the geant4 event -- valid only between BeginEvent() and EndEvent()! Geant4Event& event() const; /// Access the geant4 event by ptr. Must be checked by clients! - Geant4Event* eventPtr() const { return m_event; } + Geant4Event* eventPtr() const { return m_event; } /// Access to the kernel object - Geant4Kernel& kernel() const { return *m_kernel; } + Geant4Kernel& kernel() const { return *m_kernel; } + /// Access to geometry world + G4VPhysicalVolume* world() const; + /// Access to the user framework. Specialized function to be implemented by the client template <typename T> T& framework() const; /// Generic framework access diff --git a/DDG4/include/DDG4/Geant4Kernel.h b/DDG4/include/DDG4/Geant4Kernel.h index 761cfddda..76343497a 100644 --- a/DDG4/include/DDG4/Geant4Kernel.h +++ b/DDG4/include/DDG4/Geant4Kernel.h @@ -30,6 +30,7 @@ public: // Forward declarations class G4RunManager; class G4UIdirectory; +class G4VPhysicalVolume; /// Namespace for the AIDA detector description toolkit namespace dd4hep { @@ -91,7 +92,7 @@ namespace dd4hep { /// Property: Names with specialized factories to create G4VSensitiveDetector instances std::map<std::string, std::string> m_sensitiveDetectorTypes; /// Property: Number of events to be executed in batch mode - long m_numEvent; + long m_numEvent = 10; /// Property: Output level int m_outputLevel; @@ -101,6 +102,9 @@ namespace dd4hep { int m_numThreads; /// Flag: Master instance (id<0) or worker (id >= 0) unsigned long m_id, m_ident; + /// Access to geometry world + G4VPhysicalVolume* m_world = 0; + /// Parent reference Geant4Kernel* m_master; Geant4Kernel* m_shared; @@ -190,6 +194,10 @@ namespace dd4hep { const std::map<std::string, std::string>& sensitiveDetectorTypes() const { return m_sensitiveDetectorTypes; } + /// Access to geometry world + G4VPhysicalVolume* world() const; + /// Set the geometry world + void setWorld(G4VPhysicalVolume* volume); /** Property access */ /// Access to the properties of the object diff --git a/DDG4/plugins/Geant4DetectorGeometryConstruction.cpp b/DDG4/plugins/Geant4DetectorGeometryConstruction.cpp index 51a2891d7..ea035da34 100644 --- a/DDG4/plugins/Geant4DetectorGeometryConstruction.cpp +++ b/DDG4/plugins/Geant4DetectorGeometryConstruction.cpp @@ -135,6 +135,8 @@ void Geant4DetectorGeometryConstruction::constructGeo(Geant4DetectorConstruction ctxt->geometry->printLevel = outputLevel(); g4map.attach(ctxt->geometry); G4VPhysicalVolume* w = ctxt->geometry->world(); + // Save away the reference to the world volume + context()->kernel().setWorld(w); // Create Geant4 volume manager only if not yet available g4map.volumeManager(); if ( m_dumpHierarchy ) { diff --git a/DDG4/plugins/Geant4GDMLWriteAction.cpp b/DDG4/plugins/Geant4GDMLWriteAction.cpp new file mode 100644 index 000000000..e1209a466 --- /dev/null +++ b/DDG4/plugins/Geant4GDMLWriteAction.cpp @@ -0,0 +1,126 @@ +//========================================================================== +// 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 DD4HEP_DDG4_GEANT4GDMLWRITEACTION_H +#define DD4HEP_DDG4_GEANT4GDMLWRITEACTION_H + +// Framework include files +#include "DDG4/Geant4Action.h" + +/// Namespace for the AIDA detector description toolkit +namespace dd4hep { + + /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit + namespace sim { + + /// Class to measure the energy of escaping tracks + /** Class to dump Geant4 geometry to GDML + * + * \author M.Frank + * \version 1.0 + * \ingroup DD4HEP_SIMULATION + */ + class Geant4GDMLWriteAction : public Geant4Action { + public: + /// Property: collection names to be dumped + std::string m_output; + /// Poprerty: Flag to overwrite existing files + int m_overWrite; + public: + /// Standard constructor + Geant4GDMLWriteAction(Geant4Context* context, const std::string& nam); + /// Default destructor + virtual ~Geant4GDMLWriteAction(); + /// Install command control messenger if wanted + virtual void installCommandMessenger() override; + /// Write geometry to GDML + virtual void writeGDML(); + }; + + } // End namespace sim +} // End namespace dd4hep + +#endif /* DD4HEP_DDG4_GEANT4GDMLWRITEACTION_H */ + +//==================================================================== +// 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 +// +//==================================================================== + +// Framework include files +#include "DD4hep/InstanceCount.h" +#include "DD4hep/Printout.h" +#include "DD4hep/Primitives.h" +#include "DDG4/Geant4DataDump.h" +#include "DDG4/Geant4UIMessenger.h" + +// Geant 4 includes +#include "G4GDMLParser.hh" + +using namespace std; +using namespace dd4hep; +using namespace dd4hep::sim; + +/// Standard constructor +Geant4GDMLWriteAction::Geant4GDMLWriteAction(Geant4Context* ctxt, const string& nam) + : Geant4Action(ctxt, nam) +{ + m_needsControl = true; + declareProperty("Output", m_output = ""); + declareProperty("OverWrite", m_overWrite = 1); + InstanceCount::increment(this); +} + +/// Default destructor +Geant4GDMLWriteAction::~Geant4GDMLWriteAction() { + InstanceCount::decrement(this); +} + +/// Install command control messenger if wanted +void Geant4GDMLWriteAction::installCommandMessenger() { + Callback cb = Callback(this).make(&Geant4GDMLWriteAction::writeGDML); + m_control->addCall("write", "Write geometry to GDML file",cb); +} +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +/// Write geometry to GDML +void Geant4GDMLWriteAction::writeGDML() { + struct stat buff; + if ( m_output.empty() ) { + error("+++ No GDML file name given. Please set the output file (property Output)"); + return; + } + if ( 0 == ::stat(m_output.c_str(), &buff) && !m_overWrite ) { + error("+++ GDML file elready exists. Please set another output file (property Output)"); + return; + } + if ( 0 == ::stat(m_output.c_str(), &buff) && m_overWrite ) { + warning("+++ GDML file %s already exists. Overwriting existing file.", m_output.c_str()); + ::unlink(m_output.c_str()); + } + G4GDMLParser parser; + info("+++ Writing GDML file: %s", m_output.c_str()); + parser.Write(m_output, context()->world()); +} + +#include "DDG4/Factories.h" +DECLARE_GEANT4ACTION(Geant4GDMLWriteAction) diff --git a/DDG4/src/Geant4Context.cpp b/DDG4/src/Geant4Context.cpp index ff303f2c8..fb3908341 100644 --- a/DDG4/src/Geant4Context.cpp +++ b/DDG4/src/Geant4Context.cpp @@ -60,6 +60,11 @@ Geant4Context::~Geant4Context() { InstanceCount::decrement(this); } +/// Access to geometry world +G4VPhysicalVolume* Geant4Context::world() const { + return m_kernel->world(); +} + /// Set the geant4 run reference void Geant4Context::setRun(Geant4Run* new_run) { m_run = new_run; diff --git a/DDG4/src/Geant4Exec.cpp b/DDG4/src/Geant4Exec.cpp index 573ce5d1e..bfad2896e 100644 --- a/DDG4/src/Geant4Exec.cpp +++ b/DDG4/src/Geant4Exec.cpp @@ -422,6 +422,7 @@ namespace dd4hep { if ( 0 == m_ctxt.world ) { m_sequence->except("+++ Executing G4 detector construction did not result in a valid world volume!"); } + m_sequence->context()->kernel().setWorld(m_ctxt.world); return m_ctxt.world; } diff --git a/DDG4/src/Geant4Kernel.cpp b/DDG4/src/Geant4Kernel.cpp index e497df984..46e9cf5f8 100644 --- a/DDG4/src/Geant4Kernel.cpp +++ b/DDG4/src/Geant4Kernel.cpp @@ -102,6 +102,7 @@ Geant4Kernel::Geant4Kernel(Geant4Kernel* m, unsigned long ident) { char text[64]; m_detDesc = m_master->m_detDesc; + m_world = m_master->m_world; m_ident = m_master->m_workers.size(); m_numEvent = m_master->m_numEvent; m_runManagerType = m_master->m_runManagerType; @@ -199,6 +200,18 @@ int Geant4Kernel::numWorkers() const { return m_workers.size(); } +/// Access to geometry world +G4VPhysicalVolume* Geant4Kernel::world() const { + if ( this != m_master ) return m_master->world(); + return m_world; +} + +/// Set the geometry world +void Geant4Kernel::setWorld(G4VPhysicalVolume* volume) { + if ( this == m_master ) m_world = volume; + else m_master->setWorld(volume); +} + void Geant4Kernel::printProperties() const { printout(ALWAYS,"Geant4Kernel","OutputLevel: %d", m_outputLevel); printout(ALWAYS,"Geant4Kernel","UI: %s", m_uiName.c_str()); diff --git a/examples/CLICSiD/CMakeLists.txt b/examples/CLICSiD/CMakeLists.txt index 7946bf4e0..cc2f74aeb 100644 --- a/examples/CLICSiD/CMakeLists.txt +++ b/examples/CLICSiD/CMakeLists.txt @@ -118,6 +118,14 @@ if (DD4HEP_USE_GEANT4) REGEX_FAIL "Exception;EXCEPTION;ERROR" ) endforeach(script) # + # Write GDML from Geant4 using UI + dd4hep_add_test_reg( CLICSiD_DDG4_GDML_LONGTEST + COMMAND "${CMAKE_INSTALL_PREFIX}/bin/run_test_CLICSiD.sh" + EXEC_ARGS python ${CLICSiDEx_INSTALL}/scripts/CLIC_GDML.py + REQUIRES DDG4 Geant4 + REGEX_PASS "G4GDML. Writing 'CLICSiD.gdml' done !" + REGEX_FAIL "Exception;EXCEPTION;ERROR" ) + # # Material scan dd4hep_add_test_reg( CLICSiD_DDG4_g4material_scan_LONGTEST COMMAND "${CMAKE_INSTALL_PREFIX}/bin/run_test_CLICSiD.sh" diff --git a/examples/CLICSiD/scripts/CLIC_GDML.py b/examples/CLICSiD/scripts/CLIC_GDML.py new file mode 100644 index 000000000..fbcf9581c --- /dev/null +++ b/examples/CLICSiD/scripts/CLIC_GDML.py @@ -0,0 +1,41 @@ +""" + + Perform a material scan using Geant4 shotting geantinos + + @author M.Frank + @version 1.0 + +""" +def run(): + import logging, CLICSid, DDG4 + from DDG4 import OutputLevel as Output + + logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG) + sid = CLICSid.CLICSid() + sid.loadGeometry() + sid.geant4.printDetectors() + kernel = sid.kernel + kernel.UI = "UI" + ui = sid.geant4.setupCshUI(ui=None) + # + # Setup the GDML writer action + writer = DDG4.Action(kernel,'Geant4GDMLWriteAction/Writer') + writer.enableUI() + kernel.registerGlobalAction(writer) + # + # Now initialize. At the Geant4 command prompt we can write the geometry: + # Idle> /ddg4/Writer/write + # or by configuring the UI: + ui.Commands = [ + '/ddg4/Writer/Output CLICSiD.gdml', + '/ddg4/Writer/OverWrite 1', + '/ddg4/Writer/write', + 'exit' + ] + kernel.configure() + kernel.initialize() + kernel.run() + kernel.terminate() + +if __name__ == "__main__": + run() -- GitLab