Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
AlignmentTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file AlignmentTests.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 
44 
45 #include <cmath>
46 #include <random>
47 #include <string>
48 
49 namespace {
50 using namespace Acts;
51 using namespace ActsAlignment;
52 using namespace Acts::Test;
53 using namespace Acts::UnitLiterals;
54 
55 using StraightPropagator =
60 
61 using KalmanUpdater = Acts::GainMatrixUpdater;
62 using KalmanSmoother = Acts::GainMatrixSmoother;
63 using KalmanFitterType =
65 
66 KalmanUpdater kfUpdater;
67 KalmanSmoother kfSmoother;
68 
69 // Create a test context
73 
74 std::normal_distribution<double> normalDist(0., 1.);
75 std::default_random_engine rng(42);
76 
79  extensions.calibrator
80  .connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
81  extensions.updater.connect<&KalmanUpdater::operator()<VectorMultiTrajectory>>(
82  &kfUpdater);
83  extensions.smoother
84  .connect<&KalmanSmoother::operator()<VectorMultiTrajectory>>(&kfSmoother);
85  return extensions;
86 }
87 
91 struct TelescopeDetector {
95  TelescopeDetector(std::reference_wrapper<const GeometryContext> gctx)
96  : geoContext(gctx) {
97  // Construct the rotation
98  rotation.col(0) = Acts::Vector3(0, 0, -1);
99  rotation.col(1) = Acts::Vector3(0, 1, 0);
100  rotation.col(2) = Acts::Vector3(1, 0, 0);
101 
102  // Boundaries of the surfaces
103  rBounds = std::make_shared<const RectangleBounds>(0.1_m, 0.1_m);
104 
105  // Material of the surfaces
106  MaterialSlab matProp(makeSilicon(), 80_um);
107 
108  surfaceMaterial = std::make_shared<HomogeneousSurfaceMaterial>(matProp);
109  }
110 
114  std::shared_ptr<const TrackingGeometry> operator()() {
115  using namespace UnitLiterals;
116 
117  unsigned int nLayers = 6;
118  std::vector<ActsScalar> positions = {-500_mm, -300_mm, -100_mm,
119  100_mm, 300_mm, 500_mm};
120  auto length = positions.back() - positions.front();
121 
122  std::vector<LayerPtr> layers(nLayers);
123  for (unsigned int i = 0; i < nLayers; ++i) {
124  // The transform
125  Translation3 trans(0., 0., positions[i]);
126  Transform3 trafo(rotation * trans);
127  auto detElement = std::make_shared<DetectorElementStub>(
128  trafo, rBounds, 1._um, surfaceMaterial);
129  // The surface is not right!!!
130  auto surface = detElement->surface().getSharedPtr();
131  // Add it to the event store
132  detectorStore.push_back(std::move(detElement));
133  std::unique_ptr<SurfaceArray> surArray(new SurfaceArray(surface));
134  // The layer thickness should not be too large
135  layers[i] =
136  PlaneLayer::create(trafo, rBounds, std::move(surArray),
137  1._mm); // Associate the layer to the surface
138  auto mutableSurface = const_cast<Surface*>(surface.get());
139  mutableSurface->associateLayer(*layers[i]);
140  }
141 
142  // The volume transform
143  Translation3 transVol(0, 0, 0);
144  Transform3 trafoVol(rotation * transVol);
145  VolumeBoundsPtr boundsVol = std::make_shared<const CuboidVolumeBounds>(
146  rBounds->halfLengthX() + 10._mm, rBounds->halfLengthY() + 10._mm,
147  length + 10._mm);
148 
149  LayerArrayCreator::Config lacConfig;
150  LayerArrayCreator layArrCreator(
151  lacConfig, getDefaultLogger("LayerArrayCreator", Logging::INFO));
152  LayerVector layVec;
153  for (unsigned int i = 0; i < nLayers; i++) {
154  layVec.push_back(layers[i]);
155  }
156 
157  // Create the layer array
158  std::unique_ptr<const LayerArray> layArr(layArrCreator.layerArray(
159  geoContext, layVec, positions.front() - 2._mm, positions.back() + 2._mm,
161 
162  // Build the tracking volume
163  auto trackVolume =
164  TrackingVolume::create(trafoVol, boundsVol, nullptr, std::move(layArr),
165  nullptr, {}, "Telescope");
166 
167  return std::make_shared<const TrackingGeometry>(trackVolume);
168  }
169 
170  RotationMatrix3 rotation = RotationMatrix3::Identity();
171  std::shared_ptr<const RectangleBounds> rBounds = nullptr;
172  std::shared_ptr<const ISurfaceMaterial> surfaceMaterial = nullptr;
173 
174  std::vector<std::shared_ptr<DetectorElementStub>> detectorStore;
175 
176  std::reference_wrapper<const GeometryContext> geoContext;
177 };
178 
179 // Construct a straight-line propagator.
181  std::shared_ptr<const TrackingGeometry> geo) {
183  cfg.resolvePassive = false;
184  cfg.resolveMaterial = true;
185  cfg.resolveSensitive = true;
188  return StraightPropagator(stepper, std::move(navigator));
189 }
190 
191 // Construct a propagator using a constant magnetic field along z.
193  std::shared_ptr<const TrackingGeometry> geo, double bz,
194  std::unique_ptr<const Logger> logger) {
196  cfg.resolvePassive = false;
197  cfg.resolveMaterial = true;
198  cfg.resolveSensitive = true;
199  Navigator navigator(cfg, logger->cloneWithSuffix("Nav"));
200  auto field = std::make_shared<ConstantBField>(Vector3(0.0, 0.0, bz));
203  logger->cloneWithSuffix("Prop"));
204 }
205 
206 // Construct initial track parameters.
208  // create covariance matrix from reasonable standard deviations
209  BoundVector stddev;
210  stddev[eBoundLoc0] = 100_um;
211  stddev[eBoundLoc1] = 100_um;
212  stddev[eBoundTime] = 25_ns;
213  stddev[eBoundPhi] = 0.5_degree;
214  stddev[eBoundTheta] = 0.5_degree;
215  stddev[eBoundQOverP] = 1 / 100_GeV;
216  BoundSquareMatrix cov = stddev.cwiseProduct(stddev).asDiagonal();
217 
218  auto loc0 = 0. + stddev[eBoundLoc0] * normalDist(rng);
219  auto loc1 = 0. + stddev[eBoundLoc1] * normalDist(rng);
220  auto t = 42_ns + stddev[eBoundTime] * normalDist(rng);
221  auto phi = 0_degree + stddev[eBoundPhi] * normalDist(rng);
222  auto theta = 90_degree + stddev[eBoundTheta] * normalDist(rng);
223  auto qOverP = 1_e / 1_GeV + stddev[eBoundQOverP] * normalDist(rng);
224 
225  // define a track in the transverse plane along x
226  Vector4 mPos4(-1_m, loc0, loc1, t);
227 
228  return CurvilinearTrackParameters(mPos4, phi, theta, qOverP, cov,
230 }
231 
232 // detector resolutions
233 const MeasurementResolution resPixel = {MeasurementType::eLoc01,
234  {30_um, 50_um}};
237 };
238 
239 struct KalmanFitterInputTrajectory {
240  // The source links
241  std::vector<TestSourceLink> sourcelinks;
242  // The start parameters
243  std::optional<CurvilinearTrackParameters> startParameters;
244 };
245 
249 std::vector<KalmanFitterInputTrajectory> createTrajectories(
250  std::shared_ptr<const TrackingGeometry> geo, size_t nTrajectories) {
251  // simulation propagator
252  const auto simPropagator = makeStraightPropagator(std::move(geo));
253 
254  std::vector<KalmanFitterInputTrajectory> trajectories;
255  trajectories.reserve(nTrajectories);
256 
257  for (unsigned int iTrack = 0; iTrack < nTrajectories; iTrack++) {
258  auto start = makeParameters();
259  // Launch and collect - the measurements
260  auto measurements = createMeasurements(simPropagator, geoCtx, magCtx, start,
261  resolutions, rng);
262 
263  // Extract measurements from result of propagation.
264  KalmanFitterInputTrajectory traj;
265  traj.startParameters = start;
266  traj.sourcelinks = measurements.sourceLinks;
267 
268  trajectories.push_back(std::move(traj));
269  }
270  return trajectories;
271 }
272 } // namespace
273 
277 BOOST_AUTO_TEST_CASE(ZeroFieldKalmanAlignment) {
278  // Build detector
279  TelescopeDetector detector(geoCtx);
280  const auto geometry = detector();
281 
282  // reconstruction propagator and fitter
283  auto kfLogger = getDefaultLogger("KalmanFilter", Logging::INFO);
284  const auto kfZeroPropagator =
286  auto kfZero = KalmanFitterType(kfZeroPropagator);
287 
288  // alignment
289  auto alignLogger = getDefaultLogger("Alignment", Logging::INFO);
290  const auto alignZero = Alignment(std::move(kfZero), std::move(alignLogger));
291 
292  // Create 10 trajectories
293  const auto& trajectories = createTrajectories(geometry, 10);
294 
295  // Construct the KalmanFitter options
296 
297  auto extensions = getExtensions();
298  TestSourceLink::SurfaceAccessor surfaceAccessor{*geometry};
299  extensions.surfaceAccessor
300  .connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);
303 
304  // Construct a non-updating alignment updater
305  AlignedTransformUpdater voidAlignUpdater =
306  [](DetectorElementBase* /*element*/, const GeometryContext& /*gctx*/,
307  const Transform3& /*transform*/) { return true; };
308 
309  // Construct the alignment options
311  kfOptions, voidAlignUpdater);
312  alignOptions.maxIterations = 1;
313 
314  // Set the surfaces to be aligned (fix the layer 8)
315  unsigned int iSurface = 0;
316  std::unordered_map<const Surface*, size_t> idxedAlignSurfaces;
317  // Loop over the detector elements
318  for (auto& det : detector.detectorStore) {
319  const auto& surface = det->surface();
320  if (surface.geometryId().layer() != 8) {
321  alignOptions.alignedDetElements.push_back(det.get());
322  idxedAlignSurfaces.emplace(&surface, iSurface);
323  iSurface++;
324  }
325  }
326 
327  // Test the method to evaluate alignment state for a single track
328  const auto& inputTraj = trajectories.front();
329  kfOptions.referenceSurface = &(*inputTraj.startParameters).referenceSurface();
330 
331  auto evaluateRes = alignZero.evaluateTrackAlignmentState(
332  kfOptions.geoContext, inputTraj.sourcelinks, *inputTraj.startParameters,
333  kfOptions, idxedAlignSurfaces, AlignmentMask::All);
334  BOOST_CHECK(evaluateRes.ok());
335 
336  const auto& alignState = evaluateRes.value();
337  CHECK_CLOSE_ABS(alignState.chi2 / alignState.alignmentDof, 0.5, 1);
338 
339  // Check the dimensions
340  BOOST_CHECK_EQUAL(alignState.measurementDim, 12);
341  BOOST_CHECK_EQUAL(alignState.trackParametersDim, 36);
342  // Check the alignment dof
343  BOOST_CHECK_EQUAL(alignState.alignmentDof, 30);
344  BOOST_CHECK_EQUAL(alignState.alignedSurfaces.size(), 5);
345  // Check the measurements covariance
346  BOOST_CHECK_EQUAL(alignState.measurementCovariance.rows(), 12);
347  const SquareMatrix2 measCov =
348  alignState.measurementCovariance.block<2, 2>(2, 2);
349  SquareMatrix2 cov2D;
350  cov2D << 30_um * 30_um, 0, 0, 50_um * 50_um;
351  CHECK_CLOSE_ABS(measCov, cov2D, 1e-10);
352  // Check the track parameters covariance matrix. Its rows/columns scales
353  // with the number of measurement states
354  BOOST_CHECK_EQUAL(alignState.trackParametersCovariance.rows(), 36);
355  // Check the projection matrix
356  BOOST_CHECK_EQUAL(alignState.projectionMatrix.rows(), 12);
357  BOOST_CHECK_EQUAL(alignState.projectionMatrix.cols(), 36);
358  const ActsMatrix<2, 6> proj = alignState.projectionMatrix.block<2, 6>(0, 0);
360  CHECK_CLOSE_ABS(proj, refProj, 1e-10);
361  // Check the residual
362  BOOST_CHECK_EQUAL(alignState.residual.size(), 12);
363  // Check the residual covariance
364  BOOST_CHECK_EQUAL(alignState.residualCovariance.rows(), 12);
365  // Check the alignment to residual derivative
366  BOOST_CHECK_EQUAL(alignState.alignmentToResidualDerivative.rows(), 12);
367  BOOST_CHECK_EQUAL(alignState.alignmentToResidualDerivative.cols(), 30);
368  // Check the chi2 derivative
369  BOOST_CHECK_EQUAL(alignState.alignmentToChi2Derivative.size(), 30);
370  BOOST_CHECK_EQUAL(alignState.alignmentToChi2SecondDerivative.rows(), 30);
371 
372  // Test the align method
373  std::vector<std::vector<TestSourceLink>> trajCollection;
374  trajCollection.reserve(10);
375  std::vector<CurvilinearTrackParameters> sParametersCollection;
376  sParametersCollection.reserve(10);
377  for (const auto& traj : trajectories) {
378  trajCollection.push_back(traj.sourcelinks);
379  sParametersCollection.push_back(*traj.startParameters);
380  }
381  auto alignRes =
382  alignZero.align(trajCollection, sParametersCollection, alignOptions);
383 
384  // BOOST_CHECK(alignRes.ok());
385 }