Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SimulationActorTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file SimulationActorTests.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #include <boost/test/unit_test.hpp>
10 
28 
29 #include <array>
30 #include <cmath>
31 #include <cstddef>
32 #include <cstdint>
33 #include <limits>
34 #include <memory>
35 #include <random>
36 #include <utility>
37 #include <vector>
38 
39 using namespace Acts::UnitLiterals;
40 using namespace ActsFatras;
41 
42 namespace {
43 
44 constexpr auto tol = 4 * std::numeric_limits<Particle::Scalar>::epsilon();
45 constexpr auto inf = std::numeric_limits<Particle::Scalar>::infinity();
46 
47 struct MockDecay {
48  Particle::Scalar properTimeLimit = inf;
49 
50  template <typename generator_t>
51  constexpr Particle::Scalar generateProperTimeLimit(
52  generator_t & /*generator*/, const Particle &particle) const {
53  return particle.properTime() + properTimeLimit;
54  }
55  template <typename generator_t>
56  constexpr std::array<Particle, 0> run(generator_t & /*generator*/,
57  const Particle & /*particle*/) const {
58  return {};
59  }
60 };
61 
62 struct MockInteractionList {
63  struct Selection {
64  double x0Limit = std::numeric_limits<double>::infinity();
65  double l0Limit = std::numeric_limits<double>::infinity();
66  size_t x0Process = SIZE_MAX;
67  size_t l0Process = SIZE_MAX;
68  };
69 
70  double energyLoss = 0;
71 
72  template <typename generator_t>
73  bool runContinuous(generator_t & /*generator*/,
74  const Acts::MaterialSlab & /*slab*/, Particle &particle,
75  std::vector<Particle> &generated) const {
76  generated.push_back(particle);
77  particle.correctEnergy(-energyLoss);
78  // break if particle is not alive anymore
79  return !particle.isAlive();
80  }
81 
82  template <typename generator_t>
83  Selection armPointLike(generator_t & /*generator*/,
84  const Particle & /*particle*/) const {
85  return {};
86  }
87 
88  template <typename generator_t>
89  bool runPointLike(generator_t & /*generator*/, size_t /*processIndex*/,
90  Particle & /*particle*/,
91  std::vector<Particle> & /*generated*/) const {
92  return false;
93  }
94 };
95 
96 struct MockStepperState {
97  using Scalar = Acts::ActsScalar;
99 
100  Vector3 pos = Vector3::Zero();
101  Scalar time = 0;
102  Vector3 dir = Vector3::Zero();
103  Scalar p = 0;
104 };
105 
106 struct MockStepper {
107  using State = MockStepperState;
110 
111  auto position(const State &state) const { return state.pos; }
112  auto time(const State &state) const { return state.time; }
113  auto direction(const State &state) const { return state.dir; }
114  auto absoluteMomentum(const State &state) const { return state.p; }
115  void update(State &state, const Vector3 &pos, const Vector3 &dir, Scalar qop,
116  Scalar time) {
117  state.pos = pos;
118  state.time = time;
119  state.dir = dir;
120  state.p = 1 / qop;
121  }
122  void setStepSize(State & /*state*/, double /*stepSize*/,
123  Acts::ConstrainedStep::Type /*stype*/) const {}
124 };
125 
126 struct MockNavigatorState {
127  bool targetReached = false;
128  Acts::Surface *startSurface = nullptr;
129  Acts::Surface *currentSurface = nullptr;
130 };
131 
132 struct MockNavigator {
133  bool targetReached(const MockNavigatorState &state) const {
134  return state.targetReached;
135  }
136 
137  const Acts::Surface *startSurface(const MockNavigatorState &state) const {
138  return state.startSurface;
139  }
140 
141  const Acts::Surface *currentSurface(const MockNavigatorState &state) const {
142  return state.currentSurface;
143  }
144 };
145 
146 struct MockPropagatorState {
147  MockNavigatorState navigation;
148  MockStepperState stepping;
150 };
151 
152 template <typename SurfaceSelector>
153 struct Fixture {
154  using Generator = std::ranlux48;
155  using Actor = typename ActsFatras::detail::SimulationActor<
156  Generator, MockDecay, MockInteractionList, SurfaceSelector>;
157  using Result = typename Actor::result_type;
158 
159  // reference information for initial particle
161  ProcessType proc = ProcessType::eUndefined;
163  Particle::Scalar q = 1_e;
164  Particle::Scalar m = 1_GeV;
165  Particle::Scalar p = 1_GeV;
168  std::shared_ptr<Acts::Surface> surface;
169  Actor actor;
170  Result result;
171  MockPropagatorState state;
172  MockStepper stepper;
173  MockNavigator navigator;
174 
175  Fixture(double energyLoss, std::shared_ptr<Acts::Surface> surface_)
176  : e(std::hypot(m, p)), generator(42), surface(std::move(surface_)) {
177  const auto particle = Particle(pid, pdg, q, m)
178  .setProcess(proc)
179  .setPosition4(1_mm, 2_mm, 3_mm, 4_ns)
180  .setDirection(1, 0, 0)
182  actor.generator = &generator;
183  actor.interactions.energyLoss = energyLoss;
184  actor.initialParticle = particle;
185  state.navigation.currentSurface = surface.get();
186  state.stepping.pos = particle.position();
187  state.stepping.time = particle.time();
188  state.stepping.dir = particle.direction();
189  state.stepping.p = particle.absoluteMomentum();
190  }
191 };
192 
193 // make a surface without material.
194 std::shared_ptr<Acts::Surface> makeEmptySurface() {
195  auto surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
196  Acts::Vector3(1, 2, 3), Acts::Vector3(1, 0, 0));
197  return surface;
198 }
199 
200 // make a surface with 1% X0/L0 material.
201 std::shared_ptr<Acts::Surface> makeMaterialSurface() {
202  auto surface = makeEmptySurface();
203  auto slab = Acts::Test::makeUnitSlab();
204  surface->assignSurfaceMaterial(
205  std::make_shared<Acts::HomogeneousSurfaceMaterial>(slab));
206  return surface;
207 }
208 
209 } // namespace
210 
211 BOOST_AUTO_TEST_SUITE(FatrasSimulationActor)
212 
213 BOOST_AUTO_TEST_CASE(HitsOnEmptySurface) {
214  Fixture<EverySurface> f(125_MeV, makeEmptySurface());
215 
216  // input reference check
217  BOOST_CHECK_EQUAL(f.actor.initialParticle.particleId(), f.pid);
218  BOOST_CHECK_EQUAL(f.actor.initialParticle.process(), f.proc);
219  BOOST_CHECK_EQUAL(f.actor.initialParticle.pdg(), f.pdg);
220  BOOST_CHECK_EQUAL(f.actor.initialParticle.mass(), f.m);
221  BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p);
222  BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e);
223 
224  // call.actor: surface selection -> one hit, no material -> no secondary
225  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
226  BOOST_CHECK(f.result.isAlive);
227  CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
228  BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
229  BOOST_CHECK_EQUAL(f.result.hits.size(), 1u);
230  BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
231  // proper time must be non-NaN, but is zero since no time has passed
232  BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
233  // empty surfaces adds no material
234  BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
235  BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
236  // no processes are configured, so none can be selected
237  BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
238  BOOST_CHECK_EQUAL(f.result.x0Process, SIZE_MAX);
239  BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
240  BOOST_CHECK_EQUAL(f.result.l0Process, SIZE_MAX);
241  // check consistency between particle and stepper state
242  BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
243  BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
244  BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
245  BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
246 
247  // call.actor again: one more hit, still no secondary
248  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
249  BOOST_CHECK(f.result.isAlive);
250  CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
251  BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
252  BOOST_CHECK_EQUAL(f.result.hits.size(), 2u);
253  BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
254  BOOST_CHECK_EQUAL(f.result.hits[1].index(), 1u);
255  // proper time must be non-NaN, but is zero since no time
256  // has passed
257  BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
258  // empty surfaces adds no material
259  BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
260  BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
261  // no processes are configured, so none can be selected
262  BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
263  BOOST_CHECK_EQUAL(f.result.x0Process, SIZE_MAX);
264  BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
265  BOOST_CHECK_EQUAL(f.result.l0Process, SIZE_MAX);
266  // check consistency between particle and stepper state
267  BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
268  BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
269  BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
270  BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
271 
272  // particle identity should be the same as the initial input
273  BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
274  BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
275  BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
276  BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
277  BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
278 }
279 
280 BOOST_AUTO_TEST_CASE(HitsOnMaterialSurface) {
281  Fixture<EverySurface> f(125_MeV, makeMaterialSurface());
282 
283  // input reference check
284  BOOST_CHECK_EQUAL(f.actor.initialParticle.particleId(), f.pid);
285  BOOST_CHECK_EQUAL(f.actor.initialParticle.process(), f.proc);
286  BOOST_CHECK_EQUAL(f.actor.initialParticle.pdg(), f.pdg);
287  BOOST_CHECK_EQUAL(f.actor.initialParticle.mass(), f.m);
288  BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p);
289  BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e);
290 
291  // call.actor: surface selection -> one hit, material -> one secondary
292  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
293  BOOST_CHECK(f.result.isAlive);
294  CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 125_MeV, tol);
295  BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 1u);
296  BOOST_CHECK_EQUAL(f.result.hits.size(), 1u);
297  BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
298  // proper time must be non-NaN, but is zero since no time
299  // has passed
300  BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
301  // test material is a unit slab
302  BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 1);
303  BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 1);
304  // no processes are configured, so none can be selected
305  BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
306  BOOST_CHECK_EQUAL(f.result.x0Process, SIZE_MAX);
307  BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
308  BOOST_CHECK_EQUAL(f.result.l0Process, SIZE_MAX);
309  // check consistency between particle and stepper state
310  BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
311  BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
312  BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
313  CHECK_CLOSE_REL(f.state.stepping.p, f.result.particle.absoluteMomentum(),
314  tol);
315 
316  // call.actor again: one more hit, one more secondary
317  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
318  BOOST_CHECK(f.result.isAlive);
319  CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 250_MeV, tol);
320  BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 2u);
321  BOOST_CHECK_EQUAL(f.result.hits.size(), 2u);
322  BOOST_CHECK_EQUAL(f.result.hits[0].index(), 0u);
323  BOOST_CHECK_EQUAL(f.result.hits[1].index(), 1u);
324  // proper time must be non-NaN, but is zero since no time
325  // has passed
326  BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
327  // test material is a unit slab that was passed twice
328  BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 2);
329  BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 2);
330  // no processes are configured, so none can be selected
331  BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
332  BOOST_CHECK_EQUAL(f.result.x0Process, SIZE_MAX);
333  BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
334  BOOST_CHECK_EQUAL(f.result.l0Process, SIZE_MAX);
335  // check consistency between particle and stepper state
336  BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
337  BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
338  BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
339  BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
340 
341  // particle identity should be the same as the initial input
342  BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
343  BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
344  BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
345  BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
346  BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
347 }
348 
349 BOOST_AUTO_TEST_CASE(NoHitsEmptySurface) {
350  Fixture<NoSurface> f(125_MeV, makeEmptySurface());
351 
352  // input reference check
353  BOOST_CHECK_EQUAL(f.actor.initialParticle.particleId(), f.pid);
354  BOOST_CHECK_EQUAL(f.actor.initialParticle.process(), f.proc);
355  BOOST_CHECK_EQUAL(f.actor.initialParticle.pdg(), f.pdg);
356  BOOST_CHECK_EQUAL(f.actor.initialParticle.mass(), f.m);
357  BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p);
358  BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e);
359 
360  // call.actor: no surface sel. -> no hit, no material -> no secondary
361  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
362  BOOST_CHECK(f.result.isAlive);
363  CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
364  BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
365  BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
366  // proper time must be non-NaN, but is zero since no time
367  // has passed
368  BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
369  // empty surfaces adds no material
370  BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
371  BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
372  // no processes are configured, so none can be selected
373  BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
374  BOOST_CHECK_EQUAL(f.result.x0Process, SIZE_MAX);
375  BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
376  BOOST_CHECK_EQUAL(f.result.l0Process, SIZE_MAX);
377  // check consistency between particle and stepper state
378  BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
379  BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
380  BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
381  BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
382 
383  // call.actor again: no hit, still no secondary
384  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
385  BOOST_CHECK(f.result.isAlive);
386  CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
387  BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 0u);
388  BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
389  // proper time must be non-NaN, but is zero since no time
390  // has passed
391  BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
392  // empty surfaces adds no material
393  BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 0);
394  BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 0);
395  // no processes are configured, so none can be selected
396  BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
397  BOOST_CHECK_EQUAL(f.result.x0Process, SIZE_MAX);
398  BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
399  BOOST_CHECK_EQUAL(f.result.l0Process, SIZE_MAX);
400  // check consistency between particle and stepper state
401  BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
402  BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
403  BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
404  BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
405 
406  // particle identity should be the same as the initial input
407  BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
408  BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
409  BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
410  BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
411  BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
412 }
413 
414 BOOST_AUTO_TEST_CASE(NoHitsMaterialSurface) {
415  Fixture<NoSurface> f(125_MeV, makeMaterialSurface());
416 
417  // call.actor: no surface sel. -> no hit, material -> one secondary
418  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
419  BOOST_CHECK(f.result.isAlive);
420  CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 125_MeV, tol);
421  BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 1u);
422  BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
423  // proper time must be non-NaN, but is zero since no time
424  // has passed
425  BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
426  // test material is a unit slab
427  BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 1);
428  BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 1);
429  // no processes are configured, so none can be selected
430  BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
431  BOOST_CHECK_EQUAL(f.result.x0Process, SIZE_MAX);
432  BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
433  BOOST_CHECK_EQUAL(f.result.l0Process, SIZE_MAX);
434  // check consistency between particle and stepper state
435  BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
436  BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
437  BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
438  CHECK_CLOSE_REL(f.state.stepping.p, f.result.particle.absoluteMomentum(),
439  tol);
440 
441  // call.actor again: still no hit, one more secondary
442  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
443  BOOST_CHECK(f.result.isAlive);
444  CHECK_CLOSE_REL(f.result.particle.energy(), f.e - 250_MeV, tol);
445  BOOST_CHECK_EQUAL(f.result.generatedParticles.size(), 2u);
446  BOOST_CHECK_EQUAL(f.result.hits.size(), 0u);
447  // proper time must be non-NaN, but is zero since no time
448  // has passed
449  BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0);
450  // test material is a unit slab that was passed twice
451  BOOST_CHECK_EQUAL(f.result.particle.pathInX0(), 2);
452  BOOST_CHECK_EQUAL(f.result.particle.pathInL0(), 2);
453  // no processes are configured, so none can be selected
454  BOOST_CHECK_EQUAL(f.result.x0Limit, inf);
455  BOOST_CHECK_EQUAL(f.result.x0Process, SIZE_MAX);
456  BOOST_CHECK_EQUAL(f.result.l0Limit, inf);
457  BOOST_CHECK_EQUAL(f.result.l0Process, SIZE_MAX);
458  // check consistency between particle and stepper state
459  BOOST_CHECK_EQUAL(f.state.stepping.pos, f.result.particle.position());
460  BOOST_CHECK_EQUAL(f.state.stepping.time, f.result.particle.time());
461  BOOST_CHECK_EQUAL(f.state.stepping.dir, f.result.particle.direction());
462  BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum());
463 
464  // particle identity should be the same as the initial input
465  BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
466  BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
467  BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
468  BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
469  BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
470 }
471 
473  // configure no energy loss for the decay tests
474  Fixture<NoSurface> f(0_GeV, makeEmptySurface());
475 
476  // inverse Lorentz factor for proper time dilation: 1/gamma = m/E
477  const auto gammaInv = f.m / f.e;
478 
479  // first step w/ defaults leaves particle alive
480  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
481  BOOST_CHECK(f.result.isAlive);
482  BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
483  BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
484  BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
485  BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
486  BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
487  CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
488  BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0_ns);
489 
490  // second step w/ defaults increases proper time
491  f.state.stepping.time += 1_ns;
492  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
493  BOOST_CHECK(f.result.isAlive);
494  BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
495  BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
496  BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
497  BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
498  BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
499  CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
500  CHECK_CLOSE_REL(f.result.particle.properTime(), gammaInv * 1_ns, tol);
501 
502  // third step w/ proper time limit decays the particle
503  f.state.stepping.time += 1_ns;
504  f.result.properTimeLimit = f.result.particle.properTime() + gammaInv * 0.5_ns;
505  f.actor(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger());
506  BOOST_CHECK(not f.result.isAlive);
507  BOOST_CHECK_EQUAL(f.result.particle.particleId(), f.pid);
508  BOOST_CHECK_EQUAL(f.result.particle.process(), f.proc);
509  BOOST_CHECK_EQUAL(f.result.particle.pdg(), f.pdg);
510  BOOST_CHECK_EQUAL(f.result.particle.charge(), f.q);
511  BOOST_CHECK_EQUAL(f.result.particle.mass(), f.m);
512  CHECK_CLOSE_REL(f.result.particle.energy(), f.e, tol);
513  CHECK_CLOSE_REL(f.result.particle.properTime(), gammaInv * 2_ns, tol);
514 }
515 
516 BOOST_AUTO_TEST_SUITE_END()