Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GsfActor.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file GsfActor.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2022 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 
25 #include "Acts/Utilities/Zip.hpp"
26 
27 #include <ios>
28 #include <map>
29 #include <numeric>
30 
31 namespace Acts {
32 namespace detail {
33 
34 template <typename traj_t>
35 struct GsfResult {
37  traj_t* fittedStates{nullptr};
38 
41 
45 
48  std::optional<MultiComponentBoundTrackParameters> lastMeasurementState;
49 
51  std::size_t measurementStates = 0;
52  std::size_t measurementHoles = 0;
53  std::size_t processedStates = 0;
54 
55  std::vector<const Acts::Surface*> visitedSurfaces;
56  std::vector<const Acts::Surface*> surfacesVisitedBwdAgain;
57 
58  std::size_t nInvalidBetheHeitler = 0;
59 
60  // Propagate potential errors to the outside
62 
63  // Internal: component cache to avoid reallocation
64  std::vector<GsfComponent> componentCache;
65 };
66 
68 template <typename bethe_heitler_approx_t, typename traj_t>
69 struct GsfActor {
71  GsfActor() = default;
72 
74 
77 
78  // Actor configuration
79  struct Config {
81  std::size_t maxComponents = 16;
82 
84  const std::map<GeometryIdentifier, SourceLink>* inputMeasurements = nullptr;
85 
88  const bethe_heitler_approx_t* bethe_heitler_approx = nullptr;
89 
91  bool multipleScattering = true;
92 
94  double weightCutoff = 1.0e-4;
95 
100 
102  bool abortOnError = false;
103 
106  std::optional<std::size_t> numberMeasurements;
107 
110 
114  bool inReversePass = false;
115 
117  MixtureReductionMethod reductionMethod = MixtureReductionMethod::eMaxWeight;
118 
119  const Logger* logger{nullptr};
120 
123 
124  } m_cfg;
125 
126  const Logger& logger() const { return *m_cfg.logger; }
127 
129  traj_t traj;
130  std::vector<MultiTrajectoryTraits::IndexType> tips;
131  std::map<MultiTrajectoryTraits::IndexType, double> weights;
132  };
133 
143  template <typename propagator_state_t, typename stepper_t,
144  typename navigator_t>
145  void operator()(propagator_state_t& state, const stepper_t& stepper,
146  const navigator_t& navigator, result_type& result,
147  const Logger& /*logger*/) const {
148  assert(result.fittedStates && "No MultiTrajectory set");
149 
150  // Return is we found an error earlier
151  if (not result.result.ok()) {
152  ACTS_WARNING("result.result not ok, return!")
153  return;
154  }
155 
156  // Set error or abort utility
157  auto setErrorOrAbort = [&](auto error) {
158  if (m_cfg.abortOnError) {
159  std::abort();
160  } else {
161  result.result = error;
162  }
163  };
164 
165  // Prints some VERBOSE things and performs some asserts. Can be removed
166  // without change of behaviour
167  const detail::ScopedGsfInfoPrinterAndChecker printer(state, stepper,
168  navigator, logger());
169 
170  // We only need to do something if we are on a surface
171  if (not navigator.currentSurface(state.navigation)) {
172  return;
173  }
174 
175  const auto& surface = *navigator.currentSurface(state.navigation);
176  ACTS_VERBOSE("Step is at surface " << surface.geometryId());
177 
178  // All components must be normalized at the beginning here, otherwise the
179  // stepper misbehaves
180  [[maybe_unused]] auto stepperComponents =
181  stepper.constComponentIterable(state.stepping);
183  stepperComponents, [](const auto& cmp) { return cmp.weight(); }));
184 
185  // All components must have status "on surface". It is however possible,
186  // that currentSurface is nullptr and all components are "on surface" (e.g.,
187  // for surfaces excluded from the navigation)
188  using Status = Acts::Intersection3D::Status;
189  assert(std::all_of(
190  stepperComponents.begin(), stepperComponents.end(),
191  [](const auto& cmp) { return cmp.status() == Status::onSurface; }));
192 
193  // Early return if we already were on this surface TODO why is this
194  // necessary
195  const bool visited =
196  std::find(result.visitedSurfaces.begin(), result.visitedSurfaces.end(),
197  &surface) != result.visitedSurfaces.end();
198 
199  if (visited) {
200  ACTS_VERBOSE("Already visited surface, return");
201  return;
202  }
203 
204  result.visitedSurfaces.push_back(&surface);
205 
206  // Check what we have on this surface
207  const auto found_source_link =
208  m_cfg.inputMeasurements->find(surface.geometryId());
209  const bool haveMaterial =
210  navigator.currentSurface(state.navigation)->surfaceMaterial() &&
212  const bool haveMeasurement =
213  found_source_link != m_cfg.inputMeasurements->end();
214 
215  ACTS_VERBOSE(std::boolalpha << "haveMaterial " << haveMaterial
216  << ", haveMeasurement: " << haveMeasurement);
217 
219  // The Core Algorithm
221 
222  // Early return if nothing happens
223  if (not haveMaterial && not haveMeasurement) {
224  // No hole before first measurement
225  if (result.processedStates > 0 && surface.associatedDetectorElement()) {
226  TemporaryStates tmpStates;
227  noMeasurementUpdate(state, stepper, navigator, result, tmpStates, true);
228  }
229  return;
230  }
231 
232  for (auto cmp : stepper.componentIterable(state.stepping)) {
233  auto singleState = cmp.singleState(state);
234  cmp.singleStepper(stepper).transportCovarianceToBound(
235  singleState.stepping, surface);
236  }
237 
238  if (haveMaterial) {
239  if (haveMeasurement) {
240  applyMultipleScattering(state, stepper, navigator,
241  MaterialUpdateStage::PreUpdate);
242  } else {
243  applyMultipleScattering(state, stepper, navigator,
244  MaterialUpdateStage::FullUpdate);
245  }
246  }
247 
248  // We do not need the component cache here, we can just update our stepper
249  // state with the filtered components.
250  // NOTE because of early return before we know that we have a measurement
251  if (not haveMaterial) {
252  TemporaryStates tmpStates;
253 
254  auto res = kalmanUpdate(state, stepper, navigator, result, tmpStates,
255  found_source_link->second);
256 
257  if (not res.ok()) {
258  setErrorOrAbort(res.error());
259  return;
260  }
261 
262  updateStepper(state, stepper, tmpStates);
263  }
264  // We have material, we thus need a component cache since we will
265  // convolute the components and later reduce them again before updating
266  // the stepper
267  else {
268  TemporaryStates tmpStates;
269  Result<void> res;
270 
271  if (haveMeasurement) {
272  res = kalmanUpdate(state, stepper, navigator, result, tmpStates,
273  found_source_link->second);
274  } else {
275  res = noMeasurementUpdate(state, stepper, navigator, result, tmpStates,
276  false);
277  }
278 
279  if (not res.ok()) {
280  setErrorOrAbort(res.error());
281  return;
282  }
283 
284  // Reuse memory over all calls to the Actor in a single propagation
285  std::vector<ComponentCache>& componentCache = result.componentCache;
286  componentCache.clear();
287 
288  convoluteComponents(state, stepper, navigator, tmpStates, componentCache,
289  result.nInvalidBetheHeitler);
290 
291  if (componentCache.empty()) {
292  ACTS_WARNING(
293  "No components left after applying energy loss. "
294  "Is the weight cutoff "
295  << m_cfg.weightCutoff << " too high?");
296  ACTS_WARNING("Return to propagator without applying energy loss");
297  return;
298  }
299 
300  // reduce component number
301  const auto finalCmpNumber = std::min(
302  static_cast<std::size_t>(stepper.maxComponents), m_cfg.maxComponents);
303  m_cfg.extensions.mixtureReducer(componentCache, finalCmpNumber, surface);
304 
305  removeLowWeightComponents(componentCache);
306 
307  updateStepper(state, stepper, navigator, componentCache);
308  }
309 
310  // If we have only done preUpdate before, now do postUpdate
311  if (haveMaterial && haveMeasurement) {
312  applyMultipleScattering(state, stepper, navigator,
313  MaterialUpdateStage::PostUpdate);
314  }
315 
316  // Break the navigation if we found all measurements
319  navigator.targetReached(state.navigation, true);
320  }
321  }
322 
323  template <typename propagator_state_t, typename stepper_t,
324  typename navigator_t>
325  void convoluteComponents(propagator_state_t& state, const stepper_t& stepper,
326  const navigator_t& navigator,
327  const TemporaryStates& tmpStates,
328  std::vector<ComponentCache>& componentCache,
329  std::size_t& nInvalidBetheHeitler) const {
330  auto cmps = stepper.componentIterable(state.stepping);
331  for (auto [idx, cmp] : zip(tmpStates.tips, cmps)) {
332  auto proxy = tmpStates.traj.getTrackState(idx);
333 
334  BoundTrackParameters bound(proxy.referenceSurface().getSharedPtr(),
335  proxy.filtered(), proxy.filteredCovariance(),
336  stepper.particleHypothesis(state.stepping));
337 
338  applyBetheHeitler(state, navigator, bound, tmpStates.weights.at(idx),
339  componentCache, nInvalidBetheHeitler);
340  }
341  }
342 
343  template <typename propagator_state_t, typename navigator_t>
344  void applyBetheHeitler(const propagator_state_t& state,
345  const navigator_t& navigator,
346  const BoundTrackParameters& old_bound,
347  const double old_weight,
348  std::vector<ComponentCache>& componentCaches,
349  std::size_t& nInvalidBetheHeitler) const {
350  const auto& surface = *navigator.currentSurface(state.navigation);
351  const auto p_prev = old_bound.absoluteMomentum();
352 
353  // Evaluate material slab
354  auto slab = surface.surfaceMaterial()->materialSlab(
355  old_bound.position(state.stepping.geoContext), state.options.direction,
356  MaterialUpdateStage::FullUpdate);
357 
358  auto pathCorrection = surface.pathCorrection(
359  state.stepping.geoContext,
360  old_bound.position(state.stepping.geoContext), old_bound.direction());
361  slab.scaleThickness(pathCorrection);
362 
363  // Emit a warning if the approximation is not valid for this x/x0
364  if (not m_cfg.bethe_heitler_approx->validXOverX0(slab.thicknessInX0())) {
365  ++nInvalidBetheHeitler;
366  ACTS_DEBUG(
367  "Bethe-Heitler approximation encountered invalid value for x/x0="
368  << slab.thicknessInX0() << " at surface " << surface.geometryId());
369  }
370 
371  // Get the mixture
372  const auto mixture =
373  m_cfg.bethe_heitler_approx->mixture(slab.thicknessInX0());
374 
375  // Create all possible new components
376  for (const auto& gaussian : mixture) {
377  // Here we combine the new child weight with the parent weight.
378  // However, this must be later re-adjusted
379  const auto new_weight = gaussian.weight * old_weight;
380 
381  if (new_weight < m_cfg.weightCutoff) {
382  ACTS_VERBOSE("Skip component with weight " << new_weight);
383  continue;
384  }
385 
386  if (gaussian.mean < 1.e-8) {
387  ACTS_WARNING("Skip component with gaussian " << gaussian.mean << " +- "
388  << gaussian.var);
389  continue;
390  }
391 
392  // compute delta p from mixture and update parameters
393  auto new_pars = old_bound.parameters();
394 
395  const auto delta_p = [&]() {
396  if (state.options.direction == Direction::Forward) {
397  return p_prev * (gaussian.mean - 1.);
398  } else {
399  return p_prev * (1. / gaussian.mean - 1.);
400  }
401  }();
402 
403  assert(p_prev + delta_p > 0. && "new momentum must be > 0");
404  new_pars[eBoundQOverP] = old_bound.charge() / (p_prev + delta_p);
405 
406  // compute inverse variance of p from mixture and update covariance
407  auto new_cov = old_bound.covariance().value();
408 
409  const auto varInvP = [&]() {
410  if (state.options.direction == Direction::Forward) {
411  const auto f = 1. / (p_prev * gaussian.mean);
412  return f * f * gaussian.var;
413  } else {
414  return gaussian.var / (p_prev * p_prev);
415  }
416  }();
417 
418  new_cov(eBoundQOverP, eBoundQOverP) += varInvP;
419  assert(std::isfinite(new_cov(eBoundQOverP, eBoundQOverP)) &&
420  "new cov not finite");
421 
422  // Set the remaining things and push to vector
423  componentCaches.push_back({new_weight, new_pars, new_cov});
424  }
425  }
426 
431  void removeLowWeightComponents(std::vector<ComponentCache>& cmps) const {
432  auto proj = [](auto& cmp) -> double& { return cmp.weight; };
433 
435 
436  auto new_end = std::remove_if(cmps.begin(), cmps.end(), [&](auto& cmp) {
437  return proj(cmp) < m_cfg.weightCutoff;
438  });
439 
440  // In case we would remove all components, keep only the largest
441  if (std::distance(cmps.begin(), new_end) == 0) {
442  cmps = {*std::max_element(
443  cmps.begin(), cmps.end(),
444  [&](auto& a, auto& b) { return proj(a) < proj(b); })};
445  cmps.front().weight = 1.0;
446  } else {
447  cmps.erase(new_end, cmps.end());
449  }
450  }
451 
453  template <typename propagator_state_t, typename stepper_t>
454  void updateStepper(propagator_state_t& state, const stepper_t& stepper,
455  const TemporaryStates& tmpStates) const {
456  auto cmps = stepper.componentIterable(state.stepping);
457 
458  for (auto [idx, cmp] : zip(tmpStates.tips, cmps)) {
459  // we set ignored components to missed, so we can remove them after
460  // the loop
461  if (tmpStates.weights.at(idx) < m_cfg.weightCutoff) {
462  cmp.status() = Intersection3D::Status::missed;
463  continue;
464  }
465 
466  auto proxy = tmpStates.traj.getTrackState(idx);
467 
468  cmp.pars() =
469  MultiTrajectoryHelpers::freeFiltered(state.options.geoContext, proxy);
470  cmp.cov() = proxy.filteredCovariance();
471  cmp.weight() = tmpStates.weights.at(idx);
472  }
473 
474  stepper.removeMissedComponents(state.stepping);
475 
476  // TODO we have two normalization passes here now, this can probably be
477  // optimized
479  [&](auto cmp) -> double& { return cmp.weight(); });
480  }
481 
483  template <typename propagator_state_t, typename stepper_t,
484  typename navigator_t>
485  void updateStepper(propagator_state_t& state, const stepper_t& stepper,
486  const navigator_t& navigator,
487  const std::vector<ComponentCache>& componentCache) const {
488  const auto& surface = *navigator.currentSurface(state.navigation);
489 
490  // Clear components before adding new ones
491  stepper.clearComponents(state.stepping);
492 
493  // Finally loop over components
494  for (const auto& [weight, pars, cov] : componentCache) {
495  // Add the component to the stepper
496  BoundTrackParameters bound(surface.getSharedPtr(), pars, cov,
497  stepper.particleHypothesis(state.stepping));
498 
499  auto res = stepper.addComponent(state.stepping, std::move(bound), weight);
500 
501  if (!res.ok()) {
502  ACTS_ERROR("Error adding component to MultiStepper");
503  continue;
504  }
505 
506  auto& cmp = *res;
507  cmp.jacToGlobal() = surface.boundToFreeJacobian(state.geoContext, pars);
508  cmp.pathAccumulated() = state.stepping.pathAccumulated;
509  cmp.jacobian() = Acts::BoundMatrix::Identity();
510  cmp.derivative() = Acts::FreeVector::Zero();
511  cmp.jacTransport() = Acts::FreeMatrix::Identity();
512  }
513  }
514 
517  template <typename propagator_state_t, typename stepper_t,
518  typename navigator_t>
519  Result<void> kalmanUpdate(propagator_state_t& state, const stepper_t& stepper,
520  const navigator_t& navigator, result_type& result,
521  TemporaryStates& tmpStates,
522  const SourceLink& source_link) const {
523  const auto& surface = *navigator.currentSurface(state.navigation);
524 
525  // Boolean flag, to distinguish measurement and outlier states. This flag
526  // is only modified by the valid-measurement-branch, so only if there
527  // isn't any valid measurement state, the flag stays false and the state
528  // is thus counted as an outlier
529  bool is_valid_measurement = false;
530 
531  auto cmps = stepper.componentIterable(state.stepping);
532  for (auto cmp : cmps) {
533  auto singleState = cmp.singleState(state);
534  const auto& singleStepper = cmp.singleStepper(stepper);
535 
536  auto trackStateProxyRes = detail::kalmanHandleMeasurement(
537  *m_cfg.calibrationContext, singleState, singleStepper,
538  m_cfg.extensions, surface, source_link, tmpStates.traj,
540 
541  if (!trackStateProxyRes.ok()) {
542  return trackStateProxyRes.error();
543  }
544 
545  const auto& trackStateProxy = *trackStateProxyRes;
546 
547  // If at least one component is no outlier, we consider the whole thing
548  // as a measurementState
549  if (trackStateProxy.typeFlags().test(
551  is_valid_measurement = true;
552  }
553 
554  tmpStates.tips.push_back(trackStateProxy.index());
555  tmpStates.weights[tmpStates.tips.back()] = cmp.weight();
556  }
557 
558  computePosteriorWeights(tmpStates.traj, tmpStates.tips, tmpStates.weights);
559 
560  detail::normalizeWeights(tmpStates.tips, [&](auto idx) -> double& {
561  return tmpStates.weights.at(idx);
562  });
563 
564  // Do the statistics
565  ++result.processedStates;
566 
567  // TODO should outlier states also be counted here?
568  if (is_valid_measurement) {
569  ++result.measurementStates;
570  }
571 
572  addCombinedState(result, tmpStates, surface);
573  result.lastMeasurementTip = result.currentTip;
574 
575  using FiltProjector =
577  FiltProjector proj{tmpStates.traj, tmpStates.weights};
578 
579  std::vector<std::tuple<double, BoundVector, BoundMatrix>> v;
580 
581  // TODO Check why can zero weights can occur
582  for (const auto& idx : tmpStates.tips) {
583  const auto [w, p, c] = proj(idx);
584  if (w > 0.0) {
585  v.push_back({w, p, c});
586  }
587  }
588 
589  normalizeWeights(v, [](auto& c) -> double& { return std::get<double>(c); });
590 
591  result.lastMeasurementState = MultiComponentBoundTrackParameters(
592  surface.getSharedPtr(), std::move(v),
593  stepper.particleHypothesis(state.stepping));
594 
595  // Return success
597  }
598 
599  template <typename propagator_state_t, typename stepper_t,
600  typename navigator_t>
602  const stepper_t& stepper,
603  const navigator_t& navigator,
604  result_type& result,
605  TemporaryStates& tmpStates,
606  bool doCovTransport) const {
607  const auto& surface = *navigator.currentSurface(state.navigation);
608 
609  // Initialize as true, so that any component can flip it. However, all
610  // components should behave the same
611  bool is_hole = true;
612 
613  auto cmps = stepper.componentIterable(state.stepping);
614  for (auto cmp : cmps) {
615  auto singleState = cmp.singleState(state);
616  const auto& singleStepper = cmp.singleStepper(stepper);
617 
618  // There is some redundant checking inside this function, but do this for
619  // now until we measure this is significant
620  auto trackStateProxyRes = detail::kalmanHandleNoMeasurement(
621  singleState, singleStepper, surface, tmpStates.traj,
622  MultiTrajectoryTraits::kInvalid, doCovTransport, logger());
623 
624  if (!trackStateProxyRes.ok()) {
625  return trackStateProxyRes.error();
626  }
627 
628  const auto& trackStateProxy = *trackStateProxyRes;
629 
630  if (not trackStateProxy.typeFlags().test(TrackStateFlag::HoleFlag)) {
631  is_hole = false;
632  }
633 
634  tmpStates.tips.push_back(trackStateProxy.index());
635  tmpStates.weights[tmpStates.tips.back()] = cmp.weight();
636  }
637 
638  // These things should only be done once for all components
639  if (is_hole) {
640  ++result.measurementHoles;
641  }
642 
643  ++result.processedStates;
644 
645  addCombinedState(result, tmpStates, surface);
646 
647  return Result<void>::success();
648  }
649 
651  template <typename propagator_state_t, typename stepper_t,
652  typename navigator_t>
653  void applyMultipleScattering(propagator_state_t& state,
654  const stepper_t& stepper,
655  const navigator_t& navigator,
656  const MaterialUpdateStage& updateStage =
657  MaterialUpdateStage::FullUpdate) const {
658  const auto& surface = *navigator.currentSurface(state.navigation);
659 
660  for (auto cmp : stepper.componentIterable(state.stepping)) {
661  auto singleState = cmp.singleState(state);
662  const auto& singleStepper = cmp.singleStepper(stepper);
663 
664  detail::PointwiseMaterialInteraction interaction(&surface, singleState,
665  singleStepper);
666  if (interaction.evaluateMaterialSlab(singleState, navigator,
667  updateStage)) {
668  // In the Gsf we only need to handle the multiple scattering
670  m_cfg.multipleScattering, false);
671 
672  // Screen out material effects info
673  ACTS_VERBOSE("Material effects on surface: "
674  << surface.geometryId()
675  << " at update stage: " << updateStage << " are :");
676  ACTS_VERBOSE("eLoss = "
677  << interaction.Eloss << ", "
678  << "variancePhi = " << interaction.variancePhi << ", "
679  << "varianceTheta = " << interaction.varianceTheta << ", "
680  << "varianceQoverP = " << interaction.varianceQoverP);
681 
682  // Update the state and stepper with material effects
683  interaction.updateState(singleState, singleStepper, addNoise);
684 
685  assert(singleState.stepping.cov.array().isFinite().all() &&
686  "covariance not finite after multi scattering");
687  }
688  }
689  }
690 
691  void addCombinedState(result_type& result, const TemporaryStates& tmpStates,
692  const Surface& surface) const {
693  using PrtProjector =
695  using FltProjector =
697 
698  if (not m_cfg.inReversePass) {
699  const auto firstCmpProxy =
700  tmpStates.traj.getTrackState(tmpStates.tips.front());
701  const auto isMeasurement =
702  firstCmpProxy.typeFlags().test(MeasurementFlag);
703 
704  const auto mask =
705  isMeasurement
706  ? TrackStatePropMask::Calibrated | TrackStatePropMask::Predicted |
707  TrackStatePropMask::Filtered | TrackStatePropMask::Smoothed
708  : TrackStatePropMask::Calibrated | TrackStatePropMask::Predicted;
709 
710  result.currentTip =
711  result.fittedStates->addTrackState(mask, result.currentTip);
712  auto proxy = result.fittedStates->getTrackState(result.currentTip);
713 
714  proxy.setReferenceSurface(surface.getSharedPtr());
715  proxy.copyFrom(firstCmpProxy, mask);
716 
717  auto [prtMean, prtCov] = reduceGaussianMixture(
718  tmpStates.tips, surface, m_cfg.reductionMethod,
719  PrtProjector{tmpStates.traj, tmpStates.weights});
720  proxy.predicted() = prtMean;
721  proxy.predictedCovariance() = prtCov;
722 
723  if (isMeasurement) {
724  auto [fltMean, fltCov] = reduceGaussianMixture(
725  tmpStates.tips, surface, m_cfg.reductionMethod,
726  FltProjector{tmpStates.traj, tmpStates.weights});
727  proxy.filtered() = fltMean;
728  proxy.filteredCovariance() = fltCov;
729  proxy.smoothed() = BoundVector::Constant(-2);
730  proxy.smoothedCovariance() = BoundSquareMatrix::Constant(-2);
731  } else {
732  proxy.shareFrom(TrackStatePropMask::Predicted,
733  TrackStatePropMask::Filtered);
734  }
735 
736  } else {
737  assert((result.currentTip != MultiTrajectoryTraits::kInvalid &&
738  "tip not valid"));
739 
740  result.fittedStates->applyBackwards(
741  result.currentTip, [&](auto trackState) {
742  auto fSurface = &trackState.referenceSurface();
743  if (fSurface == &surface) {
744  result.surfacesVisitedBwdAgain.push_back(&surface);
745 
746  if (trackState.hasSmoothed()) {
747  const auto [smtMean, smtCov] = reduceGaussianMixture(
748  tmpStates.tips, surface, m_cfg.reductionMethod,
749  FltProjector{tmpStates.traj, tmpStates.weights});
750 
751  trackState.smoothed() = smtMean;
752  trackState.smoothedCovariance() = smtCov;
753  }
754  return false;
755  }
756  return true;
757  });
758  }
759  }
760 
764  m_cfg.maxComponents = options.maxComponents;
765  m_cfg.extensions = options.extensions;
766  m_cfg.abortOnError = options.abortOnError;
767  m_cfg.disableAllMaterialHandling = options.disableAllMaterialHandling;
768  m_cfg.weightCutoff = options.weightCutoff;
769  m_cfg.reductionMethod = options.stateReductionMethod;
770  m_cfg.calibrationContext = &options.calibrationContext.get();
771  }
772 };
773 
774 } // namespace detail
775 } // namespace Acts