17#include "CLHEP/Random/RandFlat.h"
18#include "CLHEP/Random/RandomEngine.h"
28#include "Acts/ActsVersion.hpp"
29#include <Acts/Utilities/StringHelpers.hpp>
32#include "G4ParticleTable.hh"
33#include "G4ParticleDefinition.hh"
35#include "G4LogicalVolume.hh"
37#include "G4VPhysicalVolume.hh"
39using namespace Acts::UnitLiterals;
45 ATH_MSG_INFO(
"ActsFatrasG4Tool updated with ACTS version: v"
46 << Acts::VersionMajor <<
"." << Acts::VersionMinor <<
"."
47 << Acts::VersionPatch <<
" [" << Acts::CommitHash.value_or(
"unknown hash") <<
"]");
60 if (
m_rngSvc.retrieve().isFailure()) {
62 return StatusCode::FAILURE;
66 if (!m_randomEngine) {
68 return StatusCode::FAILURE;
78 return StatusCode::SUCCESS;
87 return StatusCode::SUCCESS;
92 m_bField = std::make_shared<ATLASMagneticFieldWrapper>();
108 m_simulator = std::make_unique<Simulation>(std::move(simCharged), std::move(simNeutral));
122 m_simulator->charged.interactions = ActsFatras::makeStandardChargedElectroMagneticInteractions(
m_interact_minPt * Acts::UnitConstants::MeV);
127 ATH_MSG_INFO(
"Fatras simulator configured successfully.");
129 return StatusCode::SUCCESS;
133 ATH_MSG_INFO(
"ActsFatrasG4Tool::initializePhysics() called");
136 return StatusCode::SUCCESS;
144 int barcode = helper.GetBarcode();
146 barcode = track.GetTrackID();
147 ATH_MSG_DEBUG(
"Track barcode is 0, using TrackID " << barcode
148 <<
" PDG=" << track.GetDefinition()->GetPDGEncoding()
149 <<
" Ekin=" << track.GetKineticEnergy()
153 ActsFatras::Barcode fatrasBarcode = ActsFatras::Barcode().withVertexPrimary(0).withParticle(barcode);
154 auto fatrasPDG =
static_cast<Acts::PdgParticle
>(track.GetDefinition()->GetPDGEncoding());
155 double fatrasCharge = track.GetDefinition()->GetPDGCharge();
156 double fatrasMass = track.GetDefinition()->GetPDGMass();
158 ActsFatras::Particle particle(fatrasBarcode, fatrasPDG, fatrasCharge, fatrasMass);
161 particle.setDirection(track.GetMomentum().x(), track.GetMomentum().y(), track.GetMomentum().z());
164 double momentumMeV = track.GetMomentum().mag();
165 particle.setAbsoluteMomentum(momentumMeV);
168 G4ThreeVector momDir = track.GetMomentumDirection();
169 particle.setDirection(momDir.x(), momDir.y(), momDir.z());
172 particle.setPosition4(Acts::Vector4(
173 track.GetPosition().x(),
174 track.GetPosition().y(),
175 track.GetPosition().z(),
176 track.GetGlobalTime()));
179 <<
" p(MeV)=" << momentumMeV
180 <<
" mass(MeV)=" << fatrasMass);
188 ATH_MSG_ERROR(
"Fatras simulation failed: " << result.error().message());
194 const std::vector<ActsFatras::Particle>& simulatedFinalParticles,
195 G4FastStep& fastStep)
197 if (simulatedFinalParticles.empty()){
return StatusCode::SUCCESS;}
199 const auto& primary = simulatedFinalParticles.front();
201 if (!primary.isAlive()) {
202 fastStep.KillPrimaryTrack();
203 return StatusCode::SUCCESS;
206 double p = primary.absoluteMomentum();
207 double m = primary.mass();
209 double energy = std::sqrt(p*p + m*m);
210 double kinetic = (energy - m) * CLHEP::MeV;
212 fastStep.ProposePrimaryTrackFinalKineticEnergy(kinetic);
214 fastStep.ProposePrimaryTrackFinalMomentumDirection(
215 G4ThreeVector(primary.direction().x(), primary.direction().y(), primary.direction().z()).unit()
218 fastStep.ProposePrimaryTrackFinalPosition(
219 G4ThreeVector(primary.position().x(), primary.position().y(), primary.position().z())
221 return StatusCode::SUCCESS;
225 const std::vector<ActsFatras::Particle>& simulatedFinal,
227 G4FastStep& fastStep)
229 for (
size_t i = 1; i < simulatedFinal.size(); ++i) {
230 const auto& sec = simulatedFinal[i];
232 G4ParticleDefinition* def = G4ParticleTable::GetParticleTable()->FindParticle(sec.pdg());
236 G4ThreeVector dir(sec.direction().x(), sec.direction().y(), sec.direction().z());
238 double momentumMeV = sec.absoluteMomentum();
240 G4DynamicParticle dyn(def, dir, momentumMeV);
241 fastStep.CreateSecondaryTrack(
243 G4ThreeVector(sec.position().x(),
250 return StatusCode::SUCCESS;
255 const G4Track& track = *fastTrack.GetPrimaryTrack();
257 ATH_MSG_DEBUG(
"=========== FATRAS REGION DEBUG ===========");
259 const G4VSolid* envelope = fastTrack.GetEnvelopeSolid();
260 if (envelope) {
ATH_MSG_DEBUG(
"FastSim envelope solid: " << envelope->GetName());}
264 const G4VPhysicalVolume* pv = track.GetVolume();
267 const G4LogicalVolume* lv = pv->GetLogicalVolume();
269 const G4Region* region = lv->GetRegion();
270 if (region) {
ATH_MSG_DEBUG(
"Region: " << region->GetName());}
277 const G4ThreeVector& pos = track.GetPosition();
278 double r = std::sqrt(pos.x()*pos.x() + pos.y()*pos.y());
280 ATH_MSG_DEBUG(
"Position (mm): (" << pos.x()/CLHEP::mm <<
", " << pos.y()/CLHEP::mm <<
", " << pos.z()/CLHEP::mm <<
")");
282 ATH_MSG_DEBUG(
"Ekin (MeV): " << track.GetKineticEnergy()/CLHEP::MeV);
283 ATH_MSG_DEBUG(
"PDG: " << track.GetDefinition()->GetPDGEncoding());
284 ATH_MSG_DEBUG(
"===========================================");
288 const EventContext& ctx,
289 const G4Track& track,
290 const Acts::GeometryContext& anygctx,
291 const Acts::MagneticFieldContext& mctx,
293 G4FastStep& fastStep)
305 ActsFatras::Barcode bc = ActsFatras::Barcode().withVertexPrimary(0).withParticle(1);
307 Acts::PdgParticle pdg = Acts::PdgParticle(13);
311 ActsFatras::Particle fakeMuon(bc, pdg,
charge, mass);
313 fakeMuon.setPosition4({50., 0., 0., 0.});
314 fakeMuon.setDirection(0., 1., 0.);
315 fakeMuon.setAbsoluteMomentum(10000.0);
318 std::vector<ActsFatras::Particle> inputParticle{ fakeMuon };
320 std::vector<ActsFatras::Particle> simulatedInitialParticles;
321 std::vector<ActsFatras::Particle> simulatedFinalParticles;
322 std::vector<ActsFatras::Hit> hits;
326 return StatusCode::FAILURE;
329 auto result =
m_simulator->simulate(anygctx, mctx, generator, inputParticle, simulatedInitialParticles, simulatedFinalParticles, hits);
332 ATH_MSG_ERROR(
"Fatras debug injection failed: " << result.error().message());
333 fastStep.KillPrimaryTrack();
334 return StatusCode::FAILURE;
337 ATH_MSG_DEBUG(
"Injected muon hits produced: " << hits.size());
341 fastStep.KillPrimaryTrack();
342 return StatusCode::FAILURE;
345 fastStep.KillPrimaryTrack();
353 return StatusCode::SUCCESS;
359 const G4Track& track = *fastTrack.GetPrimaryTrack();
362 int trackID = track.GetTrackID();
363 int parentID = track.GetParentID();
364 int barcode = helper.GetBarcode();
365 int pdg = track.GetDefinition()->GetPDGEncoding();
366 double ekin = track.GetKineticEnergy() / CLHEP::MeV;
377 const EventContext& ctx = Gaudi::Hive::currentContext();
381 CLHEP::HepRandomEngine* randomEngine = m_randomEngine->getEngine(ctx);
384 Generator generator(CLHEP::RandFlat::shoot(randomEngine->flat()));
392 std::vector<ActsFatras::Particle> inputParticle;
397 fastStep.KillPrimaryTrack();
404 if (
runDebugInjection(ctx, track, anygctx, mctx, generator, fastStep).isFailure()){
414 Acts::Vector3 startPos(track.GetPosition().x(), track.GetPosition().y(), track.GetPosition().z());
415 auto startVolume =
m_trackingGeometry->resolveLowestTrackingVolume(anygctx, startPos);
417 if(not startVolume.ok() or *startVolume ==
nullptr){
418 ATH_MSG_DEBUG(
"Could not resolve the lowest tracking volume, skip FATRAS.");
423 if (msgLvl(MSG::DEBUG)) {
424 const G4VSolid* envelope = fastTrack.GetEnvelopeSolid();
425 if (envelope) {
ATH_MSG_DEBUG(
"FastSim envelope solid name: " << envelope->GetName());}
426 const G4VPhysicalVolume* pv = track.GetVolume();
428 const G4LogicalVolume* lv = pv->GetLogicalVolume();
430 const G4Region* region = lv->GetRegion();
439 std::vector<ActsFatras::Particle> simulatedInitialParticles;
440 std::vector<ActsFatras::Particle> simulatedFinalParticles;
441 std::vector<ActsFatras::Hit> hits;
450 auto result =
m_simulator->simulate(anygctx, mctx, generator, inputParticle, simulatedInitialParticles, simulatedFinalParticles, hits);
452 const std::string&
msg = result.error().message();
454 if (
msg.find(
"No Volume") != std::string::npos) {
455 const G4VSolid* envelope = fastTrack.GetEnvelopeSolid();
456 const G4ThreeVector& pos = track.GetPosition();
457 double r = std::sqrt(pos.x()*pos.x() + pos.y()*pos.y());
458 auto inside = envelope->Inside(pos);
460 <<
"(Position mm, R mm) = (("
461 << pos.x()/CLHEP::mm <<
", "
462 << pos.y()/CLHEP::mm <<
", "
463 << pos.z()/CLHEP::mm <<
"), "
464 <<
r/CLHEP::mm <<
")"
466 ATH_MSG_ERROR(
"ActsFatras simulation failed with 'No Volume' error: " <<
msg);
467 if (msgLvl(MSG::DEBUG)) {
468 const G4ThreeVector& mom = track.GetMomentumDirection();
470 ATH_MSG_DEBUG(
"PDG = " << track.GetDefinition()->GetPDGEncoding());
471 ATH_MSG_DEBUG(
"Ekin MeV = " << track.GetKineticEnergy() / CLHEP::MeV);
472 ATH_MSG_DEBUG(
"Position mm = (" << pos.x()/CLHEP::mm <<
", " << pos.y()/CLHEP::mm <<
", " << pos.z()/CLHEP::mm <<
")");
475 ATH_MSG_DEBUG(
"Direction = (" << mom.x() <<
", " << mom.y() <<
", " << mom.z() <<
")");
478 ATH_MSG_DEBUG(
"Track outside Acts tracking geometry -> let G4 continue.");
485 fastStep.KillPrimaryTrack();
493 if (msgLvl(MSG::DEBUG)) {
494 if (!simulatedInitialParticles.empty()) {
495 const auto& initial = simulatedInitialParticles.front();
496 double totalEdep = 0.;
497 for (
const auto&
h : hits) {totalEdep +=
h.depositedEnergy() / Acts::UnitConstants::MeV;}
498 ATH_MSG_DEBUG(
"===============================================");
501 ATH_MSG_DEBUG(
"Initial momentum [MeV]: " << initial.absoluteMomentum());
504 ATH_MSG_DEBUG(
"Final particles: " << simulatedFinalParticles.size());
505 ATH_MSG_DEBUG(
"Total deposited energy [MeV]: " << totalEdep);
506 ATH_MSG_DEBUG(
"===============================================");
510 if (!simulatedInitialParticles.empty()) {
511 ATH_MSG_DEBUG(name() <<
" initial particle " << simulatedInitialParticles.front());
513 ATH_MSG_DEBUG(name() <<
" ActsFatras simulator hits: " << hits.size());
531 if (
spawnSecondaries(simulatedFinalParticles, track, fastStep).isFailure()) {
542 const EventCache* cache = m_eventCache.get(ctx);
543 static const std::vector<SiHit>
empty;
544 if (!cache) {
return empty; }
545 return cache->pixelHits;
550 const EventCache* cache = m_eventCache.get(ctx);
551 static const std::vector<SiHit>
empty;
552 if (!cache) {
return empty; }
553 return cache->sctHits;
559 if (!cache) {
return; }
560 cache->pixelHits.clear();
561 cache->sctHits.clear();
565 return *m_eventCache.get(ctx);
570 const EventContext& ctx,
571 const G4Track& track,
572 const Acts::TrackingGeometry& trackingGeometry,
573 const std::vector<ActsFatras::Hit>& hits
577 return StatusCode::SUCCESS;
589 int barcode = helper.GetBarcode();
601 for (
const auto&
hit : hits) {
603 double energyDeposit =
hit.depositedEnergy() / Acts::UnitConstants::MeV;
605 auto geoID =
hit.geometryId();
609 auto acts_surface = trackingGeometry.findSurface(geoID);
610 if (!acts_surface)
continue;
613 if (!acts_de)
continue;
615 const Trk::Surface& atlasSurface = acts_de->atlasSurface();
621 if (!siDet)
continue;
629 double interX = (*intersection)(0);
630 double interY = (*intersection)(1);
637 const auto& actsDir =
hit.direction();
644 Amg::Vector3D localDirAmg = invSurfaceTransform.linear() * amgDir;
647 double norm = localDirAmg.norm();
648 if (norm < 1e-9)
continue;
649 double cosTheta = localDirAmg.z() / norm;
650 if (std::abs(cosTheta) < 1e-6)
continue;
653 localDirAmg *= thickness / std::abs(cosTheta);
655 int movingDir = localDirAmg.z() > 0. ? 1 : -1;
657 double distX = localDirAmg.x();
658 double distY = localDirAmg.y();
660 double localEntryX = interX - 0.5 * distX;
661 double localEntryY = interY - 0.5 * distY;
662 double localExitX = interX + 0.5 * distX;
663 double localExitY = interY + 0.5 * distY;
668 Amg::Vector3D surfaceEntry(localEntryX, localEntryY, -0.5 * movingDir * thickness);
669 Amg::Vector3D surfaceExit(localExitX, localExitY, 0.5 * movingDir * thickness);
676 HepGeom::Point3D<double> entryHep(localEntry.x(), localEntry.y(), localEntry.z());
677 HepGeom::Point3D<double> exitHep(localExit.x(), localExit.y(), localExit.z());
679 bool isPixel = siDet->
isPixel();
702 evtCache.
pixelHits.emplace_back(std::move(siHit));
704 evtCache.
sctHits.emplace_back(std::move(siHit));
706 ATH_MSG_VERBOSE(name() <<
" convert and store 1 hit, total " << evtCache.
pixelHits.size() <<
" Pixel | " << evtCache.
sctHits.size() <<
" SCT hits stored.");
708 catch (
const std::exception& e) {
709 ATH_MSG_DEBUG(name() <<
"Can not find Acts Surface (" << e.what() <<
")...Skip...");
714 return StatusCode::SUCCESS;
721 return StatusCode::SUCCESS;
const ActsDetectorElement * getActsDetectorElement(const Acts::Surface &surf)
Attempts to retrieve the ActsDetectorElement associated to the passed ActsSurface.
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
double charge(const T &p)
std::unique_ptr< const Acts::Logger > makeActsAthenaLogger(IMessageSvc *svc, const std::string &name, int level, std::optional< std::string > parent_name)
static const Attributes_t empty
a link optimized in size for a GenParticle in a McEventCollection
Class to hold geometrical description of a silicon detector element.
const GeoTrf::Transform3D & transformHit() const
Local (simulation/hit frame) to global transform.
Abstract Base Class for tracking surfaces.
virtual bool globalToLocal(const Amg::Vector3D &glob, const Amg::Vector3D &mom, Amg::Vector2D &loc) const =0
Specified by each surface type: GlobalToLocal method without dynamic memory allocation - boolean chec...
const TrkDetElementBase * associatedDetectorElement() const
return associated Detector Element
const Amg::Transform3D & transform() const
Returns HepGeom::Transform3D by reference.
Identifier associatedDetectorElementIdentifier() const
return Identifier of the associated Detector Element
This is the base class for all tracking detector elements with read-out relevant information.
std::vector< std::string > intersection(std::vector< std::string > &v1, std::vector< std::string > &v2)
constexpr double timeToAthena(T actsT)
Converts a time unit from Acts to Athena units.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
constexpr int INVALID_PARTICLE_ID
constexpr int UNDEFINED_ID