Back to home page

EIC code displayed by LXR

 
 

    


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

0001 #include <iostream>
0002 #include <iomanip>
0003 #include <sstream>
0004 #include <bitset>
0005 #include <algorithm>
0006 #include <cstring>
0007 
0008 #include "ssys.h"
0009 #include "sstr.h"
0010 
0011 #include "SBit.hh"   // TODO: sbit.h
0012 #include "SBitSet.h"
0013 
0014 #include "SGeoConfig.hh"
0015 #include "SName.h"
0016 #include "SLabel.h"
0017 
0018 #include "SLOG.hh"
0019 
0020 const plog::Severity SGeoConfig::LEVEL = SLOG::EnvLevel("SGeoConfig", "DEBUG");
0021 
0022 
0023 const char* SGeoConfig::_GEOM = ssys::getenvvar(kGEOM, nullptr );
0024 unsigned long long SGeoConfig::_EMM = SBit::FromEString(kEMM, "~0");
0025 const char* SGeoConfig::_ELVSelection   = ssys::getenvvar(kELVSelection, nullptr );
0026 const char* SGeoConfig::_SolidSelection = ssys::getenvvar(kSolidSelection, nullptr );
0027 const char* SGeoConfig::_SolidTrimesh   = ssys::getenvvar(kSolidTrimesh, nullptr );
0028 const char* SGeoConfig::_FlightConfig   = ssys::getenvvar(kFlightConfig  , nullptr );
0029 const char* SGeoConfig::_ArglistPath    = ssys::getenvvar(kArglistPath  , nullptr );
0030 const char* SGeoConfig::_CXSkipLV       = ssys::getenvvar(kCXSkipLV  , nullptr );
0031 const char* SGeoConfig::_CXSkipLV_IDXList = ssys::getenvvar(kCXSkipLV_IDXList, nullptr );
0032 
0033 void SGeoConfig::SetELVSelection(  const char* es){  _ELVSelection   = es ? strdup(es) : nullptr ; }
0034 void SGeoConfig::SetSolidSelection(const char* ss){  _SolidSelection = ss ? strdup(ss) : nullptr ; }
0035 void SGeoConfig::SetSolidTrimesh(  const char* st){  _SolidTrimesh   = st ? strdup(st) : nullptr ; }
0036 void SGeoConfig::SetFlightConfig(  const char* fc){  _FlightConfig   = fc ? strdup(fc) : nullptr ; }
0037 void SGeoConfig::SetArglistPath(   const char* ap){  _ArglistPath    = ap ? strdup(ap) : nullptr ; }
0038 void SGeoConfig::SetCXSkipLV(      const char* cx){  _CXSkipLV       = cx ? strdup(cx) : nullptr ; }
0039 
0040 unsigned long long SGeoConfig::EnabledMergedMesh(){  return _EMM ; }
0041 const char* SGeoConfig::SolidSelection(){ return _SolidSelection ; }
0042 const char* SGeoConfig::SolidTrimesh(){   return _SolidTrimesh ; }
0043 const char* SGeoConfig::FlightConfig(){   return _FlightConfig ; }
0044 const char* SGeoConfig::ArglistPath(){    return _ArglistPath ; }
0045 const char* SGeoConfig::CXSkipLV(){       return _CXSkipLV ? _CXSkipLV : "" ; }
0046 const char* SGeoConfig::CXSkipLV_IDXList(){  return _CXSkipLV_IDXList ? _CXSkipLV_IDXList : "" ; }
0047 
0048 
0049 const char* SGeoConfig::GEOM(){           return _GEOM ; }
0050 const char* SGeoConfig::ELVSelection(){   return _ELVSelection ; }
0051 
0052 
0053 /**
0054 SGeoConfig::ELVSelection
0055 --------------------------
0056 
0057 SGeoConfig::ELVSelection converts a comma delimited list of lv solid names optionally
0058 with special prefixes "t:" or "filepath:" into a comma delimited list of lvid integers.
0059 
0060 
0061 Examples using "filepath:" prefix to select volumes via solid names(aka meshnames) listed in files::
0062 
0063     GEOM cf
0064     cp meshname.txt /tmp/elv.txt
0065     vi /tmp/elv.txt
0066     ELV=filepath:/tmp/elv.txt MOI=sTarget:0:-1 ~/o/cx.sh
0067 
0068     grep Surftube meshname.txt > /tmp/elv.txt
0069     echo sTarget >> /tmp/elv.txt
0070     ELV=filepath:/tmp/elv.txt MOI=sTarget:0:-1 ~/o/cx.sh
0071 
0072 Note that the file is read via sstr::SplitTrimSuppress which will ignore lines in the file
0073 starting with "#", so another way::
0074 
0075     GEOM cf
0076     cp meshname.txt /tmp/elv.txt
0077     vi /tmp/elv.txt ## comment names to skip with "#"
0078     ELV=filepath:/tmp/elv.txt MOI=sTarget:0:-1 ~/o/cx.sh
0079 
0080     grep \# /tmp/elv.txt
0081     #HamamatsuR12860sMask_virtual
0082     #NNVTMCPPMTsMask_virtual
0083     #mask_PMT_20inch_vetosMask_virtual
0084 
0085 
0086 Examples using "t:" prefix to exclude volumes::
0087 
0088    ELV=t:HamamatsuR12860sMask_virtual,NNVTMCPPMTsMask_virtual MOI=NNVTMCPPMTsMask:0:-2  ~/o/cx.sh
0089 
0090    DISPLAY=:1 ELV=t:HamamatsuR12860sMask_virtual,NNVTMCPPMTsMask_virtual MOI=NNVTMCPPMTsMask:0:-2  ~/o/cx.sh
0091 
0092 
0093 
0094 **/
0095 
0096 const char* SGeoConfig::ELVSelection(const SName* id )
0097 {
0098     const char* elv_selection_ = ELVSelection() ;
0099     const char* elv = nullptr ;
0100     char delim = ',' ;
0101     bool VERBOSE = ssys::getenvbool(ELVSelection_VERBOSE);
0102 
0103     if(VERBOSE) std::cerr
0104         << "SGeoConfig::ELVSelection.0."
0105         << " [" << ELVSelection_VERBOSE << "] "
0106         << " elv_selection_ " << ( elv_selection_ ? elv_selection_ : "-" )
0107         << std::endl
0108         ;
0109 
0110 
0111 
0112     bool allow_missing_names = true ;
0113 
0114     if( elv_selection_ )
0115     {
0116         const char* prefix = ELVPrefix(elv_selection_);
0117 
0118         if(VERBOSE) std::cerr
0119             << "SGeoConfig::ELVSelection.1."
0120             << " prefix " << ( prefix ? prefix : "-" )
0121             << " strlen(prefix) " << ( prefix ? strlen(prefix) : 0 )
0122             << "\n"
0123             ;
0124 
0125 
0126         if( SName::Has_STARTING( elv_selection_))  // skip the hasNames check when using STARTING_
0127         {
0128             std::vector<std::string>* qq_missing = nullptr ;
0129             elv = id->getIDXListFromNames(elv_selection_, delim, prefix, qq_missing );
0130         }
0131         else
0132         {
0133             if( allow_missing_names )
0134             {
0135                 std::vector<std::string>* qq_missing = new std::vector<std::string> ;
0136                 elv = id->getIDXListFromNames(elv_selection_, delim, prefix, qq_missing );
0137 
0138                 int num_missing_names = qq_missing ? qq_missing->size() : -1 ;
0139 
0140                 if(VERBOSE) std::cerr
0141                     << "SGeoConfig::ELVSelection.5."
0142                     << " elv_selection_[" << elv_selection_ << "]"
0143                     << " allow_missing_names " << ( allow_missing_names ? "YES" : "NO " )
0144                     << " num_missing_names " << num_missing_names
0145                     << "\n"
0146                     ;
0147 
0148                  if(num_missing_names > 0) for(int i=0 ; i < num_missing_names ; i++ ) std::cerr
0149                     << "SGeoConfig::ELVSelection.6. missing_name[" << (*qq_missing)[i] << "]\n" ;
0150             }
0151             else
0152             {
0153                 std::vector<std::string>* qq_missing = nullptr ;
0154                 std::stringstream ss ;
0155                 bool has_names = id->hasNames(elv_selection_, delim, prefix, qq_missing, &ss );
0156 
0157                 if(VERBOSE) std::cerr
0158                     << "SGeoConfig::ELVSelection.2."
0159                     << " elv_selection_[" << elv_selection_ << "]"
0160                     << " has_names " << ( has_names ? "YES" : "NO " )
0161                     << " allow_missing_names " << ( allow_missing_names ? "YES" : "NO " )
0162                     << " qq_missing.size " << ( qq_missing ? qq_missing->size() : -1 )
0163                     << "\n"
0164                     ;
0165 
0166                 if(!has_names) std::cout
0167                     << "SGeoConfig::ELVSelection.3."
0168                     << " has_names " << ( has_names ? "YES" : "NO " ) << "\n"
0169                     << " qq_missing.size " << ( qq_missing ? qq_missing->size() : -1 )
0170                     << "[haslog[\n"
0171                     << ss.str()
0172                     << "]haslog[\n"
0173                     << "[id.detail\n"
0174                     << id->detail()
0175                     << "]id.detail\n"
0176                     ;
0177 
0178                 if(has_names)
0179                 {
0180                     elv = id->getIDXListFromNames(elv_selection_, delim, prefix, qq_missing );
0181                 }
0182                 else
0183                 {
0184                     elv = elv_selection_ ;  // eg when just numbers
0185                 }
0186             }
0187        }
0188     }
0189     return elv ;
0190 }
0191 
0192 
0193 /**
0194 SGeoConfig::ELVPrefix
0195 -----------------------
0196 
0197 As the above ELVSelection has a problem for solids
0198 with names starting with "t" are starting to transition
0199 to separate the modifier from the list of ints or solid names.
0200 
0201 DONE : adopt "t:" prefix for the tilde modifier.
0202 
0203 */
0204 
0205 const char* SGeoConfig::ELVPrefix(const char* elvarg)
0206 {
0207     if(elvarg == nullptr) return nullptr ;
0208     if(strlen(elvarg) == 0) return nullptr ;
0209 
0210     std::string prefix ;
0211     if(sstr::StartsWith(elvarg, "t:") || sstr::StartsWith(elvarg, "~:"))
0212     {
0213         prefix.assign("t:") ;
0214     }
0215     else if( sstr::StartsWith(elvarg,"t" ) || sstr::StartsWith(elvarg,"~") )
0216     {
0217         prefix.assign("t") ;
0218     }
0219     return prefix.empty() ? nullptr : strdup(prefix.c_str()) ;
0220 }
0221 
0222 
0223 
0224 /**
0225 SGeoConfig::ELVString (formerly CSGFoundry::ELVString)
0226 -------------------------------------------------------
0227 
0228 This is used from SGeoConfig::ELV to create the SBitSet of included/excluded LV.
0229 
0230 String configuring dynamic shape selection of form : t:110,117,134 or null when
0231 there is no selection.  The value is obtained from:
0232 
0233 * SGeoConfig::ELVSelection() which defaults to the OPTICKS_ELV_SELECTION envvar value
0234   and can be changed by the SGeoConfig::SetELVSelection static, a comma delimited list of
0235   mesh names is expected, for example:
0236   "NNVTMCPPMTsMask_virtual0x,HamamatsuR12860sMask_virtual0x,mask_PMT_20inch_vetosMask_virtual0x"
0237 
0238   If any of the names are not found in the geometry the selection request is ignored.
0239 
0240 **/
0241 
0242 const char* SGeoConfig::ELVString(const SName* id)
0243 {
0244     const char* elv = ELVSelection(id) ;
0245     LOG(LEVEL) << " elv " << ( elv ? elv : "-" ) ;
0246     return elv ;
0247 }
0248 
0249 
0250 /**
0251 SGeoConfig::ELV (formerly CSGFoundry::ELV)
0252 --------------------------------------------
0253 
0254 Sensitive to OPTICKS_ELV_SELECTION
0255 
0256 **/
0257 
0258 const SBitSet* SGeoConfig::ELV(const SName* id)
0259 {
0260     unsigned num_meshname = id->getNumName();
0261     const char* elv_ = ELVString(id);
0262     SBitSet* elv = elv_ ? SBitSet::Create(num_meshname, elv_ ) : nullptr ;
0263 
0264     LOG(LEVEL)
0265        << " num_meshname " << num_meshname
0266        << " elv_ " << ( elv_ ? elv_ : "-" )
0267        << " elv " << ( elv ? elv->desc() : "-" )
0268        ;
0269 
0270     return elv ;
0271 }
0272 
0273 
0274 
0275 
0276 
0277 
0278 
0279 
0280 
0281 
0282 
0283 std::string SGeoConfig::Desc()
0284 {
0285     std::stringstream ss ;
0286     ss << std::endl ;
0287     ss << std::setw(25) << kGEOM             << " : " << ( _GEOM   ? _GEOM   : "-" ) << std::endl ;
0288     ss << std::setw(25) << kEMM              << " : " << SBit::HexString(_EMM) << " 0x" << std::hex << _EMM << std::dec << std::endl ;
0289     ss << std::setw(25) << kELVSelection     << " : " << ( _ELVSelection   ? _ELVSelection   : "-" ) << std::endl ;
0290     ss << std::setw(25) << kSolidSelection   << " : " << ( _SolidSelection ? _SolidSelection : "-" ) << std::endl ;
0291     ss << std::setw(25) << kSolidTrimesh     << " : " << ( _SolidTrimesh   ? _SolidTrimesh   : "-" ) << std::endl ;
0292     ss << std::setw(25) << kFlightConfig     << " : " << ( _FlightConfig   ? _FlightConfig   : "-" ) << std::endl ;
0293     ss << std::setw(25) << kArglistPath      << " : " << ( _ArglistPath    ? _ArglistPath    : "-" ) << std::endl ;
0294     ss << std::setw(25) << kCXSkipLV         << " : " << CXSkipLV() << std::endl ;
0295     ss << std::setw(25) << kCXSkipLV_IDXList << " : " << CXSkipLV_IDXList() << std::endl ;
0296     std::string str = ss.str();
0297     return str ;
0298 }
0299 
0300 std::string SGeoConfig::desc() const
0301 {
0302     std::stringstream ss ;
0303     ss << "SGeoConfig::desc\n" ;
0304     std::string str = ss.str();
0305     return str ;
0306 }
0307 
0308 
0309 
0310 bool SGeoConfig::IsEnabledMergedMesh(unsigned mm) // static
0311 {
0312     bool emptylistdefault = true ;
0313     bool emm = true ;
0314     if(mm < 64)
0315     {
0316         std::bitset<64> bs(_EMM);
0317         emm = bs.count() == 0 ? emptylistdefault : bs[mm] ;
0318     }
0319     return emm ;
0320 }
0321 
0322 
0323 std::string SGeoConfig::DescEMM()
0324 {
0325     std::stringstream ss ;
0326     ss << "SGeoConfig::DescEMM " ;
0327     ss << std::setw(25) << kEMM              << " : " << SBit::HexString(_EMM) << " 0x" << std::hex << _EMM << std::dec << std::endl ;
0328 
0329     for(unsigned i=0 ; i < 64 ; i++)
0330     {
0331         bool emm = SGeoConfig::IsEnabledMergedMesh(i) ;
0332         if(emm) ss << i << " " ;
0333     }
0334     std::string s = ss.str();
0335     return s ;
0336 }
0337 
0338 
0339 std::vector<std::string>*  SGeoConfig::Arglist()
0340 {
0341     return sstr::LoadList( _ArglistPath, '\n' );
0342 }
0343 
0344 
0345 
0346 /**
0347 SGeoConfig::CXSkipLV_IDXList
0348 -----------------------------
0349 
0350 Translates names in a comma delimited list into indices according to SName.
0351 
0352 **/
0353 void SGeoConfig::SetCXSkipLV_IDXList(const SName* id)
0354 {
0355     const char* cxskiplv_ = CXSkipLV() ;
0356     bool has_names = cxskiplv_ ? id->hasNames(cxskiplv_ ) : false ;
0357     _CXSkipLV_IDXList = has_names ? id->getIDXListFromNames(cxskiplv_, ',' ) : nullptr ;
0358 }
0359 
0360 /**
0361 SGeoConfig::IsCXSkipLV
0362 ------------------------
0363 
0364 This controls mesh/solid skipping during GGeo to CSGFoundry
0365 translation as this is called from:
0366 
0367 1. CSG_GGeo_Convert::CountSolidPrim
0368 2. CSG_GGeo_Convert::convertSolid
0369 
0370 For any skips to be applied the below SGeoConfig::GeometrySpecificSetup
0371 must have been called.
0372 
0373 For example this is used for long term skipping of Water///Water
0374 virtual solids that are only there for Geant4 performance reasons,
0375 and do nothing useful for Opticks.
0376 
0377 Note that ELVSelection does something similar to this, but
0378 that is applied at every CSGFoundry::Load providing dynamic prim selection.
0379 As maintaining consistency between results and geometry is problematic
0380 with dynamic prim selection it is best to only use the dynamic approach
0381 for geometry render scanning to find bottlenecks.
0382 
0383 When creating longer lived geometry for analysis with multiple executables
0384 it is more appropriate to use CXSkipLV to effect skipping at translation.
0385 
0386 **/
0387 
0388 bool SGeoConfig::IsCXSkipLV(int lv) // static
0389 {
0390     if( _CXSkipLV_IDXList == nullptr ) return false ;
0391     std::vector<int> cxskip ;
0392     sstr::split<int>(cxskip, _CXSkipLV_IDXList, ',' );
0393 
0394     return std::count( cxskip.begin(), cxskip.end(), lv ) == 1 ;
0395 }
0396 
0397 
0398 /**
0399 SGeoConfig::GeometrySpecificSetup
0400 -----------------------------------
0401 
0402 TODO: compare GPU performance with and without these virtual skips
0403 
0404 This is invoked from:
0405 
0406 1. [DEAD CODE LOCATION] CSG_GGeo_Convert::init prior to GGeo to CSGFoundry translation
0407 2. [NOT MAINLINE CODE, ONLY TESTING/VIZ] argumentless CSGFoundry::Load
0408 
0409 The SName* id argument passes the meshnames (aka solid names)
0410 allowing detection of where a geometry appears to be JUNO by
0411 the presence of a collection of solid names within it.
0412 If JUNO is detected some JUNO specific static method calls are made.
0413 This avoids repeating these settings in tests or fiddling
0414 with envvars to configure these things.
0415 
0416 Previously did something similar using metadata in geocache
0417 or from the Opticks setup code within detector specific code.
0418 However do not want to require writing cache and prefer to minimize
0419 detector specific Opticks setup code as it is much easier
0420 to test in isolation than as an appendage to a detector framework.
0421 
0422 **AVOID "0x" SUFFIXES IN DECISION STRINGS**
0423 
0424 The "0x" address suffix on names are only present when running
0425 from a Geant4 geometry that was booted from GDML (assuming sane naming).
0426 Hence including "0x" in decision strings such as skip lists would
0427 cause different geometry when running from GDML and when running
0428 live which is to be avoided.
0429 
0430 **/
0431 void SGeoConfig::GeometrySpecificSetup(const SName* id)  // static
0432 {
0433     bool _JUNO_Detected = JUNO_Detected(id) ;
0434     LOG(LEVEL) << " _JUNO_Detected " << _JUNO_Detected ;
0435     if(_JUNO_Detected)
0436     {
0437         //const char* skips = "NNVTMCPPMTsMask_virtual,HamamatsuR12860sMask_virtual,mask_PMT_20inch_vetosMask_virtual" ;
0438         const char* skips = nullptr ;
0439 
0440         SetCXSkipLV(skips);
0441         SetCXSkipLV_IDXList(id);
0442 
0443         // USING dynamic ELVSelection here would be inappropriate : as dynamic selection
0444         // means the persisted geometry does not match the used geometry.
0445     }
0446 }
0447 
0448 bool SGeoConfig::JUNO_Detected(const SName* id)
0449 {
0450     const char* JUNO_names = "HamamatsuR12860sMask,HamamatsuR12860_PMT_20inch,NNVTMCPPMT_PMT_20inch" ;
0451     bool with_JUNO_names = id ? id->hasNames(JUNO_names) : false ;
0452     return with_JUNO_names ;
0453 }
0454 
0455