Back to home page

EIC code displayed by LXR

 
 

    


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

0001 
0002 #include <limits>
0003 #include <array>
0004 #include <csignal>
0005 
0006 #include "scuda.h"
0007 #include "squad.h"
0008 #include "squadx.h"
0009 #include "sstamp.h"
0010 #include "sprof.h"
0011 
0012 #include "sphoton.h"
0013 #include "sphotonlite.h"
0014 
0015 #include "srec.h"
0016 #include "sseq.h"
0017 #include "ssys.h"
0018 #include "sstate.h"
0019 #include "stag.h"
0020 #include "sevent.h"
0021 #include "sctx.h"
0022 #include "sdebug.h"
0023 #include "stran.h"
0024 #include "stree.h"
0025 #include "strid.h"
0026 #include "stimer.h"
0027 #include "spath.h"
0028 #include "sdirectory.h"
0029 #include "sstr.h"
0030 #include "ssys.h"
0031 #include "SLOG.hh"
0032 
0033 #include "NP.hh"
0034 #include "NPX.h"
0035 #include "NPFold.h"
0036 #include "SGeo.hh"
0037 #include "SEvt.hh"
0038 #include "SEvent.hh"
0039 #include "SSim.hh"
0040 #include "SEventConfig.hh"
0041 #include "SFrameGenstep.hh"
0042 #include "OpticksGenstep.h"
0043 #include "OpticksPhoton.h"
0044 #include "OpticksPhoton.hh"
0045 #include "SComp.h"
0046 #include "SProf.hh"
0047 #include "SRecord.h"
0048 
0049 
0050 bool SEvt::NPFOLD_VERBOSE = ssys::getenvbool(SEvt__NPFOLD_VERBOSE) ;
0051 bool SEvt::GATHER = ssys::getenvbool(SEvt__GATHER) ;
0052 bool SEvt::SAVE = ssys::getenvbool(SEvt__SAVE) ;
0053 bool SEvt::INDEX = ssys::getenvbool(SEvt__INDEX) ;
0054 bool SEvt::LIFECYCLE = ssys::getenvbool(SEvt__LIFECYCLE) ;
0055 bool SEvt::FRAME = ssys::getenvbool(SEvt__FRAME) ;
0056 bool SEvt::MINIMAL = ssys::getenvbool(SEvt__MINIMAL) ;
0057 bool SEvt::MINTIME = ssys::getenvbool(SEvt__MINTIME) ;
0058 bool SEvt::DIRECTORY = ssys::getenvbool(SEvt__DIRECTORY) ;
0059 bool SEvt::CLEAR_SIGINT = ssys::getenvbool(SEvt__CLEAR_SIGINT) ;
0060 bool SEvt::SIMTRACE = ssys::getenvbool(SEvt__SIMTRACE) ;
0061 bool SEvt::EPH_ = ssys::getenvbool(SEvt__EPH) ;
0062 bool SEvt::RUNMETA = ssys::getenvbool(SEvt__RUNMETA) ;
0063 
0064 bool SEvt::SAVE_NOTHING = ssys::getenvbool(SEvt__SAVE_NOTHING);
0065 bool SEvt::SAVE_RUNDIR = ssys::getenvbool(SEvt__SAVE_RUNDIR);
0066 
0067 
0068 const char* SEvt::descStage() const
0069 {
0070     const char* st = nullptr ;
0071     switch(stage)
0072     {
0073         case SEvt__SEvt:         st = SEvt__SEvt_         ; break ;
0074         case SEvt__init:         st = SEvt__init_         ; break ;
0075         case SEvt__beginOfEvent: st = SEvt__beginOfEvent_ ; break ;
0076         case SEvt__endOfEvent:   st = SEvt__endOfEvent_   ; break ;
0077         case SEvt__gather:       st = SEvt__gather_       ; break ;
0078         default:                 st = SEvt__OTHER_        ; break ;
0079     }
0080     return st ;
0081 }
0082 
0083 bool SEvt::IsDefined(unsigned val){ return val != UNDEF ; }
0084 
0085 stimer* SEvt::TIMER = new stimer ;
0086 void SEvt::TimerStart(){ TIMER->start(); }
0087 double SEvt::TimerDone(){ return TIMER->done() ; }
0088 uint64_t SEvt::TimerStartCount(){ return TIMER->start_count() ; }
0089 std::string SEvt::TimerDesc(){ return TIMER->desc() ; }
0090 
0091 
0092 /**
0093 SEvt::Init_RUN_META
0094 ---------------------
0095 
0096 As this is a static it happens just as libSysRap is loaded,
0097 very soon after starting the executable.
0098 
0099 Now using SProf for profile stamps, previously included with run_meta.txt::
0100 
0101    run_meta->set_meta<std::string>("SEvt__Init_RUN_META", sprof::Now() );
0102 
0103 **/
0104 
0105 
0106 NP* SEvt::Init_RUN_META() // static
0107 {
0108     NP* run_meta = NP::Make<float>(1);
0109     SProf::Add("SEvt__Init_RUN_META");
0110     return run_meta ;
0111 }
0112 
0113 NP* SEvt::RUN_META = Init_RUN_META() ;
0114 
0115 std::string* SEvt::RunMetaString() // static
0116 {
0117     return RUN_META ? &(RUN_META->meta) : nullptr ;
0118 }
0119 
0120 
0121 NP* SEvt::UU = nullptr ;
0122 NP* SEvt::UU_BURN = nullptr ; // NP::Load("$SEvt__UU_BURN") ;
0123 
0124 const plog::Severity SEvt::LEVEL = SLOG::EnvLevel("SEvt", "DEBUG");
0125 const int SEvt::GIDX = ssys::getenvint("GIDX",-1) ;
0126 const int SEvt::PIDX = ssys::getenvint("PIDX",-1) ;
0127 const int SEvt::MISSING_INDEX = std::numeric_limits<int>::max() ;
0128 const int SEvt::MISSING_INSTANCE = std::numeric_limits<int>::max() ;
0129 const int SEvt::DEBUG_CLEAR = ssys::getenvint("SEvt__DEBUG_CLEAR",-1) ;
0130 
0131 
0132 
0133 std::array<SEvt*, SEvt::MAX_INSTANCE> SEvt::INSTANCES = {{ nullptr, nullptr }} ;
0134 
0135 std::string SEvt::DescINSTANCE()  // static
0136 {
0137     std::stringstream ss ;
0138 
0139     ss << "SEvt::DescINSTANCE"
0140        << " Count() " << Count()
0141        << std::endl
0142        << " Exists(0) " << ( Exists(0) ? "YES" : "NO " )
0143        << std::endl
0144        << " Exists(1) " << ( Exists(1) ? "YES" : "NO " )
0145        << std::endl
0146        ;
0147     std::string str = ss.str();
0148     return str ;
0149 }
0150 
0151 
0152 
0153 void SEvt::setStage(int stage_)
0154 {
0155     stage = stage_ ;
0156 }
0157 int SEvt::getStage() const
0158 {
0159     return stage ;
0160 }
0161 
0162 
0163 /**
0164 SEvt::SEvt
0165 -----------
0166 
0167 Instanciation invokes SEventConfig::Initialize()
0168 
0169 The config used depends on:
0170 
0171 1. envvars such as OPTICKS_EVENT_MODE that can change default config values
0172 2. static SEventConfig method calls done before SEvt instanciation
0173    that change the default config values
0174 3. available VRAM as detected by (scontext)SEventConfig::CONTEXT
0175 
0176 **/
0177 
0178 SEvt::SEvt()
0179     :
0180     cfgrc(SEventConfig::Initialize()),
0181     index(MISSING_INDEX),
0182     instance(MISSING_INSTANCE),
0183     stage(SEvt__SEvt),
0184     gather_metadata_notopfold(0),
0185     t_BeginOfEvent(0),
0186 #ifndef PRODUCTION
0187     t_setGenstep_0(0),
0188     t_setGenstep_1(0),
0189     t_setGenstep_2(0),
0190     t_setGenstep_3(0),
0191     t_setGenstep_4(0),
0192     t_setGenstep_5(0),
0193     t_setGenstep_6(0),
0194     t_setGenstep_7(0),
0195     t_setGenstep_8(0),
0196 #endif
0197     t_PreLaunch(0),
0198     t_PostLaunch(0),
0199     t_EndOfEvent(0),
0200     t_PenultimatePoint(0),
0201     t_LastPoint(0),
0202     t_Launch(-2.),
0203     photon_selector(new sphoton_selector(SEventConfig::HitMask())),
0204     photonlite_selector(new sphotonlite_selector(SEventConfig::HitMask())),
0205     evt(new sevent),
0206     dbg(new sdebug),
0207     input_genstep(nullptr),
0208     input_photon(nullptr),
0209     input_photon_transformed(nullptr),
0210     g4state(nullptr),
0211     random(nullptr),
0212     random_array(nullptr),
0213     provider(this),   // overridden with SEvt::setCompProvider for device running from QEvt::init
0214     topfold(new NPFold),
0215     fold(nullptr),
0216     extrafold(new NPFold),
0217     cf(nullptr),
0218     hostside_running_resize_done(false),
0219     gather_done(false),
0220     is_loaded(false),
0221     is_loadfail(false),
0222     numgenstep_collected(0),   // updated by addGenstep
0223     numphoton_collected(0),   // updated by addGenstep
0224     numphoton_genstep_max(0),
0225     clear_genstep_vector_count(0),
0226     clear_output_vector_count(0),
0227     gather_total(0),
0228     genstep_total(0),
0229     photon_total(0),
0230     hit_total(0),
0231     addGenstep_array(0)
0232 {
0233     init();
0234 }
0235 
0236 /**
0237 SEvt::init
0238 -----------
0239 
0240 Only configures array maxima, no allocation yet.
0241 Device side allocation happens in QEvt::setGenstep QEvt::setNumPhoton
0242 
0243 Initially SEvt is set as its own SCompProvider,
0244 allowing U4RecorderTest/SEvt::save to gather the component
0245 arrays provided from SEvt.
0246 
0247 For device running the SCompProvider is overridden to
0248 become QEvt allowing SEvt::save to persist the
0249 components gatherered from device buffers.
0250 
0251 **/
0252 
0253 void SEvt::init()
0254 {
0255     if(NPFOLD_VERBOSE) topfold->set_verbose();
0256 
0257     setStage(SEvt__init);
0258 
0259     LOG_IF(info, LIFECYCLE) << id() ;
0260 
0261     LOG(LEVEL) << "[" ;
0262     //INSTANCE = this ;    // no-longer automated, rely on static creation methods to set INSTANCES[0] or [1]
0263 
0264     evt->init();    // array maxima set according to SEventConfig values
0265     dbg->zero();
0266 
0267     LOG(LEVEL) << evt->desc() ; // mostly zeros at this juncture
0268 
0269     SEventConfig::GatherCompList(gather_comp);  // populate gather_comp vector based on GatherCompMask
0270     SEventConfig::SaveCompList(save_comp);      // populate save_comp vector based on SaveCompMask
0271 
0272 
0273     LOG(LEVEL) << " SEventConfig::DescGatherComp "  << SEventConfig::DescGatherComp() ;
0274     LOG(LEVEL) << " SEventConfig::DescSaveComp "    << SEventConfig::DescSaveComp() ;
0275     LOG(LEVEL) << descComp() ;
0276 
0277     //initInputGenstep();   // for per-event genstep moved to SEvt::beginOfEvent/SEvt::addInputGenstep
0278 
0279 
0280 
0281     initInputPhoton();
0282 
0283     initG4State();        // HMM: G4State not an every-event thing ? first event only ?
0284     LOG(LEVEL) << "]" ;
0285 }
0286 
0287 void SEvt::setFoldVerbose(bool v)
0288 {
0289     topfold->set_verbose(v);
0290     if(fold) fold->set_verbose(v);
0291 }
0292 
0293 
0294 const char* SEvt::GetSaveDir(int idx) // static
0295 {
0296     return Exists(idx) ? Get(idx)->getSaveDir() : nullptr ;
0297 }
0298 const char* SEvt::getSaveDir() const { return topfold->savedir ; }
0299 const char* SEvt::getLoadDir() const { return topfold->loaddir ; }
0300 int SEvt::getTotalItems() const { return topfold->total_items() ; }
0301 
0302 /**
0303 SEvt::getSearchCFbase
0304 ----------------------
0305 
0306 Search for CFBase geometry folder corresponding to event arrays based on
0307 the loaded/saved SEvt directories eg with::
0308 
0309     spath::SearchDirUpTreeWithFile(dir, "CSGFoundry/solid.npy")
0310 
0311 As the start dirs tried are loaddir and savedir this has no possibility of working prior
0312 to a load or save having been done. As this is typically used for loaded SEvt
0313 this is not much of a limitation.
0314 
0315 For this to succeed to find the geometry requires event arrays are saved
0316 into folders with suitable proximity to the corresponding geometry folders.
0317 
0318 For example use this to load an event and associate it with a geometry,
0319 allowing dumping or access to the photons in any frame::
0320 
0321     SEvt* sev = SEvt::Load() ;
0322     const char* cfbase = sev->getSearchCFBase() ;
0323     const CSGFoundry* fd = CSGFoundry::Load(cfbase);
0324     sev->setGeo(fd);
0325     sev->setFrame(39216);
0326     std::cout << sev->descFull() ;
0327 
0328 See CSG/tests/CSGFoundry_SGeo_SEvt_Test.sh
0329 
0330 **/
0331 
0332 const char* SEvt::getSearchCFBase() const
0333 {
0334     const char* loaddir = getLoadDir();
0335     const char* savedir = getSaveDir();
0336     const char* cfbase = nullptr ;
0337     if(cfbase == nullptr && loaddir) cfbase = spath::SearchDirUpTreeWithFile(loaddir, SearchCFBase_RELF );
0338     if(cfbase == nullptr && savedir) cfbase = spath::SearchDirUpTreeWithFile(savedir, SearchCFBase_RELF) ;
0339     return cfbase ;
0340 }
0341 
0342 
0343 const char* SEvt::INPUT_GENSTEP_DIR = spath::Resolve("${SEvt__INPUT_GENSTEP_DIR:-$HOME/.opticks/InputGensteps}") ;
0344 const char* SEvt::INPUT_PHOTON_DIR = spath::Resolve("${SEvt__INPUT_PHOTON_DIR:-$HOME/.opticks/InputPhotons}") ;
0345 
0346 
0347 /**
0348 SEvt::ResolveInputArray
0349 ------------------------
0350 
0351 When *spec* starts with a letter AZaz it is assumed to be a relative path within
0352 the SEvt__INPUT_GENSTEP_DIR which defaults to $HOME/.opticks/InputGensteps.
0353 
0354 When *spec* starts with a non-letter such as "$" or "/" it is assumed
0355 to specify an absolute path and the *dir* argument is ignored.
0356 
0357 **/
0358 
0359 const char* SEvt::ResolveInputArray(const char* spec, const char* dir) // static
0360 {
0361     assert(strlen(spec) > 0 && sstr::EndsWith(spec, ".npy") );
0362     const char* path = sstr::StartsWithLetterAZaz(spec) ?  spath::Resolve(dir, spec) : spath::Resolve( spec ) ;
0363     return path ;
0364 }
0365 
0366 NP* SEvt::LoadInputArray(const char* path) // static
0367 {
0368     NP* a = NP::Load(path);
0369     LOG_IF(fatal, a == nullptr) << " FAILED to load input array from path " << path ;
0370 
0371     LOG(LEVEL)
0372         << " path " << ( path ? path : "-" )
0373         << " a.sstr " << ( a ? a->sstr() : "-" )
0374         ;
0375 
0376     assert( a ) ;
0377     assert( a->has_shape(-1,4,4) || a->has_shape(-1,6,4));
0378     assert( a->shape[0] > 0 );
0379 
0380     return a ;
0381 }
0382 
0383 
0384 
0385 /**
0386 SEvt::LoadInputGenstep_array
0387 -----------------------------
0388 
0389 ::
0390 
0391     export OPTICKS_INPUT_GENSTEP=$BASE/jok-tds/ALL0/p001/genstep.npy
0392     ##export OPTICKS_INPUT_GENSTEP=$BASE/jok-tds/ALL0
0393 
0394 
0395 HMM: when using a sequence of InputGenstep files need to
0396 do this for every event, invoking with an idx argument ..
0397 so this cannot be static
0398 
0399 **/
0400 
0401 
0402 NP* SEvt::LoadInputGenstep_array(int idx) // static
0403 {
0404     const char* spec = SEventConfig::InputGenstep(idx);
0405     return spec ? LoadInputGenstep_array(spec) : nullptr ;
0406 }
0407 NP* SEvt::LoadInputGenstep_array(const char* spec) // static
0408 {
0409     const char* path = ResolveInputArray( spec, INPUT_GENSTEP_DIR );
0410     NP* a = LoadInputArray(path);
0411     assert( a->has_shape(-1,6,4) );
0412     return a ;
0413 }
0414 
0415 
0416 /**
0417 SEvt::LoadInputPhoton
0418 ----------------------
0419 
0420 This is invoked by SEvt::initInputPhoton which is invoked by SEvt::init at instanciation.
0421 
0422 Resolving the input string to a path is done in one of two ways:
0423 
0424 1. if the string starts with a letter A-Za-z eg "inphoton.npy" or "RandomSpherical10.npy"
0425    it is assumed to be the name of a .npy file within the default SEvt__INPUT_PHOTON_DIR
0426    of $HOME/.opticks/InputPhotons.
0427 
0428    Create such files with ana/input_photons.py
0429 
0430 2. if the string does not start with a letter eg /path/to/some/dir/file.npy or $TOKEN/path/to/file.npy
0431    it is passed unchanged to  spath::Resolve
0432 
0433 **/
0434 
0435 NP* SEvt::LoadInputPhoton() // static
0436 {
0437     const char* spec = SEventConfig::InputPhoton();
0438     return spec ? LoadInputPhoton(spec) : nullptr ;
0439 }
0440 NP* SEvt::LoadInputPhoton(const char* spec)
0441 {
0442     bool is_ipr = IsInputPhotonRecord(spec);
0443     NP* a = nullptr ;
0444     if(!is_ipr)
0445     {
0446         a = LoadInputPhoton_photon(spec);
0447     }
0448     else
0449     {
0450         a = LoadInputPhoton_record(spec);
0451     }
0452     return a ;
0453 }
0454 
0455 bool SEvt::IsInputPhotonRecord( const char* spec )
0456 {
0457     const char* path = spath::Resolve(spec);
0458     bool is_record = sstr::EndsWith(path, "record.npy" );
0459     return is_record ;
0460 }
0461 
0462 NP* SEvt::LoadInputPhoton_photon(const char* spec)
0463 {
0464     bool is_simtrace = !SFrameGenstep::HasConfigEnv() && SEventConfig::IsRGModeSimtrace() ;
0465     LOG_IF(fatal, is_simtrace)
0466         << " simtrace with ordinary input photons is not supported "
0467         ;
0468     assert(!is_simtrace);
0469     if(is_simtrace) std::raise(SIGINT);
0470 
0471     const char* path = ResolveInputArray( spec, INPUT_PHOTON_DIR );
0472     NP* a = LoadInputArray(path);
0473     assert( a->has_shape(-1,4,4) );
0474 
0475     float t0 = SEventConfig::InputPhotonChangeTime();
0476     bool change_time = t0 >= 0.f ;
0477 
0478     LOG_IF(fatal, change_time)
0479         << " SEventConfig::InputPhotonChangeTime " << t0
0480         << " change_time " << ( change_time ? "YES" : "NO " )
0481         ;
0482 
0483     if(change_time) sphoton::ChangeTimeInsitu(a, t0);
0484     return a ;
0485 }
0486 
0487 /**
0488 SEvt::LoadInputPhoton_record
0489 ------------------------------
0490 
0491 This is is invoked when OPTICKS_INPUT_PHOTON
0492 resolves to a path ending with record.npy
0493 
0494 Creates input photon array by interpolation of
0495 step point positions from record array at
0496 simulation time given by OPTICKS_INPUT_PHOTON_RECORD_TIME
0497 (in ns).
0498 
0499 **/
0500 
0501 
0502 NP* SEvt::LoadInputPhoton_record(const char* spec)
0503 {
0504     bool is_simtrace = !SFrameGenstep::HasConfigEnv() && SEventConfig::IsRGModeSimtrace() ;
0505 
0506     const char* spec2 = SEventConfig::InputPhoton();
0507     const char* slice = SEventConfig::InputPhotonRecordSlice();
0508     const char* iprt = SEventConfig::InputPhotonRecordTime();
0509 
0510     assert( spec && spec2 && strcmp( spec2, spec) == 0 );
0511 
0512     const char* path = spath::Resolve(spec);
0513     bool is_record = sstr::EndsWith(path, "record.npy" );
0514     assert( is_record );
0515 
0516     const char* fold = spath::Dirname(path);
0517 
0518     SRecord* sr = SRecord::Load(fold, slice );
0519     NP* a = is_simtrace ? sr->getSimtraceAtTime(iprt) : sr->getPhotonAtTime(iprt) ;
0520 
0521     std::cout
0522         << "SEvt::LoadInputPhoton_record"
0523         << " is_simtrace " << ( is_simtrace ? "YES" : "NO " )
0524         << " spec [" << ( spec ? spec : "-" ) << "]\n"
0525         << " path [" << ( path ? path : "-" ) << "]\n"
0526         << " fold [" << ( fold ? fold : "-" ) << "]\n"
0527         << " slice [" << ( slice ? slice : "-" ) << "]\n"
0528         << " iprt [" << ( iprt ? iprt : "-" ) << "]\n"
0529         << " is_record " << ( is_record ? "YES" : "NO " ) << "\n"
0530         << " a " << ( a ? a->sstr() : "-" ) << "\n"
0531         ;
0532 
0533     return a ;
0534 }
0535 
0536 
0537 
0538 /**
0539 SEvt::initInputGenstep_array
0540 -----------------------------
0541 
0542 Invoked from SEvt::beginOfEvent after SEvt::setIndex
0543 Formerly invoked once only from SEvt::init
0544 
0545 **/
0546 
0547 void SEvt::initInputGenstep_array()
0548 {
0549     NP* ig = LoadInputGenstep_array(index) ;
0550     setInputGenstep_array(ig);
0551 }
0552 void SEvt::setInputGenstep_array(NP* ig)
0553 {
0554     if(ig == nullptr) return ;
0555     input_genstep = ig ;
0556     assert( input_genstep->has_shape(-1,6,4) );
0557     int numgenstep = input_genstep->shape[0] ;
0558     bool numgenstep_expect = numgenstep > 0 ;
0559     if(!numgenstep_expect) std::raise(SIGINT) ;
0560     assert( numgenstep_expect );
0561 }
0562 
0563 
0564 NP* SEvt::getInputGenstep_array() const { return input_genstep ; }
0565 bool SEvt::hasInputGenstep_array() const { return input_genstep != nullptr ; }
0566 bool SEvt::hasInputGenstep_arrayPath() const { return SEventConfig::InputGenstepPathExists(index) ; }
0567 
0568 
0569 
0570 /**
0571 SEvt::initInputPhoton
0572 -----------------------
0573 
0574 This is invoked by SEvt::init on instanciating the SEvt instance
0575 The default "SEventConfig::InputPhoton()" is nullptr meaning no input photons.
0576 This can be changed by setting an envvar in the script that runs the executable, eg::
0577 
0578    export OPTICKS_INPUT_PHOTON=CubeCorners.npy
0579    export OPTICKS_INPUT_PHOTON=$HOME/somedir/path/to/inphoton.npy
0580 
0581 Or within the code of the executable, typically in the main prior to SEvt instanciation,
0582 using eg::
0583 
0584    SEventConfig::SetInputPhoton("CubeCorners.npy")
0585    SEventConfig::SetInputPhoton("$HOME/somedir/path/to/inphoton.npy")
0586 
0587 When non-null it is resolved into a path and the array loaded at SEvt instanciation
0588 by SEvt::LoadInputPhoton
0589 
0590 **/
0591 
0592 void SEvt::initInputPhoton()
0593 {
0594     bool is_CEGS_simtrace = SFrameGenstep::HasConfigEnv() && SEventConfig::IsRGModeSimtrace() ;
0595     if(is_CEGS_simtrace) return ;  // skip input photon setup when in simtrace mode with CEGS envvar
0596 
0597     NP* ip = LoadInputPhoton() ;
0598     setInputPhoton(ip);
0599 }
0600 
0601 
0602 void SEvt::setInputPhoton(NP* p)
0603 {
0604     if(p == nullptr) return ;
0605     input_photon = p ;
0606     bool input_photon_expect = input_photon->has_shape(-1,4,4) ;
0607     if(!input_photon_expect) std::raise(SIGINT) ;
0608     assert( input_photon_expect );
0609 
0610     int numphoton = input_photon->shape[0] ;
0611     bool numphoton_expect = numphoton > 0 ;
0612     if(!numphoton_expect) std::raise(SIGINT) ;
0613     assert( numphoton_expect  );
0614 }
0615 
0616 
0617 
0618 
0619 
0620 
0621 /**
0622 SEvt::getInputPhoton_
0623 ----------------------
0624 
0625 This variant always provides the untransformed input photons.
0626 That will be nullptr unless OPTICKS_INPUT_PHOTON is defined.
0627 
0628 **/
0629 NP* SEvt::getInputPhoton_() const { return input_photon ; }
0630 bool SEvt::hasInputPhoton() const { return input_photon != nullptr ; }
0631 
0632 
0633 /**
0634 SEvt::getInputPhoton
0635 ---------------------
0636 
0637 Returns the transformed input photon if present.
0638 For the transformed photons to  be present it is necessary to have called SEvt::setFrame
0639 That is done from on high by G4CXOpticks::setupFrame which gets invoked by G4CXOpticks::setGeometry
0640 
0641 The frame and corresponding transform used can be controlled by several envvars,
0642 see CSGFoundry::getFrameE. Possible envvars include:
0643 
0644 +------------------------------+----------------------------+
0645 | envvar                       | Examples                   |
0646 +==============================+============================+
0647 | INST                         |                            |
0648 +------------------------------+----------------------------+
0649 | MOI                          | Hama:0:1000 NNVT:0:1000    |
0650 +------------------------------+----------------------------+
0651 | OPTICKS_INPUT_PHOTON_FRAME   |                            |
0652 +------------------------------+----------------------------+
0653 
0654 
0655 **/
0656 NP* SEvt::getInputPhoton() const {  return input_photon_transformed ? input_photon_transformed : input_photon  ; }
0657 bool SEvt::hasInputPhotonTransformed() const { return input_photon_transformed != nullptr ; }
0658 
0659 
0660 
0661 NP* SEvt::gatherInputGenstep() const
0662 {
0663     NP* ig = getInputGenstep_array();
0664     NP* ign = nullptr ;
0665     if(ig)
0666     {
0667         ign = ig->ebyte == 8 ? NP::MakeNarrow(ig) : ig->copy() ;
0668     }
0669     return ign ;
0670 }
0671 
0672 
0673 
0674 /**
0675 SEvt::gatherInputPhoton
0676 -------------------------
0677 
0678 To avoid issues with inphoton and saving 2nd events,
0679 treat the inphoton more like other arrays by having a distinct
0680 narrowed inphoton copy for each event.
0681 
0682 HMM: unlike other gathered component this is not being
0683 added to the fold, so will not be cleared ?
0684 
0685 TODO : switch to narrowing at initialization, so can avoid the gather
0686 
0687 
0688 **/
0689 
0690 NP* SEvt::gatherInputPhoton() const
0691 {
0692     NP* ip = getInputPhoton();
0693     NP* ipn = nullptr ;
0694     if(ip)
0695     {
0696         ipn = ip->ebyte == 8 ? NP::MakeNarrow(ip) : ip->copy() ;
0697     }
0698     return ipn ;
0699 }
0700 
0701 
0702 /**
0703 SEvt::initG4State
0704 -------------------
0705 
0706 Called by SEvt::init but does nothing unless SEventConfig::IsRunningModeG4StateSave()
0707 
0708 HMM: is this the right place ? It depends on the RunningMode.
0709 
0710 **/
0711 
0712 void SEvt::initG4State()
0713 {
0714     if(SEventConfig::IsRunningModeG4StateSave())
0715     {
0716         LOG(LEVEL) << "SEventConfig::IsRunningModeG4StateSave creating g4state array " ;
0717         NP* state = makeG4State();
0718         setG4State(state);
0719     }
0720 }
0721 
0722 /**
0723 SEvt::makeG4State
0724 ---------------------
0725 
0726 Not invoked by default.
0727 
0728 See U4Recorder::PreUserTrackingAction_Optical
0729 
0730 * HMM: thats a bit spagetti, config control ?
0731 
0732 Item values of 2*17+4=38 is appropriate for the default Geant4 10.4.2 random engine: MixMaxRng
0733 See::
0734 
0735      g4-cls MixMaxRng
0736      g4-cls RandomEngine
0737      g4-cls Randomize
0738 
0739 
0740 TODO: half the size with "unsigned" not "unsigned long",
0741 to workaround wasteful CLHEP API which uses "unsigned long" but only needs 32 bit elements
0742 
0743 **/
0744 
0745 NP* SEvt::makeG4State() const
0746 {
0747      const char* spec = SEventConfig::G4StateSpec() ; // default spec 1000:38
0748 
0749      std::vector<int> elem ;
0750      sstr::split<int>(elem, spec, ':');
0751      assert( elem.size() == 2 );
0752 
0753      int max_states = elem[0] ;
0754      int item_values = elem[1] ;
0755 
0756      NP* a =  NP::Make<unsigned long>(max_states, item_values );
0757 
0758 
0759      LOG(info)
0760          << " SEventConfig::G4StateSpec() " << spec
0761          << " max_states " << max_states
0762          << " item_values " << item_values
0763          << " a.sstr " << a->sstr()
0764          ;
0765      return a ;
0766 }
0767 
0768 void SEvt::setG4State( NP* state) { g4state = state ; }
0769 NP* SEvt::gatherG4State() const {  return g4state ; } // gather is used prior to persisting, get is used after loading
0770 const NP* SEvt::getG4State() const {  return topfold->get(SComp::Name(SCOMP_G4STATE)) ; }
0771 
0772 
0773 
0774 /**
0775 SEvt::setFrame
0776 ------------------
0777 
0778 As it is necessary to have the geometry to provide the frame this
0779 is now split from eg initInputPhotons.
0780 
0781 **simtrace running**
0782     MakeCenterExtentGensteps based on the given frame.
0783 
0784 **simulate inputphoton running**
0785     MakeInputPhotonGenstep and m2w (model-2-world)
0786     transforms the photons using the frame transform
0787 
0788 Formerly(?) for simtrace and input photon running with or without a transform
0789 it was necessary to call this for every event due to the former call to addInputGenstep,
0790 but now that the genstep setup is moved to SEvt::beginOfEvent it is only needed
0791 to call this for each frame, usually once only.
0792 
0793 
0794 simtrace stack::
0795 
0796     SEvt::setFrame
0797     SEvt::SetFrame
0798     CSGFoundry::AfterLoadOrCreate
0799     CSGFoundry::Load
0800     CSGOptiX::SimtraceMain
0801     main
0802 
0803 **/
0804 
0805 
0806 #ifdef WITH_OLD_FRAME
0807 void SEvt::setFrame(const sframe& fr )
0808 {
0809     const char* name = fr.get_name() ;
0810     LOG_IF(info, FRAME)
0811         << " [" << SEvt__FRAME << "]"
0812         << " fr.get_name " << ( name ? name : "-" ) << "\n"
0813         << " fr.desc\n"
0814         << fr.desc()
0815         ;
0816     frame = fr ;
0817     transformInputPhoton();
0818 }
0819 #else
0820 void SEvt::setFr(const sfr& _fr )
0821 {
0822     fr = _fr ;
0823     transformInputPhoton();
0824 }
0825 #endif
0826 
0827 
0828 void SEvt::setFramePlaceholder()
0829 {
0830 #ifdef WITH_OLD_FRAME
0831     sframe fr = sframe::Fabricate(0.f,0.f,0.f);
0832     setFrame(fr);
0833 #else
0834     sfr fr = sfr::MakeFromTranslateExtent<float>(0.f,0.f,0.f,1000.f);
0835     setFr(fr);
0836 #endif
0837 }
0838 
0839 
0840 
0841 
0842 const bool SEvt::transformInputPhoton_VERBOSE = ssys::getenvbool("SEvt__transformInputPhoton_VERBOSE") ;
0843 const bool SEvt::transformInputPhoton_WIDE = ssys::getenvbool("SEvt__transformInputPhoton_WIDE") ;
0844 
0845 /**
0846 SEvt::transformInputPhoton
0847 ---------------------------
0848 
0849 **/
0850 
0851 void SEvt::transformInputPhoton()
0852 {
0853     bool proceed = SEventConfig::IsRGModeSimulate() && hasInputPhoton() ;
0854 
0855     LOG(LEVEL) << " proceed " << ( proceed ? "YES" : "NO " ) ;
0856 
0857     LOG_IF(info, transformInputPhoton_VERBOSE )
0858         << " SEvt__transformInputPhoton_VERBOSE "
0859         << " SEventConfig::IsRGModeSimulate " << SEventConfig::IsRGModeSimulate()
0860         << " hasInputPhoton " <<  hasInputPhoton()
0861         << " proceed " << ( proceed ? "YES" : "NO " )
0862         << "\n"
0863 #ifdef WITH_OLD_FRAME
0864         << frame.desc()
0865 #else
0866         << fr.desc()
0867 #endif
0868         << "\n"
0869         ;
0870 
0871     if(!proceed) return ;
0872 
0873     bool normalize = true ;  // normalize mom and pol after doing the transform
0874 
0875 #ifdef WITH_OLD_FRAME
0876     NP* ipt = frame.transform_photon_m2w( input_photon, normalize );
0877 #else
0878     NP* ipt = fr.transform_photon_m2w( input_photon, normalize );
0879 #endif
0880 
0881     if(transformInputPhoton_WIDE)  // see notes/issues/G4ParticleChange_CheckIt_warnings.rst
0882     {
0883         input_photon_transformed = ipt ;
0884     }
0885     else
0886     {
0887         input_photon_transformed = ipt->ebyte == 8 ? NP::MakeNarrow(ipt) : ipt ;
0888         // narrow here to prevent immediate A:B difference with Geant4 seeing double precision
0889         // and Opticks float precision
0890     }
0891 }
0892 
0893 
0894 
0895 
0896 /**
0897 SEvt::createInputGenstep_simtrace
0898 ----------------------------------
0899 
0900 Two modes:
0901 
0902 with_CEGS
0903      normal way with envvars configuring a grid of gensteps
0904      and the (sframe)frame
0905 
0906 !with_CEGS
0907      little used simtrace mode without CEGS and with OPTICKS_INPUT_PHOTON
0908      that must be via record.npy SRecord::getSimtraceAtTime
0909 
0910 **/
0911 
0912 NP* SEvt::createInputGenstep_simtrace()
0913 {
0914     NP* igs = nullptr ;
0915 
0916     assert(SEventConfig::IsRGModeSimtrace());
0917     bool with_CEGS = SFrameGenstep::HasConfigEnv();
0918     bool with_ip = hasInputPhoton();
0919     LOG_IF(info, SIMTRACE)
0920         << "[" << SEvt__SIMTRACE << "] "
0921         << " with_CEGS " << ( with_CEGS ? "YES" : "NO " )
0922         << " with_ip " << ( with_ip ? "YES" : "NO " )
0923         ;
0924 
0925 
0926     if(with_CEGS)
0927     {
0928 #ifdef WITH_OLD_FRAME
0929         igs = SFrameGenstep::MakeCenterExtentGenstep_FromFrame(frame);
0930 #else
0931         igs = SFrameGenstep::MakeCenterExtentGenstep_FromFrame(fr);
0932 #endif
0933 
0934         LOG_IF(info, SIMTRACE)
0935             << "[" << SEvt__SIMTRACE << "] "
0936             << " simtrace igs " << ( igs ? igs->sstr() : "-" )
0937             ;
0938 
0939     }
0940     else
0941     {
0942         // simtrace mode without CEGS and with OPTICKS_INPUT_PHOTON that must be via record.npy SRecord::getSimtraceAtTime
0943         if( with_ip )
0944         {
0945             igs = SEvent::MakeInputPhotonGenstep(input_photon, OpticksGenstep_INPUT_PHOTON_SIMTRACE, nullptr) ;
0946             assert(igs);
0947         }
0948         else
0949         {
0950             // low level CSG/CSGSimtrace does this
0951         }
0952     }
0953     return igs ;
0954 }
0955 
0956 
0957 /**
0958 SEvt::createInputGenstep_simulate
0959 --------------------------------
0960 
0961 This branches between ECPU and EGPU because
0962 U4VPrimaryGenerator::GeneratePrimaries needs
0963 torch gensteps before U4Recorder::BeginOfEventAction
0964 So have to skip the genstep setup for ECPU as it should
0965 have been done already.
0966 
0967 **/
0968 
0969 
0970 
0971 NP* SEvt::createInputGenstep_simulate()
0972 {
0973     NP* igs = nullptr ;
0974 
0975     bool has_torch = SEventConfig::IsRunningModeTorch() ; // TODO: rename to InputTorch
0976     int inputs = int(hasInputGenstep_arrayPath()) + int(hasInputPhoton()) + int(has_torch) ;
0977 
0978     LOG_IF(info, LIFECYCLE) << " inputs " << inputs  ;
0979     LOG_IF(fatal, inputs > 1 )
0980         << " CANNOT COMBINE INPUTS : input_photon/input_genstep/torch "
0981         << " index " << index
0982         ;
0983     assert( inputs == 0 || inputs == 1);
0984     if( inputs == 1 )
0985     {
0986         assertZeroGenstep();
0987         if( hasInputGenstep_arrayPath() )
0988         {
0989             initInputGenstep_array();
0990             igs = getInputGenstep_array() ;
0991         }
0992         else if( hasInputPhoton())
0993         {
0994 #ifdef WITH_OLD_FRAME
0995             igs = SEvent::MakeInputPhotonGenstep(input_photon, OpticksGenstep_INPUT_PHOTON, &frame) ;
0996 #else
0997             igs = SEvent::MakeInputPhotonGenstep(input_photon, OpticksGenstep_INPUT_PHOTON, &fr) ;
0998 #endif
0999         }
1000         else if( has_torch )
1001         {
1002             if( SEvent::HasGENSTEP() )
1003             {
1004                 // expected with G4CXApp.h U4Recorder running : see G4CXApp::GeneratePrimaries
1005                 // this is because the gensteps are needed really early with Geant4 running
1006                 igs = SEvent::GetGENSTEP() ;
1007             }
1008             else
1009             {
1010                 int index_arg = getIndexArg();
1011                 igs = SEvent::MakeTorchGenstep(index_arg);  // pass index to allow changing num photons per event
1012             }
1013         }
1014     }
1015     return igs ;
1016 }
1017 
1018 
1019 
1020 /**
1021 SEvt::createInputGenstep_configured
1022 ----------------------------------
1023 
1024 **/
1025 
1026 
1027 NP* SEvt::createInputGenstep_configured()
1028 {
1029     NP* igs = nullptr ;
1030     if(SEventConfig::IsRGModeSimtrace())
1031     {
1032         igs = createInputGenstep_simtrace();
1033     }
1034     else if(SEventConfig::IsRGModeSimulate())
1035     {
1036         igs = createInputGenstep_simulate();
1037     }
1038     return igs ;
1039 }
1040 
1041 
1042 
1043 
1044 /**
1045 SEvt::addInputGenstep  (formerly addFrameGenstep)
1046 --------------------------------------------------
1047 
1048 This is invoked from SEvt::beginOfEvent after invokation of SEvt::setIndex
1049 
1050 For hostside simtrace and input photon running this must be called
1051 at the start of every event cycle to add the gensteps which trigger
1052 the allocations for result vectors.
1053 
1054 
1055 **/
1056 
1057 void SEvt::addInputGenstep()
1058 {
1059     LOG_IF(info, LIFECYCLE) << id() ;
1060     LOG(LEVEL);
1061 
1062     NP* igs = createInputGenstep_configured();
1063     addGenstep(igs);
1064 }
1065 
1066 void SEvt::assertZeroGenstep()
1067 {
1068     int prior_genstep = genstep.size() ;
1069     bool prior_genstep_zero = prior_genstep == 0 ;
1070 
1071     LOG_IF(fatal, !prior_genstep_zero )
1072         << " FIND genstep WHEN NONE ARE EXPECTED  "
1073         << " index " << index
1074         << " instance " << instance
1075         << " isECPU " << isECPU()
1076         << " isEGPU " << isEGPU()
1077         << " prior_genstep_zero " << ( prior_genstep_zero ? "YES" : "NO " )
1078         << " prior_genstep " << prior_genstep
1079         ;
1080     assert( prior_genstep_zero ) ;
1081 }
1082 
1083 
1084 
1085 const char* SEvt::getFrameId() const
1086 {
1087 #ifdef WITH_OLD_FRAME
1088     return frame.getFrameId() ;
1089 #else
1090     return fr.get_id();
1091 #endif
1092 }
1093 const NP*   SEvt::getFrameArray() const
1094 {
1095 #ifdef WITH_OLD_FRAME
1096     return frame.getFrameArray() ;
1097 #else
1098     return fr.serialize();
1099 #endif
1100 }
1101 
1102 const char* SEvt::GetFrameId(int idx){    return Exists(idx) ? Get(idx)->getFrameId() : nullptr ; }
1103 const NP*   SEvt::GetFrameArray(int idx){ return Exists(idx) ? Get(idx)->getFrameArray() : nullptr ; }
1104 
1105 
1106 /**
1107 SEvt::setFrame_HostsideSimtrace
1108 ---------------------------------
1109 
1110 Called from SEvt::setFrame when sframe::is_hostside_simtrace, eg at behest of U4Simtrace
1111 
1112 **/
1113 
1114 void SEvt::setFrame_HostsideSimtrace()
1115 {
1116     unsigned num_photon_gs = getNumPhotonFromGenstep();
1117     unsigned num_photon_evt = evt->num_photon ;
1118     LOG_IF(info, SIMTRACE)
1119          << "frame.is_hostside_simtrace"
1120          << " num_photon_gs " << num_photon_gs
1121          << " num_photon_evt " << num_photon_evt
1122          ;
1123 
1124     assert( num_photon_gs == num_photon_evt );
1125     setNumSimtrace( num_photon_gs );
1126 
1127     LOG_IF(info, SIMTRACE) << " before hostside_running_resize simtrace.size " << simtrace.size() ;
1128 
1129     hostside_running_resize();
1130 
1131     LOG_IF(info, SIMTRACE) << " after hostside_running_resize simtrace.size " << simtrace.size() ;
1132 
1133     SFrameGenstep::GenerateSimtracePhotons( simtrace, genstep );
1134 }
1135 
1136 
1137 
1138 #ifdef WITH_OLD_FRAME
1139 /**
1140 SEvt::setGeo
1141 -------------
1142 
1143 SGeo is a protocol for geometry access fulfilled by CSGFoundry (and formerly by GGeo)
1144 
1145 This connection between the SGeo geometry and SEvt is what allows
1146 the appropriate instance frame to be accessed. That is vital for
1147 looking up the sensor_identifier and sensor_index.
1148 
1149 TODO: replace this with stree.h based approach
1150 
1151 
1152 Invoked with stack::
1153 
1154     SEvt::setGeo
1155     CSGOptiX::InitEvt
1156     CSGOptiX::Create
1157     CSGOptiX::SimtraceMain () at /home/blyth/opticks/CSGOptiX/CSGOptiX.cc:156
1158     main
1159 
1160 
1161 **/
1162 
1163 void SEvt::setGeo(const SGeo* cf_)
1164 {
1165     assert(cf_);
1166     cf = cf_ ;
1167     tree = cf->getTree();
1168 }
1169 
1170 #else
1171 /**
1172 SEvt::setSim
1173 -------------
1174 
1175 This aims to remove SEvt::setGeo and CSGFoundry SGeo base
1176 **/
1177 
1178 void SEvt::setSim(const SSim* sim_)
1179 {
1180     assert(sim_);
1181     sim = sim_ ;
1182     tree = sim->get_tree();
1183 }
1184 
1185 #endif
1186 
1187 
1188 /**
1189 SEvt::setFrame
1190 -----------------
1191 
1192 This method asserts that SEvt::setGeo has been called
1193 as that is needed to provide access to sframe via
1194 the SGeo protocol base of either GGeo OR CSGFoundry
1195 
1196 TODO: replace this with stree.h based approach
1197 
1198 **/
1199 void SEvt::setFrame(unsigned ins_idx)
1200 {
1201 #ifdef WITH_OLD_FRAME
1202     LOG_IF(fatal, cf == nullptr) << "must SEvt::setGeo before being can access frames " ;
1203     assert(cf);
1204     sframe fr ;
1205     int rc = cf->getFrame(fr, ins_idx) ;
1206     if(rc!=0) std::raise(SIGINT);
1207     assert( rc == 0 );
1208     fr.prepare();
1209     setFrame(fr);
1210 #else
1211     assert(tree);
1212     sfr f = tree->get_frame_inst( ins_idx );
1213     setFr(f);
1214 #endif
1215 }
1216 
1217 
1218 //// below impl order matches decl order : KEEP IT THAT WAY
1219 
1220 
1221 /**
1222 SEvt::CreateSimtraceEvent
1223 ---------------------------
1224 
1225 Experimental creation of a new SEvt
1226 that adopts the sframe of the prior SEvt
1227 and is configured for hostside Simtrace running.
1228 
1229 The appropriate place to use this method from
1230 is EndOfRunAction after standard (non-simtrace)
1231 usage of SEvt such as saving simulation SEvt
1232 is completed.
1233 
1234 **/
1235 
1236 SEvt* SEvt::CreateSimtraceEvent()  // static
1237 {
1238     LOG_IF(info, SIMTRACE) << "[" ;
1239 
1240     SEvt* prior = SEvt::Get(0);
1241     assert(prior);
1242     if(prior == nullptr ) return nullptr ;
1243 
1244 #ifdef WITH_OLD_FRAME
1245     sframe& pfr = prior->frame ;
1246     pfr.set_hostside_simtrace();
1247 #else
1248     sfr& pfr = prior->fr ;
1249     pfr.set_hostside_simtrace();
1250 #endif
1251 
1252 
1253     if( pfr.ce.w == 0.f )
1254     {
1255         pfr.ce.w = 200.f ;
1256         LOG_IF(info, SIMTRACE)
1257             << " kludging frame extent, this happens with U4SimulateTest "
1258             << " as the CSGFoundry geometry is not available causing the "
1259             << " SEvt sframe to be default "
1260             ;
1261     }
1262 
1263     // set_hostside_simtrace into frame which
1264     // influences the action of SEvt::setFrame
1265     // especially SEvt::setFrame_HostsideSimtrace which
1266     // generates simtrace photons
1267 
1268     SEventConfig::SetRGModeSimtrace(); // now added handling to redo Initialize_Comp when changing mode
1269 
1270     LOG_IF(info, SIMTRACE) << " SWITCH : SEventConfig::SetRGModeSimtrace " ;
1271     SEvt* ste = new SEvt ;
1272 
1273 #ifdef WITH_OLD_FRAME
1274     ste->setFrame(pfr);
1275 #else
1276     ste->setFr(pfr);
1277 #endif
1278 
1279     LOG_IF(info, SIMTRACE) << "] ste.simtrace.size " << ste->simtrace.size() ;
1280 
1281     return ste ;
1282 }
1283 
1284 
1285 
1286 /**
1287 SEvt::setCompProvider
1288 ----------------------
1289 
1290 This is called for device side running only from QEvt::init
1291 
1292 **/
1293 
1294 void SEvt::setCompProvider(const SCompProvider* provider_)
1295 {
1296     provider = provider_ ;
1297     LOG(LEVEL) << descProvider() ;
1298 }
1299 
1300 bool SEvt::isSelfProvider() const {   return provider == this ; }
1301 
1302 std::string SEvt::descProvider() const
1303 {
1304     bool is_self_provider = isSelfProvider() ;
1305     std::stringstream ss ;
1306     ss << "SEvt::descProvider"
1307        << " provider.getTypeName " << provider->getTypeName()
1308        << " that address is: " << ( is_self_provider ? "SELF" : "another object" )
1309        ;
1310     std::string s = ss.str();
1311     return s ;
1312 }
1313 
1314 /**
1315 SEvt::gatherDomain
1316 --------------------
1317 
1318 Create (2,4,4) NP array and populate with quad4 from::
1319 
1320     sevent::get_domain
1321     sevent::get_config
1322 
1323 
1324 NB an alternative route for metadata is the
1325 SEvt::meta OR QEvt::meta that gets adopted as
1326 the NPFold meta in SEvt::gather_components
1327 
1328 DOMAIN IS INCREASINGLY IRRELEVANT WHEN NOT USING REC
1329 
1330 TODO: ELIMINATE
1331 
1332 **/
1333 
1334 NP* SEvt::gatherDomain() const
1335 {
1336     quad4 dom[2] ;
1337     evt->get_domain(dom[0]);
1338     evt->get_config(dom[1]);  // maxima, counts
1339     NP* domain = NP::Make<float>( 2, 4, 4 );
1340     domain->read2<float>( (float*)&dom[0] );
1341     return domain ;
1342 }
1343 
1344 int SEvt::Count()  // static
1345 {
1346     int count = 0 ;
1347     if(Exists(0)) count += 1 ;
1348     if(Exists(1)) count += 1 ;
1349     return count ;
1350 }
1351 
1352 
1353 SEvt* SEvt::Get_EGPU(){ return SEvt::Get(EGPU) ; }
1354 SEvt* SEvt::Get_ECPU(){ return SEvt::Get(ECPU) ; }
1355 
1356 SEvt* SEvt::Get(int idx)  // static
1357 {
1358     assert( idx == 0 || idx == 1 );
1359     return INSTANCES[idx] ;
1360 }
1361 void SEvt::Set(int idx, SEvt* inst) // static
1362 {
1363     assert( idx == 0 || idx == 1 );
1364     INSTANCES[idx] = inst ;
1365     LOG(LEVEL) << " idx " << idx  << " " << DescINSTANCE()  ;
1366 }
1367 
1368 /**
1369 SEvt::Create
1370 -------------
1371 
1372 Q: Does all SEvt creation use this ?
1373 
1374 
1375 **/
1376 
1377 SEvt* SEvt::Create_EGPU(){ return Create(EGPU) ; }
1378 SEvt* SEvt::Create_ECPU(){ return Create(ECPU) ; }
1379 
1380 SEvt* SEvt::Create(int ins)  // static
1381 {
1382     //const char* alldir = spath::Resolve("ALL${VERSION:-0}") ;
1383     //SEvt::SetReldir(alldir);
1384 
1385     assert( ins == 0 || ins == 1);
1386     SEvt* ev = new SEvt ;
1387     ev->setInstance(ins) ;
1388     INSTANCES[ins] = ev  ;
1389     assert( Get(ins) == ev );
1390     LOG(LEVEL) << " ins " << ins  << " " << DescINSTANCE()  ;
1391     return ev  ;
1392 }
1393 
1394 bool SEvt::isEGPU() const { return instance == EGPU ; }
1395 bool SEvt::isECPU() const { return instance == ECPU ; }
1396 
1397 /**
1398 SEvt::isFirstEvtInstance SEvt::isLastEvtInstance
1399 --------------------------------------------------
1400 
1401 When have both ECPU and EGPU the ECPU is first
1402 otherwise when there is only one event
1403 whatever it is is inevitably first(and last).
1404 
1405 **/
1406 bool SEvt::isFirstEvtInstance() const
1407 {
1408     return Exists_EGPU() && Exists_ECPU() ? isECPU() : true ;
1409 }
1410 bool SEvt::isLastEvtInstance() const
1411 {
1412     return Exists_EGPU() && Exists_ECPU() ? isEGPU() : true ;
1413 }
1414 
1415 
1416 
1417 
1418 SEvt* SEvt::getSibling() const
1419 {
1420     SEvt* sibling = nullptr ;
1421     switch(instance)
1422     {
1423         case ECPU:  sibling = Get(EGPU) ; break ;
1424         case EGPU:  sibling = Get(ECPU) ; break ;
1425         default:    sibling = nullptr ;
1426     }
1427     return sibling ;
1428 }
1429 
1430 
1431 bool SEvt::Exists(int idx)  // static
1432 {
1433     return Get(idx) != nullptr ;
1434 }
1435 
1436 bool SEvt::Exists_ECPU(){ return Exists(ECPU) ; }
1437 bool SEvt::Exists_EGPU(){ return Exists(EGPU) ; }
1438 
1439 
1440 
1441 
1442 SEvt* SEvt::CreateOrReuse(int idx)
1443 {
1444     SEvt* sev = Exists(idx) ? Get(idx) : Create(idx) ;
1445     LOG(LEVEL) << " idx " << idx  << " " << DescINSTANCE()  ;
1446     return sev ;
1447 }
1448 
1449 
1450 
1451 SEvt* SEvt::CreateOrReuse_EGPU(){  return CreateOrReuse(EGPU) ; }
1452 SEvt* SEvt::CreateOrReuse_ECPU(){  return CreateOrReuse(ECPU) ; }
1453 
1454 /**
1455 SEvt::CreateOrReuse
1456 ---------------------
1457 
1458 Creates 0, 1 OR 2 SEvt depending on SEventConfig::IntegrationMode()::
1459 
1460     OPTICKS_INTEGRATION_MODE (aka opticksMode)
1461 
1462 +-----------------+----------+----------+--------------------------------------------+
1463 |  opticksMode    | num SEvt | SEvt idx | notes                                      |
1464 +=================+==========+==========+============================================+
1465 |             0   |    0     |    -     |                                            |
1466 +-----------------+----------+----------+--------------------------------------------+
1467 |             1   |    1     |    0     |  GPU optical simulation only               |
1468 +-----------------+----------+----------+--------------------------------------------+
1469 |             2   |    1     |    1     |  CPU optical simulation only               |
1470 +-----------------+----------+----------+--------------------------------------------+
1471 |             3   |    2     |   0,1    |  both GPU and CPU optical simulations      |
1472 +-----------------+----------+----------+--------------------------------------------+
1473 
1474 **/
1475 
1476 
1477 void SEvt::CreateOrReuse()
1478 {
1479     int integrationMode = SEventConfig::IntegrationMode() ;
1480     LOG(LEVEL) << " integrationMode " << integrationMode  ;
1481 
1482     if( integrationMode == 0 )
1483     {
1484         CreateOrReuse(ECPU);          // HMM: is this needed, for metadata recording ?
1485     }
1486     else if( integrationMode == 1 )   // GPU optical simulation only
1487     {
1488         CreateOrReuse(EGPU);
1489     }
1490     else if( integrationMode == 2 )   // CPU optical simulation only
1491     {
1492         CreateOrReuse(ECPU);
1493     }
1494     else if( integrationMode == 3 )  // both CPU and GPU optical simulation
1495     {
1496         CreateOrReuse(EGPU);
1497         CreateOrReuse(ECPU);
1498     }
1499     else
1500     {
1501         LOG(LEVEL) << " NOT CREATING SEvt : unexpected integrationMode " << integrationMode ;
1502         //std::raise(SIGINT);
1503         //assert(0);
1504     }
1505     LOG(LEVEL) << DescINSTANCE()  ;
1506 }
1507 
1508 
1509 
1510 #ifdef WITH_OLD_FRAME
1511 void SEvt::SetFrame(const sframe& fr )
1512 {
1513     assert(0 && "DONT USE THIS - USE SEvt::SetFr");
1514     if(Exists(0)) Get(0)->setFrame(fr);
1515     if(Exists(1)) Get(1)->setFrame(fr);
1516 }
1517 #else
1518 void SEvt::SetFr(const sfr& fr )
1519 {
1520     if(Exists(0)) Get(0)->setFr(fr);
1521     if(Exists(1)) Get(1)->setFr(fr);
1522 }
1523 #endif
1524 
1525 
1526 
1527 
1528 
1529 void SEvt::Check(int idx)
1530 {
1531     SEvt* sev = Get(idx) ;
1532     LOG_IF(fatal, !sev) << "must instanciate SEvt before using most SEvt methods" ;
1533     assert(sev);
1534 }
1535 
1536 
1537 #ifndef PRODUCTION
1538 /**
1539 SEvt::AddTag
1540 ----------------
1541 
1542 Tags are used when recording all randoms consumed by simulation
1543 
1544 NB some statics make sense to broadcast to all INSTANCES but
1545 this is not one of them
1546 
1547 **/
1548 
1549 void SEvt::AddTag(int idx, unsigned stack, float u )
1550 {
1551     if(Exists(idx)) Get(idx)->addTag(stack,u);
1552 }
1553 int  SEvt::GetTagSlot(int idx)
1554 {
1555     return Exists(idx) ? Get(idx)->getTagSlot() : -1 ;
1556 }
1557 #endif
1558 
1559 sgs SEvt::AddGenstep(const quad6& q)
1560 {
1561     sgs label = {} ;
1562     if(Exists(0)) label = Get(0)->addGenstep(q) ;
1563     if(Exists(1)) label = Get(1)->addGenstep(q) ;
1564     return label ;
1565 }
1566 sgs SEvt::AddGenstep(const NP* a)
1567 {
1568     sgs label = {} ;
1569     if(Exists(0)) label = Get(0)->addGenstep(a) ;
1570     if(Exists(1)) label = Get(1)->addGenstep(a) ;
1571     return label ;
1572 }
1573 void SEvt::AddCarrierGenstep(){ AddGenstep(SEvent::MakeCarrierGenstep()); }
1574 void SEvt::AddTorchGenstep(){   AddGenstep(SEvent::MakeTorchGenstep());   }
1575 
1576 void SEvt::addTorchGenstep()
1577 {
1578     const NP* a = SEvent::MakeTorchGenstep() ;
1579     addGenstep(a);
1580 }
1581 
1582 
1583 
1584 SEvt* SEvt::LoadAbsolute(const char* dir_) // static
1585 {
1586     const char* dir = spath::Resolve(dir_);
1587     SEvt* ev = new SEvt ;
1588     int rc = ev->loadfold(dir) ;
1589     if(rc != 0) ev->is_loadfail = true ;
1590     return ev  ;
1591 }
1592 
1593 
1594 /**
1595 SEvt::LoadRelative (formerly Load)
1596 ------------------------------------
1597 
1598 Q: which instance slot ?
1599 A: are persisting instance in domain metadata and recovering in SEvt::onload
1600 
1601 Q: should the INSTANCE array slot be filled onload ?
1602 A: Guess so, Loading should regain the state before the save
1603 
1604 
1605 **/
1606 
1607 SEvt* SEvt::LoadRelative(const char* rel, int ins, int idx  )  // static
1608 {
1609     LOG(LEVEL) << "[" ;
1610 
1611     if(rel != nullptr) SEventConfig::SetEventReldir(rel);
1612 
1613     SEvt* ev = SEvt::Create(ins) ;
1614     if(idx > -1) ev->setIndex(idx);
1615 
1616     int rc = ev->load() ;
1617     if(rc != 0) ev->is_loadfail = true ;
1618 
1619     LOG(LEVEL) << "]" ;
1620     return ev ;
1621 }
1622 
1623 
1624 /**
1625 SEvt::ClearOutput
1626 -------------------
1627 
1628 **/
1629 
1630 void SEvt::ClearOutput()
1631 {
1632     if(Exists(0)) Get(0)->clear_output();
1633     if(Exists(1)) Get(1)->clear_output();
1634 }
1635 void SEvt::ClearGenstep()
1636 {
1637     if(Exists(0)) Get(0)->clear_genstep();
1638     if(Exists(1)) Get(1)->clear_genstep();
1639 }
1640 
1641 
1642 
1643 
1644 void SEvt::Save()
1645 {
1646     if(Exists(0)) Get(0)->save();
1647     if(Exists(1)) Get(1)->save();
1648 }
1649 
1650 
1651 bool SEvt::HaveDistinctOutputDirs() // static
1652 {
1653     if(Count() < 2) return true ;
1654     assert( Count() == 2 );
1655     SEvt* i0 = Get(0);
1656     SEvt* i1 = Get(1);
1657     return i0->index != i1->index ;
1658    // NO LONGER NEEDED ? NOW THAT USE 'A' 'B' prefix ?
1659 }
1660 
1661 
1662 void SEvt::Save(const char* dir)
1663 {
1664     assert( HaveDistinctOutputDirs() );  // check will not overwrite
1665     if(Exists(0)) Get(0)->save(dir) ;
1666     if(Exists(1)) Get(1)->save(dir) ;
1667 }
1668 
1669 void SEvt::Save(const char* dir, const char* rel)
1670 {
1671     assert( HaveDistinctOutputDirs() );  // check will not overwrite
1672     if(Exists(0)) Get(0)->save(dir, rel) ;
1673     if(Exists(1)) Get(1)->save(dir, rel) ;
1674 }
1675 
1676 void SEvt::SaveGenstepLabels(const char* dir, const char* name)
1677 {
1678     assert( HaveDistinctOutputDirs() );  // check will not overwrite
1679     if(Exists(0)) Get(0)->saveGenstepLabels(dir, name );
1680     if(Exists(1)) Get(1)->saveGenstepLabels(dir, name );
1681 }
1682 
1683 
1684 
1685 
1686 
1687 void SEvt::BeginOfRun()
1688 {
1689     SProf::Add("SEvt__BeginOfRun");
1690     SProf::Write();
1691 }
1692 
1693 
1694 
1695 
1696 void SEvt::EndOfRun()
1697 {
1698     SProf::Add("SEvt__EndOfRun");
1699     SProf::Write();
1700 }
1701 
1702 
1703 
1704 template<typename T>
1705 void SEvt::SetRunMeta(const char* k, T v )
1706 {
1707     RUN_META->set_meta<T>(k, v );
1708 }
1709 
1710 template void SEvt::SetRunMeta<int>(      const char*, int  );
1711 template void SEvt::SetRunMeta<uint64_t>( const char*, uint64_t  );
1712 template void SEvt::SetRunMeta<int64_t>(  const char*, int64_t  );
1713 template void SEvt::SetRunMeta<unsigned>( const char*, unsigned  );
1714 template void SEvt::SetRunMeta<float>(    const char*, float  );
1715 template void SEvt::SetRunMeta<double>(   const char*, double  );
1716 template void SEvt::SetRunMeta<std::string>( const char*, std::string  );
1717 
1718 void SEvt::SetRunMetaString(const char* k, const char* v ) // static
1719 {
1720     std::string* rms = RunMetaString();
1721     assert(rms);
1722     NP::SetMeta<std::string>(*rms, k, v );
1723 }
1724 
1725 
1726 /*
1727 void SEvt::SetRunProf(const char* k, const sprof& v) // static
1728 {
1729     SetRunMeta<std::string>( k, sprof::Serialize(v) );
1730 }
1731 void SEvt::SetRunProf(const char* k)   // static
1732 {
1733     SetRunMeta<std::string>( k, sprof::Now() );
1734 }
1735 void SEvt::setRunProf_Annotated(const char* hdr) const
1736 {
1737     std::string eid = getIndexString_(hdr) ;
1738     SetRunMeta<std::string>( eid.c_str(), sprof::Now() );
1739 }
1740 */
1741 
1742 
1743 
1744 /**
1745 SEvt::IsSaveNothing
1746 --------------------
1747 
1748 When using OPTICKS_EVENT_MODE of Minimal or Nothing
1749 the saving of run metadata and directory creation
1750 can be disabled with::
1751 
1752     export SEvt__SAVE_NOTHING=1
1753 
1754 **/
1755 
1756 bool SEvt::IsSaveNothing() // static
1757 {
1758     return SEventConfig::IsMinimalOrNothing() && SAVE_NOTHING ;
1759 }
1760 
1761 
1762 
1763 /**
1764 SEvt::SaveRunMeta
1765 -------------------
1766 
1767 Saving only at the end of run is problematic
1768 when jobs are prone to memory failure. So
1769 better to save at the end of every event, not
1770 just the last.
1771 
1772 The directory to save run.npy and run_meta.txt
1773 can be controlled by::
1774 
1775     SEvt__SAVE_RUNDIR
1776 
1777 When save into the RunDir one level above the event folders A000 etc..
1778 
1779 When SEvt__SAVE_RUNDIR is not defined save into the invoking directory
1780 together with the logfile and SProf.txt
1781 This simple approach makes more sense in production when event arrays
1782 are not saved.
1783 
1784 **/
1785 
1786 void SEvt::SaveRunMeta(const char* base)
1787 {
1788     const char* dir = RunDir(base);
1789     const char* name = "run.npy" ;
1790 
1791     bool is_save_nothing = IsSaveNothing();
1792 
1793     LOG_IF(info, RUNMETA)
1794         << " [" << SEvt__RUNMETA << "]"
1795         << " is_save_nothing " << ( is_save_nothing ? "YES" : "NO " )
1796         << " base " << ( base ? base : "-" )
1797         << " dir " << ( dir ? dir : "-" )
1798         << " name " << name
1799         << " SAVE_RUNDIR " << ( SAVE_RUNDIR ? "YES" : "NO " )
1800         ;
1801 
1802     if(is_save_nothing) return ;
1803 
1804     if(SAVE_RUNDIR)
1805     {
1806         RUN_META->save(dir, name) ;
1807     }
1808     else
1809     {
1810         RUN_META->save(name) ;
1811     }
1812 }
1813 
1814 
1815 
1816 
1817 void SEvt::setMetaString(const char* k, const char* v)
1818 {
1819     NP::SetMeta<std::string>(meta, k, v );
1820 }
1821 
1822 void SEvt::setMetaProf(const char* k, const sprof& v)
1823 {
1824     NP::SetMeta<std::string>(meta, k, sprof::Serialize(v) );
1825 }
1826 void SEvt::setMetaProf(const char* k)
1827 {
1828     NP::SetMeta<std::string>(meta, k, sprof::Now() );
1829 }
1830 
1831 
1832 
1833 
1834 
1835 template<typename T>
1836 void SEvt::setMeta( const char* k, T v )
1837 {
1838     NP::SetMeta<T>( meta, k, v );
1839 }
1840 
1841 template void SEvt::setMeta<uint64_t>(const char*, uint64_t );
1842 template void SEvt::setMeta<int>(const char*, int );
1843 template void SEvt::setMeta<unsigned>(const char*, unsigned );
1844 template void SEvt::setMeta<float>(const char*, float );
1845 template void SEvt::setMeta<double>(const char*, double );
1846 template void SEvt::setMeta<std::string>(const char*, std::string );
1847 
1848 
1849 
1850 /**
1851 SEvt::beginOfEvent
1852 -------------------
1853 
1854 Called from::
1855 
1856      QSim::simulate (SEvt::EGPU instance )
1857      U4Recorder::BeginOfEventAction  (SEvt::ECPU instance)
1858 
1859 Note that eventID from both Geant4 and now Opticks is zero based
1860 
1861 Lifecycle::
1862 
1863       G4CXApp::BeginOfEventAction
1864         U4Recorder::BeginOfEventAction
1865           ECPU.SEvt::beginOfEvent
1866 
1867       ... collect gensteps into whichever SEvt are instanciated
1868       ... collect gensteps into whichever SEvt are instanciated
1869       ... collect gensteps into whichever SEvt are instanciated
1870 
1871       G4CXApp::EndOfEventAction
1872         U4Recorder::EndOfEventAction
1873           ECPU.SEvt::endOfEvent
1874 
1875         G4CXOpticks::simulate
1876           QSim::simulate
1877 
1878             EGPU.SEvt::beginOfEvent
1879 
1880             QEvt::setGenstep
1881                A:SEvt::clear
1882                QEvt::setGenstep(NP*)
1883 
1884             SCSGOptiX::simulate_launch
1885 
1886             EGPU.:SEvt::endOfEvent
1887 
1888 
1889 ECPU
1890 -----
1891 
1892 As gensteps are collected before EGPU.beginOfEvent
1893 the clear_output excludes gs/gensteps as they are inputs.
1894 
1895 Need to think of the lifecycle of both ECPU and EGPU together.
1896 This remains true even with runningMode 1 which has no ECPU
1897 as still need to collect the gensteps.
1898 
1899 **/
1900 
1901 
1902 void SEvt::beginOfEvent(int eventID)
1903 {
1904     if(isFirstEvtInstance() && eventID == 0) BeginOfRun() ;
1905     if(eventID == 0) SProf::Add( isEGPU() ? "SEvt__beginOfEvent_FIRST_EGPU" : "SEvt__beginOfEvent_FIRST_ECPU" ) ;
1906 
1907     setStage(SEvt__beginOfEvent);
1908     sprof::Stamp(p_SEvt__beginOfEvent_0);
1909 
1910     LOG(LEVEL) << " eventID " << eventID ;   // 0-based
1911     setIndex(eventID);
1912 
1913     LOG_IF(info, LIFECYCLE) << id() ;
1914 
1915     clear_output();   // output vectors and fold : excluding gensteps as thats input
1916     if( addGenstep_array == 0 )
1917     {
1918         addInputGenstep();  // does genstep setup for simtrace, input photon and torch running
1919     }
1920     else
1921     {
1922         LOG(LEVEL) << "skip addInputGenstep as addGenstep_array " << addGenstep_array ;
1923     }
1924 
1925 
1926     setMeta<int64_t>("NumPhotonCollected", numphoton_collected );
1927     setMeta<int64_t>("NumGenstepCollected", numgenstep_collected );
1928 
1929     setMeta<int>("MaxBounce", evt->max_bounce );
1930 
1931     LOG_IF(info, LIFECYCLE)
1932         << " NumPhotonCollected " << numphoton_collected
1933         << " NumGenstepCollected " << numgenstep_collected
1934         << " MaxBounce " << evt->max_bounce
1935         ;
1936 
1937     sprof::Stamp(p_SEvt__beginOfEvent_1);
1938 }
1939 
1940 
1941 
1942 /**
1943 SEvt::endOfEvent
1944 ------------------
1945 
1946 Called for example from::
1947 
1948    U4Recorder::EndOfEventAction
1949    QSim::simulate
1950 
1951 Only SEventConfig::SaveComp OPTICKS_SAVE_COMP are actually saved,
1952 so can switch off all saving wuth that config.
1953 
1954 **/
1955 
1956 void SEvt::endOfEvent(int eventID)
1957 {
1958 
1959     setStage(SEvt__endOfEvent);
1960     LOG_IF(info, LIFECYCLE) << id() ;
1961     sprof::Stamp(p_SEvt__endOfEvent_0);
1962 
1963     endIndex(eventID);   // eventID is 0-based
1964     endMeta();
1965     gather_metadata();
1966 
1967     save();              // gather and save SEventConfig configured arrays
1968     clear_output();
1969     clear_genstep();
1970     clear_extra();
1971     reset_counter();
1972 
1973     SaveRunMeta(); // saving run_meta.txt at end of every event incase of crashes
1974 
1975 
1976     bool is_last_eventID = SEventConfig::IsLastEvent(eventID) ;
1977     if(is_last_eventID)
1978     {
1979         //SetRunProf( isEGPU() ? "SEvt__endOfEvent_LAST_EGPU" : "SEvt__endOfEvent_LAST_ECPU" ) ;
1980         bool is_last_evt_instance = isLastEvtInstance() ;
1981 
1982         LOG(LEVEL)
1983             << " is_last_eventID " << ( is_last_eventID ? "YES" : "NO " )
1984             << " is_last_evt_instance " << ( is_last_evt_instance ? "YES" : "NO " )
1985             ;
1986 
1987         if(is_last_evt_instance) SEvt::EndOfRun();   // invokes SaveRunMeta
1988     }
1989 
1990 
1991 }
1992 
1993 void SEvt::reset_counter()
1994 {
1995     addGenstep_array = 0  ;
1996 }
1997 
1998 
1999 void SEvt::endMeta()
2000 {
2001     setMeta<std::string>("site", "SEvt::endMeta" );
2002     assert( photon_selector->hitmask == photonlite_selector->hitmask );
2003     setMeta<int>("hitmask", photon_selector->hitmask );
2004 
2005     setMeta<int>("index", index);
2006     setMeta<int>("instance", instance);
2007 
2008     setMetaProf("SEvt__beginOfEvent_0", p_SEvt__beginOfEvent_0);
2009     setMetaProf("SEvt__beginOfEvent_1", p_SEvt__beginOfEvent_1);
2010     setMetaProf("SEvt__endOfEvent_0",   p_SEvt__endOfEvent_0);
2011     //setMetaProf("SEvt__endOfEvent_1",   p_SEvt__endOfEvent_1);
2012 
2013     setMeta<uint64_t>("t_BeginOfEvent", t_BeginOfEvent );
2014 
2015 #ifndef PRODUCTION
2016     setMeta<uint64_t>("t_setGenstep_0",  t_setGenstep_0 );
2017     setMeta<uint64_t>("t_setGenstep_1",  t_setGenstep_1 );
2018     setMeta<uint64_t>("t_setGenstep_2",  t_setGenstep_2 );
2019     setMeta<uint64_t>("t_setGenstep_3",  t_setGenstep_3 );
2020     setMeta<uint64_t>("t_setGenstep_4",  t_setGenstep_4 );
2021     setMeta<uint64_t>("t_setGenstep_5",  t_setGenstep_5 );
2022     setMeta<uint64_t>("t_setGenstep_6",  t_setGenstep_6 );
2023     setMeta<uint64_t>("t_setGenstep_7",  t_setGenstep_7 );
2024     setMeta<uint64_t>("t_setGenstep_8",  t_setGenstep_8 );
2025 #endif
2026     setMeta<uint64_t>("t_PreLaunch", t_PreLaunch );
2027     setMeta<uint64_t>("t_PostLaunch", t_PostLaunch );
2028     setMeta<uint64_t>("t_EndOfEvent",   t_EndOfEvent );
2029 
2030     setMeta<uint64_t>("t_Event", t_EndOfEvent - t_BeginOfEvent );
2031     setMeta<double>("t_Launch", t_Launch );
2032 }
2033 
2034 
2035 
2036 int SEvt::GetIndex(int idx)
2037 {
2038     return Exists(idx) ? Get(idx)->getIndex() : -1 ;
2039 }
2040 S4RandomArray* SEvt::GetRandomArray(int idx)
2041 {
2042     return Exists(idx) ? Get(idx)->random_array : nullptr ;
2043 }
2044 
2045 
2046 int64_t SEvt::GetNumPhotonCollected(int idx){    return Exists(idx) ? Get(idx)->getNumPhotonCollected() : UNDEF ; }
2047 int64_t SEvt::GetNumPhotonGenstepMax(int idx){   return Exists(idx) ? Get(idx)->getNumPhotonGenstepMax() : UNDEF ; }
2048 int64_t SEvt::GetNumPhotonFromGenstep(int idx){  return Exists(idx) ? Get(idx)->getNumPhotonFromGenstep() : UNDEF ; }
2049 int64_t SEvt::GetNumGenstepFromGenstep(int idx){ return Exists(idx) ? Get(idx)->getNumGenstepFromGenstep() : UNDEF ; }
2050 int64_t SEvt::GetNumHit(int idx){                return Exists(idx) ? Get(idx)->getNumHit() : UNDEF ; }
2051 int64_t SEvt::GetNumHit_EGPU(){  return GetNumHit(EGPU) ; }
2052 int64_t SEvt::GetNumHit_ECPU(){  return GetNumHit(ECPU) ; }
2053 
2054 
2055 
2056 NP* SEvt::GatherGenstep(int idx) {   return Exists(idx) ? Get(idx)->gatherGenstep() : nullptr ; }
2057 NP* SEvt::GetInputPhoton(int idx) {  return Exists(idx) ? Get(idx)->getInputPhoton() : nullptr ; }
2058 
2059 void SEvt::SetInputPhoton(NP* p)
2060 {
2061     if(Exists(0)) Get(0)->setInputPhoton(p) ;
2062     if(Exists(1)) Get(1)->setInputPhoton(p) ;
2063 }
2064 bool SEvt::HasInputPhoton(int idx)
2065 {
2066     return Exists(idx) ? Get(idx)->hasInputPhoton() : false ;
2067 }
2068 bool SEvt::HasInputPhoton()
2069 {
2070     return HasInputPhoton(EGPU) || HasInputPhoton(ECPU) ;
2071 }
2072 
2073 NP* SEvt::GetInputPhoton() // static
2074 {
2075     NP* ip = nullptr ;
2076     if(ip == nullptr && HasInputPhoton(EGPU)) ip = GetInputPhoton(EGPU) ;
2077     if(ip == nullptr && HasInputPhoton(ECPU)) ip = GetInputPhoton(ECPU) ;
2078     return ip ;
2079 }
2080 
2081 
2082 std::string SEvt::DescHasInputPhoton()  // static
2083 {
2084     std::stringstream ss ;
2085     ss
2086        <<  "SEvt::DescHasInputPhoton()  "
2087        << " SEventConfig::IntegrationMode " << SEventConfig::IntegrationMode()
2088        << " SEvt::HasInputPhoton(EGPU) " << HasInputPhoton(EGPU)
2089        << " SEvt::HasInputPhoton(ECPU) " << HasInputPhoton(ECPU)
2090        << std::endl
2091        << "SEvt::Brief"
2092        << std::endl
2093        << SEvt::Brief()
2094        << std::endl
2095        << "SEvt::DescInputPhoton(EGPU)"
2096        << SEvt::DescInputPhoton(EGPU)
2097        << "SEvt::DescInputPhoton(ECPU)"
2098        << SEvt::DescInputPhoton(ECPU)
2099        << std::endl
2100        ;
2101     std::string str = ss.str();
2102     return str ;
2103 }
2104 
2105 
2106 
2107 
2108 /**
2109 SEvt::clear_genstep_vector
2110 ----------------------------
2111 
2112 1. set photon counts to zero
2113 2. clears gs and genstep vectors
2114 
2115 
2116 
2117 **/
2118 
2119 void SEvt::clear_genstep_vector()
2120 {
2121     numgenstep_collected = 0 ;
2122     numphoton_collected = 0 ;
2123     numphoton_genstep_max = 0 ;
2124 
2125     clear_genstep_vector_count += 1 ;
2126 
2127     setNumPhoton(0);
2128 
2129     gs.clear();
2130     genstep.clear();
2131     gather_done = false ;
2132 }
2133 
2134 
2135 
2136 /**
2137 SEvt::clear_output_vector
2138 ---------------------------
2139 
2140 
2141 Notice
2142 
2143 1. no hit : thats a sub-selection of the photon
2144 2. genstep+gs vectors are not cleared : they are inputs to the optical simulation, not outputs
2145 
2146 Note that most of the vectors are only used with hostside running.
2147 
2148 **/
2149 
2150 
2151 void SEvt::clear_output_vector()
2152 {
2153     clear_output_vector_count += 1 ;
2154 
2155     pho.clear();
2156     slot.clear();
2157     photon.clear();
2158     record.clear();
2159     rec.clear();
2160     seq.clear();
2161     prd.clear();
2162     tag.clear();
2163     flat.clear();
2164     simtrace.clear();
2165     aux.clear();
2166     sup.clear();
2167     g4state = nullptr ;   // avoiding stale (g4state is special, as only used for 1st event)
2168 }
2169 
2170 
2171 
2172 
2173 
2174 /**
2175 SEvt::clear_output
2176 --------------------
2177 
2178 * Called from SEvt::beginOfEvent and SEvt::endOfEvent
2179 * Clear output vectors and the fold excluding the gensteps.
2180 
2181 
2182 **/
2183 
2184 void SEvt::clear_output()
2185 {
2186     setStage(SEvt__clear_output);
2187 
2188     LOG_IF(info, LIFECYCLE) << id() << " BEFORE clear_output_vector " ;
2189 
2190     clear_output_vector();
2191 
2192     const char* keylist = "genstep" ;
2193     bool copy = false ;
2194     char delim = ',' ;
2195 
2196     topfold->clear_except(keylist, copy, delim );
2197 
2198     LOG_IF(info, LIFECYCLE) << id() << " AFTER clear_output_vector " ;
2199 
2200     LOG(LEVEL) << "]" ;
2201 }
2202 
2203 void SEvt::clear_extra()
2204 {
2205     LOG_IF(info, LIFECYCLE) << id() << " BEFORE extrafold.clear " ;
2206 
2207     extrafold->clear();
2208 
2209     LOG_IF(info, LIFECYCLE) << id() << " AFTER extrafold.clear " ;
2210 }
2211 
2212 
2213 /**
2214 SEvt::clear_genstep
2215 ---------------------
2216 
2217 * canonical call from SEvt::endOfEvent
2218 
2219 **/
2220 
2221 
2222 void SEvt::clear_genstep()
2223 {
2224     setStage(SEvt__clear_genstep);
2225     LOG_IF(info, LIFECYCLE) << id() << " BEFORE clear_genstep_vector " ;
2226 
2227     clear_genstep_vector();
2228     topfold->clear_only("genstep", false, ',');
2229 
2230     LOG_IF(info, LIFECYCLE) << id() << " AFTER clear_genstep_vector " ;
2231 }
2232 
2233 
2234 /**
2235 SEvt::setIndex
2236 ---------------
2237 
2238 index_arg
2239     zero based event index expected to come from Geant4 eventID for example
2240 
2241 index
2242     event index obtained from SEventConfig::EventIndex(index_arg)
2243     which may be offset by the OPTICKS_START_INDEX envvar
2244 
2245 **/
2246 
2247 void SEvt::setIndex(int index_arg)
2248 {
2249     assert( index_arg >= 0 );
2250     index = SEventConfig::EventIndex(index_arg) ;
2251     t_BeginOfEvent = sstamp::Now();                // moved here from the static
2252 
2253     //setRunProf_Annotated("SEvt__setIndex_" );
2254     SProf::Add("SEvt__setIndex");
2255 }
2256 void SEvt::endIndex(int index_arg)
2257 {
2258     int index_expected = SEventConfig::EventIndex(index_arg) ;
2259     bool consistent = index_expected == index ;
2260     LOG_IF(fatal, !consistent)
2261          << " index_arg " << index_arg
2262          << " index_expected " << index_expected
2263          << " index " << index
2264          << " consistent " << ( consistent ? "YES" : "NO " )
2265          ;
2266     assert( consistent );
2267     t_EndOfEvent = sstamp::Now();
2268 
2269     //setRunProf_Annotated("SEvt__endIndex_" );
2270     SProf::Add("SEvt__endIndex");
2271 }
2272 
2273 /**
2274 SEvt::getIndexArg
2275 ------------------
2276 
2277 0-based index in range 0..num-1 even when a non-zero startIndex offset is in use
2278 
2279 **/
2280 
2281 int SEvt::getIndexArg() const
2282 {
2283     return SEventConfig::EventIndexArg(index);
2284 }
2285 int SEvt::getIndex() const
2286 {
2287     return index ;
2288 }
2289 int SEvt::getIndexPresentation() const
2290 {
2291     return index == MISSING_INDEX ? -1 : index ;
2292 }
2293 
2294 std::string SEvt::descIndex() const
2295 {
2296     std::stringstream ss ;
2297     ss << "SEvt::descIndex"
2298        << " index                " << index                  << " (internal index which may be offset by OPTICKS_START_INDEX) " << std::endl
2299        << " getIndexPresentation " << getIndexPresentation() << " (internal index presentation avoidsing MISSING_INDEX " << std::endl
2300        << " getIndexArg          " << getIndexArg()          << " (external index removing any offsets, mapping back to eg Geant4 eventID " << std::endl
2301        ;
2302     std::string str = ss.str() ;
2303     return str ;
2304 }
2305 
2306 void SEvt::incrementIndex()
2307 {
2308     int index_arg = getIndexArg();  // -1 when MISSING_INDEX
2309     setIndex(index_arg + 1);
2310 }
2311 void SEvt::unsetIndex()
2312 {
2313     index = MISSING_INDEX ;
2314 }
2315 
2316 
2317 
2318 void SEvt::setInstance(int instance_)
2319 {
2320     instance = instance_ ;
2321 }
2322 int SEvt::getInstance() const
2323 {
2324     return instance ;
2325 }
2326 
2327 
2328 
2329 
2330 /**
2331 SEvt::getNumGenstepFromGenstep
2332 --------------------------------
2333 
2334 Size of the genstep vector ...
2335 
2336 caution this vector is cleared by QEvt::setGenstep
2337 after which must get the count from the genstep array
2338 
2339 **/
2340 
2341 int64_t SEvt::getNumGenstepFromGenstep() const
2342 {
2343     assert( genstep.size() == gs.size() );
2344     return genstep.size() ;
2345 }
2346 
2347 /**
2348 SEvt::getNumPhotonFromGenstep
2349 ----------------------------------
2350 
2351 Total photons by summation over all collected genstep.
2352 When collecting very large numbers of gensteps the alternative
2353 SEvt::getNumPhotonCollected is faster.
2354 
2355 **/
2356 
2357 int64_t SEvt::getNumPhotonFromGenstep() const
2358 {
2359     int64_t tot = 0 ;
2360     for(unsigned i=0 ; i < genstep.size() ; i++) tot += genstep[i].numphoton() ;
2361     return tot ;
2362 }
2363 
2364 int64_t SEvt::getNumGenstepCollected() const
2365 {
2366     return numgenstep_collected ;  // updated by addGenstep
2367 }
2368 int64_t SEvt::getNumPhotonCollected() const
2369 {
2370     return numphoton_collected ;   // updated by addGenstep
2371 }
2372 int64_t SEvt::getNumPhotonGenstepMax() const
2373 {
2374     return numphoton_genstep_max ;
2375 }
2376 
2377 
2378 
2379 /**
2380 SEvt::addGenstep
2381 ------------------
2382 
2383 The sgs summary struct of the last genstep added is returned.
2384 
2385 **/
2386 
2387 sgs SEvt::addGenstep(const NP* a)
2388 {
2389     sgs s = {} ;
2390 
2391     if( a == nullptr )
2392     {
2393         LOG(error) << " a null : low level simtrace tests like CSGSimtraceTest.sh can do this  " ;
2394         return s ;
2395     }
2396 
2397     assert( addGenstep_array == 0 );
2398     addGenstep_array++ ;
2399 
2400     int num_gs = a ? a->shape[0] : -1 ;
2401     assert( num_gs > 0 );
2402     quad6* qq = (quad6*)a->bytes();
2403     for(int i=0 ; i < num_gs ; i++) s = addGenstep(qq[i]) ;
2404 
2405     if(SEventConfig::IsRGModeSimtrace() && SFrameGenstep::HasConfigEnv()) // CEGS running
2406     {
2407 #ifdef WITH_OLD_FRAME
2408         if(frame.is_hostside_simtrace()) setFrame_HostsideSimtrace();
2409 #else
2410         if(fr.is_hostside_simtrace()) setFrame_HostsideSimtrace();
2411 #endif
2412 
2413     }
2414 
2415     return s ;
2416 }
2417 
2418 
2419 /**
2420 SEvt::addGenstep
2421 ------------------
2422 
2423 The GIDX envvar is used whilst debugging to restrict to collecting
2424 a single genstep chosen by its index.  This is implemented by
2425 always collecting all genstep labels, but only collecting
2426 actual gensteps for the enabled index.
2427 
2428 In addition to appending the quad6 to the genstep vector this also
2429 does some bookkeeping, updating::
2430 
2431 1. numphoton_collected
2432 2. numgenstep_collected
2433 
2434 Also SEvt::setNumPhoton called, configuring the sizes which
2435 get allocated later.
2436 
2437 **/
2438 
2439 
2440 sgs SEvt::addGenstep(const quad6& q_)
2441 {
2442     LOG_IF(info, LIFECYCLE) << id() ;
2443     dbg->addGenstep++ ;
2444     LOG(LEVEL) << " index " << index << " instance " << instance ;
2445 
2446     unsigned gentype = q_.gentype();
2447     unsigned matline_ = q_.matline();
2448 
2449     //bool is_input_photon_gs = OpticksGenstep_::IsInputPhoton(gentype) ;
2450     bool is_cerenkov_gs = OpticksGenstep_::IsCerenkov(gentype);
2451 
2452 
2453     /*
2454     bool input_photon_with_normal_genstep = input_photon && !is_input_photon_gs  ;
2455     LOG_IF(fatal, input_photon_with_normal_genstep)
2456         << "input_photon_with_normal_genstep " << input_photon_with_normal_genstep
2457         << " MIXING input photons with other gensteps is not allowed "
2458         << " for example avoid defining OPTICKS_INPUT_PHOTON when doing simtrace"
2459         ;
2460     assert( input_photon_with_normal_genstep == false );
2461     */
2462 
2463 
2464 
2465     int gidx = int(gs.size())  ;  // 0-based genstep label index
2466     bool enabled = GIDX == -1 || GIDX == gidx ;
2467 
2468     quad6& q = const_cast<quad6&>(q_);
2469     if(!enabled) q.set_numphoton(0);
2470     // simplify handling of disabled gensteps by simply setting numphoton to zero for them
2471 
2472 
2473     if(matline_ >= G4_INDEX_OFFSET  )
2474     {
2475         unsigned mtindex = matline_ - G4_INDEX_OFFSET ;
2476         int matline = cf ? cf->lookup_mtline(mtindex) : 0 ;
2477         // cf(SGeo) used for lookup
2478         // BUT: that just uses SSim::lookup_mtline
2479         // so SEvt should hold sim(SSim) ?
2480 
2481         bool bad_ck = is_cerenkov_gs && matline == -1 ;
2482 
2483         LOG_IF(info, bad_ck )
2484             << " is_cerenkov_gs " << ( is_cerenkov_gs ? "YES" : "NO " )
2485             << " cf " << ( cf ? "YES" : "NO " )
2486             << " bad_ck "
2487             << " matline_ " << matline_
2488             << " matline " << matline
2489             << " gentype " << gentype
2490             << " mtindex " << mtindex
2491             << " G4_INDEX_OFFSET " << G4_INDEX_OFFSET
2492             << " desc_mt "
2493             << std::endl
2494             << ( cf ? cf->desc_mt() : "no-cf" )
2495             << std::endl
2496             ;
2497 
2498         q.set_matline(matline);  // <=== THIS IS CHANGING GS BACK IN CALLERS SCOPE
2499 
2500     }
2501 
2502 
2503 #ifdef SEVT_NUMPHOTON_FROM_GENSTEP_CHECK
2504     int64_t numphoton_from_genstep = getNumPhotonFromGenstep() ; // sum numphotons from all previously collected gensteps (since last clear)
2505     assert( numphoton_from_genstep == numphoton_collected );
2506 #endif
2507 
2508     int64_t q_numphoton = q.numphoton() ;          // numphoton in this genstep
2509     if(q_numphoton > numphoton_genstep_max) numphoton_genstep_max = q_numphoton ;
2510 
2511 
2512     sgs s = {} ;                      // genstep summary struct
2513 
2514     s.index = genstep.size() ;        // 0-based genstep index since last clear
2515     s.photons = q_numphoton ;         // numphoton in this genstep
2516     s.offset = numphoton_collected ;  // sum numphotons from all previously collected gensteps (since last clear)
2517     s.gentype = q.gentype() ;
2518 
2519     gs.push_back(s) ;                 // summary labels
2520     genstep.push_back(q) ;            // actual genstep params : copied into the vector
2521 
2522     numgenstep_collected += 1 ;
2523     numphoton_collected += q_numphoton ;  // keep running total for all gensteps collected, since last clear
2524 
2525 
2526     size_t tot_photon = s.offset+s.photons ;
2527 
2528     LOG_IF(debug, enabled) << " s.desc " << s.desc() << " gidx " << gidx << " enabled " << enabled << " tot_photon " << tot_photon ;
2529 
2530     bool num_photon_changed = tot_photon != evt->num_photon ;
2531 
2532     LOG(LEVEL)
2533         << " tot_photon " << tot_photon
2534         << " evt.num_photon " << evt->num_photon
2535         << " num_photon_changed " << num_photon_changed
2536         << " gs.size " << gs.size()
2537         << " genstep.size " << genstep.size()
2538         << " numgenstep_collected " << numgenstep_collected
2539         << " numphoton_collected " << numphoton_collected
2540         << " tot_photon " << tot_photon
2541         << " s.index " << s.index
2542         << " s.photons " << s.photons
2543         << " s.offset " << s.offset
2544         << " s.gentype " << s.gentype
2545         << " s.desc " << s.desc()
2546         ;
2547 
2548     setNumPhoton(tot_photon); // still call when no change for reset hostside_running_resize_done:false
2549     return s ;
2550 }
2551 
2552 
2553 
2554 /**
2555 SEvt::setNumPhoton
2556 ----------------------
2557 
2558 NB no allocations are done here, in part because this is called on adding every genstep.
2559 This is just preparing array sizes for future array allocations.
2560 
2561 
2562 This is called from SEvt::addGenstep, updating evt.num_photon
2563 according to the additional genstep collected and evt.num_seq/tag/flat/record/rec/prd
2564 depending on the configured max which when zero will keep the counts zero.
2565 
2566 Also called by QEvt::setNumPhoton prior to device side allocations.
2567 
2568 *hostside_running_resize_done:false*
2569     signals the following SEvt::beginPhoton to call SEvt::hostside_running_resize,
2570     so allocation for hostside running happens on reaching the first photon track
2571 
2572 Note that SEvt::beginPhoton is used for hostside running only (eg U4Recorder/U4SimulateTest)
2573 so as gensteps are added with SEvt::addGenstep from the U4 scintillation
2574 and Cerenkov collection SEvt::setNumPhoton increments the totals in sevent.h:evt
2575 and sets hostside_running_resize_done:false such that at the next SEvt::beginPhoton
2576 which happens from the BeginOfTrackAction the std::vector grow as
2577 needed to accommodate the photons from the last genstep collected.
2578 
2579 **/
2580 
2581 void SEvt::setNumPhoton(size_t num_photon)
2582 {
2583     //LOG_IF(info, LIFECYCLE) << id() << " num_photon " << num_photon ;
2584     bool num_photon_allowed = num_photon <= evt->max_photon ;  // NOW THIS IS ARBITRARY AND VERY HIGH LIMIT
2585 
2586     LOG_IF(fatal, !num_photon_allowed)
2587         << " num_photon/M " << num_photon/M
2588         << " evt.max_photon/M " << evt->max_photon/M
2589         << " num_photon " << num_photon
2590         << " evt.max_photon " << evt->max_photon
2591         ;
2592     assert( num_photon_allowed );
2593 
2594     LOG_IF(info, INDEX)
2595         << " set SEvt::index to sevent::index " << index
2596         << " num_photon " << num_photon
2597         ;
2598 
2599     evt->index = index ;
2600 
2601     evt->num_photon = num_photon ;
2602     evt->num_photonlite = num_photon ;
2603 
2604     evt->num_seq    = evt->max_seq  == 1 ? evt->num_photon : 0 ;
2605     evt->num_tag    = evt->max_tag  == 1 ? evt->num_photon : 0 ;
2606     evt->num_flat   = evt->max_flat == 1 ? evt->num_photon : 0 ;
2607     evt->num_sup    = evt->max_sup   > 0 ? evt->num_photon : 0 ;
2608 
2609     evt->num_record = evt->max_record * evt->num_photon ;
2610     evt->num_rec    = evt->max_rec    * evt->num_photon ;
2611     evt->num_aux    = evt->max_aux    * evt->num_photon ;
2612     evt->num_prd    = evt->max_prd    * evt->num_photon ;
2613 
2614     LOG(LEVEL)
2615         << " evt->num_photon " << evt->num_photon
2616         << " evt->num_tag " << evt->num_tag
2617         << " evt->num_flat " << evt->num_flat
2618         ;
2619 
2620     hostside_running_resize_done = false ;
2621 }
2622 
2623 
2624 
2625 
2626 /**
2627 SEvt::setNumSimtrace
2628 ---------------------
2629 
2630 **/
2631 
2632 void SEvt::setNumSimtrace(size_t num_simtrace)
2633 {
2634     bool num_simtrace_allowed = num_simtrace <= evt->max_simtrace ;
2635     LOG_IF(fatal, !num_simtrace_allowed) << " num_simtrace " << num_simtrace << " evt.max_simtrace " << evt->max_simtrace ;
2636     assert( num_simtrace_allowed );
2637     LOG(LEVEL) << " num_simtrace " << num_simtrace ;
2638 
2639     evt->num_simtrace = num_simtrace ;
2640 }
2641 
2642 
2643 /**
2644 SEvt::hostside_running_resize
2645 -------------------------------
2646 
2647 Canonically called from SEvt::beginPhoton  (also SEvt::setFrame_HostsideSimtrace)
2648 
2649 **/
2650 
2651 void SEvt::hostside_running_resize()
2652 {
2653     bool is_self_provider = isSelfProvider() ;
2654     LOG_IF(fatal, is_self_provider == false ) << " NOT-is_self_provider " << descProvider() ;
2655     LOG(LEVEL)
2656         << " is_self_provider " << is_self_provider
2657         << " hostside_running_resize_done " << hostside_running_resize_done
2658         ;
2659 
2660     assert( hostside_running_resize_done == false );
2661     assert( is_self_provider );
2662 
2663     hostside_running_resize_done = true ;
2664     hostside_running_resize_();
2665 
2666     LOG(LEVEL)
2667         << " is_self_provider " << is_self_provider
2668         << std::endl
2669         << evt->desc()
2670         ;
2671 
2672 }
2673 
2674 /**
2675 SEvt::hostside_running_resize_
2676 --------------------------------
2677 
2678 Resize the hostside std::vectors according to the sizes from sevent.h:evt
2679 and update evt array pointers to follow around the std::vectors as they get reallocated.
2680 
2681 This makes the hostside sevent.h:evt environment mimic the deviceside environment
2682 even though deviceside uses device buffers and hostside uses std::vectors.
2683 
2684 Notice how the same sevent.h struct that holds deviceside pointers
2685 is being using to hold the hostside vector data pointers.
2686 
2687 
2688 +-----------+-----------------+
2689 | vector    |   evt->         |
2690 +===========+=================+
2691 | pho       |   num_photon    |
2692 +-----------+-----------------+
2693 | slot      |   num_photon    |
2694 +-----------+-----------------+
2695 | photon    |   num_photon    |
2696 +-----------+-----------------+
2697 | record    |   num_record    |
2698 +-----------+-----------------+
2699 | rec       |   num_rec       |
2700 +-----------+-----------------+
2701 | aux       |   num_aux       |
2702 +-----------+-----------------+
2703 | seq       |   num_seq       |
2704 +-----------+-----------------+
2705 | prd       |   num_prd       |
2706 +-----------+-----------------+
2707 | tag       |   num_tag       |
2708 +-----------+-----------------+
2709 | flat      |   num_flat      |
2710 +-----------+-----------------+
2711 | simtrace  |   num_simtrace  |
2712 +-----------+-----------------+
2713 
2714 Vectors are disabled by some of the above *num*
2715 being configured to zero via SEventConfig.
2716 
2717 **/
2718 
2719 void SEvt::hostside_running_resize_()
2720 {
2721     LOG(LEVEL)
2722         << " photon.size " << photon.size()
2723         << " photon.size/M " << photon.size()/M
2724         << " => "
2725         << " evt.num_photon " << evt->num_photon
2726         << " evt.num_photon/M " << evt->num_photon/M
2727         ;
2728 
2729     bool shrink = true ;
2730 
2731     if(evt->num_photon > 0)        // no device side equivalent array
2732     {
2733         pho.resize(  evt->num_photon );
2734         if(shrink) pho.shrink_to_fit();
2735     }
2736     if(evt->num_photon > 0)       // no device side equivalent array
2737     {
2738         slot.resize( evt->num_photon );
2739         if(shrink) slot.shrink_to_fit();
2740     }
2741     // pho and slot look essential for U4Recorder bookkeeping
2742 
2743 
2744     if(evt->num_photon > 0)
2745     {
2746         photon.resize(evt->num_photon);
2747         if(shrink) photon.shrink_to_fit();
2748         evt->photon = photon.data() ;
2749     }
2750     if(evt->num_record > 0)
2751     {
2752         record.resize(evt->num_record);
2753         if(shrink) record.shrink_to_fit();
2754         evt->record = record.data() ;
2755     }
2756     if(evt->num_rec > 0)
2757     {
2758         rec.resize(evt->num_rec);
2759         if(shrink) rec.shrink_to_fit();
2760         evt->rec = rec.data() ;
2761     }
2762     if(evt->num_aux > 0)
2763     {
2764         aux.resize(evt->num_aux);
2765         if(shrink) aux.shrink_to_fit();
2766         evt->aux = aux.data() ;
2767     }
2768     if(evt->num_sup > 0)
2769     {
2770         sup.resize(evt->num_sup);
2771         if(shrink) sup.shrink_to_fit();
2772         evt->sup = sup.data() ;
2773     }
2774     if(evt->num_seq > 0)
2775     {
2776         seq.resize(evt->num_seq);
2777         if(shrink) seq.shrink_to_fit();
2778         evt->seq = seq.data() ;
2779     }
2780     if(evt->num_prd > 0)
2781     {
2782         prd.resize(evt->num_prd);
2783         if(shrink) prd.shrink_to_fit();
2784         evt->prd = prd.data() ;
2785     }
2786     if(evt->num_tag > 0)
2787     {
2788         tag.resize(evt->num_tag);
2789         if(shrink) tag.shrink_to_fit();
2790         evt->tag = tag.data() ;
2791     }
2792     if(evt->num_flat > 0)
2793     {
2794         flat.resize(evt->num_flat);
2795         if(shrink) flat.shrink_to_fit();
2796         evt->flat = flat.data() ;
2797     }
2798     if(evt->num_simtrace > 0)
2799     {
2800         simtrace.resize(evt->num_simtrace);
2801         if(shrink) simtrace.shrink_to_fit();
2802         evt->simtrace = simtrace.data() ;
2803     }
2804 }
2805 
2806 /**
2807 SEvt::get_gs
2808 --------------
2809 
2810 Lookup sgs genstep label corresponding to spho photon label
2811 
2812 **/
2813 
2814 const sgs& SEvt::get_gs(const spho& label) const
2815 {
2816     assert( label.gs < int(gs.size()) );
2817     const sgs& _gs =  gs[label.gs] ;
2818     return _gs ;
2819 }
2820 
2821 /**
2822 SEvt::get_genflag
2823 -------------------
2824 
2825 This is called for example from SEvt::beginPhoton
2826 to become the "TO" "SI" "CK" at the start of photon histories.
2827 
2828 1. lookup sgs genstep label corresponding to the spho photon label
2829 2. convert the sgs gentype into the corresponding photon genflag which must be
2830    one of CERENKOV, SCINTILLATION or TORCH
2831 
2832    * What about input photons ? Presumably they are TORCH ?
2833 
2834 **/
2835 
2836 unsigned SEvt::get_genflag(const spho& label) const
2837 {
2838     const sgs& _gs = get_gs(label);
2839     int gentype = _gs.gentype ;
2840     unsigned genflag = OpticksGenstep_::GenstepToPhotonFlag(gentype);
2841     assert( genflag == CERENKOV || genflag == SCINTILLATION || genflag == TORCH );
2842     return genflag ;
2843 }
2844 
2845 
2846 /**
2847 SEvt::beginPhoton : only used for hostside running eg with G4CXTest
2848 ---------------------------------------------------------------------------
2849 
2850 Canonically invoked from tail of U4Recorder::PreUserTrackingAction_Optical
2851 
2852 0. for hostside_running_resize_done:false calls hostside_running_resize which resizes vectors
2853    and updates evt pointers : via the toggle this only gets done when reaching first photon of the event
2854 1. determine genflag SI/CK/TO, via lookups in the sgs corresponding to the spho label
2855 2. zeros current_ctx and slot[label.id] (the "recording head" )
2856 3. start filling current_ctx.p sphoton with set_idx and set_flag
2857 
2858 **/
2859 void SEvt::beginPhoton(const spho& label)
2860 {
2861     if(!hostside_running_resize_done) hostside_running_resize();
2862 
2863     dbg->beginPhoton++ ;
2864     LOG(LEVEL) ;
2865     LOG(LEVEL) << label.desc() ;
2866 
2867     unsigned idx = label.id ;  // WIP: label.id is int, should be unsigned
2868 
2869     bool in_range = idx < pho.size() ;
2870     LOG_IF(error, !in_range)
2871         << " not in_range "
2872         << " idx " << idx
2873         << " pho.size  " << pho.size()
2874         << " label " << label.desc()
2875         ;
2876 
2877     if(!in_range) std::cerr
2878         << "SEvt::beginPhoton FATAL not in_range"
2879         << " idx " << idx
2880         << " pho.size  " << pho.size()
2881         << " label " << label.desc()
2882         << std::endl
2883         ;
2884 
2885     assert(in_range);
2886 
2887     unsigned genflag = get_genflag(label);
2888 
2889     pho[idx] = label ;        // slot in the photon label
2890     slot[idx] = 0 ;           // slot/bounce incremented only at tail of SEvt::pointPhoton
2891 
2892     current_pho = label ;
2893     current_prd.zero() ;
2894 
2895     sctx& ctx = current_ctx ;
2896     ctx.zero();
2897 
2898 #ifndef PRODUCTION
2899 #ifdef WITH_SUP
2900     quadx6& xsup = (quadx6&)ctx.sup ;
2901     xsup.q0.w.x = sstamp::Now();
2902 #endif
2903 #endif
2904 
2905     ctx.idx = idx ;
2906     ctx.evt = evt ;
2907     ctx.prd = &current_prd ;   // current_prd is populated by InstrumentedG4OpBoundaryProcess::PostStepDoIt
2908 
2909     ctx.p.index = idx ;
2910     ctx.p.set_flag(genflag);
2911 
2912     bool flagmask_one_bit = ctx.p.flagmask_count() == 1  ;
2913     LOG_IF(error, !flagmask_one_bit )
2914         << " not flagmask_one_bit "
2915         << " : should only be a single bit in the flagmask at this juncture "
2916         << " label " << label.desc()
2917          ;
2918 
2919     assert( flagmask_one_bit );
2920 }
2921 
2922 unsigned SEvt::getCurrentPhotonIdx() const { return current_pho.id ; }
2923 
2924 
2925 /**
2926 SEvt::resumePhoton
2927 ---------------------
2928 
2929 Canonically called from tail of U4Recorder::PreUserTrackingAction_Optical
2930 following a FastSim->SlowSim transition.
2931 
2932 Transitions between Geant4 "SlowSim" and FastSim result in fSuspend track status
2933 and the history of a single photon being split across multiple calls to
2934 U4RecorderTest::PreUserTrackingAction (U4Recorder::PreUserTrackingAction_Optical)
2935 
2936 Need to join up the histories, a bit like rjoin does for reemission.
2937 But with FastSim/SlowSim the G4Track are the same pointers, unlike
2938 with reemission where the G4Track pointers differ.
2939 
2940 BUT reemission photons will also go thru FastSim/SlowSim transitions,
2941 so need separate approach than the label.gn (photon generation) handling of reemission ?
2942 
2943 **/
2944 
2945 void SEvt::resumePhoton(const spho& label)
2946 {
2947     dbg->resumePhoton++ ;
2948     LOG(LEVEL);
2949     LOG(LEVEL) << label.desc() ;
2950 
2951     unsigned idx = label.id ;
2952     bool idx_expect = idx < pho.size() ;
2953     if(!idx_expect ) std::raise(SIGINT) ;
2954     assert( idx_expect );
2955 }
2956 
2957 
2958 /**
2959 SEvt::rjoin_resumePhoton
2960 -------------------------
2961 
2962 Invoked from U4Recorder::PreUserTrackingAction_Optical for
2963 reemission photons that subsequently transition from FastSim
2964 back to SlowSim.
2965 
2966 BUT as the FastSim/SlowSim transition happens inside PMT
2967 will never need to do real rjoin history rewriting  ?
2968 
2969 **/
2970 
2971 void SEvt::rjoin_resumePhoton(const spho& label)
2972 {
2973     dbg->rjoin_resumePhoton++ ;
2974     LOG(LEVEL);
2975     LOG(LEVEL) << label.desc() ;
2976 
2977     unsigned idx = label.id ;
2978     bool idx_expect = idx < pho.size() ;
2979     if(!idx_expect ) std::raise(SIGINT) ;
2980     assert( idx_expect );
2981 }
2982 
2983 
2984 /**
2985 SEvt::rjoinPhoton : only used for hostside running
2986 ---------------------------------------------------
2987 
2988 Called from tail of U4Recorder::PreUserTrackingAction_Optical for G4Track with
2989 spho label indicating a reemission generation greater than zero.
2990 
2991 HUH: BUT THAT MEANS THIS WILL BE CALLED FOR EVERY SUBSEQUENT
2992 STEP OF THE REEMISSION PHOTON NOT JUST THE FIRST THAT NEEDS REJOINING ?
2993 
2994 * SUSPECT THAT WILL GET REPEATED "RE RE RE RE" ?
2995 * MAYBE NEED ANOTHER MARK TO SAY THE REJOIN WAS DONE AND DOENST NEED RE-rjoin ?
2996 
2997 Note that this will mostly be called for photons that originate from
2998 scintillation gensteps BUT it will also happen for Cerenkov (and Torch) genstep
2999 generated photons within a scintillator due to reemission.
3000 
3001 HMM: could directly change photon[idx] via ref ?
3002 But are here taking a copy to current_photon
3003 and relying on copyback at SEvt::finalPhoton
3004 
3005 **/
3006 void SEvt::rjoinPhoton(const spho& label)
3007 {
3008     dbg->rjoinPhoton++ ;
3009     LOG(LEVEL);
3010     LOG(LEVEL) << label.desc() ;
3011 
3012     unsigned idx = label.id ;
3013     bool in_range = idx < pho.size() ;
3014     LOG_IF(fatal, !in_range )
3015          << " NOT-in_range "
3016          << " idx " << idx
3017          << " pho.size " << pho.size()
3018          ;
3019     assert( in_range );
3020     if(!in_range) std::raise(SIGINT);
3021 
3022     // check labels of parent and child are as expected
3023     const spho& parent_label = pho[idx];
3024 
3025     bool label_expect = label.isSameLineage( parent_label) && label.gen() == parent_label.gen() + 1  ;
3026     if(!label_expect) std::raise(SIGINT);
3027     assert( label_expect );
3028 
3029 
3030     // every G4Track has its own label but for reemission the lineage
3031     // through the generations is reflected by multiple such track labels
3032     // all having the same lineage (label.gs,label.ix,label.id) but different
3033     // reemission generations
3034 
3035 
3036     const sgs& _gs = get_gs(label);
3037     bool expected_gentype = OpticksGenstep_::IsExpected(_gs.gentype);  // SI/CK/TO
3038     if(!expected_gentype) std::raise(SIGINT);
3039     assert(expected_gentype);
3040     // NB: within scintillator, photons of any gentype may undergo reemission
3041 
3042     const sphoton& parent_photon = photon[idx] ;
3043     unsigned parent_idx = parent_photon.index ;
3044     bool parent_idx_expect = parent_idx == idx  ;
3045     assert( parent_idx_expect );
3046     if(!parent_idx_expect) std::raise(SIGINT);
3047 
3048 
3049     // replace pho[idx] and current_pho with the new generation label
3050     pho[idx] = label ;
3051     current_pho = label ;
3052 
3053 
3054     // RE-WRITE HISTORY : CHANGING BULK_ABSORB INTO BULK_REEMIT
3055 
3056     if( evt->photon )
3057     {
3058         current_ctx.p = photon[idx] ;
3059         sphoton& current_photon = current_ctx.p ;
3060 
3061         rjoinPhotonCheck(current_photon);
3062         current_photon.flagmask &= ~BULK_ABSORB  ; // scrub BULK_ABSORB from flagmask
3063         current_photon.set_flag(BULK_REEMIT) ;     // gets OR-ed into flagmask
3064     }
3065 
3066 #ifndef PRODUCTION
3067     const int& bounce = slot[idx] ; assert( bounce > 0 );
3068     int prior = bounce - 1 ;
3069     //int num_slots = evt->max_bounce + 1  ;
3070     int num_slots = SEventConfig::RecordLimit()  ;
3071     // at truncation point and beyond cannot compare or do rejoin fixup
3072     if( evt->seq && prior < num_slots  )
3073     {
3074         current_ctx.seq = seq[idx] ;
3075         sseq& current_seq = current_ctx.seq ;
3076 
3077         unsigned seq_flag = current_seq.get_flag(prior);
3078         rjoinSeqCheck(seq_flag);
3079         current_seq.set_flag(prior, BULK_REEMIT);
3080     }
3081 
3082     // at truncation point and beyond cannot compare or do rejoin fixup
3083     if( evt->record && prior < evt->max_record )
3084     {
3085         sphoton& rjoin_record = evt->record[evt->max_record*idx+prior]  ;
3086         rjoinRecordCheck(rjoin_record, current_ctx.p);
3087         rjoin_record.flagmask &= ~BULK_ABSORB ; // scrub BULK_ABSORB from flagmask
3088         rjoin_record.set_flag(BULK_REEMIT) ;
3089     }
3090 #endif
3091 
3092 
3093     // NOT: rec  (compressed record) are not handled
3094     //      but no longer using that as decided full recording
3095     //      is a debugging only activity so no point in going
3096     //      to great lengths squeezing memory usage for something that
3097     //      is just used while debugging
3098 }
3099 
3100 
3101 
3102 void SEvt::rjoinRecordCheck(const sphoton& rj, const sphoton& ph  ) const
3103 {
3104     assert( rj.get_index() == ph.get_index() );
3105     uint64_t idx = rj.get_index();
3106     bool d12match = sphoton::digest_match( rj, ph, 12 );
3107     if(!d12match) dbg->d12match_fail++ ;
3108     if(!d12match) ComparePhotonDump(rj, ph) ;
3109     if(!d12match) std::cout
3110         << " idx " << idx
3111         << " slot[idx] " << slot[idx]
3112         << " evt.max_record " << evt->max_record
3113         << " d12match " << ( d12match ? "YES" : "NO" ) << std::endl
3114         ;
3115     assert( d12match );
3116 }
3117 
3118 void SEvt::ComparePhotonDump(const sphoton& a, const sphoton& b )  // static
3119 {
3120     unsigned a_flag = a.flag() ;
3121     unsigned b_flag = a.flag() ;
3122     std::cout
3123         << " a.flag "  << OpticksPhoton::Flag(a_flag)
3124         << std::endl
3125         << a.desc()
3126         << std::endl
3127         << " b.flag "  << OpticksPhoton::Flag(b_flag)
3128         << std::endl
3129         << b.desc()
3130         << std::endl
3131         ;
3132 }
3133 
3134 
3135 
3136 /**
3137 SEvt::rjoinPhotonCheck
3138 ------------------------
3139 
3140 Would expect all rejoin to have BULK_ABSORB, but with
3141 very artifical single volume of Scintillator geometry keep getting photons
3142 hitting world boundary giving MISS.
3143 
3144 What about perhaps reemission immediately after reemission ? Nope, I think
3145 a new point would always be minted so the current_photon flag should
3146 never be BULK_REEMIT when rjoin runs.
3147 
3148 **/
3149 
3150 void SEvt::rjoinPhotonCheck(const sphoton& ph ) const
3151 {
3152     dbg->rjoinPhotonCheck++ ;
3153 
3154     unsigned flag = ph.flag();
3155     unsigned flagmask = ph.flagmask ;
3156 
3157     bool flag_AB = flag == BULK_ABSORB ;
3158     bool flag_MI = flag == MISS ;
3159     bool flag_xor = flag_AB ^ flag_MI ;
3160 
3161     if(flag_AB) dbg->rjoinPhotonCheck_flag_AB++ ;
3162     if(flag_MI) dbg->rjoinPhotonCheck_flag_MI++ ;
3163     if(flag_xor) dbg->rjoinPhotonCheck_flag_xor++ ;
3164 
3165     bool flagmask_AB = flagmask & BULK_ABSORB  ;
3166     bool flagmask_MI = flagmask & MISS ;
3167     bool flagmask_or = flagmask_AB | flagmask_MI ;
3168 
3169     if(flagmask_AB) dbg->rjoinPhotonCheck_flagmask_AB++ ;
3170     if(flagmask_MI) dbg->rjoinPhotonCheck_flagmask_MI++ ;
3171     if(flagmask_or) dbg->rjoinPhotonCheck_flagmask_or++ ;
3172 
3173     bool expect = flag_xor && flagmask_or ;
3174     LOG_IF(fatal, !expect)
3175         << "rjoinPhotonCheck : unexpected flag/flagmask"
3176         << " flag_AB " << flag_AB
3177         << " flag_MI " << flag_MI
3178         << " flag_xor " << flag_xor
3179         << " flagmask_AB " << flagmask_AB
3180         << " flagmask_MI " << flagmask_MI
3181         << " flagmask_or " << flagmask_or
3182         << ph.descFlag()
3183         << std::endl
3184         << ph.desc()
3185         << std::endl
3186         ;
3187     assert(expect);
3188 
3189 }
3190 
3191 void SEvt::rjoinSeqCheck(unsigned seq_flag) const
3192 {
3193     dbg->rjoinSeqCheck++ ;
3194 
3195     bool flag_AB = seq_flag == BULK_ABSORB ;
3196     bool flag_MI = seq_flag == MISS ;
3197     bool flag_xor = flag_AB ^ flag_MI ;
3198 
3199     if(flag_AB)  dbg->rjoinSeqCheck_flag_AB++ ;
3200     if(flag_MI)  dbg->rjoinSeqCheck_flag_MI++ ;
3201     if(flag_xor) dbg->rjoinSeqCheck_flag_xor++ ;
3202 
3203     LOG_IF(fatal, !flag_xor) << " flag_xor FAIL " << OpticksPhoton::Abbrev(seq_flag) << std::endl ;
3204     assert( flag_xor );
3205 }
3206 
3207 /**
3208 SEvt::pointPhoton : only used for hostside running
3209 ---------------------------------------------------
3210 
3211 Invoked from U4Recorder::UserSteppingAction_Optical to cause the
3212 current photon to be recorded into record vector.
3213 
3214 The pointPhoton and finalPhoton methods need to do the hostside equivalent of
3215 what CSGOptiX/CSGOptiX7.cu:simulate does device side, so have setup the environment to match:
3216 
3217 ctx.point looks like it is populating sevent arrays, but actually are also populating
3218 the vectors thanks to setting of the sevent pointers in SEvt::hostside_running_resize
3219 so that the pointers follow around the vectors as they get reallocated.
3220 
3221 TODO: truncation : bounce < max_bounce
3222 at truncation ctx.point/ctx.trace stop writing anything but bounce keeps incrementing
3223 **/
3224 
3225 void SEvt::pointPhoton(const spho& label)
3226 {
3227     dbg->pointPhoton++ ;
3228 
3229     assert( label.isSameLineage(current_pho) );
3230     unsigned idx = label.id ;
3231     sctx& ctx = current_ctx ;
3232 
3233 #ifndef PRODUCTION
3234     t_PenultimatePoint = t_LastPoint ;
3235     t_LastPoint = sstamp::Now() ;
3236     quad4& aux = current_ctx.aux ;
3237     quadx4& auxx = (quadx4&)aux ;
3238     auxx.q3.w.x = t_LastPoint ;  // shoe-horn uint64_t time stamp into aux
3239 #endif
3240     assert( ctx.idx == idx );
3241     int& bounce = slot[idx] ;
3242 
3243     bool first_point = bounce == 0 ;
3244     bool first_flag = ctx.p.flagmask_count() == 1 ;
3245     bool fake_first = first_flag == true && first_point == false ;
3246     LOG_IF(LEVEL, fake_first)
3247         << " fake_first detected, bounce: " << bounce
3248         << " EARLY EXIT pointPhoton"
3249         << " this happens when the POST of the first step is fake "
3250         << " resulting in GENFLAG being repeated as 2nd step first_flag still true "
3251         ;
3252     if(fake_first) return ;
3253 
3254 
3255 #ifndef PRODUCTION
3256     if(first_point == false) ctx.trace(bounce);
3257     ctx.point(bounce);  // sseq::add_nibble the current photon flag
3258 #endif
3259 
3260     LOG(LEVEL)
3261         << "(" << std::setw(5) << label.id
3262         << "," << std::setw(2) << bounce
3263         << ") "
3264         << std::setw(2) << OpticksPhoton::Abbrev(ctx.p.flag())
3265 #ifndef PRODUCTION
3266         << ctx.seq.desc_seqhis()
3267 #endif
3268         ;
3269 
3270 
3271     LOG(LEVEL)
3272         << " idx " << idx
3273         << " bounce " << bounce
3274         << " first_point " << first_point
3275         << " evt.max_record " << evt->max_record
3276         << " evt.max_rec    " << evt->max_rec
3277         << " evt.max_seq    " << evt->max_seq
3278         << " evt.max_prd    " << evt->max_prd
3279         << " evt.max_tag    " << evt->max_tag
3280         << " evt.max_flat    " << evt->max_flat
3281         << " label.desc " << label.desc()
3282 #ifndef PRODUCTION
3283         << " ctx.seq.desc_seqhis " << ctx.seq.desc_seqhis() ;
3284 #endif
3285         ;
3286 
3287     bounce += 1 ;   // increments slot array, by reference
3288 }
3289 
3290 /**
3291 SEvt::addTag
3292 -------------
3293 
3294 HMM: this needs to be called following every random consumption ... see U4Random::flat
3295 
3296 **/
3297 
3298 bool SEvt::PIDX_ENABLED = ssys::getenvbool("SEvt__PIDX_ENABLED") ;
3299 
3300 #ifndef PRODUCTION
3301 void SEvt::addTag(unsigned tag, float flat)
3302 {
3303     if(evt->tag == nullptr) return  ;
3304     stagr& tagr = current_ctx.tagr ;
3305     unsigned idx = current_ctx.idx ;
3306 
3307     LOG_IF(info, PIDX_ENABLED && int(idx) == PIDX )
3308         << " idx " << idx
3309         << " PIDX " << PIDX
3310         << " tag " << tag
3311         << " flat " << flat
3312         << " evt.tag " << evt->tag
3313         << " tagr.slot " << tagr.slot
3314         ;
3315 
3316     tagr.add(tag,flat)  ;
3317 
3318 
3319     if(random)
3320     {
3321         int flat_cursor = random->getFlatCursor();
3322         assert( flat_cursor > -1 );
3323         double flat_prior = random->getFlatPrior();
3324         bool cursor_slot_match = unsigned(flat_cursor) == tagr.slot ;
3325         LOG_IF(error, !cursor_slot_match)
3326             << " idx " << idx
3327             << " cursor_slot_match " << cursor_slot_match
3328             << " flat " << flat
3329             << " tagr.slot " << tagr.slot
3330             << " ( from SRandom "
3331             << " flat_prior " << flat_prior
3332             << " flat_cursor " << flat_cursor
3333             << "  ) "
3334             << std::endl
3335             << " MISMATCH MEANS ONE OR MORE PRIOR CONSUMPTIONS WERE NOT TAGGED "
3336             ;
3337         assert( cursor_slot_match );
3338     }
3339 }
3340 
3341 int SEvt::getTagSlot() const
3342 {
3343     if(evt->tag == nullptr) return -1 ;
3344     const stagr& tagr = current_ctx.tagr ;
3345     return tagr.slot ;
3346 }
3347 #endif
3348 
3349 
3350 
3351 /**
3352 SEvt::finalPhoton : only used for hostside running
3353 ------------------------------------------------------
3354 
3355 Canonically called from U4Recorder::PostUserTrackingAction_Optical
3356 
3357 1. asserts label is same lineage as current_pho
3358 2. using ProcessHits EPH enum value may change final photon step flags
3359    of SURFACE_DETECT(SD) to EFFICIENCY_COLLECT(EC) or EFFICIENCY_CULL(EX)
3360 
3361    Q: Does SD need to be scrubbed from flagmask to correspond to A side ?
3362 
3363 3. calls sctx::end on ctx, copying ctx.seq into evt->seq[idx]
3364 4. copies ctx.p into evt->photon[idx]
3365 
3366 **/
3367 void SEvt::finalPhoton(const spho& label)
3368 {
3369     dbg->finalPhoton++ ;
3370     LOG(LEVEL) << label.desc() ;
3371     assert( label.isSameLineage(current_pho) );
3372 
3373     unsigned idx = label.id ;
3374     sctx& ctx = current_ctx ;
3375     assert( ctx.idx == idx );
3376 
3377 #ifndef PRODUCTION
3378 #ifdef WITH_SUP
3379     quadx6& xsup = (quadx6&)ctx.sup ;
3380     xsup.q0.w.y = sstamp::Now();
3381     xsup.q1.w.x = t_PenultimatePoint ;
3382     xsup.q1.w.y = t_LastPoint ;
3383 #endif
3384     ctx.end();   // copies {seq,sup} into evt->{seq,sup}[idx] (and tag, flat when DEBUG_TAG)
3385 #endif
3386 
3387     evt->photon[idx] = ctx.p ;   // HUH: why not do this in ctx.end ?
3388 }
3389 
3390 
3391 
3392 
3393 
3394 /**
3395 SEvt::AddProcessHitsStamp
3396 ---------------------------
3397 
3398 As ProcessHits may be called multiple
3399 times for each photon this records the
3400 timestamp range of those calls and the count.
3401 
3402 Note that this relies on being zeroed for each photon.
3403 
3404 Also note that the only thing specific to "ProcessHits"
3405 is the convention of where to store the stamp range. It
3406 is up to the user to call this from the right place.
3407 
3408 **/
3409 
3410 void SEvt::AddProcessHitsStamp(int idx, int p) // static
3411 {
3412     if(Exists(idx)) Get(idx)->addProcessHitsStamp(p) ;
3413 }
3414 /**
3415 SEvt::addProcessHitsStamp
3416 ---------------------------
3417 
3418 See squadx.h and search for WITH_SUP to find where sup is populated.  Also see sevt.py for usage.
3419 
3420 +--------------------------------------+----------------------------------------------------------------------------------+
3421 | sctx::sup fields                     |                                                                                  |
3422 +==================+===================+==================================================================================+
3423 |  q0.w.x          |  q0.w.y           | q0.w.x SEvt::beginPhoton q0.w.y SEvt::finalPhoton                                |
3424 +------------------+-------------------+----------------------------------------------------------------------------------+
3425 |  q1.w.x          |  q1.w.y           | q1.w.x SEvt::finalPhoton t_PenultimatePoint q1.w.y SEvt::finalPhoton t_LastPoint |
3426 +------------------+-------------------+----------------------------------------------------------------------------------+
3427 |  q2.w.x          |  q2.w.y           | time range from SEvt::addProcessHitsStamp(0)                                     |
3428 +---------+--------+--------+----------+----------------------------------------------------------------------------------+
3429 |  q3.u.x | q3.u.y | q3.u.x | q3.u.w   | .x call count from SEvt::addProcessHitsStamp(0)                                  |
3430 +---------+--------+--------+----------+----------------------------------------------------------------------------------+
3431 |  q4.w.x          |  q4.w.y           | time range from SEvt::addProcessHitsStamp(1)                                     |
3432 +---------+--------+-------------------+----------------------------------------------------------------------------------+
3433 |  q5.u.x | q5.u.y | q5.u.z | q5.u.w   | .x call count frmo SEvt::addProcessHitsStamp(1)                                  |
3434 +---------+--------+--------+----------+----------------------------------------------------------------------------------+
3435 
3436 Use BP=SEvt::addProcessHitsStamp to find where time stamps are coming. Currently none.
3437 
3438 **/
3439 void SEvt::addProcessHitsStamp(int p)
3440 {
3441     assert( p > -1 );
3442 
3443 #ifndef PRODUCTION
3444 #ifdef WITH_SUP
3445     uint64_t now = sstamp::Now();
3446 
3447     quad6&  sup  = current_ctx.sup ;
3448     quadx6& xsup = (quadx6&)current_ctx.sup ;
3449 
3450     uint64_t* h0 = nullptr ;
3451     uint64_t* h1 = nullptr ;
3452     unsigned* hc = nullptr ;
3453 
3454     switch(p)
3455     {
3456        case 0: { h0 = &xsup.q2.w.x ; h1 = &xsup.q2.w.y ; hc = &sup.q3.u.x ;} ; break ;
3457        case 1: { h0 = &xsup.q4.w.x ; h1 = &xsup.q4.w.y ; hc = &sup.q5.u.x ;} ; break ;
3458     }
3459     assert( hc && h0 && h1 );
3460 
3461     *hc += 1 ;
3462 
3463     if(*h0 == 0)
3464     {
3465         *h0 = now ;
3466     }
3467     else if(*h1 == 0)
3468     {
3469         *h1 = now ;
3470     }
3471     else if( *h0 > 0 && *h1 > 0 )
3472     {
3473         *h1 = now ;
3474     }
3475 
3476     /*
3477     std::cout
3478         << "SEvt::addProcessHitsStamp"
3479         << " p " << p
3480         << " now " << now
3481         << " h0 " << *h0
3482         << " h1 " << *h1
3483         << " hc " << *hc
3484         << std::endl
3485         ;
3486     */
3487 #endif
3488 #endif
3489 }
3490 
3491 
3492 
3493 void SEvt::checkPhotonLineage(const spho& label) const
3494 {
3495     assert( label.isSameLineage(current_pho) );
3496 }
3497 
3498 
3499 
3500 ////////////////////////////////////////////////////////////////////////////////////////////
3501 ////////////////////////////////////////////////////////////////////////////////////////////
3502 ///////// below methods handle gathering arrays and persisting, not array content //////////
3503 ////////////////////////////////////////////////////////////////////////////////////////////
3504 ////////////////////////////////////////////////////////////////////////////////////////////
3505 
3506 
3507 NP* SEvt::gatherPho() const {  return NPX::ArrayFromData<int>( (int*)pho.data(), int(pho.size()), 4 ); }
3508 NP* SEvt::gatherGS() const {   return NPX::ArrayFromData<int>( (int*)gs.data(),  int(gs.size()), 4 );  }
3509 
3510 
3511 /**
3512 SEvt::gatherGenstep
3513 --------------------
3514 
3515 As gensteps always originate on CPU its kinda silly to call access gather-ing.
3516 
3517 **/
3518 
3519 NP* SEvt::gatherGenstep() const { return makeGenstepArrayFromVector() ; }
3520 
3521 
3522 quad6* SEvt::getGenstepVecData() const
3523 {
3524     return genstep.size() == 0 ? nullptr : (quad6*)genstep.data();
3525 }
3526 int SEvt::getGenstepVecSize() const
3527 {
3528     return genstep.size();
3529 }
3530 
3531 
3532 /**
3533 SEvt::makeGenstepArrayFromVector (formerly misnamed getGenstepArray)
3534 ----------------------------------------------------------------------
3535 
3536 Makes NP array from contents of genstep vector
3537 
3538 **/
3539 
3540 
3541 NP* SEvt::makeGenstepArrayFromVector() const
3542 {
3543     return NPX::ArrayFromData<float>( (float*)genstep.data(), int(genstep.size()), 6, 4 ) ;
3544 }
3545 
3546 std::string SEvt::descGenstepArrayFromVector() const
3547 {
3548     std::stringstream ss ;
3549     ss << "SEvt::descGenstepArrayFromVector genstep.size " << genstep.size() << "\n" ;
3550     std::string str = ss.str() ;
3551     return str ;
3552 }
3553 
3554 
3555 
3556 
3557 
3558 bool SEvt::haveGenstepVec() const { return genstep.size() > 0 ; }
3559 
3560 /**
3561 SEvt::gatherPhoton
3562 --------------------
3563 
3564 NB these gatherNAME methods are not used for GPU running
3565 
3566 1. allocates with NP::Make
3567 2. populates by reading from photon vector using the sevent.h pointer
3568    that makes things follow the on device approach
3569 
3570 NB this means the array holds an independent copy of the vector data,
3571 as do all(?) the gather methods.
3572 
3573 **/
3574 
3575 NP* SEvt::gatherPhoton() const
3576 {
3577     if( evt->photon == nullptr ) return nullptr ;
3578     NP* p = makePhoton();
3579     p->read2( (float*)evt->photon );
3580     return p ;
3581 }
3582 
3583 NP* SEvt::gatherRecord() const
3584 {
3585     if( evt->record == nullptr ) return nullptr ;
3586     NP* r = makeRecord();
3587     r->read2( (float*)evt->record );
3588     return r ;
3589 }
3590 NP* SEvt::gatherRec() const
3591 {
3592     if( evt->rec == nullptr ) return nullptr ;
3593     NP* r = makeRec();
3594     r->read2( (short*)evt->rec );
3595     return r ;
3596 }
3597 NP* SEvt::gatherAux() const
3598 {
3599     if( evt->aux == nullptr ) return nullptr ;
3600     NP* r = makeAux();
3601     r->read2( (float*)evt->aux );
3602     return r ;
3603 }
3604 NP* SEvt::gatherSup() const
3605 {
3606     if( evt->sup == nullptr ) return nullptr ;
3607     NP* p = makeSup();
3608     p->read2( (float*)evt->sup );
3609     return p ;
3610 }
3611 NP* SEvt::gatherSeq() const
3612 {
3613     if( evt->seq == nullptr ) return nullptr ;
3614     NP* s = makeSeq();
3615     s->read2( (unsigned long long*)evt->seq );
3616     return s ;
3617 }
3618 NP* SEvt::gatherPrd() const
3619 {
3620     if( evt->prd == nullptr ) return nullptr ;
3621     NP* p = makePrd();
3622     p->read2( (float*)evt->prd );
3623     return p ;
3624 }
3625 NP* SEvt::gatherTag() const
3626 {
3627     if( evt->tag == nullptr ) return nullptr ;
3628     NP* p = makeTag();
3629     p->read2( (unsigned long long*)evt->tag );
3630     return p ;
3631 }
3632 NP* SEvt::gatherFlat() const
3633 {
3634     if( evt->flat == nullptr ) return nullptr ;
3635     NP* p = makeFlat();
3636     p->read2( (float*)evt->flat );
3637     return p ;
3638 }
3639 NP* SEvt::gatherSeed() const   // COULD BE IMPLEMENTED : IF NEEDED TO DEBUG  SLOT->GS ASSOCIATION
3640 {
3641     LOG(fatal) << " not implemented for hostside running : getting this error indicates CompMask mixup " ;
3642     assert(0);
3643     return nullptr ;
3644 }
3645 
3646 /**
3647 SEvt::gatherHit
3648 -------------------------------------------
3649 
3650 Does CPU side equivalent of QEvt::gatherHit_
3651 using the photon array and the sphoton_selector
3652 
3653 HMM: notice that this relies on having gathered
3654 the photon array, and there being an entry in the fold.
3655 So cannot have HitOnly on B side ?
3656 
3657 This means that hit must come after photon in the component order
3658 
3659 **/
3660 
3661 NP* SEvt::gatherHit() const
3662 {
3663     const NP* p = fold->get(SComp::PHOTON_) ;
3664     // cannot use getPhoton here as that gets the photons from topfold which is only populated after gather
3665     NP* h = p ? p->copy_if<float, sphoton>(*photon_selector) : nullptr ;
3666 
3667     return h ;
3668 }
3669 
3670 
3671 /**
3672 SEvt::gatherHitLite
3673 ---------------------
3674 
3675 CPU side equivalent of QEvt::gatherHitLite
3676 
3677 **/
3678 
3679 
3680 NP* SEvt::gatherHitLite() const
3681 {
3682     const NP* photonlite = fold->get(SComp::PHOTONLITE_) ;
3683     NP* hitlite = sphotonlite::select( photonlite, photonlite_selector );
3684     return hitlite ;
3685 }
3686 
3687 
3688 
3689 
3690 
3691 
3692 NP* SEvt::gatherSimtrace() const
3693 {
3694     LOG_IF(info, SIMTRACE) << " evt->simtrace " << ( evt->simtrace ? "YES" : "NO " ) ;
3695     if( evt->simtrace == nullptr ) return nullptr ;
3696     NP* p = makeSimtrace();
3697     p->read2( (float*)evt->simtrace );
3698     return p ;
3699 }
3700 
3701 /**
3702 SEvt::makePhoton
3703 -----------------
3704 
3705 This is called by SEvt::gatherPhoton
3706 
3707 **/
3708 
3709 NP* SEvt::makePhoton() const
3710 {
3711     NP* p = sphoton::zeros( evt->num_photon );
3712     return p ;
3713 }
3714 
3715 NP* SEvt::makePhotonLite() const
3716 {
3717     NP* l = sphotonlite::zeros( evt->num_photon );
3718     return l ;
3719 }
3720 
3721 
3722 
3723 NP* SEvt::makeRecord() const
3724 {
3725     NP* r = NP::Make<float>( evt->num_photon, evt->max_record, 4, 4 );
3726     r->set_meta<std::string>("rpos", "4,GL_FLOAT,GL_FALSE,64,0,false" );  // eg used by examples/UseGeometryShader
3727     return r ;
3728 }
3729 NP* SEvt::makeRec() const
3730 {
3731     NP* r = NP::Make<short>( evt->num_photon, evt->max_rec, 2, 4);   // stride:  sizeof(short)*2*4 = 2*2*4 = 16
3732     r->set_meta<std::string>("rpos", "4,GL_SHORT,GL_TRUE,16,0,false" );  // eg used by examples/UseGeometryShader
3733     return r ;
3734 }
3735 NP* SEvt::makeAux() const
3736 {
3737     NP* r = NP::Make<float>( evt->num_photon, evt->max_aux, 4, 4 );
3738     return r ;
3739 }
3740 NP* SEvt::makeSup() const
3741 {
3742     NP* p = NP::Make<float>( evt->num_photon, 6, 4 );
3743     return p ;
3744 }
3745 
3746 NP* SEvt::makeSeq() const
3747 {
3748     return NP::Make<unsigned long long>( evt->num_seq, 2, sseq::NSEQ );
3749 }
3750 NP* SEvt::makePrd() const
3751 {
3752     return NP::Make<float>( evt->num_photon, evt->max_prd, 2, 4);
3753 }
3754 
3755 /**
3756 SEvt::makeTag
3757 ---------------
3758 
3759 The evt->max_tag are packed into the stag::NSEQ of each *stag* struct
3760 
3761 For example with stag::NSEQ = 2 there are two 64 bit "unsigned long long"
3762 in the *stag* struct which with 5 bits per tag is room for 2*12 = 24 tags
3763 
3764 **/
3765 
3766 NP* SEvt::makeTag() const
3767 {
3768     assert( sizeof(stag) == sizeof(unsigned long long)*stag::NSEQ );
3769     return NP::Make<unsigned long long>( evt->num_photon, stag::NSEQ);   //
3770 }
3771 NP* SEvt::makeFlat() const
3772 {
3773     assert( sizeof(sflat) == sizeof(float)*sflat::SLOTS );
3774     return NP::Make<float>( evt->num_photon, sflat::SLOTS );   //
3775 }
3776 NP* SEvt::makeSimtrace() const
3777 {
3778     return NP::Make<float>( evt->num_simtrace, 4, 4 );
3779 }
3780 
3781 
3782 
3783 
3784 
3785 
3786 /**
3787 SEvt::getMeta (an SCompProvider method)
3788 -----------------------------------------
3789 
3790 NB as metadata is collected in SEvt::gather_components
3791 from the SCompProvider this is NOT the source of metadata
3792 for GPU running, that comes from QEvt::getMeta.
3793 This is only the source for CPU U4Recorder running (aka B evts).
3794 
3795 **/
3796 
3797 
3798 std::string SEvt::getMeta() const
3799 {
3800     return meta ;
3801 }
3802 
3803 const char* SEvt::getTypeName() const
3804 {
3805     return TYPENAME ;
3806 }
3807 
3808 
3809 
3810 
3811 /**
3812 SEvt::gatherComponent
3813 ------------------------
3814 
3815 If the cmp is within the SEventConfig::GatherComp mask
3816 then invoke the corresponding gather... method otherwise
3817 return nullptr.
3818 
3819 NB this is for hostside running only, for device-side running
3820 the SCompProvider is the QEvt not the SEvt, so this method
3821 is not called
3822 
3823 **/
3824 
3825 NP* SEvt::gatherComponent(unsigned cmp) const
3826 {
3827     unsigned gather_mask = SEventConfig::GatherComp();
3828     return gather_mask & cmp ? gatherComponent_(cmp) : nullptr ;
3829 }
3830 
3831 NP* SEvt::gatherComponent_(unsigned cmp) const
3832 {
3833     NP* a = nullptr ;
3834     switch(cmp)
3835     {
3836         case SCOMP_INPHOTON:  a = gatherInputPhoton() ; break ;
3837         case SCOMP_G4STATE:   a = gatherG4State()     ; break ;
3838 
3839         case SCOMP_GENSTEP:   a = gatherGenstep()  ; break ;
3840         case SCOMP_DOMAIN:    a = gatherDomain()   ; break ;
3841         case SCOMP_PHOTON:    a = gatherPhoton()   ; break ;
3842         case SCOMP_RECORD:    a = gatherRecord()   ; break ;
3843         case SCOMP_REC:       a = gatherRec()      ; break ;
3844         case SCOMP_AUX:       a = gatherAux()      ; break ;
3845         case SCOMP_SUP:       a = gatherSup()      ; break ;
3846         case SCOMP_SEQ:       a = gatherSeq()      ; break ;
3847         case SCOMP_PRD:       a = gatherPrd()      ; break ;
3848         case SCOMP_TAG:       a = gatherTag()      ; break ;
3849         case SCOMP_FLAT:      a = gatherFlat()     ; break ;
3850 
3851         case SCOMP_SEED:      a = gatherSeed()     ; break ;
3852         case SCOMP_HIT:       a = gatherHit()      ; break ;
3853         case SCOMP_HITLITE:   a = gatherHitLite()  ; break ;
3854         case SCOMP_SIMTRACE:  a = gatherSimtrace() ; break ;
3855         case SCOMP_PHO:       a = gatherPho()      ; break ;
3856         case SCOMP_GS:        a = gatherGS()       ; break ;
3857     }
3858     return a ;
3859 }
3860 
3861 
3862 /**
3863 SEvt::saveGenstep
3864 -----------------
3865 
3866 The returned array takes a full copy of the genstep quad6 vector
3867 with all gensteps collected since the last SEvt::clear.
3868 The array is thus independent from quad6 vector, and hence is untouched
3869 by SEvt::clear
3870 
3871 **/
3872 
3873 void SEvt::saveGenstep(const char* dir) const  // HMM: NOT THE STANDARD SAVE
3874 {
3875     NP* a = gatherGenstep();
3876     if(a == nullptr) return ;
3877     LOG(LEVEL) << a->sstr() << " dir " << dir ;
3878     a->save(dir, "gs.npy");
3879 }
3880 void SEvt::saveGenstepLabels(const char* dir, const char* name) const
3881 {
3882     NP::Write<int>(dir, name, (int*)gs.data(), gs.size(), 4 );
3883 }
3884 
3885 std::string SEvt::descGS() const
3886 {
3887     std::stringstream ss ;
3888     for(unsigned i=0 ; i < getNumGenstepFromGenstep() ; i++) ss << gs[i].desc() << std::endl ;
3889     std::string s = ss.str();
3890     return s ;
3891 }
3892 
3893 std::string SEvt::descDir() const
3894 {
3895     const char* savedir = getSaveDir();
3896     const char* loaddir = getLoadDir();
3897     std::stringstream ss ;
3898     ss
3899        << " savedir " << ( savedir ? savedir : "-" )
3900        << std::endl
3901        << " loaddir " << ( loaddir ? loaddir : "-" )
3902        << std::endl
3903        << " is_loaded " << ( is_loaded ? "YES" : "NO" )
3904        << std::endl
3905        << " is_loadfail " << ( is_loadfail ? "YES" : "NO" )
3906        << std::endl
3907        ;
3908     std::string s = ss.str();
3909     return s ;
3910 }
3911 
3912 std::string SEvt::descFold() const
3913 {
3914     return topfold->desc();
3915 }
3916 std::string SEvt::Brief() // static
3917 {
3918     std::stringstream ss ;
3919     ss << "SEvt::Brief "
3920        << " SEvt::Exists(0) " << ( Exists(0) ? "Y" : "N" )
3921        << " SEvt::Exists(1) " << ( Exists(1) ? "Y" : "N" )
3922        << std::endl
3923        << " SEvt::Get(0)->brief() " << ( Exists(0) ? Get(0)->brief() : "-" )
3924        << std::endl
3925        << " SEvt::Get(1)->brief() " << ( Exists(0) ? Get(0)->brief() : "-" )
3926        << std::endl
3927        ;
3928     std::string str = ss.str();
3929     return str ;
3930 }
3931 std::string SEvt::brief() const
3932 {
3933     std::stringstream ss ;
3934     ss << "SEvt::brief "
3935        << " getIndex " << getIndex()
3936        << " hasInputPhoton " << ( hasInputPhoton() ? "Y" : "N" )
3937        << " hasInputPhotonTransformed " << ( hasInputPhotonTransformed() ? "Y" : "N" )
3938        ;
3939     std::string str = ss.str();
3940     return str ;
3941 }
3942 
3943 std::string SEvt::id() const
3944 {
3945     bool is_egpu = isEGPU();
3946     bool is_ecpu = isECPU();
3947 
3948     std::stringstream ss ;
3949     ss << "SEvt::id "
3950        << ( is_egpu ? "EGPU" : "" )
3951        << ( is_ecpu ? "ECPU" : "" )
3952        << " (" << getIndexPresentation() << ") "
3953        << " GSV " << ( haveGenstepVec() ? "YES" : "NO " )
3954        << " " << descStage()
3955        ;
3956     std::string str = ss.str();
3957     return str ;
3958 }
3959 
3960 
3961 
3962 
3963 
3964 std::string SEvt::desc() const
3965 {
3966     std::stringstream ss ;
3967     ss << evt->desc()
3968        << std::endl
3969        << descDir()
3970        << std::endl
3971        << dbg->desc()
3972        << std::endl
3973        << " g4state " << ( g4state ? g4state->sstr() : "-" )
3974        << std::endl
3975        << " SEventConfig::Initialize_COUNT " << SEventConfig::Initialize_COUNT
3976        << std::endl
3977        ;
3978     std::string str = ss.str();
3979     return str ;
3980 }
3981 
3982 std::string SEvt::descDbg() const
3983 {
3984     std::stringstream ss ;
3985     ss << dbg->desc() << std::endl ;
3986     std::string str = ss.str();
3987     return str ;
3988 }
3989 
3990 /**
3991 SEvt::gather_components : collects fresh arrays into NPFold from provider
3992 ---------------------------------------------------------------------------
3993 
3994 SEvt::gather_components is invoked by SEvt::gather from QSim::simulate::
3995 
3996 
3997      +-------------------+                 +-----------------+
3998      | QEvt/GPU buf      |                 |  SEvt/NPFold    |
3999      |   OR              | === gather ===> |                 |
4000      | SEvt vecs         |                 |                 |
4001      +-------------------+                 +-----------------+
4002 
4003 
4004 
4005 The SEvt::gather was formerly invoked from SEvt::save, but that
4006 is incompatible with getting access to hits so the invokation
4007 of the gather must now be done from high level methods such
4008 as QSim::simulate.
4009 
4010 
4011 0. invokes NPFold::add_subfold on the *topfold* giving *fold*
4012    into which the gathering is done. This is done to support
4013    multilaunch running by invoking NPFold::concat on the *topfold*
4014    after the launch loop of QSim::simulate
4015 
4016 1. invokes gatherComponent on the SCompProvider instance which is either
4017    this SEvt instance for CPU/U4Recorder running OR the QEvt instance
4018    for GPU/QSim runnning
4019 
4020    * the SCompProvider allocates an NP array and populates it either
4021      from vectors for CPU running or by copies from GPU device buffers
4022 
4023 2. the freshly created NP arrays are added to the NPFold,
4024    NB pre-existing keys cause NPFold asserts, so it is essential
4025    that SEvt::clear is called to clear the fold before gathering
4026 
4027 Note thet QEvt::setGenstep invoked SEvt::clear so the genstep vectors
4028 are clear when this gets called. So must rely on the contents of the
4029 fold to get the stats.
4030 
4031 
4032 Q: Where does the SEvt::meta get passed to the NPFold ?
4033 
4034 
4035 **/
4036 
4037 void SEvt::gather_components()   // *GATHER*
4038 {
4039     fold = topfold->add_subfold();
4040     if(NPFOLD_VERBOSE) fold->set_verbose();
4041     const char* fkey = topfold->get_last_subfold_key();  // f000 f001 f001 ...
4042 
4043 
4044     int num_genstep = -1 ;
4045     int num_photon  = -1 ;
4046     int num_hit     = -1 ;
4047 
4048     int num_comp = gather_comp.size() ;
4049 
4050     LOG(LEVEL) << " num_comp " << num_comp << " from provider " << provider->getTypeName() << " fkey " << ( fkey ? fkey : "-" )   ;
4051     LOG_IF(info, GATHER||SIMTRACE) << " num_comp " << num_comp << " from provider " << provider->getTypeName() << " fkey " << ( fkey ? fkey : "-" ) ;
4052 
4053     for(int i=0 ; i < num_comp ; i++)
4054     {
4055         unsigned cmp = gather_comp[i] ;
4056         const char* k = SComp::Name(cmp);
4057         NP* a = provider->gatherComponent(cmp);  // see QEvt::gatherComponent for GPU running
4058         bool null_component = a == nullptr ;
4059 
4060         LOG(LEVEL)
4061             << " k " << std::setw(15) << k
4062             << " a " << ( a ? a->brief() : "-" )
4063             << " null_component " << ( null_component ? "YES" : "NO " )
4064             ;
4065 
4066         LOG_IF(info, GATHER)
4067             << " k " << std::setw(15) << k
4068             << " a " << ( a ? a->brief() : "-" )
4069             << " null_component " << ( null_component ? "YES" : "NO " )
4070             ;
4071 
4072 
4073         if(null_component) continue ;
4074         fold->add(k, a);
4075 
4076         int num = a->shape[0] ;
4077         if(     SComp::IsGenstep(cmp)) num_genstep = num ;
4078         else if(SComp::IsPhoton(cmp))  num_photon = num ;
4079         else if(SComp::IsHit(cmp))     num_hit = num ;
4080     }
4081 
4082     gather_total += 1 ;
4083 
4084     if(num_genstep > -1) genstep_total += num_genstep ;
4085     if(num_photon > -1)  photon_total += num_photon ;
4086     if(num_hit > -1)     hit_total += num_hit ;
4087 
4088     LOG(LEVEL)
4089         << " num_comp " << num_comp
4090         << " num_genstep " << num_genstep
4091         << " num_photon " << num_photon
4092         << " num_hit " << num_hit
4093         << " gather_total " << gather_total
4094         << " genstep_total " << genstep_total
4095         << " photon_total " << photon_total
4096         << " hit_total " << hit_total
4097         ;
4098 }
4099 
4100 
4101 /**
4102 SEvt::gather_metadata
4103 ----------------------
4104 
4105 Sets ((NPFold)topfold).meta to SEvt::meta
4106 
4107 This is invoked from SEvt::endOfEvent (not SEvt::gather) because the metadata
4108 contains timing information, so gathering immediately prior to
4109 save allows the metadata to be more complete.
4110 
4111 Note that while the SCompProvider is QEvt(GPU) or this SEvt(CPU)
4112 in both cases the metadata is from SEvt::meta (potentially different
4113 instances of SEvt when doing both CPU and GPU running).
4114 This is because QEvt::getMeta returns SEvt::meta for from
4115 the associated QEvt::sev (SEvt) instance.
4116 
4117 Note that because SEvt::save is typically not done in production,
4118 the SProf.hh metadata data recording is more generally useful.
4119 
4120 **/
4121 
4122 
4123 void SEvt::gather_metadata()
4124 {
4125     if(topfold == nullptr)
4126     {
4127         LOG_IF(error, gather_metadata_notopfold < 10) << " gather_metadata_notopfold " << gather_metadata_notopfold ;
4128         gather_metadata_notopfold += 1 ;
4129         return ;
4130     }
4131 
4132     std::string provmeta = provider->getMeta();
4133     LOG(LEVEL) << " provmeta ["<< provmeta << "]" ;
4134     topfold->meta = provmeta ;
4135 }
4136 
4137 
4138 
4139 /**
4140 SEvt::gather
4141 -------------
4142 
4143 Collects the components configured by SEventConfig::CompMask
4144 into NPFold from the SCompProvider which can either be:
4145 
4146 * this SEvt instance for hostside running
4147 * the qudarap/QEvt instance for deviceside running, eg G4CXSimulateTest
4148 
4149 
4150 For multi-launch running genstep slices are uploaded
4151 before each launch and *gather* is called after each launch.
4152 
4153 
4154 For an example of where this should be invoked from see QSim::simulate
4155 
4156 **/
4157 
4158 void SEvt::gather()
4159 {
4160     setStage(SEvt__gather);
4161     LOG_IF(info, LIFECYCLE) << id() ;
4162 
4163     gather_components();
4164 }
4165 
4166 
4167 /**
4168 SEvt::add_array
4169 -----------------
4170 
4171 Q: WHO CALLS THIS ?
4172 A: QSim::fake_propagate
4173 
4174 **/
4175 
4176 
4177 void SEvt::add_array( const char* k, const NP* a )
4178 {
4179     LOG(LEVEL) << " k " << k << " a " << ( a ? a->sstr() : "-" ) ;
4180     extrafold->add(k, a);
4181 }
4182 
4183 void SEvt::addEventConfigArray()
4184 {
4185     extrafold->add(SEventConfig::NAME, SEventConfig::Serialize() );
4186 }
4187 
4188 /**
4189 SEvt::addProcessHits_EPH
4190 -------------------------
4191 
4192 Called from U4Recorder::addProcessHits_EPH but as that is
4193 called from U4RecorderAnaMgr::EndOfEventAction it is too late
4194 for the extrafold save.
4195 
4196 **/
4197 
4198 void SEvt::addProcessHits_EPH(NP* eph_meta)
4199 {
4200     LOG_IF(info, EPH_) << " eph_meta " << ( eph_meta ? eph_meta->sstr() : "-" ) ;
4201 
4202     saveExtra( "SProcessHits_EPH.npy", eph_meta );
4203 }
4204 
4205 
4206 
4207 
4208 int SEvt::load()
4209 {
4210     const char* base = DefaultBase();
4211     int rc = load(base);
4212     LOG(LEVEL) << "SEvt::DefaultBase " << base << " rc " << rc ;
4213     return rc ;
4214 }
4215 
4216 
4217 bool SEvt::hasIndex() const
4218 {
4219     return index != MISSING_INDEX ;
4220 }
4221 
4222 bool SEvt::hasInstance() const
4223 {
4224     return instance != MISSING_INSTANCE ;
4225 }
4226 
4227 
4228 /**
4229 SEvt::DefaultBase
4230 -------------------
4231 
4232 If argument is defined it is used, otherwise::
4233 
4234    $TMP/GEOM/$GEOM/$ExecutableName
4235 
4236 Note that when Opticks is used from python the OPTICKS_SCRIPT
4237 envvar can be used to override the not very useful detected
4238 ExecutableName of "python3.11" or whatever.
4239 
4240 **/
4241 
4242 const char* SEvt::DefaultBase(const char* base_) // static
4243 {
4244     const char* base = nullptr ;
4245     int64_t mode_save = SEventConfig::ModeSave();
4246     if( mode_save == 0 )
4247     {
4248         // controlled dir approach : good for campaigns
4249         base = base_ ? base_ : spath::DefaultOutputDir() ;
4250     }
4251     else if( mode_save == 1 )
4252     {
4253         // relative to invoking directory approach : good for quick tests
4254         base = BLANK ;
4255     }
4256 
4257     LOG_IF(info, DIRECTORY)
4258         << "\n"
4259         << " base_ [" << ( base_ ? base_ : "-" ) << "]\n"
4260         << " base  [" << ( base  ? base  : "-" ) << "]\n"
4261         << " mode_save " << mode_save << "\n"
4262         ;
4263 
4264     return base ;
4265 }
4266 
4267 
4268 /**
4269 SEvt::RunDir
4270 --------------
4271 
4272 Directory without event index, used for run level metadata.
4273 
4274 **/
4275 
4276 const char* SEvt::RunDir( const char* base_ )  // static
4277 {
4278     const char* base = DefaultBase(base_);
4279     const char* reldir = SEventConfig::EventReldir() ;
4280     const char* dir = spath::Resolve(base, reldir );
4281 
4282     bool is_save_nothing = IsSaveNothing();
4283     if(!is_save_nothing) sdirectory::MakeDirs(dir,0);
4284     return dir ;
4285 }
4286 
4287 /**
4288 SEvt::getDir
4289 ----------------------------
4290 
4291 Reimpl in a way that is faster to understand,
4292 in attempt to remove lots of tedious code by
4293 focussing tokenization into spath.h and the
4294 high level control here in one place.
4295 
4296 HMM: could expand on that approach exposing ALL$VERSION
4297 here too instead of hiding in Reldir
4298 
4299 
4300 +---------+----------------------------------------------------------------------------------------------+
4301 |  qwn    |  typical/default value                                                                       |
4302 +=========+==============================================================================================+
4303 | base_   |  "$TMP/GEOM/$GEOM/$ExecutableName"                                                           |
4304 +---------+----------------------------------------------------------------------------------------------+
4305 | base    |  "$TMP/GEOM/$GEOM/$ExecutableName"                                                           |
4306 +---------+----------------------------------------------------------------------------------------------+
4307 | reldir  |  "ALL${VERSION:-0}_${OPTICKS_EVENT_NAME:-no_opticks_event_name}"                             |
4308 +---------+----------------------------------------------------------------------------------------------+
4309 | sidx    |  "A000"                                                                                      |
4310 +---------+----------------------------------------------------------------------------------------------+
4311 | path    |  "/tmp/blyth/opticks/GEOM/J25_4_0_opticks_Debug/python3.11/ALL0_no_opticks_event_name/A000"  |
4312 +---------+----------------------------------------------------------------------------------------------+
4313 
4314 **/
4315 
4316 const char* SEvt::getDir(const char* base_) const
4317 {
4318     const char* base = DefaultBase(base_);
4319     const char* reldir = SEventConfig::EventReldir() ;
4320     const char* sidx = hasIndex() ? getIndexString(nullptr) : nullptr ;
4321     const char* path = sidx ? spath::Resolve(base,reldir,sidx ) : spath::Resolve(base, reldir) ;
4322 
4323     bool is_save_nothing = IsSaveNothing();
4324     if(!is_save_nothing) sdirectory::MakeDirs(path,0);
4325 
4326     LOG_IF(info, DIRECTORY || SIMTRACE || LIFECYCLE )
4327         << std::endl
4328         << " base_  " << ( base_ ? base_ : "-" ) << "\n"
4329         << " SEventConfig::EventReldir   " << ( reldir ? reldir : "-" ) << "\n"
4330         << " SEventConfig::_EventReldirDefault " << SEventConfig::_EventReldirDefault << "\n"
4331         << " sidx   " << ( sidx ? sidx : "-" ) << "\n"
4332         << " path   " << ( path ? path : "-" ) << "\n"
4333         ;
4334 
4335     return path ;
4336 }
4337 
4338 char SEvt::getInstancePrefix() const
4339 {
4340     char pfx = '\0' ;
4341     switch(instance)
4342     {
4343        case EGPU:             pfx = 'A' ; break ;
4344        case ECPU:             pfx = 'B' ; break ;
4345        case MISSING_INSTANCE: pfx = 'M' ; break ;
4346        default:               pfx = 'D' ; break ;
4347     }
4348     return pfx ;
4349 }
4350 
4351 std::string SEvt::getIndexString_(const char* hdr) const
4352 {
4353     assert( index >= 0 && index != MISSING_INDEX );
4354     int wid = 3 ;
4355     char pfx = getInstancePrefix();
4356     return sstr::FormatIndex_(index, pfx, wid, hdr );
4357 }
4358 
4359 const char* SEvt::getIndexString(const char* hdr) const
4360 {
4361     std::string str = getIndexString_(hdr);
4362     return strdup(str.c_str());
4363 }
4364 
4365 
4366 
4367 
4368 
4369 
4370 std::string SEvt::descSaveDir(const char* dir_) const
4371 {
4372     const char* dir = getDir(dir_);
4373     const char* reldir = SEventConfig::EventReldir() ;
4374     bool with_index = index != MISSING_INDEX ;
4375     std::stringstream ss ;
4376     ss << "SEvt::descSaveDir"
4377        << " dir_ " << ( dir_ ? dir_ : "-" )
4378        << " dir  " << ( dir  ? dir  : "-" )
4379        << " reldir " << ( reldir ? reldir : "-" )
4380        << " SEventConfig::_EventReldirDefault " << SEventConfig::_EventReldirDefault
4381        << " with_index " << ( with_index ? "Y" : "N" )
4382        << " index " << ( with_index ? index : -1 )
4383        << " this " << std::hex << this << std::dec
4384        << std::endl
4385        ;
4386     std::string str = ss.str();
4387     return str ;
4388 }
4389 
4390 int SEvt::load(const char* base_)
4391 {
4392     const char* dir = getDir(base_);
4393     LOG(LEVEL)
4394         << " base_ " << ( base_ ? base_ : "-" )
4395         << " dir " << ( dir ? dir : "-" )
4396         ;
4397     LOG_IF(fatal, dir == nullptr) << " null dir : probably missing environment : run script, not executable directly " ;
4398     assert(dir);
4399     int rc = loadfold(dir);
4400     return rc ;
4401 }
4402 
4403 int SEvt::loadfold( const char* dir )
4404 {
4405     LOG(LEVEL) << "[ topfold.load " << dir ;
4406     int rc = topfold->load(dir);
4407     LOG(LEVEL) << "] topfold.load " << dir ;
4408     is_loaded = true ;
4409     onload();
4410     return rc ;
4411 }
4412 
4413 
4414 void SEvt::onload()
4415 {
4416     const NP* domain = topfold->get(SComp::Name(SCOMP_DOMAIN)) ;
4417     if(!domain) return ;
4418 
4419     index = domain->get_meta<int>("index");
4420     instance = domain->get_meta<int>("instance");
4421 
4422     if(hasInstance()) // ie not MISSING_INSTANCE
4423     {
4424         Set(instance, this);
4425     }
4426 
4427     LOG(LEVEL)
4428         << " from domain "
4429         << " index " << index
4430         << " instance " << instance
4431         ;
4432 }
4433 
4434 
4435 
4436 void SEvt::save(const char* bas, const char* rel1, const char* rel2 )
4437 {
4438     const char* dir = spath::Resolve(bas, rel1, rel2);
4439     save(dir);
4440 }
4441 void SEvt::save(const char* bas, const char* rel )
4442 {
4443     const char* dir = spath::Resolve(bas, rel);
4444     save(dir);
4445 }
4446 
4447 /**
4448 SEvt::save
4449 --------------
4450 
4451 The component arrays are gathered by SEvt::gather_components
4452 into the NPFold and then saved. Which components to gather and save
4453 are configured via SEventConfig::GatherComp and SEventConfig::SaveComp
4454 using the SComp enumeration.
4455 
4456 The arrays are gathered from the SCompProvider object, which
4457 may be QEvt for on device running or SEvt itself for U4Recorder
4458 Geant4 tests.
4459 
4460 SEvt::save persists NP arrays into the default directory
4461 or the directory argument provided.
4462 
4463 The FALLBACK_DIR which is used for the SEvt::DefaultDir is obtained from SEventConfig::OutFold
4464 which is normally "$DefaultOutputDir" $TMP/GEOM/$GEOM/ExecutableName
4465 This can be overriden using SEventConfig::SetOutFold or by setting the
4466 envvar OPTICKS_OUT_FOLD.
4467 
4468 It is normally much easier to use the default of "$DefaultOutputDir" as this
4469 takes care of lots of the bookkeeping automatically.
4470 However in some circumstances such as with the B side of aligned running (U4RecorderTest)
4471 it is appropriate to use the override code or envvar to locate B side outputs together
4472 with the A side.
4473 
4474 
4475 **Override with TMP envvar rather than OPTICKS_OUT_FOLD to still have auto-bookkeeping**
4476 
4477 Note that when needing to override the default output directory it is usually
4478 preferable to use TMP envvar as most of the automatic bookkeeping will still be done in that case.
4479 
4480 The below examples are with GEOM envvar set to "Pasta" and "FewPMT" with different executables:
4481 
4482 +--------------------------------------------+-----------------------------------------------------------+
4483 |   TMP envvar                               |  SEvt saveDir                                             |
4484 +============================================+===========================================================+
4485 |    undefined                               |   /tmp/blyth/opticks/GEOM/Pasta/SEvtTest/ALL              |
4486 +--------------------------------------------+-----------------------------------------------------------+
4487 |   /tmp/$USER/opticks                       |   /tmp/blyth/opticks/GEOM/Pasta/SEvtTest/ALL              |
4488 +--------------------------------------------+-----------------------------------------------------------+
4489 |   undefined                                |   /tmp/blyth/opticks/GEOM/FewPMT/U4SimulateTest/ALL0/000  |
4490 +--------------------------------------------+-----------------------------------------------------------+
4491 
4492 Only when more control of the output is needed is it appropriate to use OPTICKS_OUT_FOLD envvar.
4493 
4494 +--------------------------------------------+-----------------------------------------------------------+
4495 |  OPTICKS_OUT_FOLD envvar                   |  SEvt saveDir                                             |
4496 +============================================+===========================================================+
4497 |   undefined                                |   /tmp/blyth/opticks/GEOM/Pasta/SEvtTest/ALL              |
4498 +--------------------------------------------+-----------------------------------------------------------+
4499 |   /tmp/$USER/opticks                       |   /tmp/blyth/opticks/ALL                                  |
4500 +--------------------------------------------+-----------------------------------------------------------+
4501 |   /tmp/$USER/opticks/GEOM/$GEOM/SEvtTest   |   /tmp/blyth/opticks/GEOM/Pasta/SEvtTest/ALL              |
4502 +--------------------------------------------+-----------------------------------------------------------+
4503 
4504 * see tests/SEvtTest_saveDir.sh
4505 
4506 **/
4507 
4508 void SEvt::save()
4509 {
4510     const char* base = DefaultBase(); // eg "$TMP/GEOM/$GEOM/$ExecutableName"
4511     LOG_IF(info, LIFECYCLE || SIMTRACE) << " base [" << ( base ? base : "-" ) << "]" ;
4512     save(base);
4513 }
4514 
4515 
4516 
4517 
4518 /**
4519 SEvt::save
4520 -------------
4521 
4522 Saving arrays is a debugging activity configured
4523 with SEventConfig::DescSaveComp that has a large impact on performance.
4524 
4525 Gathering arrays (eg downloading them from device buffers to host)
4526 was formerly invoked from here, but the *gather* has now been moved upwards
4527 to QSim::simulate to allow collecting hits into other collections.
4528 The arrays to gather are configured with SEventConfig::DescGatherComp
4529 separately from the arrays to save. Clearly its necessary to gather
4530 a component in order to save it.
4531 
4532 If an index has been set with SEvt::setIndex SEvt::SetIndex
4533 and not unset with SEvt::UnsetIndex SEvt::unsetIndex
4534 then the directory is suffixed with the index::
4535 
4536     /some/directory/A001
4537 
4538 
4539 Notice the mixed memory handling in the below.
4540 The save_fold is shallow copied from topfold, indicating
4541 it does not own its arrays and should not be deleted.
4542 
4543 All fine so far. But then seqnib and seqnib_table are created
4544 and added ... so those would leak without the manual deletes
4545 at the tail.
4546 
4547 How to avoid the need for such care ? NPFold has a skipdelete flag,
4548 but that is at fold level./ Perhaps need each array to have skipdelete ?
4549 
4550 
4551 
4552 1. shallowcopy from topfold to save_fold components that are configured to save, export SEvt__SAVE=1 for log
4553 
4554 2. early exit if nothing configured to save
4555 
4556 3. when "seq" history array is configured for save and is present,
4557    derive "seqnib" and "seqnib_table" from "seq" and add them to save_fold
4558 
4559 4. when "hit" array is configured for save and is present and "hitlocal" is also configured for save,
4560    derive "hitlocal" from "hit" and add to save_fold
4561 
4562 5. when "photon" array is configured for save and is present and "photonlocal" is also configured for save,
4563    derive "photonlocal" from "photon" and add to save_fold
4564 
4565 6. when "extrafold" is defined add all extra_items arrays from it into save_fold
4566 
4567 7. count *slic* items within save_fold, when more than zero proceed to save to standard dir
4568 
4569 **/
4570 
4571 void SEvt::save(const char* dir_)
4572 {
4573     LOG_IF(info, LIFECYCLE || SIMTRACE || SAVE) << id() << " dir_[" << ( dir_ ? dir_ : "-" ) << "]" ;
4574 
4575     //  gather();   MOVED gather upwards to allow copying hits into other collections
4576 
4577     LOG(LEVEL) << descComponent() ;
4578     LOG(LEVEL) << descFold() ;
4579 
4580 
4581     // 1. shallowcopy from topfold to save_fold components that are configured to save, export SEvt__SAVE=1 for log
4582     std::string save_comp = SEventConfig::DescSaveComp() ;
4583     NPFold* save_fold = topfold->shallowcopy(save_comp.c_str());
4584 
4585     LOG_IF(info, SAVE) << " save_comp[" << save_comp << "]" ;
4586     //LOG_IF(info, SAVE) << " topfold.desc   " << ( topfold ? topfold->desc() : "-" )  ;
4587     //LOG_IF(info, SAVE) << " save_fold.desc " << ( save_fold ? save_fold->desc() : "-" ) ;
4588 
4589 
4590     // 2. early exit if nothing configured to save
4591 
4592     LOG_IF(LEVEL, save_fold == nullptr) << " NOTHING TO SAVE SEventConfig::SaveCompLabel/OPTICKS_SAVE_COMP  " << save_comp ;
4593     if(save_fold == nullptr) return ;
4594 
4595     // 3. when "seq" history array is configured for save and is present,
4596     //    derive "seqnib" and "seqnib_table" from "seq" and add them to save_fold
4597 
4598     const NP* seq = save_fold->get("seq");
4599     NP* seqnib = nullptr ;
4600     NP* seqnib_table = nullptr ;
4601     if(seq)
4602     {
4603         seqnib = CountNibbles(seq) ;
4604         seqnib_table = CountNibbles_Table(seqnib) ;
4605         save_fold->add("seqnib", seqnib );
4606         save_fold->add("seqnib_table", seqnib_table );
4607         // NPFold::add does nothing with nullptr array
4608     }
4609 
4610 
4611     // 4. when "hit" array is configured for save and is present and "hitlocal" is also configured for save,
4612     //    derive "hitlocal" from "hit" and add to save_fold
4613 
4614     const NP* hit = save_fold->get(SComp::HIT_);
4615     if(hit && SEventConfig::HasSaveComp(SComp::HITLOCAL_))
4616     {
4617         bool consistency_check = true ;
4618         NP* hitlocal = localize_photon(hit, consistency_check);
4619         assert(hitlocal);
4620         save_fold->add(SComp::HITLOCAL_, hitlocal );
4621     }
4622 
4623     // 5. when "photon" array is configured for save and is present and "photonlocal" is also configured for save,
4624     //    derive "photonlocal" from "photon" and add to save_fold
4625 
4626     const NP* photon = save_fold->get(SComp::PHOTON_);
4627     if(photon && SEventConfig::HasSaveComp(SComp::PHOTONLOCAL_))
4628     {
4629         bool consistency_check = true ;
4630         NP* photonlocal = localize_photon(photon, consistency_check);
4631         assert(photonlocal);
4632         save_fold->add(SComp::PHOTONLOCAL_, photonlocal );
4633     }
4634 
4635     // 6. when "extrafold" is defined add all extra_items arrays from it into save_fold
4636 
4637     if(extrafold)
4638     {
4639         int extra_items = extrafold->num_items();
4640         LOG_IF(info, EPH_) << "adding extra_items " << extra_items << " to save_fold " ;
4641 
4642         for(int i=0 ; i < extra_items ; i++)
4643         {
4644             const char* key = extrafold->get_key(i);
4645             const NP* arr = extrafold->get_array(i);
4646             save_fold->add(key, arr);
4647         }
4648     }
4649 
4650 
4651     // 7. count *slic* items within save_fold, when more than zero proceed to save to standard dir
4652 
4653     int slic = save_fold->_save_local_item_count();
4654     if( slic > 0 )
4655     {
4656         const char* dir = getDir(dir_);   // THIS CREATES DIRECTORY
4657         LOG_IF(info, MINIMAL||SIMTRACE) << dir << " [" << save_comp << "]"  ;
4658         LOG(LEVEL) << descSaveDir(dir_) ;
4659 
4660         LOG(LEVEL) << "[ save_fold.save " << dir ;
4661         save_fold->save(dir);
4662         LOG(LEVEL) << "] save_fold.save " << dir ;
4663 
4664         int num_save_comp = SEventConfig::NumSaveComp();
4665         if(num_save_comp > 0 ) saveFrame(dir);
4666         // could add frame to the fold ?
4667         // for now just restrict to saving frame when other components are saved
4668     }
4669     else
4670     {
4671         LOG(LEVEL) << "SKIP SAVE AS NPFold::_save_local_item_count zero " ;
4672     }
4673 
4674 
4675     // 8. delete adhoc derived arrays after any saves
4676 
4677     // deletions must be after the save
4678     delete seqnib ;
4679     delete seqnib_table ;
4680     // NB: NOT DELETING save_fold AS IT IS A SHALLOW COPY : IT DOES NOT OWN THE ARRAYS
4681 }
4682 
4683 
4684 
4685 
4686 
4687 
4688 
4689 
4690 
4691 
4692 
4693 
4694 
4695 
4696 
4697 /**
4698 SEvt::saveExtra(name,a)
4699 ------------------------
4700 
4701 Save extra array into the standard SEvt folder
4702 The name is expected to end with ".npy"
4703 
4704 For example with name of "somearray.npy" might
4705 save to::
4706 
4707     /data1/blyth/tmp/GEOM/SEVT_TEST/SEvtTest/ALL0_no_opticks_event_name/somearray.npy
4708 
4709 The directory depends on the name of the executable eg "SEvtTest" and envvars::
4710 
4711     TMP                  /data1/blyth/tmp
4712     GEOM                 SEVT_TEST
4713     VERSION              0
4714     OPTICKS_EVENT_NAME   no_opticks_event_name
4715 
4716 **/
4717 
4718 void SEvt::saveExtra( const char* name, const NP* a  ) const
4719 {
4720     const char* dir = getDir();
4721     LOG_IF(info, LIFECYCLE)
4722         << "saveExtra(name,a)\n"
4723         << "name[" << name << "]\n"
4724         << "dir [" << dir << "]\n"
4725         << "   a[" << ( a ? a->sstr() : "-" ) << "]"
4726         ;
4727 
4728     a->save(dir, name);
4729 }
4730 
4731 
4732 /**
4733 SEvt::saveExtra(base, name,a)
4734 ---------------------------------
4735 
4736 The name is expected to end with ".npy"
4737 
4738 Save extra array into directory with the specified base folder,
4739 but with the organizational reldir.  For example with base "/tmp"
4740 and name "somearray.npy" and no other envvars or SEvt index
4741 setup might save to::
4742 
4743     /tmp/ALL0_no_opticks_event_name/somearray.npy
4744 
4745 Setting envvars VERSION and OPTICKS_EVENT_NAME would
4746 change the reldir. Also setting an index on the event would
4747 add a subfold like "A000" or "B000".
4748 
4749 **/
4750 
4751 void SEvt::saveExtra(const char* base, const char* name, const NP* a ) const
4752 {
4753     const char* dir = getDir(base);
4754     LOG_IF(info, LIFECYCLE)
4755         << "saveExtra(base,name,a)\n"
4756         << "base[" << base << "]\n"
4757         << "dir [" << dir  << "]\n"
4758         << "name[" << name << "]\n"
4759         << "   a[" << ( a ? a->sstr() : "-" ) << "]"
4760         ;
4761     a->save(dir, name );
4762 }
4763 
4764 void SEvt::saveFrame(const char* dir) const
4765 {
4766     LOG(LEVEL) << "[ dir " << dir ;
4767 #ifdef WITH_OLD_FRAME
4768     frame.save(dir);
4769 #else
4770     fr.save(dir);
4771 #endif
4772     LOG(LEVEL) << "] dir " << dir ;
4773 }
4774 
4775 
4776 std::string SEvt::descComponent() const
4777 {
4778     return DescComponent(topfold);
4779 }
4780 
4781 std::string SEvt::DescComponent(const NPFold* f)  // static
4782 {
4783     const NP* genstep  = f->get(SComp::Name(SCOMP_GENSTEP)) ;
4784     const NP* seed     = f->get(SComp::Name(SCOMP_SEED)) ;
4785     const NP* photon   = f->get(SComp::Name(SCOMP_PHOTON)) ;
4786     const NP* hit      = f->get(SComp::Name(SCOMP_HIT)) ;
4787     const NP* record   = f->get(SComp::Name(SCOMP_RECORD)) ;
4788     const NP* rec      = f->get(SComp::Name(SCOMP_REC)) ;
4789     const NP* aux      = f->get(SComp::Name(SCOMP_REC)) ;
4790     const NP* sup      = f->get(SComp::Name(SCOMP_SUP)) ;
4791     const NP* seq      = f->get(SComp::Name(SCOMP_SEQ)) ;
4792     const NP* domain   = f->get(SComp::Name(SCOMP_DOMAIN)) ;
4793     const NP* simtrace = f->get(SComp::Name(SCOMP_SIMTRACE)) ;
4794     const NP* g4state  = f->get(SComp::Name(SCOMP_G4STATE)) ;
4795     const NP* pho      = f->get(SComp::Name(SCOMP_PHO)) ;
4796     const NP* gs       = f->get(SComp::Name(SCOMP_GS)) ;
4797 
4798     std::stringstream ss ;
4799     ss << "SEvt::DescComponent"
4800        << std::endl
4801        << std::setw(20) << " SEventConfig::DescGatherComp " << SEventConfig::DescGatherComp() << std::endl
4802        << std::endl
4803        << std::setw(20) << " SEventConfig::DescSaveComp " << SEventConfig::DescSaveComp() << std::endl
4804        << std::setw(20) << "hit" << " "
4805        << std::setw(20) << ( hit ? hit->sstr() : "-" )
4806        << " "
4807        << std::endl
4808        << std::setw(20) << "seed" << " "
4809        << std::setw(20) << ( seed ? seed->sstr() : "-" )
4810        << " "
4811        << std::endl
4812        << std::setw(20) << "genstep" << " "
4813        << std::setw(20) << ( genstep ? genstep->sstr() : "-" )
4814        << " "
4815        << std::setw(30) << "SEventConfig::MaxGenstep"
4816        << std::setw(20) << SEventConfig::MaxGenstep()
4817        << std::endl
4818        << std::setw(20) << "photon" << " "
4819        << std::setw(20) << ( photon ? photon->sstr() : "-" )
4820        << " "
4821        << std::setw(30) << "SEventConfig::MaxPhoton"
4822        << std::setw(20) << SEventConfig::MaxPhoton()
4823        << std::endl
4824        << std::setw(20) << "record" << " "
4825        << std::setw(20) << ( record ? record->sstr() : "-" )
4826        << " "
4827        << std::setw(30) << "SEventConfig::MaxRecord"
4828        << std::setw(20) << SEventConfig::MaxRecord()
4829        << std::endl
4830        << std::setw(20) << "aux" << " "
4831        << std::setw(20) << ( aux ? aux->sstr() : "-" )
4832        << " "
4833        << std::setw(30) << "SEventConfig::MaxAux"
4834        << std::setw(20) << SEventConfig::MaxAux()
4835        << std::endl
4836        << std::setw(20) << "sup" << " "
4837        << std::setw(20) << ( sup ? sup->sstr() : "-" )
4838        << " "
4839        << std::setw(30) << "SEventConfig::MaxSup"
4840        << std::setw(20) << SEventConfig::MaxSup()
4841        << std::endl
4842        << std::setw(20) << "rec" << " "
4843        << std::setw(20) << ( rec ? rec->sstr() : "-" )
4844        << " "
4845        << std::setw(30) << "SEventConfig::MaxRec"
4846        << std::setw(20) << SEventConfig::MaxRec()
4847        << std::endl
4848        << std::setw(20) << "seq" << " "
4849        << std::setw(20) << ( seq ? seq->sstr() : "-" )
4850        << " "
4851        << std::setw(30) << "SEventConfig::MaxSeq"
4852        << std::setw(20) << SEventConfig::MaxSeq()
4853        << std::endl
4854        << std::setw(20) << "domain" << " "
4855        << std::setw(20) << ( domain ? domain->sstr() : "-" )
4856        << " "
4857        << std::endl
4858        << std::setw(20) << "simtrace" << " "
4859        << std::setw(20) << ( simtrace ? simtrace->sstr() : "-" )
4860        << " "
4861        << std::endl
4862        << std::setw(20) << "g4state" << " "
4863        << std::setw(20) << ( g4state ? g4state->sstr() : "-" )
4864        << " "
4865        << std::endl
4866        << std::setw(20) << "pho" << " "
4867        << std::setw(20) << ( pho ? pho->sstr() : "-" )
4868        << " "
4869        << std::endl
4870        << std::setw(20) << "gs" << " "
4871        << std::setw(20) << ( gs ? gs->sstr() : "-" )
4872        << " "
4873        << std::endl
4874        ;
4875     std::string str = ss.str();
4876     return str ;
4877 }
4878 std::string SEvt::descComp() const
4879 {
4880     std::stringstream ss ;
4881     ss << "SEvt::descComp "
4882        << " gather_comp.size " << gather_comp.size()
4883        << " SComp::Desc(gather_comp) " << SComp::Desc(gather_comp)
4884        << std::endl
4885        << " save_comp.size "   << save_comp.size()
4886        << " SComp::Desc(save_comp) " << SComp::Desc(save_comp)
4887        << std::endl
4888        ;
4889     std::string s = ss.str();
4890     return s ;
4891 }
4892 
4893 std::string SEvt::descVec() const
4894 {
4895     std::stringstream ss ;
4896     ss << "SEvt::descVec "
4897        << " gather_comp " << gather_comp.size()
4898        << " save_comp " << save_comp.size()
4899        << " genstep " << genstep.size()
4900        << " gs " << gs.size()
4901        << " pho " << pho.size()
4902        << " slot " << slot.size()
4903        << " photon " << photon.size()
4904        << " record " << record.size()
4905        << " rec " << rec.size()
4906        << " seq " << seq.size()
4907        << " prd " << prd.size()
4908        << " tag " << tag.size()
4909        << " flat " << flat.size()
4910        << " simtrace " << simtrace.size()
4911        << " aux " << aux.size()
4912        << " sup " << sup.size()
4913        ;
4914     std::string s = ss.str();
4915     return s ;
4916 }
4917 
4918 
4919 
4920 const NP* SEvt::getGenstep() const { return topfold->get(SComp::GENSTEP_) ;}
4921 
4922 const NP* SEvt::getPhoton() const {    return topfold->get(    SEventConfig::PhotonCompOneName()) ; }
4923 size_t    SEvt::getNumPhoton() const { return topfold->get_num(SEventConfig::PhotonCompOneName()) ; }
4924 const NP* SEvt::getHit() const {       return topfold->get(    SEventConfig::HitCompOneName()) ; }
4925 size_t    SEvt::getNumHit() const {    return topfold->get_num(SEventConfig::HitCompOneName()) ; }
4926 
4927 const NP* SEvt::getAux() const {     return topfold->get(SComp::AUX_) ; }
4928 const NP* SEvt::getSup() const {     return topfold->get(SComp::SUP_) ; }
4929 const NP* SEvt::getPho() const {     return topfold->get(SComp::PHO_) ; }
4930 const NP* SEvt::getGS() const {      return topfold->get(SComp::GS_) ; }
4931 
4932 
4933 std::string SEvt::descSimulate() const
4934 {
4935     unsigned num_hit = getNumHit() ;
4936     bool is_undef = num_hit == SEvt::UNDEF ;
4937 
4938     std::stringstream ss ;
4939     ss << "SEvt::descSimulate"
4940        << " instance " << getInstance()
4941        << " index " << getIndex()
4942        << " num_genstep " << getNumGenstepFromGenstep()
4943        << " num_photon " << getNumPhotonCollected()
4944        << " num_hit " << getNumHit()
4945        << " num_hit.is_undef " << ( is_undef ? "YES" : "NO " )
4946        << " sev.brief " << brief()
4947        ;
4948 
4949     std::string str = ss.str();
4950     return str ;
4951 }
4952 
4953 /**
4954 SEvt::getCounts
4955 ----------------
4956 
4957 **/
4958 
4959 std::string SEvt::getCounts() const
4960 {
4961     int64_t gs = getNumGenstepCollected();
4962     int64_t ph = getNumPhotonCollected();
4963     int64_t ht = getNumHit();
4964 
4965     std::stringstream ss ;
4966     ss
4967       << "numGenstepCollected=" << gs
4968       << ","
4969       << "numPhotonCollected=" << ph
4970       << ","
4971       << "numHit=" << ht
4972       ;
4973 
4974     std::string str = ss.str();
4975     return str ;
4976 }
4977 
4978 
4979 
4980 
4981 
4982 
4983 
4984 
4985 
4986 void SEvt::getPhoton(sphoton& p, unsigned idx) const  // global
4987 {
4988     const NP* photon = getPhoton();
4989     sphoton::Get(p, photon, idx );
4990 }
4991 void SEvt::getHit(sphoton& p, unsigned idx) const
4992 {
4993     const NP* hit = getHit();
4994     sphoton::Get(p, hit, idx );
4995 }
4996 
4997 /**
4998 SEvt::getLocalPhoton
4999 --------------------
5000 
5001 sphoton::iindex instance index used to get instance frame
5002 from (SGeo*)cf which is used to transform the photon
5003 
5004 **/
5005 
5006 void SEvt::getLocalPhoton(sphoton& lp, unsigned idx) const
5007 {
5008     getPhoton(lp, idx);
5009 
5010 #ifdef WITH_OLD_FRAME
5011     sframe fr ;
5012     getPhotonFrame(fr, lp);   // HMM: this is just using lp.iindex
5013     fr.transform_w2m(lp);
5014 #else
5015     sfr fr ;
5016     getPhotonFrame(fr, lp);   // HMM: this is just using lp.iindex
5017     fr.transform_w2m(lp);
5018 #endif
5019 
5020 }
5021 
5022 
5023 /**
5024 SEvt::localize_photon
5025 ----------------------
5026 
5027 NB *hit* arrays are subsets of *photon* arrays
5028 so this works for both *photon* and *hit* arrays.
5029 
5030 **/
5031 
5032 
5033 NP* SEvt::localize_photon(const NP* photon, bool consistency_check) const
5034 {
5035     return tree ? tree->localize_photon(photon, consistency_check) : nullptr ;
5036 }
5037 
5038 
5039 
5040 /**
5041 SEvt::getLocalHit_LEAKY
5042 ------------------------
5043 
5044 This impl was formerly SEvt::getLocalHit
5045 
5046 1. copy *idx* hit from NP array into sphoton& lp struct
5047 2. uses lp.iindex (instance index) to lookup the frame from the SGeo* cf geometry
5048 
5049    * TODO: check sensor_identifier, it should now be done GPU side already ?
5050 
5051 Dec 19,2023 : Added sensor_identifier subtract one
5052 to correspond to the addition of one in::
5053 
5054    CSGFoundry::addInstance firstcall:true
5055    CSGFoundry::addInstanceVector
5056 
5057 The need for the offset by one arises from the optixInstance identifier
5058 range limitation meaning that need zero to mean not-a-sensor
5059 and not -1 0xffffffff
5060 
5061 BUT: the limitation is on the GPU side geometry optixInstance identifier,
5062 the limitation does not apply to::
5063 
5064 1. photon/hit struct in GPU or CPU
5065 2. stree inst/iinst that only exist CPU side
5066 
5067 Actually the reason for not needing -1 in the new impl
5068 is simpler than that.
5069 
5070 It is only the sqat4.h identity that is incremented, the source identity
5071 from the stree.h/iinst is never incremented (as never uploaded).
5072 
5073 **/
5074 
5075 void SEvt::getLocalHit_LEAKY(sphit& ht, sphoton& lp, unsigned idx) const
5076 {
5077     getHit(lp, idx);   // copy *idx* hit from NP array into sphoton& lp struct
5078 
5079 #ifdef WITH_OLD_FRAME
5080     sframe fr = {} ;
5081     getPhotonFrame(fr, lp);
5082     fr.transform_w2m(lp);
5083     ht.iindex = fr.inst() ;
5084     ht.sensor_identifier = fr.sensor_identifier() - 1 ;  // THIS IS OLD, UNUSED AND NOW WRONG
5085     ht.sensor_index = fr.sensor_index();
5086 #else
5087     sfr fr = {} ;
5088     getPhotonFrame(fr, lp);
5089     fr.transform_w2m(lp);
5090     ht.iindex = fr.get_inst() ;
5091     ht.sensor_identifier = fr.get_sensorid() ; // NO -1 BUT UNUSED ANYHOW
5092     ht.sensor_index = fr.get_sensorix();
5093 #endif
5094 
5095 }
5096 
5097 /**
5098 SEvt::getLocalHit
5099 -------------------
5100 
5101 Canonical usage from U4HitGet::FromEvt
5102 
5103 This implementation gets the w2m instance transform
5104 directly using stree::get_iinst avoiding the use of the sframe.h
5105 
5106 The advantages are:
5107 
5108 1. avoids transform inversion gymnastics for every hit
5109    by using the inverse transform from the stree.iinst(double)
5110    instead of using the CSGFoundry qat4(float) transforms
5111 
5112 2. double precision transform handling means the local positions
5113    suffer from less precision loss
5114 
5115 3. avoids unidentified memory leak in the sframe.h/qat4.h
5116    transform handling
5117 
5118 4. avoids complications from the sensor_identifier offsetting
5119 
5120 
5121 Note that GPU side geometry has -1 fiddle : but that doesnt effect stree geometry
5122 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5123 
5124 GPU side geometry must offset the sensor_identifier by one due to
5125 optixInstance identifier range limitation. The offset being
5126 done in the below geometry preparation methods::
5127 
5128    CSGFoundry::addInstance firstcall:true
5129    CSGFoundry::addInstanceVector
5130 
5131 
5132 With Opticks-as-a-service doing localization server side would double hits transfer size
5133 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5134 
5135 This suggests should do the localization in the client ? But that means the client
5136 needs to have the Opticks geometry (specifically stree) to access the transforms.
5137 Actually a robust client needs to form a geometry digest to ensure that its
5138 using the same geometry as the server anyhow.
5139 
5140 **/
5141 
5142 void SEvt::getLocalHit(sphit& ht, sphoton& lp, unsigned idx) const
5143 {
5144     getHit(lp, idx);   // copy *idx* hit from NP array (starts global frame) into sphoton& lp struct of caller
5145     unsigned iindex = lp.iindex() ;
5146     unsigned identity = lp.get_identity();
5147     assert( identity != 0 ); // identity:0 indicates not-a-sensor should never happen for hits
5148     unsigned sensor_identifier = identity - 1 ;
5149 
5150     const glm::tmat4x4<double>* tr = tree ? tree->get_iinst(iindex) : nullptr ;
5151 
5152     LOG_IF(fatal, tr == nullptr)
5153          << " FAILED TO GET INSTANCE TRANSFORM : WHEN TESTING NEEDS SSim::Load NOT SSim::Create"
5154          << " iindex " << iindex
5155          << " tree " << ( tree ? "YES" : "NO " )
5156          << " tree.desc_inst " << ( tree ? tree->desc_inst() : "-" )
5157          ;
5158     assert( tr );
5159 
5160     bool normalize = true ;
5161     lp.transform( *tr, normalize );   // inplace transforms lp (pos, mom, pol) into local frame
5162 
5163 
5164     // below checking should be optional : likely hot code
5165 
5166     glm::tvec4<int64_t> col3 = {} ;
5167     strid::Decode( *tr, col3 );
5168 
5169     ht.iindex            = col3[0] ;
5170     ht.sensor_identifier = col3[2] ;  // NB : NO "-1" HERE : SEE ABOVE COMMENT
5171     ht.sensor_index      = col3[3] ;
5172     // Q: Where is this encoded ?i Whats 1?
5173 
5174     assert( ht.iindex == iindex );
5175     assert( ht.sensor_identifier == sensor_identifier );
5176 }
5177 
5178 
5179 void SEvt::localize_photon_inplace( sphoton& p ) const
5180 {
5181     assert(tree);
5182     tree->localize_photon_inplace(p);
5183 }
5184 
5185 
5186 
5187 
5188 
5189 
5190 
5191 /**
5192 SEvt::getPhotonFrame  (TODO: replace with "getInstanceFrame" with iindex integer argument)
5193 ----------------------------------------------------------------------------------------------
5194 
5195 Note that this relies on the photon iindex which
5196 may not be set for photons ending in some places.
5197 It should always be set for photons ending on PMTs
5198 assuming properly instanced geometry.
5199 
5200 The use of geometry information from this low level
5201 struct is accomplished using the SGeo(cf) instance
5202 that is fullfilled from higher level CSGFoundry
5203 instance
5204 
5205 TODO: move to getting frame from stree
5206 
5207 
5208 **/
5209 
5210 #ifdef WITH_OLD_FRAME
5211 void SEvt::getPhotonFrame( sframe& fr, const sphoton& p ) const
5212 {
5213     assert(cf);
5214     cf->getFrame(fr, p.iindex() );
5215     fr.prepare();
5216 }
5217 #else
5218 void SEvt::getPhotonFrame( sfr& fr, const sphoton& p ) const
5219 {
5220     assert(tree);
5221     fr = tree->get_frame_inst( p.iindex() );
5222 }
5223 #endif
5224 
5225 
5226 
5227 std::string SEvt::descNum() const
5228 {
5229     std::stringstream ss ;
5230     ss << "SEvt::descNum"
5231        << " getNumGenstepFromGenstep "  <<  getNumGenstepFromGenstep()
5232        << " getNumPhotonFromGenstep "   <<  getNumPhotonFromGenstep()
5233        << " getNumPhotonCollected "     <<  getNumPhotonCollected()
5234        << " getNumPhoton(from NPFold) " <<  getNumPhoton()
5235        << " getNumHit(from NPFold) "    <<  getNumHit()
5236        << std::endl
5237        ;
5238 
5239     std::string s = ss.str();
5240     return s ;
5241 }
5242 
5243 std::string SEvt::descPhoton(unsigned max_print) const
5244 {
5245     unsigned num_photon = getNumPhoton();
5246     bool num_photon_unset =  (int)num_photon == -1 ;
5247 
5248     unsigned num_print = num_photon_unset ? 0 : std::min(max_print, num_photon);
5249 
5250     std::stringstream ss ;
5251     ss << "SEvt::descPhoton"
5252        << " num_photon " <<  num_photon
5253        << " num_photon_unset " <<  ( num_photon_unset ? "YES" : "NO " )
5254        << " max_print " <<  max_print
5255        << " num_print " <<  num_print
5256        << std::endl
5257        ;
5258 
5259     sphoton p ;
5260     for(unsigned idx=0 ; idx < num_print ; idx++)
5261     {
5262         getPhoton(p, idx);
5263         ss << p.desc() << std::endl  ;
5264     }
5265 
5266     std::string s = ss.str();
5267     return s ;
5268 }
5269 
5270 /**
5271 SEvt::descLocalPhoton SEvt::descFramePhoton
5272 ----------------------------------------------
5273 
5274 Note that SEvt::descLocalPhoton uses the frame of the
5275 instance index of each photon whereas the SEvt::descFramePhoton
5276 uses a single frame that is provided by SEvt::setFrame methods.
5277 
5278 Hence caution wrt which frame is applicable for local photon.
5279 
5280 **/
5281 
5282 std::string SEvt::descLocalPhoton(unsigned max_print) const
5283 {
5284     unsigned num_photon = getNumPhoton();
5285     bool num_photon_unset =  (int)num_photon == -1 ;
5286     unsigned num_print = num_photon_unset ? 0 : std::min(max_print, num_photon);
5287 
5288     std::stringstream ss ;
5289     ss << "SEvt::descLocalPhoton"
5290        << " num_photon " <<  num_photon
5291        << " num_photon_unset " <<  ( num_photon_unset ? "YES" : "NO " )
5292        << " max_print " <<  max_print
5293        << " num_print " <<  num_print
5294        << std::endl
5295        ;
5296 
5297     sphoton lp ;
5298     for(unsigned idx=0 ; idx < num_print ; idx++)
5299     {
5300         getLocalPhoton(lp, idx);
5301         ss << lp.desc() << std::endl  ;
5302     }
5303     std::string s = ss.str();
5304     return s ;
5305 }
5306 
5307 std::string SEvt::descFramePhoton(unsigned max_print) const
5308 {
5309     unsigned num_photon = getNumPhoton();
5310     unsigned num_print = std::min(max_print, num_photon) ;
5311 
5312 #ifdef WITH_OLD_FRAME
5313     bool zero_frame = frame.is_zero() ;
5314 #else
5315     bool zero_frame = fr.is_zero() ;
5316 #endif
5317 
5318     std::stringstream ss ;
5319     ss << "SEvt::descFramePhoton"
5320        << " num_photon " <<  num_photon
5321        << " max_print " <<  max_print
5322        << " num_print " <<  num_print
5323        << " zero_frame " << zero_frame
5324        << std::endl
5325        ;
5326 
5327     if(zero_frame)
5328     {
5329         ss << "CANNOT getFramePhoton WITHOUT FRAME SET " << std::endl ;
5330     }
5331     else
5332     {
5333         sphoton fp ;
5334         for(unsigned idx=0 ; idx < num_print ; idx++)
5335         {
5336             getFramePhoton(fp, idx);
5337             ss << fp.desc() << std::endl  ;
5338         }
5339     }
5340     std::string s = ss.str();
5341     return s ;
5342 }
5343 
5344 
5345 std::string SEvt::descInputGenstep() const
5346 {
5347     const char* ig = SEventConfig::InputGenstep() ;
5348     int c1 = 35 ;
5349     const char* div = " : " ;
5350     std::stringstream ss ;
5351     ss << "SEvt::descInputGenstep" << std::endl ;
5352     ss << std::setw(c1) << " SEventConfig::IntegrationMode "  << div << SEventConfig::IntegrationMode() << std::endl ;
5353     ss << std::setw(c1) << " SEventConfig::InputGenstep "      << div << ( ig  ? ig  : "-" ) << std::endl ;
5354     ss << std::setw(c1) << " input_genstep "   << div << ( input_genstep ? input_genstep->sstr() : "-" )     << std::endl ;
5355     ss << std::setw(c1) << " input_genstep.lpath " << div << ( input_genstep ? input_genstep->get_lpath() : "--" ) << std::endl ;
5356     std::string s = ss.str();
5357     return s ;
5358 }
5359 
5360 std::string SEvt::descInputPhoton() const
5361 {
5362     const char* ip = SEventConfig::InputPhoton() ;
5363     const char* ipf = SEventConfig::InputPhotonFrame() ;
5364     int c1 = 35 ;
5365 
5366     const char* div = " : " ;
5367     std::stringstream ss ;
5368     ss << "SEvt::descInputPhoton" << std::endl ;
5369     ss << std::setw(c1) << " SEventConfig::IntegrationMode "  << div << SEventConfig::IntegrationMode() << std::endl ;
5370     ss << std::setw(c1) << " SEventConfig::InputPhoton "      << div << ( ip  ? ip  : "-" ) << std::endl ;
5371     ss << std::setw(c1) << " SEventConfig::InputPhotonFrame " << div << ( ipf ? ipf : "-" ) << std::endl ;
5372     ss << std::setw(c1) << " hasInputPhoton " << div << ( hasInputPhoton() ? "YES" : "NO " ) << std::endl ;
5373     ss << std::setw(c1) << " input_photon "   << div << ( input_photon ? input_photon->sstr() : "-" )     << std::endl ;
5374     ss << std::setw(c1) << " input_photon.lpath " << div << ( input_photon ? input_photon->get_lpath() : "--" ) << std::endl ;
5375     ss << std::setw(c1) << " hasInputPhotonTransformed " << div << ( hasInputPhotonTransformed() ? "YES" : "NO " ) ;
5376     std::string s = ss.str();
5377     return s ;
5378 }
5379 
5380 std::string SEvt::DescInputGenstep(int idx) // static
5381 {
5382     return Exists(idx) ? Get(idx)->descInputGenstep() : "-" ;
5383 }
5384 std::string SEvt::DescInputPhoton(int idx) // static
5385 {
5386     return Exists(idx) ? Get(idx)->descInputPhoton() : "-" ;
5387 }
5388 
5389 
5390 
5391 
5392 
5393 
5394 std::string SEvt::descFull(unsigned max_print) const
5395 {
5396     std::stringstream ss ;
5397     ss << "[ SEvt::descFull "  << std::endl ;
5398     ss << ( cf ? cf->descBase() : "no-cf" ) << std::endl ;
5399     ss << descDir() << std::endl ;
5400     ss << descNum() << std::endl ;
5401     ss << descComponent() << std::endl ;
5402     ss << descInputPhoton() << std::endl ;
5403 
5404     ss << descPhoton(max_print) << std::endl ;
5405     ss << descLocalPhoton(max_print) << std::endl ;
5406     ss << descFramePhoton(max_print) << std::endl ;
5407 
5408     ss << ( cf ? cf->descBase() : "no-cf" ) << std::endl ;
5409     ss << ( topfold ? topfold->desc() : "no-topfold" ) << std::endl ;
5410     ss << "] SEvt::descFull "  << std::endl ;
5411     std::string s = ss.str();
5412     return s ;
5413 }
5414 
5415 
5416 /**
5417 SEvt::getFramePhoton SEvt::getFrameHit
5418 ---------------------------------------
5419 
5420 frame set by SEvt::setFrame is used to transform the photon into local "model" frame
5421 
5422 **/
5423 
5424 void SEvt::getFramePhoton(sphoton& lp, unsigned idx) const
5425 {
5426     getPhoton(lp, idx);
5427     applyFrameTransform(lp);
5428 }
5429 void SEvt::getFrameHit(sphoton& lp, unsigned idx) const
5430 {
5431     getHit(lp, idx);
5432     applyFrameTransform(lp);
5433 }
5434 void SEvt::applyFrameTransform(sphoton& lp) const
5435 {
5436 #ifdef WITH_OLD_FRAME
5437     bool zero_frame = frame.is_zero() ;
5438 #else
5439     bool zero_frame = fr.is_zero() ;
5440 #endif
5441 
5442     LOG_IF(fatal, zero_frame) << " must setFrame before can applyFrameTransform " ;
5443     assert(!zero_frame);
5444 
5445 #ifdef WITH_OLD_FRAME
5446     frame.transform_w2m(lp);
5447 #else
5448     bool normalize = true ;
5449     bool inverse = true ; // w2m ? IS THAT CORRECT
5450     fr.transform(lp, normalize, inverse);
5451 #endif
5452 
5453 
5454 }
5455 
5456 /**
5457 SEvt::CountNibbles
5458 --------------------
5459 
5460 Create array of ints the same length as seq with nibble counts,
5461 from 0 to 32(typically).
5462 
5463 **/
5464 
5465 NP* SEvt::CountNibbles( const NP* seq ) // static
5466 {
5467     std::vector<sseq> qq ;
5468     NPX::VecFromArray<sseq>(qq, seq );
5469     int num_seq = qq.size();
5470 
5471     NP* seqnib = NP::Make<int>( seq->shape[0] ) ;
5472     int* nn = seqnib->values<int>() ;
5473     for(int i=0 ; i < num_seq ; i++)
5474     {
5475         const sseq& q = qq[i] ;
5476         nn[i] = q.seqhis_nibbles();
5477     }
5478     return seqnib ;
5479 }
5480 
5481 /**
5482 SEvt::CountNibbles_Table
5483 --------------------------
5484 
5485 **/
5486 
5487 NP* SEvt::CountNibbles_Table( const NP* seqnib ) // static
5488 {
5489     int num_seqnib = seqnib->shape[0] ;
5490     const int* nn = seqnib->cvalues<int>() ;
5491 
5492     int ni =  sseq::SLOTS + 1 ;
5493     NP* seqnib_table = NP::Make<int>(ni, 1) ;
5494     int* cc = seqnib_table->values<int>() ;
5495     for(int i=0 ; i < num_seqnib ; i++)
5496     {
5497         int nibs = nn[i] ;
5498         assert( nibs < ni );
5499         cc[nibs] += 1 ;
5500     }
5501     return seqnib_table ;
5502 }
5503 
5504 
5505 
5506 
5507 
5508