25#include "Acts/Definitions/Units.hpp"
26#include "Acts/Geometry/GeometryContext.hpp"
27#include "Acts/Geometry/TrackingGeometry.hpp"
28#include "Acts/Surfaces/BoundaryTolerance.hpp"
29#include "Acts/Surfaces/Surface.hpp"
30#include "Acts/Surfaces/SurfacePlacementBase.hpp"
31#include "ActsPlugins/Root/TGeoDetectorElement.hpp"
33#include <TGeoManager.h>
34#include <TGeoMatrix.h>
45const std::vector<int> gNoSensors{};
50 const auto geoId =
surface.geometryId();
51 auto [it, inserted] = mBySensor.try_emplace(sensorID, Entry{&
surface, geoId});
53 throw std::runtime_error(
"o2::acts::SurfaceIndexMap: sensor " +
std::to_string(sensorID) +
54 " is already mapped");
56 mByGeoId[geoId].push_back(sensorID);
61 const auto it = mBySensor.find(sensorID);
62 return it == mBySensor.end() ? nullptr : it->second.surface;
67 const auto it = mBySensor.find(sensorID);
68 return it == mBySensor.end() ? Acts::GeometryIdentifier{} : it->second.geoId;
73 const auto it = mByGeoId.find(geoId);
74 return it == mByGeoId.end() ? gNoSensors : it->second;
91 explicit CentreLookup(
double cellSize) : mCellSize(cellSize) {}
96 mCells[cellOf(
position)].push_back(mEntries.size() - 1);
100 const Acts::Surface* findNearest(
const Acts::Vector3&
position,
double tolerance)
const
103 const Acts::Surface*
best =
nullptr;
104 double bestDist2 = tolerance * tolerance;
105 for (std::int64_t dx = -1; dx <= 1; ++dx) {
106 for (std::int64_t dy = -1; dy <= 1; ++dy) {
107 for (std::int64_t dz = -1; dz <= 1; ++dz) {
108 const auto it = mCells.find(Cell{cell.x + dx, cell.y + dy, cell.z + dz});
109 if (it == mCells.end()) {
112 for (
const auto idx : it->second) {
113 const double dist2 = (mEntries[
idx].position -
position).squaredNorm();
114 if (dist2 <= bestDist2) {
125 std::size_t
size()
const {
return mEntries.size(); }
133 std::int64_t
x = 0,
y = 0,
z = 0;
137 std::size_t operator()(
const Cell&
c)
const noexcept
139 std::size_t
h =
static_cast<std::size_t
>(
c.x) * 0x9e3779b97f4a7c15ULL;
140 h ^=
static_cast<std::size_t
>(
c.y) + 0x9e3779b97f4a7c15ULL + (
h << 6) + (
h >> 2);
141 h ^=
static_cast<std::size_t
>(
c.z) + 0x9e3779b97f4a7c15ULL + (
h << 6) + (
h >> 2);
146 Cell cellOf(
const Acts::Vector3& p)
const
148 return Cell{
static_cast<std::int64_t
>(std::floor(p[0] / mCellSize)),
149 static_cast<std::int64_t
>(std::floor(p[1] / mCellSize)),
150 static_cast<std::int64_t
>(std::floor(p[2] / mCellSize))};
153 double mCellSize = 1.;
154 std::vector<Entry> mEntries;
155 std::unordered_map<Cell, std::vector<std::size_t>, CellHash> mCells;
164struct SensitiveSurfaces {
167 std::unordered_map<const TGeoNode*, std::vector<const Acts::Surface*>>
byNode;
170 explicit SensitiveSurfaces(
double cellSize) :
byCentre(cellSize) {}
173SensitiveSurfaces collectSensitive(
const Acts::TrackingGeometry& geometry,
174 const Acts::GeometryContext& gctx,
double cellSize)
176 SensitiveSurfaces out(cellSize);
177 geometry.apply([&](
const Acts::Surface&
surface) {
178 const auto* placement =
surface.surfacePlacement();
179 if (placement ==
nullptr || !placement->isSensitive()) {
182 const auto* tgeoElement =
dynamic_cast<const ActsPlugins::TGeoDetectorElement*
>(placement);
183 if (tgeoElement !=
nullptr) {
184 out.byNode[&tgeoElement->tgeoNode()].push_back(&
surface);
189 if (
type == Acts::Surface::SurfaceType::Cylinder ||
type == Acts::Surface::SurfaceType::Disc) {
190 out.layerSurfaces.push_back(&
surface);
199struct SensorPlacement {
206 double rot[9] = {0.};
207 double tra[3] = {0.};
208 cache.
getMatrixL2G(sensorID).GetComponents(rot[0], rot[1], rot[2], tra[0],
209 rot[3], rot[4], rot[5], tra[1],
210 rot[6], rot[7], rot[8], tra[2]);
212 out.position = Acts::Vector3{tra[0] * Acts::UnitConstants::cm, tra[1] * Acts::UnitConstants::cm,
213 tra[2] * Acts::UnitConstants::cm};
214 out.rotation << rot[0], rot[1], rot[2], rot[3], rot[4], rot[5], rot[6], rot[7], rot[8];
222const Acts::Surface* matchByNode(
const SensitiveSurfaces&
surfaces,
const std::string&
path,
223 const Acts::GeometryContext& gctx,
const SensorPlacement& placement,
224 double tolerance,
bool& ambiguous)
227 if (gGeoManager ==
nullptr) {
228 throw std::runtime_error(
"o2::acts::makeSurfaceIndex: no TGeo geometry loaded");
230 gGeoManager->PushPath();
231 const bool ok = gGeoManager->cd(
path.c_str());
232 const TGeoNode*
node = ok ? gGeoManager->GetCurrentNode() :
nullptr;
233 gGeoManager->PopPath();
234 if (
node ==
nullptr) {
242 if (it->second.size() == 1) {
243 return it->second.front();
245 const Acts::Surface*
best =
nullptr;
246 for (
const auto*
surface : it->second) {
247 if ((
surface->localToGlobalTransform(gctx).translation() - placement.position).norm() <= tolerance) {
248 if (best !=
nullptr) {
260const Acts::Surface* matchByPlacement(
const SensitiveSurfaces&
surfaces,
261 const Acts::GeometryContext& gctx,
262 const SensorPlacement& placement,
double tolerance,
268 constexpr double kRotationTolerance = 1e-9;
270 if (
const auto*
surface =
surfaces.byCentre.findNearest(placement.position, tolerance);
281 const Acts::Surface*
best =
nullptr;
283 const auto& transform =
surface->localToGlobalTransform(gctx);
284 if ((transform.translation() - placement.position).norm() <= tolerance &&
285 (transform.rotation() - placement.rotation).norm() <= kRotationTolerance) {
286 if (best !=
nullptr) {
299 const Acts::GeometryContext& gctx,
305 throw std::runtime_error(
"o2::acts::makeSurfaceIndex: the detector matrix cache is not built");
308 throw std::runtime_error(
309 "o2::acts::makeSurfaceIndex: the L2G matrix cache is empty. Call fillMatrixCache() with the "
310 "L2G bit set before building the index.");
314 const double tolerance = toleranceCm * Acts::UnitConstants::cm;
316 const auto surfaces = collectSensitive(geometry, gctx, std::max(tolerance, 1e-6));
317 const int nSensors = cache.
getSize();
320 "Matching {} {} sensors against {} sensitive ACTS surfaces ({} per-sensor, {} whole-layer) "
321 "by {} (tolerance {} cm)",
324 pathProvider ?
"TGeo node" :
"placement", toleranceCm);
325 if (pathProvider &&
surfaces.nWithoutNode != 0) {
326 LOGP(warn,
"{} sensitive surfaces have no TGeo detector element and cannot be matched by node",
331 std::size_t nUnmatched = 0;
332 std::size_t nAmbiguous = 0;
333 std::unordered_map<Acts::GeometryIdentifier, int> claimedBy;
334 std::ostringstream failures;
337 for (
int sensorID = 0; sensorID < nSensors; ++sensorID) {
338 const auto placement = sensorPlacementOf(cache, sensorID);
340 bool ambiguous =
false;
341 const Acts::Surface*
match =
343 ? matchByNode(
surfaces, pathProvider(sensorID), gctx, placement, tolerance, ambiguous)
344 : matchByPlacement(
surfaces, gctx, placement, tolerance, ambiguous);
346 if (
match ==
nullptr) {
347 ambiguous ? ++nAmbiguous : ++nUnmatched;
348 if (nReported < 10) {
349 failures <<
"\n sensor " << sensorID <<
" at ("
350 << placement.position[0] / Acts::UnitConstants::cm <<
", "
351 << placement.position[1] / Acts::UnitConstants::cm <<
", "
352 << placement.position[2] / Acts::UnitConstants::cm <<
") cm "
353 << (ambiguous ?
"matches several sensitive ACTS surfaces" :
"matches no sensitive ACTS surface");
361 const auto geoId =
match->geometryId();
362 const auto [it, inserted] = claimedBy.try_emplace(geoId, sensorID);
364 throw std::runtime_error(
365 "o2::acts::makeSurfaceIndex: sensors " +
std::to_string(it->second) +
" and " +
366 std::to_string(sensorID) +
" both resolve to the ACTS surface with geometry id " +
368 ". They are indistinguishable by the information available, so the index would be wrong.");
373 if (nUnmatched != 0 || nAmbiguous != 0) {
375 if (nAmbiguous != 0 && !pathProvider) {
377 " Pass a SensorPathProvider (e.g. GeometryTGeo::getMatrixPath) so sensors are identified by "
378 "their TGeo node instead of their placement.";
380 throw std::runtime_error(
"o2::acts::makeSurfaceIndex: of " +
std::to_string(nSensors) +
381 " sensors, " +
std::to_string(nUnmatched) +
" matched no surface and " +
382 std::to_string(nAmbiguous) +
" were ambiguous." + hint + failures.str());
385 LOGP(info,
"Sensor index built: {} sensors on {} surfaces",
index.size(),
index.getNSurfaces());
std::unique_ptr< expressions::Node > node
std::vector< const Acts::Surface * > layerSurfaces
Acts::RotationMatrix3 rotation
const Acts::Surface * surface
std::unordered_map< const TGeoNode *, std::vector< const Acts::Surface * > > byNode
Lookup between O2 sensor indices and ACTS surfaces.
std::vector< std::shared_ptr< Surface > > surfaces
Class for time synchronization of RawReader instances.
const Acts::Surface * getSurface(int sensorID) const
const std::vector< int > & getSensorIDs(Acts::GeometryIdentifier geoId) const
void add(int sensorID, const Acts::Surface &surface)
Acts::GeometryIdentifier getGeometryId(int sensorID) const
static constexpr const char * getName(ID id)
names of defined detectors
const o2::detectors::DetID & getDetID() const
const Mat3D & getMatrixL2G(int sensID) const
const MatrixCache< Mat3D > & getCacheL2G() const
bool match(const std::vector< std::string > &queries, const char *pattern)
GLint GLint GLsizei GLint GLenum GLenum type
GLsizei const GLchar *const * path
GLdouble GLdouble GLdouble z
SurfaceIndexMap makeSurfaceIndex(const Acts::TrackingGeometry &geometry, const Acts::GeometryContext &gctx, const o2::detectors::DetMatrixCache &cache, double toleranceCm=1e-3, SensorPathProvider pathProvider={})
std::function< std::string(int sensorID)> SensorPathProvider
Full TGeo node path of one sensor, e.g. o2::trk::GeometryTGeo::getMatrixPath().
const bool const bool const int FollowDirection BestTrial TrackITSInternal< NLayers > & best
bool operator==(const observer_ptr< W1 > &p1, const observer_ptr< W2 > &p2)
std::string to_string(gsl::span< T, Size > span)