Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-08 08:21:47

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/PdgParticle.hpp"
0013 #include "Acts/Definitions/Units.hpp"
0014 #include "Acts/Geometry/GeometryContext.hpp"
0015 #include "Acts/Geometry/TrackingVolume.hpp"
0016 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
0017 #include "Acts/Material/MaterialSlab.hpp"
0018 #include "Acts/Propagator/ConstrainedStep.hpp"
0019 #include "Acts/Surfaces/CurvilinearSurface.hpp"
0020 #include "Acts/Surfaces/PlaneSurface.hpp"
0021 #include "Acts/Surfaces/Surface.hpp"
0022 #include "Acts/Utilities/Logger.hpp"
0023 #include "ActsFatras/EventData/Barcode.hpp"
0024 #include "ActsFatras/EventData/GenerationProcess.hpp"
0025 #include "ActsFatras/EventData/Particle.hpp"
0026 #include "ActsFatras/Kernel/detail/SimulationActor.hpp"
0027 #include "ActsFatras/Selectors/SurfaceSelectors.hpp"
0028 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0029 #include "ActsTests/CommonHelpers/PredefinedMaterials.hpp"
0030 
0031 #include <array>
0032 #include <cmath>
0033 #include <cstddef>
0034 #include <cstdint>
0035 #include <limits>
0036 #include <memory>
0037 #include <random>
0038 #include <utility>
0039 #include <vector>
0040 
0041 using namespace Acts;
0042 using namespace Acts::UnitLiterals;
0043 using namespace ActsFatras;
0044 
0045 namespace ActsTests {
0046 
0047 constexpr auto tol = 4 * std::numeric_limits<double>::epsilon();
0048 constexpr auto inf = std::numeric_limits<double>::infinity();
0049 
0050 struct MockDecay {
0051   double properTimeLimit = inf;
0052 
0053   template <typename generator_t>
0054   constexpr double generateProperTimeLimit(generator_t & /*generator*/,
0055                                            const Particle &particle) const {
0056     return particle.properTime() + properTimeLimit;
0057   }
0058   template <typename generator_t>
0059   constexpr std::array<Particle, 0> run(generator_t & /*generator*/,
0060                                         const Particle & /*particle*/) const {
0061     return {};
0062   }
0063 };
0064 
0065 struct MockInteractionList {
0066   struct Selection {
0067     double x0Limit = std::numeric_limits<double>::infinity();
0068     double l0Limit = std::numeric_limits<double>::infinity();
0069     std::size_t x0Process = std::numeric_limits<std::size_t>::max();
0070     std::size_t l0Process = std::numeric_limits<std::size_t>::max();
0071   };
0072 
0073   double energyLoss = 0;
0074 
0075   template <typename generator_t>
0076   bool runContinuous(generator_t & /*generator*/, const MaterialSlab & /*slab*/,
0077                      Particle &particle,
0078                      std::vector<Particle> &generated) const {
0079     generated.push_back(particle);
0080     particle.loseEnergy(energyLoss);
0081     // break if particle is not alive anymore
0082     return !particle.isAlive();
0083   }
0084 
0085   template <typename generator_t>
0086   Selection armPointLike(generator_t & /*generator*/,
0087                          const Particle & /*particle*/) const {
0088     return {};
0089   }
0090 
0091   template <typename generator_t>
0092   bool runPointLike(generator_t & /*generator*/, std::size_t /*processIndex*/,
0093                     Particle & /*particle*/,
0094                     std::vector<Particle> & /*generated*/) const {
0095     return false;
0096   }
0097 };
0098 
0099 struct MockStepperState {
0100   Vector3 pos = Vector3::Zero();
0101   double time = 0;
0102   Vector3 dir = Vector3::Zero();
0103   double p = 0;
0104 };
0105 
0106 struct MockStepper {
0107   using State = MockStepperState;
0108 
0109   auto position(const State &state) const { return state.pos; }
0110   auto time(const State &state) const { return state.time; }
0111   auto direction(const State &state) const { return state.dir; }
0112   auto absoluteMomentum(const State &state) const { return state.p; }
0113   void update(State &state, const Vector3 &pos, const Vector3 &dir, double qop,
0114               double time) {
0115     state.pos = pos;
0116     state.time = time;
0117     state.dir = dir;
0118     state.p = 1 / qop;
0119   }
0120   void updateStepSize(State & /*state*/, double /*stepSize*/,
0121                       ConstrainedStep::Type /*stype*/) const {}
0122   void releaseStepSize(State & /*state*/,
0123                        ConstrainedStep::Type /*stype*/) const {}
0124 };
0125 
0126 struct MockNavigatorState {
0127   Surface *startSurface = nullptr;
0128   Surface *currentSurface = nullptr;
0129 };
0130 
0131 struct MockNavigator {
0132   const Surface *startSurface(const MockNavigatorState &state) const {
0133     return state.startSurface;
0134   }
0135 
0136   const Surface *currentSurface(const MockNavigatorState &state) const {
0137     return state.currentSurface;
0138   }
0139 
0140   const TrackingVolume *currentVolume(
0141       const MockNavigatorState & /*state*/) const {
0142     return nullptr;
0143   }
0144 
0145   bool endOfWorldReached(const MockNavigatorState & /*state*/) const {
0146     return false;
0147   }
0148 };
0149 
0150 struct MockPropagatorState {
0151   MockNavigatorState navigation;
0152   MockStepperState stepping;
0153   GeometryContext geoContext = GeometryContext::dangerouslyDefaultConstruct();
0154   PropagatorStage stage = PropagatorStage::invalid;
0155 
0156   struct {
0157     std::vector<std::uint32_t> constrainToVolumeIds;
0158   } options;
0159 };
0160 
0161 template <typename SurfaceSelector>
0162 struct Fixture {
0163   using Generator = std::ranlux48;
0164   using Actor = typename ActsFatras::detail::SimulationActor<
0165       Generator, MockDecay, MockInteractionList, SurfaceSelector>;
0166   using Result = typename Actor::result_type;
0167 
0168   // reference information for initial particle
0169   Barcode pid = Barcode().withVertexPrimary(12u).withParticle(3u);
0170   GenerationProcess proc = GenerationProcess::eUndefined;
0171   PdgParticle pdg = PdgParticle::eProton;
0172   double q = 1_e;
0173   double m = 1_GeV;
0174   double p = 1_GeV;
0175   double e;
0176   Generator generator;
0177   std::shared_ptr<Surface> surface;
0178   Actor actor;
0179   Result result;
0180   MockPropagatorState state;
0181   MockStepper stepper;
0182   MockNavigator navigator;
0183 
0184   Fixture(double energyLoss, std::shared_ptr<Surface> surface_)
0185       : e(std::hypot(m, p)), generator(42), surface(std::move(surface_)) {
0186     const auto particle = Particle(pid, pdg, q, m)
0187                               .setProcess(proc)
0188                               .setPosition4(1_mm, 2_mm, 3_mm, 4_ns)
0189                               .setDirection(1, 0, 0)
0190                               .setAbsoluteMomentum(p);
0191     actor.generator = &generator;
0192     actor.interactions.energyLoss = energyLoss;
0193     actor.initialParticle = particle;
0194     state.stage = PropagatorStage::postStep;
0195     state.navigation.currentSurface = surface.get();
0196     state.stepping.pos = particle.position();
0197     state.stepping.time = particle.time();
0198     state.stepping.dir = particle.direction();
0199     state.stepping.p = particle.absoluteMomentum();
0200   }
0201 };
0202 
0203 // make a surface without material.
0204 std::shared_ptr<Surface> makeEmptySurface() {
0205   std::shared_ptr<PlaneSurface> surface =
0206       CurvilinearSurface(Vector3(1, 2, 3), Vector3(1, 0, 0)).planeSurface();
0207   return surface;
0208 }
0209 
0210 // make a surface with 1% X0/L0 material.
0211 std::shared_ptr<Surface> makeMaterialSurface() {
0212   auto surface = makeEmptySurface();
0213   auto slab = makeUnitSlab();
0214   surface->assignSurfaceMaterial(
0215       std::make_shared<HomogeneousSurfaceMaterial>(slab));
0216   return surface;
0217 }
0218 
0219 BOOST_AUTO_TEST_SUITE(KernelSuite)
0220 
0221 BOOST_AUTO_TEST_CASE(HitsOnEmptySurface) {
0222   Fixture<EverySurface> f(125_MeV, makeEmptySurface());
0223 
0224   // input reference check
0225   BOOST_CHECK_EQUAL(f.actor.initialParticle.particleId(), f.pid);
0226   BOOST_CHECK_EQUAL(f.actor.initialParticle.process(), f.proc);
0227   BOOST_CHECK_EQUAL(f.actor.initialParticle.pdg(), f.pdg);
0228   BOOST_CHECK_EQUAL(f.actor.initialParticle.mass(), f.m);
0229   BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p);
0230   BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e);
0231 
0232   // call.actor: pre propagation
0233   f.state.stage = PropagatorStage::prePropagation;
0234   BOOST_CHECK(
0235       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0236           .ok());
0237 
0238   // call.actor: surface selection -> one hit, no material -> no secondary
0239   f.state.stage = PropagatorStage::postStep;
0240   BOOST_CHECK(
0241       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0242           .ok());
0243   BOOST_CHECK(f.result.isAlive);
0244   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0245   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
0246   BOOST_CHECK_EQUAL(f.result.hits.size(), 1u);
0247   BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
0248   // proper time must be non-NaN, but is zero since no time has passed
0249   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0250   // empty surfaces adds no material
0251   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
0252   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
0253   // no processes are configured, so none can be selected
0254   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0255   BOOST_CHECK_EQUAL(f.result.x0Process,
0256                     std::numeric_limits<std::size_t>::max());
0257   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0258   BOOST_CHECK_EQUAL(f.result.l0Process,
0259                     std::numeric_limits<std::size_t>::max());
0260   // check consistency between particle and stepper state
0261   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0262   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0263   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0264   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0265 
0266   // call.actor again: one more hit, still no secondary
0267   f.state.stage = PropagatorStage::postStep;
0268   BOOST_CHECK(
0269       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0270           .ok());
0271   BOOST_CHECK(f.result.isAlive);
0272   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0273   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
0274   BOOST_CHECK_EQUAL(f.result.hits.size(), 2u);
0275   BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
0276   BOOST_CHECK_EQUAL(f.result.hits[1].index(), 1u);
0277   // proper time must be non-NaN, but is zero since no time
0278   // has passed
0279   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0280   // empty surfaces adds no material
0281   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
0282   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
0283   // no processes are configured, so none can be selected
0284   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0285   BOOST_CHECK_EQUAL(f.result.x0Process,
0286                     std::numeric_limits<std::size_t>::max());
0287   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0288   BOOST_CHECK_EQUAL(f.result.l0Process,
0289                     std::numeric_limits<std::size_t>::max());
0290   // check consistency between particle and stepper state
0291   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0292   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0293   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0294   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0295 
0296   // particle identity should be the same as the initial input
0297   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0298   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0299   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0300   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0301   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0302 }
0303 
0304 BOOST_AUTO_TEST_CASE(HitsOnMaterialSurface) {
0305   Fixture<EverySurface> f(125_MeV, makeMaterialSurface());
0306 
0307   // input reference check
0308   BOOST_CHECK_EQUAL(f.actor.initialParticle.particleId(), f.pid);
0309   BOOST_CHECK_EQUAL(f.actor.initialParticle.process(), f.proc);
0310   BOOST_CHECK_EQUAL(f.actor.initialParticle.pdg(), f.pdg);
0311   BOOST_CHECK_EQUAL(f.actor.initialParticle.mass(), f.m);
0312   BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p);
0313   BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e);
0314 
0315   // call.actor: pre propagation
0316   f.state.stage = PropagatorStage::prePropagation;
0317   BOOST_CHECK(
0318       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0319           .ok());
0320 
0321   // call.actor: surface selection -> one hit, material -> one secondary
0322   f.state.stage = PropagatorStage::postStep;
0323   BOOST_CHECK(
0324       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0325           .ok());
0326   BOOST_CHECK(f.result.isAlive);
0327   CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 125_MeV, tol);
0328   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 1u);
0329   BOOST_CHECK_EQUAL(f.result.hits.size(), 1u);
0330   BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
0331   // proper time must be non-NaN, but is zero since no time
0332   // has passed
0333   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0334   // test material is a unit slab
0335   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 1);
0336   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 1);
0337   // no processes are configured, so none can be selected
0338   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0339   BOOST_CHECK_EQUAL(f.result.x0Process,
0340                     std::numeric_limits<std::size_t>::max());
0341   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0342   BOOST_CHECK_EQUAL(f.result.l0Process,
0343                     std::numeric_limits<std::size_t>::max());
0344   // check consistency between particle and stepper state
0345   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0346   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0347   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0348   CHECK_CLOSE_REL(f.state.stepping.p, f.result.particle.absoluteMomentum(),
0349                   tol);
0350 
0351   // call.actor again: one more hit, one more secondary
0352   f.state.stage = PropagatorStage::postStep;
0353   BOOST_CHECK(
0354       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0355           .ok());
0356   BOOST_CHECK(f.result.isAlive);
0357   CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 250_MeV, tol);
0358   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 2u);
0359   BOOST_CHECK_EQUAL(f.result.hits.size(), 2u);
0360   BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
0361   BOOST_CHECK_EQUAL(f.result.hits[1].index(), 1u);
0362   // proper time must be non-NaN, but is zero since no time
0363   // has passed
0364   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0365   // test material is a unit slab that was passed twice
0366   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 2);
0367   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 2);
0368   // no processes are configured, so none can be selected
0369   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0370   BOOST_CHECK_EQUAL(f.result.x0Process,
0371                     std::numeric_limits<std::size_t>::max());
0372   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0373   BOOST_CHECK_EQUAL(f.result.l0Process,
0374                     std::numeric_limits<std::size_t>::max());
0375   // check consistency between particle and stepper state
0376   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0377   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0378   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0379   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0380 
0381   // particle identity should be the same as the initial input
0382   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0383   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0384   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0385   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0386   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0387 }
0388 
0389 BOOST_AUTO_TEST_CASE(NoHitsEmptySurface) {
0390   Fixture<NoSurface> f(125_MeV, makeEmptySurface());
0391 
0392   // input reference check
0393   BOOST_CHECK_EQUAL(f.actor.initialParticle.particleId(), f.pid);
0394   BOOST_CHECK_EQUAL(f.actor.initialParticle.process(), f.proc);
0395   BOOST_CHECK_EQUAL(f.actor.initialParticle.pdg(), f.pdg);
0396   BOOST_CHECK_EQUAL(f.actor.initialParticle.mass(), f.m);
0397   BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p);
0398   BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e);
0399 
0400   // call.actor: pre propagation
0401   f.state.stage = PropagatorStage::prePropagation;
0402   BOOST_CHECK(
0403       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0404           .ok());
0405 
0406   // call.actor: no surface sel. -> no hit, no material -> no secondary
0407   f.state.stage = PropagatorStage::postStep;
0408   BOOST_CHECK(
0409       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0410           .ok());
0411   BOOST_CHECK(f.result.isAlive);
0412   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0413   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
0414   BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
0415   // proper time must be non-NaN, but is zero since no time
0416   // has passed
0417   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0418   // empty surfaces adds no material
0419   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
0420   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
0421   // no processes are configured, so none can be selected
0422   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0423   BOOST_CHECK_EQUAL(f.result.x0Process,
0424                     std::numeric_limits<std::size_t>::max());
0425   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0426   BOOST_CHECK_EQUAL(f.result.l0Process,
0427                     std::numeric_limits<std::size_t>::max());
0428   // check consistency between particle and stepper state
0429   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0430   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0431   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0432   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0433 
0434   // call.actor again: no hit, still no secondary
0435   f.state.stage = PropagatorStage::postStep;
0436   BOOST_CHECK(
0437       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0438           .ok());
0439   BOOST_CHECK(f.result.isAlive);
0440   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0441   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
0442   BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
0443   // proper time must be non-NaN, but is zero since no time
0444   // has passed
0445   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0446   // empty surfaces adds no material
0447   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
0448   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
0449   // no processes are configured, so none can be selected
0450   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0451   BOOST_CHECK_EQUAL(f.result.x0Process,
0452                     std::numeric_limits<std::size_t>::max());
0453   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0454   BOOST_CHECK_EQUAL(f.result.l0Process,
0455                     std::numeric_limits<std::size_t>::max());
0456   // check consistency between particle and stepper state
0457   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0458   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0459   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0460   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0461 
0462   // particle identity should be the same as the initial input
0463   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0464   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0465   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0466   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0467   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0468 }
0469 
0470 BOOST_AUTO_TEST_CASE(NoHitsMaterialSurface) {
0471   Fixture<NoSurface> f(125_MeV, makeMaterialSurface());
0472 
0473   // call.actor: pre propagation
0474   f.state.stage = PropagatorStage::prePropagation;
0475   BOOST_CHECK(
0476       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0477           .ok());
0478 
0479   // call.actor: no surface sel. -> no hit, material -> one secondary
0480   f.state.stage = PropagatorStage::postStep;
0481   BOOST_CHECK(
0482       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0483           .ok());
0484   BOOST_CHECK(f.result.isAlive);
0485   CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 125_MeV, tol);
0486   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 1u);
0487   BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
0488   // proper time must be non-NaN, but is zero since no time
0489   // has passed
0490   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0491   // test material is a unit slab
0492   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 1);
0493   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 1);
0494   // no processes are configured, so none can be selected
0495   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0496   BOOST_CHECK_EQUAL(f.result.x0Process,
0497                     std::numeric_limits<std::size_t>::max());
0498   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0499   BOOST_CHECK_EQUAL(f.result.l0Process,
0500                     std::numeric_limits<std::size_t>::max());
0501   // check consistency between particle and stepper state
0502   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0503   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0504   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0505   CHECK_CLOSE_REL(f.state.stepping.p, f.result.particle.absoluteMomentum(),
0506                   tol);
0507 
0508   // call.actor again: still no hit, one more secondary
0509   f.state.stage = PropagatorStage::postStep;
0510   BOOST_CHECK(
0511       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0512           .ok());
0513   BOOST_CHECK(f.result.isAlive);
0514   CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 250_MeV, tol);
0515   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 2u);
0516   BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
0517   // proper time must be non-NaN, but is zero since no time
0518   // has passed
0519   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0520   // test material is a unit slab that was passed twice
0521   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 2);
0522   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 2);
0523   // no processes are configured, so none can be selected
0524   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0525   BOOST_CHECK_EQUAL(f.result.x0Process,
0526                     std::numeric_limits<std::size_t>::max());
0527   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0528   BOOST_CHECK_EQUAL(f.result.l0Process,
0529                     std::numeric_limits<std::size_t>::max());
0530   // check consistency between particle and stepper state
0531   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0532   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0533   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0534   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0535 
0536   // particle identity should be the same as the initial input
0537   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0538   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0539   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0540   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0541   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0542 }
0543 
0544 BOOST_AUTO_TEST_CASE(Decay) {
0545   // configure no energy loss for the decay tests
0546   Fixture<NoSurface> f(0_GeV, makeEmptySurface());
0547 
0548   // inverse Lorentz factor for proper time dilation: 1/gamma = m/E
0549   const auto gammaInv = f.m / f.e;
0550 
0551   // call.actor: pre propagation
0552   f.state.stage = PropagatorStage::prePropagation;
0553   BOOST_CHECK(
0554       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0555           .ok());
0556 
0557   // first step w/ defaults leaves particle alive
0558   f.state.stage = PropagatorStage::postStep;
0559   BOOST_CHECK(
0560       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0561           .ok());
0562   BOOST_CHECK(f.result.isAlive);
0563   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0564   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0565   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0566   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0567   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0568   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0569   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0_ns);
0570 
0571   // second step w/ defaults increases proper time
0572   f.state.stage = PropagatorStage::postStep;
0573   f.state.stepping.time += 1_ns;
0574   BOOST_CHECK(
0575       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0576           .ok());
0577   BOOST_CHECK(f.result.isAlive);
0578   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0579   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0580   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0581   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0582   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0583   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0584   CHECK_CLOSE_REL(f.result.particle.properTime(), gammaInv * 1_ns, tol);
0585 
0586   // third step w/ proper time limit decays the particle
0587   f.state.stage = PropagatorStage::postStep;
0588   f.state.stepping.time += 1_ns;
0589   f.result.properTimeLimit = f.result.particle.properTime() + gammaInv * 0.5_ns;
0590   BOOST_CHECK(
0591       f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger())
0592           .ok());
0593   BOOST_CHECK(!f.result.isAlive);
0594   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0595   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0596   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0597   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0598   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0599   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0600   CHECK_CLOSE_REL(f.result.particle.properTime(), gammaInv * 2_ns, tol);
0601 }
0602 
0603 BOOST_AUTO_TEST_SUITE_END()
0604 
0605 }  // namespace ActsTests