Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:49:04

0001 /**
0002 G4CXOpticks.cc
0003 ================
0004 
0005 Q: Why is QSim exposed here, can that be hidden inside cx(CSGOptiX) ?
0006 A: WIP: moving the QSim methods to be called via CSGOptiX
0007 
0008 **/
0009 
0010 
0011 
0012 #include <csignal>
0013 #include "SLOG.hh"
0014 
0015 
0016 #include "spath.h"
0017 #include "ssys.h"
0018 
0019 #include "SEvt.hh"
0020 #include "SSim.hh"
0021 #include "SOpticksResource.hh"
0022 
0023 #include "U4VolumeMaker.hh"
0024 #include "U4Recorder.hh"
0025 
0026 #include "SEventConfig.hh"
0027 #include "U4GDML.h"
0028 #include "U4Tree.h"
0029 
0030 #include "CSGFoundry.h"
0031 
0032 #include "CSGOptiX.h"
0033 
0034 
0035 #ifdef WITH_QS
0036 #include "QSim.hh"
0037 #endif
0038 
0039 
0040 #include "G4CXOpticks.hh"
0041 
0042 using namespace std;
0043 
0044 const plog::Severity G4CXOpticks::LEVEL = SLOG::EnvLevel("G4CXOpticks", "DEBUG");
0045 
0046 U4SensorIdentifier* G4CXOpticks::SensorIdentifier = nullptr ;
0047 void G4CXOpticks::SetSensorIdentifier( U4SensorIdentifier* sid ){ SensorIdentifier = sid ; }  // static
0048 
0049 
0050 G4CXOpticks* G4CXOpticks::INSTANCE = nullptr ;
0051 G4CXOpticks* G4CXOpticks::Get(){ return INSTANCE ; }
0052 const U4Tree* G4CXOpticks::GetU4Tree(){ return INSTANCE ? INSTANCE->tr : nullptr ; }
0053 
0054 
0055 /**
0056 G4CXOpticks::SetGeometry
0057 --------------------------
0058 
0059 Called for example from Detector framework LSExpDetectorConstruction_Opticks::Setup
0060 
0061 **/
0062 
0063 G4CXOpticks* G4CXOpticks::SetGeometry()
0064 {
0065     G4CXOpticks* gx = new G4CXOpticks ;
0066     gx->setGeometry();
0067     return gx ;
0068 }
0069 
0070 G4CXOpticks* G4CXOpticks::SetGeometryFromGDML()
0071 {
0072     G4CXOpticks* gx = new G4CXOpticks ;
0073     gx->setGeometryFromGDML();
0074     return gx ;
0075 }
0076 
0077 
0078 
0079 G4CXOpticks* G4CXOpticks::SetGeometry(const G4VPhysicalVolume* world)
0080 {
0081     G4CXOpticks* gx = new G4CXOpticks ;
0082     gx->setGeometry(world);
0083     return gx ;
0084 }
0085 
0086 
0087 
0088 /**
0089 G4CXOpticks::SetGeometry_JUNO
0090 ------------------------------
0091 
0092 Invoked from JUNOSW LSExpDetectorConstruction_Opticks::Setup
0093 
0094 * Moved high level JUNO setup from there to here for faster update cycle.
0095 
0096 **/
0097 
0098 
0099 G4CXOpticks* G4CXOpticks::SetGeometry_JUNO(const G4VPhysicalVolume* world, const G4VSensitiveDetector* sd, NPFold* jpmt, const NP* jlut )
0100 {
0101     LOG(LEVEL) << "[" ;
0102 
0103     // boot SSim adding extra juno PMT info
0104     SSim::CreateOrReuse();   // done previously by G4CXOpticks::G4CXOpticks in opticksMode > 0 or here in opticksMode:0
0105     SSim::AddExtraSubfold("jpmt", jpmt );
0106     SSim::AddMultiFilm(snam::MULTIFILM, jlut);
0107 
0108     SEvt::CreateOrReuse() ;  // creates 1/2 SEvt depending on OPTICKS_INTEGRATION_MODE (which via above assert matches opticksMode)
0109 
0110 
0111     int opticksMode = SEventConfig::IntegrationMode();
0112     if(opticksMode == 0 || opticksMode == 2) SetNoGPU() ;
0113 
0114     LOG(info) << "[ WITH_G4CXOPTICKS opticksMode " << opticksMode << " sd " << sd  ;
0115 
0116     G4CXOpticks* gx = nullptr ;
0117 
0118     if( opticksMode == 0 || opticksMode == 1 || opticksMode == 3 || opticksMode == 2 )
0119     {
0120         gx = SetGeometry(world) ;
0121         SaveGeometry();
0122     }
0123 
0124     LOG(LEVEL) << "]" ;
0125     return gx ;
0126 }
0127 
0128 
0129 
0130 
0131 
0132 
0133 
0134 
0135 
0136 
0137 
0138 
0139 
0140 
0141 
0142 
0143 
0144 
0145 
0146 void G4CXOpticks::Finalize() // static
0147 {
0148     LOG(LEVEL);
0149 }
0150 
0151 bool G4CXOpticks::NoGPU = false ;
0152 void G4CXOpticks::SetNoGPU(bool no_gpu){ NoGPU = no_gpu ; }
0153 bool G4CXOpticks::IsNoGPU(){  return NoGPU ; }
0154 
0155 
0156 /**
0157 G4CXOpticks::G4CXOpticks
0158 --------------------------
0159 
0160 HMM: sim in GX ctor seems out of place, shouldnt that be coming from CSGFoundry ?
0161 
0162 * part of reason is need to get stree from SSim during conversion before CSGFoundry ctor
0163 * maybe CSGFoundry should be adopting any preexisting SSim
0164 
0165 * HMM: is this is an argument for SSim to live with SEvt rather than with the geometry ?
0166 * NO : constant nature of SSim makes saving it with the geometry make a lot of sense,
0167   so best to stay with the geometry
0168 
0169 **/
0170 
0171 G4CXOpticks::G4CXOpticks()
0172     :
0173     sim(SSim::CreateOrReuse()),
0174     tr(nullptr),
0175     wd(nullptr),
0176     fd(nullptr),
0177     cx(nullptr),
0178 #ifdef WITH_QS
0179     qs(nullptr),
0180 #endif
0181     t0(schrono::stamp())
0182 {
0183     init();
0184 }
0185 
0186 void G4CXOpticks::init()
0187 {
0188     INSTANCE = this ;
0189     LOG(LEVEL) << Desc() << std::endl << desc();
0190 }
0191 
0192 G4CXOpticks::~G4CXOpticks()
0193 {
0194     schrono::TP t1 = schrono::stamp();
0195     double dt = schrono::duration(t0, t1 );
0196     LOG(LEVEL) << "lifetime " << std::setw(10) << std::fixed << std::setprecision(3) << dt << " s " ;
0197 }
0198 
0199 
0200 std::string G4CXOpticks::Desc()
0201 {
0202     return CSGOptiX::Desc() ;
0203 }
0204 
0205 std::string G4CXOpticks::desc() const
0206 {
0207     std::stringstream ss ;
0208     ss << "G4CXOpticks::desc"
0209        << " sim " << ( sim ? "Y" : "N" )
0210        << " tr " << ( tr ? "Y" : "N" )
0211        << " wd " << ( wd ? "Y" : "N" )
0212        << " fd " << ( fd ? "Y" : "N" )
0213        << " cx " << ( cx ? "Y" : "N" )
0214 #ifdef WITH_QS
0215        << " qs " << ( qs ? "Y" : "N" )
0216 #endif
0217        ;
0218     std::string s = ss.str();
0219     return s ;
0220 }
0221 
0222 /**
0223 G4CXOpticks::setGeometryFromGDML
0224 -----------------------------------
0225 
0226 **/
0227 
0228 void G4CXOpticks::setGeometryFromGDML()
0229 {
0230     LOG(LEVEL) << " argumentless " ;
0231 
0232     if(spath::has_CFBaseFromGEOM())
0233     {
0234         LOG(LEVEL) << " has_CFBaseFromGEOM " ;
0235         setGeometry(spath::Resolve("$CFBaseFromGEOM/origin.gdml"));
0236     }
0237     else
0238     {
0239         LOG(fatal) << " failed to setGeometryFromGDML " ;
0240         assert(0);
0241     }
0242 }
0243 
0244 
0245 void G4CXOpticks::setGeometry()
0246 {
0247     if(spath::has_CFBaseFromGEOM())
0248     {
0249         LOG(LEVEL) << " SomeGDMLPath " ;
0250         setGeometry(SOpticksResource::SomeGDMLPath());
0251     }
0252     else if(ssys::hasenv_("GEOM"))
0253     {
0254        // this may load GDML using U4VolumeMaker::PVG if "${GEOM}_GDMLPath" is defined
0255         LOG(LEVEL) << " GEOM/U4VolumeMaker::PV " ;
0256         setGeometry( U4VolumeMaker::PV() );
0257     }
0258     else if(SOpticksResource::CFBaseFromGEOM())
0259     {
0260         LOG(LEVEL) << "[ CSGFoundry::Load " ;
0261         CSGFoundry* cf = CSGFoundry::Load() ;
0262         LOG(LEVEL) << "] CSGFoundry::Load " ;
0263 
0264         LOG(LEVEL) << "[ setGeometry(cf)  " ;
0265         setGeometry(cf);
0266         LOG(LEVEL) << "] setGeometry(cf)  " ;
0267     }
0268     else
0269     {
0270         LOG(fatal) << " failed to setGeometry " ;
0271         assert(0);
0272     }
0273 }
0274 
0275 
0276 
0277 void G4CXOpticks::setGeometry(const char* gdmlpath)
0278 {
0279     LOG(LEVEL) << " gdmlpath [" << gdmlpath << "]" ;
0280     const G4VPhysicalVolume* world = U4GDML::Read(gdmlpath);
0281 
0282     setGeometry(world);
0283 }
0284 
0285 /**
0286 G4CXOpticks::setGeometry
0287 -------------------------
0288 
0289 Geometry translation from Geant4 to NVIDIA OptiX acceleration structures with
0290 associated shader binding table (SBT) data and CUDA programs
0291 is done in several stages::
0292 
0293 
0294           u4/U4Tree                  CSG/CSGImport                  CSGOptiX
0295       G4 —--———————> sysrap/stree.h —————-----——> CSG/CSGFoundry ———-----——> OptiX AS
0296 
0297 First stage is steered by U4Tree::Create which traverses the Geant4 tree of
0298 volumes collecting geometry information into the sysrap/stree.h data structure.
0299 Note that the stree.h comes from the low level sysrap package which does not
0300 depend on Geant4.  The stree is persisted into folders of .txt lists
0301 and .npy arrays located at::
0302 
0303     $HOME/.opticks/GEOM/$GEOM/CSGFoundry/SSim/stree
0304 
0305 Where the "$GEOM" variable is a user selected geometry identifer such as::
0306 
0307      J23_1_0_rc3_ok0
0308 
0309 
0310 * U4Tree/stree+SSim replaces the former GGeo+X4+.. packages
0311 
0312 **/
0313 
0314 
0315 void G4CXOpticks::setGeometry(const G4VPhysicalVolume* world )
0316 {
0317     LOG(LEVEL) << "[ G4VPhysicalVolume world " << world ;
0318     assert(world);
0319     wd = world ;
0320 
0321     assert(sim && "sim instance should have been grabbed/created in ctor" );
0322     stree* st = sim->get_tree();
0323 
0324     LOG(LEVEL) << "[U4Tree::Create " ;
0325     tr = U4Tree::Create(st, world, SensorIdentifier ) ;
0326     LOG(LEVEL) << "]U4Tree::Create " ;
0327 
0328 
0329     LOG(LEVEL) << "[SSim::initSceneFromTree" ;
0330     sim->initSceneFromTree(); // not so easy to do at lower level as do not want to change to SSim arg to U4Tree::Create for headeronly testing
0331     LOG(LEVEL) << "]SSim::initSceneFromTree" ;
0332 
0333 
0334     LOG(LEVEL) << "[CSGFoundry::CreateFromSim" ;
0335     CSGFoundry* fd_ = CSGFoundry::CreateFromSim() ; // adopts SSim::INSTANCE
0336     LOG(LEVEL) << "]CSGFoundry::CreateFromSim" ;
0337 
0338 
0339     LOG(LEVEL) << "[setGeometry(fd_)" ;
0340     setGeometry(fd_);
0341     LOG(LEVEL) << "]setGeometry(fd_)" ;
0342 
0343     LOG(info) << Desc() ;
0344 
0345     LOG(LEVEL) << "] G4VPhysicalVolume world " << world ;
0346 }
0347 
0348 
0349 
0350 /**
0351 G4CXOpticks::setGeometry
0352 ---------------------------
0353 
0354 Prior to CSGOptiX::Create the SEvt instance is created.
0355 
0356 Q: is there a more general place for SEvt hookup ?
0357 A: SSim could hold the SEvt together with stree ?
0358 
0359 But SEvt feels like it should be separate,
0360 as the SSim focus is initialization and SEvt focus is post-init.
0361 
0362 **/
0363 
0364 const char* G4CXOpticks::setGeometry_saveGeometry = ssys::getenvvar("G4CXOpticks__setGeometry_saveGeometry") ;
0365 void G4CXOpticks::setGeometry(CSGFoundry* fd_)
0366 {
0367     setGeometry_(fd_);
0368 }
0369 
0370 
0371 /**
0372 G4CXOpticks::setGeometry_
0373 ---------------------------
0374 
0375 Has side-effect of calling SSim::serialize which
0376 adds the stree and extra subfolds to the SSim subfold.
0377 
0378 Unclear where exactly SSim::serialize needs to happen
0379 for sure it must be after U4Tree::Create and before QSim
0380 instanciation within CSGOptiX::Create
0381 
0382 Note would be better to encapsulate this SSim handling
0383 within U4Tree.h but reluctant currently as U4Tree.h is
0384 header only and SSim.hh is not.
0385 
0386 Q: Is the difficulty only due to the need to defer for the extras ?
0387 
0388 **/
0389 
0390 void G4CXOpticks::setGeometry_(CSGFoundry* fd_)
0391 {
0392     fd = fd_ ;
0393     LOG(LEVEL) << "[ fd " << fd ;
0394 
0395     bool hasDevice = SEventConfig::HasDevice();
0396 
0397     if(NoGPU == false && hasDevice == true)
0398     {
0399         LOG(LEVEL) << "[ CSGOptiX::Create " ;
0400         cx = CSGOptiX::Create(fd);   // uploads geometry to GPU
0401         LOG(LEVEL) << "] CSGOptiX::Create " ;
0402     }
0403     else
0404     {
0405         LOG(info)
0406             << " skip CSGOptiX::Create as NoGPU set OR failed to detect CUDA capable device "
0407             << " NoGPU " << NoGPU
0408             << " hasDevice " << ( hasDevice ? "YES" : "NO " )
0409             ;
0410     }
0411 
0412 #ifdef WITH_QS
0413     qs = cx ? cx->sim : nullptr ;   // QSim
0414 
0415     QSim* qsg = QSim::Get()  ;
0416 
0417     LOG(LEVEL)
0418         << " cx " << ( cx ? "Y" : "N" )
0419         << " qs " << ( qs ? "Y" : "N" )
0420         << " QSim::Get " << ( qsg ? "Y" : "N" )
0421         ;
0422 
0423     assert( qs == qsg );
0424 #endif
0425 
0426 
0427     LOG(LEVEL) << "] fd " << fd ;
0428 
0429 
0430     // try moving this later, so can save things from cx and qs for debug purposes
0431     if( setGeometry_saveGeometry != nullptr )
0432     {
0433         LOG(info) << "[ G4CXOpticks__setGeometry_saveGeometry " ;
0434         saveGeometry(setGeometry_saveGeometry);
0435         LOG(info) << "] G4CXOpticks__setGeometry_saveGeometry " ;
0436     }
0437 }
0438 
0439 
0440 
0441 
0442 std::string G4CXOpticks::descSimulate() const
0443 {
0444     SEvt* sev = SEvt::Get_EGPU() ;
0445     assert(sev);
0446     std::stringstream ss ;
0447     ss
0448        << "[G4CXOpticks::descSimulate" << std::endl
0449        << sev->descSimulate()
0450        << "]G4CXOpticks::descSimulate" << std::endl
0451        ;
0452 
0453     std::string str = ss.str();
0454     return str ;
0455 }
0456 
0457 /**
0458 G4CXOpticks::simulate
0459 -----------------------
0460 
0461 GPU launch doing generation and simulation done here
0462 
0463 **/
0464 
0465 
0466 void G4CXOpticks::simulate(int eventID, bool reset_ )
0467 {
0468     LOG_IF(fatal, NoGPU) << "NoGPU SKIP" ;
0469     if(NoGPU) return ;
0470 
0471     LOG(LEVEL) << "[ "  << eventID;
0472     LOG(LEVEL) << desc() ;
0473 
0474     assert(cx);
0475 
0476 #ifdef WITH_QS
0477     assert(qs);
0478     qs->simulate(eventID, reset_ );
0479 #else
0480     cx->simulate(eventID, reset_);
0481 #endif
0482 
0483 
0484     LOG(LEVEL) << "] " << eventID ;
0485 
0486 }
0487 
0488 /**
0489 G4CXOpticks::reset
0490 ---------------------
0491 
0492 This needs to be called after invoking G4CXOpticks::simulate
0493 when argument reset:false has been used in order to allow copy hits
0494 from the opticks/SEvt into other collections prior to invoking
0495 the reset.
0496 
0497 **/
0498 
0499 void G4CXOpticks::reset(int eventID)
0500 {
0501     LOG_IF(fatal, NoGPU) << "NoGPU SKIP" ;
0502     if(NoGPU) return ;
0503 
0504     assert( SEventConfig::IsRGModeSimulate() );
0505 
0506     unsigned num_hit_0 = SEvt::GetNumHit_EGPU() ;
0507     LOG(LEVEL) << "[ " << eventID << " num_hit_0 " << num_hit_0  ;
0508 
0509 #ifdef WITH_QS
0510     assert(qs);
0511     qs->reset(eventID);
0512 #else
0513     cx->reset(eventID);
0514 #endif
0515 
0516     unsigned num_hit_1 = SEvt::GetNumHit_EGPU() ;
0517     LOG(LEVEL) << "] " << eventID << " num_hit_1 " << num_hit_1  ;
0518 }
0519 
0520 
0521 
0522 
0523 
0524 void G4CXOpticks::simtrace(int eventID)
0525 {
0526     LOG_IF(fatal, NoGPU) << "NoGPU SKIP" ;
0527     if(NoGPU) return ;
0528 
0529     LOG(LEVEL) << "[" ;
0530 
0531 #ifdef WITH_QS
0532     assert(qs);
0533     qs->simtrace(eventID);
0534 #else
0535     assert(cx);
0536     cx->simtrace(eventID);
0537 #endif
0538 
0539 
0540     LOG(LEVEL) << "]" ;
0541 }
0542 
0543 void G4CXOpticks::render()
0544 {
0545     LOG_IF(fatal, NoGPU) << "NoGPU SKIP" ;
0546     if(NoGPU) return ;
0547 
0548     LOG(LEVEL) << "[" ;
0549     assert( cx );
0550     assert( SEventConfig::IsRGModeRender() );
0551     cx->render() ;
0552     LOG(LEVEL) << "]" ;
0553 }
0554 
0555 
0556 
0557 
0558 
0559 
0560 /**
0561 G4CXOpticks::saveGeometry
0562 ---------------------------
0563 
0564 This is called from G4CXOpticks::setGeometry after geometry translation when::
0565 
0566     export G4CXOpticks__setGeometry_saveGeometry=1
0567     export G4CXOpticks=INFO       # to see the directory path
0568 
0569 What is saved includes the gdml and CSGFoundry folder.
0570 Grab that locally::
0571 
0572     GEOM get  ## grab the remote geometry to local
0573 
0574 **/
0575 
0576 
0577 void G4CXOpticks::saveGeometry() const
0578 {
0579     const char* dir = SEventConfig::OutFold() ;
0580     LOG(LEVEL)  << "dir [" << ( dir ? dir : "-" )  ;
0581     saveGeometry(dir) ;
0582 }
0583 
0584 
0585 
0586 void G4CXOpticks::saveGeometry(const char* dir_) const
0587 {
0588     LOG(LEVEL) << " dir_ " << dir_ ;
0589     const char* dir = spath::Resolve(dir_);
0590     LOG(LEVEL) << "[ " << ( dir ? dir : "-" ) ;
0591     LOG(info)  << "[ " << ( dir ? dir : "-" ) ;
0592     std::cout << "G4CXOpticks::saveGeometry [ " << ( dir ? dir : "-" ) << std::endl ;
0593 
0594     if(wd) U4GDML::Write(wd, dir, "origin.gdml" );  // world
0595     if(fd) fd->save(dir) ;
0596 
0597     LOG(LEVEL) << "] " << ( dir ? dir : "-" ) ;
0598 }
0599 
0600 
0601 
0602 
0603 /**
0604 G4CXOpticks::SaveGeometry
0605 ----------------------------
0606 
0607 Saving geometry with this method requires BOTH:
0608 
0609 1. invoking this method from your Opticks integration code, call must be after SetGeometry
0610 2. have defined the below envvar configuring the directory to save into, conventionally::
0611 
0612    export G4CXOpticks__SaveGeometry_DIR=$HOME/.opticks/GEOM/$GEOM
0613 
0614 NB this is distinct from saving when setGeometry is run as
0615 that is too soon to save when additional SSim information
0616 is added.
0617 
0618 HMM : THIS MAY NO LONGER BE TRUE AS NOW USING  SSim::CreateOrReuse
0619 SO CAN NOW SSim::Add... BEFORE G4CXOpticks::SetGeometry
0620 
0621 IF THE setGeometry_saveGeomery proves sufficient can remove this
0622 
0623 **/
0624 
0625 void G4CXOpticks::SaveGeometry() // static
0626 {
0627     LOG_IF(error, INSTANCE==nullptr) << " INSTANCE nullptr : NOTHING TO SAVE " ;
0628     if(INSTANCE == nullptr) return ;
0629 
0630     const char* dir = ssys::getenvvar(SaveGeometry_KEY) ;
0631     LOG_IF(LEVEL, dir == nullptr ) << " not saving as envvar not set " << SaveGeometry_KEY  ;
0632     if(dir == nullptr) return ;
0633     LOG(info) << " save to dir " << dir << " configured via envvar " << SaveGeometry_KEY ;
0634     INSTANCE->saveGeometry(dir);
0635 }
0636 
0637 
0638 
0639 /**
0640 G4CXOpticks::SensitiveDetector_Initialize
0641 -------------------------------------------
0642 
0643 Optional lifecycle method intended to be called from Geant4 integration
0644 G4VSensitiveDetector::Initialize
0645 
0646 **/
0647 
0648 void G4CXOpticks::SensitiveDetector_Initialize(int eventID)
0649 {
0650     LOG(LEVEL) << " eventID " << eventID ;
0651     U4Recorder* recorder = U4Recorder::Get();
0652     if(recorder)
0653     {
0654         recorder->BeginOfEventAction_(eventID);
0655     }
0656 }
0657 
0658 
0659 /**
0660 G4CXOpticks::SensitiveDetector_EndOfEvent
0661 -------------------------------------------
0662 
0663 Optional lifecycle method intended to be called from Geant4 integration
0664 G4VSensitiveDetector::EndOfEvent
0665 
0666 The Geant4 call order gleaned from g4-cls G4EventManager
0667 with the related calls to G4CXOpticks and U4Recorder
0668 that were used previously::
0669 
0670     G4VSensitiveDetector::Initialize(G4HCofThisEvent*HCE)
0671     (eg junoSD_PMT_v2::Initialize)
0672 
0673     G4UserEventAction::BeginOfEventAction  ==> U4Recorder::BeginOfEventAction
0674 
0675 
0676     G4VSensitiveDetector::EndOfEvent(G4HCofThisEvent*HCE)   ==> G4CXOpticks::simulate : AS HITS NEEDED HERE
0677     (eg junoSD_PMT_v2::EndOfEvent)
0678 
0679     G4UserEventAction::EndOfEventAction    ==> U4Recorder::EndOfEventAction
0680 
0681 
0682 
0683 
0684 The above old call order is problematic as the simulate call needs the gensteps
0685 from the SEvt::ECPU that is only wrapped up at U4Recorder::EndOfEventAction.
0686 
0687 
0688 
0689 
0690 
0691 This SensitiveDetector pair of methods enables rearranging the order::
0692 
0693     G4VSensitiveDetector::Initialize       ==> U4Recorder::BeginOfEventAction
0694     (eg junoSD_PMT_v2::Initialize)
0695 
0696     G4UserEventAction::BeginOfEventAction
0697 
0698 
0699     G4VSensitiveDetector::EndOfEvent       ==> U4Recorder::EndOfEventAction,
0700     (eg junoSD_PMT_v2::EndOfEvent)
0701                                            ==> G4CXOpticks::simulate : AS HITS NEEDED HERE
0702 
0703     G4UserEventAction::EndOfEventAction
0704 
0705 Note that the Stepping and Tracking actions handled by
0706 the U4Recorder can proceed unchanged by this.
0707 
0708 **/
0709 
0710 void G4CXOpticks::SensitiveDetector_EndOfEvent(int eventID)
0711 {
0712     LOG(LEVEL) << " eventID " << eventID ;
0713     U4Recorder* recorder = U4Recorder::Get();
0714     if(recorder)
0715     {
0716         recorder->EndOfEventAction_(eventID);
0717     }
0718 }
0719 
0720 
0721 void from_gdml(filesystem::path gdml_file, filesystem::path out_prefix)
0722 {
0723     G4GDMLParser parser;
0724     parser.Read(gdml_file.string(), false);
0725 
0726     const G4VPhysicalVolume *world = parser.GetWorldVolume();
0727 
0728     if (!world)
0729     {
0730         LOG_ERROR << "Failed creatng G4 volume from GDML " << gdml_file << endl;
0731     }
0732     else
0733     {
0734         G4CXOpticks *g4cx = G4CXOpticks::SetGeometry(world);
0735 
0736         auto outpath = out_prefix / gdml_file.stem();
0737 
0738         g4cx->saveGeometry(outpath.string().c_str());
0739 
0740         LOG_INFO << "Created G4 volume " << world->GetName() << " from " << gdml_file << endl;
0741         LOG_INFO << "Saved CSG tree to " << outpath << endl;
0742 
0743         delete g4cx;
0744     }
0745 }