From 54bda708c550759eedb5e260ddc6965fb9a9f72b Mon Sep 17 00:00:00 2001
From: Markus Frank <Markus.Frank@cern.ch>
Date: Fri, 5 Jul 2024 17:06:58 +0200
Subject: [PATCH] Add example showing effect to regex steered sensitive
 detector assignment

---
 .../Geant4DetectorConstructionResources.cpp   | 307 ++++++++++++++++++
 examples/ClientTests/CMakeLists.txt           |  18 +
 .../compact/BoxOfStraws_sensitive.xml         |  88 +++++
 examples/ClientTests/scripts/BoxOfStraws.py   |  35 +-
 4 files changed, 438 insertions(+), 10 deletions(-)
 create mode 100644 DDG4/plugins/Geant4DetectorConstructionResources.cpp
 create mode 100644 examples/ClientTests/compact/BoxOfStraws_sensitive.xml

diff --git a/DDG4/plugins/Geant4DetectorConstructionResources.cpp b/DDG4/plugins/Geant4DetectorConstructionResources.cpp
new file mode 100644
index 000000000..f07c283a5
--- /dev/null
+++ b/DDG4/plugins/Geant4DetectorConstructionResources.cpp
@@ -0,0 +1,307 @@
+//==========================================================================
+//  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 Markus Frank
+//  \date   2015-11-09
+//
+//==========================================================================
+
+// Framework include files
+#include <DDG4/Geant4DetectorConstruction.h>
+
+// C/C++ include files
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
+  namespace sim {
+
+    /// Debug class to dump resources usage during detector construction
+    /**
+     *  Debug class to dump resources usage during detector construction
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_SIMULATION
+     */
+    class Geant4DetectorConstructionResources : public Geant4DetectorConstruction   {
+    public:
+      /// Class to store the status information of a process from /proc/<pid>/status
+      /**
+       *
+       * \author  M.Frank
+       * \version 1.0
+       */
+      class __attribute__((__packed__)) StatusProcess {
+      public:
+        char   comm[399];
+        char   state;
+        int    umask;
+        int    tgid;
+        int    ngid;
+        int    pid;
+        int    ppid;
+        int    uid;
+        int    gid;
+        int    utrace;
+        int    fdSize;
+        long   vmPeak;
+        long   vmSize;
+        long   vmLock;
+        long   vmPin;
+        long   vmHWM;
+        long   vmRSS;
+        long   vmRSSano;
+        long   vmRSSfil;
+        long   vmRSSshm;
+        long   vmData;
+        long   vmStack;
+        long   vmExe;
+        long   vmLib;
+        long   vmPTE;
+        long   vmSwap;
+        /// Default constructor
+        StatusProcess() {}
+      };
+      std::unique_ptr<StatusProcess> snapshot;
+      std::string when   { "geometry|sensitives" };
+
+      void print_status(const char* tag, const StatusProcess& sp) const;
+      
+    public:
+      /// Initializing constructor for DDG4
+      Geant4DetectorConstructionResources(Geant4Context* ctxt, const std::string& nam);
+      /// Default destructor
+      virtual ~Geant4DetectorConstructionResources();
+      /// Sensitive detector construction callback. Called at "Construct()"
+      virtual void constructGeo(Geant4DetectorConstructionContext*)  override;
+      /// Sensitives construction callback. Called at "ConstructSDandField()"
+      virtual void constructSensitives(Geant4DetectorConstructionContext* ctxt)  override;
+    };
+  }    // End namespace sim
+}      // End namespace dd4hep
+
+
+// Framework include files
+#include <DD4hep/InstanceCount.h>
+#include <DD4hep/Printout.h>
+#include <DD4hep/Plugins.h>
+#include <DD4hep/Detector.h>
+
+#include <DDG4/Geant4Mapping.h>
+#include <DDG4/Geant4Kernel.h>
+#include <DDG4/Factories.h>
+
+#include <stdexcept>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+using namespace dd4hep::sim;
+
+DECLARE_GEANT4ACTION(Geant4DetectorConstructionResources)
+
+namespace  {
+  
+  class SysFile  {
+  public:
+    class FileDescriptor    {
+      /// File handle
+      int m_fd;
+      
+    public:
+      /// Initializing constructor
+      FileDescriptor(int value) : m_fd(value)      {}
+      /// Default destructor. Non-virtuality is intended. Do not inherit!
+      ~FileDescriptor();
+      /// Access file handle
+      int get() const      {   return m_fd;   }
+    };
+    
+      
+  public:
+    /// File name
+    std::string m_name;
+    /// Initializing constructor
+    SysFile(const char* name) : m_name(name) {}
+    /// Initializing constructor
+    SysFile(const std::string& name) : m_name(name) {}
+    /// Default destructor. Non-virtuality is intended. Do not inherit!
+    ~SysFile() {}
+    /// Read buffer from file in  one go
+    int read(char* buffer, size_t len) const;
+  };
+
+  /// Default destructor. Non-virtuality is intended. Do not inherit!
+  SysFile::FileDescriptor::~FileDescriptor()   {
+    if (m_fd > 0)  ::close(m_fd);
+    m_fd = 0;
+  }
+
+  /// Read buffer from file in  one go
+  int SysFile::read(char* buf, size_t siz) const  {
+    FileDescriptor fd(::open(m_name.c_str(),O_RDONLY));  
+    if( fd.get() < 0 )  {
+      std::string err = "Failed to open "+m_name+" ";
+      throw std::runtime_error(err+std::make_error_code(std::errc(errno)).message());
+    }
+    std::size_t tmp = 0;
+    while ( tmp < siz )  {
+      int sc = ::read(fd.get(),buf+tmp,siz-tmp);
+      if ( sc >  0 ) {
+        tmp += sc;
+      }
+      else if ( sc == 0 )  {
+        buf[tmp] = 0;
+        return tmp;
+      }
+      else if ( errno == EINTR )  {
+        printf("EINTR~!!!!\n");
+        continue;
+      }
+      else  {
+        break;
+      }
+    }
+    if ( tmp != siz )  {
+      std::string err = "Read of system file "+m_name+" failed:";
+      throw std::runtime_error(err+std::make_error_code(std::errc(errno)).message());
+    }
+    return tmp;
+  }
+
+  int read_info(Geant4DetectorConstructionResources::StatusProcess& proc, int proc_id) {
+    char buff[2048], *ptr=buff;
+    std::string fn = "/proc/"+std::to_string(proc_id)+"/status";
+    int nitem=0, cnt=SysFile(fn.c_str()).read(buff,sizeof(buff));
+    if(cnt>0)  {
+      int ival;
+      long int lval;
+
+      while(ptr && ptr<(buff+cnt)) {
+        char* p   = ::strchr(ptr,'\t');
+        char* end = ::strchr(ptr,'\n');
+
+        ptr = (end) ? end+1 : 0;
+        if ( 0 == p ) continue;
+        ++p;
+        switch(++nitem) {
+        case 1:   ::sscanf(p,"%s",proc.comm);                  break;
+        case 2:   ::sscanf(p,"%d",&ival); proc.umask = ival;   break;
+        case 3:   ::sscanf(p,"%c",&proc.state);                break;
+        case 4:   ::sscanf(p,"%d",&ival); proc.tgid    = ival; break;
+        case 5:   ::sscanf(p,"%d",&ival); proc.ngid    = ival; break;
+        case 6:   ::sscanf(p,"%d",&ival); proc.pid     = ival; break;
+        case 7:   ::sscanf(p,"%d",&ival); proc.ppid    = ival; break;
+        case 8:   ::sscanf(p,"%d",&ival); proc.utrace  = ival; break;
+        case 9:   ::sscanf(p,"%d",&ival); proc.uid     = ival; break;
+        case 10:  ::sscanf(p,"%d",&ival); proc.gid     = ival; break;
+        case 11:  ::sscanf(p,"%d",&ival); proc.fdSize  = ival; break;
+        case 17:  ::sscanf(p,"%ld",&lval); proc.vmPeak  = lval; break;
+        case 18:  ::sscanf(p,"%ld",&lval); proc.vmSize  = lval; break;
+        case 19:  ::sscanf(p,"%ld",&lval); proc.vmLock  = lval; break;
+        case 20:  ::sscanf(p,"%ld",&lval); proc.vmPin   = lval; break;
+        case 21:  ::sscanf(p,"%ld",&lval); proc.vmHWM   = lval; break;
+        case 22:  ::sscanf(p,"%ld",&lval); proc.vmRSS   = lval; break;
+        case 23:  ::sscanf(p,"%ld",&lval); proc.vmRSSano= lval; break;
+        case 24:  ::sscanf(p,"%ld",&lval); proc.vmRSSfil= lval; break;
+        case 25:  ::sscanf(p,"%ld",&lval); proc.vmRSSshm= lval; break;
+        case 26:  ::sscanf(p,"%ld",&lval); proc.vmData  = lval; break;
+        case 27:  ::sscanf(p,"%ld",&lval); proc.vmStack = lval; break;
+        case 28:  ::sscanf(p,"%ld",&lval); proc.vmExe   = lval; break;
+        case 29:  ::sscanf(p,"%ld",&lval); proc.vmLib   = lval; break;
+        case 30:  ::sscanf(p,"%ld",&lval); proc.vmPTE   = lval; break;
+        case 31:  ::sscanf(p,"%ld",&lval); proc.vmSwap  = lval; break;
+        case 32:  return 1;
+        default:                                                break;
+        }
+      }
+      return 1;
+    }
+    return 0;
+  }
+}
+
+/// Initializing constructor for other clients
+Geant4DetectorConstructionResources::Geant4DetectorConstructionResources(Geant4Context* ctxt, const std::string& nam)
+: Geant4DetectorConstruction(ctxt,nam)
+{
+  declareProperty("When", this->when);
+  InstanceCount::increment(this);
+}
+
+/// Default destructor
+Geant4DetectorConstructionResources::~Geant4DetectorConstructionResources() {
+  InstanceCount::decrement(this);
+}
+
+/// Sensitive detector construction callback. Called at "Construct()"
+void Geant4DetectorConstructionResources::constructGeo(Geant4DetectorConstructionContext*)   {
+  if ( this->when.find("geometry") != std::string::npos )  {
+    this->snapshot = std::make_unique<StatusProcess>();
+    read_info(*this->snapshot, ::getpid());
+    this->print_status("ConstructGeo: ", *this->snapshot);
+  }
+}
+
+void Geant4DetectorConstructionResources::print_status(const char* tag, const StatusProcess& sp) const {
+  this->always("%s Name:     \t%s", tag, sp.comm);
+  this->always("%s State:    \t%c", tag, sp.state);
+  this->always("%s Umask:    \t%8d", tag, sp.umask);
+  this->always("%s Tgid:     \t%8d", tag, sp.tgid);
+  this->always("%s Pid:      \t%8d", tag, sp.pid);
+  this->always("%s PPid:     \t%8d", tag, sp.ppid);
+  this->always("%s utrace:   \t%8d", tag, sp.utrace);
+  this->always("%s Uid:      \t%8d", tag, sp.uid);
+  this->always("%s Gid:      \t%8d", tag, sp.gid);
+  this->always("%s FDSize:   \t%8d", tag, sp.fdSize);
+  this->always("%s VmPeak:   \t%8ld kB", tag, sp.vmPeak);
+  this->always("%s VmSize:   \t%8ld kB", tag, sp.vmSize);
+  this->always("%s VmLck:    \t%8ld kB", tag, sp.vmLock);
+  this->always("%s VmHWM:    \t%8ld kB", tag, sp.vmHWM);
+  this->always("%s VmRSS:    \t%8ld kB", tag, sp.vmRSS);
+  this->always("%s VmRSS     anon:    \t%8ld kB", tag, sp.vmRSSano);
+  this->always("%s VmRSS     file:    \t%8ld kB", tag, sp.vmRSSfil);
+  this->always("%s VmRSS     shm:     \t%8ld kB", tag, sp.vmRSSshm);
+  this->always("%s VmData:   \t%8ld kB", tag, sp.vmData);
+  this->always("%s VmStk:    \t%8ld kB", tag, sp.vmStack);
+  this->always("%s VmExe:    \t%8ld kB", tag, sp.vmExe);
+  this->always("%s VmLib:    \t%8ld kB", tag, sp.vmLib);
+  this->always("%s VmPTE:    \t%8ld kB", tag, sp.vmPTE);
+}
+
+/// Sensitive detector construction callback. Called at "ConstructSDandField()"
+void Geant4DetectorConstructionResources::constructSensitives(Geant4DetectorConstructionContext*)   {
+  if ( this->when.find("sensitives") != std::string::npos )  {
+    StatusProcess rd;
+    read_info(rd, ::getpid());
+    this->print_status("ConstructSD:  ", rd);
+#if 0
+    if ( snapshot )   {
+      const auto& snap = *this->snapshot;
+       this->always("   --> DIFFERENCE:  FDSize:   \t%8d", rd.fdSize - snap.fdSize);
+       this->always("   --> DIFFERENCE:  VmPeak:   \t%8ld kB", rd.vmPeak - snap.vmPeak);
+       this->always("   --> DIFFERENCE:  VmSize:   \t%8ld kB", rd.vmSize - snap.vmSize);
+       this->always("   --> DIFFERENCE:  VmLck:    \t%8ld kB", rd.vmLock - snap.vmLock);
+       this->always("   --> DIFFERENCE:  VmHWM:    \t%8ld kB", rd.vmHWM - snap.vmHWM);
+       this->always("   --> DIFFERENCE:  VmRSS:    \t%8ld kB", rd.vmRSS - snap.vmRSS);
+       this->always("   --> DIFFERENCE:  VmRSS     anon:    \t%8ld kB", rd.vmRSSano - snap.vmRSSano);
+       this->always("   --> DIFFERENCE:  VmRSS     file:    \t%8ld kB", rd.vmRSSfil - snap.vmRSSfil);
+       this->always("   --> DIFFERENCE:  VmRSS     shm:     \t%8ld kB", rd.vmRSSshm - snap.vmRSSshm);
+       this->always("   --> DIFFERENCE:  VmData:   \t%8ld kB", rd.vmData - snap.vmData);
+       this->always("   --> DIFFERENCE:  VmStk:    \t%8ld kB", rd.vmStack - snap.vmStack);
+       this->always("   --> DIFFERENCE:  VmExe:    \t%8ld kB", rd.vmExe - snap.vmExe);
+       this->always("   --> DIFFERENCE:  VmLib:    \t%8ld kB", rd.vmLib - snap.vmLib);
+       this->always("   --> DIFFERENCE:  VmPTE:    \t%8ld kB", rd.vmPTE - snap.vmPTE);
+    }
+#endif
+  }
+  snapshot.reset();
+}
diff --git a/examples/ClientTests/CMakeLists.txt b/examples/ClientTests/CMakeLists.txt
index 2e9ffca65..8ef15dc46 100644
--- a/examples/ClientTests/CMakeLists.txt
+++ b/examples/ClientTests/CMakeLists.txt
@@ -614,4 +614,22 @@ if (DD4HEP_USE_GEANT4)
       REGEX_FAIL "Error;ERROR; Exception"
     )
   endif()
