#include "WireTrackingManager.hh" #include "ClassPW.h" #include "G4Event.hh" #include "G4RunManager.hh" #include "G4Step.hh" #include "G4StepPoint.hh" #include "G4Track.hh" #include "G4SystemOfUnits.hh" #include #include namespace { constexpr double kAnodeRadius = 37.0 * mm; constexpr double kCathodeRadius = 43.0 * mm; bool CrossesRadius(double preR, double postR, double targetR) { return (preR - targetR) * (postR - targetR) <= 0.0 && std::abs(postR - preR) > 1e-12; } TVector3 ToTVector3(const G4ThreeVector& value) { return TVector3(value.x(), value.y(), value.z()); } bool SolveRadiusIntersection(const G4ThreeVector& origin, const G4ThreeVector& direction, double radius, double& distance) { const double a = direction.x() * direction.x() + direction.y() * direction.y(); if (a <= 0.0) { return false; } const double b = 2.0 * (origin.x() * direction.x() + origin.y() * direction.y()); const double c = origin.x() * origin.x() + origin.y() * origin.y() - radius * radius; const double discriminant = b * b - 4.0 * a * c; if (discriminant < 0.0) { return false; } const double sqrtDiscriminant = std::sqrt(discriminant); const double t1 = (-b - sqrtDiscriminant) / (2.0 * a); const double t2 = (-b + sqrtDiscriminant) / (2.0 * a); distance = -1.0; if (t1 >= 0.0 && t2 >= 0.0) { distance = std::min(t1, t2); } else if (t1 >= 0.0) { distance = t1; } else if (t2 >= 0.0) { distance = t2; } return distance >= 0.0; } } WireTrackingManager& WireTrackingManager::Instance() { static WireTrackingManager instance; return instance; } WireTrackingManager::WireTrackingManager() : fCurrentEventId(-1) {} WireTrackingManager::~WireTrackingManager() {} void WireTrackingManager::EnsureEvent(int eventId) { if (eventId != fCurrentEventId) { fCurrentEventId = eventId; fTrackInfo.clear(); } } void WireTrackingManager::UpdateForStep(const G4Step* step) { if (step == nullptr) { return; } const G4Track* track = step->GetTrack(); if (track == nullptr) { return; } const G4Event* event = G4RunManager::GetRunManager()->GetCurrentEvent(); if (event == nullptr) { return; } EnsureEvent(event->GetEventID()); const G4StepPoint* prePoint = step->GetPreStepPoint(); const G4StepPoint* postPoint = step->GetPostStepPoint(); if (prePoint == nullptr || postPoint == nullptr) { return; } if (prePoint->GetPhysicalVolume() == nullptr || prePoint->GetPhysicalVolume()->GetName() != "Target") { return; } if (track->GetDefinition()->GetParticleType() == "nucleus") { return; } const G4ThreeVector& prePos = prePoint->GetPosition(); const G4ThreeVector& postPos = postPoint->GetPosition(); const double preR = prePos.perp(); const double postR = postPos.perp(); if (!CrossesRadius(preR, postR, kAnodeRadius) && !CrossesRadius(preR, postR, kCathodeRadius)) { return; } PW pw; pw.ConstructGeo(); pw.FindWireID(ToTVector3(track->GetVertexPosition()), ToTVector3(track->GetMomentumDirection()), false); auto& info = fTrackInfo[track->GetTrackID()]; auto interpolateEnergy = [&](double targetR) { const double fraction = (targetR - preR) / (postR - preR); return prePoint->GetKineticEnergy() + fraction * (postPoint->GetKineticEnergy() - prePoint->GetKineticEnergy()); }; if (!info.sawAnode && CrossesRadius(preR, postR, kAnodeRadius)) { info.sawAnode = true; info.anodeId = pw.GetNearestID().first; info.anodeEnergy = interpolateEnergy(kAnodeRadius); } if (!info.sawCathode && CrossesRadius(preR, postR, kCathodeRadius)) { info.sawCathode = true; info.cathodeId = pw.GetNearestID().second; info.cathodeEnergy = interpolateEnergy(kCathodeRadius); } if (info.sawAnode && info.sawCathode) { info.deltaE = std::abs(info.anodeEnergy - info.cathodeEnergy); } } bool WireTrackingManager::GetInfo(int eventId, int trackId, WireCrossingInfo& info) { EnsureEvent(eventId); auto iterator = fTrackInfo.find(trackId); if (iterator == fTrackInfo.end()) { return false; } info = iterator->second; return true; } WireCrossingInfo WireTrackingManager::InferFromTrackGeometry(const G4Track* track, const G4StepPoint* hitPoint) { WireCrossingInfo info; if (track == nullptr || hitPoint == nullptr) { return info; } const G4ThreeVector origin = track->GetVertexPosition(); G4ThreeVector direction = hitPoint->GetPosition() - origin; const double totalPath = direction.mag(); if (totalPath <= 0.0) { return info; } direction = direction.unit(); PW pw; pw.ConstructGeo(); pw.FindWireID(ToTVector3(origin), ToTVector3(direction), false); info.anodeId = pw.GetNearestID().first; info.cathodeId = pw.GetNearestID().second; const double vertexEnergy = track->GetVertexKineticEnergy(); const double hitEnergy = hitPoint->GetKineticEnergy(); double anodeDistance = 0.0; double cathodeDistance = 0.0; if (SolveRadiusIntersection(origin, direction, 37.0 * mm, anodeDistance)) { const double fraction = std::clamp(anodeDistance / totalPath, 0.0, 1.0); info.anodeEnergy = vertexEnergy + fraction * (hitEnergy - vertexEnergy); info.sawAnode = true; } if (SolveRadiusIntersection(origin, direction, 43.0 * mm, cathodeDistance)) { const double fraction = std::clamp(cathodeDistance / totalPath, 0.0, 1.0); info.cathodeEnergy = vertexEnergy + fraction * (hitEnergy - vertexEnergy); info.sawCathode = true; } if (info.sawAnode && info.sawCathode) { info.deltaE = std::abs(info.anodeEnergy - info.cathodeEnergy); } return info; }