From d43b18d3a122ed0ab0defb85b84561e9c5d85902 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Fri, 1 Aug 2025 18:25:56 -0400 Subject: [PATCH 01/35] docs: update README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index ab657e6818..6d501410ba 100644 --- a/README.md +++ b/README.md @@ -125,3 +125,12 @@ Process](https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/ ... ``` + + +## Performance studies + +``` +docker run --rm -it -v $HOME:/esi -v $HOME/eic-opticks:/src/eic-opticks -e DISPLAY=$DISPLAY -e HOME=/esi --net=host ghcr.io/bnlnpps/eic-opticks:develop +cmake -S $OPTICKS_HOME -B $OPTICKS_BUILD -DCMAKE_INSTALL_PREFIX=$OPTICKS_PREFIX -DOptiX_INSTALL_DIR=/opt/optix -DCMAKE_BUILD_TYPE=Debug +cmake --build $OPTICKS_BUILD --parallel --target install +``` From c22acd15060c8c51208909ecaa02a5f288547256 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Fri, 1 Aug 2025 19:03:52 -0400 Subject: [PATCH 02/35] move code from esi-g4ox and rename files --- src/g4appmt.h | 614 ++++++++++++++++++++++++++++++++++++++++++++++ src/simg4oxmt.cpp | 130 ++++++++++ 2 files changed, 744 insertions(+) create mode 100644 src/g4appmt.h create mode 100644 src/simg4oxmt.cpp diff --git a/src/g4appmt.h b/src/g4appmt.h new file mode 100644 index 0000000000..76e239260b --- /dev/null +++ b/src/g4appmt.h @@ -0,0 +1,614 @@ +#include "G4Cerenkov.hh" +#include "G4Scintillation.hh" +#include +#include +#include + +#include "G4BooleanSolid.hh" +#include "G4CX/G4CXOpticks.hh" +#include "G4Electron.hh" +#include "G4Event.hh" +#include "G4GDMLParser.hh" +#include "G4LogicalVolumeStore.hh" +#include "G4OpBoundaryProcess.hh" +#include "G4OpticalPhoton.hh" +#include "G4PhysicalConstants.hh" +#include "G4PrimaryParticle.hh" +#include "G4PrimaryVertex.hh" +#include "G4SDManager.hh" +#include "G4SubtractionSolid.hh" +#include "G4SystemOfUnits.hh" +#include "G4ThreeVector.hh" +#include "G4Track.hh" +#include "G4TrackStatus.hh" +#include "G4UserEventAction.hh" +#include "G4UserRunAction.hh" +#include "G4UserSteppingAction.hh" +#include "G4UserTrackingAction.hh" +#include "G4VPhysicalVolume.hh" +#include "G4VProcess.hh" +#include "G4RunManager.hh" +#include "G4VUserDetectorConstruction.hh" +#include "G4VUserPrimaryGeneratorAction.hh" +#include "SysRap/NP.hh" +#include "SysRap/SEvt.hh" +#include "SysRap/STrackInfo.h" +#include "SysRap/spho.h" +#include "SysRap/sphoton.h" +#include "U4.hh" +#include "U4/U4Random.hh" +#include "U4/U4StepPoint.hh" +#include "U4/U4Touchable.h" +#include "U4/U4Track.h" +#include "G4RunManagerFactory.hh" +#include "G4AutoLock.hh" + + +namespace { G4Mutex genstep_mutex = G4MUTEX_INITIALIZER; } + + +bool IsSubtractionSolid(G4VSolid *solid) +{ + if (!solid) + return false; + + // Check if the solid is directly a G4SubtractionSolid + if (dynamic_cast(solid)) + return true; + + // If the solid is a Boolean solid, check its constituent solids + G4BooleanSolid *booleanSolid = dynamic_cast(solid); + if (booleanSolid) + { + G4VSolid *solidA = booleanSolid->GetConstituentSolid(0); + G4VSolid *solidB = booleanSolid->GetConstituentSolid(1); + + // Recursively check the constituent solids + if (IsSubtractionSolid(solidA) || IsSubtractionSolid(solidB)) + return true; + } + + // For other solid types, return false + return false; +} + +std::string str_tolower(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; +} + +struct PhotonHit : public G4VHit +{ + PhotonHit() = default; + + PhotonHit(unsigned id, G4double energy, G4double time, G4ThreeVector position, G4ThreeVector direction, + G4ThreeVector polarization) + : fid(id), fenergy(energy), ftime(time), fposition(position), fdirection(direction), fpolarization(polarization) + { + } + + // Copy constructor + PhotonHit(const PhotonHit &right) + : G4VHit(right), fid(right.fid), fenergy(right.fenergy), ftime(right.ftime), fposition(right.fposition), + fdirection(right.fdirection), fpolarization(right.fpolarization) + { + } + + // Assignment operator + const PhotonHit &operator=(const PhotonHit &right) + { + if (this != &right) + { + G4VHit::operator=(right); + fid = right.fid; + fenergy = right.fenergy; + ftime = right.ftime; + fposition = right.fposition; + fdirection = right.fdirection; + fpolarization = right.fpolarization; + } + return *this; + } + + // Equality operator + G4bool operator==(const PhotonHit &right) const + { + return (this == &right); + } + + // Print method + void Print() override + { + G4cout << "Detector id: " << fid << " energy: " << fenergy << " nm" + << " time: " << ftime << " ns" + << " position: " << fposition << " direction: " << fdirection << " polarization: " << fpolarization + << G4endl; + } + + // Member variables + G4int fid{0}; + G4double fenergy{0}; + G4double ftime{0}; + G4ThreeVector fposition{0, 0, 0}; + G4ThreeVector fdirection{0, 0, 0}; + G4ThreeVector fpolarization{0, 0, 0}; +}; + +using PhotonHitsCollection = G4THitsCollection; + +struct PhotonSD : public G4VSensitiveDetector +{ + PhotonSD(G4String name) : G4VSensitiveDetector(name), fHCID(-1) + { + G4String HCname = name + "_HC"; + collectionName.insert(HCname); + G4cout << collectionName.size() << " PhotonSD name: " << name << " collection Name: " << HCname << G4endl; + } + + void Initialize(G4HCofThisEvent *hce) override + { + fPhotonHitsCollection = new PhotonHitsCollection(SensitiveDetectorName, collectionName[0]); + if (fHCID < 0) + { + //G4cout << "PhotonSD::Initialize: " << SensitiveDetectorName << " " << collectionName[0] << G4endl; + fHCID = G4SDManager::GetSDMpointer()->GetCollectionID(collectionName[0]); + } + hce->AddHitsCollection(fHCID, fPhotonHitsCollection); + } + + G4bool ProcessHits(G4Step *aStep, G4TouchableHistory *) override + { + G4Track *theTrack = aStep->GetTrack(); + if (theTrack->GetDefinition() != G4OpticalPhoton::OpticalPhotonDefinition()) + return false; + + G4double theEnergy = theTrack->GetTotalEnergy() / CLHEP::eV; + + // Create a new hit (CopyNr is set to 0 as DetectorID is omitted) + PhotonHit *newHit = new PhotonHit( + 0, // CopyNr set to 0 + theEnergy, theTrack->GetGlobalTime(), aStep->GetPostStepPoint()->GetPosition(), + aStep->GetPostStepPoint()->GetMomentumDirection(), aStep->GetPostStepPoint()->GetPolarization()); + + fPhotonHitsCollection->insert(newHit); + theTrack->SetTrackStatus(fStopAndKill); + return true; + } + + void EndOfEvent(G4HCofThisEvent *) override + { + + G4int NbHits = fPhotonHitsCollection->entries(); + //G4cout << "PhotonSD::EndOfEvent Number of PhotonHits: " << NbHits << G4endl; + + // Open an output file (text mode) + + /* + int tid = G4Threading::G4GetThreadId(); + std::ostringstream fname; + fname << "g4_photon_hits_thread" << tid << ".txt"; + std::ofstream outFile(fname.str().c_str(), std::ios::app); + + + if (!outFile.is_open()) + { + G4cerr << "Error opening output file g4_photon_hits.txt!" << G4endl; + return; + } + + // Loop over all recorded hits TOBEDONE: move this to endofrunaction + for (G4int i = 0; i < NbHits; i++) + { + PhotonHit *hit = (*fPhotonHitsCollection)[i]; + + G4int id = hit->fid; + G4double energy = hit->fenergy; + G4double time = hit->ftime; + G4ThreeVector position = hit->fposition; + G4ThreeVector direction = hit->fdirection; + G4ThreeVector pol = hit->fpolarization; + + // Write out info in a style similar to Opticks hits + + outFile << "Adding hit from Geant4: " << energy << " eV " + << "(" << position.x() << ", " << position.y() << ", " << position.z() << ") " + << "(" << direction.x() << ", " << direction.y() << ", " << direction.z() << ") " + << "(" << pol.x() << ", " << pol.y() << ", " << pol.z() << ") " + << "Time=" << time << " " + << "ID=" << id << G4endl; + + } + + // Close the file + outFile.close(); */ + } + + void AddOpticksHits() + { + SEvt *sev = SEvt::Get_EGPU(); + unsigned int num_hits = sev->GetNumHit(0); + + for (int idx = 0; idx < int(num_hits); idx++) + { + sphoton hit; + sev->getHit(hit, idx); + G4ThreeVector position = G4ThreeVector(hit.pos.x, hit.pos.y, hit.pos.z); + G4ThreeVector direction = G4ThreeVector(hit.mom.x, hit.mom.y, hit.mom.z); + G4ThreeVector polarization = G4ThreeVector(hit.pol.x, hit.pol.y, hit.pol.z); + int theCreationProcessid; + if (OpticksPhoton::HasCerenkovFlag(hit.flagmask)) + { + theCreationProcessid = 0; + } + else if (OpticksPhoton::HasScintillationFlag(hit.flagmask)) + { + theCreationProcessid = 1; + } + else + { + theCreationProcessid = -1; + } + std::cout << hit.wavelength << " " << position << " " << direction << " " << polarization << std::endl; + + PhotonHit *newHit = new PhotonHit(0, hit.wavelength, hit.time, position, direction, polarization); + fPhotonHitsCollection->insert(newHit); + } + } + + private: + PhotonHitsCollection *fPhotonHitsCollection{nullptr}; + G4int fHCID; +}; + +struct DetectorConstruction : G4VUserDetectorConstruction +{ + DetectorConstruction(std::filesystem::path gdml_file) : gdml_file_(gdml_file) + { + } + + G4VPhysicalVolume *Construct() override + { + parser_.Read(gdml_file_.string(), false); + G4VPhysicalVolume *world = parser_.GetWorldVolume(); + + G4CXOpticks::SetGeometry(world); + G4LogicalVolumeStore *lvStore = G4LogicalVolumeStore::GetInstance(); + + static G4VisAttributes invisibleVisAttr(false); + + // Check if the store is not empty + if (lvStore && !lvStore->empty()) + { + // Iterate over all logical volumes in the store + for (auto &logicalVolume : *lvStore) + { + G4VSolid *solid = logicalVolume->GetSolid(); + + // Check if the solid uses subtraction + if (IsSubtractionSolid(solid)) + { + // Assign the invisible visual attributes to the logical volume + logicalVolume->SetVisAttributes(&invisibleVisAttr); + + // Optionally, print out the name of the logical volume + G4cout << "Hiding logical volume: " << logicalVolume->GetName() << G4endl; + } + } + } + + return world; + } + + void ConstructSDandField() override + { + G4cout << "ConstructSDandField is called." << G4endl; + G4SDManager *SDman = G4SDManager::GetSDMpointer(); + + const G4GDMLAuxMapType *auxmap = parser_.GetAuxMap(); + for (auto const &[logVol, listType] : *auxmap) + { + for (auto const &auxtype : listType) + { + if (auxtype.type == "SensDet") + { + G4cout << "Attaching sensitive detector to logical volume: " << logVol->GetName() << G4endl; + G4String name = logVol->GetName() + "_PhotonDetector"; + PhotonSD *aPhotonSD = new PhotonSD(name); + SDman->AddNewDetector(aPhotonSD); + logVol->SetSensitiveDetector(aPhotonSD); + } + } + } + } + + private: + std::filesystem::path gdml_file_; + G4GDMLParser parser_; +}; + +struct PrimaryGenerator : G4VUserPrimaryGeneratorAction +{ + SEvt *sev; + + PrimaryGenerator(SEvt *sev) : sev(sev) + { + } + + void GeneratePrimaries(G4Event *event) override + { + G4ThreeVector position_mm(-0.4 * m, -0.3 * m, -0.3 * m); + G4double time_ns = 0; + G4ThreeVector direction(0, 0.2, 0.8); + G4double wavelength_nm = 0.1; + + G4PrimaryVertex *vertex = new G4PrimaryVertex(position_mm, time_ns); + G4PrimaryParticle *particle = new G4PrimaryParticle(G4Electron::Definition()); + particle->SetKineticEnergy(5 * GeV); + particle->SetMomentumDirection(direction); + vertex->SetPrimary(particle); + event->AddPrimaryVertex(vertex); + } +}; + +struct EventAction : G4UserEventAction +{ + SEvt *sev; + + EventAction(SEvt *sev) : sev(sev) + { + } + + void BeginOfEventAction(const G4Event *event) override + { + } + + void EndOfEventAction(const G4Event *event) override + { + } +}; + +struct RunAction : G4UserRunAction +{ + RunAction() + { + } + + void BeginOfRunAction(const G4Run *run) override + { + } + + void EndOfRunAction(const G4Run *run) override + { + + if (G4Threading::IsMasterThread()) + { + G4CXOpticks *gx = G4CXOpticks::Get(); + + auto start = std::chrono::high_resolution_clock::now(); + gx->simulate(0, false); + cudaDeviceSynchronize(); + auto end = std::chrono::high_resolution_clock::now(); + // Compute duration + std::chrono::duration elapsed = end - start; + std::cout << "Simulation time: " << elapsed.count() << " seconds" << std::endl; + + + // unsigned int num_hits = SEvt::GetNumHit(EGPU); + SEvt *sev = SEvt::Get_EGPU(); + unsigned int num_hits = sev->GetNumHit(0); + std::cout << "Opticks: NumCollected: " << sev->GetNumGenstepFromGenstep(0) << std::endl; + + std::cout << "Opticks: NumCollected: " << sev->GetNumPhotonCollected(0) << std::endl; + + std::cout << "Opticks: NumHits: " << num_hits << std::endl; + + /* + std::ofstream outFile("opticks_hits_output.txt"); + if (!outFile.is_open()) + { + std::cerr << "Error opening output file!" << std::endl; + return; + } + + for (int idx = 0; idx < int(num_hits); idx++) + { + sphoton hit; + sev->getHit(hit, idx); + G4ThreeVector position = G4ThreeVector(hit.pos.x, hit.pos.y, hit.pos.z); + G4ThreeVector direction = G4ThreeVector(hit.mom.x, hit.mom.y, hit.mom.z); + G4ThreeVector polarization = G4ThreeVector(hit.pol.x, hit.pol.y, hit.pol.z); + int theCreationProcessid; + if (OpticksPhoton::HasCerenkovFlag(hit.flagmask)) + { + theCreationProcessid = 0; + } + else if (OpticksPhoton::HasScintillationFlag(hit.flagmask)) + { + theCreationProcessid = 1; + } + else + { + theCreationProcessid = -1; + } + // std::cout << "Adding hit from Opticks:" << hit.wavelength << " " << position << " " << direction << " + // " + // << polarization << std::endl; + outFile << hit.wavelength << " " + << "(" << position.x() << ", " << position.y() << ", " << position.z() << ") " + << "(" << direction.x() << ", " << direction.y() << ", " << direction.z() << ") " + << "(" << polarization.x() << ", " << polarization.y() << ", " << polarization.z() << ") " + << "CreationProcessID=" << theCreationProcessid << std::endl; + } + + outFile.close(); + */ + } + } +}; + +struct SteppingAction : G4UserSteppingAction +{ + SEvt *sev; + + SteppingAction(SEvt *sev) : sev(sev) + { + } + + void UserSteppingAction(const G4Step *aStep) + { + + G4Track *aTrack; + G4int fNumPhotons = 0; + + G4StepPoint *preStep = aStep->GetPostStepPoint(); + G4VPhysicalVolume *volume = preStep->GetPhysicalVolume(); + + if ( aStep->GetTrack()->GetDefinition() == G4OpticalPhoton::OpticalPhotonDefinition()) { + // Kill if step count exceeds 10000 to avoid reflection forever + if (aStep->GetTrack()->GetCurrentStepNumber() > 10000) { + aStep->GetTrack()->SetTrackStatus(fStopAndKill); + }} + + + if (volume && volume->GetName() == "MirrorPyramid") + { + aTrack = aStep->GetTrack(); + if (aTrack->GetDefinition() != G4OpticalPhoton::OpticalPhotonDefinition()) + { + aTrack->SetTrackStatus(fStopAndKill); + }} + + G4SteppingManager *fpSteppingManager = + G4EventManager::GetEventManager()->GetTrackingManager()->GetSteppingManager(); + G4StepStatus stepStatus = fpSteppingManager->GetfStepStatus(); + if (stepStatus != fAtRestDoItProc) + { + G4ProcessVector *procPost = fpSteppingManager->GetfPostStepDoItVector(); + size_t MAXofPostStepLoops = fpSteppingManager->GetMAXofPostStepLoops(); + for (size_t i3 = 0; i3 < MAXofPostStepLoops; i3++) + { + if ((*procPost)[i3]->GetProcessName() == "Cerenkov") + { + aTrack = aStep->GetTrack(); + const G4DynamicParticle *aParticle = aTrack->GetDynamicParticle(); + G4double charge = aParticle->GetDefinition()->GetPDGCharge(); + const G4Material *aMaterial = aTrack->GetMaterial(); + G4MaterialPropertiesTable *MPT = aMaterial->GetMaterialPropertiesTable(); + + G4MaterialPropertyVector *Rindex = MPT->GetProperty(kRINDEX); + if (!Rindex || Rindex->GetVectorLength() == 0) + { + G4cout << "WARNING: Material has no valid RINDEX data. Skipping Cerenkov calculation." << G4endl; + return; + } + + + G4Cerenkov *proc = (G4Cerenkov *)(*procPost)[i3]; + fNumPhotons = proc->GetNumPhotons(); + + G4AutoLock lock(&genstep_mutex); // <-- Mutex is locked here + + + if (fNumPhotons > 0) + { + G4double Pmin = Rindex->Energy(0); + G4double Pmax = Rindex->GetMaxEnergy(); + G4double nMax = Rindex->GetMaxValue(); + G4double beta1 = aStep->GetPreStepPoint()->GetBeta(); + G4double beta2 = aStep->GetPostStepPoint()->GetBeta(); + G4double beta = (beta1 + beta2) * 0.5; + G4double BetaInverse = 1. / beta; + G4double maxCos = BetaInverse / nMax; + G4double maxSin2 = (1.0 - maxCos) * (1.0 + maxCos); + G4double MeanNumberOfPhotons1 = + proc->GetAverageNumberOfPhotons(charge, beta1, aMaterial, Rindex); + G4double MeanNumberOfPhotons2 = + proc->GetAverageNumberOfPhotons(charge, beta2, aMaterial, Rindex); + + + + + U4::CollectGenstep_G4Cerenkov_modified(aTrack, aStep, fNumPhotons, BetaInverse, Pmin, Pmax, + maxCos, maxSin2, MeanNumberOfPhotons1, + MeanNumberOfPhotons2); + //std::cout << "MeanNumberOfPhotons1" << MeanNumberOfPhotons1 << std::endl; + + const G4Event* event = G4EventManager::GetEventManager()->GetConstCurrentEvent(); + if (!event) return; // Always check for null + G4int eventid = event->GetEventID(); + + // G4CXOpticks::Get()->simulate(eventid, false); + // cudaDeviceSynchronize(); + // unsigned int num_hits = SEvt::GetNumHit(0); + unsigned int num_hits = 0; + // std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + if (num_hits > 0) + { + G4HCtable *hctable = G4SDManager::GetSDMpointer()->GetHCtable(); + for (G4int i = 0; i < hctable->entries(); ++i) + { + std::string sdn = hctable->GetSDname(i); + std::size_t found = sdn.find("PhotonDetector"); + if (found != std::string::npos) + { + std::cout << "PhotonDetector: " << sdn << std::endl; + PhotonSD *aSD = + (PhotonSD *)G4SDManager::GetSDMpointer()->FindSensitiveDetector(sdn); + // aSD->AddOpticksHits(); + } + } + } + } + } + } + } + } +}; + +struct TrackingAction : G4UserTrackingAction +{ + const G4Track *transient_fSuspend_track = nullptr; + SEvt *sev; + + TrackingAction(SEvt *sev) : sev(sev) + { + } + + void PreUserTrackingAction_Optical_FabricateLabel(const G4Track *track) + { + } + + void PreUserTrackingAction(const G4Track *track) override + { + } + + void PostUserTrackingAction(const G4Track *track) override + { + } +}; + +struct G4App +{ + G4App(std::filesystem::path gdml_file) + : sev(SEvt::CreateOrReuse_EGPU()), det_cons_(new DetectorConstruction(gdml_file)), + prim_gen_(new PrimaryGenerator(sev)), event_act_(new EventAction(sev)), run_act_(new RunAction()), + stepping_(new SteppingAction(sev)), + + tracking_(new TrackingAction(sev)) + { + } + + //~G4App(){ G4CXOpticks::Finalize();} + + // Create "global" event + SEvt *sev; + + G4VUserDetectorConstruction *det_cons_; + G4VUserPrimaryGeneratorAction *prim_gen_; + EventAction *event_act_; + RunAction *run_act_; + SteppingAction *stepping_; + TrackingAction *tracking_; +}; diff --git a/src/simg4oxmt.cpp b/src/simg4oxmt.cpp new file mode 100644 index 0000000000..3240b35360 --- /dev/null +++ b/src/simg4oxmt.cpp @@ -0,0 +1,130 @@ +#include + +#include + +#include "FTFP_BERT.hh" +#include "G4OpticalPhysics.hh" +#include "G4VModularPhysicsList.hh" + +#include "G4UIExecutive.hh" +#include "G4UImanager.hh" +#include "G4VisExecutive.hh" + +#include "SysRap/OPTICKS_LOG.hh" + +#include "g4app.h" + +#include "G4VUserActionInitialization.hh" +#include "G4RunManagerFactory.hh" +#include "G4RunManager.hh" + + +using namespace std; + + +struct ActionInitialization : public G4VUserActionInitialization +{ +private: + G4App* fG4App; // Store the pointer to G4App + +public: + // Note the signature: now we take a pointer to the G4App itself + ActionInitialization(G4App* app) + : G4VUserActionInitialization(), fG4App(app) + { + } + + virtual void BuildForMaster() const override + { + SetUserAction(fG4App->run_act_); + } + + virtual void Build() const override + { + SetUserAction(fG4App->prim_gen_); + SetUserAction(fG4App->run_act_); + SetUserAction(fG4App->event_act_); + SetUserAction(fG4App->tracking_); + SetUserAction(fG4App->stepping_); + } +}; + +int main(int argc, char **argv) +{ + + long seed = static_cast(time(nullptr)); + CLHEP::HepRandom::setTheSeed(seed); + G4cout << "Random seed set to: " << seed << G4endl; + OPTICKS_LOG(argc, argv); + + argparse::ArgumentParser program("simg4ox", "0.0.0"); + + string gdml_file, macro_name; + bool interactive; + + program.add_argument("-g", "--gdml") + .help("path to GDML file") + .default_value(string("geom.gdml")) + .nargs(1) + .store_into(gdml_file); + + program.add_argument("-m", "--macro") + .help("path to G4 macro") + .default_value(string("run.mac")) + .nargs(1) + .store_into(macro_name); + + program.add_argument("-i", "--interactive") + .help("whether to open an interactive window with a viewer") + .flag() + .store_into(interactive); + + try + { + program.parse_args(argc, argv); + } + catch (const exception &err) + { + cerr << err.what() << endl; + cerr << program; + exit(EXIT_FAILURE); + } + + // Configure Geant4 + // The physics list must be instantiated before other user actions + G4VModularPhysicsList *physics = new FTFP_BERT; + physics->RegisterPhysics(new G4OpticalPhysics); + + auto *run_mgr = G4RunManagerFactory::CreateRunManager(); + run_mgr->SetUserInitialization(physics); + + + G4App *g4app = new G4App(gdml_file); + + ActionInitialization* ActionInit = new ActionInitialization(g4app); + run_mgr->SetUserInitialization(ActionInit); + run_mgr->SetUserInitialization(g4app->det_cons_); + //run_mgr->Initialize(); + + G4UIExecutive *uix = nullptr; + G4VisManager *vis = nullptr; + + if (interactive) + { + uix = new G4UIExecutive(argc, argv); + vis = new G4VisExecutive; + vis->Initialize(); + } + + G4UImanager *ui = G4UImanager::GetUIpointer(); + ui->ApplyCommand("/control/execute " + macro_name); + + if (interactive) + { + uix->SessionStart(); + } + + delete uix; + + return EXIT_SUCCESS; +} From 752f10a738e3f8d28edf348eaa0fb93bb5e94ea3 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Fri, 1 Aug 2025 19:20:55 -0400 Subject: [PATCH 03/35] build: add new executable simg4oxmt --- src/CMakeLists.txt | 10 +++++++++- src/g4appmt.h | 22 +++++++++++----------- src/simg4oxmt.cpp | 4 ++-- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cccbfa8258..2ed4381026 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,14 @@ target_include_directories(simg4ox PRIVATE $ ) +# simg4ox runs Geant4 and OptiX simulations +add_executable(simg4oxmt simg4oxmt.cpp g4appmt.h) +target_link_libraries(simg4oxmt gphox) +target_include_directories(simg4oxmt PRIVATE + $ + $ +) + # simtox creates a numpy file with initial photons for simulation add_executable(simtox simtox.cpp) @@ -42,7 +50,7 @@ target_include_directories(simtox PRIVATE $ ) -install(TARGETS consgeo simg4ox simtox gphox +install(TARGETS consgeo simg4ox simg4oxmt simtox gphox EXPORT ${PROJECT_NAME}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/src/g4appmt.h b/src/g4appmt.h index 76e239260b..6d11ef5899 100644 --- a/src/g4appmt.h +++ b/src/g4appmt.h @@ -5,7 +5,7 @@ #include #include "G4BooleanSolid.hh" -#include "G4CX/G4CXOpticks.hh" +#include "g4cx/G4CXOpticks.hh" #include "G4Electron.hh" #include "G4Event.hh" #include "G4GDMLParser.hh" @@ -30,16 +30,16 @@ #include "G4RunManager.hh" #include "G4VUserDetectorConstruction.hh" #include "G4VUserPrimaryGeneratorAction.hh" -#include "SysRap/NP.hh" -#include "SysRap/SEvt.hh" -#include "SysRap/STrackInfo.h" -#include "SysRap/spho.h" -#include "SysRap/sphoton.h" -#include "U4.hh" -#include "U4/U4Random.hh" -#include "U4/U4StepPoint.hh" -#include "U4/U4Touchable.h" -#include "U4/U4Track.h" +#include "sysrap/NP.hh" +#include "sysrap/SEvt.hh" +#include "sysrap/STrackInfo.h" +#include "sysrap/spho.h" +#include "sysrap/sphoton.h" +#include "u4/U4.hh" +#include "u4/U4Random.hh" +#include "u4/U4StepPoint.hh" +#include "u4/U4Touchable.h" +#include "u4/U4Track.h" #include "G4RunManagerFactory.hh" #include "G4AutoLock.hh" diff --git a/src/simg4oxmt.cpp b/src/simg4oxmt.cpp index 3240b35360..0eb8bb0460 100644 --- a/src/simg4oxmt.cpp +++ b/src/simg4oxmt.cpp @@ -10,9 +10,9 @@ #include "G4UImanager.hh" #include "G4VisExecutive.hh" -#include "SysRap/OPTICKS_LOG.hh" +#include "sysrap/OPTICKS_LOG.hh" -#include "g4app.h" +#include "g4appmt.h" #include "G4VUserActionInitialization.hh" #include "G4RunManagerFactory.hh" From 794031415380f288ea3897623fb8a326ef78f079 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Fri, 1 Aug 2025 19:30:00 -0400 Subject: [PATCH 04/35] add run_performance.py script --- optiphy/tools/run_performance.py | 69 ++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 optiphy/tools/run_performance.py diff --git a/optiphy/tools/run_performance.py b/optiphy/tools/run_performance.py new file mode 100644 index 0000000000..d95fd22015 --- /dev/null +++ b/optiphy/tools/run_performance.py @@ -0,0 +1,69 @@ +import subprocess +import re + +timings_file = "timings.txt" +opticks_file = "Opticks.txt" + +run_mac_template = """ +/run/numberOfThreads {threads} +/run/verbose 1 +/process/optical/cerenkov/setStackPhotons {flag} +/run/initialize +/run/beamOn 50000 +""" + +def parse_real_time(time_str): + # Parses 'real\t0m41.149s' to seconds + match = re.search(r'real\s+(\d+)m([\d.]+)s', time_str) + if match: + minutes = int(match.group(1)) + seconds = float(match.group(2)) + return minutes * 60 + seconds + return None + +def parse_sim_time(output): + match = re.search(r"Simulation time:\s*([\d.]+)\s*seconds", output) + if match: + return float(match.group(1)) + return None + +with open(timings_file, "w") as tf, open(opticks_file, "w") as of: + for threads in range(1, 21): + times = {} + sim_time_true = None + for flag in ['true', 'false']: + # Write run.mac with current flag + with open("run.mac", "w") as rm: + rm.write(run_mac_template.format(threads=threads, flag=flag)) + # Run with time in bash to capture real/user/sys + result = subprocess.run( + ["bash", "-c", "time ./build/src/simg4ox -g esi-g4ox/geom/pfrich_min_FINAL.gdml -m run.mac"], + capture_output=True, text=True + ) + stdout = result.stdout + stderr = result.stderr + + # Save simulation time for true run only + if flag == 'true': + sim_time_true = parse_sim_time(stdout + stderr) + if sim_time_true is not None: + of.write(f"{threads} {sim_time_true}\n") + of.flush() + + # Extract real time + real_match = re.search(r"real\s+\d+m[\d.]+s", stderr) + if real_match: + real_sec = parse_real_time(real_match.group()) + times[flag] = real_sec + else: + print(f"[!] Could not find 'real' time for threads={threads} flag={flag}") + + # Write the difference to timings.txt (true - false) + if 'true' in times and 'false' in times: + diff = times['true'] - times['false'] + tf.write(f"{threads} {diff}\n") + tf.flush() + else: + print(f"[!] Missing times for threads={threads}") + +print("Done.") From 22ee1ae54d01fba0ff3e91f5f007b880e310ee00 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Fri, 1 Aug 2025 19:39:25 -0400 Subject: [PATCH 05/35] add geom file pfrich_min_FINAL.gdml from esi-g4ox --- tests/geom/pfrich_min_FINAL.gdml | 2678 ++++++++++++++++++++++++++++++ 1 file changed, 2678 insertions(+) create mode 100644 tests/geom/pfrich_min_FINAL.gdml diff --git a/tests/geom/pfrich_min_FINAL.gdml b/tests/geom/pfrich_min_FINAL.gdml new file mode 100644 index 0000000000..b433db3eb8 --- /dev/null +++ b/tests/geom/pfrich_min_FINAL.gdml @@ -0,0 +1,2678 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + >>>�~T~ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 9ac27720ca42fcac87c0ad5f00ae34336278d267 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Fri, 1 Aug 2025 19:41:51 -0400 Subject: [PATCH 06/35] fix paths in performance script --- optiphy/tools/run_performance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optiphy/tools/run_performance.py b/optiphy/tools/run_performance.py index d95fd22015..e24af95a46 100644 --- a/optiphy/tools/run_performance.py +++ b/optiphy/tools/run_performance.py @@ -37,7 +37,7 @@ def parse_sim_time(output): rm.write(run_mac_template.format(threads=threads, flag=flag)) # Run with time in bash to capture real/user/sys result = subprocess.run( - ["bash", "-c", "time ./build/src/simg4ox -g esi-g4ox/geom/pfrich_min_FINAL.gdml -m run.mac"], + ["bash", "-c", "time simg4oxmt -g tests/geom/pfrich_min_FINAL.gdml -m run.mac"], capture_output=True, text=True ) stdout = result.stdout From a017d95b32085086e0c2fd87485319ae93c79214 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Fri, 1 Aug 2025 19:46:17 -0400 Subject: [PATCH 07/35] make run_performance to executable --- optiphy/tools/run_performance.py | 76 +++++++++++++++++--------------- pyproject.toml | 1 + 2 files changed, 42 insertions(+), 35 deletions(-) mode change 100644 => 100755 optiphy/tools/run_performance.py diff --git a/optiphy/tools/run_performance.py b/optiphy/tools/run_performance.py old mode 100644 new mode 100755 index e24af95a46..56955a9115 --- a/optiphy/tools/run_performance.py +++ b/optiphy/tools/run_performance.py @@ -27,43 +27,49 @@ def parse_sim_time(output): return float(match.group(1)) return None -with open(timings_file, "w") as tf, open(opticks_file, "w") as of: - for threads in range(1, 21): - times = {} - sim_time_true = None - for flag in ['true', 'false']: - # Write run.mac with current flag - with open("run.mac", "w") as rm: - rm.write(run_mac_template.format(threads=threads, flag=flag)) - # Run with time in bash to capture real/user/sys - result = subprocess.run( - ["bash", "-c", "time simg4oxmt -g tests/geom/pfrich_min_FINAL.gdml -m run.mac"], - capture_output=True, text=True - ) - stdout = result.stdout - stderr = result.stderr - # Save simulation time for true run only - if flag == 'true': - sim_time_true = parse_sim_time(stdout + stderr) - if sim_time_true is not None: - of.write(f"{threads} {sim_time_true}\n") - of.flush() +def main(): + with open(timings_file, "w") as tf, open(opticks_file, "w") as of: + for threads in range(1, 21): + times = {} + sim_time_true = None + for flag in ['true', 'false']: + # Write run.mac with current flag + with open("run.mac", "w") as rm: + rm.write(run_mac_template.format(threads=threads, flag=flag)) + # Run with time in bash to capture real/user/sys + result = subprocess.run( + ["bash", "-c", "time simg4oxmt -g tests/geom/pfrich_min_FINAL.gdml -m run.mac"], + capture_output=True, text=True + ) + stdout = result.stdout + stderr = result.stderr - # Extract real time - real_match = re.search(r"real\s+\d+m[\d.]+s", stderr) - if real_match: - real_sec = parse_real_time(real_match.group()) - times[flag] = real_sec + # Save simulation time for true run only + if flag == 'true': + sim_time_true = parse_sim_time(stdout + stderr) + if sim_time_true is not None: + of.write(f"{threads} {sim_time_true}\n") + of.flush() + + # Extract real time + real_match = re.search(r"real\s+\d+m[\d.]+s", stderr) + if real_match: + real_sec = parse_real_time(real_match.group()) + times[flag] = real_sec + else: + print(f"[!] Could not find 'real' time for threads={threads} flag={flag}") + + # Write the difference to timings.txt (true - false) + if 'true' in times and 'false' in times: + diff = times['true'] - times['false'] + tf.write(f"{threads} {diff}\n") + tf.flush() else: - print(f"[!] Could not find 'real' time for threads={threads} flag={flag}") + print(f"[!] Missing times for threads={threads}") + + print("Done.") - # Write the difference to timings.txt (true - false) - if 'true' in times and 'false' in times: - diff = times['true'] - times['false'] - tf.write(f"{threads} {diff}\n") - tf.flush() - else: - print(f"[!] Missing times for threads={threads}") -print("Done.") +if __name__ == '__main__': + main() diff --git a/pyproject.toml b/pyproject.toml index e3d82cc3aa..7ef5bc28e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ dependencies = [ generate-input-photons = "optiphy.tools.generate_input_photons:main" plot-csg = "optiphy.tools.plot_csg:main" serve-path = "optiphy.tools.serve_path:main" +run-performance = "optiphy.tools.run_performance:main" [build-system] requires = ["setuptools", "wheel"] From 234bff29086fe6bfab43ee686923ca0dd459d8c2 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Mon, 4 Aug 2025 13:11:22 -0400 Subject: [PATCH 08/35] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CSG/csg_intersect_leaf_newcone.h | 2 - src/g4appmt.h | 222 +++++++++---------------------- src/simg4oxmt.cpp | 28 ++-- 3 files changed, 77 insertions(+), 175 deletions(-) diff --git a/CSG/csg_intersect_leaf_newcone.h b/CSG/csg_intersect_leaf_newcone.h index 576c39bff3..6c335b2438 100644 --- a/CSG/csg_intersect_leaf_newcone.h +++ b/CSG/csg_intersect_leaf_newcone.h @@ -119,5 +119,3 @@ void intersect_leaf_newcone( bool& valid_isect, float4& isect, const quad& q0, c isect.w = t_cand ; } } - - diff --git a/src/g4appmt.h b/src/g4appmt.h index 6d11ef5899..88258fcb45 100644 --- a/src/g4appmt.h +++ b/src/g4appmt.h @@ -1,11 +1,10 @@ -#include "G4Cerenkov.hh" -#include "G4Scintillation.hh" #include #include #include +#include "G4AutoLock.hh" #include "G4BooleanSolid.hh" -#include "g4cx/G4CXOpticks.hh" +#include "G4Cerenkov.hh" #include "G4Electron.hh" #include "G4Event.hh" #include "G4GDMLParser.hh" @@ -15,7 +14,10 @@ #include "G4PhysicalConstants.hh" #include "G4PrimaryParticle.hh" #include "G4PrimaryVertex.hh" +#include "G4RunManager.hh" +#include "G4RunManagerFactory.hh" #include "G4SDManager.hh" +#include "G4Scintillation.hh" #include "G4SubtractionSolid.hh" #include "G4SystemOfUnits.hh" #include "G4ThreeVector.hh" @@ -27,9 +29,10 @@ #include "G4UserTrackingAction.hh" #include "G4VPhysicalVolume.hh" #include "G4VProcess.hh" -#include "G4RunManager.hh" #include "G4VUserDetectorConstruction.hh" #include "G4VUserPrimaryGeneratorAction.hh" + +#include "g4cx/G4CXOpticks.hh" #include "sysrap/NP.hh" #include "sysrap/SEvt.hh" #include "sysrap/STrackInfo.h" @@ -40,12 +43,11 @@ #include "u4/U4StepPoint.hh" #include "u4/U4Touchable.h" #include "u4/U4Track.h" -#include "G4RunManagerFactory.hh" -#include "G4AutoLock.hh" - - -namespace { G4Mutex genstep_mutex = G4MUTEX_INITIALIZER; } +namespace +{ +G4Mutex genstep_mutex = G4MUTEX_INITIALIZER; +} bool IsSubtractionSolid(G4VSolid *solid) { @@ -120,8 +122,7 @@ struct PhotonHit : public G4VHit // Print method void Print() override { - G4cout << "Detector id: " << fid << " energy: " << fenergy << " nm" - << " time: " << ftime << " ns" + G4cout << "Detector id: " << fid << " energy: " << fenergy << " nm" << " time: " << ftime << " ns" << " position: " << fposition << " direction: " << fdirection << " polarization: " << fpolarization << G4endl; } @@ -151,7 +152,7 @@ struct PhotonSD : public G4VSensitiveDetector fPhotonHitsCollection = new PhotonHitsCollection(SensitiveDetectorName, collectionName[0]); if (fHCID < 0) { - //G4cout << "PhotonSD::Initialize: " << SensitiveDetectorName << " " << collectionName[0] << G4endl; + // G4cout << "PhotonSD::Initialize: " << SensitiveDetectorName << " " << collectionName[0] << G4endl; fHCID = G4SDManager::GetSDMpointer()->GetCollectionID(collectionName[0]); } hce->AddHitsCollection(fHCID, fPhotonHitsCollection); @@ -178,50 +179,7 @@ struct PhotonSD : public G4VSensitiveDetector void EndOfEvent(G4HCofThisEvent *) override { - - G4int NbHits = fPhotonHitsCollection->entries(); - //G4cout << "PhotonSD::EndOfEvent Number of PhotonHits: " << NbHits << G4endl; - - // Open an output file (text mode) - - /* - int tid = G4Threading::G4GetThreadId(); - std::ostringstream fname; - fname << "g4_photon_hits_thread" << tid << ".txt"; - std::ofstream outFile(fname.str().c_str(), std::ios::app); - - - if (!outFile.is_open()) - { - G4cerr << "Error opening output file g4_photon_hits.txt!" << G4endl; - return; - } - - // Loop over all recorded hits TOBEDONE: move this to endofrunaction - for (G4int i = 0; i < NbHits; i++) - { - PhotonHit *hit = (*fPhotonHitsCollection)[i]; - - G4int id = hit->fid; - G4double energy = hit->fenergy; - G4double time = hit->ftime; - G4ThreeVector position = hit->fposition; - G4ThreeVector direction = hit->fdirection; - G4ThreeVector pol = hit->fpolarization; - - // Write out info in a style similar to Opticks hits - - outFile << "Adding hit from Geant4: " << energy << " eV " - << "(" << position.x() << ", " << position.y() << ", " << position.z() << ") " - << "(" << direction.x() << ", " << direction.y() << ", " << direction.z() << ") " - << "(" << pol.x() << ", " << pol.y() << ", " << pol.z() << ") " - << "Time=" << time << " " - << "ID=" << id << G4endl; - - } - - // Close the file - outFile.close(); */ + G4int NbHits = fPhotonHitsCollection->entries(); } void AddOpticksHits() @@ -338,7 +296,7 @@ struct PrimaryGenerator : G4VUserPrimaryGeneratorAction void GeneratePrimaries(G4Event *event) override { G4ThreeVector position_mm(-0.4 * m, -0.3 * m, -0.3 * m); - G4double time_ns = 0; + G4double time_ns = 0; G4ThreeVector direction(0, 0.2, 0.8); G4double wavelength_nm = 0.1; @@ -380,70 +338,26 @@ struct RunAction : G4UserRunAction void EndOfRunAction(const G4Run *run) override { - - if (G4Threading::IsMasterThread()) - { - G4CXOpticks *gx = G4CXOpticks::Get(); - - auto start = std::chrono::high_resolution_clock::now(); - gx->simulate(0, false); - cudaDeviceSynchronize(); - auto end = std::chrono::high_resolution_clock::now(); - // Compute duration - std::chrono::duration elapsed = end - start; - std::cout << "Simulation time: " << elapsed.count() << " seconds" << std::endl; - - - // unsigned int num_hits = SEvt::GetNumHit(EGPU); - SEvt *sev = SEvt::Get_EGPU(); - unsigned int num_hits = sev->GetNumHit(0); - std::cout << "Opticks: NumCollected: " << sev->GetNumGenstepFromGenstep(0) << std::endl; - - std::cout << "Opticks: NumCollected: " << sev->GetNumPhotonCollected(0) << std::endl; - - std::cout << "Opticks: NumHits: " << num_hits << std::endl; - - /* - std::ofstream outFile("opticks_hits_output.txt"); - if (!outFile.is_open()) + if (G4Threading::IsMasterThread()) { - std::cerr << "Error opening output file!" << std::endl; - return; + G4CXOpticks *gx = G4CXOpticks::Get(); + + auto start = std::chrono::high_resolution_clock::now(); + gx->simulate(0, false); + cudaDeviceSynchronize(); + auto end = std::chrono::high_resolution_clock::now(); + // Compute duration + std::chrono::duration elapsed = end - start; + std::cout << "Simulation time: " << elapsed.count() << " seconds" << std::endl; + + // unsigned int num_hits = SEvt::GetNumHit(EGPU); + SEvt *sev = SEvt::Get_EGPU(); + unsigned int num_hits = sev->GetNumHit(0); + + std::cout << "Opticks: NumCollected: " << sev->GetNumGenstepFromGenstep(0) << std::endl; + std::cout << "Opticks: NumCollected: " << sev->GetNumPhotonCollected(0) << std::endl; + std::cout << "Opticks: NumHits: " << num_hits << std::endl; } - - for (int idx = 0; idx < int(num_hits); idx++) - { - sphoton hit; - sev->getHit(hit, idx); - G4ThreeVector position = G4ThreeVector(hit.pos.x, hit.pos.y, hit.pos.z); - G4ThreeVector direction = G4ThreeVector(hit.mom.x, hit.mom.y, hit.mom.z); - G4ThreeVector polarization = G4ThreeVector(hit.pol.x, hit.pol.y, hit.pol.z); - int theCreationProcessid; - if (OpticksPhoton::HasCerenkovFlag(hit.flagmask)) - { - theCreationProcessid = 0; - } - else if (OpticksPhoton::HasScintillationFlag(hit.flagmask)) - { - theCreationProcessid = 1; - } - else - { - theCreationProcessid = -1; - } - // std::cout << "Adding hit from Opticks:" << hit.wavelength << " " << position << " " << direction << " - // " - // << polarization << std::endl; - outFile << hit.wavelength << " " - << "(" << position.x() << ", " << position.y() << ", " << position.z() << ") " - << "(" << direction.x() << ", " << direction.y() << ", " << direction.z() << ") " - << "(" << polarization.x() << ", " << polarization.y() << ", " << polarization.z() << ") " - << "CreationProcessID=" << theCreationProcessid << std::endl; - } - - outFile.close(); - */ - } } }; @@ -457,31 +371,34 @@ struct SteppingAction : G4UserSteppingAction void UserSteppingAction(const G4Step *aStep) { - G4Track *aTrack; G4int fNumPhotons = 0; - + G4StepPoint *preStep = aStep->GetPostStepPoint(); - G4VPhysicalVolume *volume = preStep->GetPhysicalVolume(); + G4VPhysicalVolume *volume = preStep->GetPhysicalVolume(); - if ( aStep->GetTrack()->GetDefinition() == G4OpticalPhoton::OpticalPhotonDefinition()) { - // Kill if step count exceeds 10000 to avoid reflection forever - if (aStep->GetTrack()->GetCurrentStepNumber() > 10000) { - aStep->GetTrack()->SetTrackStatus(fStopAndKill); - }} + if (aStep->GetTrack()->GetDefinition() == G4OpticalPhoton::OpticalPhotonDefinition()) + { + // Kill if step count exceeds 10000 to avoid reflection forever + if (aStep->GetTrack()->GetCurrentStepNumber() > 10000) + { + aStep->GetTrack()->SetTrackStatus(fStopAndKill); + } + } - - if (volume && volume->GetName() == "MirrorPyramid") + if (volume && volume->GetName() == "MirrorPyramid") { - aTrack = aStep->GetTrack(); + aTrack = aStep->GetTrack(); if (aTrack->GetDefinition() != G4OpticalPhoton::OpticalPhotonDefinition()) { aTrack->SetTrackStatus(fStopAndKill); - }} - - G4SteppingManager *fpSteppingManager = + } + } + + G4SteppingManager *fpSteppingManager = G4EventManager::GetEventManager()->GetTrackingManager()->GetSteppingManager(); G4StepStatus stepStatus = fpSteppingManager->GetfStepStatus(); + if (stepStatus != fAtRestDoItProc) { G4ProcessVector *procPost = fpSteppingManager->GetfPostStepDoItVector(); @@ -495,20 +412,19 @@ struct SteppingAction : G4UserSteppingAction G4double charge = aParticle->GetDefinition()->GetPDGCharge(); const G4Material *aMaterial = aTrack->GetMaterial(); G4MaterialPropertiesTable *MPT = aMaterial->GetMaterialPropertiesTable(); - - G4MaterialPropertyVector *Rindex = MPT->GetProperty(kRINDEX); - if (!Rindex || Rindex->GetVectorLength() == 0) - { - G4cout << "WARNING: Material has no valid RINDEX data. Skipping Cerenkov calculation." << G4endl; + + G4MaterialPropertyVector *Rindex = MPT->GetProperty(kRINDEX); + if (!Rindex || Rindex->GetVectorLength() == 0) + { + G4cout << "WARNING: Material has no valid RINDEX data. Skipping Cerenkov calculation." + << G4endl; return; - } + } - - G4Cerenkov *proc = (G4Cerenkov *)(*procPost)[i3]; + G4Cerenkov *proc = (G4Cerenkov *)(*procPost)[i3]; fNumPhotons = proc->GetNumPhotons(); - G4AutoLock lock(&genstep_mutex); // <-- Mutex is locked here - + G4AutoLock lock(&genstep_mutex); // <-- Mutex is locked here if (fNumPhotons > 0) { @@ -525,24 +441,17 @@ struct SteppingAction : G4UserSteppingAction proc->GetAverageNumberOfPhotons(charge, beta1, aMaterial, Rindex); G4double MeanNumberOfPhotons2 = proc->GetAverageNumberOfPhotons(charge, beta2, aMaterial, Rindex); - - - - - U4::CollectGenstep_G4Cerenkov_modified(aTrack, aStep, fNumPhotons, BetaInverse, Pmin, Pmax, + + U4::CollectGenstep_G4Cerenkov_modified(aTrack, aStep, fNumPhotons, BetaInverse, Pmin, Pmax, maxCos, maxSin2, MeanNumberOfPhotons1, MeanNumberOfPhotons2); - //std::cout << "MeanNumberOfPhotons1" << MeanNumberOfPhotons1 << std::endl; - const G4Event* event = G4EventManager::GetEventManager()->GetConstCurrentEvent(); - if (!event) return; // Always check for null - G4int eventid = event->GetEventID(); + const G4Event *event = G4EventManager::GetEventManager()->GetConstCurrentEvent(); + if (!event) + return; // Always check for null + G4int eventid = event->GetEventID(); - // G4CXOpticks::Get()->simulate(eventid, false); - // cudaDeviceSynchronize(); - // unsigned int num_hits = SEvt::GetNumHit(0); unsigned int num_hits = 0; - // std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (num_hits > 0) { @@ -556,7 +465,6 @@ struct SteppingAction : G4UserSteppingAction std::cout << "PhotonDetector: " << sdn << std::endl; PhotonSD *aSD = (PhotonSD *)G4SDManager::GetSDMpointer()->FindSensitiveDetector(sdn); - // aSD->AddOpticksHits(); } } } diff --git a/src/simg4oxmt.cpp b/src/simg4oxmt.cpp index 0eb8bb0460..484d1b516b 100644 --- a/src/simg4oxmt.cpp +++ b/src/simg4oxmt.cpp @@ -14,29 +14,26 @@ #include "g4appmt.h" -#include "G4VUserActionInitialization.hh" -#include "G4RunManagerFactory.hh" #include "G4RunManager.hh" - +#include "G4RunManagerFactory.hh" +#include "G4VUserActionInitialization.hh" using namespace std; - struct ActionInitialization : public G4VUserActionInitialization { -private: - G4App* fG4App; // Store the pointer to G4App + private: + G4App *fG4App; // Store the pointer to G4App -public: + public: // Note the signature: now we take a pointer to the G4App itself - ActionInitialization(G4App* app) - : G4VUserActionInitialization(), fG4App(app) + ActionInitialization(G4App *app) : G4VUserActionInitialization(), fG4App(app) { } virtual void BuildForMaster() const override { - SetUserAction(fG4App->run_act_); + SetUserAction(fG4App->run_act_); } virtual void Build() const override @@ -51,10 +48,10 @@ struct ActionInitialization : public G4VUserActionInitialization int main(int argc, char **argv) { - - long seed = static_cast(time(nullptr)); + + long seed = static_cast(time(nullptr)); CLHEP::HepRandom::setTheSeed(seed); - G4cout << "Random seed set to: " << seed << G4endl; + G4cout << "Random seed set to: " << seed << G4endl; OPTICKS_LOG(argc, argv); argparse::ArgumentParser program("simg4ox", "0.0.0"); @@ -98,13 +95,12 @@ int main(int argc, char **argv) auto *run_mgr = G4RunManagerFactory::CreateRunManager(); run_mgr->SetUserInitialization(physics); - G4App *g4app = new G4App(gdml_file); - ActionInitialization* ActionInit = new ActionInitialization(g4app); + ActionInitialization *ActionInit = new ActionInitialization(g4app); run_mgr->SetUserInitialization(ActionInit); run_mgr->SetUserInitialization(g4app->det_cons_); - //run_mgr->Initialize(); + // run_mgr->Initialize(); G4UIExecutive *uix = nullptr; G4VisManager *vis = nullptr; From efe9b80fe793059201015fc8ef32cacbd18801f8 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Mon, 4 Aug 2025 16:58:26 -0400 Subject: [PATCH 09/35] set output dir to /tmp --- optiphy/tools/run_performance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/optiphy/tools/run_performance.py b/optiphy/tools/run_performance.py index 56955a9115..33adc52753 100755 --- a/optiphy/tools/run_performance.py +++ b/optiphy/tools/run_performance.py @@ -1,8 +1,8 @@ import subprocess import re -timings_file = "timings.txt" -opticks_file = "Opticks.txt" +timings_file = "/tmp/timings.txt" +opticks_file = "/tmp/Opticks.txt" run_mac_template = """ /run/numberOfThreads {threads} From 0ef017dd6a1bdf20914835846056c9fed68ba1ea Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Tue, 5 Aug 2025 18:07:47 -0400 Subject: [PATCH 10/35] refactor(run_performance): use argparse and pathlib for I/O path configuration --- optiphy/tools/run_performance.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/optiphy/tools/run_performance.py b/optiphy/tools/run_performance.py index 33adc52753..69cda7274b 100755 --- a/optiphy/tools/run_performance.py +++ b/optiphy/tools/run_performance.py @@ -1,8 +1,8 @@ +import argparse import subprocess import re -timings_file = "/tmp/timings.txt" -opticks_file = "/tmp/Opticks.txt" +from pathlib import Path run_mac_template = """ /run/numberOfThreads {threads} @@ -29,8 +29,17 @@ def parse_sim_time(output): def main(): - with open(timings_file, "w") as tf, open(opticks_file, "w") as of: - for threads in range(1, 21): + parser = argparse.ArgumentParser() + parser.add_argument('-g', '--gdml', type=Path, default=Path('tests/geom/pfrich_min_FINAL.gdml'), help="Path to a custom GDML geometry file") + parser.add_argument('-o', '--outpath', type=Path, default=Path('./'), help="Path where the output file will be saved") + + args = parser.parse_args() + + geant_file = args.outpath/"timing_geant.txt" + optix_file = args.outpath/"timing_optix.txt" + + with open(geant_file, "w") as gfile, open(optix_file, "w") as ofile: + for threads in range(50, 0, -1): times = {} sim_time_true = None for flag in ['true', 'false']: @@ -38,8 +47,10 @@ def main(): with open("run.mac", "w") as rm: rm.write(run_mac_template.format(threads=threads, flag=flag)) # Run with time in bash to capture real/user/sys + cmd = f"time simg4oxmt -g {args.gdml} -m run.mac" + print(f"Running {threads} threads: {cmd}") result = subprocess.run( - ["bash", "-c", "time simg4oxmt -g tests/geom/pfrich_min_FINAL.gdml -m run.mac"], + ["bash", "-c", cmd], capture_output=True, text=True ) stdout = result.stdout @@ -49,8 +60,8 @@ def main(): if flag == 'true': sim_time_true = parse_sim_time(stdout + stderr) if sim_time_true is not None: - of.write(f"{threads} {sim_time_true}\n") - of.flush() + ofile.write(f"{threads} {sim_time_true}\n") + ofile.flush() # Extract real time real_match = re.search(r"real\s+\d+m[\d.]+s", stderr) @@ -60,11 +71,11 @@ def main(): else: print(f"[!] Could not find 'real' time for threads={threads} flag={flag}") - # Write the difference to timings.txt (true - false) + # Write the difference to timing_geant.txt (true - false) if 'true' in times and 'false' in times: diff = times['true'] - times['false'] - tf.write(f"{threads} {diff}\n") - tf.flush() + gfile.write(f"{threads} {diff}\n") + gfile.flush() else: print(f"[!] Missing times for threads={threads}") From eb82acdf7b78bf1f012233a4d1a1fde528f6832d Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Fri, 5 Dec 2025 16:28:07 -0500 Subject: [PATCH 11/35] Update tests/geom/pfrich_min_FINAL.gdml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/geom/pfrich_min_FINAL.gdml | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/geom/pfrich_min_FINAL.gdml b/tests/geom/pfrich_min_FINAL.gdml index b433db3eb8..9dcb4fb0f1 100644 --- a/tests/geom/pfrich_min_FINAL.gdml +++ b/tests/geom/pfrich_min_FINAL.gdml @@ -2037,7 +2037,6 @@ - >>>�~T~ From e4ef18b5536850727c0411e96a933f0def795afd Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Fri, 5 Dec 2025 16:29:10 -0500 Subject: [PATCH 12/35] Update src/simg4oxmt.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/simg4oxmt.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/simg4oxmt.cpp b/src/simg4oxmt.cpp index 484d1b516b..a135fa147e 100644 --- a/src/simg4oxmt.cpp +++ b/src/simg4oxmt.cpp @@ -100,7 +100,6 @@ int main(int argc, char **argv) ActionInitialization *ActionInit = new ActionInitialization(g4app); run_mgr->SetUserInitialization(ActionInit); run_mgr->SetUserInitialization(g4app->det_cons_); - // run_mgr->Initialize(); G4UIExecutive *uix = nullptr; G4VisManager *vis = nullptr; From f3f7a16bf7a07cf042404b1afee1e559fca6a025 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Fri, 5 Dec 2025 16:29:50 -0500 Subject: [PATCH 13/35] Update src/simg4oxmt.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/simg4oxmt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/simg4oxmt.cpp b/src/simg4oxmt.cpp index a135fa147e..1c2b5b8e98 100644 --- a/src/simg4oxmt.cpp +++ b/src/simg4oxmt.cpp @@ -97,8 +97,8 @@ int main(int argc, char **argv) G4App *g4app = new G4App(gdml_file); - ActionInitialization *ActionInit = new ActionInitialization(g4app); - run_mgr->SetUserInitialization(ActionInit); + ActionInitialization *actionInit = new ActionInitialization(g4app); + run_mgr->SetUserInitialization(actionInit); run_mgr->SetUserInitialization(g4app->det_cons_); G4UIExecutive *uix = nullptr; From 7aa127dfe381a9066921eaf9114301291769d2bb Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Mon, 8 Dec 2025 11:47:50 -0500 Subject: [PATCH 14/35] Update README --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6d501410ba..6a1624f0f3 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,12 @@ Process](https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/ ## Performance studies ``` -docker run --rm -it -v $HOME:/esi -v $HOME/eic-opticks:/src/eic-opticks -e DISPLAY=$DISPLAY -e HOME=/esi --net=host ghcr.io/bnlnpps/eic-opticks:develop -cmake -S $OPTICKS_HOME -B $OPTICKS_BUILD -DCMAKE_INSTALL_PREFIX=$OPTICKS_PREFIX -DOptiX_INSTALL_DIR=/opt/optix -DCMAKE_BUILD_TYPE=Debug -cmake --build $OPTICKS_BUILD --parallel --target install +mkdir -p /tmp/out/dev +mkdir -p /tmp/out/rel + +docker build -t eic-opticks:perf-dev --target=develop +docker run --rm -t -v /tmp/out:/tmp/out eic-opticks:perf-dev run-performance -o /tmp/out/dev + +docker build -t eic-opticks:perf-rel --target=release +docker run --rm -t -v /tmp/out:/tmp/out eic-opticks:perf-rel run-performance -o /tmp/out/rel ``` From 472b72b000838eb7c124372c49a4153697ebf9d7 Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Mon, 8 Dec 2025 16:12:49 -0500 Subject: [PATCH 15/35] Add OPTICKS_EVENT_MODE environment variable Set environment variable for event mode to be sure not to export many debug info and slow down the GPU code --- optiphy/tools/run_performance.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/optiphy/tools/run_performance.py b/optiphy/tools/run_performance.py index 69cda7274b..926299b92a 100755 --- a/optiphy/tools/run_performance.py +++ b/optiphy/tools/run_performance.py @@ -1,7 +1,7 @@ import argparse import subprocess import re - +import os from pathlib import Path run_mac_template = """ @@ -12,6 +12,8 @@ /run/beamOn 50000 """ +os.environ["OPTICKS_EVENT_MODE"] = "Minimal" + def parse_real_time(time_str): # Parses 'real\t0m41.149s' to seconds match = re.search(r'real\s+(\d+)m([\d.]+)s', time_str) From ab8113428bf4f7e11f9227686da2349ce1cfc13f Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Thu, 11 Dec 2025 17:32:52 -0500 Subject: [PATCH 16/35] Make run_performance.py use OPTICKS_HOME environment variable so it runs from everywhere --- optiphy/tools/run_performance.py | 34 ++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/optiphy/tools/run_performance.py b/optiphy/tools/run_performance.py index 926299b92a..00e55adc31 100755 --- a/optiphy/tools/run_performance.py +++ b/optiphy/tools/run_performance.py @@ -14,6 +14,14 @@ os.environ["OPTICKS_EVENT_MODE"] = "Minimal" +def get_opticks_home(): + """Get OPTICKS_HOME from environment, warn if not set.""" + opticks_home = os.environ.get("OPTICKS_HOME") + if opticks_home is None: + print("Warning: $OPTICKS_HOME is not defined, so this script should be called from the eic-opticks directory.") + return Path(".") + return Path(opticks_home) + def parse_real_time(time_str): # Parses 'real\t0m41.149s' to seconds match = re.search(r'real\s+(\d+)m([\d.]+)s', time_str) @@ -29,16 +37,26 @@ def parse_sim_time(output): return float(match.group(1)) return None - def main(): + opticks_home = get_opticks_home() + parser = argparse.ArgumentParser() - parser.add_argument('-g', '--gdml', type=Path, default=Path('tests/geom/pfrich_min_FINAL.gdml'), help="Path to a custom GDML geometry file") + parser.add_argument('-g', '--gdml', type=Path, default=None, help="Path to a custom GDML geometry file (relative to current directory)") parser.add_argument('-o', '--outpath', type=Path, default=Path('./'), help="Path where the output file will be saved") - args = parser.parse_args() - geant_file = args.outpath/"timing_geant.txt" - optix_file = args.outpath/"timing_optix.txt" + # If gdml not provided, use default path relative to OPTICKS_HOME + if args.gdml is None: + gdml_path = opticks_home / 'tests/geom/pfrich_min_FINAL.gdml' + else: + # User-provided path is used as-is (relative to current directory or absolute) + gdml_path = args.gdml + + # run.mac is created in OPTICKS_HOME + run_mac_path = opticks_home / "run.mac" + + geant_file = args.outpath / "timing_geant.txt" + optix_file = args.outpath / "timing_optix.txt" with open(geant_file, "w") as gfile, open(optix_file, "w") as ofile: for threads in range(50, 0, -1): @@ -46,10 +64,11 @@ def main(): sim_time_true = None for flag in ['true', 'false']: # Write run.mac with current flag - with open("run.mac", "w") as rm: + with open(run_mac_path, "w") as rm: rm.write(run_mac_template.format(threads=threads, flag=flag)) + # Run with time in bash to capture real/user/sys - cmd = f"time simg4oxmt -g {args.gdml} -m run.mac" + cmd = f"time simg4oxmt -g {gdml_path} -m {run_mac_path}" print(f"Running {threads} threads: {cmd}") result = subprocess.run( ["bash", "-c", cmd], @@ -83,6 +102,5 @@ def main(): print("Done.") - if __name__ == '__main__': main() From f3d977e9cbf7414e34b3cc2ece81d90b6ae76aad Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Fri, 12 Dec 2025 10:27:51 -0500 Subject: [PATCH 17/35] Move particle gun to create photons in raindrop geom --- src/g4appmt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g4appmt.h b/src/g4appmt.h index 88258fcb45..e83936db72 100644 --- a/src/g4appmt.h +++ b/src/g4appmt.h @@ -295,7 +295,7 @@ struct PrimaryGenerator : G4VUserPrimaryGeneratorAction void GeneratePrimaries(G4Event *event) override { - G4ThreeVector position_mm(-0.4 * m, -0.3 * m, -0.3 * m); + G4ThreeVector position_mm(0.0 * m, 0.0 * m, 0.0 * m); G4double time_ns = 0; G4ThreeVector direction(0, 0.2, 0.8); G4double wavelength_nm = 0.1; From 3a4bc9c0b1f8d5b8dbaf43393a3c2fbec97f5b9b Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Fri, 12 Dec 2025 10:35:32 -0500 Subject: [PATCH 18/35] Reduce number of beamOn events from 50000 to 500 to be sure to make it work with small GPU RAM --- optiphy/tools/run_performance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optiphy/tools/run_performance.py b/optiphy/tools/run_performance.py index 00e55adc31..6f34d1e8de 100755 --- a/optiphy/tools/run_performance.py +++ b/optiphy/tools/run_performance.py @@ -9,7 +9,7 @@ /run/verbose 1 /process/optical/cerenkov/setStackPhotons {flag} /run/initialize -/run/beamOn 50000 +/run/beamOn 500 """ os.environ["OPTICKS_EVENT_MODE"] = "Minimal" From 4096e4e99f0fbafff34b963cd5064330da455372 Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Fri, 12 Dec 2025 12:33:16 -0500 Subject: [PATCH 19/35] Add log file for performance run outputs Added logging functionality to capture G4 printouts from the performance run. Including number of Opticks hits --- optiphy/tools/run_performance.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/optiphy/tools/run_performance.py b/optiphy/tools/run_performance.py index 6f34d1e8de..5551d1035c 100755 --- a/optiphy/tools/run_performance.py +++ b/optiphy/tools/run_performance.py @@ -57,8 +57,9 @@ def main(): geant_file = args.outpath / "timing_geant.txt" optix_file = args.outpath / "timing_optix.txt" + log_file = args.outpath / "g4logs.txt" - with open(geant_file, "w") as gfile, open(optix_file, "w") as ofile: + with open(geant_file, "w") as gfile, open(optix_file, "w") as ofile, open(log_file, "w") as logfile: for threads in range(50, 0, -1): times = {} sim_time_true = None @@ -76,6 +77,13 @@ def main(): ) stdout = result.stdout stderr = result.stderr + # Save full output to log file + logfile.write(f"\n{'='*60}\n") + logfile.write(f"Threads: {threads}, StackPhotons: {flag}\n") + logfile.write(f"{'='*60}\n") + logfile.write(f"--- STDOUT ---\n{stdout}\n") + logfile.write(f"--- STDERR ---\n{stderr}\n") + logfile.flush() # Save simulation time for true run only if flag == 'true': From 0c4da7998bb84bec995df6c75f1dd905377ff169 Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Fri, 12 Dec 2025 14:47:57 -0500 Subject: [PATCH 20/35] Update README with user-defined inputs Added section on user-defined inputs and geometry loading. --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 6a1624f0f3..90658362c7 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,24 @@ Process](https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/ ... ``` +## User/developer defined inputs +### Defining primary particles + +There are certain user defined inputs that the user/developer has to define. In the ```src/simg4oxmt``` example that imports ```src/g4appmt.h``` we provide a working example with a simple geometry. The User/developer has to change the following details: +**Number of primary particles** to simulate in a macro file and the **number of G4 threads**. For example: + +``` +/run/numberOfThreads {threads} +/run/verbose 1 +/process/optical/cerenkov/setStackPhotons {flag} +/run/initialize +/run/beamOn 500 +``` + +Here if the setStackPhotons defines **whether G4 will propagate optical photons or not**. In production Opticks (GPU) takes care of the optical photon propagation. Additionally the user has to define the **starting position**, **momentum** etc of the primary particles define in the **GeneratePrimaries** function in ``src/g4appmt.h```. The hits of the optical photons are returned in the **EndOfRunAction** function. If more photons are simulated than can fit in the GPU RAM the execution of a GPU call should be moved to **EndOfEventAction** together with retriving the hits. + +### Loading in geometry into EIC-Opticks ## Performance studies From 22bf1e0e8594feeda7d51e2646606795a691aa0d Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Fri, 12 Dec 2025 14:56:47 -0500 Subject: [PATCH 21/35] Update README with setStackPhotons and GDML details Clarified the explanation of setStackPhotons and added details about GDML geometry import. --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 90658362c7..2f292faf60 100644 --- a/README.md +++ b/README.md @@ -140,10 +140,20 @@ There are certain user defined inputs that the user/developer has to define. In /run/beamOn 500 ``` -Here if the setStackPhotons defines **whether G4 will propagate optical photons or not**. In production Opticks (GPU) takes care of the optical photon propagation. Additionally the user has to define the **starting position**, **momentum** etc of the primary particles define in the **GeneratePrimaries** function in ``src/g4appmt.h```. The hits of the optical photons are returned in the **EndOfRunAction** function. If more photons are simulated than can fit in the GPU RAM the execution of a GPU call should be moved to **EndOfEventAction** together with retriving the hits. +Here setStackPhotons defines **whether G4 will propagate optical photons or not**. In production Opticks (GPU) takes care of the optical photon propagation. Additionally the user has to define the **starting position**, **momentum** etc of the primary particles define in the **GeneratePrimaries** function in ``src/g4appmt.h```. The hits of the optical photons are returned in the **EndOfRunAction** function. If more photons are simulated than can fit in the GPU RAM the execution of a GPU call should be moved to **EndOfEventAction** together with retriving the hits. ### Loading in geometry into EIC-Opticks +EIC-Opticks can import geometries with GDML format automatically. There are about 10 primitives supported now, eg. G4Box. G4Trd or G4Trap are not supported yet, we are working on them. ```src/simg4oxmt``` takes GDML files through arguments, eg. ```src/simg4oxmt -g mygdml.gdml```. + +The GDML must define all optical properties of surfaces of materials including: +- Efficiency (used by EIC-Opticks to specify detection efficiency and assign sensitive surfaces) +- Refractive index +- Group velocity +- Reflectivity +- Etc. + + ## Performance studies ``` From 5e4b12b1efb1c75686a78123d3dcd36987431e45 Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Fri, 12 Dec 2025 15:07:43 -0500 Subject: [PATCH 22/35] Change default GDML path raindrop in run_performance.py --- optiphy/tools/run_performance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optiphy/tools/run_performance.py b/optiphy/tools/run_performance.py index 5551d1035c..c73fb1862c 100755 --- a/optiphy/tools/run_performance.py +++ b/optiphy/tools/run_performance.py @@ -47,7 +47,7 @@ def main(): # If gdml not provided, use default path relative to OPTICKS_HOME if args.gdml is None: - gdml_path = opticks_home / 'tests/geom/pfrich_min_FINAL.gdml' + gdml_path = opticks_home / 'tests/geom/opticks_raindrop.gdml' else: # User-provided path is used as-is (relative to current directory or absolute) gdml_path = args.gdml From 13729f3e470e86dba2677f7182bba37c56a79b7f Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Fri, 12 Dec 2025 15:35:01 -0500 Subject: [PATCH 23/35] Enhance README with performance studies details Added explanation for performance studies comparing EIC-Opticks and G4 simulations. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2f292faf60..145971d12c 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,8 @@ The GDML must define all optical properties of surfaces of materials including: ## Performance studies +In order to quantify the speed-up achieved by EIC-Opticks compared to G4 we provide a python code that runs the same G4 simulation with and without tracking optical photons in G4. The difference of the runs will yield the time required to simulate photons. Meanwhile the same photons are simulated on GPU with EIC-Opticks and the simulation time is saved. + ``` mkdir -p /tmp/out/dev mkdir -p /tmp/out/rel From 96e1e8d3e3188e4ebf377091b2a835b93e1ca8ea Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Mon, 15 Dec 2025 14:21:27 -0500 Subject: [PATCH 24/35] docs(README): fix formatting/line-wrapping --- README.md | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 145971d12c..ae63508f38 100644 --- a/README.md +++ b/README.md @@ -125,12 +125,17 @@ Process](https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/ ... ``` + + ## User/developer defined inputs ### Defining primary particles -There are certain user defined inputs that the user/developer has to define. In the ```src/simg4oxmt``` example that imports ```src/g4appmt.h``` we provide a working example with a simple geometry. The User/developer has to change the following details: -**Number of primary particles** to simulate in a macro file and the **number of G4 threads**. For example: +There are certain user defined inputs that the user/developer has to define. In +the ```src/simg4oxmt``` example that imports ```src/g4appmt.h``` we provide +a working example with a simple geometry. The User/developer has to change the +following details: **Number of primary particles** to simulate in a macro file +and the **number of G4 threads**. For example: ``` /run/numberOfThreads {threads} @@ -140,14 +145,25 @@ There are certain user defined inputs that the user/developer has to define. In /run/beamOn 500 ``` -Here setStackPhotons defines **whether G4 will propagate optical photons or not**. In production Opticks (GPU) takes care of the optical photon propagation. Additionally the user has to define the **starting position**, **momentum** etc of the primary particles define in the **GeneratePrimaries** function in ``src/g4appmt.h```. The hits of the optical photons are returned in the **EndOfRunAction** function. If more photons are simulated than can fit in the GPU RAM the execution of a GPU call should be moved to **EndOfEventAction** together with retriving the hits. +Here setStackPhotons defines **whether G4 will propagate optical photons or +not**. In production Opticks (GPU) takes care of the optical photon propagation. +Additionally the user has to define the **starting position**, **momentum** etc +of the primary particles define in the **GeneratePrimaries** function in +``src/g4appmt.h```. The hits of the optical photons are returned in the +**EndOfRunAction** function. If more photons are simulated than can fit in the +GPU RAM the execution of a GPU call should be moved to **EndOfEventAction** +together with retriving the hits. ### Loading in geometry into EIC-Opticks -EIC-Opticks can import geometries with GDML format automatically. There are about 10 primitives supported now, eg. G4Box. G4Trd or G4Trap are not supported yet, we are working on them. ```src/simg4oxmt``` takes GDML files through arguments, eg. ```src/simg4oxmt -g mygdml.gdml```. +EIC-Opticks can import geometries with GDML format automatically. There are +about 10 primitives supported now, eg. G4Box. G4Trd or G4Trap are not supported +yet, we are working on them. ```src/simg4oxmt``` takes GDML files through +arguments, eg. ```src/simg4oxmt -g mygdml.gdml```. The GDML must define all optical properties of surfaces of materials including: -- Efficiency (used by EIC-Opticks to specify detection efficiency and assign sensitive surfaces) +- Efficiency (used by EIC-Opticks to specify detection efficiency and assign + sensitive surfaces) - Refractive index - Group velocity - Reflectivity @@ -156,7 +172,11 @@ The GDML must define all optical properties of surfaces of materials including: ## Performance studies -In order to quantify the speed-up achieved by EIC-Opticks compared to G4 we provide a python code that runs the same G4 simulation with and without tracking optical photons in G4. The difference of the runs will yield the time required to simulate photons. Meanwhile the same photons are simulated on GPU with EIC-Opticks and the simulation time is saved. +In order to quantify the speed-up achieved by EIC-Opticks compared to G4 we +provide a python code that runs the same G4 simulation with and without tracking +optical photons in G4. The difference of the runs will yield the time required +to simulate photons. Meanwhile the same photons are simulated on GPU with +EIC-Opticks and the simulation time is saved. ``` mkdir -p /tmp/out/dev From ff34c93ee9dd0ebf2709828600c6ea65b80d1c50 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Mon, 15 Dec 2025 14:31:45 -0500 Subject: [PATCH 25/35] style(opticks_raindrop.gdml): whitespace and indentation cleanup only --- tests/geom/opticks_raindrop.gdml | 39 ++++++++++---------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/tests/geom/opticks_raindrop.gdml b/tests/geom/opticks_raindrop.gdml index b40f3fc47b..79ad74ee8d 100644 --- a/tests/geom/opticks_raindrop.gdml +++ b/tests/geom/opticks_raindrop.gdml @@ -1,23 +1,16 @@ - + - - - - - - - - - + + + + + @@ -50,8 +43,6 @@ - - @@ -122,8 +113,8 @@ - - + + @@ -131,7 +122,7 @@ - + @@ -151,8 +142,8 @@ - - + + @@ -177,20 +168,14 @@ - - - - - - - + From 04f4d4a2c4741d8dea6b68b411e066c2503c64c2 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Wed, 30 Jul 2025 10:26:22 -0400 Subject: [PATCH 26/35] workaround for incorrect ray intersections with open cones --- CSG/csg_intersect_leaf_newcone.h | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/CSG/csg_intersect_leaf_newcone.h b/CSG/csg_intersect_leaf_newcone.h index 6c335b2438..980172508f 100644 --- a/CSG/csg_intersect_leaf_newcone.h +++ b/CSG/csg_intersect_leaf_newcone.h @@ -98,24 +98,13 @@ void intersect_leaf_newcone( bool& valid_isect, float4& isect, const quad& q0, c t_far = z_far > z1 && z_far < z2 && t_far > t_min ? t_far : RT_DEFAULT_MAX ; const float4 roots = make_float4( t_near, t_far, t_cap1, t_cap2 ); - const float t_cand = fminf(roots) ; - - valid_isect = t_cand > t_min && t_cand < RT_DEFAULT_MAX ; - if(valid_isect) - { - if( t_cand == t_cap1 || t_cand == t_cap2 ) - { - isect.x = 0.f ; - isect.y = 0.f ; - isect.z = t_cand == t_cap2 ? 1.f : -1.f ; - } - else - { - float3 n = normalize(make_float3( o.x+t_cand*d.x, o.y+t_cand*d.y, (z0-(o.z+t_cand*d.z))*tth2 )) ; - isect.x = n.x ; - isect.y = n.y ; - isect.z = n.z ; - } - isect.w = t_cand ; - } + const float t_cand = fminf(roots) ; + + float3 intersection_point = make_float3(o.x + t_cand * d.x, o.y + t_cand * d.y, o.z + t_cand * d.z); + float3 n = normalize(make_float3(intersection_point.x, intersection_point.y, (z0 - intersection_point.z) * tth2)); + + isect.x = n.x; + isect.y = n.y; + isect.z = n.z; + isect.w = t_cand; } From 8be507402f7d73eb46cf06c5f66a514f189e6db3 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Mon, 15 Dec 2025 16:32:56 -0500 Subject: [PATCH 27/35] Revert "Move particle gun to create photons in raindrop geom" This reverts commit f3d977e9cbf7414e34b3cc2ece81d90b6ae76aad. --- src/g4appmt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g4appmt.h b/src/g4appmt.h index e83936db72..88258fcb45 100644 --- a/src/g4appmt.h +++ b/src/g4appmt.h @@ -295,7 +295,7 @@ struct PrimaryGenerator : G4VUserPrimaryGeneratorAction void GeneratePrimaries(G4Event *event) override { - G4ThreeVector position_mm(0.0 * m, 0.0 * m, 0.0 * m); + G4ThreeVector position_mm(-0.4 * m, -0.3 * m, -0.3 * m); G4double time_ns = 0; G4ThreeVector direction(0, 0.2, 0.8); G4double wavelength_nm = 0.1; From 6e16acbca713b631bc01fe6a1f9a3d8356190fb6 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Tue, 16 Dec 2025 11:52:08 -0500 Subject: [PATCH 28/35] Revert "workaround for incorrect ray intersections with open cones" This reverts commit 04f4d4a2c4741d8dea6b68b411e066c2503c64c2. --- CSG/csg_intersect_leaf_newcone.h | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/CSG/csg_intersect_leaf_newcone.h b/CSG/csg_intersect_leaf_newcone.h index 980172508f..6c335b2438 100644 --- a/CSG/csg_intersect_leaf_newcone.h +++ b/CSG/csg_intersect_leaf_newcone.h @@ -98,13 +98,24 @@ void intersect_leaf_newcone( bool& valid_isect, float4& isect, const quad& q0, c t_far = z_far > z1 && z_far < z2 && t_far > t_min ? t_far : RT_DEFAULT_MAX ; const float4 roots = make_float4( t_near, t_far, t_cap1, t_cap2 ); - const float t_cand = fminf(roots) ; - - float3 intersection_point = make_float3(o.x + t_cand * d.x, o.y + t_cand * d.y, o.z + t_cand * d.z); - float3 n = normalize(make_float3(intersection_point.x, intersection_point.y, (z0 - intersection_point.z) * tth2)); - - isect.x = n.x; - isect.y = n.y; - isect.z = n.z; - isect.w = t_cand; + const float t_cand = fminf(roots) ; + + valid_isect = t_cand > t_min && t_cand < RT_DEFAULT_MAX ; + if(valid_isect) + { + if( t_cand == t_cap1 || t_cand == t_cap2 ) + { + isect.x = 0.f ; + isect.y = 0.f ; + isect.z = t_cand == t_cap2 ? 1.f : -1.f ; + } + else + { + float3 n = normalize(make_float3( o.x+t_cand*d.x, o.y+t_cand*d.y, (z0-(o.z+t_cand*d.z))*tth2 )) ; + isect.x = n.x ; + isect.y = n.y ; + isect.z = n.z ; + } + isect.w = t_cand ; + } } From 89369073c81008bc577cea39544185115573e08d Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Tue, 16 Dec 2025 11:56:03 -0500 Subject: [PATCH 29/35] workaround for incorrect ray intersections with open cones - 2 --- CSG/csg_intersect_leaf_newcone.h | 34 +++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/CSG/csg_intersect_leaf_newcone.h b/CSG/csg_intersect_leaf_newcone.h index 6c335b2438..01a98455af 100644 --- a/CSG/csg_intersect_leaf_newcone.h +++ b/CSG/csg_intersect_leaf_newcone.h @@ -100,22 +100,24 @@ void intersect_leaf_newcone( bool& valid_isect, float4& isect, const quad& q0, c const float4 roots = make_float4( t_near, t_far, t_cap1, t_cap2 ); const float t_cand = fminf(roots) ; - valid_isect = t_cand > t_min && t_cand < RT_DEFAULT_MAX ; - if(valid_isect) + valid_isect = (t_cand > t_min && t_cand < RT_DEFAULT_MAX); + if (valid_isect) { - if( t_cand == t_cap1 || t_cand == t_cap2 ) - { - isect.x = 0.f ; - isect.y = 0.f ; - isect.z = t_cand == t_cap2 ? 1.f : -1.f ; - } - else - { - float3 n = normalize(make_float3( o.x+t_cand*d.x, o.y+t_cand*d.y, (z0-(o.z+t_cand*d.z))*tth2 )) ; - isect.x = n.x ; - isect.y = n.y ; - isect.z = n.z ; - } - isect.w = t_cand ; + float3 intersection_point = make_float3( + o.x + t_cand * d.x, + o.y + t_cand * d.y, + o.z + t_cand * d.z + ); + float3 n = normalize(make_float3( + intersection_point.x, + intersection_point.y, + (z0 - intersection_point.z)*tth2 + )); + + + isect.x = n.x; + isect.y = n.y; + isect.z = n.z; + isect.w = t_cand; } } From a4bb33f9ada7fc511added301af8ff6ea2072800 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Thu, 18 Dec 2025 11:14:44 -0500 Subject: [PATCH 30/35] Revert "workaround for incorrect ray intersections with open cones - 2" This reverts commit 89369073c81008bc577cea39544185115573e08d. --- CSG/csg_intersect_leaf_newcone.h | 34 +++++++++++++++----------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/CSG/csg_intersect_leaf_newcone.h b/CSG/csg_intersect_leaf_newcone.h index 01a98455af..6c335b2438 100644 --- a/CSG/csg_intersect_leaf_newcone.h +++ b/CSG/csg_intersect_leaf_newcone.h @@ -100,24 +100,22 @@ void intersect_leaf_newcone( bool& valid_isect, float4& isect, const quad& q0, c const float4 roots = make_float4( t_near, t_far, t_cap1, t_cap2 ); const float t_cand = fminf(roots) ; - valid_isect = (t_cand > t_min && t_cand < RT_DEFAULT_MAX); - if (valid_isect) + valid_isect = t_cand > t_min && t_cand < RT_DEFAULT_MAX ; + if(valid_isect) { - float3 intersection_point = make_float3( - o.x + t_cand * d.x, - o.y + t_cand * d.y, - o.z + t_cand * d.z - ); - float3 n = normalize(make_float3( - intersection_point.x, - intersection_point.y, - (z0 - intersection_point.z)*tth2 - )); - - - isect.x = n.x; - isect.y = n.y; - isect.z = n.z; - isect.w = t_cand; + if( t_cand == t_cap1 || t_cand == t_cap2 ) + { + isect.x = 0.f ; + isect.y = 0.f ; + isect.z = t_cand == t_cap2 ? 1.f : -1.f ; + } + else + { + float3 n = normalize(make_float3( o.x+t_cand*d.x, o.y+t_cand*d.y, (z0-(o.z+t_cand*d.z))*tth2 )) ; + isect.x = n.x ; + isect.y = n.y ; + isect.z = n.z ; + } + isect.w = t_cand ; } } From 683cf3a67ddd6a13425afae2722e2a8e456e7a23 Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Tue, 23 Dec 2025 16:54:45 -0500 Subject: [PATCH 31/35] Update position in GeneratePrimaries method Move gun so the opticks_raindrop.gdml has photons created --- src/g4appmt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g4appmt.h b/src/g4appmt.h index 88258fcb45..e83936db72 100644 --- a/src/g4appmt.h +++ b/src/g4appmt.h @@ -295,7 +295,7 @@ struct PrimaryGenerator : G4VUserPrimaryGeneratorAction void GeneratePrimaries(G4Event *event) override { - G4ThreeVector position_mm(-0.4 * m, -0.3 * m, -0.3 * m); + G4ThreeVector position_mm(0.0 * m, 0.0 * m, 0.0 * m); G4double time_ns = 0; G4ThreeVector direction(0, 0.2, 0.8); G4double wavelength_nm = 0.1; From 4101e08ed73b0802b42bbd3b7fe67d199d1a5101 Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Tue, 6 Jan 2026 17:19:52 -0500 Subject: [PATCH 32/35] Reorder physvolref elements in opticks_raindrop.gdml Fix ordering so EIC-Opticks hits match up --- tests/geom/opticks_raindrop.gdml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/geom/opticks_raindrop.gdml b/tests/geom/opticks_raindrop.gdml index 79ad74ee8d..b42151b253 100644 --- a/tests/geom/opticks_raindrop.gdml +++ b/tests/geom/opticks_raindrop.gdml @@ -169,8 +169,8 @@ - - + + From 7fd2968dd07372cb1567b5b396f5b942f8b09094 Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Tue, 6 Jan 2026 17:23:18 -0500 Subject: [PATCH 33/35] Log number of PhotonHits at end of event --- src/g4appmt.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/g4appmt.h b/src/g4appmt.h index e83936db72..b8d3d62199 100644 --- a/src/g4appmt.h +++ b/src/g4appmt.h @@ -180,6 +180,7 @@ struct PhotonSD : public G4VSensitiveDetector void EndOfEvent(G4HCofThisEvent *) override { G4int NbHits = fPhotonHitsCollection->entries(); + G4cout << "PhotonSD::EndOfEvent Number of PhotonHits: " << NbHits << G4endl; } void AddOpticksHits() From 1715f2e73d3396e856f5a6c356a7bb77f88bf880 Mon Sep 17 00:00:00 2001 From: ggalgoczi Date: Wed, 7 Jan 2026 11:04:35 -0500 Subject: [PATCH 34/35] Change kinetic energy of particle from 5 GeV to 0.005 GeV --- src/g4appmt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g4appmt.h b/src/g4appmt.h index b8d3d62199..f5ccfc35d7 100644 --- a/src/g4appmt.h +++ b/src/g4appmt.h @@ -303,7 +303,7 @@ struct PrimaryGenerator : G4VUserPrimaryGeneratorAction G4PrimaryVertex *vertex = new G4PrimaryVertex(position_mm, time_ns); G4PrimaryParticle *particle = new G4PrimaryParticle(G4Electron::Definition()); - particle->SetKineticEnergy(5 * GeV); + particle->SetKineticEnergy(0.005 * GeV); particle->SetMomentumDirection(direction); vertex->SetPrimary(particle); event->AddPrimaryVertex(vertex); From b327ff226a122e0aa4fc637a5a9bd7ee27025a26 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Wed, 7 Jan 2026 15:08:38 -0500 Subject: [PATCH 35/35] Revert "Change kinetic energy of particle from 5 GeV to 0.005 GeV" This reverts commit 1715f2e73d3396e856f5a6c356a7bb77f88bf880. --- src/g4appmt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g4appmt.h b/src/g4appmt.h index f5ccfc35d7..b8d3d62199 100644 --- a/src/g4appmt.h +++ b/src/g4appmt.h @@ -303,7 +303,7 @@ struct PrimaryGenerator : G4VUserPrimaryGeneratorAction G4PrimaryVertex *vertex = new G4PrimaryVertex(position_mm, time_ns); G4PrimaryParticle *particle = new G4PrimaryParticle(G4Electron::Definition()); - particle->SetKineticEnergy(0.005 * GeV); + particle->SetKineticEnergy(5 * GeV); particle->SetMomentumDirection(direction); vertex->SetPrimary(particle); event->AddPrimaryVertex(vertex);