+  #
+  # Test Geant4VolumeManager resource usage
+  dd4hep_add_test_reg(ClientTests_g4_setup_BoxOfStraws_sensitive
+      COMMAND    "${CMAKE_INSTALL_PREFIX}/bin/run_test_ClientTests.sh"
+      EXEC_ARGS  ${Python_EXECUTABLE} ${ClientTestsEx_INSTALL}/scripts/BoxOfStraws.py
+                 -print_level 3 -sensitive
+      REGEX_PASS "ResourcesAfterConstruction       ConstructSD:   VmRSS"
+      REGEX_FAIL "Error;ERROR; Exception"
+  )
+  #
+  # Test without Geant4VolumeManager, but sensitive detector assignment using regex
+  dd4hep_add_test_reg(ClientTests_g4_setup_BoxOfStraws_non_sensitive
+      COMMAND    "${CMAKE_INSTALL_PREFIX}/bin/run_test_ClientTests.sh"
+      EXEC_ARGS  ${Python_EXECUTABLE} ${ClientTestsEx_INSTALL}/scripts/BoxOfStraws.py
+                 -print_level 3
+      REGEX_PASS "ResourcesAfterConstruction       ConstructSD:   VmRSS"
+      REGEX_FAIL "Error;ERROR; Exception"
+  )
 endif(DD4HEP_USE_GEANT4)
