Skip to content

Commit fa687cd

Browse files
authored
refactor!: Distinct MultiComponentTrackParameters (#5150)
Decouples the `MultiComponentTrackParameters` interface from the usual bound track parameters. This is motivated by their distinct nature. A `MultiComponentTrackParameters` cannot be uniquely represented as a single bound track parameter but requires a user specified merging procedure. Additionally, refactors the component storage to use SoA over AoS to model optional covariance more efficiently.
1 parent bcd11ba commit fa687cd

15 files changed

+507
-356
lines changed

Core/include/Acts/EventData/MultiComponentTrackParameters.hpp

Lines changed: 180 additions & 181 deletions
Large diffs are not rendered by default.

Core/include/Acts/EventData/TrackParametersConcept.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "Acts/Geometry/GeometryContext.hpp"
1414

1515
#include <optional>
16+
#include <tuple>
1617

1718
namespace Acts {
1819

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// This file is part of the ACTS project.
2+
//
3+
// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/.
8+
9+
#pragma once
10+
11+
#include "Acts/Definitions/TrackParametrization.hpp"
12+
13+
#include <tuple>
14+
15+
namespace Acts::detail {
16+
17+
template <typename cmp_t>
18+
concept ComponentWithoutCovarianceConcept =
19+
requires { typename std::tuple_size<std::remove_cvref_t<cmp_t>>::type; } &&
20+
(std::tuple_size_v<std::remove_cvref_t<cmp_t>> == 2) &&
21+
requires(const cmp_t& cmp) {
22+
{ std::get<0>(cmp) } -> std::convertible_to<const double&>;
23+
{ std::get<1>(cmp) } -> std::convertible_to<const BoundVector&>;
24+
};
25+
26+
template <typename cmp_t>
27+
concept ComponentWithCovarianceConcept =
28+
requires { typename std::tuple_size<std::remove_cvref_t<cmp_t>>::type; } &&
29+
(std::tuple_size_v<std::remove_cvref_t<cmp_t>> == 3) &&
30+
requires(const cmp_t& cmp) {
31+
{ std::get<0>(cmp) } -> std::convertible_to<const double&>;
32+
{ std::get<1>(cmp) } -> std::convertible_to<const BoundVector&>;
33+
{ std::get<2>(cmp) } -> std::convertible_to<const BoundMatrix&>;
34+
};
35+
36+
template <typename cmp_t>
37+
concept ComponentConcept = ComponentWithoutCovarianceConcept<cmp_t> ||
38+
ComponentWithCovarianceConcept<cmp_t>;
39+
40+
template <typename projector_t, typename cmp_t>
41+
concept ComponentWithoutCovarianceProjectorConcept =
42+
requires(const projector_t& proj, const cmp_t& cmp) {
43+
{ proj(cmp) } -> ComponentWithoutCovarianceConcept;
44+
};
45+
46+
template <typename projector_t, typename cmp_t>
47+
concept ComponentWithCovarianceProjectorConcept =
48+
requires(const projector_t& proj, const cmp_t& cmp) {
49+
{ proj(cmp) } -> ComponentWithCovarianceConcept;
50+
};
51+
52+
template <typename projector_t, typename cmp_t>
53+
concept ComponentProjectorConcept =
54+
ComponentWithoutCovarianceProjectorConcept<projector_t, cmp_t> ||
55+
ComponentWithCovarianceProjectorConcept<projector_t, cmp_t>;
56+
57+
template <typename component_range_t, typename projector_t>
58+
concept ComponentRangeAndProjectorWithoutCovarianceConcept =
59+
std::ranges::range<component_range_t> &&
60+
requires(const component_range_t& cmps, const projector_t& proj) {
61+
{ proj(*cmps.begin()) } -> ComponentWithoutCovarianceConcept;
62+
};
63+
64+
template <typename component_range_t, typename projector_t>
65+
concept ComponentRangeAndProjectorWithCovarianceConcept =
66+
std::ranges::range<component_range_t> &&
67+
requires(const component_range_t& cmps, const projector_t& proj) {
68+
{ proj(*cmps.begin()) } -> ComponentWithCovarianceConcept;
69+
};
70+
71+
} // namespace Acts::detail

Core/include/Acts/Propagator/MultiStepperLoop.hpp

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
#include <cstddef>
3232
#include <limits>
3333
#include <sstream>
34-
#include <vector>
3534

3635
#include <boost/container/small_vector.hpp>
3736

@@ -274,27 +273,19 @@ class MultiStepperLoop final {
274273
/// @param par The multi-component bound track parameters
275274
void initialize(State& state,
276275
const MultiComponentBoundTrackParameters& par) const {
277-
if (par.components().empty()) {
278-
throw std::invalid_argument(
279-
"Cannot construct MultiEigenStepperLoop::State with empty "
280-
"multi-component parameters");
281-
}
282-
283276
state.particleHypothesis = par.particleHypothesis();
284277

285278
const auto surface = par.referenceSurface().getSharedPtr();
286279

287-
for (auto i = 0ul; i < par.components().size(); ++i) {
280+
for (std::size_t i = 0; i < par.size(); ++i) {
288281
const auto& [weight, singlePars] = par[i];
289282
auto& cmp = state.components.emplace_back(
290283
m_singleStepper.makeState(state.options), weight,
291284
IntersectionStatus::onSurface);
292285
m_singleStepper.initialize(cmp.state, singlePars);
293286
}
294287

295-
if (std::get<2>(par.components().front())) {
296-
state.covTransport = true;
297-
}
288+
state.covTransport = par.hasCovariance();
298289
}
299290

300291
/// A proxy struct which allows access to a single component of the

Core/include/Acts/Propagator/MultiStepperLoop.ipp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ auto MultiStepperLoop<S, R>::boundState(
2121
-> Result<BoundState> {
2222
assert(!state.components.empty());
2323

24-
std::vector<std::tuple<double, BoundVector, Covariance>> cmps;
25-
cmps.reserve(numberComponents(state));
24+
MultiComponentBoundTrackParameters params(
25+
surface.getSharedPtr(), transportCov, state.particleHypothesis);
26+
params.reserve(numberComponents(state));
27+
2628
double accumulatedPathLength = 0.0;
2729

2830
for (auto i = 0ul; i < numberComponents(state); ++i) {
@@ -48,20 +50,20 @@ auto MultiStepperLoop<S, R>::boundState(
4850

4951
if (bs.ok()) {
5052
const auto& btp = std::get<BoundTrackParameters>(*bs);
51-
cmps.emplace_back(state.components[i].weight, btp.parameters(),
52-
btp.covariance().value_or(Acts::BoundMatrix::Zero()));
53+
params.pushComponent(state.components[i].weight, btp.parameters(),
54+
btp.covariance());
5355
accumulatedPathLength +=
5456
std::get<double>(*bs) * state.components[i].weight;
5557
}
5658
}
5759

58-
if (cmps.empty()) {
60+
if (params.empty()) {
5961
return MultiStepperError::AllComponentsConversionToBoundFailed;
6062
}
6163

62-
return BoundState{MultiComponentBoundTrackParameters(
63-
surface.getSharedPtr(), cmps, state.particleHypothesis),
64-
Jacobian::Zero(), accumulatedPathLength};
64+
params.normalizeWeights();
65+
66+
return BoundState{params, Jacobian::Zero(), accumulatedPathLength};
6567
}
6668

6769
template <Concepts::SingleStepper S, typename R>

Core/include/Acts/Propagator/Propagator.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,6 @@ class Propagator final
9393
/// Re-define bound track parameters dependent on the stepper
9494
using StepperBoundTrackParameters =
9595
detail::stepper_bound_parameters_type_t<stepper_t>;
96-
static_assert(BoundTrackParametersConcept<StepperBoundTrackParameters>,
97-
"Stepper bound track parameters do not fulfill bound "
98-
"parameters concept.");
9996
static_assert(std::copy_constructible<StepperBoundTrackParameters>,
10097
"return track parameter type must be copy-constructible");
10198

Core/include/Acts/Propagator/Propagator.ipp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,6 @@ template <typename propagator_state_t, typename parameters_t,
311311
typename path_aborter_t>
312312
Result<void> Propagator<S, N>::initialize(propagator_state_t& state,
313313
const parameters_t& start) const {
314-
static_assert(BoundTrackParametersConcept<parameters_t>,
315-
"Parameters do not fulfill bound parameters concept.");
316-
317314
m_stepper.initialize(state.stepping, start);
318315

319316
state.position = m_stepper.position(state.stepping);

Core/include/Acts/TrackFitting/GaussianSumFitter.hpp

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ struct GaussianSumFitter {
305305
if constexpr (!IsMultiParameters::value) {
306306
params = MultiComponentBoundTrackParameters(
307307
sParameters.referenceSurface().getSharedPtr(),
308-
sParameters.parameters(), *sParameters.covariance(),
308+
sParameters.parameters(), sParameters.covariance(),
309309
sParameters.particleHypothesis());
310310
} else {
311311
params = sParameters;
@@ -377,18 +377,19 @@ struct GaussianSumFitter {
377377
? *options.referenceSurface
378378
: sParameters.referenceSurface();
379379

380-
std::vector<std::tuple<double, BoundVector, std::optional<BoundMatrix>>>
381-
inflatedParamVector;
382380
assert(!fwdGsfResult.lastMeasurementComponents.empty());
383381
assert(fwdGsfResult.lastMeasurementSurface != nullptr);
384-
for (auto& [w, p, cov] : fwdGsfResult.lastMeasurementComponents) {
385-
inflatedParamVector.emplace_back(
386-
w, p, cov * options.reverseFilteringCovarianceScaling);
387-
}
388382

389383
MultiComponentBoundTrackParameters inflatedParams(
390384
fwdGsfResult.lastMeasurementSurface->getSharedPtr(),
391-
std::move(inflatedParamVector), sParameters.particleHypothesis());
385+
fwdGsfResult.lastMeasurementComponents,
386+
[&options](const auto& cmp)
387+
-> std::tuple<double, const BoundVector&, BoundMatrix> {
388+
return {
389+
std::get<0>(cmp), std::get<1>(cmp),
390+
std::get<2>(cmp) * options.reverseFilteringCovarianceScaling};
391+
},
392+
sParameters.particleHypothesis());
392393

393394
auto state = m_propagator.template makeState<decltype(bwdPropOptions),
394395
MultiStepperSurfaceReached>(
@@ -504,17 +505,10 @@ struct GaussianSumFitter {
504505

505506
if (options.referenceSurface) {
506507
const auto& params = *bwdResult->endParameters;
508+
const auto singleParams = params.merge(options.componentMergeMethod);
507509

508-
const auto [finalPars, finalCov] = detail::Gsf::mergeGaussianMixture(
509-
params.components(),
510-
[](const auto& cmp) {
511-
auto&& [weight_l, pars_l, opt_cov_l] = cmp;
512-
return std::tie(weight_l, pars_l, *opt_cov_l);
513-
},
514-
params.referenceSurface(), options.componentMergeMethod);
515-
516-
track.parameters() = finalPars;
517-
track.covariance() = finalCov;
510+
track.parameters() = singleParams.parameters();
511+
track.covariance() = singleParams.covariance().value();
518512

519513
track.setReferenceSurface(params.referenceSurface().getSharedPtr());
520514

Core/src/EventData/MultiComponentTrackParameters.cpp

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,113 @@
1111
#include "Acts/EventData/TrackParameters.hpp"
1212
#include "Acts/TrackFitting/detail/GsfComponentMerging.hpp"
1313

14+
#include <cstddef>
15+
#include <numeric>
16+
#include <ranges>
17+
1418
namespace Acts {
1519

1620
BoundTrackParameters MultiComponentBoundTrackParameters::merge(
1721
const ComponentMergeMethod method) const {
18-
const bool hasCov = std::get<2>(m_components.front()).has_value();
22+
if (size() == 0) {
23+
throw std::logic_error(
24+
"Cannot merge MultiComponentBoundTrackParameters with zero components");
25+
}
26+
27+
if (size() == 1) {
28+
return BoundTrackParameters(
29+
m_surface, m_parameters[0],
30+
hasCovariance() ? std::optional(m_covariances[0]) : std::nullopt,
31+
m_particleHypothesis);
32+
}
1933

20-
if (!hasCov) {
34+
if (!hasCovariance()) {
2135
const BoundVector singleParams = detail::Gsf::mergeGaussianMixtureParams(
22-
m_components,
23-
[](const auto& cmp) -> std::tuple<double, const BoundVector&> {
24-
return {std::get<0>(cmp), std::get<1>(cmp)};
36+
std::views::iota(std::size_t{0}, size()),
37+
[this](const std::size_t i) -> std::tuple<double, const BoundVector&> {
38+
return {m_weights[i], m_parameters[i]};
2539
},
2640
*m_surface, method);
2741
return BoundTrackParameters(m_surface, singleParams, std::nullopt,
2842
m_particleHypothesis);
2943
}
3044

3145
const auto [singleParams, singleCov] = detail::Gsf::mergeGaussianMixture(
32-
m_components,
33-
[](const auto& cmp)
46+
std::views::iota(std::size_t{0}, size()),
47+
[this](const std::size_t i)
3448
-> std::tuple<double, const BoundVector&, const BoundMatrix&> {
35-
return {std::get<0>(cmp), std::get<1>(cmp), std::get<2>(cmp).value()};
49+
return {m_weights[i], m_parameters[i], m_covariances[i]};
3650
},
3751
*m_surface, method);
3852
return BoundTrackParameters(m_surface, singleParams, singleCov,
3953
m_particleHypothesis);
4054
}
4155

56+
void MultiComponentBoundTrackParameters::reserve(const std::size_t n) {
57+
m_weights.reserve(n);
58+
m_parameters.reserve(n);
59+
if (hasCovariance()) {
60+
m_covariances.reserve(n);
61+
}
62+
}
63+
64+
void MultiComponentBoundTrackParameters::clear() {
65+
m_weights.clear();
66+
m_parameters.clear();
67+
m_covariances.clear();
68+
}
69+
70+
void MultiComponentBoundTrackParameters::pushComponent(
71+
const double weight, const ParametersVector& params) {
72+
if (hasCovariance()) {
73+
throw std::logic_error(
74+
"Cannot push component without covariance to "
75+
"MultiComponentBoundTrackParameters with covariance");
76+
}
77+
78+
m_weights.push_back(weight);
79+
m_parameters.push_back(params);
80+
}
81+
82+
void MultiComponentBoundTrackParameters::pushComponent(
83+
const double weight, const ParametersVector& params,
84+
const CovarianceMatrix& cov) {
85+
if (!hasCovariance()) {
86+
throw std::logic_error(
87+
"Cannot push component with covariance to "
88+
"MultiComponentBoundTrackParameters without covariance");
89+
}
90+
91+
m_weights.push_back(weight);
92+
m_parameters.push_back(params);
93+
m_covariances.push_back(cov);
94+
}
95+
96+
void MultiComponentBoundTrackParameters::pushComponent(
97+
const double weight, const ParametersVector& params,
98+
const std::optional<CovarianceMatrix>& cov) {
99+
if (hasCovariance() != cov.has_value()) {
100+
}
101+
102+
m_weights.push_back(weight);
103+
m_parameters.push_back(params);
104+
if (hasCovariance()) {
105+
m_covariances.push_back(*cov);
106+
}
107+
}
108+
109+
void MultiComponentBoundTrackParameters::normalizeWeights() {
110+
const double sumWeights =
111+
std::accumulate(m_weights.begin(), m_weights.end(), 0.0);
112+
if (sumWeights <= 0.0) {
113+
throw std::logic_error(
114+
"Cannot normalize weights of MultiComponentBoundTrackParameters: sum "
115+
"of weights is not strictly positive: " +
116+
std::to_string(sumWeights));
117+
}
118+
for (double& weight : m_weights) {
119+
weight /= sumWeights;
120+
}
121+
}
122+
42123
} // namespace Acts

0 commit comments

Comments
 (0)