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