Back to home page

EIC code displayed by LXR

 
 

    


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

0001 #pragma once
0002 
0003 #include <csignal>
0004 
0005 class G4Step ; 
0006 class G4StepPoint ; 
0007 class G4LogicalSurface ; 
0008 class G4VPhysicalVolume ; 
0009 
0010 struct sphoton ; 
0011 struct SCF ; 
0012 
0013 
0014 enum { 
0015    U4Step_UNSET, 
0016    U4Step_NOT_AT_BOUNDARY,
0017    U4Step_MOTHER_TO_CHILD, 
0018    U4Step_CHILD_TO_MOTHER,
0019    U4Step_CHILD_TO_CHILD,
0020    U4Step_UNEXPECTED
0021 }; 
0022 
0023 struct U4Step
0024 {
0025     static const SCF* CF ; 
0026 
0027     static constexpr const char* UNSET = "UNSET" ; 
0028     static constexpr const char* NOT_AT_BOUNDARY = "NOT_AT_BOUNDARY" ; 
0029     static constexpr const char* MOTHER_TO_CHILD = "MOTHER_TO_CHILD" ; // AKA enteredDaughter
0030     static constexpr const char* CHILD_TO_MOTHER = "CHILD_TO_MOTHER" ; 
0031     static constexpr const char* CHILD_TO_CHILD  = "CHILD_TO_CHILD" ;  // ABOMINATION SUGGESTING BROKEN GEOMETRY 
0032     static constexpr const char* UNEXPECTED      = "UNEXPECTED" ; 
0033 
0034     static const char* MockOpticksBoundaryIdentity_NOTE ; 
0035     static void MockOpticksBoundaryIdentity(sphoton& current_photon,  const G4Step* step, unsigned idx); 
0036 
0037     static unsigned PackIdentity(unsigned prim_idx, unsigned instance_id); 
0038     static int KludgePrimIdx(const G4Step* step, unsigned type, unsigned idx); 
0039 
0040     static const char* Name(unsigned type); 
0041     static bool IsProblem(unsigned type);  
0042     static unsigned Classify(const G4Step* step); 
0043     static bool IsOnBoundary( const G4Step* step ); 
0044     static std::string BoundarySpec( const G4Step* step ); 
0045     static std::string BoundarySpec_(const G4Step* step ); 
0046     static const G4VSolid* Solid(const G4StepPoint* point ); 
0047     static G4LogicalSurface* GetLogicalSurface(const G4VPhysicalVolume* thePrePV, const G4VPhysicalVolume* thePostPV); 
0048     static std::string Spec(const G4Step* step); 
0049 
0050 
0051     static bool IsSameMaterial(const G4Step* step, const char* q_matname ); 
0052     static bool IsPVBorder(const G4Step* step, const char* q_pvname1, const char* q_pvname2 ); 
0053     static bool IsSameMaterialPVBorder( const G4Step* step, const char* q_matname, const char* q_pvname1, const char* q_pvname2  ); 
0054 
0055 };
0056 
0057 
0058 const SCF* U4Step::CF = SCF::Create() ; 
0059 
0060 
0061 /**
0062 U4Step::MockOpticksBoundaryIdentity
0063 ---------------------------------------
0064 
0065 This only partially mimicks the Opticks identity, using the primname index as stand in for real prim_idx.
0066 That should match Opticks only with simple geom without repeated prim or instances.
0067 
0068 cx/CSGOptiX7.cu::
0069 
0070     406 extern "C" __global__ void __closesthit__ch()
0071     407 {
0072     408     unsigned iindex = optixGetInstanceIndex() ;    // 0-based index within IAS
0073     409     unsigned instance_id = optixGetInstanceId() ;  // user supplied instanceId, see IAS_Builder::Build and InstanceId.h 
0074     410     unsigned prim_idx = optixGetPrimitiveIndex() ; // GAS_Builder::MakeCustomPrimitivesBI_11N  (1+index-of-CSGPrim within CSGSolid/GAS)
0075     411     unsigned identity = (( prim_idx & 0xffff ) << 16 ) | ( instance_id & 0xffff ) ;
0076 
0077 TODO: find way to fully reproduce the Opticks identity with instance index, 
0078 probably that would mean dealing with long lists of volume names : the difficulty
0079 is the factorization which means multiple volumes are within each instance so would
0080 have to list all volumes 
0081 
0082 **/
0083 
0084 const char* U4Step::MockOpticksBoundaryIdentity_NOTE = R"(
0085 U4Step::MockOpticksBoundaryIdentity 
0086 ====================================
0087 
0088 Mocking Opticks requires CFBASE envvar which allows instanciation of SCF
0089 This means that when changing the U4VolumeMaker geometry it is necessary to
0090 run the Opticks gxs.sh GPU simulation first and grab the CSGFoundry geometry 
0091 back to the machine running the Geant4 simulation. 
0092 
0093 eg::
0094 
0095     gx
0096     ./gxs.sh run    # workstation
0097     ./gxs.sh grab   # laptop 
0098 
0099     u4
0100     ./u4s.sh run     # CFBASE is set by the script to pick up the CF geometry
0101 
0102 )"; 
0103 
0104 
0105 inline void U4Step::MockOpticksBoundaryIdentity(sphoton& current_photon,  const G4Step* step, unsigned idx)  // static
0106 {
0107     if(CF == nullptr) std::cerr << MockOpticksBoundaryIdentity_NOTE ; 
0108     assert(CF); 
0109 
0110     std::string spec = BoundarySpec(step) ; // empty when not boundary   
0111     unsigned boundary = spec.empty() ? 0 : CF->getBoundary(spec.c_str()) ; 
0112     unsigned type = U4Step::Classify(step); 
0113     int kludge_prim_idx = KludgePrimIdx(step, type, idx) ; 
0114     unsigned kludge_prim_idx_ = kludge_prim_idx == -1 ? 0xffff : kludge_prim_idx ; 
0115 
0116     float cosThetaSign = 0.f ; // aka orient 
0117 
0118     switch(type)
0119     {
0120         case U4Step_NOT_AT_BOUNDARY: cosThetaSign =  0.f ; break ;
0121         case U4Step_MOTHER_TO_CHILD: cosThetaSign = -1.f ; break ;    // photon direction against the normal 
0122         case U4Step_CHILD_TO_MOTHER: cosThetaSign =  1.f ; break ;    // photon direction with the normal 
0123     }
0124 
0125     /*
0126     if( U4Step::IsProblem(type) || type ==  U4Step_NOT_AT_BOUNDARY || kludge_prim_idx == -1  )
0127     {
0128         std::cerr
0129              << "U4Step::MockOpticksBoundaryIdentity"
0130              << " problem step "
0131              << " idx " << idx
0132              << " type " << type 
0133              << " U4Step::Name " << U4Step::Name(type)
0134              << " cosThetaSign " << cosThetaSign
0135              << " spec " << spec 
0136              << " boundary " << boundary 
0137              << " kludge_prim_idx " << kludge_prim_idx
0138              << " kludge_prim_idx_ " << kludge_prim_idx_
0139              << std::endl 
0140              ;  
0141 
0142         std::cerr << " pre  " << U4StepPoint::DescPositionTime(step->GetPreStepPoint()) << std::endl ; 
0143         std::cerr << " post " << U4StepPoint::DescPositionTime(step->GetPostStepPoint()) << std::endl ; 
0144     }
0145     */
0146  
0147 
0148     // HMM: what does Opticks do for not at boundary ? 
0149     current_photon.set_orient( cosThetaSign );   // sets a bit : would be 0 if not set
0150     current_photon.set_boundary( boundary);
0151     current_photon.identity = PackIdentity( kludge_prim_idx_, 0u) ;
0152 }
0153 inline unsigned U4Step::PackIdentity(unsigned prim_idx, unsigned instance_id) 
0154 {
0155     unsigned identity = (( prim_idx & 0xffff ) << 16 ) | ( instance_id & 0xffff ) ;
0156     return identity ; 
0157 }
0158 
0159 /**
0160 U4Step::KludgePrimIdx
0161 ------------------------
0162 
0163 ::
0164 
0165      +---Rock--------------------------------+
0166      |                                       |
0167      |                                       |
0168      |    +--------------Air----------+      |
0169      |    |                           |      |
0170      |    |            Water          |      |
0171      |    |          /     \          |      |
0172      |    |  +----->|-    >|         >|      |
0173      |    |          \    /           |      |
0174      |    |           ---             |      |
0175      |    |                           |      |
0176      |    +---------------------------+      |
0177      |                                       |
0178      |                                       |
0179      +---------------------------------------+
0180 
0181 
0182 Opticks provides the primIdx of the hit surface, without regard 
0183 for the direction of the photon : because that is a characteristic
0184 of the geometry that is essentially a label on the geometry.
0185 
0186 * Opticks knows the boundary, and the orientation wrt the normal, 
0187   so it knows the material but not the next prim until it does 
0188   another intersection
0189 
0190 * this difference arises from the boundary based Opticks geometry model 
0191   vs volume based Geant4 geometry model
0192 
0193 The first attempt to mimic this with Geant4 always used the "post-solid" 
0194 but that does not match Opticks when leaving solids. To mimic Opticks it is 
0195 necessary to use pre-solid or the post-solid depending on the orientation. 
0196 
0197 **/
0198 
0199 
0200 inline int U4Step::KludgePrimIdx(const G4Step* step, unsigned type, unsigned idx)
0201 {
0202 
0203     const G4StepPoint* pre = step->GetPreStepPoint() ; 
0204     const G4StepPoint* post = step->GetPostStepPoint() ; 
0205  
0206     const G4VSolid* pre_so = U4Step::Solid(pre) ;  
0207     const G4VSolid* post_so = U4Step::Solid(post) ;
0208     G4String pre_so_name = pre_so ? pre_so->GetName() : "" ; 
0209     G4String post_so_name = post_so ? post_so->GetName() : "" ;
0210 
0211     if( pre_so_name.empty() || post_so_name.empty() ) 
0212          std::cerr 
0213              << "U4Step::KludgePrimIdx EMPTY NAME " 
0214              << " pre_so_name " << pre_so_name
0215              << " post_so_name " << post_so_name
0216              << std::endl
0217              ;
0218 
0219  
0220     int pre_prim_idx = CF->getPrimIdx(pre_so_name.c_str()) ; 
0221     int post_prim_idx = CF->getPrimIdx(post_so_name.c_str()) ; 
0222 
0223     int kludge_prim_idx = 0 ;    // this will only match Opticks for very simple geometries 
0224     switch(type)
0225     {
0226         case U4Step_MOTHER_TO_CHILD: kludge_prim_idx = post_prim_idx ; break ; 
0227         case U4Step_CHILD_TO_MOTHER: kludge_prim_idx = pre_prim_idx  ; break ; 
0228         default:                     kludge_prim_idx = 0             ; break ;      
0229     }
0230 
0231     /*
0232     std::cout 
0233         << " U4Step::Name " << U4Step::Name(type)
0234         << " pre_so_name " << std::setw(20) << pre_so_name 
0235         << " pre_prim_idx " << std::setw(4) << pre_prim_idx 
0236         << " post_so_name " << std::setw(20) << post_so_name 
0237         << " post_prim_idx " << std::setw(4) << post_prim_idx 
0238         << " kludge_prim_idx " << std::setw(4) << kludge_prim_idx
0239         << std::endl 
0240         ; 
0241     */ 
0242 
0243     return kludge_prim_idx ; 
0244 }
0245 
0246 
0247 
0248 
0249 
0250 
0251 
0252 inline const char* U4Step::Name(unsigned type)
0253 {
0254     const char* s = nullptr ; 
0255     switch(type)
0256     {
0257        case U4Step_UNSET:           s = UNSET           ; break ; 
0258        case U4Step_NOT_AT_BOUNDARY: s = NOT_AT_BOUNDARY ; break ; 
0259        case U4Step_MOTHER_TO_CHILD: s = MOTHER_TO_CHILD ; break ; 
0260        case U4Step_CHILD_TO_MOTHER: s = CHILD_TO_MOTHER ; break ; 
0261        case U4Step_CHILD_TO_CHILD:  s = CHILD_TO_CHILD  ; break ; 
0262        case U4Step_UNEXPECTED:      s = UNEXPECTED      ; break ; 
0263     }
0264     return s ; 
0265 } 
0266 
0267 inline bool U4Step::IsProblem(unsigned type)
0268 {
0269     return type == U4Step_UNSET || type == U4Step_CHILD_TO_CHILD || type == U4Step_UNEXPECTED ; 
0270 }
0271 
0272 
0273 
0274 /**
0275 U4Step::Classify
0276 -----------------------------
0277 
0278 ::
0279 
0280       +------------------------------------------+
0281       | thePrePV                                 |
0282       |                                          |
0283       |                                          |
0284       |       +------------------------+         |
0285       |       | thePostPV              |         |
0286       |       |                        |         |
0287       |   +--->                        |         |
0288       |       |                        |         |
0289       |       |                        |         |
0290       |       +------------------------+         |
0291       |                                          |
0292       |                                          |
0293       |                                          |
0294       +------------------------------------------+
0295 
0296 
0297       enteredDaughter = thePostPV->GetMotherLogical() == thePrePV ->GetLogicalVolume()   "MOTHER_TO_CHILD"
0298 
0299       enteredDaughter:True 
0300           "inwards" photons
0301 
0302 
0303       +------------------------------------------+
0304       | thePostPV                                |
0305       |                                          |
0306       |                                          |
0307       |       +------------------------+         |
0308       |       | thePrePV               |         |
0309       |       |                        |         |
0310       |       <---+                    |         |
0311       |       |                        |         |
0312       |       |                        |         |
0313       |       +------------------------+         |
0314       |                                          |
0315       |                                          |
0316       |                                          |
0317       +------------------------------------------+
0318 
0319 
0320       enteredDaughter = thePostPV->GetMotherLogical() == thePrePV ->GetLogicalVolume() 
0321 
0322       enteredDaughter:False
0323           "outwards" photons
0324 
0325 **/
0326 
0327 
0328 inline unsigned U4Step::Classify(const G4Step* step)
0329 {
0330     const G4StepPoint* pre = step->GetPreStepPoint() ; 
0331     const G4StepPoint* post = step->GetPostStepPoint() ; 
0332     bool  not_post_boundary = post->GetStepStatus() != fGeomBoundary ;
0333     if(not_post_boundary) return U4Step_NOT_AT_BOUNDARY ; 
0334 
0335     const G4VPhysicalVolume* thePrePV = pre->GetPhysicalVolume();
0336     const G4VPhysicalVolume* thePostPV = post->GetPhysicalVolume();
0337 
0338     bool pre_is_post_mother = thePrePV->GetLogicalVolume() == thePostPV->GetMotherLogical() ;   // aka enteredDaughter
0339     bool pre_mother_is_post = thePrePV->GetMotherLogical() == thePostPV->GetLogicalVolume() ; 
0340     bool pre_mother_is_post_mother = thePrePV->GetMotherLogical() == thePostPV->GetMotherLogical() ; 
0341 
0342     bool expect_exclusive = int(pre_is_post_mother) + int(pre_mother_is_post) + int(pre_mother_is_post_mother) <= 1 ; 
0343     assert( expect_exclusive ) ;   // otherwise broken geometry ? 
0344     if(!expect_exclusive) std::raise(SIGINT);  
0345 
0346     unsigned type = U4Step_UNSET ; 
0347     if(      pre_is_post_mother )        type = U4Step_MOTHER_TO_CHILD ; 
0348     else if( pre_mother_is_post )        type = U4Step_CHILD_TO_MOTHER ; 
0349     else if( pre_mother_is_post_mother ) type = U4Step_CHILD_TO_CHILD ;   // abomination suggesting broken geometry 
0350     else                                 type = U4Step_UNEXPECTED ; 
0351 
0352     return type ; 
0353 }
0354 
0355 
0356 inline bool U4Step::IsOnBoundary( const G4Step* step ) // static 
0357 {
0358     const G4StepPoint* post = step->GetPostStepPoint() ; 
0359     G4bool isOnBoundary = post->GetStepStatus() == fGeomBoundary ;
0360     return isOnBoundary ; 
0361 }
0362 
0363 
0364 
0365 /**
0366 U4Step::BoundarySpec_
0367 ------------------------------
0368 
0369 Spec is a string composed of 4 elements delimted by 3 "/"::
0370 
0371     omat/osur/isur/imat
0372 
0373 The osur and isur can be blank, the omat and imat cannot be blank
0374 
0375 
0376 enteredDaughter
0377     True:  "inwards" photons 
0378     False: "outwards" photons 
0379  
0380 **/
0381 
0382 inline std::string U4Step::BoundarySpec(const G4Step* step ) // static
0383 {
0384     return IsOnBoundary(step) ? BoundarySpec_(step) : "" ;  
0385 }
0386 inline std::string U4Step::BoundarySpec_(const G4Step* step) // static 
0387 {
0388     const G4StepPoint* pre = step->GetPreStepPoint() ; 
0389     const G4StepPoint* post = step->GetPostStepPoint() ; 
0390     const G4Material* m1 = pre->GetMaterial();
0391     const G4Material* m2 = post->GetMaterial();
0392     const char* n1 = m1->GetName().c_str() ;  
0393     const char* n2 = m2->GetName().c_str() ;  
0394 
0395     const G4VPhysicalVolume* thePrePV = pre->GetPhysicalVolume();
0396     const G4VPhysicalVolume* thePostPV = post->GetPhysicalVolume();
0397     G4bool enteredDaughter = thePostPV->GetMotherLogical() == thePrePV ->GetLogicalVolume();
0398 
0399     const char* omat = enteredDaughter ? n1 : n2 ; 
0400     const char* imat = enteredDaughter ? n2 : n1 ; 
0401 
0402     const G4LogicalSurface* surf1 = GetLogicalSurface(thePrePV, thePostPV ); 
0403     const G4LogicalSurface* surf2 = GetLogicalSurface(thePostPV, thePrePV ); 
0404 
0405     const char* osur = nullptr ; 
0406     const char* isur = nullptr ; 
0407 
0408     if( enteredDaughter )
0409     {
0410         osur = surf1 ? surf1->GetName().c_str() : nullptr ; 
0411         isur = surf2 ? surf2->GetName().c_str() : nullptr ; 
0412     }
0413     else
0414     {
0415         osur = surf2 ? surf2->GetName().c_str() : nullptr ; 
0416         isur = surf1 ? surf1->GetName().c_str() : nullptr ; 
0417     }
0418 
0419     std::stringstream ss ; 
0420     ss 
0421        << omat 
0422        << "/" 
0423        << ( osur ? osur : "" ) 
0424        << "/" 
0425        << ( isur ? isur : "" ) 
0426        << "/" 
0427        << imat
0428        ; 
0429 
0430     std::string s = ss.str(); 
0431     return s ; 
0432 }
0433 
0434 
0435 inline G4LogicalSurface* U4Step::GetLogicalSurface(const G4VPhysicalVolume* thePrePV, const G4VPhysicalVolume* thePostPV)
0436 {
0437     G4LogicalSurface* Surface = G4LogicalBorderSurface::GetSurface(thePrePV, thePostPV);
0438     if (Surface == nullptr)
0439     {
0440         G4bool enteredDaughter = thePostPV->GetMotherLogical() == thePrePV ->GetLogicalVolume();
0441 
0442         if(enteredDaughter)
0443         {
0444             Surface = G4LogicalSkinSurface::GetSurface(thePostPV->GetLogicalVolume());
0445             if(Surface == nullptr) Surface = G4LogicalSkinSurface::GetSurface(thePrePV->GetLogicalVolume());
0446         }    
0447         else  // "leavingDaughter"
0448         {
0449             Surface = G4LogicalSkinSurface::GetSurface(thePrePV->GetLogicalVolume());
0450             if(Surface == NULL) Surface = G4LogicalSkinSurface::GetSurface(thePostPV->GetLogicalVolume());
0451         }
0452     }
0453     return Surface ; 
0454 }
0455 
0456 
0457 
0458 inline const G4VSolid* U4Step::Solid(const G4StepPoint* point ) // static
0459 {
0460     const G4VPhysicalVolume* pv  = point->GetPhysicalVolume();
0461     const G4LogicalVolume* lv = pv ? pv->GetLogicalVolume() : nullptr ;
0462     const G4VSolid* so = lv ? lv->GetSolid() : nullptr ; 
0463     return so ; 
0464 }
0465 
0466 /**
0467 U4Step::Spec
0468 -------------
0469 
0470 Used for fake skipping from U4Recorder::UserSteppingAction_Optical using U4Recorder::IsFake
0471 configured via envvars::
0472 
0473     U4Recorder__FAKES
0474     U4Recorder__FAKES_SKIP
0475 
0476 **/
0477 
0478 inline std::string U4Step::Spec(const G4Step* step) // static
0479 {
0480     const G4Track* track = step->GetTrack(); 
0481 
0482     G4VPhysicalVolume* pv = track->GetVolume() ; 
0483     G4VPhysicalVolume* next_pv = track->GetNextVolume() ; 
0484 
0485     const G4StepPoint* pre = step->GetPreStepPoint() ; 
0486     const G4StepPoint* post = step->GetPostStepPoint() ; 
0487 
0488     const G4VPhysicalVolume* pre_pv = pre->GetPhysicalVolume();
0489     const G4VPhysicalVolume* post_pv = post->GetPhysicalVolume();
0490 
0491 
0492     bool pre_pv_expect = pv == pre_pv ;
0493     bool post_pv_expect = next_pv == post_pv ;
0494 
0495     assert( pre_pv_expect );  
0496     assert( post_pv_expect );  
0497 
0498     if(!pre_pv_expect) std::raise(SIGINT);  
0499     if(!post_pv_expect) std::raise(SIGINT);  
0500 
0501 
0502     const G4Material* pre_mat = pre->GetMaterial(); 
0503     const G4Material* post_mat = post->GetMaterial(); 
0504 
0505     std::stringstream ss ; 
0506     ss 
0507        << ( pre_mat ? pre_mat->GetName() : "-" ) 
0508        << "/" 
0509        << ( post_mat ? post_mat->GetName() : "-" ) 
0510        << ":"
0511        << ( pv ? pv->GetName() : "-" ) 
0512        << "/" 
0513        << ( next_pv ? next_pv->GetName() : "-" ) 
0514        ; 
0515 
0516     std::string str = ss.str() ; 
0517     return str ; 
0518 }
0519 
0520 
0521 inline bool U4Step::IsSameMaterial(const G4Step* step, const char* q_matname ) // static
0522 {
0523     const G4StepPoint* pre = step->GetPreStepPoint() ; 
0524     const G4StepPoint* post = step->GetPostStepPoint() ; 
0525 
0526     const G4Material* pre_mat = pre->GetMaterial(); 
0527     const G4Material* post_mat = post->GetMaterial(); 
0528 
0529     const char* matname = pre_mat->GetName().c_str(); 
0530     bool same_material = pre_mat == post_mat && strcmp(matname, q_matname) == 0 ; 
0531 
0532     return same_material ;   
0533 }
0534 
0535 
0536 inline bool U4Step::IsPVBorder(const G4Step* step, const char* q_pvname1, const char* q_pvname2 ) // static
0537 {
0538     const G4StepPoint* pre = step->GetPreStepPoint() ; 
0539     const G4StepPoint* post = step->GetPostStepPoint() ; 
0540 
0541     const G4VPhysicalVolume* pre_pv = pre->GetPhysicalVolume();
0542     const G4VPhysicalVolume* post_pv = post->GetPhysicalVolume();
0543 
0544     const char* pre_pvname = pre_pv->GetName().c_str(); 
0545     const char* post_pvname = post_pv->GetName().c_str(); 
0546 
0547     bool pre_pvname1  = strstr( pre_pvname, q_pvname1 ) != nullptr ; 
0548     bool pre_pvname2  = strstr( pre_pvname, q_pvname2 ) != nullptr ; 
0549     bool post_pvname1 = strstr( post_pvname, q_pvname1 ) != nullptr ; 
0550     bool post_pvname2 = strstr( post_pvname, q_pvname2 ) != nullptr ; 
0551 
0552     bool is_pvborder_12 = pre_pvname1 && post_pvname2 ;  
0553     bool is_pvborder_21 = pre_pvname2 && post_pvname1 ;  
0554     bool is_pvborder = is_pvborder_12 || is_pvborder_21 ; 
0555 
0556     return is_pvborder ; 
0557 }
0558 
0559 inline bool U4Step::IsSameMaterialPVBorder( const G4Step* step, const char* q_matname, const char* q_pvname1, const char* q_pvname2  ) // static
0560 {
0561     return IsSameMaterial(step, q_matname) && IsPVBorder(step, q_pvname1, q_pvname2 ); 
0562 }
0563 
0564