Warning, file /acts/Examples/Io/HepMC3/src/HepMC3InputConverter.cpp was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Io/HepMC3/HepMC3InputConverter.hpp"
0010
0011 #include "Acts/Utilities/ScopedTimer.hpp"
0012
0013 #include <HepMC3/GenEvent.h>
0014 #include <HepMC3/GenParticle.h>
0015 #include <HepMC3/GenVertex.h>
0016
0017
0018
0019 #if defined(__clang__)
0020 #pragma clang diagnostic push
0021 #pragma clang diagnostic ignored "-Wkeyword-macro"
0022 #endif
0023 #define private public
0024 #include <HepMC3/Print.h>
0025 #undef private
0026 #if defined(__clang__)
0027 #pragma clang diagnostic pop
0028 #endif
0029
0030 using namespace Acts::UnitLiterals;
0031
0032 namespace ActsExamples {
0033
0034 HepMC3InputConverter::HepMC3InputConverter(const Config& config,
0035 Acts::Logging::Level level)
0036 : IAlgorithm("HepMC3InputConverter", level), m_cfg(config) {
0037 m_outputParticles.initialize(m_cfg.outputParticles);
0038 m_outputVertices.initialize(m_cfg.outputVertices);
0039 m_inputEvent.initialize(m_cfg.inputEvent);
0040 }
0041
0042 ProcessCode HepMC3InputConverter::execute(const AlgorithmContext& ctx) const {
0043 ACTS_DEBUG("HepMC3InputConverter::execute");
0044 const auto& genEvent = *m_inputEvent(ctx);
0045 ACTS_DEBUG("Have " << genEvent.vertices().size() << " vertices");
0046 ACTS_DEBUG("Have " << genEvent.particles().size() << " particles");
0047 ACTS_DEBUG("Have " << genEvent.event_number() << " event number");
0048
0049 convertHepMC3ToInternalEdm(ctx, genEvent);
0050
0051 return ProcessCode::SUCCESS;
0052 }
0053
0054 namespace {
0055
0056 constexpr int kBeamParticleStatus = 4;
0057 constexpr int kUndecayedParticleStatus = 1;
0058
0059 Acts::Vector4 convertPosition(const HepMC3::FourVector& vec) {
0060 return Acts::Vector4(vec.x() * 1_mm, vec.y() * 1_mm, vec.z() * 1_mm,
0061 vec.t() * 1_mm);
0062 }
0063
0064 std::string printListing(const auto& vertices, const auto& particles) {
0065 auto findParticle = [&](SimBarcode particleId) {
0066 if (auto it = std::ranges::find_if(particles,
0067 [&](const auto& particle) {
0068 return particle.particleId() ==
0069 particleId;
0070 });
0071 it != particles.end()) {
0072 return *it;
0073 }
0074 throw std::runtime_error("Particle not found");
0075 };
0076 std::stringstream ss;
0077
0078 for (const auto& vertex : vertices) {
0079 ss << "Vtx: " << vertex.vertexId() << " at "
0080 << vertex.position4.transpose() << "\n";
0081 for (const auto& [idx, particleId] : Acts::enumerate(vertex.incoming)) {
0082 const auto& particle = findParticle(particleId);
0083 if (idx == 0) {
0084 ss << " I: ";
0085 } else {
0086 ss << " ";
0087 }
0088 ss << particle;
0089 ss << "\n";
0090 }
0091
0092 for (const auto& [idx, particleId] : Acts::enumerate(vertex.outgoing)) {
0093 const auto& particle = findParticle(particleId);
0094 if (idx == 0) {
0095 ss << " O: ";
0096 } else {
0097 ss << " ";
0098 }
0099 ss << particle;
0100 ss << "\n";
0101 }
0102 }
0103
0104 return ss.str();
0105 };
0106 }
0107
0108 void HepMC3InputConverter::handleVertex(const HepMC3::GenVertex& genVertex,
0109 SimVertex& vertex,
0110 std::vector<SimVertex>& vertices,
0111 std::vector<SimParticle>& particles,
0112 std::size_t& nSecondaryVertices,
0113 std::size_t& nParticles,
0114 std::vector<bool>& seenVertices) const {
0115 for (const auto& particle : genVertex.particles_out()) {
0116 if (particle->end_vertex() != nullptr) {
0117
0118 const auto& endVertex = *particle->end_vertex();
0119 if (seenVertices.at(std::abs(endVertex.id()) - 1)) {
0120
0121 continue;
0122 }
0123 seenVertices.at(std::abs(endVertex.id()) - 1) = true;
0124
0125 const auto endVertexPosition = convertPosition(endVertex.position());
0126
0127 ACTS_VERBOSE("Found secondary vertex at "
0128 << endVertexPosition.transpose());
0129 double distance = (endVertexPosition.template head<3>() -
0130 vertex.position4.template head<3>())
0131 .squaredNorm();
0132
0133 if (distance <=
0134 m_cfg.vertexSpatialThreshold * m_cfg.vertexSpatialThreshold &&
0135 m_cfg.mergeSecondaries) {
0136 handleVertex(endVertex, vertex, vertices, particles, nSecondaryVertices,
0137 nParticles, seenVertices);
0138 } else {
0139
0140 SimVertex secondaryVertex;
0141 nSecondaryVertices += 1;
0142 secondaryVertex.id =
0143 SimVertexBarcode{vertex.id}.withVertexSecondary(nSecondaryVertices);
0144 secondaryVertex.position4 = convertPosition(endVertex.position());
0145
0146 handleVertex(endVertex, secondaryVertex, vertices, particles,
0147 nSecondaryVertices, nParticles, seenVertices);
0148
0149
0150 if (!secondaryVertex.outgoing.empty()) {
0151 vertices.push_back(secondaryVertex);
0152 }
0153 }
0154 } else {
0155 if (particle->status() != kUndecayedParticleStatus) {
0156 ACTS_ERROR("Undecayed particle has status "
0157 << particle->status() << "(and not "
0158 << kUndecayedParticleStatus << ")");
0159 }
0160
0161 nParticles += 1;
0162 SimBarcode particleId =
0163 SimBarcode()
0164 .withVertexPrimary(vertex.vertexId().vertexPrimary())
0165 .withVertexSecondary(vertex.vertexId().vertexSecondary())
0166 .withParticle(nParticles);
0167
0168 Acts::PdgParticle pdg{particle->pdg_id()};
0169 double mass = 0.0;
0170 double charge = 0.0;
0171
0172 if (pdg != Acts::PdgParticle::eInvalid) {
0173 if (auto massOpt = Acts::findMass(pdg); massOpt.has_value()) {
0174 mass = massOpt.value();
0175 } else {
0176 ACTS_ERROR("No mass found for PDG ID " << pdg);
0177 throw std::bad_optional_access{};
0178 }
0179
0180 if (auto chargeOpt = Acts::findCharge(pdg); chargeOpt.has_value()) {
0181 charge = chargeOpt.value();
0182 } else {
0183 ACTS_ERROR("No charge found for PDG ID " << pdg);
0184 throw std::bad_optional_access{};
0185 }
0186 }
0187
0188 if (std::isnan(mass) || std::isnan(charge)) {
0189
0190
0191
0192 continue;
0193 }
0194
0195 SimParticle simParticle{particleId, pdg, charge, mass};
0196 simParticle.initialState().setPosition4(vertex.position4);
0197
0198 const HepMC3::FourVector& genMomentum = particle->momentum();
0199 Acts::Vector3 momentum{genMomentum.px() * 1_GeV, genMomentum.py() * 1_GeV,
0200 genMomentum.pz() * 1_GeV};
0201 double p = momentum.norm();
0202
0203 simParticle.initialState().setDirection(momentum.normalized());
0204 simParticle.initialState().setAbsoluteMomentum(p);
0205
0206 particles.push_back(simParticle);
0207 vertex.outgoing.insert(particleId);
0208 }
0209 }
0210 }
0211
0212 void HepMC3InputConverter::convertHepMC3ToInternalEdm(
0213 const AlgorithmContext& ctx, const HepMC3::GenEvent& genEvent) const {
0214 ACTS_DEBUG("Converting HepMC3 event to internal EDM");
0215
0216 ACTS_VERBOSE("Have " << genEvent.vertices().size() << " vertices");
0217 ACTS_VERBOSE("Have " << genEvent.particles().size() << " particles");
0218
0219 using VertexCluster = std::vector<HepMC3::ConstGenVertexPtr>;
0220 std::vector<VertexCluster> vertexClusters;
0221
0222 ACTS_VERBOSE("Finding primary vertex clusters with threshold "
0223 << m_cfg.primaryVertexSpatialThreshold);
0224
0225
0226
0227 {
0228 Acts::ScopedTimer timer("Finding primary vertex clusters", logger(),
0229 Acts::Logging::DEBUG);
0230 for (const auto& vertex : genEvent.vertices()) {
0231 if (vertex->particles_in().empty() ||
0232 std::ranges::all_of(vertex->particles_in(), [](const auto& particle) {
0233 return particle->status() == kBeamParticleStatus;
0234 })) {
0235
0236
0237
0238 ACTS_VERBOSE("Found primary vertex candidate at:\n"
0239 << [&]() {
0240 std::stringstream ss;
0241 HepMC3::Print::listing(ss, vertex);
0242 return ss.str();
0243 }());
0244
0245 auto position = convertPosition(vertex->position());
0246
0247 if (auto it = std::ranges::find_if(
0248 vertexClusters,
0249 [&](const auto& cluster) {
0250 const auto clusterPosition =
0251 convertPosition(cluster.at(0)->position());
0252 return (position - clusterPosition)
0253 .template head<3>()
0254 .cwiseAbs()
0255 .maxCoeff() < m_cfg.primaryVertexSpatialThreshold;
0256 });
0257 it != vertexClusters.end() && m_cfg.mergePrimaries) {
0258
0259 it->push_back(vertex);
0260 } else {
0261
0262 vertexClusters.push_back({vertex});
0263 }
0264 }
0265 }
0266 }
0267
0268 ACTS_DEBUG("Found " << vertexClusters.size()
0269 << " primary vertex clusters (~ primary vertices)");
0270
0271 std::vector<SimVertex> verticesUnordered;
0272 std::vector<SimParticle> particlesUnordered;
0273
0274 std::vector<bool> seenVertices;
0275 seenVertices.resize(genEvent.vertices().size());
0276
0277 for (const auto& cluster : vertexClusters) {
0278 for (const auto& vertex : cluster) {
0279 seenVertices.at(std::abs(vertex->id()) - 1) = true;
0280 }
0281 }
0282
0283 std::size_t nPrimaryVertices = 0;
0284 {
0285 Acts::ScopedTimer timer("Converting HepMC3 vertices to internal EDM",
0286 logger(), Acts::Logging::DEBUG);
0287
0288 std::size_t nUndecayedParticles = 0;
0289 for (const auto& particle : genEvent.particles()) {
0290 if (particle->end_vertex() == nullptr) {
0291 nUndecayedParticles += 1;
0292 }
0293 }
0294 ACTS_DEBUG("Found " << nUndecayedParticles
0295 << " undecayed particles in the event");
0296 ACTS_DEBUG("Reserving " << genEvent.vertices().size() / 2
0297 << " vertices and " << nUndecayedParticles
0298 << " particles");
0299 verticesUnordered.reserve(genEvent.vertices().size() / 2);
0300 particlesUnordered.reserve(nUndecayedParticles);
0301
0302 for (auto& cluster : vertexClusters) {
0303 ACTS_VERBOSE("Primary vertex cluster at "
0304 << convertPosition(cluster.at(0)->position()).transpose()
0305 << " containing " << cluster.size() << " vertices:\n"
0306 <<
0307 [&]() {
0308 std::stringstream ss;
0309 for (auto& vertex : cluster) {
0310 HepMC3::Print::listing(ss, vertex);
0311 }
0312 return ss.str();
0313 }()
0314 << "--------------");
0315
0316 nPrimaryVertices += 1;
0317 SimVertex primaryVertex;
0318 primaryVertex.id = SimVertexBarcode().withVertexPrimary(nPrimaryVertices);
0319 primaryVertex.position4 = convertPosition(cluster.at(0)->position());
0320
0321 std::size_t nSecondaryVertices = 0;
0322 std::size_t nParticles = 0;
0323
0324 for (auto& genVertex : cluster) {
0325 handleVertex(*genVertex, primaryVertex, verticesUnordered,
0326 particlesUnordered, nSecondaryVertices, nParticles,
0327 seenVertices);
0328 }
0329 verticesUnordered.push_back(primaryVertex);
0330 }
0331 }
0332
0333 ACTS_DEBUG("Converted " << particlesUnordered.size() << " particles and "
0334 << verticesUnordered.size() << " vertices");
0335
0336 if (m_cfg.printListing) {
0337 ACTS_VERBOSE("Converted event record:\n"
0338 << printListing(verticesUnordered, particlesUnordered));
0339 }
0340
0341 {
0342 Acts::ScopedTimer timer("Sorting particles and vertices", logger(),
0343 Acts::Logging::DEBUG);
0344
0345 std::ranges::sort(particlesUnordered, [](const auto& a, const auto& b) {
0346 return a.particleId() < b.particleId();
0347 });
0348 std::ranges::sort(verticesUnordered, [](const auto& a, const auto& b) {
0349 return a.vertexId() < b.vertexId();
0350 });
0351 }
0352
0353 if (m_cfg.checkConsistency) {
0354 ACTS_DEBUG("Checking consistency of particles");
0355 auto equalParticleIds = [](const auto& a, const auto& b) {
0356 return a.particleId() == b.particleId();
0357 };
0358 if (auto it =
0359 std::ranges::adjacent_find(particlesUnordered, equalParticleIds);
0360 it != particlesUnordered.end()) {
0361 ACTS_ERROR("Found duplicate particle id " << it->particleId());
0362 }
0363 auto equalVertexIds = [](const auto& a, const auto& b) {
0364 return a.vertexId() == b.vertexId();
0365 };
0366 if (auto it = std::ranges::adjacent_find(verticesUnordered, equalVertexIds);
0367 it != verticesUnordered.end()) {
0368 ACTS_ERROR("Found duplicate vertex id " << it->vertexId());
0369 }
0370 }
0371
0372 SimParticleContainer particles{particlesUnordered.begin(),
0373 particlesUnordered.end()};
0374 SimVertexContainer vertices{verticesUnordered.begin(),
0375 verticesUnordered.end()};
0376
0377
0378 m_outputParticles(ctx, std::move(particles));
0379 m_outputVertices(ctx, std::move(vertices));
0380 }
0381
0382 }