Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DetectorNavigator.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file DetectorNavigator.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2023 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 #pragma once
10 
14 #include "Acts/Detector/Portal.hpp"
17 #include "Acts/Geometry/Layer.hpp"
22 
23 #include <iomanip>
24 #include <iterator>
25 #include <sstream>
26 #include <string>
27 
28 #include <boost/algorithm/string.hpp>
29 #include <boost/container/small_vector.hpp>
30 
31 namespace Acts {
32 namespace Experimental {
33 
35  public:
36  struct Config {
38  const Detector* detector = nullptr;
39 
42  bool resolveSensitive = true;
44  bool resolveMaterial = true;
46  bool resolvePassive = false;
47  };
48 
54  struct State : public NavigationState {
56  const Surface* startSurface = nullptr;
58  const Surface* currentSurface = nullptr;
60  const Surface* targetSurface = nullptr;
62  bool targetReached = false;
64  bool navigationBreak = false;
65  };
66 
72  std::shared_ptr<const Logger> _logger =
73  getDefaultLogger("DetectorNavigator",
75  : m_cfg{cfg}, m_logger{std::move(_logger)} {}
76 
77  State makeState(const Surface* startSurface,
78  const Surface* targetSurface) const {
79  State result;
80  result.startSurface = startSurface;
81  result.targetSurface = targetSurface;
82  return result;
83  }
84 
85  void resetState(State& state, const GeometryContext& /*geoContext*/,
86  const Vector3& /*pos*/, const Vector3& /*dir*/,
87  const Surface* /*ssurface*/,
88  const Surface* /*tsurface*/) const {
89  // Reset everything first
90  state = State();
91 
92  // TODO fill state
93  }
94 
95  const Surface* currentSurface(const State& state) const {
96  return state.currentSurface;
97  }
98 
99  const TrackingVolume* currentVolume(const State& /*state*/) const {
100  return nullptr; // TODO we do not have a tracking volume
101  }
102 
103  const IVolumeMaterial* currentVolumeMaterial(const State& state) const {
104  return state.currentVolume->volumeMaterial();
105  }
106 
107  const Surface* startSurface(const State& state) const {
108  return state.startSurface;
109  }
110 
111  const Surface* targetSurface(const State& state) const {
112  return state.targetSurface;
113  }
114 
115  bool targetReached(const State& state) const { return state.targetReached; }
116 
117  bool endOfWorldReached(State& state) const {
118  return state.currentVolume == nullptr;
119  }
120 
121  bool navigationBreak(const State& state) const {
122  return state.navigationBreak;
123  }
124 
125  void currentSurface(State& state, const Surface* surface) const {
126  state.currentSurface = surface;
127  }
128 
129  void targetReached(State& state, bool targetReached) const {
130  state.targetReached = targetReached;
131  }
132 
133  void navigationBreak(State& state, bool navigationBreak) const {
134  state.navigationBreak = navigationBreak;
135  }
136 
137  void insertExternalSurface(State& /*state*/,
138  GeometryIdentifier /*geoid*/) const {
139  // TODO what about external surfaces?
140  }
141 
151  template <typename propagator_state_t, typename stepper_t>
152  void initialize(propagator_state_t& state, const stepper_t& stepper) const {
153  ACTS_VERBOSE(volInfo(state) << posInfo(state, stepper) << "initialize");
154 
155  auto& nState = state.navigation;
156 
157  if (nState.currentDetector == nullptr) {
158  ACTS_VERBOSE("assigning detector from the config.");
159  nState.currentDetector = m_cfg.detector;
160  }
161 
162  if (nState.currentDetector == nullptr) {
163  ACTS_ERROR("panic: no detector");
164  return;
165  }
166  }
167 
178  template <typename propagator_state_t, typename stepper_t>
179  void preStep(propagator_state_t& state, const stepper_t& stepper) const {
180  ACTS_VERBOSE(volInfo(state)
181  << posInfo(state, stepper) << "Entering navigator::preStep.");
182 
183  auto& nState = state.navigation;
184  fillNavigationState(state, stepper, nState);
185 
186  if (inactive()) {
187  ACTS_VERBOSE(volInfo(state)
188  << posInfo(state, stepper) << "navigator inactive");
189  return;
190  }
191 
192  if (nState.currentVolume == nullptr) {
193  initializeTarget(state, stepper);
194  }
195 
196  if (nState.currentSurface != nullptr) {
197  ACTS_VERBOSE(volInfo(state)
198  << posInfo(state, stepper) << "stepping through surface");
199  } else if (nState.currentPortal != nullptr) {
200  ACTS_VERBOSE(volInfo(state)
201  << posInfo(state, stepper) << "stepping through portal");
202 
203  nState.surfaceCandidates.clear();
205 
206  nState.currentPortal->updateDetectorVolume(state.geoContext, nState);
207 
208  initializeTarget(state, stepper);
209  }
210 
213  // Screen output how much is left to try
214  ACTS_VERBOSE(volInfo(state)
215  << posInfo(state, stepper)
217  nState.surfaceCandidates.cend())
218  << " out of " << nState.surfaceCandidates.size()
219  << " surfaces remain to try.");
220  // Take the surface
221  const auto& c = *(nState.surfaceCandidate);
222  const auto& surface =
223  (c.surface != nullptr) ? (*c.surface) : (c.portal->surface());
224  // Screen output which surface you are on
225  ACTS_VERBOSE(volInfo(state) << posInfo(state, stepper)
226  << "next surface candidate will be "
227  << surface.geometryId());
228  // Estimate the surface status
229  bool boundaryCheck = c.boundaryCheck;
230  auto surfaceStatus = stepper.updateSurfaceStatus(
231  state.stepping, surface, state.options.direction, boundaryCheck,
232  state.options.targetTolerance, logger());
233  if (surfaceStatus == Intersection3D::Status::reachable) {
234  ACTS_VERBOSE(volInfo(state)
235  << posInfo(state, stepper)
236  << "surface reachable, step size updated to "
237  << stepper.outputStepSize(state.stepping));
238  break;
239  }
240  }
241 
242  nState.currentSurface = nullptr;
243  nState.currentPortal = nullptr;
244  }
245 
253  template <typename propagator_state_t, typename stepper_t>
254  void postStep(propagator_state_t& state, const stepper_t& stepper) const {
255  ACTS_VERBOSE(volInfo(state)
256  << posInfo(state, stepper) << "Entering navigator::postStep.");
257 
258  auto& nState = state.navigation;
259  fillNavigationState(state, stepper, nState);
260 
261  if (inactive()) {
262  ACTS_VERBOSE(volInfo(state)
263  << posInfo(state, stepper) << "navigator inactive");
264  return;
265  }
266 
267  if (nState.currentDetector == nullptr) {
268  initialize(state, stepper);
269  return;
270  }
271 
273  ACTS_VERBOSE(volInfo(state)
274  << posInfo(state, stepper)
275  << "no surface candidates - waiting for target call");
276  return;
277  }
278 
279  const Portal* nextPortal = nullptr;
280  const Surface* nextSurface = nullptr;
281  bool isPortal = false;
282  bool boundaryCheck = nState.surfaceCandidate->boundaryCheck;
283 
284  if (nState.surfaceCandidate->surface != nullptr) {
285  nextSurface = nState.surfaceCandidate->surface;
286  } else if (nState.surfaceCandidate->portal != nullptr) {
287  nextPortal = nState.surfaceCandidate->portal;
288  nextSurface = &nextPortal->surface();
289  isPortal = true;
290  } else {
291  ACTS_ERROR(volInfo(state)
292  << posInfo(state, stepper)
293  << "panic: not a surface not a portal - what is it?");
294  return;
295  }
296 
297  // TODO not sure about the boundary check
298  auto surfaceStatus = stepper.updateSurfaceStatus(
299  state.stepping, *nextSurface, state.options.direction, boundaryCheck,
300  state.options.targetTolerance, logger());
301 
302  // Check if we are at a surface
303  if (surfaceStatus == Intersection3D::Status::onSurface) {
304  ACTS_VERBOSE(volInfo(state)
305  << posInfo(state, stepper) << "landed on surface");
306 
307  if (isPortal) {
308  ACTS_VERBOSE(volInfo(state) << posInfo(state, stepper)
309  << "this is a portal, storing it.");
310 
311  nState.currentPortal = nextPortal;
312 
313  ACTS_VERBOSE(volInfo(state)
314  << posInfo(state, stepper) << "current portal set to "
316  } else {
317  ACTS_VERBOSE(volInfo(state) << posInfo(state, stepper)
318  << "this is a surface, storing it.");
319 
320  // If we are on the surface pointed at by the iterator, we can make
321  // it the current one to pass it to the other actors
322  nState.currentSurface = nextSurface;
323  ACTS_VERBOSE(volInfo(state)
324  << posInfo(state, stepper) << "current surface set to "
327  }
328  }
329  }
330 
331  private:
332  Config m_cfg;
333 
334  std::shared_ptr<const Logger> m_logger;
335 
336  template <typename propagator_state_t>
337  std::string volInfo(const propagator_state_t& state) const {
338  auto& nState = state.navigation;
339 
340  return (nState.currentVolume ? nState.currentVolume->name() : "No Volume") +
341  " | ";
342  }
343 
344  template <typename propagator_state_t, typename stepper_t>
345  std::string posInfo(const propagator_state_t& state,
346  const stepper_t& stepper) const {
347  std::stringstream ss;
348  ss << stepper.position(state.stepping).transpose();
349  ss << " | ";
350  return ss.str();
351  }
352 
353  const Logger& logger() const { return *m_logger; }
354 
359  bool inactive() const {
360  if (m_cfg.detector == nullptr) {
361  return true;
362  }
363 
364  if (!m_cfg.resolveSensitive && !m_cfg.resolveMaterial &&
365  !m_cfg.resolvePassive) {
366  return true;
367  }
368 
369  return false;
370  }
371 
389  template <typename propagator_state_t, typename stepper_t>
390  void initializeTarget(propagator_state_t& state,
391  const stepper_t& stepper) const {
392  ACTS_VERBOSE(volInfo(state)
393  << posInfo(state, stepper) << "initialize target");
394 
395  auto& nState = state.navigation;
396 
397  if (nState.currentVolume == nullptr) {
399  state.geoContext, nState.position);
400 
401  if (nState.currentVolume != nullptr) {
402  ACTS_VERBOSE(volInfo(state)
403  << posInfo(state, stepper) << "switched detector volume");
404  }
405  }
406 
407  if (nState.currentVolume == nullptr) {
408  ACTS_ERROR(volInfo(state)
409  << posInfo(state, stepper) << "panic: no current volume");
410  return;
411  }
412 
413  nState.currentVolume->updateNavigationState(state.geoContext, nState);
414 
415  // Sort properly the surface candidates
416  auto& nCandidates = nState.surfaceCandidates;
417  std::sort(nCandidates.begin(), nCandidates.end(),
418  [&](const auto& a, const auto& b) {
419  // The two path lengths
420  ActsScalar pathToA = a.objectIntersection.pathLength();
421  ActsScalar pathToB = b.objectIntersection.pathLength();
422  return pathToA < pathToB;
423  });
424  // Set the surface candidate
425  nState.surfaceCandidate = nCandidates.begin();
426  }
427 
428  template <typename propagator_state_t, typename stepper_t>
429  void fillNavigationState(propagator_state_t& state, const stepper_t& stepper,
430  NavigationState& nState) const {
431  nState.position = stepper.position(state.stepping);
432  nState.direction = stepper.direction(state.stepping);
433  nState.absMomentum = stepper.absoluteMomentum(state.stepping);
434  auto fieldResult = stepper.getField(state.stepping, nState.position);
435  if (!fieldResult.ok()) {
436  ACTS_ERROR(volInfo(state) << posInfo(state, stepper)
437  << "could not read from the magnetic field");
438  }
439  nState.magneticField = *fieldResult;
440  }
441 };
442 
443 } // namespace Experimental
444 } // namespace Acts