Skip to content

Commit

Permalink
feat(gx2f): Add new error NotEnoughMeasurements (#2981)
Browse files Browse the repository at this point in the history
## What?
Returns an error, in case the number of measurements is too low for a fit.

This check takes into account the evaluated dimensions of the measurements. To fit, we need at least NDF+1 measurements. However, we n-dimensional measurements count for n measurements, reducing the effective number of needed measurements.

 We might encounter the case, where we cannot use some (parts of a) measurements, maybe if we do not support that kind of measurement. This is also taken into account here.

`ndf = 4` is chosen, since this a minimum that makes sense for us, but a more general approach is desired.

## Why?
We had in PR #2966 FPE Problems due to a division by zero in:
https://github.com/acts-project/acts/blob/a42f23b96876a0fdfba5a8ab1b6c90a9b1f2dc30/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp#L716
This happened, when no measurements were evaluated by the collector. It could happen that we didn't collect any measurements, since the ODD uses also some 3-dimensional measurements. Currently, the GX2F can only handle 1- and 2-dimensional measurements.

## Future TODO
Make `ndf` dependent on the problem. For this we need to find a way to deduce, how many parameters we want to fit.
  • Loading branch information
AJPfleger authored Feb 22, 2024
1 parent a42f23b commit 15ace85
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 2 deletions.
24 changes: 22 additions & 2 deletions Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,10 @@ class Gx2Fitter {
} else if (trackStateProxy.calibratedSize() == 2) {
collector<2>(trackStateProxy, result, *actorLogger);
} else {
ACTS_WARNING(
"Only measurements of 1 and 2 dimensions are implemented yet.");
ACTS_WARNING("Found measurement with "
<< trackStateProxy.calibratedSize()
<< " dimensions. Only measurements of 1 and 2 "
"dimensions are implemented yet.");
}

// Set the measurement type flag
Expand Down Expand Up @@ -675,6 +677,24 @@ class Gx2Fitter {
ACTS_VERBOSE("gx2fResult.collectorProjectedJacobians.size() = "
<< gx2fResult.collectorProjectedJacobians.size());

// This check takes into account the evaluated dimensions of the
// measurements. To fit, we need at least NDF+1 measurements. However,
// we n-dimensional measurements count for n measurements, reducing the
// effective number of needed measurements.
// We might encounter the case, where we cannot use some (parts of a)
// measurements, maybe if we do not support that kind of measurement. This
// is also taken into account here.
// `ndf = 4` is chosen, since this a minimum that makes sense for us, but
// a more general approach is desired.
// TODO genernalize for n-dimensional fit
constexpr std::size_t ndf = 4;
if (ndf + 1 > gx2fResult.collectorResiduals.size()) {
ACTS_INFO("Not enough measurements. Require "
<< ndf + 1 << ", but only "
<< gx2fResult.collectorResiduals.size() << " could be used.");
return Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements;
}

chi2sum = 0;
aMatrix = BoundMatrix::Zero();
bVector = BoundVector::Zero();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ enum class GlobalChiSquareFitterError {
// ensure all values are non-zero
AIsNotInvertible = 1,
DidNotConverge = 2,
NotEnoughMeasurements = 3,
};

std::error_code make_error_code(
Expand Down
68 changes: 68 additions & 0 deletions Tests/UnitTests/Core/TrackFitting/Gx2fTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,74 @@ BOOST_AUTO_TEST_CASE(DidNotConverge) {

ACTS_INFO("*** Test: DidNotConverge -- Finish");
}

BOOST_AUTO_TEST_CASE(NotEnoughMeasurements) {
ACTS_INFO("*** Test: NotEnoughMeasurements -- Start");

std::default_random_engine rng(42);

ACTS_DEBUG("Create the detector");
const std::size_t nSurfaces = 2;
Detector detector;
detector.geometry = makeToyDetector(geoCtx, nSurfaces);

ACTS_DEBUG("Set the start parameters for measurement creation and fit");
const auto parametersMeasurements = makeParameters();
const auto startParametersFit = makeParameters(
7_mm, 11_mm, 15_mm, 42_ns, 10_degree, 80_degree, 1_GeV, 1_e);

ACTS_DEBUG("Create the measurements");
// simulation propagator
using SimPropagator =
Acts::Propagator<Acts::StraightLineStepper, Acts::Navigator>;
const SimPropagator simPropagator = makeStraightPropagator(detector.geometry);
const auto measurements =
createMeasurements(simPropagator, geoCtx, magCtx, parametersMeasurements,
resMapAllPixel, rng);
const auto sourceLinks = prepareSourceLinks(measurements.sourceLinks);
ACTS_VERBOSE("sourceLinks.size() = " << sourceLinks.size());

BOOST_REQUIRE_EQUAL(sourceLinks.size(), nSurfaces);

ACTS_DEBUG("Set up the fitter");
const Surface* rSurface = &parametersMeasurements.referenceSurface();

using RecoStepper = EigenStepper<>;
const auto recoPropagator =
makeConstantFieldPropagator<RecoStepper>(detector.geometry, 0_T);

using RecoPropagator = decltype(recoPropagator);
using Gx2Fitter =
Experimental::Gx2Fitter<RecoPropagator, VectorMultiTrajectory>;
const Gx2Fitter fitter(recoPropagator, gx2fLogger->clone());

Experimental::Gx2FitterExtensions<VectorMultiTrajectory> extensions;
extensions.calibrator
.connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
TestSourceLink::SurfaceAccessor surfaceAccessor{*detector.geometry};
extensions.surfaceAccessor
.connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);

const Experimental::Gx2FitterOptions gx2fOptions(
geoCtx, magCtx, calCtx, extensions, PropagatorPlainOptions(), rSurface,
false, false, FreeToBoundCorrection(false), 6, true, 0);

Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
Acts::VectorMultiTrajectory{}};

ACTS_DEBUG("Fit the track");
ACTS_VERBOSE("startParameter unsmeared:\n" << parametersMeasurements);
ACTS_VERBOSE("startParameter fit:\n" << startParametersFit);
const auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(),
startParametersFit, gx2fOptions, tracks);

BOOST_REQUIRE(!res.ok());
BOOST_CHECK_EQUAL(
res.error(),
Acts::Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements);

ACTS_INFO("*** Test: NotEnoughMeasurements -- Finish");
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace Test
} // namespace Acts

0 comments on commit 15ace85

Please sign in to comment.