File indexing completed on 2026-04-09 07:49:12
0001 #include <filesystem>
0002 #include <fstream>
0003 #include <iostream>
0004
0005 #include "G4AutoLock.hh"
0006 #include "G4BooleanSolid.hh"
0007 #include "G4Cerenkov.hh"
0008 #include "G4Electron.hh"
0009 #include "G4Event.hh"
0010 #include "G4GDMLParser.hh"
0011 #include "G4LogicalVolumeStore.hh"
0012 #include "G4OpBoundaryProcess.hh"
0013 #include "G4OpticalPhoton.hh"
0014 #include "G4PhysicalConstants.hh"
0015 #include "G4PrimaryParticle.hh"
0016 #include "G4PrimaryVertex.hh"
0017 #include "G4RunManager.hh"
0018 #include "G4RunManagerFactory.hh"
0019 #include "G4SDManager.hh"
0020 #include "G4Scintillation.hh"
0021 #include "G4SubtractionSolid.hh"
0022 #include "G4SystemOfUnits.hh"
0023 #include "G4ThreeVector.hh"
0024 #include "G4Track.hh"
0025 #include "G4TrackStatus.hh"
0026 #include "G4UserEventAction.hh"
0027 #include "G4UserRunAction.hh"
0028 #include "G4UserSteppingAction.hh"
0029 #include "G4UserTrackingAction.hh"
0030 #include "G4VPhysicalVolume.hh"
0031 #include "G4VProcess.hh"
0032 #include "G4VUserDetectorConstruction.hh"
0033 #include "G4VUserPrimaryGeneratorAction.hh"
0034
0035 #include "g4cx/G4CXOpticks.hh"
0036 #include "sysrap/NP.hh"
0037 #include "sysrap/SEvt.hh"
0038 #include "sysrap/STrackInfo.h"
0039 #include "sysrap/spho.h"
0040 #include "sysrap/sphoton.h"
0041 #include "u4/U4.hh"
0042 #include "u4/U4Random.hh"
0043 #include "u4/U4StepPoint.hh"
0044 #include "u4/U4Touchable.h"
0045 #include "u4/U4Track.h"
0046
0047 namespace
0048 {
0049 G4Mutex genstep_mutex = G4MUTEX_INITIALIZER;
0050 }
0051
0052 bool IsSubtractionSolid(G4VSolid *solid)
0053 {
0054 if (!solid)
0055 return false;
0056
0057
0058 if (dynamic_cast<G4SubtractionSolid *>(solid))
0059 return true;
0060
0061
0062 G4BooleanSolid *booleanSolid = dynamic_cast<G4BooleanSolid *>(solid);
0063 if (booleanSolid)
0064 {
0065 G4VSolid *solidA = booleanSolid->GetConstituentSolid(0);
0066 G4VSolid *solidB = booleanSolid->GetConstituentSolid(1);
0067
0068
0069 if (IsSubtractionSolid(solidA) || IsSubtractionSolid(solidB))
0070 return true;
0071 }
0072
0073
0074 return false;
0075 }
0076
0077 std::string str_tolower(std::string s)
0078 {
0079 std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
0080 return s;
0081 }
0082
0083 struct PhotonHit : public G4VHit
0084 {
0085 PhotonHit() = default;
0086
0087 PhotonHit(unsigned id, G4double energy, G4double time, G4ThreeVector position, G4ThreeVector direction,
0088 G4ThreeVector polarization)
0089 : fid(id), fenergy(energy), ftime(time), fposition(position), fdirection(direction), fpolarization(polarization)
0090 {
0091 }
0092
0093
0094 PhotonHit(const PhotonHit &right)
0095 : G4VHit(right), fid(right.fid), fenergy(right.fenergy), ftime(right.ftime), fposition(right.fposition),
0096 fdirection(right.fdirection), fpolarization(right.fpolarization)
0097 {
0098 }
0099
0100
0101 const PhotonHit &operator=(const PhotonHit &right)
0102 {
0103 if (this != &right)
0104 {
0105 G4VHit::operator=(right);
0106 fid = right.fid;
0107 fenergy = right.fenergy;
0108 ftime = right.ftime;
0109 fposition = right.fposition;
0110 fdirection = right.fdirection;
0111 fpolarization = right.fpolarization;
0112 }
0113 return *this;
0114 }
0115
0116
0117 G4bool operator==(const PhotonHit &right) const
0118 {
0119 return (this == &right);
0120 }
0121
0122
0123 void Print() override
0124 {
0125 G4cout << "Detector id: " << fid << " energy: " << fenergy << " nm" << " time: " << ftime << " ns"
0126 << " position: " << fposition << " direction: " << fdirection << " polarization: " << fpolarization
0127 << G4endl;
0128 }
0129
0130
0131 G4int fid{0};
0132 G4double fenergy{0};
0133 G4double ftime{0};
0134 G4ThreeVector fposition{0, 0, 0};
0135 G4ThreeVector fdirection{0, 0, 0};
0136 G4ThreeVector fpolarization{0, 0, 0};
0137 };
0138
0139 using PhotonHitsCollection = G4THitsCollection<PhotonHit>;
0140
0141 struct PhotonSD : public G4VSensitiveDetector
0142 {
0143 PhotonSD(G4String name) : G4VSensitiveDetector(name), fHCID(-1)
0144 {
0145 G4String HCname = name + "_HC";
0146 collectionName.insert(HCname);
0147 G4cout << collectionName.size() << " PhotonSD name: " << name << " collection Name: " << HCname << G4endl;
0148 }
0149
0150 void Initialize(G4HCofThisEvent *hce) override
0151 {
0152 fPhotonHitsCollection = new PhotonHitsCollection(SensitiveDetectorName, collectionName[0]);
0153 if (fHCID < 0)
0154 {
0155
0156 fHCID = G4SDManager::GetSDMpointer()->GetCollectionID(collectionName[0]);
0157 }
0158 hce->AddHitsCollection(fHCID, fPhotonHitsCollection);
0159 }
0160
0161 G4bool ProcessHits(G4Step *aStep, G4TouchableHistory *) override
0162 {
0163 G4Track *theTrack = aStep->GetTrack();
0164 if (theTrack->GetDefinition() != G4OpticalPhoton::OpticalPhotonDefinition())
0165 return false;
0166
0167 G4double theEnergy = theTrack->GetTotalEnergy() / CLHEP::eV;
0168
0169
0170 PhotonHit *newHit = new PhotonHit(
0171 0,
0172 theEnergy, theTrack->GetGlobalTime(), aStep->GetPostStepPoint()->GetPosition(),
0173 aStep->GetPostStepPoint()->GetMomentumDirection(), aStep->GetPostStepPoint()->GetPolarization());
0174
0175 fPhotonHitsCollection->insert(newHit);
0176 theTrack->SetTrackStatus(fStopAndKill);
0177 return true;
0178 }
0179
0180 void EndOfEvent(G4HCofThisEvent *) override
0181 {
0182 G4int NbHits = fPhotonHitsCollection->entries();
0183 G4cout << "PhotonSD::EndOfEvent Number of PhotonHits: " << NbHits << G4endl;
0184 }
0185
0186 void AddOpticksHits()
0187 {
0188 G4AutoLock lock(&genstep_mutex);
0189 SEvt *sev = SEvt::Get_EGPU();
0190 unsigned int num_hits = sev->GetNumHit(0);
0191
0192 for (int idx = 0; idx < int(num_hits); idx++)
0193 {
0194 sphoton hit;
0195 sev->getHit(hit, idx);
0196 G4ThreeVector position = G4ThreeVector(hit.pos.x, hit.pos.y, hit.pos.z);
0197 G4ThreeVector direction = G4ThreeVector(hit.mom.x, hit.mom.y, hit.mom.z);
0198 G4ThreeVector polarization = G4ThreeVector(hit.pol.x, hit.pol.y, hit.pol.z);
0199 int theCreationProcessid;
0200 if (OpticksPhoton::HasCerenkovFlag(hit.flagmask))
0201 {
0202 theCreationProcessid = 0;
0203 }
0204 else if (OpticksPhoton::HasScintillationFlag(hit.flagmask))
0205 {
0206 theCreationProcessid = 1;
0207 }
0208 else
0209 {
0210 theCreationProcessid = -1;
0211 }
0212
0213 PhotonHit *newHit = new PhotonHit(0, hit.wavelength, hit.time, position, direction, polarization);
0214 fPhotonHitsCollection->insert(newHit);
0215 }
0216 }
0217
0218 private:
0219 PhotonHitsCollection *fPhotonHitsCollection{nullptr};
0220 G4int fHCID;
0221 };
0222
0223 struct DetectorConstruction : G4VUserDetectorConstruction
0224 {
0225 DetectorConstruction(std::filesystem::path gdml_file) : gdml_file_(gdml_file)
0226 {
0227 }
0228
0229 G4VPhysicalVolume *Construct() override
0230 {
0231 parser_.Read(gdml_file_.string(), false);
0232 G4VPhysicalVolume *world = parser_.GetWorldVolume();
0233
0234 G4CXOpticks::SetGeometry(world);
0235 G4LogicalVolumeStore *lvStore = G4LogicalVolumeStore::GetInstance();
0236
0237 static G4VisAttributes invisibleVisAttr(false);
0238
0239
0240 if (lvStore && !lvStore->empty())
0241 {
0242
0243 for (auto &logicalVolume : *lvStore)
0244 {
0245 G4VSolid *solid = logicalVolume->GetSolid();
0246
0247
0248 if (IsSubtractionSolid(solid))
0249 {
0250
0251 logicalVolume->SetVisAttributes(&invisibleVisAttr);
0252
0253
0254 G4cout << "Hiding logical volume: " << logicalVolume->GetName() << G4endl;
0255 }
0256 }
0257 }
0258
0259 return world;
0260 }
0261
0262 void ConstructSDandField() override
0263 {
0264 G4cout << "ConstructSDandField is called." << G4endl;
0265 G4SDManager *SDman = G4SDManager::GetSDMpointer();
0266
0267 const G4GDMLAuxMapType *auxmap = parser_.GetAuxMap();
0268 for (auto const &[logVol, listType] : *auxmap)
0269 {
0270 for (auto const &auxtype : listType)
0271 {
0272 if (auxtype.type == "SensDet")
0273 {
0274 G4cout << "Attaching sensitive detector to logical volume: " << logVol->GetName() << G4endl;
0275 G4String name = logVol->GetName() + "_PhotonDetector";
0276 PhotonSD *aPhotonSD = new PhotonSD(name);
0277 SDman->AddNewDetector(aPhotonSD);
0278 logVol->SetSensitiveDetector(aPhotonSD);
0279 }
0280 }
0281 }
0282 }
0283
0284 private:
0285 std::filesystem::path gdml_file_;
0286 G4GDMLParser parser_;
0287 };
0288
0289 struct PrimaryGenerator : G4VUserPrimaryGeneratorAction
0290 {
0291 SEvt *sev;
0292
0293 PrimaryGenerator(SEvt *sev) : sev(sev)
0294 {
0295 }
0296
0297 void GeneratePrimaries(G4Event *event) override
0298 {
0299 G4ThreeVector position_mm(0.0 * m, 0.0 * m, 0.0 * m);
0300 G4double time_ns = 0;
0301 G4ThreeVector direction(0, 0.2, 0.8);
0302 G4double wavelength_nm = 0.1;
0303
0304 G4PrimaryVertex *vertex = new G4PrimaryVertex(position_mm, time_ns);
0305 G4PrimaryParticle *particle = new G4PrimaryParticle(G4Electron::Definition());
0306 particle->SetKineticEnergy(5 * GeV);
0307 particle->SetMomentumDirection(direction);
0308 vertex->SetPrimary(particle);
0309 event->AddPrimaryVertex(vertex);
0310 }
0311 };
0312
0313 struct EventAction : G4UserEventAction
0314 {
0315 SEvt *sev;
0316
0317 EventAction(SEvt *sev) : sev(sev)
0318 {
0319 }
0320
0321 void BeginOfEventAction(const G4Event *event) override
0322 {
0323 }
0324
0325 void EndOfEventAction(const G4Event *event) override
0326 {
0327 }
0328 };
0329
0330 struct RunAction : G4UserRunAction
0331 {
0332 RunAction()
0333 {
0334 }
0335
0336 void BeginOfRunAction(const G4Run *run) override
0337 {
0338 }
0339
0340 void EndOfRunAction(const G4Run *run) override
0341 {
0342 if (G4Threading::IsMasterThread())
0343 {
0344 G4CXOpticks *gx = G4CXOpticks::Get();
0345
0346 auto start = std::chrono::high_resolution_clock::now();
0347 gx->simulate(0, false);
0348 cudaDeviceSynchronize();
0349 auto end = std::chrono::high_resolution_clock::now();
0350
0351 std::chrono::duration<double> elapsed = end - start;
0352 std::cout << "Simulation time: " << elapsed.count() << " seconds" << std::endl;
0353
0354
0355 SEvt *sev = SEvt::Get_EGPU();
0356 unsigned int num_hits = sev->GetNumHit(0);
0357
0358 std::cout << "Opticks: NumCollected: " << sev->GetNumGenstepFromGenstep(0) << std::endl;
0359 std::cout << "Opticks: NumCollected: " << sev->GetNumPhotonCollected(0) << std::endl;
0360 std::cout << "Opticks: NumHits: " << num_hits << std::endl;
0361 std::ofstream outFile("opticks_hits_output.txt");
0362 if (!outFile.is_open())
0363 {
0364 std::cerr << "Error opening output file!" << std::endl;
0365 return;
0366 }
0367
0368 for (int idx = 0; idx < int(num_hits); idx++)
0369 {
0370 sphoton hit;
0371 sev->getHit(hit, idx);
0372 G4ThreeVector position = G4ThreeVector(hit.pos.x, hit.pos.y, hit.pos.z);
0373 G4ThreeVector direction = G4ThreeVector(hit.mom.x, hit.mom.y, hit.mom.z);
0374 G4ThreeVector polarization = G4ThreeVector(hit.pol.x, hit.pol.y, hit.pol.z);
0375 int theCreationProcessid;
0376 if (OpticksPhoton::HasCerenkovFlag(hit.flagmask))
0377 {
0378 theCreationProcessid = 0;
0379 }
0380 else if (OpticksPhoton::HasScintillationFlag(hit.flagmask))
0381 {
0382 theCreationProcessid = 1;
0383 }
0384 else
0385 {
0386 theCreationProcessid = -1;
0387 }
0388 outFile << hit.time << " " << hit.wavelength << " " << "(" << position.x() << ", " << position.y()
0389 << ", " << position.z() << ") " << "(" << direction.x() << ", " << direction.y() << ", "
0390 << direction.z() << ") " << "(" << polarization.x() << ", " << polarization.y() << ", "
0391 << polarization.z() << ") " << "CreationProcessID=" << theCreationProcessid << std::endl;
0392 }
0393
0394 outFile.close();
0395 }
0396 }
0397 };
0398
0399 struct SteppingAction : G4UserSteppingAction
0400 {
0401 SEvt *sev;
0402
0403 SteppingAction(SEvt *sev) : sev(sev)
0404 {
0405 }
0406
0407 void UserSteppingAction(const G4Step *aStep)
0408 {
0409 G4Track *aTrack;
0410 G4int fNumPhotons = 0;
0411
0412 G4StepPoint *preStep = aStep->GetPostStepPoint();
0413 G4VPhysicalVolume *volume = preStep->GetPhysicalVolume();
0414
0415 if (aStep->GetTrack()->GetDefinition() == G4OpticalPhoton::OpticalPhotonDefinition())
0416 {
0417
0418 if (aStep->GetTrack()->GetCurrentStepNumber() > 10000)
0419 {
0420 aStep->GetTrack()->SetTrackStatus(fStopAndKill);
0421 }
0422 }
0423
0424 if (volume && volume->GetName() == "MirrorPyramid")
0425 {
0426 aTrack = aStep->GetTrack();
0427 if (aTrack->GetDefinition() != G4OpticalPhoton::OpticalPhotonDefinition())
0428 {
0429 aTrack->SetTrackStatus(fStopAndKill);
0430 }
0431 }
0432
0433 G4SteppingManager *fpSteppingManager =
0434 G4EventManager::GetEventManager()->GetTrackingManager()->GetSteppingManager();
0435 G4StepStatus stepStatus = fpSteppingManager->GetfStepStatus();
0436
0437 if (stepStatus != fAtRestDoItProc)
0438 {
0439 G4ProcessVector *procPost = fpSteppingManager->GetfPostStepDoItVector();
0440 size_t MAXofPostStepLoops = fpSteppingManager->GetMAXofPostStepLoops();
0441 for (size_t i3 = 0; i3 < MAXofPostStepLoops; i3++)
0442 {
0443 if ((*procPost)[i3]->GetProcessName() == "Cerenkov")
0444 {
0445 aTrack = aStep->GetTrack();
0446 const G4DynamicParticle *aParticle = aTrack->GetDynamicParticle();
0447 G4double charge = aParticle->GetDefinition()->GetPDGCharge();
0448 const G4Material *aMaterial = aTrack->GetMaterial();
0449 G4MaterialPropertiesTable *MPT = aMaterial->GetMaterialPropertiesTable();
0450
0451 G4MaterialPropertyVector *Rindex = MPT->GetProperty(kRINDEX);
0452 if (!Rindex || Rindex->GetVectorLength() == 0)
0453 {
0454 G4cout << "WARNING: Material has no valid RINDEX data. Skipping Cerenkov calculation."
0455 << G4endl;
0456 return;
0457 }
0458
0459 G4Cerenkov *proc = (G4Cerenkov *)(*procPost)[i3];
0460 fNumPhotons = proc->GetNumPhotons();
0461
0462 G4AutoLock lock(&genstep_mutex);
0463
0464 if (fNumPhotons > 0)
0465 {
0466 G4double Pmin = Rindex->Energy(0);
0467 G4double Pmax = Rindex->GetMaxEnergy();
0468 G4double nMax = Rindex->GetMaxValue();
0469 G4double beta1 = aStep->GetPreStepPoint()->GetBeta();
0470 G4double beta2 = aStep->GetPostStepPoint()->GetBeta();
0471 G4double beta = (beta1 + beta2) * 0.5;
0472 G4double BetaInverse = 1. / beta;
0473 G4double maxCos = BetaInverse / nMax;
0474 G4double maxSin2 = (1.0 - maxCos) * (1.0 + maxCos);
0475 G4double MeanNumberOfPhotons1 =
0476 proc->GetAverageNumberOfPhotons(charge, beta1, aMaterial, Rindex);
0477 G4double MeanNumberOfPhotons2 =
0478 proc->GetAverageNumberOfPhotons(charge, beta2, aMaterial, Rindex);
0479
0480 U4::CollectGenstep_G4Cerenkov_modified(aTrack, aStep, fNumPhotons, BetaInverse, Pmin, Pmax,
0481 maxCos, maxSin2, MeanNumberOfPhotons1,
0482 MeanNumberOfPhotons2);
0483 }
0484 }
0485 if ((*procPost)[i3]->GetProcessName() == "Scintillation")
0486 {
0487 G4Scintillation *proc1 = (G4Scintillation *)(*procPost)[i3];
0488 fNumPhotons = proc1->GetNumPhotons();
0489 if (fNumPhotons > 0)
0490 {
0491 aTrack = aStep->GetTrack();
0492 const G4Material *aMaterial = aTrack->GetMaterial();
0493 G4MaterialPropertiesTable *MPT = aMaterial->GetMaterialPropertiesTable();
0494 if (!MPT || !MPT->ConstPropertyExists(kSCINTILLATIONTIMECONSTANT1))
0495 {
0496 G4cout << "WARNING: Material has no valid SCINTILLATIONTIMECONSTANT1 data. Skipping "
0497 "Scintillation calculation."
0498 << G4endl;
0499 return;
0500 }
0501 G4double SCINTILLATIONTIMECONSTANT1 = MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT1);
0502
0503 U4::CollectGenstep_DsG4Scintillation_r4695(aTrack, aStep, fNumPhotons, 1,
0504 SCINTILLATIONTIMECONSTANT1);
0505 }
0506 }
0507 }
0508 }
0509 }
0510 };
0511
0512 struct TrackingAction : G4UserTrackingAction
0513 {
0514 const G4Track *transient_fSuspend_track = nullptr;
0515 SEvt *sev;
0516
0517 TrackingAction(SEvt *sev) : sev(sev)
0518 {
0519 }
0520
0521 void PreUserTrackingAction_Optical_FabricateLabel(const G4Track *track)
0522 {
0523 }
0524
0525 void PreUserTrackingAction(const G4Track *track) override
0526 {
0527 }
0528
0529 void PostUserTrackingAction(const G4Track *track) override
0530 {
0531 }
0532 };
0533
0534 struct G4App
0535 {
0536 G4App(std::filesystem::path gdml_file)
0537 : sev(SEvt::CreateOrReuse_EGPU()), det_cons_(new DetectorConstruction(gdml_file)),
0538 prim_gen_(new PrimaryGenerator(sev)), event_act_(new EventAction(sev)), run_act_(new RunAction()),
0539 stepping_(new SteppingAction(sev)),
0540
0541 tracking_(new TrackingAction(sev))
0542 {
0543 }
0544
0545
0546
0547
0548 SEvt *sev;
0549
0550 G4VUserDetectorConstruction *det_cons_;
0551 G4VUserPrimaryGeneratorAction *prim_gen_;
0552 EventAction *event_act_;
0553 RunAction *run_act_;
0554 SteppingAction *stepping_;
0555 TrackingAction *tracking_;
0556 };