File indexing completed on 2025-08-05 08:10:23
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/unit_test.hpp>
0011
0012 #include "Acts/Definitions/ParticleData.hpp"
0013 #include "Acts/EventData/TrackParameters.hpp"
0014 #include "Acts/MagneticField/ConstantBField.hpp"
0015 #include "Acts/Propagator/EigenStepper.hpp"
0016 #include "Acts/Propagator/Navigator.hpp"
0017 #include "Acts/Propagator/StraightLineStepper.hpp"
0018 #include "Acts/Tests/CommonHelpers/CylindricalTrackingGeometry.hpp"
0019 #include "Acts/Utilities/Logger.hpp"
0020 #include "Acts/Utilities/UnitVectors.hpp"
0021 #include "ActsFatras/Kernel/InteractionList.hpp"
0022 #include "ActsFatras/Kernel/Simulation.hpp"
0023 #include "ActsFatras/Physics/Decay/NoDecay.hpp"
0024 #include "ActsFatras/Physics/StandardInteractions.hpp"
0025 #include "ActsFatras/Selectors/ParticleSelectors.hpp"
0026 #include "ActsFatras/Selectors/SurfaceSelectors.hpp"
0027
0028 #include <algorithm>
0029 #include <random>
0030
0031 using namespace Acts::UnitLiterals;
0032
0033 namespace {
0034
0035
0036 struct SplitEnergyLoss {
0037 double splitMomentumMin = 5_GeV;
0038
0039 template <typename generator_t>
0040 bool operator()(generator_t& ,
0041 const Acts::MaterialSlab& ,
0042 ActsFatras::Particle& particle,
0043 std::vector<ActsFatras::Particle>& generated) const {
0044 const auto p = particle.absoluteMomentum();
0045 if (splitMomentumMin < p) {
0046 particle.setAbsoluteMomentum(0.5 * p);
0047 const auto pid = particle.particleId().makeDescendant();
0048 generated.push_back(
0049 particle.withParticleId(pid).setAbsoluteMomentum(0.5 * p));
0050 }
0051
0052 return false;
0053 }
0054 };
0055
0056
0057
0058 using Navigator = Acts::Navigator;
0059 using MagneticField = Acts::ConstantBField;
0060
0061 using ChargedStepper = Acts::EigenStepper<>;
0062 using ChargedPropagator = Acts::Propagator<ChargedStepper, Navigator>;
0063
0064 using NeutralStepper = Acts::StraightLineStepper;
0065 using NeutralPropagator = Acts::Propagator<NeutralStepper, Navigator>;
0066
0067
0068
0069 using Generator = std::ranlux48;
0070
0071 using ChargedSelector = ActsFatras::EveryParticle;
0072 using ChargedInteractions =
0073 ActsFatras::InteractionList<ActsFatras::detail::StandardScattering,
0074 SplitEnergyLoss>;
0075 using ChargedSimulation =
0076 ActsFatras::SingleParticleSimulation<ChargedPropagator, ChargedInteractions,
0077 ActsFatras::EverySurface,
0078 ActsFatras::NoDecay>;
0079
0080 using NeutralSelector = ActsFatras::EveryParticle;
0081 using NeutralInteractions = ActsFatras::InteractionList<>;
0082 using NeutralSimulation =
0083 ActsFatras::SingleParticleSimulation<NeutralPropagator, NeutralInteractions,
0084 ActsFatras::NoSurface,
0085 ActsFatras::NoDecay>;
0086
0087 using Simulation = ActsFatras::Simulation<ChargedSelector, ChargedSimulation,
0088 NeutralSelector, NeutralSimulation>;
0089
0090
0091
0092 const auto rangePdg =
0093 boost::unit_test::data::make(std::vector<Acts::PdgParticle>{
0094 Acts::PdgParticle::eElectron,
0095 Acts::PdgParticle::eMuon,
0096 Acts::PdgParticle::ePionPlus,
0097 Acts::PdgParticle::ePionZero,
0098 });
0099 const auto rangePhi = boost::unit_test::data::make(std::vector<double>{
0100 -135_degree,
0101 -45_degree,
0102 45_degree,
0103 135_degree,
0104 });
0105 const auto rangeEta = boost::unit_test::data::make(std::vector<double>{
0106 -1.0,
0107 0.0,
0108 1.0,
0109 3.0,
0110 });
0111 const auto rangeP = boost::unit_test::data::make(std::vector<double>{
0112 1_GeV,
0113 10_GeV,
0114 });
0115 const auto rangeNumParticles = boost::unit_test::data::make(std::vector<int>{
0116 1,
0117 2,
0118 });
0119
0120 const auto dataset =
0121 rangePdg * rangePhi * rangeEta * rangeP * rangeNumParticles;
0122
0123
0124 template <typename Container>
0125 void sortByParticleId(Container& container) {
0126 std::sort(container.begin(), container.end(),
0127 [](const auto& lhs, const auto& rhs) {
0128 return lhs.particleId() < rhs.particleId();
0129 });
0130 }
0131 template <typename Container>
0132 bool areParticleIdsUnique(const Container& sortedByParticleId) {
0133
0134 auto ret =
0135 std::adjacent_find(sortedByParticleId.begin(), sortedByParticleId.end(),
0136 [](const auto& lhs, const auto& rhs) {
0137 return lhs.particleId() == rhs.particleId();
0138 });
0139 return ret == sortedByParticleId.end();
0140 }
0141 template <typename Container, typename Value>
0142 bool containsParticleId(const Container& sortedByParticleId,
0143 const Value& value) {
0144 return std::binary_search(sortedByParticleId.begin(),
0145 sortedByParticleId.end(), value,
0146 [](const auto& lhs, const auto& rhs) {
0147 return lhs.particleId() < rhs.particleId();
0148 });
0149 }
0150
0151 }
0152
0153 BOOST_DATA_TEST_CASE(FatrasSimulation, dataset, pdg, phi, eta, p,
0154 numParticles) {
0155 using namespace Acts::UnitLiterals;
0156
0157 Acts::GeometryContext geoCtx;
0158 Acts::MagneticFieldContext magCtx;
0159 Acts::Logging::Level logLevel = Acts::Logging::Level::DEBUG;
0160
0161
0162 Acts::Test::CylindricalTrackingGeometry geoBuilder(geoCtx);
0163 auto trackingGeometry = geoBuilder();
0164
0165
0166 Navigator navigator({trackingGeometry});
0167 ChargedStepper chargedStepper(
0168 std::make_shared<Acts::ConstantBField>(Acts::Vector3{0, 0, 1_T}));
0169 ChargedPropagator chargedPropagator(std::move(chargedStepper), navigator);
0170 NeutralPropagator neutralPropagator(NeutralStepper(), navigator);
0171
0172
0173 ChargedSimulation simulatorCharged(
0174 std::move(chargedPropagator),
0175 Acts::getDefaultLogger("ChargedSimulation", logLevel));
0176 NeutralSimulation simulatorNeutral(
0177 std::move(neutralPropagator),
0178 Acts::getDefaultLogger("NeutralSimulation", logLevel));
0179 Simulation simulator(std::move(simulatorCharged),
0180 std::move(simulatorNeutral));
0181
0182
0183
0184 Generator generator;
0185
0186 std::vector<ActsFatras::Particle> input;
0187 std::vector<ActsFatras::Particle> simulatedInitial;
0188 std::vector<ActsFatras::Particle> simulatedFinal;
0189 std::vector<ActsFatras::Hit> hits;
0190
0191
0192 for (auto i = numParticles; 0 < i; --i) {
0193 const auto pid = ActsFatras::Barcode().setVertexPrimary(42).setParticle(i);
0194 const auto particle =
0195 ActsFatras::Particle(pid, pdg)
0196 .setDirection(Acts::makeDirectionFromPhiEta(phi, eta))
0197 .setAbsoluteMomentum(p);
0198 input.push_back(std::move(particle));
0199 }
0200 BOOST_TEST_INFO(input.front());
0201 BOOST_CHECK_EQUAL(input.size(), numParticles);
0202
0203
0204 auto result = simulator.simulate(geoCtx, magCtx, generator, input,
0205 simulatedInitial, simulatedFinal, hits);
0206
0207
0208 BOOST_CHECK(result.ok());
0209
0210
0211 BOOST_CHECK_EQUAL(simulatedInitial.size(), simulatedFinal.size());
0212 for (std::size_t i = 0; i < simulatedInitial.size(); ++i) {
0213 const auto& initialParticle = simulatedInitial[i];
0214 const auto& finalParticle = simulatedFinal[i];
0215
0216 BOOST_CHECK_EQUAL(initialParticle.particleId(), finalParticle.particleId());
0217 BOOST_CHECK_EQUAL(initialParticle.process(), finalParticle.process());
0218 BOOST_CHECK_EQUAL(initialParticle.pdg(), finalParticle.pdg());
0219 BOOST_CHECK_EQUAL(initialParticle.charge(), finalParticle.charge());
0220 BOOST_CHECK_EQUAL(initialParticle.mass(), finalParticle.mass());
0221 }
0222
0223
0224
0225 BOOST_CHECK_LE(input.size(), simulatedInitial.size());
0226 BOOST_CHECK_LE(input.size(), simulatedFinal.size());
0227
0228 if (Acts::findCharge(pdg) != 0) {
0229 BOOST_CHECK_LT(0u, hits.size());
0230 }
0231
0232
0233 sortByParticleId(input);
0234 sortByParticleId(simulatedInitial);
0235 sortByParticleId(simulatedFinal);
0236 sortByParticleId(hits);
0237
0238
0239 BOOST_CHECK(areParticleIdsUnique(input));
0240 BOOST_CHECK(areParticleIdsUnique(simulatedInitial));
0241 BOOST_CHECK(areParticleIdsUnique(simulatedFinal));
0242
0243
0244 for (const auto& particle : input) {
0245 BOOST_CHECK(containsParticleId(simulatedInitial, particle));
0246 BOOST_CHECK(containsParticleId(simulatedFinal, particle));
0247 }
0248
0249 for (const auto& hit : hits) {
0250 BOOST_CHECK(containsParticleId(simulatedInitial, hit));
0251 BOOST_CHECK(containsParticleId(simulatedFinal, hit));
0252 }
0253 }