diff --git a/examples/ClientTests/compact/BoxOfStraws_sensitive.xml b/examples/ClientTests/compact/BoxOfStraws_sensitive.xml
new file mode 100644
index 000000000..de951417c
--- /dev/null
+++ b/examples/ClientTests/compact/BoxOfStraws_sensitive.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd>
+<!-- #==========================================================================
+     #  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.
+     #
+     #==========================================================================
+-->
+ 
+  <info name="BoxOfStraws"
+	title="Test with a box of straws"
+	author="Markus Frank"
+	url="http://www.cern.ch"
+	status="development"
+	version="1.0">
+    <comment>Detector scalability test with a box of straws</comment>        
+  </info>
+
+  <includes>
+    <gdmlFile  ref="${DD4hepINSTALL}/DDDetectors/compact/elements.xml"/>
+    <gdmlFile  ref="${DD4hepINSTALL}/DDDetectors/compact/materials.xml"/>
+  </includes>
+
+  <define>
+    <constant name="world_size" value="3*m"/>
+    <constant name="world_x" value="world_size"/>
+    <constant name="world_y" value="world_size"/>
+    <constant name="world_z" value="world_size"/>
+  </define>
+
+  <display>
+    <vis name="Invisible" showDaughters="false" visible="false"/>
+    <vis name="InvisibleWithChildren" showDaughters="true" visible="false"/>
+    <vis name="VisibleRed"   alpha="0.4" r="1.0" g="0.0" b="0.0" showDaughters="true" visible="true"/>
+    <vis name="VisibleBlue"  alpha="1.0" r="0.0" g="0.0" b="1.0" showDaughters="true" visible="true"/>
+    <vis name="VisibleGreen" alpha="1.0" r="0.0" g="1.0" b="0.0" showDaughters="false" visible="true"/>
+    <vis name="VisibleGray"  alpha="0.5" r="0.5" g="0.5" b="0.5" showDaughters="true" visible="true"/>
+  </display>
+
+  <limits>
+    <limitset name="BoxOfStrawsLimitSet">
+      <limit name="step_length_max"  particles="*" value="5.0" unit="mm" />
+      <limit name="track_length_max" particles="*" value="1.0" unit="mm" />
+    </limitset>
+  </limits>
+
+  <regions>
+    <region name="StrawRegion" eunit="MeV" lunit="mm" cut="0.01" threshold="0.01">
+      <limitsetref name="BoxOfStrawsLimitSet"/>
+    </region>
+  </regions>
+
+  <detectors>
+    <detector id="1" name="BoxOfStrawsDet" type="DD4hep_BoxOfStraws" readout="BoxOfStrawsHits" vis="VisibleGreen" region="StrawRegion" limits="BoxOfStrawsLimitSet">
+      <box      x="1*m"    y="1*m" z="1000*mm"  limits="BoxOfStrawsLimitSet" vis="VisibleRed"/>
+      <straw rmax="0.5*mm" y="1*m" thickness="0.1*mm" vis="VisibleBlue">
+        <material name="Iron"/>
+      </straw>
+      <gas vis="VisibleGreen">
+        <material name="Argon"/>
+        <sensitive/>
+      </gas>
+      <position x="0*m"   y="0*m"   z="0*m"/>
+      <rotation x="0"     y="0"     z="0"/>
+    </detector>
+  </detectors>
+  
+  <readouts>
+    <readout name="BoxOfStrawsHits">
+      <id>system:8,layer:16,straw:16,y:-12</id>
+    </readout>
+  </readouts>
+
+  <fields>
+    <field name="GlobalSolenoid" type="solenoid" 
+	   inner_field="3.0*tesla"
+	   outer_field="-1.5*tesla" 
+	   zmax="2*m"
+	   outer_radius="2*m">
+    </field>
+  </fields>
+
+</lccdd>
diff --git a/examples/ClientTests/scripts/BoxOfStraws.py b/examples/ClientTests/scripts/BoxOfStraws.py
index d161c466f..55ba94738 100644
--- a/examples/ClientTests/scripts/BoxOfStraws.py
+++ b/examples/ClientTests/scripts/BoxOfStraws.py
@@ -33,7 +33,11 @@ def run():
   kernel = DDG4.Kernel()
   logger = DDG4.Logger('BoxOfStraws')
   install_dir = os.environ['DD4hepExamplesINSTALL']
