Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-10 07:50:32

0001 /**
0002 U4Recorder.cc
0003 =============
0004 
0005 Boundary class changes need to match in all the below::
0006 
0007     U4OpBoundaryProcess.h
0008     U4Physics.cc
0009     U4Recorder.cc
0010     U4StepPoint.cc
0011 
0012 **/
0013 
0014 
0015 #include "scuda.h"
0016 #include "squad.h"
0017 #include "sphoton.h"
0018 
0019 #include "STrackInfo.h"
0020 #include "SProcessHits_EPH.h"
0021 #include "spho.h"
0022 #include "srec.h"
0023 #include "ssys.h"
0024 #include "stimer.h"
0025 #include "smeta.h"
0026 
0027 #include "NP.hh"
0028 #include "SPath.hh"
0029 #include "ssys.h"
0030 #include "ssolid.h"
0031 #include "SEvt.hh"
0032 #include "SLOG.hh"
0033 #include "SOpBoundaryProcess.hh"
0034 #include "CustomStatus.h"
0035 
0036 #include "G4LogicalBorderSurface.hh"
0037 #include "G4Event.hh"
0038 
0039 #include "U4Recorder.hh"
0040 #include "U4Engine.h"
0041 #include "U4Track.h"
0042 #include "U4StepPoint.hh"
0043 #include "U4OpBoundaryProcess.h"
0044 #include "U4OpBoundaryProcessStatus.h"
0045 #include "U4TrackStatus.h"
0046 #include "U4Random.hh"
0047 
0048 #include "U4UniformRand.h"
0049 NP* U4UniformRand::UU = nullptr ; // HMM: misplaced n InstrumentedG4OpBoundaryProcess
0050 // UU gets set by U4Recorder::saveOrLoadStates when doing single photon reruns
0051 
0052 #include "U4Fake.h"
0053 #include "U4VolumeMaker.hh"
0054 
0055 #include "U4Surface.h"
0056 #include "U4Process.h"
0057 #include "U4Touchable.h"
0058 #include "U4Simtrace.h"
0059 #include "U4Version.h"
0060 
0061 #include "SCF.h"
0062 #include "U4Step.h"
0063 
0064 #ifdef WITH_PMTSIM
0065 #include "PMTSim.hh"
0066 #endif
0067 
0068 
0069 #include "G4OpBoundaryProcess.hh"
0070 #ifdef WITH_CUSTOM4
0071 #include "C4OpBoundaryProcess.hh"
0072 #include "C4CustomART.h"
0073 #include "C4CustomART_Debug.h"
0074 #include "C4TrackInfo.h"
0075 #include "C4Pho.h"
0076 //#include "C4Version.h"
0077 #elif PMTSIM_STANDALONE
0078 #include "CustomART.h"
0079 #include "CustomART_Debug.h"
0080 #endif
0081 
0082 
0083 const plog::Severity U4Recorder::LEVEL = SLOG::EnvLevel("U4Recorder", "DEBUG");
0084 UName                U4Recorder::SPECS = {} ;
0085 
0086 const int U4Recorder::STATES = ssys::getenvint("U4Recorder_STATES",-1) ;
0087 const int U4Recorder::RERUN  = ssys::getenvint("U4Recorder_RERUN",-1) ;
0088 
0089 const bool U4Recorder::SEvt_NPFold_VERBOSE  = ssys::getenvbool("U4Recorder__SEvt_NPFold_VERBOSE") ;
0090 const bool U4Recorder::PIDX_ENABLED = ssys::getenvbool("U4Recorder__PIDX_ENABLED") ;
0091 const bool U4Recorder::EndOfRunAction_Simtrace = ssys::getenvbool("U4Recorder__EndOfRunAction_Simtrace") ;
0092 const int  U4Recorder::UseGivenVelocity_KLUDGE = ssys::getenvint(_UseGivenVelocity_KLUDGE, 0) ;
0093 const char* U4Recorder::REPLICA_NAME_SELECT = ssys::getenvvar("U4Recorder__REPLICA_NAME_SELECT", "PMT") ;
0094 
0095 
0096 const int U4Recorder::PIDX = ssys::getenvint("PIDX",-1) ;
0097 const int U4Recorder::EIDX = ssys::getenvint("EIDX",-1) ;
0098 const int U4Recorder::GIDX = ssys::getenvint("GIDX",-1) ;
0099 
0100 std::string U4Recorder::Desc() // static
0101 {
0102     std::stringstream ss ;
0103     ss << _UseGivenVelocity_KLUDGE << ":" << UseGivenVelocity_KLUDGE << "\n"
0104        << U4Version::Desc()
0105        << Switches()
0106        << "\n"
0107        ;
0108     std::string str = ss.str();
0109     return str ;
0110 }
0111 std::string U4Recorder::DescFull() // static
0112 {
0113     std::stringstream ss ;
0114     ss << "U4Recorder::Desc" << std::endl
0115        << " U4Recorder_STATES                   : " << STATES << std::endl
0116        << " U4Recorder_RERUN                    : " << RERUN << std::endl
0117        << " U4Recorder__PIDX_ENABLED            : " << ( PIDX_ENABLED            ? "YES" : "NO " ) << std::endl
0118        << " U4Recorder__SEvt_NPFold_VERBOSE     : " << ( SEvt_NPFold_VERBOSE     ? "YES" : "NO " ) << std::endl
0119        << " U4Recorder__EndOfRunAction_Simtrace : " << ( EndOfRunAction_Simtrace ? "YES" : "NO " ) << std::endl
0120        << " U4Recorder__REPLICA_NAME_SELECT     : " << ( REPLICA_NAME_SELECT     ? REPLICA_NAME_SELECT : "-" ) << std::endl
0121        << " PIDX                                : " << PIDX << std::endl
0122        << " EIDX                                : " << EIDX << std::endl
0123        << " GIDX                                : " << GIDX << std::endl
0124        << " U4Version::Desc                     : " << U4Version::Desc() << "\n"
0125        ;
0126 
0127     bool uoc = UserSteppingAction_Optical_ClearNumberOfInteractionLengthLeft ;
0128     ss << UserSteppingAction_Optical_ClearNumberOfInteractionLengthLeft_ << ":" << int(uoc) << std::endl ;
0129     ss << Switches() << std::endl ;
0130     std::string str = ss.str();
0131     return str ;
0132 }
0133 
0134 
0135 
0136 /**
0137 U4Recorder::Switches
0138 ---------------------
0139 
0140 HMM: preping metadata kvs would be more useful
0141 **/
0142 
0143 std::string U4Recorder::Switches()  // static
0144 {
0145     std::stringstream ss ;
0146     ss << "U4Recorder::Switches" << std::endl ;
0147 #ifdef WITH_CUSTOM4
0148     ss << "WITH_CUSTOM4" << std::endl ;
0149 #else
0150     ss << "NOT:WITH_CUSTOM4" << std::endl ;
0151 #endif
0152 #ifdef WITH_PMTSIM
0153     ss << "WITH_PMTSIM" << std::endl ;
0154 #else
0155     ss << "NOT:WITH_PMTSIM" << std::endl ;
0156 #endif
0157 #ifdef PMTSIM_STANDALONE
0158     ss << "PMTSIM_STANDALONE" << std::endl ;
0159 #else
0160     ss << "NOT:PMTSIM_STANDALONE" << std::endl ;
0161 #endif
0162 
0163 #ifdef PRODUCTION
0164     ss << "PRODUCTION" << std::endl ;
0165 #else
0166     ss << "NOT:PRODUCTION" << std::endl ;
0167 #endif
0168 
0169 #ifdef WITH_INSTRUMENTED_DEBUG
0170     ss << "WITH_INSTRUMENTED_DEBUG" << std::endl ;
0171 #else
0172     ss << "NOT:WITH_INSTRUMENTED_DEBUG" << std::endl ;
0173 #endif
0174 
0175     std::string str = ss.str();
0176     return str ;
0177 }
0178 
0179 
0180 
0181 
0182 std::string U4Recorder::EnabledLabel() // static   (formerly Desc)
0183 {
0184     std::stringstream ss ;
0185     if( GIDX > -1 ) ss << "GIDX_" << GIDX << "_" ;
0186     if( EIDX > -1 ) ss << "EIDX_" << EIDX << "_" ;
0187     if( GIDX == -1 && EIDX == -1 ) ss << "ALL" ;
0188     std::string s = ss.str();
0189     return s ;
0190 }
0191 
0192 
0193 
0194 
0195 
0196 
0197 /**
0198 U4Recorder::Enabled
0199 ---------------------
0200 
0201 This is used when EIDX and/or GIDX envvars are defined causing
0202 early exits from::
0203 
0204     U4Recorder::PreUserTrackingAction_Optical
0205     U4Recorder::PostUserTrackingAction_Optical
0206     U4Recorder::UserSteppingAction_Optical
0207 
0208 Hence GIDX and EIDX provide a way to skip the expensive recording
0209 of other photons whilst debugging single gensteps or photons.
0210 
0211 Formerly used PIDX rather than EIDX but that was confusing because it
0212 is contrary to the normal use of PIDX to control debug printout for an idx.
0213 
0214 This is typically used only during early stage debugging.
0215 
0216 **/
0217 
0218 bool U4Recorder::Enabled(const spho& label)
0219 {
0220     return GIDX == -1 ?
0221                         ( EIDX == -1 || label.id == EIDX )
0222                       :
0223                         ( GIDX == -1 || label.gs == GIDX )
0224                       ;
0225 }
0226 
0227 
0228 std::string U4Recorder::desc() const
0229 {
0230     std::stringstream ss ;
0231     ss << "U4Recorder::desc" ;
0232     std::string s = ss.str();
0233     return s ;
0234 }
0235 
0236 
0237 
0238 U4Recorder* U4Recorder::INSTANCE = nullptr ;
0239 U4Recorder* U4Recorder::Get(){ return INSTANCE ; }
0240 
0241 
0242 /**
0243 U4Recorder::U4Recorder
0244 -----------------------
0245 
0246 CAUTION: LSExpDetectorConstruction_Opticks::Setup for opticksMode:2
0247 will instanciate SEvt if this is not used
0248 
0249 **/
0250 
0251 U4Recorder::U4Recorder()
0252     :
0253     eventID(-1),
0254     transient_fSuspend_track(nullptr),
0255     rerun_rand(nullptr),
0256     sev(SEvt::CreateOrReuse(SEvt::ECPU)),
0257     tree(nullptr)
0258 {
0259     init();
0260 }
0261 
0262 void U4Recorder::init()
0263 {
0264     INSTANCE = this ;
0265     init_SEvt();
0266 }
0267 
0268 void U4Recorder::init_SEvt()
0269 {
0270     smeta::Collect(sev->meta, "U4Recorder::init_SEvt" );
0271 
0272 #ifdef WITH_CUSTOM4
0273     // Custom4 0.1.8 has octal bug in C4Version.h SO skip the version metadata
0274     NP::SetMeta<std::string>(sev->meta, "C4Version", "TBD" ) ; // C4Version::Version() );
0275 #else
0276     NP::SetMeta<std::string>(sev->meta, "C4Version", "NOT-WITH_CUSTOM4" );
0277 #endif
0278 
0279     NP::SetMeta<int>(sev->meta, _UseGivenVelocity_KLUDGE,  UseGivenVelocity_KLUDGE );
0280     NP::SetMeta<int>(sev->meta, "G4VERSION_NUMBER", G4VERSION_NUMBER );
0281 
0282     LOG(LEVEL) << " sev " << std::hex << sev << std::dec ;
0283     LOG(LEVEL) << "sev->meta[" << sev->meta << "]" ;
0284 
0285     if(SEvt_NPFold_VERBOSE)
0286     {
0287         LOG(info) << " U4Recorder__SEvt_NPFold_VERBOSE : setting SEvt:setFoldVerbose " ;
0288         sev->setFoldVerbose(true);
0289     }
0290 }
0291 
0292 
0293 /**
0294 U4Recorder::addProcessHits_EPH
0295 -------------------------------
0296 
0297 Invoked from U4RecorderAnaMgr::EndOfEventAction
0298 
0299 HMM: what about clearing for the next evt ?
0300 
0301 **/
0302 
0303 
0304 void U4Recorder::addProcessHits_EPH(NP* eph_meta)
0305 {
0306     sev->addProcessHits_EPH(eph_meta);
0307 }
0308 
0309 
0310 void U4Recorder::setU4Tree(const U4Tree* _tree)
0311 {
0312     tree = _tree ;
0313 }
0314 const U4Tree* U4Recorder::getU4Tree() const
0315 {
0316     return tree ;
0317 }
0318 
0319 
0320 
0321 
0322 void U4Recorder::BeginOfRunAction(const G4Run*)
0323 {
0324     LOG(LEVEL);
0325 }
0326 
0327 /**
0328 U4Recorder::EndOfRunAction
0329 ---------------------------
0330 
0331 **/
0332 void U4Recorder::EndOfRunAction(const G4Run*)
0333 {
0334     SEvt::SetRunMeta<int>("FAKES_SKIP", int(FAKES_SKIP) );
0335 
0336 
0337     LOG(LEVEL)
0338         << "[ U4Recorder__EndOfRunAction_Simtrace : " << ( EndOfRunAction_Simtrace ? "YES" : "NO " )
0339         ;
0340 
0341     if(EndOfRunAction_Simtrace)
0342     {
0343         U4Simtrace::EndOfRunAction(tree) ;
0344     }
0345 
0346     LOG(LEVEL)
0347         << "] U4Recorder__EndOfRunAction_Simtrace : " << ( EndOfRunAction_Simtrace ? "YES" : "NO " )
0348         ;
0349 }
0350 
0351 void U4Recorder::BeginOfEventAction(const G4Event*)
0352 {
0353     LOG(fatal) << "CHANGE TO U4Recorder::BeginOfEventAction_ : see G4CXOpticks::SensitiveDetector_EndOfEvent " ;
0354     assert(0);
0355 }
0356 void U4Recorder::EndOfEventAction(const G4Event*)
0357 {
0358     LOG(fatal) << "CHANGE TO U4Recorder::EndOfEventAction_ : see G4CXOpticks::SensitiveDetector_EndOfEvent" ;
0359     assert(0);
0360 }
0361 
0362 
0363 void U4Recorder::BeginOfEventAction_(int eventID_)
0364 {
0365     eventID = eventID_ ;
0366     LOG(info) << " eventID " << eventID ;
0367     LOG_IF(info, SEvt::LIFECYCLE ) << " eventID " << eventID ;
0368     sev->beginOfEvent(eventID);
0369 }
0370 
0371 /**
0372 U4Recorder::EndOfEventAction
0373 -----------------------------
0374 
0375 This is happening later than expected causing lifecycle
0376 issues between the SEvt::ECPU and SEvt::EGPU
0377 
0378 **/
0379 
0380 void U4Recorder::EndOfEventAction_(int eventID_)
0381 {
0382     assert( eventID == eventID_ );
0383     LOG_IF(info, SEvt::LIFECYCLE ) << " eventID " << eventID ;
0384 
0385     #if defined(WITH_PMTSIM) && defined(POM_DEBUG)
0386         NP* mtda = PMTSim::ModelTrigger_Debug_Array();
0387         std::string name = mtda->get_meta<std::string>("NAME", "MTDA.npy") ;
0388         sev->add_array( name.c_str(), mtda );
0389     #else
0390         LOG(LEVEL) << "not-(WITH_PMTSIM and POM_DEBUG)"  ;
0391     #endif
0392 
0393 
0394     // adding to extrafold
0395     sev->add_array("TRS.npy", U4VolumeMaker::GetTransforms() );
0396     sev->add_array("U4R.npy", MakeMetaArray() );
0397     sev->addEventConfigArray();
0398 
0399     sev->gather() ;             // now gathers into fold, separate from topfold
0400 
0401     // ~/j/issues/jok-tds-B-side-missing-NPFold-concat.rst
0402     sev->topfold->concat();         // in this trivial one fold case just copies pointers and arranges skipdelete on fold arrays
0403     sev->topfold->clear_subfold();
0404 
0405     sev->endOfEvent(eventID_);  // does save and clear
0406 
0407     const char* savedir = sev->getSaveDir() ;
0408     LOG(LEVEL) << " savedir " << ( savedir ? savedir : "-" );
0409     SaveMeta(savedir);
0410 }
0411 
0412 
0413 void U4Recorder::PreUserTrackingAction(const G4Track* track){  LOG(LEVEL) ; if(U4Track::IsOptical(track)) PreUserTrackingAction_Optical(track); }
0414 void U4Recorder::PostUserTrackingAction(const G4Track* track){ LOG(LEVEL) ; if(U4Track::IsOptical(track)) PostUserTrackingAction_Optical(track); }
0415 
0416 void U4Recorder::PreUserTrackingAction_(const G4Track* track, int* label){  LOG(LEVEL) ; if(U4Track::IsOptical(track)) PreUserTrackingAction_Optical_(track, label); }
0417 void U4Recorder::PostUserTrackingAction_(const G4Track* track, int* label){ LOG(LEVEL) ; if(U4Track::IsOptical(track)) PostUserTrackingAction_Optical_(track, label); }
0418 
0419 
0420 
0421 #include "U4OpBoundaryProcess.h"
0422 
0423 void U4Recorder::UserSteppingAction(const G4Step* step)
0424 {
0425     if(!U4Track::IsOptical(step->GetTrack())) return ;
0426 
0427 #if defined(WITH_CUSTOM4)
0428      UserSteppingAction_Optical<C4OpBoundaryProcess>(step);
0429 #elif defined(WITH_PMTSIM)
0430      UserSteppingAction_Optical<CustomG4OpBoundaryProcess>(step);
0431 #elif defined(WITH_INSTRUMENTED_DEBUG)
0432      UserSteppingAction_Optical<InstrumentedG4OpBoundaryProcess>(step);
0433 #else
0434      UserSteppingAction_Optical<G4OpBoundaryProcess>(step);
0435 #endif
0436 }
0437 
0438 
0439 /**
0440 U4Recorder::PreUserTrackingAction_Optical
0441 -------------------------------------------
0442 
0443 1. access photon label from the G4Track
0444    or fabricate the label if there is none.
0445    Scintillation and Cerenkov photons should always already
0446    have labels associated, but torch gensteps or input photons
0447    do not thus labels are created and associated here.
0448 
0449 2. photon label is passed along to the appropriate SEvt methods,
0450    such as beginPhoton, resumePhoton, rjoinPhoton, rjoin_resumePhoton
0451 
0452 
0453 **Reemission Rejoining**
0454 
0455 At the tail of this method SEvt::beginPhoton OR SEvt::rejoinPhoton is called
0456 with the spho label as argument. Which to call depends on label.gn which
0457 is greater than zero for reemission generations that need to be re-joined.
0458 
0459 HMM: can this same mechanism be used for FastSim handback to OrdinarySim ?
0460 
0461 **/
0462 
0463 void U4Recorder::PreUserTrackingAction_Optical(const G4Track* track)
0464 {
0465     spho ulabel = {} ;
0466     PreUserTrackingAction_Optical_GetLabel(ulabel, track);
0467     PreUserTrackingAction_Optical_(track, ulabel.data() );
0468 }
0469 
0470 
0471 void U4Recorder::PreUserTrackingAction_Optical_(const G4Track* track, int* label)
0472 {
0473     LOG(LEVEL) << "[" ;
0474     if(UseGivenVelocity_KLUDGE == 1)
0475     {
0476         LOG(LEVEL) << " YES:UseGivenVelocity_KLUDGE " ;
0477         G4Track* _track = const_cast<G4Track*>(track) ;
0478         _track->UseGivenVelocity(true); // notes/issues/Geant4_using_GROUPVEL_from_wrong_initial_material_after_refraction.rst
0479     }
0480     else
0481     {
0482         LOG(LEVEL) << " ASIS: without UseGivenVelocity_KLUDGE " ;
0483     }
0484 
0485 
0486     spho& ulabel = reinterpret_cast<spho&>(*label);
0487 
0488     if(ulabel.isPlaceholder())
0489     {
0490         int track_id = U4Track::Id(track);
0491         ulabel.set_fabricated_(track_id);
0492     }
0493 
0494 
0495     bool skip = !Enabled(ulabel) ;
0496     LOG_IF( info, skip ) << " Enabled-SKIP  EIDX/GIDX " << EIDX << "/" << GIDX ;
0497     if(skip) return ;
0498 
0499     bool modulo = ulabel.id % 100000 == 0  ;
0500     LOG_IF(info, modulo) << " modulo 100000 : ulabel.id " << ulabel.id ;
0501 
0502     U4Random::SetSequenceIndex(ulabel.id);
0503 
0504     bool resume_fSuspend = track == transient_fSuspend_track ;
0505     G4TrackStatus tstat = track->GetTrackStatus();
0506     LOG(LEVEL)
0507         << " track " << track
0508         << " status:" << U4TrackStatus::Name(tstat)
0509         << " resume_fSuspend " << ( resume_fSuspend ? "YES" : "NO" )
0510         ;
0511     assert( tstat == fAlive );
0512 
0513     SEvt* sev = SEvt::Get_ECPU();
0514     LOG_IF(fatal, sev == nullptr) << " SEvt::Get(1) returned nullptr " ;
0515     assert(sev);
0516 
0517     if(ulabel.gen() == 0)
0518     {
0519         if(resume_fSuspend == false)
0520         {
0521             sev->beginPhoton(ulabel);  // THIS ZEROS THE SLOT
0522         }
0523         else  // resume_fSuspend:true happens following FastSim ModelTrigger:YES, DoIt
0524         {
0525             sev->resumePhoton(ulabel);
0526         }
0527     }
0528     else if( ulabel.gen() > 0 )   // HMM: thats going to stick for reemission photons
0529     {
0530         if(resume_fSuspend == false)
0531         {
0532             sev->rjoinPhoton(ulabel);
0533         }
0534         else   // resume_fSuspend:true happens following FastSim ModelTrigger:YES, DoIt
0535         {
0536             sev->rjoin_resumePhoton(ulabel);
0537         }
0538     }
0539     LOG(LEVEL) << "]" ;
0540     //std::cout << "] U4Recorder::PreUserTrackingAction_Optical " << std::endl ;
0541 }
0542 
0543 /**
0544 U4Recorder::PreUserTrackingAction_Optical_GetLabel
0545 ---------------------------------------------------
0546 
0547 Optical photon G4Track arriving here from U4 instrumented Scintillation and Cerenkov processes
0548 are always labelled, having the label set at the end of the generation loop by U4::GenPhotonEnd,
0549 see examples::
0550 
0551    u4/tests/DsG4Scintillation.cc
0552    u4/tests/G4Cerenkov_modified.cc
0553 
0554 However primary optical photons arising from input photons or torch gensteps
0555 are not labelled at generation as that is probably not possible without hacking
0556 Geant4 GeneratePrimaries.
0557 
0558 As a workaround for photon G4Track arriving at U4Recorder without labels,
0559 the U4Track::SetFabricatedLabel method is below used to creates a label based entirely
0560 on a 0-based track_id with genstep index set to zero. This standin for a real label
0561 is only really equivalent for events with a single torch/inputphoton genstep.
0562 But torch gensteps are typically used for debugging so this restriction is ok.
0563 
0564 HMM: not easy to workaround this restriction as often will collect multiple gensteps
0565 before getting around to seeing any tracks from them so cannot devine the genstep index for a track
0566 by consulting gensteps collected by SEvt. YES: but this experience is from C and S gensteps,
0567 not torch ones so needs some experimentation to see what approach to take.
0568 
0569 **/
0570 
0571 void U4Recorder::PreUserTrackingAction_Optical_GetLabel( spho& ulabel, const G4Track* track )
0572 {
0573 #ifdef WITH_CUSTOM4_OLD
0574     C4Pho* label = C4TrackInfo<C4Pho>::GetRef(track);
0575 #elif WITH_CUSTOM4
0576     C4Pho* label = C4TrackInfo::GetRef(track);
0577 #else
0578     spho* label = STrackInfo::GetRef(track);
0579 #endif
0580 
0581     if( label == nullptr ) // happens with torch gensteps and input photons
0582     {
0583         PreUserTrackingAction_Optical_FabricateLabel(track) ;
0584 #ifdef WITH_CUSTOM4_OLD
0585         label = C4TrackInfo<C4Pho>::GetRef(track);
0586 #elif WITH_CUSTOM4
0587         label = C4TrackInfo::GetRef(track);
0588 #else
0589         label = STrackInfo::GetRef(track);
0590 #endif
0591     }
0592     assert( label && label->isDefined() );
0593 
0594 #ifdef WITH_CUSTOM4
0595     assert( C4Pho::N == spho::N );
0596 #endif
0597     std::array<int,spho::N> a_label ;
0598     label->serialize(a_label) ;
0599 
0600     ulabel.load(a_label);
0601 
0602     // serialize/load provides firebreak between C4Pho and spho
0603     // so the SEvt doesnt need to depend on CUSTOM4
0604 }
0605 
0606 void U4Recorder::PreUserTrackingAction_Optical_FabricateLabel( const G4Track* track )
0607 {
0608     int rerun_id = SEventConfig::G4StateRerun() ;
0609     if( rerun_id > -1 )
0610     {
0611         LOG(LEVEL) << " setting rerun_id " << rerun_id ;
0612         G4Track* _track = const_cast<G4Track*>(track) ;
0613         U4Track::SetId(_track, rerun_id) ;
0614     }
0615 
0616 #ifdef WITH_CUSTOM4
0617     U4Track::SetFabricatedLabel(track);
0618 #else
0619     U4Track::SetFabricatedLabel(track);
0620 #endif
0621 
0622 #ifdef WITH_CUSTOM4_OLD
0623     C4Pho* label = C4TrackInfo<C4Pho>::GetRef(track);
0624 #elif WITH_CUSTOM4
0625     C4Pho* label = C4TrackInfo::GetRef(track);
0626 #else
0627     spho* label = STrackInfo::GetRef(track);
0628 #endif
0629     assert(label) ;
0630 
0631     LOG(LEVEL)
0632         << " labelling photon :"
0633         << " track " << track
0634         << " label " << label
0635         << " label.desc " << label->desc()
0636         ;
0637 
0638     saveOrLoadStates(label->id);  // moved here as labelling happens once per torch/input photon
0639 }
0640 
0641 
0642 void U4Recorder::PreUserTrackingAction_Optical_FabricateLabel_( const G4Track* track, int* label )
0643 {
0644     spho& q = reinterpret_cast<spho&>(*label) ;
0645     if(q.isPlaceholder())
0646     {
0647         int track_id = U4Track::Id(track);
0648         q.set_fabricated_(track_id);
0649     }
0650 }
0651 
0652 
0653 /**
0654 U4Recorder::GetLabel
0655 ----------------------
0656 
0657 Unlike the above PreUserTrackingAction_Optical_GetLabel
0658 this does not handle the case of the track not being labelled.
0659 
0660 **/
0661 
0662 void U4Recorder::GetLabel( spho& ulabel, const G4Track* track )
0663 {
0664 #ifdef WITH_CUSTOM4_OLD
0665     C4Pho* label = C4TrackInfo<C4Pho>::GetRef(track);
0666 #elif WITH_CUSTOM4
0667     C4Pho* label = C4TrackInfo::GetRef(track);
0668 #else
0669     spho* label = STrackInfo::GetRef(track);
0670 #endif
0671     assert( label && label->isDefined() && "all photons are expected to be labelled" );
0672 
0673 #ifdef WITH_CUSTOM4
0674     assert( C4Pho::N == spho::N );
0675 #endif
0676     std::array<int,spho::N> a_label ;
0677     label->serialize(a_label) ;
0678 
0679     ulabel.load(a_label);
0680 }
0681 
0682 /**
0683 U4Recorder::saveOrLoadStates to/from NP g4state array managed by SEvt
0684 -----------------------------------------------------------------------
0685 
0686 Called from U4Recorder::PreUserTrackingAction_Optical
0687 
0688 For pure-optical Geant4 photon rerunning without pre-cooked randoms
0689 need to save the engine state into SEvt prior to using
0690 any Geant4 randoms for the photon. So can restore the random engine
0691 back to this state.
0692 
0693 NB for rerunning to reproduce a selected single photon this requires:
0694 
0695 1. optical primaries have corresponding selection applied in SGenerate::GeneratePhotons
0696    as called from U4VPrimaryGenerator::GeneratePrimaries
0697 
0698 2. U4Track::SetId adjusts track id to the rerun id within
0699    U4Recorder::PreUserTrackingAction_Optical prior to calling this
0700 
0701 **/
0702 
0703 void U4Recorder::saveOrLoadStates( int id )
0704 {
0705     bool first_event = eventID == 0 ;
0706     LOG_IF(LEVEL, !first_event ) << " skip as not first_event eventID " << eventID ;
0707     if(!first_event) return ;
0708 
0709     bool g4state_save = SEventConfig::IsRunningModeG4StateSave() ;
0710     bool g4state_rerun = SEventConfig::IsRunningModeG4StateRerun() ;
0711     bool g4state_active =  g4state_save || g4state_rerun ;
0712     if( g4state_active == false ) return ;
0713 
0714     SEvt* sev = SEvt::Get_ECPU();
0715 
0716     if(g4state_save)
0717     {
0718         NP* g4state = sev->gatherG4State();
0719         LOG_IF( fatal, g4state == nullptr ) << " cannot U4Engine::SaveState with null g4state " ;
0720         assert( g4state );
0721 
0722         int max_state = g4state ? g4state->shape[0] : 0  ;
0723 
0724         if( id == SEventConfig::_G4StateRerun )
0725         {
0726             LOG(LEVEL)
0727                 << "U4Engine::SaveState for id (SEventConfig::_G4StateRerun) " << id
0728                 << " from the max_state " << max_state
0729                 << std::endl
0730                 << U4Engine::DescStateArray()
0731                 ;
0732         }
0733         U4Engine::SaveState( g4state, id );
0734     }
0735     else if(g4state_rerun)
0736     {
0737         const NP* g4state = sev->getG4State();
0738         LOG_IF( fatal, g4state == nullptr ) << " cannot U4Engine::RestoreState with null g4state " ;
0739         assert( g4state );
0740 
0741         U4Engine::RestoreState( g4state, id );
0742 
0743 
0744         if( id == SEventConfig::_G4StateRerun )
0745         {
0746             rerun_rand = U4UniformRand::Get(1000);
0747             U4UniformRand::UU = rerun_rand ;
0748             SEvt::UU = rerun_rand ;  // better hitching it somewhere thats always accessible
0749 
0750             LOG(LEVEL)
0751                 << "U4Engine::RestoreState for id (SEventConfig::_G4StateRerun)  " << id
0752                 << std::endl
0753                 << U4Engine::DescStateArray()
0754                 << std::endl
0755                 << " rerun_rand (about to be consumed, did RestoreState after collecting them)  "
0756                 << std::endl
0757                 << rerun_rand->repr<double>()
0758                 << std::endl
0759                 ;
0760 
0761             U4Engine::RestoreState( g4state, id );
0762         }
0763     }
0764 }
0765 
0766 /**
0767 U4Recorder::saveRerunRand
0768 ---------------------------
0769 
0770 TODO: adopt SEvt::add_array
0771 
0772 **/
0773 
0774 
0775 void U4Recorder::saveRerunRand(const char* dir) const
0776 {
0777     if( rerun_rand == nullptr ) return ;
0778 
0779     int id = SEventConfig::_G4StateRerun ;
0780     std::string name = U::FormName("U4Recorder_G4StateRerun_", id, ".npy" );
0781     rerun_rand->save( dir, name.c_str());
0782 }
0783 
0784 /**
0785 U4Recorder::SaveMeta
0786 ----------------------
0787 
0788 This is called from U4Recorder::EndOfEventAction after saving the SEvt
0789 in order to have savedir.
0790 
0791 SPECS.names
0792     added and enumerations used in UserSteppingAction_Optical
0793     enumeration spec values at each point a.f.aux[:,:,2,3].view(np.int32)
0794 
0795 With standalone testing this is called from U4App::EndOfEventAction/U4App::SaveMeta
0796 
0797 HMM this is saving extra metadata onto the SEvt savedir.
0798 Could instead add SEvt API for addition of metadata,
0799 avoiding need to passaround savedir.
0800 This would make it easier to add SEvt metadata
0801 from within the monolith as just need access to SEvt
0802 and dont need to catch it after the save.
0803 
0804 **/
0805 
0806 void U4Recorder::SaveMeta(const char* savedir)  // static
0807 {
0808     if(savedir == nullptr) return ;
0809 
0810     assert(INSTANCE);
0811     INSTANCE->saveRerunRand(savedir);
0812 }
0813 
0814 
0815 NP* U4Recorder::MakeMetaArray() // static
0816 {
0817     std::string descfakes = U4Recorder::DescFakes() ;
0818     LOG(LEVEL) << descfakes ;
0819 
0820     NP* u4r = NP::Make<int>(1) ; // dummy array providing somewhere to hang the SPECS
0821     u4r->fill(0) ;
0822     u4r->set_names(U4Recorder::SPECS.names);
0823     u4r->set_meta<std::string>("DescFakes", descfakes );
0824 
0825     return u4r ;
0826 }
0827 
0828 
0829 /**
0830 U4Recorder::PostUserTrackingAction_Optical
0831 
0832 **/
0833 
0834 void U4Recorder::PostUserTrackingAction_Optical(const G4Track* track)
0835 {
0836     spho ulabel = {} ;
0837     GetLabel(ulabel, track);
0838     PostUserTrackingAction_Optical_(track, ulabel.data());
0839 }
0840 
0841 void U4Recorder::PostUserTrackingAction_Optical_(const G4Track* track, int* label)
0842 {
0843     spho& ulabel = reinterpret_cast<spho&>(*label);
0844     if(!Enabled(ulabel)) return ; // EIDX, GIDX skipping
0845 
0846     G4TrackStatus tstat = track->GetTrackStatus();
0847     LOG(LEVEL) << U4TrackStatus::Name(tstat) ;
0848 
0849     bool is_fStopAndKill = tstat == fStopAndKill ;
0850     bool is_fSuspend     = tstat == fSuspend ;
0851     bool is_fStopAndKill_or_fSuspend = is_fStopAndKill || is_fSuspend  ;
0852     LOG_IF(info, !is_fStopAndKill_or_fSuspend ) << " not is_fStopAndKill_or_fSuspend  post.tstat " << U4TrackStatus::Name(tstat) ;
0853     assert( is_fStopAndKill_or_fSuspend );
0854 
0855 
0856     if(is_fStopAndKill)
0857     {
0858         SEvt* sev = SEvt::Get_ECPU();
0859         U4Random::SetSequenceIndex(-1);
0860         sev->finalPhoton(ulabel);
0861         transient_fSuspend_track = nullptr ;
0862 #ifndef PRODUCTION
0863         bool PIDX_DUMP = ulabel.id == PIDX && PIDX_ENABLED ;
0864         sseq& seq = sev->current_ctx.seq ;
0865 
0866         LOG_IF(info, PIDX_DUMP )    // CURIOUS : THIS IS NOT SHOWING UP
0867             << " l.id " << std::setw(5) << ulabel.id
0868             << " seq " << seq.brief()
0869             ;
0870 
0871         if(PIDX_DUMP) std::cerr
0872             << "U4Recorder::PostUserTrackingAction_Optical.fStopAndKill "
0873             << " ulabel.id " << std::setw(6) << ulabel.id
0874             << " seq.brief " << seq.brief()
0875             << std::endl
0876             ;
0877 #endif
0878     }
0879     else if(is_fSuspend)
0880     {
0881         transient_fSuspend_track = track ;
0882     }
0883     LOG(LEVEL) << "]" ;
0884 }
0885 
0886 /**
0887 U4Recorder::UserSteppingAction_Optical
0888 ---------------------------------------
0889 
0890 **Step Point Recording**
0891 
0892 Each step has (pre,post) and post becomes pre of next step, so there
0893 are two ways to record all points.
0894 *post-based* seems preferable as truncation from various limits will complicate
0895 the tail of the recording.
0896 
0897 1. post-based::
0898 
0899    step 0: pre + post
0900    step 1: post
0901    step 2: post
0902    ...
0903    step n: post
0904 
0905 2. pre-based::
0906 
0907    step 0: pre
0908    step 1: pre
0909    step 2: pre
0910    ...
0911    step n: pre + post
0912 
0913 Q: What about reemission continuation ?
0914 A: The RE point should be at the same point as the AB that it scrubs,
0915    so the continuing step zero should only record *post*
0916 
0917 **Detecting First Step**
0918 
0919 HMM: need to know the step index, actually just need to know that are at the first step.
0920 Can get that by counting bits in the flagmask, as only the first step will have only
0921 one bit set from the genflag. The single bit genflag gets set by SEvt::beginPhoton
0922 so only the first call to UserSteppingAction_Optical after PreUserTrackingAction_Optical
0923 will fulfil *single_bit*.
0924 
0925 HMM: but if subsequent step points failed to set a non-zero flag could get that repeated
0926 
0927 * YEP: this is happening when first post is on a fake
0928 
0929 **bop info is mostly missing**
0930 
0931 *bop* was formerly only available WITH_PMTFASTSIM whilst using InstrumentedG4OpBoundaryProcess
0932 as that ISA SOpBoundaryProcess giving access via SOpBoundaryProcess::INSTANCE
0933 have now generalize that to work with CustomG4OpBoundaryProcess
0934 
0935 **Track Labelling**
0936 
0937 Q: Where is the STrackInfo labelling added to the track with FastSim ?
0938 A: Labelling added to track at the tail of the FastSim DoIt, eg "jcv junoPMTOpticalModel"
0939 
0940 **Limited Applicability Quantities**
0941 
0942 1. For most step points the customBoundaryStatus is not applicable
0943    it only applies to very specfic surfaces.
0944    So getting it for every step point is kinda confusing.
0945    Need to scrub it when it doesnt apply, and cannot
0946    do that using is_boundary_flag.
0947    How to detect when it is relevant from here ?
0948 
0949 2. Similarly when not at boundary the recoveredNormal is meaningless
0950 
0951 
0952 **How to skip same material fakes from the FastSim-compromised-kludged-unnatural geometry ?**
0953 
0954 ::
0955 
0956           Vacuum | Vacuum
0957                  |
0958          -------+|+-------
0959                  |
0960 
0961 
0962 **current_aux**
0963 
0964 ::
0965 
0966    current_aux.q1.i.w  : ascii status integer 'F' for first
0967 
0968 
0969 **How to incorporate info from ProcessHits into the SEvt ?**
0970 
0971 g4-cls G4SteppingManager::
0972 
0973     230 // Send G4Step information to Hit/Dig if the volume is sensitive
0974     231    fCurrentVolume = fStep->GetPreStepPoint()->GetPhysicalVolume();
0975     232    StepControlFlag =  fStep->GetControlFlag();
0976     233    if( fCurrentVolume != 0 && StepControlFlag != AvoidHitInvocation) {
0977     234       fSensitive = fStep->GetPreStepPoint()->
0978     235                                    GetSensitiveDetector();
0979     236       if( fSensitive != 0 ) {
0980     237         fSensitive->Hit(fStep);
0981     238       }
0982     239    }
0983     240
0984     241 // User intervention process.
0985     242    if( fUserSteppingAction != 0 ) {
0986     243       fUserSteppingAction->UserSteppingAction(fStep);
0987     244    }
0988 
0989 * G4VSensitive::Hit/G4VSensitive::ProcessHits happens before UserSteppingAction
0990   so if plant the ProcessHits enum into the track label
0991   can then copy that into the current_aux
0992 
0993 
0994 Q: Where does the "TO" flag come from ?
0995 A: See SEvt::beginPhoton
0996 
0997 Q: Can the dependency on boundary process type be avoided ?
0998 A: HMM: Perhaps if CustomG4OpBoundary process inherited from G4OpBoundaryProcess
0999    avoid some complexities but maybe just add others. Would rather not go there.
1000    Prefer simplicity.
1001 
1002 
1003 FIXED : Logging issue with U4
1004 ------------------------------
1005 
1006 Somehow SLOG-logging from U4 had stopped appearing.
1007 Turned out this was caused by the OPTICKS_U4 switch
1008 being accidentally flipped to PRIVATE in CMakeLists.txt
1009 : that must stay PUBLIC for SLOG to work.
1010 
1011 **/
1012 
1013 template <typename T>
1014 void U4Recorder::UserSteppingAction_Optical(const G4Step* step)
1015 {
1016     const G4Track* track = step->GetTrack();
1017     G4VPhysicalVolume* pv = track->GetVolume() ;
1018     const G4VTouchable* touch = track->GetTouchable();
1019 
1020     LOG(LEVEL) << "[  pv "  << ( pv ? pv->GetName() : "-" ) ;
1021 
1022     spho ulabel = {} ;
1023     GetLabel( ulabel, track );
1024     if(!Enabled(ulabel)) return ;   // EIDX, GIDX skipping
1025 
1026     const G4StepPoint* pre = step->GetPreStepPoint() ;
1027     const G4StepPoint* post = step->GetPostStepPoint() ;
1028 
1029     G4ThreeVector delta = step->GetDeltaPosition();
1030     double step_mm = delta.mag()/mm  ;
1031 
1032     SEvt* sev = SEvt::Get_ECPU();
1033     sev->checkPhotonLineage(ulabel);
1034 
1035     sphoton& current_photon = sev->current_ctx.p ;
1036 
1037 #ifndef PRODUCTION
1038     quad4&   current_aux    = sev->current_ctx.aux ;
1039     current_aux.zero_v(3, 3);   // may be set below
1040 #endif
1041 
1042     // first_flag identified by the flagmask having a single bit (all genflag are single bits, set in beginPhoton)
1043     bool first_flag = current_photon.flagmask_count() == 1 ;
1044     if(first_flag)
1045     {
1046         LOG(LEVEL) << " first_flag, track " << track ;
1047         U4StepPoint::Update(current_photon, pre);   // populate current_photon with pos,mom,pol,time,wavelength
1048 #ifndef PRODUCTION
1049         current_aux.q1.i.w = int('F') ;
1050 #endif
1051         sev->pointPhoton(ulabel);        // sctx::point copying current into buffers
1052     }
1053 
1054     bool warn = true ;
1055     bool is_tir = false ;
1056     unsigned flag = U4StepPoint::Flag<T>(post, warn, is_tir ) ;
1057 
1058     bool is_fastsim_flag = flag == DEFER_FSTRACKINFO ;
1059     bool is_boundary_flag = OpticksPhoton::IsBoundaryFlag(flag) ;  // SD SA DR SR BR BT
1060     bool is_surface_flag = OpticksPhoton::IsSurfaceDetectOrAbsorbFlag(flag) ;  // SD SA
1061     bool is_detect_flag = OpticksPhoton::IsDetectFlag(flag) ;  // SD
1062     // TODO: GPU side now using EC for the flagmask
1063 
1064 #ifndef PRODUCTION
1065     if(is_boundary_flag) CollectBoundaryAux<T>(&current_aux) ;
1066 #endif
1067 
1068 /*
1069 #ifdef U4RECORDER_EXPENSIVE_IINDEX
1070     // doing replica number search for every step is very expensive and often pointless
1071     // its the kind of thing to do only for low stats or simple geometry running
1072     current_photon.iindex = U4Touchable::ReplicaNumber(touch, REPLICA_NAME_SELECT);
1073 #else
1074     current_photon.iindex = is_surface_flag ? U4Touchable::ReplicaNumber(touch, REPLICA_NAME_SELECT) : -2 ;
1075 #endif
1076 */
1077 
1078     unsigned iindex = is_detect_flag ?
1079               U4Touchable::ImmediateReplicaNumber(touch)
1080               :
1081               U4Touchable::AncestorReplicaNumber(touch)
1082               ;
1083 
1084     current_photon.set_iindex__( iindex );  // WHAT ABOUT HITCOUNT ?
1085 
1086 
1087     LOG(LEVEL)
1088         << " flag " << flag
1089         << " " << OpticksPhoton::Flag(flag)
1090         << " is_tir " << is_tir
1091         << " is_fastsim_flag " << is_fastsim_flag
1092         << " is_boundary_flag " << is_boundary_flag
1093         << " is_surface_flag " << is_surface_flag
1094         ;
1095 
1096     // DEFER_FSTRACKINFO : special flag signalling that
1097     // the FastSim DoIt status needs to be accessed via the
1098     // trackinfo label
1099     //
1100     // FastSim status char "?DART" is set at the tail of junoPMTOpticalModel::DoIt
1101 
1102     if(is_fastsim_flag)
1103     {
1104         char fstrackinfo_stat = ulabel.uc4.w ;
1105         // label->uc4.w = '_' ;
1106         // scrub after access : HMM IS THIS NEEDED ? NOT EASY NOW THAN USE COPY: ulabel
1107 
1108         switch(fstrackinfo_stat)
1109         {
1110            case 'T': flag = BOUNDARY_TRANSMIT ; break ;
1111            case 'R': flag = BOUNDARY_REFLECT  ; break ;
1112            case 'A': flag = SURFACE_ABSORB    ; break ;
1113            case 'D': flag = SURFACE_DETECT    ; break ;
1114            case '_': flag = 0                 ; break ;
1115            default:  flag = 0                 ; break ;
1116         }
1117         LOG_IF(error, flag == 0)
1118             << " DEFER_FSTRACKINFO "
1119             << " FAILED TO GET THE FastSim status from trackinfo "
1120             << " fstrackinfo_stat " << fstrackinfo_stat
1121             << " fstrackinfo_stat == '\0' " << ( fstrackinfo_stat == '\0' ? "YES" : "NO " )
1122             ;
1123 
1124         LOG(LEVEL)
1125             << " DEFER_FSTRACKINFO "
1126             << " fstrackinfo_stat " << fstrackinfo_stat
1127             << " flag " << OpticksPhoton::Flag(flag)
1128             ;
1129     }
1130 
1131     LOG_IF(error, flag == 0)
1132         << " ERR flag zero : post "
1133         << std::endl
1134         << "U4StepPoint::DescPositionTime(post)"
1135         << std::endl
1136         << U4StepPoint::DescPositionTime(post)
1137         << std::endl
1138         << "U4StepPoint::Desc<T>(post)"
1139         << std::endl
1140         << U4StepPoint::Desc<T>(post)
1141         ;
1142 
1143     assert( flag > 0 );
1144 
1145     bool PIDX_DUMP = ulabel.id == PIDX && PIDX_ENABLED ;
1146     bool is_fake = false ;
1147     unsigned fakemask = 0 ;
1148     double   fake_duration(-2.);
1149 
1150     int st = -2 ;
1151 
1152     if(FAKES_SKIP && ( flag == BOUNDARY_TRANSMIT || flag == BOUNDARY_REFLECT ) )
1153     {
1154        // fake detection is very expensive : only do when needed
1155 
1156         std::string spec_ = U4Step::Spec(step) ;  // ctrl-c sampling suspect slow
1157         const char* spec = spec_.c_str();
1158         fakemask = ClassifyFake(step, flag, spec, PIDX_DUMP, &fake_duration ) ;
1159         is_fake = fakemask > 0 ;
1160         st = ( is_fake ? -1 : 1 )*SPECS.add(spec, false ) ;
1161     }
1162 
1163 
1164     if(PIDX_DUMP)
1165     {
1166         std::cout
1167             << "U4Recorder::UserSteppingAction_Optical"
1168             << " PIDX " << PIDX
1169             << " post " << U4StepPoint::DescPositionTime(post)
1170             << " is_fastsim_flag " << is_fastsim_flag
1171             << " FAKES_SKIP " << FAKES_SKIP
1172             << " is_fake " << is_fake
1173             << " fakemask " << fakemask
1174             << std::endl
1175             ;
1176         LOG(LEVEL) << U4StepPoint::DescPositionTime(post) ;
1177     }
1178 
1179 
1180 
1181 #ifndef PRODUCTION
1182     //current_aux.q2.u.z = ulabel.uc4packed();  // CAUTION: stomping on cdbg.pmtid setting above
1183     //current_aux.q2.i.w = st ;                  // CAUTION: stomping on cdbg.spare setting above
1184 
1185     current_aux.q2.i.z = fakemask ;              // CAUTION: stomping on cdbg.pmtid setting above
1186     current_aux.q2.f.w = float(fake_duration) ;  // CAUTION: stomping on cdbg.spare setting above
1187 #endif
1188 
1189 
1190 
1191     bool slow_fake = fake_duration > SLOW_FAKE ;
1192 
1193     LOG_IF(info, PIDX_DUMP || slow_fake  )
1194         << " l.id " << std::setw(3) << ulabel.id
1195         << " step_mm " << std::fixed << std::setw(10) << std::setprecision(4) << step_mm
1196         << " abbrev " << OpticksPhoton::Abbrev(flag)
1197         << " st " << std::setw(3) << st
1198         << " is_fake " << ( is_fake ? "YES" : "NO " )
1199         << " fakemask " << fakemask
1200         << " fake_duration " << fake_duration
1201         << " slow_fake " << slow_fake
1202         << " SLOW_FAKE " << SLOW_FAKE
1203         << " U4Fake::Desc " << U4Fake::Desc(fakemask)
1204         ;
1205 
1206     if( flag == NAN_ABORT )
1207     {
1208         LOG(LEVEL) << " skip post saving for StepTooSmall ulabel.id " << ulabel.id  ;
1209     }
1210     else if( FAKES_SKIP && is_fake  )
1211     {
1212         LOG(LEVEL) << " FAKES_SKIP skip post identified as fake ulabel.id " << ulabel.id  ;
1213     }
1214     else
1215     {
1216         G4TrackStatus tstat = track->GetTrackStatus();
1217 
1218         Check_TrackStatus_Flag(tstat, flag, "UserSteppingAction_Optical" );
1219 
1220         U4StepPoint::Update(current_photon, post);
1221 
1222 
1223 
1224         unsigned eph = ulabel.eph();  // SProcessHits_EPH.h
1225         if( flag == SURFACE_DETECT || flag == SURFACE_ABSORB || flag == BULK_ABSORB )
1226         {
1227             EPH_FlagCheck(flag,eph);
1228         }
1229         if( flag == SURFACE_DETECT )
1230         {
1231             flag = EPH_EFFICIENCY_COLLECT_OR_CULL( eph );
1232             LOG_IF(info, SEvt::EPH_) << "changed SD flag to " << OpticksPhoton::Abbrev(flag) << " due to eph " << EPH::Name(eph) ;
1233         }
1234 
1235 
1236         current_photon.set_flag( flag );
1237 
1238         if(U4Step::CF) U4Step::MockOpticksBoundaryIdentity(current_photon, step, ulabel.id );
1239 
1240         sev->pointPhoton(ulabel);     // save SEvt::current_photon/rec/seq/prd into sevent
1241     }
1242 
1243     if(UserSteppingAction_Optical_ClearNumberOfInteractionLengthLeft)
1244     {
1245         U4Process::ClearNumberOfInteractionLengthLeft(*track, *step);
1246     }
1247 
1248     LOG(LEVEL) << "]" ;
1249 }
1250 
1251 
1252 /**
1253 U4Recorder::EFFICIENCY_COLLECT_OR_CULL
1254 ---------------------------------------
1255 
1256 Distinguish original flag of SURFACE_DETECT into EFFICIENCY_COLLECT OR EFFICIENCY_CULL
1257 depending on eph enum communicated from Geant4 ProcessHits.
1258 
1259 +--------------------+----------------------------------------------+-----------------------+
1260 |  original_flag     |  EPH:: flags                                 |  returned flag        |
1261 +====================+==============================================+=======================+
1262 |  SURFACE_DETECT    |  SAVENORM YMERGE                             |  EFFICIENCY_COLLECT   |
1263 +--------------------+----------------------------------------------+-----------------------+
1264 |  SURFACE_DETECT    |   NDECULL                                    |  EFFICIENCY_CULL      |
1265 +--------------------+----------------------------------------------+-----------------------+
1266 
1267 **/
1268 
1269 unsigned U4Recorder::EPH_EFFICIENCY_COLLECT_OR_CULL(unsigned eph)  // static
1270 {
1271     bool eph_collect = eph == EPH::SAVENORM || eph == EPH::YMERGE ;
1272     bool eph_cull    = eph == EPH::NDECULL  ;
1273     assert( eph_collect ^ eph_cull ) ;
1274     return eph_collect ? EFFICIENCY_COLLECT : EFFICIENCY_CULL ;
1275 }
1276 
1277 
1278 /**
1279 U4Recorder::EPH_FlagCheck
1280 --------------------------
1281 
1282 See SProcessHits_EPH.h
1283 
1284 Arrive at RIP final flag based on the eph enum from ProcessHits, that
1285 got here via the track info label plus U4RecorderAnaMgr/U4Recorder
1286 
1287 see "jcv junoSD_PMT_v2"
1288 
1289 
1290 Observed correspondence between flags
1291 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1292 
1293 +--------------------+----------------------------------------------+-----------------------+
1294 |  original_flag     |  EPH:: flags                                 |  returned flag        |
1295 +====================+==============================================+=======================+
1296 |  SURFACE_ABSORB    |  UNSET NEDEP                                 |  SURFACE_ABSORB       |
1297 +--------------------+----------------------------------------------+-----------------------+
1298 |  SURFACE_DETECT    |  SAVENORM YMERGE                             |  EFFICIENCY_COLLECT   |
1299 +--------------------+----------------------------------------------+-----------------------+
1300 |  SURFACE_DETECT    |   NDECULL                                    |  EFFICIENCY_CULL      |
1301 +--------------------+----------------------------------------------+-----------------------+
1302 |  BULK_ABSORB       |  UNSET NEDEP NBOUND                          |  BULK_ABSORB          |
1303 +--------------------+----------------------------------------------+-----------------------+
1304 
1305 **/
1306 void U4Recorder::EPH_FlagCheck(unsigned original_flag, unsigned eph)  // static
1307 {
1308     bool expected_original_flag = original_flag == SURFACE_DETECT || original_flag == SURFACE_ABSORB || original_flag == BULK_ABSORB ;
1309 
1310     bool is_eph_surface_absorb = eph == EPH::NEDEP || eph == EPH::UNSET ;
1311     bool is_eph_bulk_absorb    = eph == EPH::NEDEP || eph == EPH::UNSET || eph == EPH::NBOUND ;
1312     bool is_eph_collect        = eph == EPH::SAVENORM || eph == EPH::YMERGE ;
1313     bool is_eph_cull           = eph == EPH::NDECULL  ;
1314     bool is_eph_detect         = eph == EPH::SAVENORM || eph == EPH::YMERGE || eph == EPH::NDECULL  ;
1315 
1316     bool consistent_flag = (  original_flag == SURFACE_DETECT && is_eph_detect ) ||
1317                            (  original_flag == SURFACE_ABSORB && is_eph_surface_absorb ) ||
1318                            (  original_flag == BULK_ABSORB    && is_eph_bulk_absorb ) ;
1319 
1320     bool surprise_flag = ( original_flag == SURFACE_ABSORB  && is_eph_cull );
1321 
1322 
1323     LOG_IF(info, !consistent_flag || !expected_original_flag )
1324          << " original_flag " << OpticksPhoton::Flag(original_flag)
1325          << " expected_original_flag " << ( expected_original_flag ? "YES" : "NO " )
1326          << " eph " << std::setw(2) << eph
1327          << " eph_ " << EPH::Name(eph)
1328          << " is_eph_collect " << ( is_eph_collect ? "YES" : "NO " )
1329          << " is_eph_cull " << ( is_eph_cull ? "YES" : "NO " )
1330          << " consistent_flag " << ( consistent_flag ? "YES" : "NO " )
1331          << " surpise_flag " << (  surprise_flag ?  "YES" : "NO" )
1332          ;
1333 
1334     assert( consistent_flag || surprise_flag );
1335     assert( expected_original_flag );
1336 }
1337 
1338 
1339 
1340 
1341 
1342 
1343 
1344 const double U4Recorder::SLOW_FAKE = ssys::getenvdouble("U4Recorder__SLOW_FAKE", 1e-2) ;
1345 const bool U4Recorder::UserSteppingAction_Optical_ClearNumberOfInteractionLengthLeft = ssys::getenvbool(UserSteppingAction_Optical_ClearNumberOfInteractionLengthLeft_) ;
1346 
1347 
1348 
1349 
1350 
1351 
1352 
1353 
1354 
1355 
1356 
1357 /**
1358 U4Recorder::CollectBoundaryAux
1359 -------------------------------
1360 
1361 Templated use from U4Recorder::UserSteppingAction_Optical
1362 
1363 **/
1364 
1365 template <typename T>
1366 void U4Recorder::CollectBoundaryAux(quad4* )  // static
1367 {
1368     LOG(LEVEL) << "generic do nothing" ;
1369 }
1370 
1371 
1372 /**
1373 U4Recorder::CollectBoundaryAux
1374 --------------------------------
1375 
1376 CAN THIS BE MOVED ELSEWHERE TO SIMPLIFY DEPS ?
1377 
1378 Maybe move into one of::
1379 
1380     CustomG4OpBoundaryProcess
1381     CustomART
1382     CustomART_Debug
1383 
1384 **/
1385 
1386 #if defined(WITH_CUSTOM4)
1387 template<>
1388 void U4Recorder::CollectBoundaryAux<C4OpBoundaryProcess>(quad4* current_aux)
1389 {
1390     C4OpBoundaryProcess* bop = U4OpBoundaryProcess::Get<C4OpBoundaryProcess>() ;
1391     assert(bop) ;
1392     assert(current_aux);
1393 
1394     char customStatus = bop ? bop->m_custom_status : 'B' ;
1395     C4CustomART* cart   = bop ? bop->m_custom_art : nullptr ;
1396     const double* recoveredNormal =  bop ? (const double*)&(bop->theRecoveredNormal) : nullptr ;
1397 
1398 #ifdef C4_DEBUG
1399     C4CustomART_Debug* cdbg = cart ? &(cart->dbg) : nullptr ;
1400 #else
1401     C4CustomART_Debug* cdbg = nullptr ;
1402 #endif
1403 
1404     LOG(LEVEL)
1405         << " bop " << ( bop ? "Y" : "N" )
1406         << " cart " << ( cart ? "Y" : "N" )
1407         << " cdbg " << ( cdbg ? "Y" : "N" )
1408         << " current_aux " << ( current_aux ? "Y" : "N" )
1409         << " bop.m_custom_status " << customStatus
1410         << " CustomStatus::Name " << CustomStatus::Name(customStatus)
1411         ;
1412 
1413     if(cdbg && customStatus == 'Y') current_aux->load( cdbg->data(), C4CustomART_Debug::N ) ;
1414     current_aux->set_v(3, recoveredNormal, 3);   // nullptr are just ignored
1415     current_aux->q3.i.w = int(customStatus) ;    // moved from q1 to q3
1416 }
1417 
1418 #elif defined(WITH_PMTSIM) || defined(WITH_CUSTOM_BOUNDARY)
1419 
1420 // THIS CODE IS TO BE DELETED ONCE THE ABOVE IS WORKING
1421 
1422 template<>
1423 void U4Recorder::CollectBoundaryAux<CustomG4OpBoundaryProcess>(quad4* current_aux)
1424 {
1425     CustomG4OpBoundaryProcess* bop = U4OpBoundaryProcess::Get<CustomG4OpBoundaryProcess>() ;
1426     assert(bop) ;
1427 
1428     char customStatus = bop ? bop->m_custom_status : 'B' ;
1429     CustomART* cart   = bop ? bop->m_custom_art : nullptr ;
1430     const double* recoveredNormal =  bop ? (const double*)&(bop->theRecoveredNormal) : nullptr ;
1431     CustomART_Debug* cdbg = cart ? &(cart->dbg) : nullptr ;
1432 
1433     LOG(LEVEL)
1434         << " bop " << ( bop ? "Y" : "N" )
1435         << " cart " << ( cart ? "Y" : "N" )
1436         << " cdbg " << ( cdbg ? "Y" : "N" )
1437         << " current_aux " << ( current_aux ? "Y" : "N" )
1438         << " bop.m_custom_status " << customStatus
1439         << " CustomStatus::Name " << CustomStatus::Name(customStatus)
1440         ;
1441 
1442     assert( current_aux );
1443     if(cdbg && customStatus == 'Y')
1444     {
1445         // much of the contents of CustomART,CustomART_Debug
1446         // only meaningful after doIt call : hence require customStatus 'Y'
1447 
1448         current_aux->q0.f.x = cdbg->A ;
1449         current_aux->q0.f.y = cdbg->R ;
1450         current_aux->q0.f.z = cdbg->T ;
1451         current_aux->q0.f.w = cdbg->_qe ;
1452 
1453         current_aux->q1.f.x = cdbg->An ;
1454         current_aux->q1.f.y = cdbg->Rn ;
1455         current_aux->q1.f.z = cdbg->Tn ;
1456         current_aux->q1.f.w = cdbg->escape_fac ;  // HMM: this stomps on  ascii status integer
1457 
1458         current_aux->q2.f.x = cdbg->minus_cos_theta ;
1459         current_aux->q2.f.y = cdbg->wavelength_nm  ;
1460         current_aux->q2.f.z = cdbg->pmtid ;       // HMM: q2.i.z maybe set to fakemask below
1461         current_aux->q2.f.w = -1. ;               // HMM: q2.i.w gets set to step spec index
1462     }
1463 
1464     current_aux->set_v(3, recoveredNormal, 3);   // nullptr are just ignored
1465     current_aux->q3.i.w = int(customStatus) ;    // moved from q1 to q3
1466 }
1467 #endif
1468 
1469 
1470 
1471 
1472 
1473 /**
1474 U4Recorder::ClassifyFake
1475 --------------------------
1476 
1477 Think about stepping around geometry with back foot "pre" and front foot "post".
1478 As take the next step the former "post" becomes the "pre" of the next step.
1479 
1480 U4Recorder operates by setting the flag and collecting info regarding
1481 "post" points of each step (pre, post) pair. The "pre" point only gets
1482 collected for the first step.
1483 
1484 Consider fake skipping a Vac/Vac coincident border::
1485 
1486 
1487                               | |                        |
1488                               | |                        |
1489        0----------------------1-2------------------------3---
1490                               | |                        |
1491                               | |                        |
1492                             coincident
1493                             border between
1494                             volumes
1495 
1496 Without any fake skipping would have::
1497 
1498 * 0->1
1499 * 1->2
1500 * 2->3
1501 
1502 With fake skipping that becomes:
1503 
1504 * 0->3
1505 
1506 Notice that there is some conflation over whether should classify fake steps or fake points.
1507 Handling fake points would be cleaner but the info of the other point might be useful,
1508 so leaving asis given that current incantation seems to work.
1509 
1510 +-----------------+---------------------------------------------------------------------------+
1511 | enum (0x1 << n) | U4Recorder::ClassifyFake heuristics, all contribute to fakemask           |
1512 +=================+===========================================================================+
1513 | FAKE_STEP_MM    | step length less than EPSILON thats not a reflection turnaround           |
1514 +-----------------+---------------------------------------------------------------------------+
1515 | FAKE_FDIST      | distance to body_phys volume in direction of photon is less than EPSILON  |
1516 +-----------------+---------------------------------------------------------------------------+
1517 | FAKE_SURFACE    | body_phys solid frame localPoint EInside is kSurface (powerful)           |
1518 +-----------------+---------------------------------------------------------------------------+
1519 | FAKE_MANUAL     | manual selection via spec label (not recommended anymore)                 |
1520 +-----------------+---------------------------------------------------------------------------+
1521 | FAKE_VV_INNER12 | U4Step::IsSameMaterialPVBorder Vacuum inner1_phys/inner2_phys             |
1522 +-----------------+---------------------------------------------------------------------------+
1523 
1524 **/
1525 
1526 const double U4Recorder::EPSILON = 1e-4 ;
1527 const bool U4Recorder::ClassifyFake_FindPV_r = ssys::getenvbool("U4Recorder__ClassifyFake_FindPV_r" );
1528 stimer* U4Recorder::TIMER = new stimer ;
1529 
1530 unsigned U4Recorder::ClassifyFake(const G4Step* step, unsigned flag, const char* spec, bool dump, double* duration )
1531 {
1532     if(duration) TIMER->start() ;
1533 
1534     unsigned fakemask = 0 ;
1535     G4ThreeVector delta = step->GetDeltaPosition();
1536     double step_mm = delta.mag()/mm  ;
1537 
1538     // these are cheap and easy fake detection
1539     bool is_reflect_flag = OpticksPhoton::IsReflectFlag(flag);
1540     bool is_small_step   = step_mm < EPSILON && is_reflect_flag == false ;
1541     bool is_vacvac_inner1_inner2 = U4Step::IsSameMaterialPVBorder(step, "Vacuum", "inner1_phys", "inner2_phys") ;
1542 
1543     if(is_small_step)            fakemask |= U4Fake::FAKE_STEP_MM ;
1544     if(is_vacvac_inner1_inner2)  fakemask |= U4Fake::FAKE_VV_INNER12 ;
1545     if(IsListedFake(spec))       fakemask |= U4Fake::FAKE_MANUAL ;
1546 
1547 
1548     // the below are powerful, but expensive fake detection
1549 
1550     const char* fake_pv_name = "body_phys" ;
1551     const G4Track* track = step->GetTrack();
1552     const G4VTouchable* touch = track->GetTouchable();
1553     const G4VPhysicalVolume* pv = track->GetVolume() ;
1554     const char* pv_name = pv->GetName().c_str();
1555     bool pv_name_proceed =
1556          strstr(pv_name, "PMT")  != nullptr ||
1557          strstr(pv_name, "nnvt") != nullptr ||
1558          strstr(pv_name, "hama") != nullptr
1559          ;
1560 
1561     // finding volume by name in the touch stack is much quicker than the recursive U4Volume::FindPV
1562     const G4VPhysicalVolume* fpv = U4Touchable::FindPV(touch, fake_pv_name, U4Touchable::MATCH_END );
1563     int maxdepth = 2 ;
1564 
1565     if( fpv == nullptr && ClassifyFake_FindPV_r && pv_name_proceed )
1566     {
1567         fpv = U4Volume::FindPV( pv, fake_pv_name, sstr::MATCH_END, maxdepth );
1568     }
1569 
1570     //LOG_IF(info, fpv == nullptr )
1571     // this is happening a lot
1572     if(dump && fpv == nullptr) std::cout
1573         << "U4Recorder::ClassifyFake"
1574         << " fpv null "
1575         << " pv_name " << ( pv_name ? pv_name : "-" )
1576         << " pv_name_proceed " << ( pv_name_proceed ? "YES" : "NO ")
1577         << " ClassifyFake_FindPV_r " << ( ClassifyFake_FindPV_r ? "YES" : "NO " )
1578         << U4Touchable::Desc(touch)
1579         << U4Touchable::Brief(touch)
1580         << std::endl
1581         ;
1582 
1583     G4LogicalVolume* flv = fpv ? fpv->GetLogicalVolume() : nullptr ;
1584     G4VSolid* fso = flv ? flv->GetSolid() : nullptr ;
1585 
1586     const G4AffineTransform& transform = touch->GetHistory()->GetTopTransform();
1587     const G4StepPoint* post = step->GetPostStepPoint() ;
1588     const G4ThreeVector& theGlobalPoint = post->GetPosition();
1589     const G4ThreeVector& theGlobalDirection = post->GetMomentumDirection() ;
1590 
1591     G4ThreeVector theLocalPoint     = transform.TransformPoint(theGlobalPoint);
1592     G4ThreeVector theLocalDirection = transform.TransformAxis(theGlobalDirection);
1593     G4ThreeVector theLocalIntersect(0.,0.,0.)  ;
1594 
1595     EInside fin = kOutside ;
1596     G4double fdist = fso == nullptr ? kInfinity : ssolid::Distance_( fso, theLocalPoint, theLocalDirection, fin, &theLocalIntersect ) ;
1597 
1598 
1599     if(fdist < EPSILON)    fakemask |= U4Fake::FAKE_FDIST ;
1600     if(fin == kSurface)    fakemask |= U4Fake::FAKE_SURFACE ;
1601 
1602     if(duration) *duration = TIMER->done() ;
1603 
1604     //LOG_IF(info, dump)
1605     if(dump) std::cout
1606         << "U4Recorder::ClassifyFake"
1607         << " fdist " << ( fdist == kInfinity ? -1. : fdist )
1608         << " fin " << sgeomdefs::EInside_(fin)
1609         << " fso " << ( fso ? fso->GetName() : "-" )
1610         << " theLocalPoint " << theLocalPoint
1611         << " theLocalDirection " << theLocalDirection
1612         << " theLocalIntersect " << theLocalIntersect
1613         << " fakemask " << fakemask
1614         << " desc " << U4Fake::Desc(fakemask)
1615         << " duration " << std::scientific << ( duration ? *duration : -1. )
1616         << std::endl
1617         ;
1618 
1619     return fakemask ;
1620 }
1621 
1622 
1623 std::vector<std::string>* U4Recorder::FAKES      = ssys::getenv_vec<std::string>("U4Recorder__FAKES", "" );
1624 bool                      U4Recorder::FAKES_SKIP = ssys::getenvbool(             "U4Recorder__FAKES_SKIP") ;
1625 
1626 bool U4Recorder::IsListedFake( const char* spec ){ return ssys::is_listed(FAKES, spec ) ; }
1627 
1628 std::string U4Recorder::DescFakes() // static
1629 {
1630     std::stringstream ss ;
1631     ss << "U4Recorder::DescFakes  " << std::endl
1632        << "U4Recorder::FAKES_SKIP " << ( FAKES_SKIP ? "YES" : "NO " ) << std::endl
1633        << "U4Recorder::FAKES      " << ( FAKES      ? "YES" : "NO " ) << std::endl
1634        << "FAKES.size             " << ( FAKES ? FAKES->size() : -1 ) << std::endl
1635        ;
1636 
1637     if(FAKES) for(int i=0 ; i < int(FAKES->size()) ; i++) ss << (*FAKES)[i] << std::endl ;
1638     std::string str = ss.str();
1639     return str ;
1640 }
1641 
1642 
1643 
1644 
1645 /**
1646 //U4Track::SetStopAndKill(track);
1647 In CFG4 did StopAndKill but so far seems no reason to do that. Probably that was for aligning truncation.
1648 **/
1649 
1650 void U4Recorder::Check_TrackStatus_Flag(G4TrackStatus tstat, unsigned flag, const char* from )
1651 {
1652     LOG(LEVEL) << " step.tstat " << U4TrackStatus::Name(tstat) << " " << OpticksPhoton::Flag(flag) << " from " << from  ;
1653 
1654     if( tstat == fAlive )
1655     {
1656         bool is_live_flag = OpticksPhoton::IsLiveFlag(flag);    // with FastSim seeing fSuspend
1657         LOG_IF(error, !is_live_flag )
1658             << " is_live_flag " << is_live_flag
1659             << " unexpected trackStatus/flag  "
1660             << " trackStatus " << U4TrackStatus::Name(tstat)
1661             << " flag " << OpticksPhoton::Flag(flag)
1662             ;
1663 
1664         //assert( is_live_flag );  SKIP ASSERT
1665     }
1666     else if( tstat == fSuspend )
1667     {
1668         LOG(LEVEL) << " fSuspend : this happens when hand over to FastSim, ie when ModelTrigger:YES " ;
1669     }
1670     else if( tstat == fStopAndKill )
1671     {
1672         bool is_terminal_flag = OpticksPhoton::IsTerminalFlag(flag);
1673         LOG_IF(error, !is_terminal_flag )
1674             << " is_terminal_flag " << is_terminal_flag
1675             << " unexpected trackStatus/flag  "
1676             << " trackStatus " << U4TrackStatus::Name(tstat)
1677             << " flag " << OpticksPhoton::Flag(flag)
1678             ;
1679         assert( is_terminal_flag );
1680     }
1681     else
1682     {
1683         LOG(fatal)
1684             << " unexpected trackstatus "
1685             << " trackStatus " << U4TrackStatus::Name(tstat)
1686             << " flag " << OpticksPhoton::Flag(flag)
1687             ;
1688     }
1689 }
1690 
1691 
1692 
1693 #if defined(WITH_CUSTOM4)
1694 #include "C4OpBoundaryProcess.hh"
1695 template void U4Recorder::UserSteppingAction_Optical<C4OpBoundaryProcess>(const G4Step*) ;
1696 #elif defined(WITH_PMTSIM)
1697 #include "CustomG4OpBoundaryProcess.hh"
1698 template void U4Recorder::UserSteppingAction_Optical<CustomG4OpBoundaryProcess>(const G4Step*) ;
1699 #elif defined(WITH_INSTRUMENTED_DEBUG)
1700 #include "InstrumentedG4OpBoundaryProcess.hh"
1701 template void U4Recorder::UserSteppingAction_Optical<InstrumentedG4OpBoundaryProcess>(const G4Step*) ;
1702 #else
1703 #include "G4OpBoundaryProcess.hh"
1704 template void U4Recorder::UserSteppingAction_Optical<G4OpBoundaryProcess>(const G4Step*) ;
1705 #endif
1706 
1707