Project
Loading...
Searching...
No Matches
IOUtils.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13
14#include <algorithm>
15#include <cmath>
16#include <limits>
17#include <type_traits>
18#include <vector>
19
21#include "GPUCommonMath.h"
23#include "MFTBase/GeometryTGeo.h"
24#include "MathUtils/Utils.h"
25
26namespace
27{
28
29template <o2::detectors::DetID::ID DetId, typename GeomT>
31 GeomT* geom, const o2::itsmft::CompClusterExt& cluster,
32 gsl::span<const unsigned char>::iterator& patterns,
34{
35 if (dict == nullptr) {
36 throw std::runtime_error("Cluster dictionary is not available");
37 }
38 if (geom == nullptr) {
39 throw std::runtime_error("Cluster geometry is not available");
40 }
41
42 const auto sensorID = cluster.getSensorID();
43 if (sensorID >= geom->getSize()) {
44 throw std::runtime_error("Cluster sensor ID is outside the detector geometry");
45 }
46 const int layer = geom->getLayer(sensorID);
47 constexpr int nLayers = DetId == o2::detectors::DetID::ITS ? o2::itsmft::tracking::ITSNLayers : o2::itsmft::tracking::MFTNLayers;
48 if (layer < 0 || layer >= nLayers) {
49 throw std::runtime_error("Cluster layer is outside the detector");
50 }
51
52 const auto clusterData = o2::itsmft::ioutils::extractClusterData(cluster, patterns, dict);
53 const float sigma2Row = clusterData.sig2Row;
54 const float sigma2Col = clusterData.sig2Col;
55
56 if constexpr (DetId == o2::detectors::DetID::ITS) {
57 const auto trkXYZ = geom->getMatrixT2L(sensorID) ^ clusterData.coordinates;
58 const auto gloXYZ = geom->getMatrixL2G(sensorID) * clusterData.coordinates;
59 return {{gloXYZ.x(), gloXYZ.y(), gloXYZ.z()},
60 {trkXYZ.x(), trkXYZ.y(), trkXYZ.z(), geom->getSensorRefAlpha(sensorID)},
61 {sigma2Row, 0.f, sigma2Col},
62 clusterData.nPixels,
63 layer};
64 } else {
65 if (!geom->getCacheL2G().isFilled() || geom->getCacheL2G().getSize() <= sensorID) {
66 throw std::runtime_error("Cluster geometry is not available");
67 }
68 const auto gloXYZ = geom->getMatrixL2G(sensorID) * clusterData.coordinates;
69 return {{gloXYZ.x(), gloXYZ.y(), gloXYZ.z()}, {}, {sigma2Row, 0.f, sigma2Col}, clusterData.nPixels, layer};
70 }
71}
72
73template <o2::detectors::DetID::ID DetId, typename Consume>
74void decodeDetectorSource(const o2::itsmft::tracking::ClusterSourceInput& source, const Consume& consume)
75{
76 using Geometry = std::conditional_t<DetId == o2::detectors::DetID::ITS, o2::its::GeometryTGeo, o2::mft::GeometryTGeo>;
77 Geometry* geometry = nullptr;
78 if (!source.clusters.empty()) {
79 geometry = Geometry::Instance();
80 geometry->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G));
81 }
82 consume([&](const auto& cluster, auto& patterns) {
83 return decodeCluster<DetId>(geometry, cluster, patterns, source.dictionary);
84 });
85}
86
87} // namespace
88
90{
91namespace
92{
93// Project decoded ITS facts into the accepted cylindrical convention.
94GlobalMeasurement makeCylinderGlobalMeasurement(const DecodedCluster& decoded, uint32_t clusterId)
95{
96 const float sine = std::sin(decoded.cylinderFrame.frameAngle);
97 const float cosine = std::cos(decoded.cylinderFrame.frameAngle);
98 const auto& covariance = decoded.rowColumnCovariance;
99 return GlobalMeasurement{
100 decoded.global.x,
101 decoded.global.y,
102 decoded.global.z,
103 {sine * sine * covariance.uu,
104 -sine * cosine * covariance.uu,
105 -sine * covariance.uv,
106 cosine * cosine * covariance.uu,
107 cosine * covariance.uv,
108 covariance.vv},
109 0.f, // Radius and phi are computed after subtracting the beam position.
110 0.f,
111 clusterId};
112}
113
114// Project decoded MFT facts into z-normal, global-x/global-y disk coordinates.
115// ALPIDE row is established as global x and column as global y by the MFT
116// geometry decoder. No legacy TrackingFrameInfo participates in this mapping.
117GlobalMeasurement makeDiskGlobalMeasurement(const DecodedCluster& decoded, uint32_t clusterId)
118{
119 return GlobalMeasurement{
120 decoded.global.x,
121 decoded.global.y,
122 decoded.global.z,
123 {decoded.rowColumnCovariance.uu, decoded.rowColumnCovariance.uv, 0.f,
124 decoded.rowColumnCovariance.vv, 0.f, 0.f},
125 0.f, // Radius and phi are computed after subtracting the beam position.
126 0.f,
127 clusterId};
128}
129
130SurfaceMeasurement makeCylinderSurfaceMeasurement(const DecodedCluster& decoded)
131{
132 return {decoded.cylinderFrame, decoded.rowColumnCovariance};
133}
134
135SurfaceMeasurement makeDiskSurfaceMeasurement(const DecodedCluster& decoded)
136{
137 return {{decoded.global.z, decoded.global.x, decoded.global.y, 0.f},
138 decoded.rowColumnCovariance};
139}
140
141bool covariance2DIsPositiveSemidefinite(float cxx, float cxy, float cyy) noexcept
142{
143 if (cxx < 0.f || cyy < 0.f) {
144 return false;
145 }
146 const double diagonalProduct = static_cast<double>(cxx) * cyy;
147 const double cxySquared = static_cast<double>(cxy) * cxy;
148 const double tolerance = 16. * std::numeric_limits<float>::epsilon() *
149 std::max(diagonalProduct, cxySquared);
150 return diagonalProduct - cxySquared >= -tolerance;
151}
152
153bool globalCovarianceIsPositiveSemidefinite(const GlobalCovariance3F& covariance) noexcept
154{
155 const float xx = covariance[GlobalMeasurement::XX];
156 const float xy = covariance[GlobalMeasurement::XY];
157 const float xz = covariance[GlobalMeasurement::XZ];
158 const float yy = covariance[GlobalMeasurement::YY];
159 const float yz = covariance[GlobalMeasurement::YZ];
160 const float zz = covariance[GlobalMeasurement::ZZ];
161 if (!covariance2DIsPositiveSemidefinite(xx, xy, yy) ||
162 !covariance2DIsPositiveSemidefinite(xx, xz, zz) ||
163 !covariance2DIsPositiveSemidefinite(yy, yz, zz)) {
164 return false;
165 }
166 const double determinant =
167 static_cast<double>(xx) * yy * zz + 2. * static_cast<double>(xy) * xz * yz -
168 static_cast<double>(xx) * yz * yz - static_cast<double>(yy) * xz * xz -
169 static_cast<double>(zz) * xy * xy;
170 const double scale = std::max({std::abs(static_cast<double>(xx) * yy * zz),
171 std::abs(2. * static_cast<double>(xy) * xz * yz),
172 std::abs(static_cast<double>(xx) * yz * yz),
173 std::abs(static_cast<double>(yy) * xz * xz),
174 std::abs(static_cast<double>(zz) * xy * xy)});
175 return o2::gpu::GPUCommonMath::Finite(static_cast<float>(determinant)) &&
176 determinant >= -32. * std::numeric_limits<float>::epsilon() * scale;
177}
178
179bool decodedMeasurementIsValid(const GlobalMeasurement& global,
180 const SurfaceMeasurement& local) noexcept
181{
182 return globalCovarianceIsPositiveSemidefinite(global.covariance) &&
183 covariance2DIsPositiveSemidefinite(local.covariance.uu, local.covariance.uv, local.covariance.vv);
184}
185
186void clearFrameAndSidecars(TimeFrame& frame,
187 std::vector<std::vector<uint32_t>>* externalIndicesBySurface,
188 std::vector<std::vector<uint32_t>>* clusterSizesBySurface) noexcept
189{
190 frame.resetTimeFrame();
191 if (externalIndicesBySurface != nullptr) {
192 externalIndicesBySurface->clear();
193 }
194 if (clusterSizesBySurface != nullptr) {
195 clusterSizesBySurface->clear();
196 }
197}
198
199} // namespace
200
201namespace detail
202{
203void prepareSources(TimeFrame& frame, const SurfaceCatalogView& catalog,
204 gsl::span<const ClusterSourceInput> sources,
205 std::vector<std::vector<uint32_t>>* externalIndicesBySurface,
206 std::vector<std::vector<uint32_t>>* clusterSizesBySurface, bool requireCompleteMapping)
207{
208 clearFrameAndSidecars(frame, externalIndicesBySurface, clusterSizesBySurface);
209 if (!frame.isConfigured()) {
210 throw std::runtime_error("TimeFrame is not configured");
211 }
212 if (requireCompleteMapping && sources.empty()) {
213 throw std::runtime_error("Malformed cluster loading input");
214 }
215 const auto nSources = static_cast<uint32_t>(sources.size());
216
217 std::vector<bool> seen(nSources, false);
218 std::vector<ClusterSourceId> sourceBySurface(catalog.nSurfaces, ClusterSourceId::invalid());
219 for (const auto& src : sources) {
220 if (!src.id.isValid() || src.id.value() >= nSources) {
221 throw std::runtime_error(std::format("Source IDs must be dense source={}", src.id.value()));
222 }
223 if (seen[src.id.value()]) {
224 throw std::runtime_error(std::format("Duplicate source ID source={}", src.id.value()));
225 }
226 seen[src.id.value()] = true;
227 if (src.detector != o2::detectors::DetID::ITS && src.detector != o2::detectors::DetID::MFT) {
228 throw std::runtime_error(std::format("Unsupported source detector source={}", src.id.value()));
229 }
230 if (!src.clusters.empty() && src.dictionary == nullptr) {
231 throw std::runtime_error(std::format("Cluster dictionary is not available source={} rof={} clusterIndex={}", src.id.value(), 0, 0));
232 }
233 for (const auto surface : src.layerToSurface) {
234 if (!surface.isValid() || surface.value() >= catalog.nSurfaces || surface.value() >= frame.getDetectorConfiguration().size()) {
235 throw std::runtime_error(std::format("Invalid source-to-surface layer mapping source={}", src.id.value()));
236 }
237 if (sourceBySurface[surface.value()].isValid()) {
238 throw std::runtime_error(std::format("Invalid source-to-surface layer mapping source={}", src.id.value()));
239 }
240 if (catalog.getSurface(surface).detectorId != static_cast<uint8_t>(src.detector)) {
241 throw std::runtime_error(std::format("Source detector does not match its surface source={}", src.id.value()));
242 }
243 sourceBySurface[surface.value()] = src.id;
244 }
245 }
246 if (requireCompleteMapping) {
247 for (uint16_t position = 0; position < frame.getDetectorConfiguration().size(); ++position) {
248 if (position < sourceBySurface.size() && sourceBySurface[position].isValid()) {
249 continue;
250 }
251 // Attribute an omitted surface only when one source owns its detector.
252 ClusterSourceId owner;
253 for (const auto& source : sources) {
254 if (static_cast<uint8_t>(source.detector) != frame.getDetectorConfiguration().getSurfaceCatalog().getSurface(LayerId{position}).detectorId) {
255 continue;
256 }
257 if (owner.isValid()) {
258 throw std::runtime_error("Invalid source-to-surface layer mapping");
259 }
260 owner = source.id;
261 }
262 throw std::runtime_error(std::format("Invalid source-to-surface layer mapping source={}", owner.value()));
263 }
264 }
265}
267{
268 int64_t expectedNext = 0;
269 for (uint32_t r = 0; r < src.rofs.size(); ++r) {
270 const auto& rof = src.rofs[r];
271 const int64_t first = rof.getFirstEntry();
272 const int64_t n = rof.getNEntries();
273 if (n < 0 || first != expectedNext) {
274 throw std::runtime_error(std::format("Invalid ROF cluster range source={} rof={}", src.id.value(), r));
275 }
276 expectedNext = first + n;
277 if (expectedNext > static_cast<int64_t>(src.clusters.size())) {
278 throw std::runtime_error(std::format("Invalid ROF cluster range source={} rof={}", src.id.value(), r));
279 }
280 }
281 if (expectedNext != static_cast<int64_t>(src.clusters.size())) {
282 throw std::runtime_error(std::format("Invalid ROF cluster range source={} rof={}", src.id.value(), static_cast<uint32_t>(src.rofs.size())));
283 }
284}
285void appendCluster(TimeFrame& frame, const SurfaceCatalogView& catalog,
286 const ClusterSourceInput& src, const DecodedCluster& decoded,
287 uint32_t r, uint32_t externalIndex,
288 std::vector<std::vector<uint32_t>>& externalIndices,
289 std::vector<std::vector<uint32_t>>& clusterSizes)
290{
291 if (decoded.layer < 0 || static_cast<size_t>(decoded.layer) >= src.layerToSurface.size()) {
292 throw std::runtime_error(std::format("Invalid source-to-surface layer mapping source={} rof={} clusterIndex={}", src.id.value(), r, externalIndex));
293 }
294 const auto expectedSurface = src.layerToSurface[decoded.layer];
295 const auto& surfaceDescriptor = catalog.getSurface(expectedSurface);
296 const auto localClusterId = static_cast<uint32_t>(frame.getGlobalMeasurements(expectedSurface).size());
297 // Apply alignment systematics once, for both detectors, before projecting
298 // either covariance. Use the same resolved configuration as search windows,
299 // indexed by the mapped surface (not the detector-local layer).
300 auto corrected = decoded;
301 const auto& configuration = frame.getDetectorConfiguration();
302 corrected.rowColumnCovariance.uu += configuration.systError2Row.empty() ? 0.f : configuration.systError2Row.at(expectedSurface.value());
303 corrected.rowColumnCovariance.vv += configuration.systError2Col.empty() ? 0.f : configuration.systError2Col.at(expectedSurface.value());
304 GlobalMeasurement global;
305 SurfaceMeasurement measurement;
306 if (surfaceDescriptor.kind == SurfaceKind::Cylinder) {
307 global = makeCylinderGlobalMeasurement(corrected, localClusterId);
308 measurement = makeCylinderSurfaceMeasurement(corrected);
309 } else {
310 global = makeDiskGlobalMeasurement(corrected, localClusterId);
311 measurement = makeDiskSurfaceMeasurement(corrected);
312 }
313 if (!decodedMeasurementIsValid(global, measurement)) {
314 throw std::runtime_error(std::format("Malformed cluster loading input source={} rof={} clusterIndex={}", src.id.value(), r, externalIndex));
315 }
316 global.x -= frame.getBeamX();
317 global.y -= frame.getBeamY();
318 global.radius = std::hypot(global.x, global.y);
319 global.phi = o2::its::math_utils::computePhi(global.x, global.y);
320 if (src.labels != nullptr) {
321 frame.addMeasurement(expectedSurface, global, measurement, src.labels->getLabels(externalIndex));
322 } else {
323 frame.addMeasurement(expectedSurface, global, measurement);
324 }
325 clusterSizes[expectedSurface.value()].push_back(decoded.nPixels);
326 externalIndices[expectedSurface.value()].push_back(externalIndex);
327}
329 const std::vector<std::vector<int>>& boundaries)
330{
331 for (uint16_t layer = 0; layer < source.layerToSurface.size(); ++layer) {
332 frame.setROFClusters(source.layerToSurface[layer].value(), boundaries[layer]);
333 }
334}
335} // namespace detail
336
337void loadTimeFrameSources(TimeFrame& frame, gsl::span<const ClusterSourceInput> sources,
338 SurfaceCatalogView catalog,
339 std::vector<std::vector<uint32_t>>* externalIndicesBySurface,
340 std::vector<std::vector<uint32_t>>* clusterSizesBySurface)
341{
342 detail::prepareSources(frame, catalog, sources, externalIndicesBySurface, clusterSizesBySurface, true);
343 std::vector<std::vector<uint32_t>> externalIndices(catalog.nSurfaces);
344 std::vector<std::vector<uint32_t>> clusterSizes(catalog.nSurfaces);
345 bool hasMCInformation = false;
346 for (const auto& source : sources) {
348 const auto load = [&](const auto& decode) {
349 detail::loadDecodedSource(frame, catalog, source, decode, externalIndices, clusterSizes);
350 };
351 if (source.detector == o2::detectors::DetID::ITS) {
352 decodeDetectorSource<o2::detectors::DetID::ITS>(source, load);
353 } else {
354 decodeDetectorSource<o2::detectors::DetID::MFT>(source, load);
355 }
356 hasMCInformation |= source.labels != nullptr;
357 }
358 frame.setHasMCInformation(hasMCInformation);
359 if (externalIndicesBySurface != nullptr) {
360 *externalIndicesBySurface = std::move(externalIndices);
361 }
362 if (clusterSizesBySurface != nullptr) {
363 *clusterSizesBySurface = std::move(clusterSizes);
364 }
365}
366
367} // namespace o2::itsmft::tracking
General auxilliary methods.
Passive common TimeFrame owner.
Definition of the GeometryTGeo class.
Shared cluster I/O utilities for ITS and MFT (based on ITStracking/IOUtils.h)
UShort_t getSensorID() const
SurfaceCatalogView getSurfaceCatalog() const noexcept
GLdouble n
Definition glcorearb.h:1982
GLenum src
Definition glcorearb.h:1767
GLsizeiptr size
Definition glcorearb.h:659
GLint first
Definition glcorearb.h:399
GLsizei GLsizei GLchar * source
Definition glcorearb.h:798
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLboolean r
Definition glcorearb.h:1233
GLsizei GLenum * sources
Definition glcorearb.h:2516
const bool const int nLayers
ClusterData< T > extractClusterData(const CompClusterExt &c, gsl::span< const unsigned char >::iterator &patterns, const TopologyDictionary *dict)
Definition IOUtils.h:81
void loadDecodedSource(TimeFrame &frame, const SurfaceCatalogView &catalog, const ClusterSourceInput &src, const Decode &decode, std::vector< std::vector< uint32_t > > &externalIndices, std::vector< std::vector< uint32_t > > &clusterSizes)
Definition IOUtils.h:151
void validateClusterRanges(const ClusterSourceInput &)
Definition IOUtils.cxx:266
void storeSourceROFClusters(TimeFrame &, const ClusterSourceInput &, const std::vector< std::vector< int > > &)
Definition IOUtils.cxx:328
void appendCluster(TimeFrame &, const SurfaceCatalogView &, const ClusterSourceInput &, const DecodedCluster &, uint32_t, uint32_t, std::vector< std::vector< uint32_t > > &, std::vector< std::vector< uint32_t > > &)
Definition IOUtils.cxx:285
void prepareSources(TimeFrame &, const SurfaceCatalogView &, gsl::span< const ClusterSourceInput >, std::vector< std::vector< uint32_t > > *, std::vector< std::vector< uint32_t > > *, bool requireCompleteMapping=false)
Definition IOUtils.cxx:203
void loadTimeFrameSources(TimeFrame &, gsl::span< const ClusterSourceInput >, SurfaceCatalogView, std::vector< std::vector< uint32_t > > *externalIndicesBySurface=nullptr, std::vector< std::vector< uint32_t > > *clusterSizesBySurface=nullptr)
Definition IOUtils.cxx:337
constexpr int MFTNLayers
MFT CA half-disk layer count.
constexpr int ITSNLayers
ITS CA layer count.
void setROFClusters(std::size_t position, gsl::span< const int > boundaries)
void setHasMCInformation(bool value) noexcept
Definition TimeFrame.h:87
const DetectorConfiguration & getDetectorConfiguration() const noexcept
Definition TimeFrame.h:157
void addMeasurement(LayerId surface, GlobalMeasurement global, const SurfaceMeasurement &measurement)
Definition TimeFrame.cxx:56
bool isConfigured() const noexcept
Definition TimeFrame.h:156
gsl::span< const GlobalMeasurement > getGlobalMeasurements(LayerId surface) const
Definition TimeFrame.cxx:46
coder decode(ctfImage, triggersD, clustersD)