-  kernel.loadGeometry(str('file:' + install_dir + '/examples/ClientTests/compact/BoxOfStraws.xml'))
+
+  if args.sensitive:
+    kernel.loadGeometry(str('file:' + install_dir + '/examples/ClientTests/compact/BoxOfStraws_sensitive.xml'))
+  else:
+    kernel.loadGeometry(str('file:' + install_dir + '/examples/ClientTests/compact/BoxOfStraws.xml'))
 
   DDG4.importConstants(kernel.detectorDescription(), debug=False)
   geant4 = DDG4.Geant4(kernel)
@@ -54,19 +58,25 @@ def run():
   prt.OutputType = 3  # Print both: table and tree
   kernel.eventAction().adopt(prt)
   #
+  seq, act = geant4.addDetectorConstruction('Geant4DetectorConstructionResources/ResourcesBeforeConstruction')
+  act.When = "geometry";
+  #
   # Configure G4 geometry setup
   seq, act = geant4.addDetectorConstruction('Geant4DetectorGeometryConstruction/ConstructGeo')
   act.DebugVolumes = True
   #
-  # Assign sensitive detectors according to the declarations 'tracker' or 'calorimeter', etc
-  seq, act = geant4.addDetectorConstruction('Geant4DetectorSensitivesConstruction/ConstructSD')
+  if args.sensitive:
+    # Assign sensitive detectors according to the declarations 'tracker' or 'calorimeter', etc
+    seq, act = geant4.addDetectorConstruction('Geant4DetectorSensitivesConstruction/ConstructSD')
+  else:
+    # Assign sensitive detectors in Geant4 by matching a regular expression in the detector sub-tree
+    seq, act = geant4.addDetectorConstruction('Geant4RegexSensitivesConstruction/ConstructSDRegEx')
+    act.Detector = 'BoxOfStrawsDet'
+    act.OutputLevel = Output.ALWAYS
+    act.Match = ['/world_volume_(.*)/BoxOfStrawsDet_(.*)/layer_(.*)/straw_(.*)/gas_(.*)']
   #
