31 namespace dd4hep {
namespace detail {
namespace tools {
57 struct DetectorCheck {
58 using StructureElements = std::map<DetElement, size_t>;
64 const std::string& test;
65 FND(
const std::string& c) : test(c) {}
66 bool operator()(
const VolIDs::value_type& c)
const {
return c.first == test; }
69 size_t elements { 0 };
71 void reset() { elements = errors = 0; }
72 counters& operator+=(
const counters& c) {
73 elements += c.elements;
86 std::string m_name {
"GeometryCheck" };
88 counters m_place_counters, m_sens_counters, m_geo_counters, m_struct_counters;
89 StructureElements m_structure_elements;
91 bool check_structure {
false };
92 bool check_geometry {
false };
93 bool check_placements {
false };
94 bool check_volmgr {
false };
95 bool check_sensitive {
false };
96 bool ignore_detector {
false };
101 DetectorCheck(
Detector& description);
103 virtual ~DetectorCheck() =
default;
123 static long run(
Detector& description,
int argc,
char** argv);
124 static void help(
int argc,
char** argv);
126 const char* tag_fail(
size_t errs) {
127 return errs==0 ?
"PASSED" :
"FAILED";
133 DetectorCheck::DetectorCheck(
Detector& desc)
134 : description(desc), m_mapping(desc.world())
140 m_current_sensitive = description.sensitiveDetector(de.
name());
142 if ( m_current_sensitive.isValid() ) {
143 m_current_iddesc = m_current_sensitive.readout().idSpec();
145 return m_current_sensitive;
148 void DetectorCheck::execute(
DetElement sdet,
size_t depth) {
149 const char* line =
"============================";
150 struct counters count_volmgr_sens, count_volmgr_place;
151 struct counters total, count_struct;
152 struct counters count_geo, count_geo_sens;
155 ++m_place_counters.errors;
156 except(
"VolumeMgrTest",
"The detector element is not known to the geometry.");
161 m_current_detector = m_det;
164 if ( check_sensitive || check_volmgr ) {
165 if ( m_det == m_det.world() ) {
170 m_current_sensitive = description.sensitiveDetector(m_det.name());
171 if ( !m_current_sensitive.isValid() ) {
172 printout(ERROR, m_name,
173 "The sensitive detector of subdetector %s "
174 "is not known to the geometry.", m_det.name());
177 m_current_iddesc = m_current_sensitive.readout().idSpec();
181 if ( check_structure ) {
182 printout(ALWAYS, m_name,
"%s%s Executing STRUCTURE test %s%s", line, line, line, line);
184 checkDetElementTree(m_det.path(), m_det, pv);
185 count_struct.elements = m_structure_elements.size();
186 count_struct.errors = m_struct_counters.errors;
187 total += count_struct;
188 m_structure_elements.clear();
189 m_struct_counters.reset();
191 if ( check_geometry ) {
192 printout(ALWAYS, m_name,
"%s%s Executing GEOMETRY test %s%s", line, line, line, line);
194 checkVolumeTree(m_det, pv);
195 count_geo = m_geo_counters;
196 count_geo_sens = m_sens_counters;
197 total += count_geo_sens;
199 m_sens_counters.reset();
200 m_geo_counters.reset();
203 if ( check_volmgr ) {
208 printout(ALWAYS, m_name,
"%s%s Executing VOLUME MANAGER test %s%s", line, line, line, line);
209 chain.emplace_back(pv);
210 m_volMgr = description.volumeManager();
211 if ( !m_volMgr.isValid() ) {
212 printout(ERROR, m_name,
"Volume manager is not instantiated. Required for test!");
215 if ( pv.
volume() != description.worldVolume() ) {
218 m_sens_counters.reset();
219 m_current_detector = m_det;
220 checkManagerVolumeTree(m_det, pv, std::move(ids), chain, 1, depth);
221 count_volmgr_place = m_place_counters;
222 count_volmgr_sens = m_sens_counters;
223 total += count_volmgr_place;
224 total += count_volmgr_sens;
225 m_place_counters.reset();
226 m_sens_counters.reset();
229 if ( check_structure ) {
230 printout(count_struct.errors > 0 ? ERROR : ALWAYS,
231 m_name,
"+++ %s: Checked %10ld structure elements. Num.Errors:%6ld (structure test)",
232 tag_fail(count_struct.errors), count_struct.elements, count_struct.errors);
234 if ( check_geometry ) {
235 if ( check_sensitive ) {
236 printout(count_geo_sens.errors > 0 ? ERROR : ALWAYS,
237 m_name,
"+++ %s: Checked %10ld sensitive elements. Num.Errors:%6ld (geometry test)",
238 tag_fail(count_geo_sens.errors), count_geo_sens.elements, count_geo_sens.errors);
240 printout(count_geo.errors > 0 ? ERROR : ALWAYS,
241 m_name,
"+++ %s: Checked %10ld placements. Num.Errors:%6ld (geometry test)",
242 tag_fail(count_geo.errors), count_geo.elements, count_geo.errors);
244 if ( check_volmgr ) {
245 if ( check_sensitive ) {
246 printout(count_volmgr_sens.errors > 0 ? ERROR : ALWAYS,
247 m_name,
"+++ %s: Checked %10ld sensitive elements. Num.Errors:%6ld (phys.VolID test)",
248 tag_fail(count_volmgr_sens.errors), count_volmgr_sens.elements, count_volmgr_sens.errors);
250 printout(count_volmgr_place.errors > 0 ? ERROR : ALWAYS,
251 m_name,
"+++ %s: Checked %10ld sensitive placements. Num.Errors:%6ld (phys.VolID test)",
252 tag_fail(count_volmgr_place.errors), count_volmgr_sens.elements, count_volmgr_place.errors);
254 printout(ALWAYS, m_name,
"+++ %s: Checked a total of %11ld elements. Num.Errors:%6ld (Some elements checked twice)",
255 tag_fail(total.errors), total.elements, total.errors);
260 bool det_valid =
true;
261 bool parent_valid =
true;
262 bool place_valid =
true;
263 bool det_place_valid =
true;
264 bool vol_valid =
true;
265 auto nerrs = m_struct_counters.errors;
266 const char* de_path = detector.
path().c_str();
269 printout(ERROR, m_name,
"Invalid DetElement placement: %s", de_path);
270 ++m_struct_counters.errors;
273 if ( detector.
path() != path ) {
274 printout(ERROR, m_name,
"Invalid DetElement [path mismatch]: %s <> %s",
275 de_path, path.c_str());
276 ++m_struct_counters.errors;
279 printout(ERROR, m_name,
"Invalid DetElement [No parent]: %s", de_path);
280 ++m_struct_counters.errors;
281 parent_valid =
false;
284 printout(ERROR, m_name,
"Invalid DetElement [No placement]: %s", de_path);
285 ++m_struct_counters.errors;
286 det_place_valid =
false;
289 printout(ERROR, m_name,
"Invalid DetElement [No volume]: %s", de_path);
290 ++m_struct_counters.errors;
294 printout(ERROR, m_name,
"Invalid DetElement [Mismatched placement]: %s", de_path);
295 ++m_struct_counters.errors;
296 det_place_valid =
false;
298 auto count = ++m_structure_elements[detector];
301 printout(ERROR, m_name,
"DetElement %s parent: %s is placed %ld times! Only single placement allowed.",
302 de_path, par.
isValid() ? par.
path().c_str() :
"", m_structure_elements[detector]);
303 ++m_struct_counters.errors;
307 printout(ERROR, m_name,
"Invalid DetElement [No ideal alignment]: %s", de_path);
308 ++m_struct_counters.errors;
312 printout(ERROR, m_name,
"Invalid DetElement [No survey alignment]: %s", de_path);
313 ++m_struct_counters.errors;
317 if ( matrix.IsIdentity() ) {
320 printout(nerrs != m_struct_counters.errors ? ERROR : INFO, m_name,
321 "DetElement %s [%s] parent: %s placement: %s [%s] volume: %s",
322 path.c_str(), yes_no(det_valid), yes_no(parent_valid), yes_no(det_place_valid),
323 yes_no(place_valid), yes_no(vol_valid));
324 return nerrs == m_struct_counters.errors;
329 auto nerrs = m_struct_counters.errors;
331 printout(ERROR, m_name,
"Invalid DetElement seen: %s", path.c_str());
332 ++m_struct_counters.errors;
335 bool is_world = detector == detector.
world();
337 checkDetElement(path, detector, pv);
339 for (
const auto& c : detector.
children() ) {
344 m_current_detector = de;
347 printout(ERROR, m_name,
"Invalid DetElement [Parent mismatch]: %s", de.
path().c_str());
348 printout(ERROR, m_name,
" apparent parent: %s structural parent: %s",
350 ++m_struct_counters.errors;
353 checkDetElementTree(path +
"/" + c.first, de, de.
placement());
355 return nerrs == m_struct_counters.errors;
361 ++m_geo_counters.elements;
364 printout(ERROR, m_name,
"Invalid DetElement [Invalid handle]");
365 ++m_geo_counters.errors;
369 printout(ERROR, m_name,
"Invalid PlacedVolume [Invalid handle] DetElement: %s", e.
path().c_str());
370 ++m_geo_counters.errors;
375 printout(ERROR, m_name,
"Invalid Volume [Invalid handle] DetElement: %s", e.
path().c_str());
376 ++m_geo_counters.errors;
382 ++m_sens_counters.elements;
384 printout(ERROR, m_name,
"Invalid SensitiveDetector DetElement: %s", e.
path().c_str());
385 ++m_sens_counters.errors;
389 printout(ERROR, m_name,
"Inconsistent sensitive detectors for DetElement: %s", e.
path().c_str());
390 ++m_sens_counters.errors;
397 const TGeoNode* current = pv.
ptr();
398 TObjArray* nodes = current->GetNodes();
399 int num_children = nodes ? nodes->GetEntriesFast() : 0;
400 bool is_world = detector == description.
world();
403 checkSingleVolume(detector, pv);
405 for(
int i=0; i < num_children; ++i) {
406 TGeoNode* node = (TGeoNode*)nodes->At(i);
411 m_current_detector = de;
412 get_current_sensitive_detector();
416 for (
const auto& c : detector.
children() ) {
417 if ( c.second.placement() == place ) {
422 checkVolumeTree(de, place);
432 std::stringstream err, log;
438 ++m_place_counters.elements;
441 vid = m_current_iddesc.encode(child_ids);
442 top_sdet = m_volMgr.lookupDetector(vid);
443 det_elem = m_volMgr.lookupDetElement(vid);
444 mgr_ctxt = m_volMgr.lookupContext(vid);
447 PlacedVolume det_place = m_volMgr.lookupDetElementPlacement(vid);
448 ++m_sens_counters.elements;
449 if ( !ignore_detector && pv.
ptr() != det_place.
ptr() ) {
450 err <<
"VolumeMgrTest: Wrong placement "
451 <<
" got " << det_place.
name() <<
" (" << (
void*)det_place.
ptr() <<
")"
452 <<
" instead of " << pv.
name() <<
" (" << (
void*)pv.
ptr() <<
") "
453 <<
" vid:" << volumeID(vid);
454 ++m_place_counters.errors;
456 else if ( top_sdet.
ptr() != detector.
ptr() ) {
457 top_sdet = m_volMgr.lookupDetector(vid);
458 err <<
"VolumeMgrTest: Wrong associated sub-detector element vid=" << volumeID(vid)
459 <<
" got " << top_sdet.
path() <<
" (" << (
void*)top_sdet.
ptr() <<
") "
460 <<
" instead of " << detector.
path() <<
" (" << (
void*)detector.
ptr() <<
")"
461 <<
" vid:" << volumeID(vid);
462 ++m_place_counters.errors;
466 err <<
"VolumeMgrTest: Wrong associated detector element vid=" << volumeID(vid)
467 <<
" got " << det_elem.
path() <<
" (" << (
void*)det_elem.
ptr() <<
") "
468 <<
" instead of " << detector.
path() <<
" (" << (
void*)detector.
ptr() <<
")"
469 <<
" vid:" << volumeID(vid);
470 ++m_place_counters.errors;
472 else if ( top_sdet.
ptr() != m_det.ptr() ) {
473 err <<
"VolumeMgrTest: Wrong associated detector "
474 <<
" vid:" << volumeID(vid);
475 ++m_place_counters.errors;
480 err <<
"Lookup " << pv.
name() <<
" id:" << volumeID(vid)
481 <<
" path:" << detector.
path() <<
" error:" << ex.what();
482 ++m_place_counters.errors;
487 log <<
"Volume:" << std::setw(50) << std::left << pv.
name();
490 log <<
" IDDesc:" << (
char*)(dsc.
ptr() == m_current_iddesc.ptr() ?
"OK " :
"BAD");
491 if ( dsc.
ptr() != m_current_iddesc.ptr() ) ++m_place_counters.errors;
494 log << std::setw(11) <<
" ";
496 id_desc = m_current_iddesc.
str(vid);
498 <<
" vid:" << volumeID(vid)
500 if ( !err.str().empty() ) {
501 printout(ERROR, m_det.name(),err.str()+
" "+log.str());
505 id_desc = m_current_iddesc.str(det_elem.
volumeID());
506 printout(INFO, m_det.name(),log.str());
507 printout(INFO, m_det.name(),
" Elt:%-64s vid:%s %s Parent-OK:%3s",
508 det_elem.
path().c_str(),volumeID(det_elem.
volumeID()).c_str(),
515 for (
size_t i = chain.size()-1; i > 0; --i) {
517 const TGeoMatrix* mat = chain[i]->GetMatrix();
518 trafo.MultiplyLeft(mat);
520 for (
size_t i = chain.size(); i > 0; --i) {
521 const TGeoMatrix* mat = chain[i-1]->GetMatrix();
522 if ( printLevel() <= INFO ) {
523 ::printf(
"Placement [%d] VolID:%s\t\t",
int(i),chain[i-1].volIDs().str().c_str());
527 det_elem = m_volMgr.lookupDetElement(vid);
528 if ( printLevel() <= INFO ) {
529 ::printf(
"Computed Trafo (from placements):\t\t");
531 ::printf(
"DetElement Trafo: %s [%s]\t\t",
532 det_elem.
path().c_str(),volumeID(det_elem.
volumeID()).c_str());
534 ::printf(
"VolumeMgr Trafo: %s [%s]\t\t",det_elem.
path().c_str(),volumeID(vid).c_str());
535 m_volMgr.worldTransformation(m_mapping,vid).Print();
539 if ( 0 == mgr_ctxt ) {
540 printout(ERROR, m_det.name(),
"VOLUME_MANAGER FAILED: Could not find entry for vid:%s.",
541 volumeID(vid).c_str());
542 ++m_place_counters.errors;
548 printout(ERROR, m_det.name(),
"DETELEMENT_PERSISTENCY FAILED: World transformation have DIFFERET pointer!");
549 ++m_place_counters.errors;
552 if ( !ignore_detector ) {
559 printout(ERROR, m_det.name(),
"DETELEMENT_PLACEMENT FAILED: World transformation DIFFER.");
560 ++m_place_counters.errors;
563 printout(INFO, m_det.name(),
"DETELEMENT_PLACEMENT: PASSED. All matrices equal: %s",
564 volumeID(vid).c_str());
573 printout(ERROR, m_det.name(),
"VOLUME_PLACEMENT FAILED: World transformation DIFFER.");
574 ++m_place_counters.errors;
577 printout(INFO, m_det.name(),
"VOLUME_PLACEMENT: PASSED. All matrices equal: %s",
578 volumeID(vid).c_str());
585 err <<
"Matrix " << pv.
name() <<
" id:" << volumeID(vid)
586 <<
" path:" << detector.
path() <<
" error:" << ex.what();
587 ++m_place_counters.errors;
595 size_t depth,
size_t mx_depth)
597 if ( depth <= mx_depth ) {
598 const TGeoNode* current = pv.
ptr();
599 TObjArray* nodes = current->GetNodes();
600 int num_children = nodes ? nodes->GetEntriesFast() : 0;
601 bool is_world = detector == description.
world();
603 for(
int i=0; i<num_children; ++i) {
604 TGeoNode* node = (TGeoNode*)nodes->At(i);
607 Chain child_chain(chain);
611 for (
const auto& c : detector.
children() ) {
612 if ( c.second.placement() == place ) {
617 m_current_detector = de;
618 get_current_sensitive_detector();
621 child_chain.emplace_back(place);
622 child_ids.
insert(child_ids.end(), place.volIDs().begin(), place.volIDs().end());
623 checkManagerSingleVolume(de, place, child_ids, child_chain);
624 checkManagerVolumeTree(de, place, std::move(child_ids), child_chain, depth+1, mx_depth);
632 "DD4hep_DetectorCheck -option [-option] \n"
633 " -help Print this help message \n"
634 " -name <subdetector name> Name of the subdetector to be checked \n"
635 " \"ALL\" or \"all\": loop over known subdetectors\n"
636 " \"world\" start from the mother of all... \n"
637 " -structure Check structural tree consistency \n"
638 " -geometry Check geometry tree consistency \n"
639 " -sensitve Check consistency between detector and volume \n"
640 " settings of sensitive detectors. \n"
641 " -volmgr Check volume manager entries against volIDs of \n"
642 " sensitive volume placements. \n\n"
643 " NOTE: Option requires proper PhysVolID setup \n"
644 " of the sensitive volume placements ! \n"
645 " -ignore_detector Ignore DetElement placement check for -volmgr \n"
647 std::cout <<
"Arguments: " << std::endl;
648 for(
int iarg=0; iarg<argc;++iarg) {
649 std::cout <<
"Argument[" << iarg <<
"] = " << argv[iarg] << std::endl;
655 long DetectorCheck::run(
Detector& description,
int argc,
char** argv) {
658 bool geometry =
false;
659 bool structure =
false;
660 bool sensitive =
false;
661 bool placements =
false;
662 bool ignore_de =
false;
663 printout(ALWAYS,
"DetectorCheck",
"++ Processing plugin...");
664 for(
int iarg=0; iarg<argc;++iarg) {
665 if ( argv[iarg] == 0 )
break;
666 if ( ::strncasecmp(argv[iarg],
"-name",4) == 0 && (iarg+1) < argc )
668 else if ( ::strncasecmp(argv[iarg],
"-structure",4) == 0 )
670 else if ( ::strncasecmp(argv[iarg],
"-placements",4) == 0 )
672 else if ( ::strncasecmp(argv[iarg],
"-volmgr",4) == 0 )
674 else if ( ::strncasecmp(argv[iarg],
"-geometry",4) == 0 )
676 else if ( ::strncasecmp(argv[iarg],
"-sensitive",4) == 0 )
678 else if ( ::strncasecmp(argv[iarg],
"-ignore_detelement",4) == 0 )
680 else if ( ::strncasecmp(argv[iarg],
"-help",4) == 0 )
685 if ( argc == 0 )
help(argc, argv);
686 if ( !name.empty() ) {
687 DetectorCheck test(description);
688 if ( name ==
"all" || name ==
"All" || name ==
"ALL" ) {
690 printout(INFO,
"DetectorCheck",
"++ Processing subdetector: %s",
det.second.name());
691 test.check_structure = structure;
692 test.check_placements = placements;
693 test.check_volmgr = volmgr;
694 test.check_geometry = geometry;
695 test.check_sensitive = sensitive;
696 test.ignore_detector = ignore_de;
697 test.execute(
det.second, 9999);
703 printout(INFO,
"DetectorCheck",
"++ Processing subdetector: %s",name.c_str());
704 test.check_structure = structure;
705 test.check_placements = placements;
706 test.check_volmgr = volmgr;
707 test.check_geometry = geometry;
708 test.check_sensitive = sensitive;
709 test.ignore_detector = ignore_de;
710 test.execute(
det, 9999);
719 const char* args[] = {
"-name", argc > 0 ? argv[0] :
"world",
"-structure",
"-geometry",
"-volmgr", 0 };
720 return DetectorCheck::run(description, 6, (
char**)args);