92 const EventContext& ctx,
97 std::vector<ISFParticle*> selectedParticles;
98 for (
const auto isfp : particles) {
100 ATH_MSG_VERBOSE(
"ISFParticle " << *isfp <<
" does not pass selection. Ignoring.");
103 selectedParticles.push_back(isfp);
105 if (selectedParticles.empty()) {
107 return StatusCode::SUCCESS;
111 CLHEP::HepRandomEngine* randomEngine = m_randomEngine->getEngine(ctx);
112 Generator generator(CLHEP::RandFlat::shoot(randomEngine->flat()));
113 ATH_MSG_VERBOSE(name() <<
" RNG seed " << CLHEP::RandFlat::shoot(randomEngine->flat()));
115 << selectedParticles.size() <<
" particles for simulation.");
119 auto bField = std::make_shared<ATLASMagneticFieldWrapper>();
138 simulator.charged.interactions = ActsFatras::makeStandardChargedElectroMagneticInteractions(
m_interact_minPt * Acts::UnitConstants::MeV);
148 ATH_MSG_VERBOSE(name() <<
" Processing particles in ISFParticleVector.");
149 for (
const auto isfp : selectedParticles) {
156 ATH_MSG_DEBUG(name() <<
" Convert ISF::Particle(mass) " << isfp->id()<<
"|" << *isfp<<
"(" << isfp->mass() <<
")");
157 std::vector<ActsFatras::Particle> input = std::vector<ActsFatras::Particle>{
158 ActsFatras::Particle(ActsFatras::Barcode().withVertexPrimary(0).withParticle(isfp->id()),
static_cast<Acts::PdgParticle
>(isfp->pdgCode()),
159 isfp->charge(),isfp->mass() * Acts::UnitConstants::MeV)
160 .setDirection(Acts::makeDirectionFromPhiEta(isfp->momentum().phi(), isfp->momentum().eta()))
161 .setAbsoluteMomentum(isfp->momentum().mag() * Acts::UnitConstants::MeV)
163 ATH_MSG_DEBUG(name() <<
" Propagating ActsFatras::Particle vertex|particle|generation|subparticle, " << input[0]);
164 std::vector<ActsFatras::Particle> simulatedInitial;
165 std::vector<ActsFatras::Particle> simulatedFinal;
166 std::vector<ActsFatras::Hit> hits;
168 auto result=simulator.simulate(anygctx, mctx, generator, input, simulatedInitial, simulatedFinal, hits);
169 auto simulatedFailure=result.value();
170 if (simulatedFailure.size()>0){
171 for (
const auto& simfail : simulatedFailure){
172 auto errCode = Acts::make_error_code(Acts::PropagatorError(simfail.error.value()));
173 ATH_MSG_WARNING(name() <<
" Particle id " <<simfail.particle.particleId()<<
": fail to be simulated during Propagation: " << errCode.message());
174 ATH_MSG_WARNING(name() <<
" Particle vertex|particle|generation|subparticle"<<simfail.particle <<
" starts from position" << Acts::toString(simfail.particle.position()) <<
" and direction " << Acts::toString(simfail.particle.direction()));
175 return StatusCode::SUCCESS;
179 ATH_MSG_DEBUG(name() <<
" initial particle " << simulatedInitial[0]);
180 ATH_MSG_DEBUG(name() <<
" ActsFatras simulator hits: " << hits.size());
182 for (
const auto& hit : hits) {
187 ATH_MSG_DEBUG(name() <<
" No. of particles after ActsFatras simulator: " << simulatedFinal.size());
188 if (!simulatedFinal.empty()){
190 auto itr = simulatedFinal.begin();
192 std::vector<ActsFatras::Hit> particle_hits;
193 if (itr->numberOfHits() > 0) {
194 std::copy(hits.begin(), hits.begin()+itr->numberOfHits(), std::back_inserter(particle_hits));
198 auto isKilled = !itr->isAlive();
199 int maxGeneration = simulatedFinal.back().particleId().generation();
201 for (
int gen = 0; gen <= maxGeneration; ++gen){
202 ATH_MSG_DEBUG(name() <<
" start with generation "<< gen <<
"|" << maxGeneration <<
": "<< *itr);
203 auto vecsecisfp = std::make_unique<ISF::ISFParticleVector>();
204 std::unique_ptr<ISF::ISFParticle> newisfp =
nullptr;
206 while (itr != simulatedFinal.end() &&
static_cast<int>(itr->particleId().generation()) == gen) {
207 ATH_MSG_DEBUG(name() <<
" genration "<< gen <<
"|" << maxGeneration <<
": "<< *itr);
208 if(itr->isSecondary()){
212 double mass = itr->mass() / Acts::UnitConstants::MeV;
213 double charge = itr->charge();
214 int pdgid = itr->pdg();
218 auto secisfp = std::make_unique<ISF::ISFParticle>(pos,mom,mass,
charge,pdgid,status,properTime,*isfp,
id);
219 secisfp->setNextGeoID(
m_geoIDSvc->identifyNextGeoID(*secisfp));
220 ATH_MSG_DEBUG(name() <<
" secondaries particle (ACTS): "<<*itr<<
"("<<itr->momentum()<<
")|time "<<itr->time()<<
"|process "<<
getATLASProcessCode(itr->process()));
221 ATH_MSG_DEBUG(name() <<
" secondaries particle (ISF): pdg=" << secisfp->pdgCode()
222 <<
" pos=" << secisfp->position() <<
" mom=" << secisfp->momentum()
223 <<
" GeoID=" <<
m_geoIDSvc->identifyNextGeoID(*secisfp));
224 vecsecisfp->push_back(secisfp.release());
228 ATH_MSG_DEBUG(name() <<
" primary particle found with generation ("<< gen <<
")");
231 auto fisfp = std::make_unique<ISF::ISFParticle>(*isfp);
234 ATH_MSG_DEBUG(name() <<
" After simulation, primary particle state: " << *fisfp);
236 ATH_MSG_VERBOSE(
"ISFParticle" << fisfp <<
" after simulation does not pass selection. Ignoring for boundary check.");
240 <<
" new particle GeoID: " <<
m_geoIDSvc->identifyGeoID(*fisfp)
241 <<
", nextGeoID: " <<
m_geoIDSvc->identifyNextGeoID(*fisfp));
244 ATH_MSG_DEBUG(name() <<
" Extrapolating using ActsExtrapolationTool");
247 Acts::BoundTrackParameters startParams = Acts::BoundTrackParameters::createCurvilinear(
248 itr->fourPosition(), itr->direction(), itr->qOverP(), std::nullopt, itr->hypothesis());
251 auto do_exit_startsurface =
checkStartSurface(mctx, anygctx, surfaceCheckPropagator, startParams);
252 ATH_MSG_DEBUG(name() <<
" checkStartSurface returned: " << do_exit_startsurface);
253 if (do_exit_startsurface) {
254 ATH_MSG_DEBUG(name() <<
" Particle starts at a valid surface, doing extrapolation...");
260 auto stepsResult =
m_extrapolationTool->propagationSteps(ctx, startParams, Acts::Direction::Forward());
261 auto steps = stepsResult.value().first;
262 ATH_MSG_DEBUG(name() <<
" Number of propagation steps: " << steps.size());
263 if (steps.size() != 0) {
264 for (
const auto& step : steps) {
265 ATH_MSG_DEBUG(name() <<
" [Acts] Step at position " << step.position
266 <<
" (eta " << Acts::VectorHelpers::eta(step.position)
267 <<
") with GeoID " << step.geoID);
269 nextGeoID =
m_geoIDSvc->identifyGeoID(entryPos);
270 ATH_MSG_DEBUG(name() <<
" [Acts] GeoID from service: " << nextGeoID);
272 ATH_MSG_DEBUG(name() <<
" Boundary crossing detected at GeoID " << nextGeoID);
277 ATH_MSG_WARNING(name() <<
" No propagation steps returned by ActsExtrapolationTool");
280 catch (
const std::exception& e) {
287 double mass = itr->mass() / Acts::UnitConstants::MeV;
288 double charge = itr->charge();
289 int pdgid = itr->pdg();
293 newisfp = std::make_unique<ISF::ISFParticle>(entryPos, mom, mass,
charge, pdgid, isfp->status(), properTime, *isfp, isfp->id(), isfp->barcode());
294 newisfp->setNextGeoID(nextGeoID);
295 ATH_MSG_DEBUG(name() <<
" Truthbinding of parent ISFParticle: " << (isfp->getTruthBinding() ?
"exists" :
"null"));
296 if (isfp->getTruthBinding()) {
297 ATH_MSG_DEBUG(name() <<
" Current GenParticle: " << isfp->getTruthBinding()->getCurrentGenParticle());
299 ATH_MSG_DEBUG(name() <<
" Created new ISFParticle at boundary with nextGeoID: "
301 <<
"(" << nextGeoID <<
")");
306 ATH_MSG_DEBUG(name() <<
" [ISF] Processing boundary particle with nextGeoID: "
308 <<
"(" << newisfp->nextGeoID() <<
")");
328 vecsecisfp->push_back(newisfp.release());
335 ATH_MSG_DEBUG(name() <<
" No starting surface found, skipping boundary check and extrapolation.");
343 if (!vecsecisfp->empty()) {
352 geoID = newisfp->nextGeoID();
373 for (
auto *secisfp : *vecsecisfp){
374 if (secisfp->getTruthBinding()) {
375 secondaries.push_back(secisfp);
376 ATH_MSG_DEBUG(name() <<
" Secondary particle written out to truth.\n Parent ("
377 << *isfp <<
")\n Secondary (" << *secisfp <<
")");
379 ATH_MSG_DEBUG(
"Secondary particle push back to ISF, TruthBinding: " << secisfp->getTruthBinding()->getCurrentGenParticle() <<
" (current) | " << secisfp->getTruthBinding()->getPrimaryGenParticle() <<
" (primary) | " << secisfp->getTruthBinding()->getGenerationZeroGenParticle() <<
" (zero)");
380 if (secisfp->getTruthBinding()->getCurrentGenParticle() !=
nullptr)
ATH_MSG_DEBUG(
"Secondary particle GenParticle EndVertex: " << (secisfp->getTruthBinding()->getCurrentGenParticle()->end_vertex() ?
HepMC::barcode(secisfp->getTruthBinding()->getCurrentGenParticle()->end_vertex()) : 1));
382 ATH_MSG_WARNING(
"Secondary particle not written out to truth.\n Parent ("
383 << *isfp <<
")\n Secondary (" << *secisfp <<
")");
390 ATH_MSG_VERBOSE(name() <<
" No. of secondaries: " << secondaries.size());
393 std::vector<ActsFatras::Particle>().swap(input);
394 std::vector<ActsFatras::Particle>().swap(simulatedInitial);
395 std::vector<ActsFatras::Particle>().swap(simulatedFinal);
396 std::vector<ActsFatras::Hit>().swap(hits);
398 return StatusCode::SUCCESS;