-  # Assign sensitive detectors in Geant4 by matching a regular expression in the detector sub-tree
-  seq, act = geant4.addDetectorConstruction('Geant4RegexSensitivesConstruction/ConstructSDRegEx')
-  act.Detector = 'BoxOfStrawsDet'
-  act.OutputLevel = Output.ALWAYS
-  act.Match = ['/world_volume_(.*)/BoxOfStrawsDet_(.*)/layer_(.*)/straw_(.*)/gas_(.*)']
-  act.Match = ['/world_volume_(.*)/BoxOfStrawsDet_(.*)/layer_(.*)/straw_(.*)']
+  seq, act = geant4.addDetectorConstruction('Geant4DetectorConstructionResources/ResourcesAfterConstruction')
+  act.When = "sensitives";
   #
   # Configure I/O
   geant4.setupROOTOutput('RootOutput', 'BoxOfStraws_' + time.strftime('%Y-%m-%d_%H-%M'))
@@ -100,7 +110,12 @@ def run():
   # Now build the physics list:
   phys = geant4.setupPhysics(str('QGSP_BERT'))
   phys.dump()
-  geant4.execute()
+  if args.simultate:
+    geant4.execute()
+  else:
+    geant4.kernel().configure()
+    geant4.kernel().initialize()
+    geant4.kernel().terminate()
 
 
 if __name__ == "__main__":
-- 
GitLab