Project
Loading...
Searching...
No Matches
VecGeomChecks.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
12#include "VecGeomChecks.h"
13
14#include "TGeoBBox.h"
15#include "TGeoManager.h"
16#include "TGeoMatrix.h"
17#include "TGeoNode.h"
18#include "TGeoVolume.h"
19#include "TRandom3.h"
20
21#include <VecGeom/base/Transformation3D.h>
22#include <VecGeom/management/GeoManager.h>
23#include <VecGeom/navigation/GlobalLocator.h>
24#include <VecGeom/navigation/NavigationState.h>
25#include <VecGeom/navigation/VNavigator.h>
26#include <VecGeom/volumes/LogicalVolume.h>
27#include <VecGeom/volumes/PlacedVolume.h>
28
29#include <fairlogger/Logger.h>
30
31#include <algorithm>
32#include <cmath>
33#include <map>
34#include <string>
35#include <utility>
36#include <vector>
37
38using V3 = vecgeom::Vector3D<double>;
39
40namespace o2::simsetup
41{
42
43std::size_t checkVecGeomLocation(std::size_t samples)
44{
45 auto* world = vecgeom::GeoManager::Instance().GetWorld();
46 auto const* box = dynamic_cast<TGeoBBox const*>(gGeoManager->GetTopVolume()->GetShape());
47 if (world == nullptr || box == nullptr) {
48 LOG(warning) << "Cannot cross-check the VecGeom location: no world";
49 return 0;
50 }
51 TRandom3 rnd(12345);
52 vecgeom::NavigationState state;
53 std::map<std::string, std::size_t> byVolume;
54 std::size_t bad = 0;
55 for (std::size_t i = 0; i < samples; ++i) {
56 const double x = box->GetOrigin()[0] + box->GetDX() * (2. * rnd.Rndm() - 1.);
57 const double y = box->GetOrigin()[1] + box->GetDY() * (2. * rnd.Rndm() - 1.);
58 const double z = box->GetOrigin()[2] + box->GetDZ() * (2. * rnd.Rndm() - 1.);
59 auto* node = gGeoManager->FindNode(x, y, z);
60 const std::string tgeoName = (node != nullptr) ? node->GetVolume()->GetName() : "<outside>";
61 state.Clear();
62 vecgeom::GlobalLocator::LocateGlobalPoint(world, V3(x, y, z), state, true);
63 auto const* top = state.Top();
64 const std::string vgName = (top != nullptr) ? top->GetLogicalVolume()->GetName() : "<outside>";
65 if (tgeoName != vgName) {
66 ++bad;
67 ++byVolume[tgeoName + " -> " + vgName];
68 }
69 }
70 LOG(info) << "VecGeom location cross-check: " << bad << " of " << samples << " points land in a "
71 << "different volume than TGeo puts them in";
72 std::vector<std::pair<std::size_t, std::string>> worst;
73 for (auto const& e : byVolume) {
74 worst.emplace_back(e.second, e.first);
75 }
76 std::sort(worst.rbegin(), worst.rend());
77 for (std::size_t i = 0; i < worst.size() && i < 15; ++i) {
78 LOG(info) << " " << worst[i].first << " " << worst[i].second;
79 }
80 return bad;
81}
82
83std::size_t checkVecGeomVolume(const char* name, std::size_t perPlacement, std::size_t maxPlacements)
84{
85 auto* world = vecgeom::GeoManager::Instance().GetWorld();
86 auto* vol = gGeoManager->GetVolume(name);
87 if (world == nullptr || vol == nullptr) {
88 LOG(warning) << "Cannot cross-check volume " << name << ": not in the geometry";
89 return 0;
90 }
91 auto const* box = dynamic_cast<TGeoBBox const*>(vol->GetShape());
92 if (box == nullptr) {
93 LOG(warning) << "Cannot cross-check volume " << name << ": its shape has no bounding box";
94 return 0;
95 }
96 LOG(info) << "VecGeom sizes: " << vecgeom::GeoManager::Instance().GetRegisteredVolumesCount()
97 << " logical, " << vecgeom::GeoManager::Instance().GetPlacedVolumesCount() << " placed, "
98 << vecgeom::VPlacedVolume::GetIdCount() << " ids handed out";
99 // Divisions and other generated placements are where the two trees are most likely to differ
100 // in shape rather than in position, so report the daughter counts before sampling anything.
101 if (auto* lv = vecgeom::GeoManager::Instance().FindLogicalVolume(name)) {
102 LOG(info) << " " << name << ": TGeo " << vol->GetNdaughters() << " daughters, VecGeom "
103 << lv->GetDaughters().size();
104 }
105 {
106 TIter nextVol(gGeoManager->GetListOfVolumes());
107 TGeoVolume* mother = nullptr;
108 while ((mother = static_cast<TGeoVolume*>(nextVol())) != nullptr) {
109 bool found = false;
110 for (int i = 0; i < mother->GetNdaughters(); ++i) {
111 if (mother->GetNode(i)->GetVolume() == vol) {
112 found = true;
113 break;
114 }
115 }
116 if (!found) {
117 continue;
118 }
119 auto* mlv = vecgeom::GeoManager::Instance().FindLogicalVolume(mother->GetName());
120 LOG(info) << " mother " << mother->GetName() << ": TGeo " << mother->GetNdaughters()
121 << " daughters, VecGeom " << (mlv != nullptr ? (long)mlv->GetDaughters().size() : -1)
122 << (mother->GetFinder() != nullptr ? " (divided)" : "");
123 break;
124 }
125 }
126 TRandom3 rnd(4321);
127 vecgeom::NavigationState state;
128 std::map<std::string, std::size_t> byResult;
129 std::size_t placements = 0, tested = 0, bad = 0, badTransform = 0;
130 double worstTransform = 0.;
131 TGeoIterator it(gGeoManager->GetTopVolume());
132 TGeoNode* node = nullptr;
133 while ((node = it.Next()) != nullptr && placements < maxPlacements) {
134 if (node->GetVolume() != vol) {
135 continue;
136 }
137 ++placements;
138 TGeoHMatrix matrix = *it.GetCurrentMatrix();
139 for (std::size_t k = 0; k < perPlacement; ++k) {
140 double local[3] = {box->GetOrigin()[0] + box->GetDX() * (2. * rnd.Rndm() - 1.),
141 box->GetOrigin()[1] + box->GetDY() * (2. * rnd.Rndm() - 1.),
142 box->GetOrigin()[2] + box->GetDZ() * (2. * rnd.Rndm() - 1.)};
143 if (!vol->GetShape()->Contains(local)) {
144 continue;
145 }
146 double global[3];
147 matrix.LocalToMaster(local, global);
148 ++tested;
149 auto* found = gGeoManager->FindNode(global[0], global[1], global[2]);
150 const std::string tgeoName = (found != nullptr) ? found->GetVolume()->GetName() : "<outside>";
151 state.Clear();
152 vecgeom::GlobalLocator::LocateGlobalPoint(world, V3(global[0], global[1], global[2]), state, true);
153 auto const* top = state.Top();
154 const std::string vgName = (top != nullptr) ? top->GetLogicalVolume()->GetName() : "<outside>";
155 if (tgeoName != vgName) {
156 ++bad;
157 ++byResult[tgeoName + " -> " + vgName];
158 continue;
159 }
160 // The volume is right; check that the state also composes the right transform. That goes
161 // through the navigation index table, which is built separately from the daughter lists
162 // the locators use, so it can be wrong where containment looks perfect.
163 vecgeom::Transformation3D trans;
164 state.TopMatrix(trans);
165 const auto vgLocal = trans.Transform(V3(global[0], global[1], global[2]));
166 const double d = std::sqrt((vgLocal[0] - local[0]) * (vgLocal[0] - local[0]) +
167 (vgLocal[1] - local[1]) * (vgLocal[1] - local[1]) +
168 (vgLocal[2] - local[2]) * (vgLocal[2] - local[2]));
169 worstTransform = std::max(worstTransform, d);
170 if (d > 1.e-6) {
171 ++badTransform;
172 }
173 }
174 }
175 LOG(info) << "VecGeom volume cross-check " << name << ": worst local-point deviation "
176 << worstTransform << " cm, " << badTransform << " points above 1e-6 cm";
177 LOG(info) << "VecGeom volume cross-check " << name << ": " << placements << " placements, " << tested
178 << " points, " << bad << " located differently than TGeo";
179 for (auto const& e : byResult) {
180 LOG(info) << " " << e.second << " " << e.first;
181 }
182 return bad;
183}
184
185void checkVecGeomRays(std::size_t rays)
186{
187 auto* world = vecgeom::GeoManager::Instance().GetWorld();
188 if (world == nullptr) {
189 return;
190 }
191 TRandom3 rnd(97531);
192 std::map<std::string, std::size_t> tgeoSeen, vgSeen;
193 constexpr std::size_t kMaxSteps = 20000;
194
195 for (std::size_t r = 0; r < rays; ++r) {
196 const double cost = 2. * rnd.Rndm() - 1.;
197 const double sint = std::sqrt(1. - cost * cost);
198 const double phi = 2. * M_PI * rnd.Rndm();
199 const double dir[3] = {sint * std::cos(phi), sint * std::sin(phi), cost};
200
201 gGeoManager->InitTrack(0., 0., 0., dir[0], dir[1], dir[2]);
202 for (std::size_t k = 0; k < kMaxSteps && !gGeoManager->IsOutside(); ++k) {
203 ++tgeoSeen[gGeoManager->GetCurrentVolume()->GetName()];
204 gGeoManager->FindNextBoundaryAndStep();
205 }
206
207 vecgeom::NavigationState cur, next;
208 V3 pos(0., 0., 0.);
209 const V3 vdir(dir[0], dir[1], dir[2]);
210 vecgeom::GlobalLocator::LocateGlobalPoint(world, pos, cur, true);
211 for (std::size_t k = 0; k < kMaxSteps && cur.Top() != nullptr; ++k) {
212 ++vgSeen[cur.Top()->GetLogicalVolume()->GetName()];
213 double safety = 0.;
214 auto const* nav = cur.Top()->GetLogicalVolume()->GetNavigator();
215 const double step = nav->ComputeStepAndSafetyAndPropagatedState(pos, vdir, vecgeom::kInfLength, cur, next,
216 false, safety);
217 if (!(step < vecgeom::kInfLength)) {
218 break;
219 }
220 pos = pos + step * vdir;
221 cur = next;
222 }
223 }
224
225 // A volume one engine never enters is the sharpest signal: a hit can only be made in a
226 // volume a track actually reaches, so these are the ones that lose a detector its hits.
227 std::vector<std::string> neverVG, neverTGeo;
228 std::vector<std::pair<long, std::string>> diff;
229 for (auto const& e : tgeoSeen) {
230 const std::size_t vg = vgSeen.count(e.first) ? vgSeen[e.first] : 0;
231 if (vg == 0) {
232 neverVG.push_back(e.first);
233 }
234 const long d = static_cast<long>(e.second) - static_cast<long>(vg);
235 if (d != 0) {
236 diff.emplace_back(std::labs(d), e.first);
237 }
238 }
239 for (auto const& e : vgSeen) {
240 if (tgeoSeen.count(e.first) == 0) {
241 neverTGeo.push_back(e.first);
242 }
243 }
244 std::sort(diff.rbegin(), diff.rend());
245 LOG(info) << "VecGeom ray cross-check over " << rays << " rays: " << tgeoSeen.size() << " volumes seen by TGeo, "
246 << vgSeen.size() << " by VecGeom, " << diff.size() << " entered a different number of times";
247 LOG(info) << " never entered by VecGeom (" << neverVG.size() << "):";
248 for (std::size_t i = 0; i < neverVG.size() && i < 60; ++i) {
249 LOG(info) << " " << neverVG[i] << " (TGeo " << tgeoSeen[neverVG[i]] << ")";
250 }
251 LOG(info) << " never entered by TGeo (" << neverTGeo.size() << "):";
252 for (std::size_t i = 0; i < neverTGeo.size() && i < 30; ++i) {
253 LOG(info) << " " << neverTGeo[i] << " (VecGeom " << vgSeen[neverTGeo[i]] << ")";
254 }
255}
256
257} // namespace o2::simsetup
std::unique_ptr< expressions::Node > node
int32_t i
SurfaceTrackState state
uint16_t pos
Definition RawData.h:3
vecgeom::Vector3D< double > V3
GLint GLenum GLint x
Definition glcorearb.h:403
GLdouble GLdouble GLdouble GLdouble top
Definition glcorearb.h:4077
GLuint const GLchar * name
Definition glcorearb.h:781
GLsizei samples
Definition glcorearb.h:1309
GLint y
Definition glcorearb.h:270
GLboolean r
Definition glcorearb.h:1233
GLsizei const GLint * box
Definition glcorearb.h:4697
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
void checkVecGeomRays(std::size_t rays)
std::size_t checkVecGeomLocation(std::size_t samples)
std::size_t checkVecGeomVolume(const char *name, std::size_t perPlacement, std::size_t maxPlacements)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"