Back to home page

EIC code displayed by LXR

 
 

    


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

0001 #pragma once
0002 /**
0003 U4Tree.h : explore minimal approach to geometry translation
0004 ==============================================================
0005 
0006 Almost all the geometry information is populated
0007 and persisted within the *st* (sysrap/stree.h) member
0008 The other members simply hold on to Geant4 pointers
0009 that make no sense to persist and cannot live
0010 within sysrap as that does not depend on G4 headers.
0011 
0012 Actually header only impls can live anywhere, so
0013 U4Tree.h could move to sysrap as S4Tree.h.
0014 BUT: are more comfortable to do that only with small highly focussed headers.
0015 
0016 Currently this header is not so big, but expect that this will
0017 grow into the central header of the more direct geometry translation.
0018 
0019 See also:
0020 
0021 * sysrap/stree.h
0022 * sysrap/tests/stree_test.cc
0023 
0024 logging
0025 ---------
0026 
0027 Cannot have U4Tree SLOG::EnvLevel because is currently header only,
0028 and cannot easily init a static in header only situation in C++11,
0029 With C++17 can supposedly do this easily with "inline static". See
0030 
0031 https://stackoverflow.com/questions/11709859/how-to-have-static-data-members-in-a-header-only-library
0032 
0033 Pragmatic workaround for runtime logging level is to
0034 adopt the log level int from SSim st.level which is
0035 controlled via envvar::
0036 
0037     export SSim__stree_level=1   # 0:one 1:minimal 2:some 3:verbose 4:extreme
0038 
0039 
0040 **/
0041 
0042 
0043 #include <map>
0044 #include <algorithm>
0045 #include <string>
0046 #include <sstream>
0047 #include <csignal>
0048 
0049 #include <glm/glm.hpp>
0050 #include "G4VPhysicalVolume.hh"
0051 #include "G4LogicalVolume.hh"
0052 #include "G4PVPlacement.hh"
0053 #include "G4Material.hh"
0054 #include "G4LogicalSurface.hh"
0055 #include "G4OpRayleigh.hh"
0056 #include "G4OpticalPhoton.hh"
0057 
0058 #include "NP.hh"
0059 
0060 #include "sdigest.h"
0061 #include "sfreq.h"
0062 #include "stree.h"
0063 #include "suniquename.h"
0064 #include "snd.hh"
0065 #include "sdomain.h"
0066 #include "sstr.h"
0067 #include "ssys.h"
0068 
0069 #include "SSimtrace.h"
0070 #include "SEventConfig.hh"
0071 
0072 #include "U4SensorIdentifier.h"
0073 #include "U4SensorIdentifierDefault.h"
0074 
0075 #include "U4Transform.h"
0076 #include "U4Material.hh"
0077 #include "U4Mat.h"
0078 
0079 #include "U4Surface.h"
0080 #include "U4SurfacePerfect.h"
0081 #include "U4SurfaceArray.h"
0082 
0083 #include "U4Mesh.h"
0084 #include "U4Scint.h"
0085 
0086 #include "U4Solid.h"
0087 #include "U4PhysicsTable.h"
0088 #include "U4MaterialTable.h"
0089 #include "U4TreeBorder.h"
0090 #include "U4Recorder.hh"
0091 
0092 #include <plog/Log.h>
0093 
0094 
0095 struct U4Tree
0096 {
0097     static constexpr const plog::Severity LEVEL = plog::info ;
0098     friend struct U4SimtraceTest ;       // for U4Tree ctor
0099     friend struct U4SimtraceSimpleTest ; // for U4Tree ctor
0100 
0101     stree*                                      st ;
0102     const G4VPhysicalVolume* const              top ;
0103     U4SensorIdentifier*                         sid ;
0104     int                                         level ;
0105     // export SSim__stree_level=1 controls this
0106 
0107     std::map<const G4LogicalVolume* const, int> lvidx ;
0108     std::vector<const G4VPhysicalVolume*>       pvs ;
0109     std::vector<const G4Material*>              materials ;
0110     std::vector<const G4LogicalSurface*>        surfaces ;   // both skin and border
0111     int                                         num_surface_standard ;  // not including implicits
0112     std::vector<const G4VSolid*>                solids ;
0113     U4PhysicsTable<G4OpRayleigh>*               rayleigh_table ;
0114     U4Scint*                                    scint ;
0115 
0116     // disable the below with settings with by defining the below envvar
0117     static constexpr const char* __DISABLE_OSUR_IMPLICIT = "U4Tree__DISABLE_OSUR_IMPLICIT" ;
0118     static constexpr const char* __DISABLE_ISUR_IMPLICIT = "U4Tree__DISABLE_ISUR_IMPLICIT" ;
0119     static constexpr const char* __MATERIAL_DEBUG = "U4Tree__MATERIAL_DEBUG" ;
0120     static constexpr const char* __SOLID_DEBUG = "U4Tree__SOLID_DEBUG" ;
0121     bool                                        enable_osur ;
0122     bool                                        enable_isur ;
0123     int                                         material_debug ;
0124     int                                         solid_debug ;
0125 
0126     static U4Tree* Create(
0127         stree* st,
0128         const G4VPhysicalVolume* const top,
0129         U4SensorIdentifier* sid=nullptr
0130         );
0131 
0132     // using SSim::Get SSim::get_tree is tempting
0133     // but that would add SSim dependency and SSim is not header only
0134     // static U4Tree* Create( const G4VPhysicalVolume* const top, U4SensorIdentifier* sid=nullptr );
0135 
0136 private:
0137     U4Tree(
0138         stree* st,
0139         const G4VPhysicalVolume* const top=nullptr,
0140         U4SensorIdentifier* sid=nullptr
0141         );
0142 
0143     void init();
0144 
0145     void initSid();
0146     static U4PhysicsTable<G4OpRayleigh>* CreateRayleighTable();
0147     void initRayleigh();
0148     void initMaterials();
0149     void initMaterials_NoRINDEX();
0150 
0151     void initMaterials_r(const G4VPhysicalVolume* const pv);
0152     void initMaterial(const G4Material* const mt);
0153 
0154     void initScint();
0155     void initSurfaces();
0156 
0157     void initSolids();
0158     void initSolids_Keys();
0159     void initSolids_Mesh();
0160 
0161     void initSolids_r(const G4VPhysicalVolume* const pv);
0162     void initSolid(const G4LogicalVolume* const lv);
0163 
0164 
0165     void initSolid(const G4VSolid* const so, int lvid );
0166 
0167     void initNodes();
0168     int  initNodes_r(
0169         const G4VPhysicalVolume* const pv,
0170         const G4VPhysicalVolume* const pv_p,
0171         int depth,
0172         int sibdex,
0173         int parent
0174         );
0175 
0176     void initSurfaces_Serialize();
0177     void initStandard();
0178     void initRecorder();
0179 
0180 public:   //  accessors
0181     const G4Material*       getMaterial(int idx) const ;
0182     const G4LogicalSurface* getSurface(int idx) const ;
0183 
0184     std::string              desc() const ;
0185     const G4VPhysicalVolume* get_pv_(int nidx) const ;
0186     const G4PVPlacement*     get_pv(int nidx) const ;
0187     int                      get_pv_copyno(int nidx) const ;
0188 
0189     int get_nidx(const G4VPhysicalVolume* pv) const ;
0190     int get_prim_for_nidx(int nidx) const ;
0191     const char* get_prname(int globalPrimIdx) const ;
0192 
0193 
0194 private:
0195     // identifySensitive called from U4Tree::Create
0196     void identifySensitive();
0197     void identifySensitiveInstances();
0198     void identifySensitiveGlobals();
0199 
0200 public:
0201     void simtrace_scan(const char* base) const ;
0202     static void SimtraceScan( const G4VPhysicalVolume* const pv, const char* base );
0203 };
0204 
0205 
0206 
0207 
0208 /**
0209 U4Tree::Create
0210 ----------------
0211 
0212 Canonically invoked from G4CXOpticks::setGeometry
0213 
0214 HMM: can these be moved into U4Tree ctor now ?
0215 
0216 **/
0217 inline U4Tree* U4Tree::Create(
0218     stree* st,
0219     const G4VPhysicalVolume* const top,
0220     U4SensorIdentifier* sid
0221     )
0222 {
0223     if(st->level > 0) std::cout << "[ U4Tree::Create " << std::endl ;
0224 
0225     LOG(LEVEL) << "[new U4Tree" ;
0226     U4Tree* tree = new U4Tree(st, top, sid ) ;
0227     LOG(LEVEL) << "]new U4Tree" ;
0228 
0229     LOG(LEVEL) << "[stree::factorize" ;
0230     st->factorize();
0231     LOG(LEVEL) << "]stree::factorize" ;
0232 
0233     LOG(LEVEL) << "[U4Tree::identifySensitive" ;
0234     tree->identifySensitive();
0235     LOG(LEVEL) << "]U4Tree::identifySensitive" ;
0236 
0237 
0238     LOG(LEVEL) << "[stree::add_inst" ;
0239     st->add_inst();
0240     LOG(LEVEL) << "]stree::add_inst" ;
0241 
0242     if(st->level > 0) std::cout << "] U4Tree::Create " << std::endl ;
0243 
0244 
0245     LOG(LEVEL) << "[stree::postcreate" ;
0246     st->postcreate() ;
0247     LOG(LEVEL) << "]stree::postcreate" ;
0248 
0249     return tree ;
0250 }
0251 
0252 inline U4Tree::U4Tree(
0253     stree* st_,
0254     const G4VPhysicalVolume* const top_,
0255     U4SensorIdentifier* sid_
0256     )
0257     :
0258     st(st_),
0259     top(top_),
0260     sid(sid_ ? sid_ : new U4SensorIdentifierDefault),
0261     level(st->level),
0262     num_surface_standard(-1),
0263     rayleigh_table(CreateRayleighTable()),
0264     scint(nullptr),
0265     enable_osur(!ssys::getenvbool(__DISABLE_OSUR_IMPLICIT)),
0266     enable_isur(!ssys::getenvbool(__DISABLE_ISUR_IMPLICIT)),
0267     material_debug(ssys::getenvint(__MATERIAL_DEBUG,0)),
0268     solid_debug(ssys::getenvint(__SOLID_DEBUG,0))
0269 {
0270     init();
0271 }
0272 
0273 /**
0274 U4Tree::init
0275 --------------
0276 
0277 **/
0278 
0279 inline void U4Tree::init()
0280 {
0281     if(top == nullptr) return ;
0282 
0283     LOG(LEVEL) << "-initSid" ;
0284     initSid();
0285     LOG(LEVEL) << "-initRayleigh" ;
0286     initRayleigh();
0287     LOG(LEVEL) << "-initMaterials" ;
0288     initMaterials();
0289     LOG(LEVEL) << "-initMaterials_NoRINDEX" ;
0290     initMaterials_NoRINDEX();
0291 
0292     LOG(LEVEL) << "-initScint" ;
0293     initScint();
0294 
0295     LOG(LEVEL) << "-initSurfaces" ;
0296     initSurfaces();
0297 
0298     LOG(LEVEL) << "-initSolids" ;
0299     initSolids();
0300     LOG(LEVEL) << "-initNodes" ;
0301     initNodes();
0302     LOG(LEVEL) << "-initSurfaces_Serialize" ;
0303     initSurfaces_Serialize();
0304 
0305     LOG(LEVEL) << "-initStandard" ;
0306     initStandard();
0307 
0308     LOG(LEVEL) << "-initRecorder" ;
0309     initRecorder();
0310 
0311     std::cout << "U4Tree::init " <<  desc() << std::endl;
0312 
0313 }
0314 
0315 inline void U4Tree::initSid()
0316 {
0317     sid->setLevel(st->level);
0318 }
0319 
0320 /**
0321 U4Tree::initMaterials
0322 -----------------------
0323 
0324 Canonically invoked from U4Tree::init
0325 
0326 1. recursive traverse collecting material pointers from all active
0327    LV into materials vector in postorder of first encounter.
0328 
0329 2. creates SSim/stree/material holding properties of all active materials
0330 
0331 3. creates standard *mat* array using U4Material::MakeStandardArray
0332    from the MPT of the materials, with an override for Water/RAYLEIGH
0333    from the rayleigh_table. The override is needed as G4OpRayleigh
0334    calculates RAYLEIGH scattering lengths from RINDEX for materials named
0335    "Water".
0336 
0337 NOTE THAT MATERIALS NAMED "vetoWater" ARE NOT SPECIAL CASED
0338 SO THERE WILL BE MUCH LESS SCATTERING IN "vetoWater" THAN IN "Water"
0339 
0340 **/
0341 
0342 inline void U4Tree::initMaterials()
0343 {
0344     LOG_IF(info, material_debug > 0 ) << "[" ;
0345 
0346     initMaterials_r(top);
0347     st->material = U4Material::MakePropertyFold(materials);
0348 
0349     std::map<std::string, G4PhysicsVector*> prop_override ;
0350 
0351     G4PhysicsVector* Water_RAYLEIGH = rayleigh_table->find("Water") ;
0352     if(Water_RAYLEIGH) prop_override["Water/RAYLEIGH"] = Water_RAYLEIGH ;
0353 
0354     st->standard->mat = U4Material::MakeStandardArray(materials, prop_override) ;
0355 
0356     LOG_IF(info, material_debug > 0 ) << "]" ;
0357 }
0358 
0359 inline void U4Tree::initMaterials_NoRINDEX()
0360 {
0361     int num_materials = materials.size() ;
0362     for(int i=0 ; i < num_materials ; i++)
0363     {
0364         const G4Material* mt = materials[i] ;
0365         const char* mtn = mt->GetName().c_str();
0366         const G4MaterialPropertyVector* rindex = U4Mat::GetRINDEX( mt ) ;
0367         if(rindex == nullptr) st->mtname_no_rindex.push_back(mtn) ;
0368     }
0369 }
0370 
0371 
0372 /**
0373 U4Tree::initScint
0374 ------------------
0375 
0376 **/
0377 
0378 inline void U4Tree::initScint()
0379 {
0380     scint = U4Scint::Create(st->material) ;
0381     if(scint)
0382     {
0383         st->standard->icdf = scint->icdf ;
0384     }
0385 }
0386 
0387 /**
0388 U4Tree::CreateRayleighTable
0389 ----------------------------
0390 
0391 Trying to find pre-existing G4OpRayleigh process
0392 with the argumentless U4PhysicsTable ctor fails
0393 when U4Tree instanciation happens where it does currently.
0394 As a workaround pass in a throwaway G4OpRayleigh
0395 just to get access to its physics table.
0396 
0397 **/
0398 
0399 inline U4PhysicsTable<G4OpRayleigh>* U4Tree::CreateRayleighTable() // static
0400 {
0401     G4OpRayleigh* proc = new G4OpRayleigh ;
0402 
0403     G4ParticleDefinition* OpticalPhoton = G4OpticalPhoton::Definition() ;
0404     proc->BuildPhysicsTable(*OpticalPhoton);
0405 
0406     U4PhysicsTable<G4OpRayleigh>* tab = new U4PhysicsTable<G4OpRayleigh>(proc) ;
0407     return tab ;
0408 }
0409 
0410 
0411 /**
0412 U4Tree::initRayleigh
0413 ---------------------
0414 
0415 Retain pointer from rayleigh_table formed in ctor into
0416 stree.standard.rayleigh
0417 
0418 **/
0419 
0420 inline void U4Tree::initRayleigh()
0421 {
0422     if(level > 0) std::cerr
0423         << "U4Tree::initRayleigh"
0424         << " rayleigh_table " << std::endl
0425         << ( rayleigh_table ? rayleigh_table->desc() : "-" )
0426         << std::endl
0427         ;
0428 
0429     st->standard->rayleigh = rayleigh_table ? rayleigh_table->tab : nullptr  ;
0430 }
0431 
0432 
0433 inline void U4Tree::initMaterials_r(const G4VPhysicalVolume* const pv)
0434 {
0435     const G4LogicalVolume* lv = pv->GetLogicalVolume() ;
0436     int num_child = int(lv->GetNoDaughters()) ;
0437     for (int i=0 ; i < num_child ;i++ ) initMaterials_r( lv->GetDaughter(i) );
0438 
0439     // postorder visit after recursive call
0440     G4Material* mt = lv->GetMaterial() ;
0441 
0442     if(mt == nullptr) std::cerr
0443        << "U4Tree::initMaterials_r"
0444        << " pv " << ( pv ? pv->GetName() : "-" )
0445        << " lv " << ( lv ? lv->GetName() : "-" )
0446        << " num_child " << num_child
0447        << " mt " << ( mt ? mt->GetName() : "-" )
0448        << std::endl
0449        ;
0450 
0451     assert(mt);
0452 
0453     std::vector<const G4Material*>& m = materials ;
0454 
0455     bool new_material = std::find(m.begin(), m.end(), mt) == m.end();
0456 
0457     if(new_material)
0458     {
0459         LOG_IF(info, material_debug > 0 )
0460            << " pv " << ( pv ? pv->GetName() : "-" )
0461            << " lv " << ( lv ? lv->GetName() : "-" )
0462            << " num_child " << num_child
0463            ;
0464 
0465         LOG_IF(info, material_debug > 0 )
0466            << " pv " << ( pv ? pv->GetName() : "-" )
0467            << " lv " << ( lv ? lv->GetName() : "-" )
0468            << " num_child " << num_child
0469            << " mt " << ( mt ? mt->GetName() : "-" )
0470            ;
0471 
0472         initMaterial(mt);
0473     }
0474 }
0475 
0476 /**
0477 U4Tree::initMaterial
0478 ----------------------
0479 
0480 Invokes stree::add_material with mtname and g4index
0481 collecting into stree::mtname and stree::mtindex vectors
0482 
0483 **/
0484 
0485 inline void U4Tree::initMaterial(const G4Material* const mt)
0486 {
0487     materials.push_back(mt);
0488     const G4String& _mtname = mt->GetName() ;
0489     unsigned g4index = mt->GetIndex() ;
0490     const char* mtname = _mtname.c_str();
0491     st->add_material( mtname, g4index  );
0492 }
0493 
0494 
0495 /**
0496 U4Tree::initSurfaces
0497 ----------------------
0498 
0499 1. U4Surface::Collect G4LogicalBorderSurface, G4LogicalSkinSurface pointers
0500    into *surfaces* vector of G4LogicalSurface
0501 
0502 2. Create stree::surface NPFold with U4Surface::MakeFold with all the
0503    surface properties
0504 
0505 2. collect surface indices and names into stree with with stree::add_surface
0506 
0507 **/
0508 
0509 
0510 inline void U4Tree::initSurfaces()
0511 {
0512     U4Surface::Collect(surfaces);
0513 
0514     U4Surface::CollectRawNames(st->suname_raw, surfaces);
0515 
0516     sstr::StripTail_Unique( st->suname, st->suname_raw, "0x" );
0517 
0518     assert( st->suname.size() == st->suname_raw.size() );
0519 
0520 
0521     st->surface = U4Surface::MakeFold(surfaces, st->suname );
0522 
0523     num_surface_standard = int(surfaces.size()) ;
0524 
0525 
0526     /*
0527     // no longet needed for standard surfaces ?
0528     for(int i=0 ; i < num_surface_standard ; i++)
0529     {
0530         const G4LogicalSurface* ls = surfaces[i] ;
0531         const G4String& rawname_ = ls->GetName() ;
0532         const char* rawname = rawname_.c_str();
0533         const char* name = st->suname[i].c_str();
0534 
0535         st->add_surface(name);
0536     }
0537     */
0538 
0539 }
0540 
0541 /**
0542 U4Tree::initSurfaces_Serialize
0543 -------------------------------
0544 
0545 As this requires to run after implicit surfaces are
0546 collected in initNodes it is too soon to do this
0547 within initSurfaces
0548 
0549 Its too late to add implicit names here because
0550 they are needed by stree::get_boundary_name
0551 
0552 Regarding the perfects, expect will need to add them
0553 earlier too, once develop a test geometry that uses them.
0554 Moved perfect name collection before serialize
0555 HMM: tis unclear where they should be added ?
0556 
0557 **What are perfect surfaces used for ?**
0558 
0559 Vaguely recall that the purpose of the named perfect
0560 surfaces is with simple CSGFoundry forged geometries
0561 that piggyback off other (usually full) geometries
0562 for their material and surface properties.
0563 Hence there is no ordering problem as entire geometries are
0564 loaded and reused for the piggyback.
0565 
0566 All that is needed is to plant the perfects for
0567 subsequent reuse within the test geomerty.
0568 
0569 **/
0570 
0571 inline void U4Tree::initSurfaces_Serialize()
0572 {
0573     std::vector<U4SurfacePerfect> perfect ;
0574     U4SurfacePerfect::Get(perfect);  // perfect Detect,Absorb,Specular,Diffuse surfaces
0575 
0576     int num_perfect = perfect.size();
0577     for(int i=0 ; i < num_perfect ; i++)
0578     {
0579         const U4SurfacePerfect& perf = perfect[i] ;
0580         const char* name = perf.name.c_str() ;
0581         st->add_extra_surface( name );
0582     }
0583 
0584 
0585     U4SurfaceArray serialize(surfaces, st->implicit, perfect) ;
0586     st->standard->sur = serialize.sur ;
0587 
0588 }
0589 
0590 
0591 
0592 /**
0593 U4Tree::initSolids
0594 -------------------
0595 
0596 Uses postorder recursive traverse, ie the "visit" is in the
0597 tail after the recursive call, to match the traverse used
0598 by GDML, and hence giving the same "postorder" indices
0599 for the solid lvIdx.
0600 
0601 The entire volume tree is recursed, but only the
0602 first occurence of each LV solid gets converted
0603 (because they are all the same).
0604 Done this way to have consistent lvIdx soIdx indexing with GDML.
0605 
0606 cf X4PhysicalVolume::convertSolids
0607 
0608 **/
0609 
0610 inline void U4Tree::initSolids()
0611 {
0612     if(solid_debug > -1) std::cout << "[U4Tree::initSolids" << std::endl ;
0613 
0614     initSolids_r(top);
0615     initSolids_Keys();
0616     initSolids_Mesh();
0617 
0618     if(solid_debug > 0) std::cout
0619           << "-U4Tree::initSolids\n"
0620           << " [" << __SOLID_DEBUG << "] " << solid_debug << "\n"
0621           << st->desc_solids()
0622           << "\n"
0623           ;
0624 
0625     if(solid_debug> -1) std::cout << "]U4Tree::initSolids" << std::endl ;
0626 }
0627 
0628 /**
0629 U4Tree::initSolids_Keys
0630 ------------------------
0631 
0632 The st->soname_raw which may have 0x suffixes are
0633 tail stripped and if needed uniqued with _0 _1 suffix
0634 to form st->soname
0635 
0636 **/
0637 
0638 inline void U4Tree::initSolids_Keys()
0639 {
0640     sstr::StripTail_Unique( st->soname, st->soname_raw, "0x" );
0641     assert( st->soname.size() == st->soname_raw.size() );
0642 }
0643 
0644 
0645 /**
0646 U4Tree::initSolids_Mesh
0647 -------------------------
0648 
0649 Uses U4Mesh/G4Polyhedron to triangulate all G4VSolid
0650 with the results serialized into st->mesh NPFold
0651 **/
0652 
0653 inline void U4Tree::initSolids_Mesh()
0654 {
0655     st->mesh = U4Mesh::MakeFold(solids, st->soname ) ;
0656 }
0657 
0658 
0659 /**
0660 U4Tree::initSolids_r
0661 ----------------------
0662 
0663 The raw source names of solids are collected into st->soname_raw vector
0664 
0665 **/
0666 
0667 inline void U4Tree::initSolids_r(const G4VPhysicalVolume* const pv)
0668 {
0669     const G4LogicalVolume* const lv = pv->GetLogicalVolume();
0670     int num_child = int(lv->GetNoDaughters()) ;
0671     for (int i=0 ; i < num_child ;i++ ) initSolids_r( lv->GetDaughter(i) );
0672 
0673     // postorder visit after recursive call
0674     if(lvidx.find(lv) == lvidx.end()) initSolid(lv);
0675 }
0676 inline void U4Tree::initSolid(const G4LogicalVolume* const lv)
0677 {
0678     int lvid = lvidx.size() ;
0679     lvidx[lv] = lvid ;
0680     const G4VSolid* const so = lv->GetSolid();
0681     initSolid(so, lvid);
0682 }
0683 
0684 /**
0685 U4Tree::initSolid
0686 ----------------------
0687 
0688 Decided that intermediate CSG node tree is needed,
0689 as too difficult to leap direct from G4 to CSG
0690 models and a dependency fire break is advantageous.
0691 
0692 cf X4PhysicalVolume::ConvertSolid_ X4Solid::Convert
0693 
0694 
0695 HMM: this could be the place to branch for
0696 special handling of deep CSG trees, based on
0697 hints planted in the G4VSolid name of the root
0698 solid. Doing up here rather than within U4Solid::Convert
0699 would avoid recursive complications.
0700 
0701 BUT: could rely on CSG_LISTNODE hints within the
0702 tree to direct the alt conversion
0703 
0704 **/
0705 
0706 inline void U4Tree::initSolid(const G4VSolid* const so, int lvid )
0707 {
0708     G4String _name = so->GetName() ; // bizarre: G4VSolid::GetName returns by value, not reference
0709     const char* name = _name.c_str();
0710 
0711     assert( int(solids.size()) == lvid );
0712     int d = 0 ;
0713     sn* root = U4Solid::Convert(so, lvid, d );
0714     assert( root );
0715 
0716     solids.push_back(so);
0717     st->soname_raw.push_back(name);
0718     st->solids.push_back(root);
0719 
0720 }
0721 
0722 
0723 
0724 /**
0725 U4Tree::initNodes
0726 -----------------
0727 
0728 Serialize the n-ary tree of structural nodes (the volumes) into nds and trs
0729 vectors within stree holding structural node info and transforms.
0730 
0731 Q: Is the surfaces vector complete before this runs ?
0732 A: YES, U4Tree::initSurfaces collects the vector of surfaces before initNodes
0733    runs, so can rely on not meeting new standard surfaces in initNodes.
0734 
0735 **/
0736 
0737 inline void U4Tree::initNodes()
0738 {
0739     int nidx = initNodes_r(top, nullptr, 0, -1, -1 );
0740     bool nidx_expect = nidx == 0 ;
0741     assert( nidx_expect );
0742     if(!nidx_expect) std::raise(SIGINT);
0743 
0744 }
0745 
0746 /**
0747 U4Tree::initNodes_r
0748 -----------------------
0749 
0750 Most of the visit is preorder before the recursive call,
0751 but sibling to sibling links are done within the
0752 sibling loop using the node index returned by the
0753 recursive call.
0754 
0755 The initial bd int4 (omat,osur,isur,imat) may have
0756 osur and isur overrides that add implicit surfaces when
0757 no prior surface is present and material properties are
0758 RINDEX->NoRINDEX.
0759 
0760 Implicit surfaces are needed for Opticks to reproduce Geant4 fStopAndKill
0761 behavior using additional perfect absorber surfaces in the Opticks
0762 geometry model that are not present in the Geant4 geometry model.
0763 
0764 From the Opticks point of view implicits and perfects are
0765 just handled as standard surfaces with sur entries.
0766 
0767 enable_osur:false
0768     reduces the number of implicits a lot,
0769     which is convenient for initial testing
0770 
0771 enable_osur:true
0772     THIS MUST BE ENABLED FOR Geant4 matching
0773     WITHOUT IT SOME Water///Steel boundaries for example
0774     FAIL TO ABSORB PHOTONS : SEE ~/j/issues/3inch_PMT_geometry_after_virtual_delta.rst
0775 
0776 
0777 NB no-selection collection of below vectors for consistent "nidx" node indices
0778 
0779 stree::nds
0780     snode for all structural nodes aka volumes
0781 
0782 stree::digs
0783     digests for all nodes
0784 
0785 stree::m2w
0786     model2world transforms for all nodes
0787 
0788 stree::w2m
0789     world2model transforms for all nodes
0790 
0791 stree::gtd
0792     flattened transforms for all nodes "GGeo Transform Debug"
0793 
0794 Q:if gtd really just for debug, can it be macroed away ?
0795 
0796 **/
0797 
0798 
0799 
0800 inline int U4Tree::initNodes_r(
0801     const G4VPhysicalVolume* const pv,
0802     const G4VPhysicalVolume* const pv_p,
0803     int depth,
0804     int sibdex,
0805     int parent )
0806 {
0807     // preorder visit before recursive call
0808     U4TreeBorder border(st, pv, pv_p) ;
0809 
0810     int omat = stree::GetPointerIndex<G4Material>(      materials, border.omat_);
0811     int osur = stree::GetPointerIndex<G4LogicalSurface>(surfaces,  border.osur_);
0812     int isur = stree::GetPointerIndex<G4LogicalSurface>(surfaces,  border.isur_);
0813     int imat = stree::GetPointerIndex<G4Material>(      materials, border.imat_);
0814 
0815     int4 bd = {omat, osur, isur, imat } ;
0816 
0817     // overrides add implicit surfaces when no prior surface and RINDEX->NoRINDEX
0818     if(enable_osur && border.has_osur_override(bd)) border.do_osur_override(bd);
0819     if(enable_isur && border.has_isur_override(bd)) border.do_isur_override(bd);
0820 
0821     border.check(bd);
0822     int boundary = st->add_boundary(bd) ;
0823     assert( boundary > -1 );
0824 
0825 
0826     const G4LogicalVolume* const lv = pv->GetLogicalVolume();
0827     int num_child = int(lv->GetNoDaughters()) ;
0828     int lvid = lvidx[lv] ;
0829 
0830     const G4PVPlacement* pvp = dynamic_cast<const G4PVPlacement*>(pv) ;
0831     int copyno = pvp ? pvp->GetCopyNo() : -1 ;
0832 
0833     glm::tmat4x4<double> tr_m2w(1.) ;
0834     U4Transform::GetObjectTransform(tr_m2w, pv);
0835 
0836     glm::tmat4x4<double> tr_w2m(1.) ;
0837     U4Transform::GetFrameTransform(tr_w2m, pv);
0838 
0839     std::string dig = stree::Digest(lvid, tr_m2w);
0840 
0841     int nidx = st->nds.size() ;  // 0-based node index
0842 
0843     snode nd ;
0844 
0845     nd.index = nidx ;
0846     nd.depth = depth ;
0847     nd.sibdex = sibdex ;
0848     nd.parent = parent ;
0849 
0850     nd.num_child = num_child ;
0851     nd.first_child = -1 ;  // gets changed inplace from lower recursion level
0852     nd.next_sibling = -1 ;
0853     nd.lvid = lvid ;
0854 
0855     nd.copyno = copyno ;
0856     nd.boundary = boundary ;
0857 
0858     nd.sensor_id = -1 ;      // HMM: -1 to mean not-a-sensor is problematic GPU side
0859     nd.sensor_index = -1 ;
0860     nd.sensor_name = -1 ;
0861     // changed later by U4Tree::identifySensitiveInstances and stree::reorderSensors
0862 
0863     nd.repeat_index = 0 ;
0864     nd.repeat_ordinal = -1 ;
0865     // changed for instance subtrees by stree::labelFactorSubtrees, remainder left 0/-1
0866 
0867     pvs.push_back(pv);
0868 
0869     st->nds.push_back(nd);
0870     st->digs.push_back(dig);
0871     st->m2w.push_back(tr_m2w);
0872     st->w2m.push_back(tr_w2m);
0873 
0874 
0875     // "GGeo Transform Debug" comparison
0876     glm::tmat4x4<double> tt_gtd(1.) ;
0877     glm::tmat4x4<double> vv_gtd(1.) ;
0878 
0879     bool local = false ;
0880     bool reverse = false ;
0881     std::ostream* out = nullptr ;
0882     stree::VTR* t_stack = nullptr ;
0883 
0884     st->get_node_product( tt_gtd, vv_gtd, nidx, local, reverse, out, t_stack );
0885     // product of m2w transforms from root down to nidx,
0886     // must be after push_backs of nd and tr_m2w
0887 
0888     st->gtd.push_back(tt_gtd);
0889 
0890 
0891     if(sibdex == 0 && nd.parent > -1) st->nds[nd.parent].first_child = nd.index ;
0892     // record first_child nidx into parent snode by reaching up thru the recursion levels
0893 
0894     int p_sib = -1 ;
0895     int i_sib = -1 ;
0896     for (int i=0 ; i < num_child ;i++ )
0897     {
0898         p_sib = i_sib ;
0899         // node index of previous child gets set for i > 0
0900 
0901         //                    ch_pv ch_parent_pv ch_depth ch_sibdex ch_parent
0902         i_sib = initNodes_r( lv->GetDaughter(i), pv, depth+1, i, nd.index );
0903 
0904         if(p_sib > -1) st->nds[p_sib].next_sibling = i_sib ;
0905         // after first child, reach back to previous sibling snode
0906         // to set the sib->sib linkage, default -1
0907     }
0908 
0909     return nd.index ;
0910 }
0911 
0912 
0913 
0914 
0915 
0916 
0917 /**
0918 U4Tree::initStandard
0919 ----------------------
0920 
0921 Have now transitioned from a former unholy mixture of old and new.
0922 Using sstandard::deferred_init
0923 
0924 
0925 **/
0926 
0927 inline void U4Tree::initStandard()
0928 {
0929     st->initStandard();
0930 }
0931 
0932 
0933 /**
0934 U4Tree::initRecorder
0935 --------------------
0936 
0937 Recorder needs the U4Tree for the connection
0938 between Geant4 volumes and Opticks nidx (node indices)
0939 etc..  This is used by U4Simtrace.h to give Opticks
0940 geometry model identity info for U4Navigator.h intersects.
0941 
0942 **/
0943 
0944 
0945 inline void U4Tree::initRecorder()
0946 {
0947     U4Recorder* recorder = U4Recorder::Get();
0948     if(recorder) recorder->setU4Tree(this);
0949     if(!recorder) std::cerr << "U4Tree::initRecorder FAIL no U4Recorder\n" ;
0950 }
0951 
0952 
0953 
0954 
0955 inline const G4Material* U4Tree::getMaterial(int idx) const
0956 {
0957     return idx > -1 ? materials[idx] : nullptr ;
0958 }
0959 inline const G4LogicalSurface* U4Tree::getSurface(int idx) const
0960 {
0961     return idx > -1 ? surfaces[idx] : nullptr ;
0962 }
0963 
0964 inline std::string U4Tree::desc() const
0965 {
0966     std::stringstream ss ;
0967     ss << "U4Tree::desc" << std::endl
0968        << " st "  << ( st ? "Y" : "N" ) << std::endl
0969        << " top " << ( top ? "Y" : "N" ) << std::endl
0970        << " sid " << ( sid ? "Y" : "N" ) << std::endl
0971        << " level " << level << std::endl
0972        << " lvidx " << lvidx.size() << std::endl
0973        << " pvs " << pvs.size() << std::endl
0974        << " materials " << materials.size() << std::endl
0975        << " surfaces " << surfaces.size() << std::endl
0976        << " solids " << solids.size() << std::endl
0977        << " enable_osur " << ( enable_osur ? "YES" : "NO " ) << std::endl
0978        << " enable_isur " << ( enable_isur ? "YES" : "NO " ) << std::endl
0979        ;
0980     std::string str = ss.str();
0981     return str ;
0982 }
0983 
0984 
0985 inline const G4VPhysicalVolume* U4Tree::get_pv_(int nidx) const
0986 {
0987     return nidx > -1 && nidx < int(pvs.size()) ? pvs[nidx] : nullptr ;
0988 }
0989 inline const G4PVPlacement* U4Tree::get_pv(int nidx) const
0990 {
0991     const G4VPhysicalVolume* pv_ = get_pv_(nidx);
0992     return dynamic_cast<const G4PVPlacement*>(pv_) ;
0993 }
0994 inline int U4Tree::get_pv_copyno(int nidx) const
0995 {
0996     const G4PVPlacement* pv = get_pv(nidx) ;
0997     return pv ? pv->GetCopyNo() : -1 ;
0998 }
0999 
1000 
1001 inline int U4Tree::get_nidx(const G4VPhysicalVolume* pv) const
1002 {
1003     int nidx = std::distance( pvs.begin(), std::find( pvs.begin(), pvs.end(), pv ) ) ;
1004     return nidx < int(pvs.size()) ? nidx : -1 ;
1005 }
1006 
1007 /**
1008 U4Tree::get_prim_for_nidx
1009 --------------------------
1010 
1011 Return globalPrimIdx from the argument nidx, using the stree::nidx_prim vector.
1012 
1013 **/
1014 
1015 inline int U4Tree::get_prim_for_nidx(int nidx) const
1016 {
1017     assert(st);
1018     int globalPrimIdx = st->get_prim_for_nidx(nidx) ;
1019     return globalPrimIdx ;
1020 }
1021 
1022 inline const char* U4Tree::get_prname(int globalPrimIdx) const
1023 {
1024     assert(st);
1025     const char* prn = st->get_prname(globalPrimIdx);
1026     return prn ;
1027 }
1028 
1029 
1030 
1031 /**
1032 U4Tree::identifySensitive
1033 ----------------------------
1034 
1035 This is called from U4Tree::Create after U4Tree instanciation
1036 and stree::factorize is called, but before stree::add_inst.
1037 
1038 Initially tried to simply use lv->GetSensitiveDetector() to
1039 identify sensor nodes by that is problematic because
1040 the SD is not on the volume with the copyNo and this
1041 use of copyNo is detector specific.  Also not all JUNO SD
1042 are actually sensitive.
1043 
1044 
1045 1. identifySensitiveInstances : sets stree/snode sensor fields
1046    of instance outer volume nodes
1047 
1048 2. identifySensitiveGlobals : sets stree/snode sensor field
1049    of remainder nodes identified as sensitive
1050    (none expected when using U4SensorIdentifierDefault)
1051 
1052 3. stree::reorderSensors
1053 
1054    * recursive nd traverse setting nd.sensor_index
1055    * nd loop collecting nd.sensor_id to update stree::sensor_id
1056 
1057 
1058 **/
1059 
1060 inline void U4Tree::identifySensitive()
1061 {
1062     if(level > 0) std::cerr
1063         << "[ U4Tree::identifySensitive "
1064         << std::endl
1065         ;
1066 
1067     st->sensor_count = 0 ;
1068 
1069     identifySensitiveInstances();
1070     identifySensitiveGlobals();
1071 
1072     st->reorderSensors();
1073     // change nd.sensor_index to facilitate comparison with GGeo
1074 
1075 
1076     if(level > 0) std::cerr
1077         << "] U4Tree::identifySensitive"
1078         << " st.sensor_count " << st->sensor_count
1079         << std::endl
1080         ;
1081 }
1082 
1083 /**
1084 U4Tree::identifySensitiveInstances
1085 ------------------------------------
1086 
1087 Canonically invoked from U4Tree::Create after stree factorization
1088 and before instance creation.
1089 
1090 This uses stree/sfactor to get node indices of the outer
1091 volumes of all instances. These together with U4SensorIdentifier/sid
1092 allow sensor_id and sensor_index results (-1 when not sensors)
1093 to be added into the stree/snode.
1094 
1095 These values are subsequently used by instance creation and
1096 are inserted into the instance transform fourth column.
1097 
1098 NOTE that the nd.sensor_index may be subsequently changed by
1099 stree::reorderSensors
1100 
1101 NB changes made to U4Tree::identifySensitiveInstances should
1102 usually be made in tandem with U4Tree::identifySensitiveGlobals
1103 
1104 **/
1105 
1106 inline void U4Tree::identifySensitiveInstances()
1107 {
1108     unsigned num_factor = st->get_num_factor();
1109     if(level > 0) std::cerr
1110         << "[ U4Tree::identifySensitiveInstances"
1111         << " num_factor " << num_factor
1112         << " st.sensor_count " << st->sensor_count
1113         << std::endl
1114         ;
1115 
1116     for(unsigned i=0 ; i < num_factor ; i++)
1117     {
1118         std::vector<int> outer ;
1119         st->get_factor_nodes(outer, i);
1120         // nidx of outer volumes of the instances for each factor
1121 
1122         sfactor& fac = st->get_factor_(i);
1123         fac.sensors = 0  ;
1124 
1125         for(unsigned j=0 ; j < outer.size() ; j++)
1126         {
1127             int nidx = outer[j] ;
1128             const G4VPhysicalVolume* pv = get_pv_(nidx) ;
1129             const char* pvn = pv->GetName().c_str() ;
1130 
1131             int sensor_id = sid->getInstanceIdentity(pv) ;
1132             assert( sensor_id >= -1 );  // sensor_id:-1 signifies "not-a-sensor"
1133 
1134             int sensor_index = sensor_id > -1 ? st->sensor_count : -1 ;
1135             int sensor_name = -1 ;
1136 
1137             if(sensor_id > -1 )
1138             {
1139                 st->sensor_count += 1 ;  // count over all factors
1140                 fac.sensors += 1 ;       // count sensors for each factor
1141                 sensor_name = suniquename::Add(pvn, st->sensor_name ) ;
1142             }
1143 
1144             snode& nd = st->nds[nidx] ;
1145             nd.sensor_id = sensor_id ;  // -1:not-a-sensor-at-this-juncture
1146             nd.sensor_index = sensor_index ;
1147             nd.sensor_name = sensor_name ;
1148 
1149             if(level > 1) std::cerr
1150                 << "U4Tree::identifySensitiveInstances"
1151                 << " i " << std::setw(7) << i
1152                 << " sensor_id " << std::setw(7) << sensor_id
1153                 << " sensor_index " << std::setw(7) << sensor_index
1154                 << std::endl
1155                 ;
1156         }
1157 
1158         if(level > 0) std::cerr
1159             << "U4Tree::identifySensitiveInstances"
1160             << " factor " << i
1161             << " fac.sensors " << fac.sensors
1162             << std::endl
1163             ;
1164     }
1165 
1166     if(level > 0) std::cerr
1167         << "] U4Tree::identifySensitiveInstances"
1168         << " num_factor " << num_factor
1169         << " st.sensor_count " << st->sensor_count
1170         << std::endl
1171         ;
1172 }
1173 
1174 /**
1175 U4Tree::identifySensitiveGlobals
1176 ----------------------------------
1177 
1178 This remains rather untested as JUNO geometry does not have sensitive globals.
1179 
1180 **/
1181 
1182 inline void U4Tree::identifySensitiveGlobals()
1183 {
1184     std::vector<int> remainder ;
1185     st->get_remainder_nidx(remainder) ;
1186 
1187     if(level > 0) std::cerr
1188         << "[ U4Tree::identifySensitiveGlobals"
1189         << " st.sensor_count " << st->sensor_count
1190         << " remainder.size " << remainder.size()
1191         << std::endl
1192         ;
1193 
1194     for(unsigned i=0 ; i < remainder.size() ; i++)
1195     {
1196         int nidx = remainder[i] ;
1197         snode& nd = st->nds[nidx] ;
1198 
1199         const G4VPhysicalVolume* pv = get_pv_(nidx) ;
1200         const G4VPhysicalVolume* ppv = get_pv_(nd.parent) ;
1201 
1202         const char* pvn = pv->GetName().c_str() ;
1203         const char* ppvn = ppv ? ppv->GetName().c_str() : nullptr  ;
1204 
1205         int sensor_id = sid->getGlobalIdentity(pv, ppv ) ;
1206         int sensor_index = sensor_id > -1 ? st->sensor_count : -1 ;
1207         int sensor_name = -1 ;
1208 
1209         if(sensor_id > -1 )
1210         {
1211             st->sensor_count += 1 ;  // count over all factors
1212             sensor_name = suniquename::Add(pvn, st->sensor_name ) ;
1213         }
1214         nd.sensor_id = sensor_id ;
1215         nd.sensor_index = sensor_index ;
1216         nd.sensor_name = sensor_name ;
1217 
1218         if(level > 1) std::cerr
1219             << "U4Tree::identifySensitiveGlobals"
1220             << " i " << std::setw(7) << i
1221             << " nidx " << std::setw(6) << nidx
1222             << " sensor_id " << std::setw(7) << sensor_id
1223             << " sensor_index " << std::setw(7) << sensor_index
1224             << " pvn " << pvn
1225             << " ppvn " << ( ppvn ? ppvn : "-" )
1226             << std::endl
1227             ;
1228     }
1229     if(level > 0) std::cerr
1230         << "] U4Tree::identifySensitiveGlobals "
1231         << " st.sensor_count " << st->sensor_count
1232         << " remainder.size " << remainder.size()
1233         << std::endl
1234         ;
1235 }
1236 
1237 
1238 
1239 
1240 /**
1241 U4Tree::simtrace_scan
1242 ------------------------
1243 
1244 HMM: could use NPFold instead of saving to file after the scan of each solid ?
1245 (actually the simple approach probably better in terms of memory)
1246 
1247 Note that SEventConfig::RGModeLabel with U4SimtraceTest.sh
1248 starts as simulate (the default) and then gets changed to
1249 simtrace after the first SSimtrace::Scan call.
1250 
1251 **/
1252 
1253 inline void U4Tree::simtrace_scan(const char* base) const
1254 {
1255     LOG(info) << "[ " << SEventConfig::RGModeLabel() ;
1256     st->save_trs(base);
1257     assert( st->soname.size() == solids.size() );
1258     for(unsigned i=0 ; i < st->soname.size() ; i++)  // over unique solid names
1259     {
1260         const char* soname = st->soname[i].c_str();
1261         const G4VSolid* solid = solids[i] ;
1262         G4String name = solid->GetName();  // bizarre by value
1263 
1264         bool soname_expect = strcmp( name.c_str(), soname ) == 0  ;
1265         assert(soname_expect);
1266         if(!soname_expect) std::raise(SIGINT);
1267 
1268         LOG(info) << " i " << std::setw(3) << i << " RGMode: " << SEventConfig::RGModeLabel() ;
1269         SSimtrace::Scan(solid) ;
1270     }
1271     LOG(info) << "] " << SEventConfig::RGModeLabel() ;
1272 }
1273 
1274 
1275 
1276 /**
1277 U4Tree::SimtraceScan
1278 -----------------------
1279 
1280 **/
1281 
1282 inline void U4Tree::SimtraceScan( const G4VPhysicalVolume* const pv, const char* base ) // static
1283 {
1284     stree st ;
1285     U4Tree ut(&st, pv ) ;
1286     ut.simtrace_scan(base) ;
1287 }
1288 
1289