GeometryTestAlg.cxx
Go to the documentation of this file.
1 /**
2  * @file GeometryTestAlg.cxx
3  * @brief Unit test for geometry functionalities: implementation file
4  * @date 2011/02/17
5  * @author brebel@fnal.gov
6  * @see GeometryTestAlg.h
7  */
8 
9 // our header
10 #include "test/Geometry/GeometryTestAlg.h"
11 
12 // GArSoft includes
13 #include "Geometry/GeometryCore.h"
14 #include "Geometry/AuxDetGeo.h"
15 
16 // Framework includes
17 #include "fhiclcpp/ParameterSet.h"
19 
20 // ROOT includes
21 #include "TGeoManager.h"
22 #include "TStopwatch.h"
23 #include "TGeoNode.h"
24 #include "TGeoVolume.h"
25 #include "TClass.h"
26 
27 // C/C++ standard libraries
28 #include <cmath>
29 #include <vector>
30 #include <iterator> // std::inserter()
31 #include <algorithm> // std::copy()
32 #include <set>
33 #include <array>
34 #include <string>
35 #include <sstream>
36 #include <iostream>
37 #include <cassert>
38 #include <limits> // std::numeric_limits<>
39 #include <initializer_list>
40 
41 
42 namespace {
43  template <typename T>
44  inline T sqr(T v) { return v*v; }
45 
46  template <typename T>
47  std::string to_string(const T& v) {
48  std::ostringstream sstr;
49  sstr << v;
50  return sstr.str();
51  } // ::to_string()
52 
53 } // local namespace
54 
55 
56 namespace simple_geo {
57 
58  struct Point2D {
59  double y = 0.;
60  double z = 0.;
61 
62  Point2D() = default;
63  Point2D(double new_y, double new_z): y(new_y), z(new_z) {}
64  }; // struct Point2D
65 
66  Point2D operator+ (Point2D const& a, Point2D const& b)
67  { return { a.y + b.y, a.z + b.z }; }
68  Point2D operator* (Point2D const& p, double f)
69  { return { p.y * f, p.z * f }; }
70  Point2D operator/ (Point2D const& p, double f)
71  { return { p.y / f, p.z / f }; }
72  template <typename Stream>
73  Stream& operator<< (Stream& out, Point2D const& p)
74  { out << "( " << p.y << " ; " << p.z << " )"; return out; }
75 
76  class Area {
77  public:
78  Area() = default;
79 
80  Area(Point2D const& a, Point2D const& b)
81  {
82  set_sorted(min.y, max.y, a.y, b.y);
83  set_sorted(min.z, max.z, a.z, b.z);
84  } // Area(Point2D x2)
85 
86  Point2D const& Min() const { return min; }
87  Point2D const& Max() const { return max; }
88  Point2D Center() const { return (min + max) / 2; }
89  double DeltaY() const { return Max().y - Min().y; }
90  double DeltaZ() const { return Max().z - Min().z; }
91  bool isEmpty() const { return (DeltaY() == 0) || (DeltaZ() == 0); }
92 
93  void IncludePoint(Point2D const& point)
94  {
95  set_min_max(min.y, max.y, point.y);
96  set_min_max(min.z, max.z, point.z);
97  } // Include()
98 
99  void Include(Area const& area)
100  { IncludePoint(area.min); IncludePoint(area.max); }
101 
102  void Intersect(Area const& area)
103  {
104  set_max(min.y, area.min.y);
105  set_min(max.y, area.max.y);
106  set_max(min.z, area.min.z);
107  set_min(max.z, area.max.z);
108  }
109 
110  protected:
112 
113  void set_min(double& var, double val) { if (val < var) var = val; }
114  void set_max(double& var, double val) { if (val > var) var = val; }
115  void set_min_max(double& min_var, double& max_var, double val)
116  { set_min(min_var, val); set_max(max_var, val); }
117  void set_sorted(double& min_var, double& max_var, double a, double b)
118  {
119  if (a > b) { min_var = b; max_var = a; }
120  else { min_var = a; max_var = b; }
121  }
122  }; // class Area
123 
124 } // namespace simple_geo
125 
126 namespace gar{
127  namespace geo{
128 
129 
130  //......................................................................
131  GeometryTestAlg::GeometryTestAlg(fhicl::ParameterSet const& pset)
132  : geom(nullptr)
133  {
134  // initialize the list of non-fatal exceptions
135  std::vector<std::string> NonFatalErrors(pset.get<std::vector<std::string>>
136  ("ForgiveExceptions", std::vector<std::string>()));
137  std::copy(NonFatalErrors.begin(), NonFatalErrors.end(),
138  std::inserter(fNonFatalExceptions, fNonFatalExceptions.end()));
139 
140  // initialize the list of tests to be run
141  //
142  // our name selector accepts everything by default;
143  // the default set skips the following:
144  fRunTests.AddToDefinition("default",
145  "-CheckOverlaps",
146  "-ThoroughCheck");
147 
148  fRunTests.ParseNames("@default"); // let's start from default
149 
150  // read and parse the test list from the configuration parameters (if any)
151  fRunTests.Parse(pset.get<std::vector<std::string>>("RunTests", {}));
152 
153  std::ostringstream sstr;
155 
156  MF_LOG_INFO("GeometryTestAlg") << "Test selection:" << sstr.str();
157 
158  } // GeometryTestAlg::GeometryTestAlg()
159 
160  //......................................................................
161  unsigned int GeometryTestAlg::Run()
162  {
163 
164  if (!geom) {
165  throw cet::exception("GeometryTestAlg")
166  << "GeometryTestAlg not configured: no valid geometry provided.\n";
167  }
168 
169  unsigned int nErrors = 0; // currently unused
170 
171  // change the printed version number when changing the "GeometryTest" output
172  MF_LOG_VERBATIM("GeometryTest")
173  << "GeometryTest version 1.0";
174 
175  MF_LOG_VERBATIM("GeometryTest")
176  << "\tRunning on detector: '"
177  << geom->DetectorName()
178  << "'"
179  << "\n\tGeometry file: "
180  << geom->ROOTFile();
181 
182  try{
183  if (shouldRunTests("CheckOverlaps")) {
184  MF_LOG_INFO("GeometryTest") << "test for overlaps ...";
185  gGeoManager->CheckOverlaps(1e-5);
186  gGeoManager->PrintOverlaps();
187  if (!gGeoManager->GetListOfOverlaps()->IsEmpty()) {
188  mf::LogError("GeometryTest")
189  << gGeoManager->GetListOfOverlaps()->GetSize()
190  << " overlaps found in geometry during overlap test!";
191  ++nErrors;
192  }
193  MF_LOG_INFO("GeometryTest") << "complete.";
194  }
195 
196  if (shouldRunTests("ThoroughCheck")) {
197  MF_LOG_INFO("GeometryTest") << "thorough geometry test ...";
198  gGeoManager->CheckGeometryFull();
199  if (!gGeoManager->GetListOfOverlaps()->IsEmpty()) {
200  mf::LogError("GeometryTest")
201  << gGeoManager->GetListOfOverlaps()->GetSize()
202  << " overlaps found in geometry during thorough test!";
203  ++nErrors;
204  }
205  MF_LOG_INFO("GeometryTest") << "complete.";
206  }
207 
208  if (shouldRunTests("FindVolumes")) {
209  MF_LOG_INFO("GeometryTest") << "test FindAllVolumes method ...";
210  testFindVolumes();
211  MF_LOG_INFO("GeometryTest") << "complete.";
212  }
213 
214  if (shouldRunTests("NearestWire")) {
215  MF_LOG_INFO("GeometryTest") << "testNearestChannel...";
217  MF_LOG_INFO("GeometryTest") << "complete.";
218  }
219 
220  if (shouldRunTests("Stepping")) {
221  MF_LOG_INFO("GeometryTest") << "testStepping...";
222  testStepping();
223  MF_LOG_INFO("GeometryTest") << "complete.";
224  }
225 
226  }
227  catch (cet::exception &e) {
228  mf::LogWarning("GeometryTest") << "exception caught: \n" << e;
229  if (fNonFatalExceptions.count(e.category()) == 0) throw;
230  }
231 
232  std::ostringstream out;
233  if (!fRunTests.CheckQueryRegistry(out)) {
234  throw cet::exception("GeometryTest")
235  << "(postumous) configuration error detected!\n"
236  << out.rdbuf();
237  }
238 
239  mf::LogInfo log("GeometryTest");
240  log << "Tests completed:";
241  auto tests_run = fRunTests.AcceptedNames();
242  if (tests_run.empty()) {
243  log << "\n no test run";
244  }
245  else {
246  log << "\n " << tests_run.size() << " tests run:\t ";
247  for (std::string const& test_name: tests_run) log << " " << test_name;
248  }
249  auto tests_skipped = fRunTests.RejectedNames();
250  if (!tests_skipped.empty()) {
251  log << "\n " << tests_skipped.size() << " tests skipped:\t ";
252  for (std::string const& test_name: tests_skipped) log << " " << test_name;
253  }
254 
255  return nErrors;
256  } // GeometryTestAlg::Run()
257 
258 
259 
260  //......................................................................
262  {
263  uint32_t channels = geom->NChannels();
264  MF_LOG_VERBATIM("GeometryTest")
265  << "there are "
266  << channels
267  << " channels";
268 
269  return;
270  }
271 
272  //......................................................................
273  // great sanity check for geometry, only call in analyze when debugging
275  {
276  MF_LOG_VERBATIM("GeometryTest")
277  << " TPC: radius: "
278  << geom->TPCRadius()
279  << " length: "
280  << geom->TPCLength();
281 
282  TVector3 origin(0., 0., 0.);
283  TVector3 outside(1.e6, 1.e6, 1.e6);
284 
285  MF_LOG_VERBATIM("GeometryTest")
286  << "test check of point in world: \n(0, 0, 0) is in volume "
287  << geom->VolumeName(origin)
288  << "\n (1e6, 1e6, 1e6) is in volume "
289  << geom->VolumeName(outside);
290 
291  return;
292  }
293 
294  //--------------------------------------------------------------------------
296  MF_LOG_VERBATIM("GeometryTest")
297  << "Detector " << geom->DetectorName();
298  } // GeometryTestAlg::printAllGeometry()
299 
300 
301  //......................................................................
303 
304  unsigned int nErrors = 0;
305 
306  std::set<std::string> volume_names;
307  std::vector<TGeoNode const*> nodes;
308 
309  // world
310  volume_names.insert(geom->GetWorldVolumeName());
311  nodes = geom->FindAllVolumes(volume_names);
312  {
313  mf::LogVerbatim log("GeometryTest");
314  log
315  << "Found "
316  << nodes.size()
317  << " world volumes '"
318  << geom->GetWorldVolumeName() << "':";
319  for (TGeoNode const* node: nodes) {
320  TGeoVolume const* pVolume = node->GetVolume();
321  log << "\n - '" << pVolume->GetName() << "' (a "
322  << pVolume->GetShape()->GetName() << ")";
323  } // for
324  } // anonymous block
325  if (nodes.size() != 1) {
326  ++nErrors;
327  mf::LogError("GeometryTest")
328  << "Found " << nodes.size() << " world volumes '"
329  << geom->GetWorldVolumeName() << "! [expecting: one!!]";
330  } // if nodes
331 
332  return nErrors;
333  } // GeometryTestAlg::testFindWorldVolumes()
334 
335  //--------------------------------------------------------------------------
337 
338  unsigned int nErrors = 0;
339 
340  // search the full path of all TPCs
341  std::set<std::string> volume_names({"volTPC"});
342  std::vector<std::vector<TGeoNode const*>> node_paths = geom->FindAllVolumePaths(volume_names);
343 
344  mf::LogVerbatim log("GeometryTest");
345  log
346  << "Found "
347  << node_paths.size()
348  << " TPC volumes:";
349  for (auto const& path: node_paths) {
350  TGeoNode const* node = path.back();
351  TGeoVolume const* pVolume = node->GetVolume();
352  log << "\n - '" << pVolume->GetName() << "' (a "
353  << pVolume->GetShape()->GetName() << ") with " << (path.size()-1)
354  << " ancestors";
355  for (TGeoNode const* pNode: path) {
356  TGeoVolume const* pVolume_loc = pNode->GetVolume();
357  log << "\n * '" << pVolume_loc->GetName() << "' (a "
358  << pVolume_loc->GetShape()->GetName() << ") with a "
359  << pNode->GetMatrix()->IsA()->GetName() << " that "
360  << (pNode->GetMatrix()->IsTranslation()? "is": "is not")
361  << " a simple translation";
362  } // for node
363  } // for path
364 
365  return nErrors;
366  } // GeometryTestAlg::testFindTPCvolumePaths()
367 
368  //--------------------------------------------------------------------------
370  /*
371  * Finds and checks a selected number of volumes by name:
372  * - world volume
373  * - cryostat volumes
374  * - TPCs (returns full paths)
375  */
376 
377  unsigned int nErrors = 0;
378 
379  nErrors += testFindWorldVolumes();
380  nErrors += testFindTPCvolumePaths();
381 
382  if (nErrors != 0) {
383  throw cet::exception("FindVolumes")
384  << "Collected " << nErrors << " errors during FindAllVolumes() test!\n";
385  }
386 
387  } // GeometryTestAlg::testFindVolumes()
388 
389 
390  //......................................................................
392  {
393  // Even if you comment it out, please leave the TStopWatch code
394  // in this code for additional testing. The NearestChannel routine
395  // is the most frequently called in the simulation, so its execution time
396  // is an important component of GArSoft's speed.
397  //TStopwatch stopWatch;
398  //stopWatch.Start();
399 
400  float posWorld[3] = {0.0,0.0,0.0};
401  posWorld[1] = 0.5 * geom->TPCRadius();
402  posWorld[2] = 0.5 * geom->TPCLength();
403 
404  try{
405 
406  MF_LOG_VERBATIM("GeometryTestAlg")
407  << " find nearest channel to point ("
408  << posWorld[0]
409  << ", "
410  << posWorld[1]
411  << ", "
412  << posWorld[2]
413  << ")";
414 
415  // The float[] version tested here is used by the TVector3 version, so this test both.
416  unsigned int nearest = geom->NearestChannel(posWorld);
417 
418  MF_LOG_VERBATIM("GeometryTestAlg")
419  << "\t ...nearest channel to point: "
420  << nearest;
421 
422  // We also want to test the std::vector<float> version
423  std::array<float, 3> posWorldV;
424  for (int i = 0; i < 3; ++i) posWorldV[i] = posWorld[i];
425 
426  nearest = geom->NearestChannel(posWorldV.data());
427 
428  MF_LOG_VERBATIM("GeometryTestAlg")
429  << "\t ...nearest channel to point: "
430  << nearest;
431 
432  }
433  catch(cet::exception &e){
434  mf::LogWarning("GeoTestCaughtException") << e;
435  if (fNonFatalExceptions.count(e.category()) == 0) throw;
436  }
437 
438  //stopWatch.Stop();
439  MF_LOG_DEBUG("GeometryTest") << "\tdone testing nearest channel";
440  //stopWatch.Print();
441 
442  // trigger an exception with NearestChannel
443  MF_LOG_VERBATIM("GeometryTest")
444  << "\tattempt to cause an exception to be caught "
445  << "when looking for a nearest channel";
446 
447  // pick a position out of the world
448  geom->WorldBox(nullptr, posWorld + 0,
449  nullptr, posWorld + 1,
450  nullptr, posWorld + 2);
451  for (int i = 0; i < 3; ++i) posWorld[i] *= 2.;
452 
453  bool hasThrown = false;
454  unsigned int nearest_to_what = 0;
455  try{
456  nearest_to_what = geom->NearestChannel(posWorld);
457  }
458  catch(const cet::exception& e){
459  MF_LOG_WARNING("GeoTestCaughtException")
460  << "caught execpetion: "
461  << e;
462  hasThrown = true;
463  }
464 
465  if (!hasThrown) {
466  MF_LOG_WARNING("GeoTestErrorNearestChannel")
467  << "GeometryCore::NearestChannel() did not raise an exception on out-of-world position ("
468  << posWorld[0]
469  << "; "
470  << posWorld[1]
471  << "; "
472  << posWorld[2]
473  << "), and returned "
474  << nearest_to_what
475  << " instead.\n This is normally considered a failure.";
476  }
477 
478  return;
479  }
480 
481  //......................................................................
483  {
484  //
485  // Test stepping.
486  //
487  double xyz[3] = {0.};
488  double dxyz[3] = {0., 0., 1.};
489 
490 
491  MF_LOG_VERBATIM("GeometryTest")
492  << "initial"
493  << "\n\tposition:" << xyz[0] << "\t" << xyz[1] << "\t" << xyz[2]
494  << "\n\tdirection:" << dxyz[0] << "\t" << dxyz[1] << "\t" << dxyz[2];
495 
496  gGeoManager->InitTrack(xyz, dxyz);
497  for (int i=0; i<10; ++i) {
498  const double* pos = gGeoManager->GetCurrentPoint();
499  const double* dir = gGeoManager->GetCurrentDirection();
500  MF_LOG_VERBATIM("GeometryTest") << "\tnode = "
501  << gGeoManager->GetCurrentNode()->GetName()
502  << "\n\t\tpos=" << "\t"
503  << pos[0] << "\t"
504  << pos[1] << "\t"
505  << pos[2]
506  << "\n\t\tdir=" << "\t"
507  << dir[0] << "\t"
508  << dir[1] << "\t"
509  << dir[2]
510  << "\n\t\tmat = "
511  << gGeoManager->GetCurrentNode()->GetVolume()->GetMaterial()->GetName();
512 
513  gGeoManager->FindNextBoundary();
514  gGeoManager->FindNormal();
515  gGeoManager->Step(kTRUE,kTRUE);
516  }
517 
518  xyz[0] = 306.108; xyz[1] = -7.23775; xyz[2] = 856.757;
519  gGeoManager->InitTrack(xyz, dxyz);
520  MF_LOG_VERBATIM("GeometryTest") << "\tnode = "
521  << gGeoManager->GetCurrentNode()->GetName()
522  << "\n\tmat = "
523  << gGeoManager->GetCurrentNode()->GetVolume()->GetMaterial()->GetName();
524 
525  gGeoManager->GetCurrentNode()->GetVolume()->GetMaterial()->Print();
526 
527  }
528 
529  //......................................................................
530  inline bool GeometryTestAlg::shouldRunTests(std::string test_name) const {
531  return fRunTests(test_name);
532  } // GeometryTestAlg::shouldRunTests()
533 
534  }//end namespace
535 } // gar
testing::NameSelector fRunTests
test filter
std::vector< TGeoNode const * > FindAllVolumes(std::set< std::string > const &vol_names) const
Returns all the nodes with volumes with any of the specified names.
double DeltaY() const
Point2D Center() const
void set_sorted(double &min_var, double &max_var, double a, double b)
void Include(Area const &area)
std::string string
Definition: nybbler.cc:12
void AddToDefinition(std::string set_name, NAMES...names)
Parses a list of names and add them to the specified definition.
Definition: NameSelector.h:127
void Intersect(Area const &area)
Names_t RejectedNames() const
Returns a list of names that were rejected.
Definition: NameSelector.h:153
void ParseNames(NAMES...names)
Parses a list of names and adds them to the selector.
Definition: NameSelector.h:97
void PrintConfiguration(std::ostream &) const
Prints the configuration into a stream.
string dir
MaybeLogger_< ELseverityLevel::ELsev_error, false > LogError
Area(Point2D const &a, Point2D const &b)
bool isEmpty() const
Point2D(double new_y, double new_z)
Point2D operator*(Point2D const &p, double f)
void WorldBox(float *xlo, float *xhi, float *ylo, float *yhi, float *zlo, float *zhi) const
Fills the arguments with the boundaries of the world.
T sqr(T v)
unsigned int testFindWorldVolumes()
unsigned int testFindTPCvolumePaths()
Names_t AcceptedNames() const
Returns a list of names that were accepted.
Definition: NameSelector.h:150
unsigned int NearestChannel(float const worldLoc[3]) const
Returns the ID of the channel nearest to the specified position.
std::set< std::string > fNonFatalExceptions
const double e
#define nodes
float TPCRadius() const
Returns the radius of the TPC (y or z direction)
Definition: GeometryCore.h:755
void Parse(LIST const &items)
Parses the names in the list and adds them to the selector.
Definition: NameSelector.h:77
Point2D const & Min() const
const double a
T get(std::string const &key) const
Definition: ParameterSet.h:271
bool CheckQueryRegistry() const
Checks that no known element with valid response was left unqueried.
Definition: NameSelector.h:160
p
Definition: test.py:223
void IncludePoint(Point2D const &point)
static int max(int a, int b)
Point2D operator/(Point2D const &p, double f)
#define MF_LOG_INFO(category)
virtual unsigned int Run()
Runs the test, returns a number of errors (very unlikely!)
Point2D const & Max() const
gar::geo::GeometryCore const * geom
pointer to geometry service provider
bool shouldRunTests(std::string test_name) const
General GArSoft Utilities.
Stream & operator<<(Stream &out, Point2D const &p)
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
int var
Definition: 018_def.c:9
double DeltaZ() const
void set_min(double &var, double val)
void set_max(double &var, double val)
#define MF_LOG_VERBATIM(category)
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
#define MF_LOG_DEBUG(id)
std::vector< std::vector< TGeoNode const * > > FindAllVolumePaths(std::set< std::string > const &vol_names) const
Returns paths of all nodes with volumes with the specified names.
std::string DetectorName() const
Returns a string with the name of the detector, as configured.
Definition: GeometryCore.h:495
T copy(T const &v)
static bool * b
Definition: config.cpp:1043
float TPCLength() const
Returns the length of the TPC (x direction)
Definition: GeometryCore.h:763
const std::string VolumeName(TVector3 const &point) const
Returns the name of the deepest volume containing specified point.
#define MF_LOG_WARNING(category)
unsigned int NChannels() const
Point2D operator+(Point2D const &a, Point2D const &b)
LArSoft geometry interface.
Definition: ChannelGeo.h:16
std::string GetWorldVolumeName() const
std::string ROOTFile() const
Returns the full directory path to the geometry file source.
Definition: GeometryCore.h:477
std::string to_string(ModuleType const mt)
Definition: ModuleType.h:34
void set_min_max(double &min_var, double &max_var, double val)
constexpr Point origin()
Returns a origin position with a point of the specified type.
Definition: geo_vectors.h:227
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33