File indexing completed on 2026-05-15 07:41:49
0001 #include "OpticsEvent.hh"
0002
0003 #include <DD4hep/InstanceCount.h>
0004 #include <DDG4/Factories.h>
0005 #include <DDG4/Geant4Context.h>
0006 #include <DDG4/Geant4Data.h>
0007 #include <DDG4/Geant4HitCollection.h>
0008 #include <DDG4/Geant4SensDetAction.h>
0009
0010 #include <G4Event.hh>
0011
0012 #include <chrono>
0013 #include <cuda_runtime.h>
0014
0015 #include <G4CXOpticks.hh>
0016 #include <NP.hh>
0017 #include <NPFold.h>
0018 #include <SComp.h>
0019 #include <SEvt.hh>
0020 #include <map>
0021 #include <sphoton.h>
0022
0023 namespace ddeicopticks
0024 {
0025
0026 OpticsEvent::OpticsEvent(dd4hep::sim::Geant4Context *ctxt, std::string const &name)
0027 : dd4hep::sim::Geant4EventAction(ctxt, name)
0028 {
0029 dd4hep::InstanceCount::increment(this);
0030 declareProperty("Verbose", verbose_);
0031 declareProperty("PhotonThreshold", photon_threshold_);
0032 }
0033
0034
0035 OpticsEvent::~OpticsEvent()
0036 {
0037 dd4hep::InstanceCount::decrement(this);
0038 }
0039
0040
0041 void OpticsEvent::begin(G4Event const *event)
0042 {
0043 int eventID = event->GetEventID();
0044
0045 if (verbose_ > 0)
0046 info("OpticsEvent::begin -- event #%d", eventID);
0047
0048
0049
0050
0051 if (!batch_begun_)
0052 {
0053 SEvt::CreateOrReuse_EGPU();
0054 SEvt *sev = SEvt::Get_EGPU();
0055 if (sev)
0056 sev->beginOfEvent(eventID);
0057 batch_begun_ = true;
0058 }
0059 }
0060
0061
0062 void OpticsEvent::end(G4Event const *event)
0063 {
0064 int eventID = event->GetEventID();
0065
0066 G4CXOpticks *gx = G4CXOpticks::Get();
0067 if (!gx)
0068 {
0069 error("OpticsEvent::end -- G4CXOpticks not initialized");
0070 return;
0071 }
0072
0073 SEvt *sev = SEvt::Get_EGPU();
0074 if (!sev)
0075 {
0076 error("OpticsEvent::end -- no EGPU SEvt instance");
0077 return;
0078 }
0079
0080 int64_t num_genstep = sev->getNumGenstepFromGenstep();
0081 int64_t num_photon = sev->getNumPhotonFromGenstep();
0082
0083 if (verbose_ > 0 || num_genstep > 0)
0084 {
0085 info("Event #%d: %lld gensteps, %lld photons accumulated", eventID, static_cast<long long>(num_genstep),
0086 static_cast<long long>(num_photon));
0087 }
0088
0089
0090 if (photon_threshold_ > 0 && num_photon < photon_threshold_)
0091 return;
0092
0093 if (num_genstep > 0)
0094 {
0095 auto sim_t0 = std::chrono::high_resolution_clock::now();
0096 gx->simulate(eventID, false);
0097 cudaDeviceSynchronize();
0098 auto sim_t1 = std::chrono::high_resolution_clock::now();
0099 double simulate_ms = std::chrono::duration<double, std::milli>(sim_t1 - sim_t0).count();
0100
0101 unsigned num_hit = sev->getNumHit();
0102 info("OPTICKS_GPU_TIME event=%d ms=%.3f photons=%lld hits=%u", eventID, simulate_ms,
0103 static_cast<long long>(num_photon), num_hit);
0104
0105
0106 if (verbose_ >= 2)
0107 {
0108 NPFold *tf = sev->topfold;
0109 const NP *photon = tf ? tf->get("photon") : nullptr;
0110 if (photon)
0111 {
0112 int np = photon->shape[0];
0113 const sphoton *pp = (const sphoton *)photon->cvalues<float>();
0114 std::map<unsigned, int> flag_counts;
0115 std::map<unsigned, int> boundary_counts;
0116 for (int i = 0; i < np; i++)
0117 {
0118 flag_counts[pp[i].flagmask]++;
0119 boundary_counts[pp[i].boundary()]++;
0120 }
0121 for (auto &[f, c] : flag_counts)
0122 info(" flagmask 0x%08x : %d photons", f, c);
0123 for (auto &[b, c] : boundary_counts)
0124 info(" boundary %u : %d photons", b, c);
0125 }
0126 }
0127
0128
0129
0130 if (photon_threshold_ == 0 && num_hit > 0)
0131 injectHits(event, sev, num_hit);
0132
0133 sev->endOfEvent(eventID);
0134 gx->reset(eventID);
0135 }
0136 else
0137 {
0138 if (verbose_ > 0)
0139 info("Event #%d: no gensteps, skipping GPU simulation", eventID);
0140 if (photon_threshold_ == 0)
0141 sev->endOfEvent(eventID);
0142 }
0143
0144 batch_begun_ = false;
0145 }
0146
0147
0148 void OpticsEvent::injectHits(G4Event const *event, SEvt *sev, unsigned num_hit)
0149 {
0150 using dd4hep::sim::Geant4HitCollection;
0151 using dd4hep::sim::Geant4SensDetSequences;
0152 using dd4hep::sim::Geant4Tracker;
0153
0154 int eventID = event->GetEventID();
0155
0156 Geant4SensDetSequences &sens = context()->sensitiveActions();
0157 auto const &seqs = sens.sequences();
0158
0159 if (seqs.empty())
0160 {
0161 warning("Event #%d: no sensitive detectors registered -- "
0162 "call setupTracker() in steering script",
0163 eventID);
0164 return;
0165 }
0166
0167 if (seqs.size() > 1)
0168 {
0169 warning("Event #%d: %zu sensitive detectors registered -- "
0170 "hit routing by sensor identity not yet implemented, "
0171 "injecting into first SD only",
0172 eventID, seqs.size());
0173 }
0174
0175
0176
0177
0178 for (auto const &[det_name, seq] : seqs)
0179 {
0180 Geant4HitCollection *coll = seq->collection(0);
0181 if (!coll)
0182 continue;
0183
0184 for (unsigned i = 0; i < num_hit; i++)
0185 {
0186 sphoton ph;
0187 sev->getHit(ph, i);
0188 coll->add(createTrackerHit(ph));
0189 }
0190
0191 info("Event #%d: injected %u hits into '%s' collection", eventID, num_hit, det_name.c_str());
0192 break;
0193 }
0194 }
0195
0196
0197 dd4hep::sim::Geant4Tracker::Hit *OpticsEvent::createTrackerHit(sphoton const &ph)
0198 {
0199 using dd4hep::sim::Geant4Tracker;
0200
0201 auto *hit = new Geant4Tracker::Hit();
0202
0203 hit->position = {ph.pos.x, ph.pos.y, ph.pos.z};
0204 hit->momentum = {ph.mom.x, ph.mom.y, ph.mom.z};
0205 hit->length = ph.wavelength;
0206 hit->energyDeposit = 0;
0207 hit->cellID = ph.pmtid();
0208
0209 hit->truth.trackID = ph.index;
0210 hit->truth.pdgID = 0;
0211 hit->truth.deposit = 0;
0212 hit->truth.time = ph.time;
0213 hit->truth.length = ph.wavelength;
0214 hit->truth.x = ph.pos.x;
0215 hit->truth.y = ph.pos.y;
0216 hit->truth.z = ph.pos.z;
0217 hit->truth.px = ph.mom.x;
0218 hit->truth.py = ph.mom.y;
0219 hit->truth.pz = ph.mom.z;
0220
0221 return hit;
0222 }
0223
0224
0225 }
0226
0227 DECLARE_GEANT4ACTION_NS(ddeicopticks, OpticsEvent)