ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
InteractorTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file InteractorTests.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2020 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 
11 #include <limits>
12 #include <random>
13 
20 
21 using namespace ActsFatras;
22 
23 namespace {
25 
26 struct MockPhysicsList {
27  double energyLoss = 0;
28 
29  template <typename generator_t>
30  bool operator()(generator_t &, const Acts::MaterialProperties &,
31  Particle &particle, std::vector<Particle> &generated) const {
32  generated.push_back(particle);
33  particle.correctEnergy(-energyLoss);
34  // break if particle is not alive anymore
35  return !particle;
36  }
37 };
38 
39 struct MockStepperState {
40  using Scalar = double;
41  using Vector3 = Acts::ActsVector<double, 3>;
42 
43  Vector3 position;
44  Scalar time;
45  Vector3 direction;
46  Scalar momentum;
47 };
48 
49 struct MockStepper {
50  using Scalar = MockStepperState::Scalar;
51  using Vector3 = MockStepperState::Vector3;
52 
53  auto position(MockStepperState &state) const { return state.position; }
54  auto time(MockStepperState &state) const { return state.time; }
55  auto direction(MockStepperState &state) const { return state.direction; }
56  auto momentum(MockStepperState &state) const { return state.momentum; }
57  void update(MockStepperState &state, const Vector3 &position,
58  const Vector3 &direction, Scalar momentum, Scalar time) {
59  state.position = position;
60  state.time = time;
61  state.direction = direction;
62  state.momentum = momentum;
63  }
64 };
65 
66 struct MockPropagatorState {
67  struct {
68  bool targetReached = false;
69  Acts::Surface *currentSurface = nullptr;
70  } navigation;
71  MockStepperState stepping;
73 };
74 
75 template <typename SurfaceSelector>
76 struct Fixture {
77  using Generator = std::ranlux48;
78  using Interactor = typename ActsFatras::Interactor<Generator, MockPhysicsList,
79  SurfaceSelector>;
81 
83  std::shared_ptr<Acts::Surface> surface;
84  Interactor interactor;
85  InteractorResult result;
86  MockPropagatorState state;
87  MockStepper stepper;
88 
89  Fixture(double energyLoss, std::shared_ptr<Acts::Surface> surface_)
90  : generator(42), surface(std::move(surface_)) {
91  // use zero-mass to simplify the math
92  const auto particle =
93  Particle(Barcode().setVertexPrimary(12u).setParticle(3u),
95  .setPosition4(1, 2, 3, 4)
96  .setDirection(1, 0, 0)
97  .setAbsMomentum(100);
98  interactor.generator = &generator;
99  interactor.physics.energyLoss = energyLoss;
100  interactor.particle = particle;
101  state.navigation.currentSurface = surface.get();
102  state.stepping.position = particle.position();
103  state.stepping.time = particle.time();
104  state.stepping.direction = particle.unitDirection();
105  state.stepping.momentum = particle.absMomentum();
106  }
107 };
108 
109 // make a surface without material.
110 std::shared_ptr<Acts::Surface> makeEmptySurface() {
111  auto surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
112  Acts::Vector3D(1, 2, 3), Acts::Vector3D(1, 0, 0));
113  return surface;
114 }
115 
116 // make a surface with material.
117 std::shared_ptr<Acts::Surface> makeMaterialSurface() {
118  auto surface = makeEmptySurface();
119  auto slab = Acts::Test::makePercentSlab();
120  surface->assignSurfaceMaterial(
121  std::make_shared<Acts::HomogeneousSurfaceMaterial>(std::move(slab)));
122  return surface;
123 }
124 } // namespace
125 
126 BOOST_AUTO_TEST_SUITE(FatrasInteractor)
127 
128 BOOST_AUTO_TEST_CASE(HitsOnEmptySurface) {
129  Fixture<EverySurface> f(0.5, makeEmptySurface());
130 
131  // call interactor: surface selection -> one hit, no material -> no secondary
132  f.interactor(f.state, f.stepper, f.result);
133  BOOST_TEST(f.result.generatedParticles.size() == 0u);
134  BOOST_TEST(f.result.hits.size() == 1u);
135  BOOST_TEST(f.result.hits[0].index() == 0u);
136 
137  // call interactor again: one more hit, still no secondary
138  f.interactor(f.state, f.stepper, f.result);
139  BOOST_TEST(f.result.generatedParticles.size() == 0u);
140  BOOST_TEST(f.result.hits.size() == 2u);
141  BOOST_TEST(f.result.hits[0].index() == 0u);
142  BOOST_TEST(f.result.hits[1].index() == 1u);
143 
144  // particle identity should be the same as the initial input
145  BOOST_TEST(f.result.particle.particleId() ==
146  f.interactor.particle.particleId());
147  BOOST_TEST(f.result.particle.process() == f.interactor.particle.process());
148  BOOST_TEST(f.result.particle.pdg() == f.interactor.particle.pdg());
149  BOOST_TEST(f.result.particle.charge() == f.interactor.particle.charge());
150  BOOST_TEST(f.result.particle.mass() == f.interactor.particle.mass());
151  // particle energy has not changed since there were no interactions
152  CHECK_CLOSE_REL(f.result.particle.energy(), f.interactor.particle.energy(),
153  eps);
154 }
155 
156 BOOST_AUTO_TEST_CASE(HitsOnMaterialSurface) {
157  Fixture<EverySurface> f(0.5, makeMaterialSurface());
158 
159  // call interactor: surface selection -> one hit, material -> one secondary
160  f.interactor(f.state, f.stepper, f.result);
161  BOOST_TEST(f.result.generatedParticles.size() == 1u);
162  BOOST_TEST(f.result.hits.size() == 1u);
163  BOOST_TEST(f.result.hits[0].index() == 0u);
164 
165  // call interactor again: one more hit, one more secondary
166  f.interactor(f.state, f.stepper, f.result);
167  BOOST_TEST(f.result.generatedParticles.size() == 2u);
168  BOOST_TEST(f.result.hits.size() == 2u);
169  BOOST_TEST(f.result.hits[0].index() == 0u);
170  BOOST_TEST(f.result.hits[1].index() == 1u);
171 
172  // particle identity should be the same as the initial input
173  BOOST_TEST(f.result.particle.particleId() ==
174  f.interactor.particle.particleId());
175  BOOST_TEST(f.result.particle.process() == f.interactor.particle.process());
176  BOOST_TEST(f.result.particle.pdg() == f.interactor.particle.pdg());
177  BOOST_TEST(f.result.particle.charge() == f.interactor.particle.charge());
178  BOOST_TEST(f.result.particle.mass() == f.interactor.particle.mass());
179  // particle energy has changed due to interactions
180  CHECK_CLOSE_REL((f.result.particle.energy() + 1),
181  f.interactor.particle.energy(), eps);
182 }
183 
184 BOOST_AUTO_TEST_CASE(NoHitsEmptySurface) {
185  Fixture<NoSurface> f(0.5, makeEmptySurface());
186 
187  // call interactor: no surface sel. -> no hit, no material -> no secondary
188  f.interactor(f.state, f.stepper, f.result);
189  BOOST_TEST(f.result.generatedParticles.size() == 0u);
190  BOOST_TEST(f.result.hits.size() == 0u);
191 
192  // call interactor again: no hit, still no secondary
193  f.interactor(f.state, f.stepper, f.result);
194  BOOST_TEST(f.result.generatedParticles.size() == 0u);
195  BOOST_TEST(f.result.hits.size() == 0u);
196 
197  // particle identity should be the same as the initial input
198  BOOST_TEST(f.result.particle.particleId() ==
199  f.interactor.particle.particleId());
200  BOOST_TEST(f.result.particle.process() == f.interactor.particle.process());
201  BOOST_TEST(f.result.particle.pdg() == f.interactor.particle.pdg());
202  BOOST_TEST(f.result.particle.charge() == f.interactor.particle.charge());
203  BOOST_TEST(f.result.particle.mass() == f.interactor.particle.mass());
204  // particle energy has not changed since there were no interactions
205  CHECK_CLOSE_REL(f.result.particle.energy(), f.interactor.particle.energy(),
206  eps);
207 }
208 
209 BOOST_AUTO_TEST_CASE(NoHitsMaterialSurface) {
210  Fixture<NoSurface> f(0.5, makeMaterialSurface());
211 
212  // call interactor: no surface sel. -> no hit, material -> one secondary
213  f.interactor(f.state, f.stepper, f.result);
214  BOOST_TEST(f.result.generatedParticles.size() == 1u);
215  BOOST_TEST(f.result.hits.size() == 0u);
216 
217  // call interactor again: still no hit, one more secondary
218  f.interactor(f.state, f.stepper, f.result);
219  BOOST_TEST(f.result.generatedParticles.size() == 2u);
220  BOOST_TEST(f.result.hits.size() == 0u);
221 
222  // particle identity should be the same as the initial input
223  BOOST_TEST(f.result.particle.particleId() ==
224  f.interactor.particle.particleId());
225  BOOST_TEST(f.result.particle.process() == f.interactor.particle.process());
226  BOOST_TEST(f.result.particle.pdg() == f.interactor.particle.pdg());
227  BOOST_TEST(f.result.particle.charge() == f.interactor.particle.charge());
228  BOOST_TEST(f.result.particle.mass() == f.interactor.particle.mass());
229  // particle energy has changed due to interactions
230  CHECK_CLOSE_REL((f.result.particle.energy() + 1),
231  f.interactor.particle.energy(), eps);
232 }
233 
234 BOOST_AUTO_TEST_SUITE_END()