Skip to content

Commit d48b3a3

Browse files
sawenzelclaude
andcommitted
Cross-check the geometry doctor's reachability audit against VecGeom
This lets the reachability audit ask a second navigator the same question, so that a placement's shadowing can be told apart from one navigator's arbitrary choice inside an overlap. - GeometryManager gains ensureVecGeomWorld() and vecGeomLocate(), which return the TGeo nodes of the path VecGeom locates a point in. Neither exposes a VecGeom type, and both answer at runtime, so a caller that does not see the private O2_WITH_VECGEOM define can still use them. - The doctor takes --navigator tgeo|vecgeom|both. 'both' reports the placements where the two engines disagree about who owns a point. - The converter flattens assemblies, so a VecGeom path is the TGeo path with its assembly levels removed and a flattened node is renamed <original>_assemblyinternalcount_<n>. The comparison accounts for both. - On the full ALICE geometry at 1000 samples, 575 of 27647 placements disagree. Both navigators independently find the same five unreachable MCH and beam pipe placements; they differ on the HMPID absorbers, which TGeo gives 9.7 % of their own material and VecGeom gives all of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 7f7b4f7 commit d48b3a3

4 files changed

Lines changed: 236 additions & 32 deletions

File tree

Detectors/Base/include/DetectorsBase/GeometryManager.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "MathUtils/Cartesian.h"
2828
#include "DetectorsBase/MatCell.h"
2929
#include <mutex>
30+
#include <vector>
3031
class TGeoHMatrix; // lines 11-11
3132
class TGeoManager; // lines 9-9
3233
class TGeoNavigator;
@@ -138,6 +139,18 @@ class GeometryManager : public TObject
138139
static constexpr bool isVecGeomAvailable() { return false; }
139140
#endif
140141

142+
/// Builds the VecGeom world from the currently loaded TGeo geometry, once per process,
143+
/// and reports whether a VecGeom navigator is available at all. Unlike
144+
/// isVecGeomAvailable() this is a runtime answer, so a caller outside this library --
145+
/// which does not see the private O2_WITH_VECGEOM define -- can still ask.
146+
static bool ensureVecGeomWorld();
147+
148+
/// The VecGeom navigator's answer for a point: fills \p chain with the TGeo nodes of the
149+
/// located path, top node first. False when this build has no VecGeom backend or the
150+
/// point lies outside the world. Assemblies are flattened in the VecGeom geometry, so
151+
/// the chain is shorter than the TGeo path through the same point.
152+
static bool vecGeomLocate(double x, double y, double z, std::vector<TGeoNode*>& chain);
153+
141154
private:
142155
/// Default constructor
143156
GeometryManager() = default;

Detectors/Base/src/GeometryManager.cxx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,3 +678,48 @@ o2::base::MatBudget GeometryManager::vecGeomMaterialBudget(float x0, float y0, f
678678
}
679679

680680
#endif // O2_WITH_VECGEOM
681+
682+
//_____________________________________________________________________________________
683+
bool GeometryManager::ensureVecGeomWorld()
684+
{
685+
#ifdef O2_WITH_VECGEOM
686+
ensureVecGeomWorldBuilt();
687+
return true;
688+
#else
689+
return false;
690+
#endif
691+
}
692+
693+
//_____________________________________________________________________________________
694+
bool GeometryManager::vecGeomLocate(double x, double y, double z, std::vector<TGeoNode*>& chain)
695+
{
696+
chain.clear();
697+
#ifdef O2_WITH_VECGEOM
698+
ensureVecGeomWorldBuilt();
699+
// One state per thread, as for the material budget above.
700+
thread_local vecgeom::NavigationState* state =
701+
vecgeom::NavigationState::MakeInstance(vecgeom::GeoManager::Instance().getMaxDepth());
702+
state->Clear();
703+
const vecgeom::Vector3D<vecgeom::Precision> point(x, y, z);
704+
if (vecgeom::GlobalLocator::LocateGlobalPoint(vecgeom::GeoManager::Instance().GetWorld(), point, *state, true) ==
705+
nullptr) {
706+
return false;
707+
}
708+
auto const& converter = tgeo2vecgeom::RootGeoManager::Instance();
709+
for (int level = 0; level < (int)state->GetCurrentLevel(); ++level) {
710+
auto const* placed = state->At(level);
711+
auto const* node = placed != nullptr ? converter.tgeonode(placed) : nullptr;
712+
if (node == nullptr) {
713+
chain.clear();
714+
return false;
715+
}
716+
chain.push_back(const_cast<TGeoNode*>(node));
717+
}
718+
return !chain.empty();
719+
#else
720+
(void)x;
721+
(void)y;
722+
(void)z;
723+
return false;
724+
#endif
725+
}

run/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ o2_add_executable(g4-determine-unknown-pdg-properties
115115
o2_add_executable(geometry-doctor
116116
COMPONENT_NAME sim
117117
SOURCES o2sim_geometry_doctor.cxx
118-
PUBLIC_LINK_LIBRARIES O2::Field ROOT::Geom Boost::program_options
118+
PUBLIC_LINK_LIBRARIES O2::Field O2::DetectorsBase ROOT::Geom
119+
Boost::program_options
119120
nlohmann_json::nlohmann_json)
120121

121122
o2_add_executable(mctracks-proxy

0 commit comments

Comments
 (0)