Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:11:39

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