Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-29 07:55:48

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/Particle.hpp"
0025 #include "ActsFatras/EventData/ProcessType.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.correctEnergy(-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;
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   ProcessType proc = ProcessType::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   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0235 
0236   // call.actor: surface selection -> one hit, no material -> no secondary
0237   f.state.stage = PropagatorStage::postStep;
0238   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0239   BOOST_CHECK(f.result.isAlive);
0240   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0241   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
0242   BOOST_CHECK_EQUAL(f.result.hits.size(), 1u);
0243   BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
0244   // proper time must be non-NaN, but is zero since no time has passed
0245   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0246   // empty surfaces adds no material
0247   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
0248   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
0249   // no processes are configured, so none can be selected
0250   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0251   BOOST_CHECK_EQUAL(f.result.x0Process,
0252                     std::numeric_limits<std::size_t>::max());
0253   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0254   BOOST_CHECK_EQUAL(f.result.l0Process,
0255                     std::numeric_limits<std::size_t>::max());
0256   // check consistency between particle and stepper state
0257   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0258   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0259   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0260   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0261 
0262   // call.actor again: one more hit, still no secondary
0263   f.state.stage = PropagatorStage::postStep;
0264   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0265   BOOST_CHECK(f.result.isAlive);
0266   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0267   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
0268   BOOST_CHECK_EQUAL(f.result.hits.size(), 2u);
0269   BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
0270   BOOST_CHECK_EQUAL(f.result.hits[1].index(), 1u);
0271   // proper time must be non-NaN, but is zero since no time
0272   // has passed
0273   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0274   // empty surfaces adds no material
0275   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
0276   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
0277   // no processes are configured, so none can be selected
0278   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0279   BOOST_CHECK_EQUAL(f.result.x0Process,
0280                     std::numeric_limits<std::size_t>::max());
0281   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0282   BOOST_CHECK_EQUAL(f.result.l0Process,
0283                     std::numeric_limits<std::size_t>::max());
0284   // check consistency between particle and stepper state
0285   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0286   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0287   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0288   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0289 
0290   // particle identity should be the same as the initial input
0291   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0292   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0293   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0294   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0295   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0296 }
0297 
0298 BOOST_AUTO_TEST_CASE(HitsOnMaterialSurface) {
0299   Fixture<EverySurface> f(125_MeV, makeMaterialSurface());
0300 
0301   // input reference check
0302   BOOST_CHECK_EQUAL(f.actor.initialParticle.particleId(), f.pid);
0303   BOOST_CHECK_EQUAL(f.actor.initialParticle.process(), f.proc);
0304   BOOST_CHECK_EQUAL(f.actor.initialParticle.pdg(), f.pdg);
0305   BOOST_CHECK_EQUAL(f.actor.initialParticle.mass(), f.m);
0306   BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p);
0307   BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e);
0308 
0309   // call.actor: pre propagation
0310   f.state.stage = PropagatorStage::prePropagation;
0311   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0312 
0313   // call.actor: surface selection -> one hit, material -> one secondary
0314   f.state.stage = PropagatorStage::postStep;
0315   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0316   BOOST_CHECK(f.result.isAlive);
0317   CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 125_MeV, tol);
0318   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 1u);
0319   BOOST_CHECK_EQUAL(f.result.hits.size(), 1u);
0320   BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
0321   // proper time must be non-NaN, but is zero since no time
0322   // has passed
0323   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0324   // test material is a unit slab
0325   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 1);
0326   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 1);
0327   // no processes are configured, so none can be selected
0328   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0329   BOOST_CHECK_EQUAL(f.result.x0Process,
0330                     std::numeric_limits<std::size_t>::max());
0331   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0332   BOOST_CHECK_EQUAL(f.result.l0Process,
0333                     std::numeric_limits<std::size_t>::max());
0334   // check consistency between particle and stepper state
0335   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0336   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0337   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0338   CHECK_CLOSE_REL(f.state.stepping.p, f.result.particle.absoluteMomentum(),
0339                   tol);
0340 
0341   // call.actor again: one more hit, one more secondary
0342   f.state.stage = PropagatorStage::postStep;
0343   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0344   BOOST_CHECK(f.result.isAlive);
0345   CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 250_MeV, tol);
0346   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 2u);
0347   BOOST_CHECK_EQUAL(f.result.hits.size(), 2u);
0348   BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
0349   BOOST_CHECK_EQUAL(f.result.hits[1].index(), 1u);
0350   // proper time must be non-NaN, but is zero since no time
0351   // has passed
0352   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0353   // test material is a unit slab that was passed twice
0354   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 2);
0355   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 2);
0356   // no processes are configured, so none can be selected
0357   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0358   BOOST_CHECK_EQUAL(f.result.x0Process,
0359                     std::numeric_limits<std::size_t>::max());
0360   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0361   BOOST_CHECK_EQUAL(f.result.l0Process,
0362                     std::numeric_limits<std::size_t>::max());
0363   // check consistency between particle and stepper state
0364   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0365   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0366   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0367   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0368 
0369   // particle identity should be the same as the initial input
0370   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0371   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0372   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0373   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0374   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0375 }
0376 
0377 BOOST_AUTO_TEST_CASE(NoHitsEmptySurface) {
0378   Fixture<NoSurface> f(125_MeV, makeEmptySurface());
0379 
0380   // input reference check
0381   BOOST_CHECK_EQUAL(f.actor.initialParticle.particleId(), f.pid);
0382   BOOST_CHECK_EQUAL(f.actor.initialParticle.process(), f.proc);
0383   BOOST_CHECK_EQUAL(f.actor.initialParticle.pdg(), f.pdg);
0384   BOOST_CHECK_EQUAL(f.actor.initialParticle.mass(), f.m);
0385   BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p);
0386   BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e);
0387 
0388   // call.actor: pre propagation
0389   f.state.stage = PropagatorStage::prePropagation;
0390   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0391 
0392   // call.actor: no surface sel. -> no hit, no material -> no secondary
0393   f.state.stage = PropagatorStage::postStep;
0394   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0395   BOOST_CHECK(f.result.isAlive);
0396   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0397   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
0398   BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
0399   // proper time must be non-NaN, but is zero since no time
0400   // has passed
0401   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0402   // empty surfaces adds no material
0403   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
0404   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
0405   // no processes are configured, so none can be selected
0406   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0407   BOOST_CHECK_EQUAL(f.result.x0Process,
0408                     std::numeric_limits<std::size_t>::max());
0409   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0410   BOOST_CHECK_EQUAL(f.result.l0Process,
0411                     std::numeric_limits<std::size_t>::max());
0412   // check consistency between particle and stepper state
0413   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0414   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0415   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0416   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0417 
0418   // call.actor again: no hit, still no secondary
0419   f.state.stage = PropagatorStage::postStep;
0420   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0421   BOOST_CHECK(f.result.isAlive);
0422   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0423   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
0424   BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
0425   // proper time must be non-NaN, but is zero since no time
0426   // has passed
0427   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0428   // empty surfaces adds no material
0429   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
0430   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
0431   // no processes are configured, so none can be selected
0432   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0433   BOOST_CHECK_EQUAL(f.result.x0Process,
0434                     std::numeric_limits<std::size_t>::max());
0435   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0436   BOOST_CHECK_EQUAL(f.result.l0Process,
0437                     std::numeric_limits<std::size_t>::max());
0438   // check consistency between particle and stepper state
0439   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0440   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0441   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0442   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0443 
0444   // particle identity should be the same as the initial input
0445   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0446   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0447   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0448   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0449   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0450 }
0451 
0452 BOOST_AUTO_TEST_CASE(NoHitsMaterialSurface) {
0453   Fixture<NoSurface> f(125_MeV, makeMaterialSurface());
0454 
0455   // call.actor: pre propagation
0456   f.state.stage = PropagatorStage::prePropagation;
0457   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0458 
0459   // call.actor: no surface sel. -> no hit, material -> one secondary
0460   f.state.stage = PropagatorStage::postStep;
0461   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0462   BOOST_CHECK(f.result.isAlive);
0463   CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 125_MeV, tol);
0464   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 1u);
0465   BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
0466   // proper time must be non-NaN, but is zero since no time
0467   // has passed
0468   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0469   // test material is a unit slab
0470   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 1);
0471   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 1);
0472   // no processes are configured, so none can be selected
0473   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0474   BOOST_CHECK_EQUAL(f.result.x0Process,
0475                     std::numeric_limits<std::size_t>::max());
0476   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0477   BOOST_CHECK_EQUAL(f.result.l0Process,
0478                     std::numeric_limits<std::size_t>::max());
0479   // check consistency between particle and stepper state
0480   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0481   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0482   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0483   CHECK_CLOSE_REL(f.state.stepping.p, f.result.particle.absoluteMomentum(),
0484                   tol);
0485 
0486   // call.actor again: still no hit, one more secondary
0487   f.state.stage = PropagatorStage::postStep;
0488   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0489   BOOST_CHECK(f.result.isAlive);
0490   CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 250_MeV, tol);
0491   BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 2u);
0492   BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
0493   // proper time must be non-NaN, but is zero since no time
0494   // has passed
0495   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
0496   // test material is a unit slab that was passed twice
0497   BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 2);
0498   BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 2);
0499   // no processes are configured, so none can be selected
0500   BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
0501   BOOST_CHECK_EQUAL(f.result.x0Process,
0502                     std::numeric_limits<std::size_t>::max());
0503   BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
0504   BOOST_CHECK_EQUAL(f.result.l0Process,
0505                     std::numeric_limits<std::size_t>::max());
0506   // check consistency between particle and stepper state
0507   BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
0508   BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
0509   BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
0510   BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
0511 
0512   // particle identity should be the same as the initial input
0513   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0514   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0515   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0516   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0517   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0518 }
0519 
0520 BOOST_AUTO_TEST_CASE(Decay) {
0521   // configure no energy loss for the decay tests
0522   Fixture<NoSurface> f(0_GeV, makeEmptySurface());
0523 
0524   // inverse Lorentz factor for proper time dilation: 1/gamma = m/E
0525   const auto gammaInv = f.m / f.e;
0526 
0527   // call.actor: pre propagation
0528   f.state.stage = PropagatorStage::prePropagation;
0529   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0530 
0531   // first step w/ defaults leaves particle alive
0532   f.state.stage = PropagatorStage::postStep;
0533   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0534   BOOST_CHECK(f.result.isAlive);
0535   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0536   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0537   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0538   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0539   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0540   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0541   BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0_ns);
0542 
0543   // second step w/ defaults increases proper time
0544   f.state.stage = PropagatorStage::postStep;
0545   f.state.stepping.time += 1_ns;
0546   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0547   BOOST_CHECK(f.result.isAlive);
0548   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0549   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0550   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0551   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0552   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0553   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0554   CHECK_CLOSE_REL(f.result.particle.properTime(), gammaInv * 1_ns, tol);
0555 
0556   // third step w/ proper time limit decays the particle
0557   f.state.stage = PropagatorStage::postStep;
0558   f.state.stepping.time += 1_ns;
0559   f.result.properTimeLimit = f.result.particle.properTime() + gammaInv * 0.5_ns;
0560   f.actor.act(f.state, f.stepper, f.navigator, f.result, getDummyLogger());
0561   BOOST_CHECK(!f.result.isAlive);
0562   BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
0563   BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
0564   BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
0565   BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
0566   BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
0567   CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
0568   CHECK_CLOSE_REL(f.result.particle.properTime(), gammaInv * 2_ns, tol);
0569 }
0570 
0571 BOOST_AUTO_TEST_SUITE_END()
0572 
0573 }  // namespace ActsTests