Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NeuralCalibrator.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file NeuralCalibrator.cpp
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 
13 
14 #include <TFile.h>
15 
16 namespace detail {
17 
18 template <typename Array>
19 size_t fillChargeMatrix(Array& arr, const ActsExamples::Cluster& cluster,
20  size_t size0 = 7u, size_t size1 = 7u) {
21  // First, rescale the activations to sum to unity. This promotes
22  // numerical stability in the index computation
23  double totalAct = 0;
24  for (const ActsExamples::Cluster::Cell& cell : cluster.channels) {
25  totalAct += cell.activation;
26  }
27  std::vector<double> weights;
28  for (const ActsExamples::Cluster::Cell& cell : cluster.channels) {
29  weights.push_back(cell.activation / totalAct);
30  }
31 
32  double acc0 = 0;
33  double acc1 = 0;
34  for (size_t i = 0; i < cluster.channels.size(); i++) {
35  acc0 += cluster.channels.at(i).bin[0] * weights.at(i);
36  acc1 += cluster.channels.at(i).bin[1] * weights.at(i);
37  }
38 
39  // By convention, put the center pixel in the middle cell.
40  // Achieved by translating the cluster --> compute the offsets
41  int offset0 = static_cast<int>(acc0) - size0 / 2;
42  int offset1 = static_cast<int>(acc1) - size1 / 2;
43 
44  // Zero the charge matrix first, to guard against leftovers
45  arr = Eigen::ArrayXXf::Zero(1, size0 * size1);
46  // Fill the matrix
47  for (const ActsExamples::Cluster::Cell& cell : cluster.channels) {
48  // Translate each pixel
49  int iMat = cell.bin[0] - offset0;
50  int jMat = cell.bin[1] - offset1;
51  if (iMat >= 0 && iMat < (int)size0 && jMat >= 0 && jMat < (int)size1) {
52  typename Array::Index index = iMat * size0 + jMat;
53  if (index < arr.size()) {
54  arr(index) = cell.activation;
55  }
56  }
57  }
58  return size0 * size1;
59 }
60 
61 } // namespace detail
62 
64  const std::filesystem::path& modelPath, size_t nComponents,
65  std::vector<size_t> volumeIds)
66  : m_env(ORT_LOGGING_LEVEL_WARNING, "NeuralCalibrator"),
67  m_model(m_env, modelPath.c_str()),
68  m_nComponents{nComponents},
69  m_volumeIds{std::move(volumeIds)} {}
70 
72  const MeasurementContainer& measurements, const ClusterContainer* clusters,
74  const Acts::SourceLink& sourceLink,
76  trackState) const {
77  trackState.setUncalibratedSourceLink(sourceLink);
78  const IndexSourceLink& idxSourceLink = sourceLink.get<IndexSourceLink>();
79  assert((idxSourceLink.index() < measurements.size()) and
80  "Source link index is outside the container bounds");
81 
82  if (std::find(m_volumeIds.begin(), m_volumeIds.end(),
83  idxSourceLink.geometryId().volume()) == m_volumeIds.end()) {
84  m_fallback.calibrate(measurements, clusters, gctx, cctx, sourceLink,
85  trackState);
86  return;
87  }
88 
89  Acts::NetworkBatchInput inputBatch(1, m_nInputs);
90  auto input = inputBatch(0, Eigen::all);
91 
92  // TODO: Matrix size should be configurable perhaps?
93  size_t matSize0 = 7u;
94  size_t matSize1 = 7u;
95  size_t iInput = ::detail::fillChargeMatrix(
96  input, (*clusters)[idxSourceLink.index()], matSize0, matSize1);
97 
98  input[iInput++] = idxSourceLink.geometryId().volume();
99  input[iInput++] = idxSourceLink.geometryId().layer();
100 
101  const Acts::Surface& referenceSurface = trackState.referenceSurface();
102 
103  std::visit(
104  [&](const auto& measurement) {
105  auto E = measurement.expander();
106  auto P = measurement.projector();
107  Acts::ActsVector<Acts::eBoundSize> fpar = E * measurement.parameters();
109  E * measurement.covariance() * E.transpose();
110 
112  fpar[Acts::eBoundPhi], fpar[Acts::eBoundTheta]);
113  Acts::Vector3 globalPosition = referenceSurface.localToGlobal(
114  gctx, fpar.segment<2>(Acts::eBoundLoc0), dir);
115 
116  // Rotation matrix. When applied to global coordinates, they
117  // are rotated into the local reference frame of the
118  // surface. Note that this such a rotation can be found by
119  // inverting a matrix whose columns correspond to the
120  // coordinate axes of the local coordinate system.
122  referenceSurface.referenceFrame(gctx, globalPosition, dir)
123  .inverse();
124  std::pair<double, double> angles =
126 
127  input[iInput++] = angles.first;
128  input[iInput++] = angles.second;
129  input[iInput++] = fpar[Acts::eBoundLoc0];
130  input[iInput++] = fpar[Acts::eBoundLoc1];
131  input[iInput++] = fcov(Acts::eBoundLoc0, Acts::eBoundLoc0);
132  input[iInput++] = fcov(Acts::eBoundLoc1, Acts::eBoundLoc1);
133  if (iInput != m_nInputs) {
134  throw std::runtime_error("Expected input size of " +
135  std::to_string(m_nInputs) +
136  ", got: " + std::to_string(iInput));
137  }
138 
139  // Input is a single row, hence .front()
140  std::vector<float> output =
141  m_model.runONNXInference(inputBatch).front();
142  // Assuming 2-D measurements, the expected params structure is:
143  // [ 0, nComponent[ --> priors
144  // [ nComponent, 3*nComponent[ --> means
145  // [3*nComponent, 5*nComponent[ --> variances
146  size_t nParams = 5 * m_nComponents;
147  if (output.size() != nParams) {
148  throw std::runtime_error(
149  "Got output vector of size " + std::to_string(output.size()) +
150  ", expected size " + std::to_string(nParams));
151  }
152 
153  // Most probable value computation of mixture density
154  size_t iMax = 0;
155  if (m_nComponents > 1) {
156  iMax = std::distance(
157  output.begin(),
158  std::max_element(output.begin(), output.begin() + m_nComponents));
159  }
160  size_t iLoc0 = m_nComponents + iMax * 2;
161  size_t iVar0 = 3 * m_nComponents + iMax * 2;
162 
163  fpar[Acts::eBoundLoc0] = output[iLoc0];
164  fpar[Acts::eBoundLoc1] = output[iLoc0 + 1];
165  fcov(Acts::eBoundLoc0, Acts::eBoundLoc0) = output[iVar0];
166  fcov(Acts::eBoundLoc1, Acts::eBoundLoc1) = output[iVar0 + 1];
167 
168  constexpr size_t kSize =
170  std::array<Acts::BoundIndices, kSize> indices = measurement.indices();
171  Acts::ActsVector<kSize> cpar = P * fpar;
172  Acts::ActsSquareMatrix<kSize> ccov = P * fcov * P.transpose();
173 
174  Acts::SourceLink sl{idxSourceLink};
175 
177  std::move(sl), indices, cpar, ccov);
178 
179  trackState.allocateCalibrated(calibrated.size());
180  trackState.setCalibrated(calibrated);
181  },
182  measurements[idxSourceLink.index()]);
183 }