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
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events d
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"