33 namespace dd4hep {
namespace detail {
namespace tools {
59 struct DetectorCheck {
60 using StructureElements = std::map<DetElement, size_t>;
66 const std::string& test;
67 FND(
const std::string& c) : test(c) {}
68 bool operator()(
const VolIDs::value_type& c)
const {
return c.first == test; }
71 size_t elements { 0 };
73 void reset() { elements = errors = 0; }
74 counters& operator+=(
const counters& c) {
75 elements += c.elements;
88 std::string m_name {
"GeometryCheck" };
90 counters m_place_counters, m_sens_counters, m_geo_counters, m_struct_counters;
91 StructureElements m_structure_elements;
93 bool check_structure {
false };
94 bool check_geometry {
false };
95 bool check_placements {
false };
96 bool check_volmgr {
false };
97 bool check_sensitive {
false };
98 bool ignore_detector {
false };
103 DetectorCheck(
Detector& description);
105 virtual ~DetectorCheck() =
default;
125 static long run(
Detector& description,
int argc,
char** argv);
126 static void help(
int argc,
char** argv);
128 const char* tag_fail(
size_t errs) {
129 return errs==0 ?
"PASSED" :
"FAILED";
135 DetectorCheck::DetectorCheck(
Detector& desc)
136 : description(desc), m_mapping(desc.world())
142 m_current_sensitive = description.sensitiveDetector(de.
name());
144 if ( m_current_sensitive.isValid() ) {
145 m_current_iddesc = m_current_sensitive.readout().idSpec();
147 return m_current_sensitive;
150 void DetectorCheck::execute(
DetElement sdet,
size_t depth) {
151 const char* line =
"============================";
152 struct counters count_volmgr_sens, count_volmgr_place;
153 struct counters total, count_struct;
154 struct counters count_geo, count_geo_sens;
157 ++m_place_counters.errors;
158 except(
"VolumeMgrTest",
"The detector element is not known to the geometry.");
163 m_current_detector = m_det;
166 if ( check_sensitive || check_volmgr ) {
167 if ( m_det == m_det.world() ) {
172 m_current_sensitive = description.sensitiveDetector(m_det.name());
173 if ( !m_current_sensitive.isValid() ) {
174 printout(ERROR, m_name,
175 "The sensitive detector of subdetector %s "
176 "is not known to the geometry.", m_det.name());
179 m_current_iddesc = m_current_sensitive.readout().idSpec();
183 if ( check_structure ) {
184 printout(ALWAYS, m_name,
"%s%s Executing STRUCTURE test %s%s", line, line, line, line);
186 checkDetElementTree(m_det.path(), m_det, pv);
187 count_struct.elements = m_structure_elements.size();
188 count_struct.errors = m_struct_counters.errors;
189 total += count_struct;
190 m_structure_elements.clear();
191 m_struct_counters.reset();
193 if ( check_geometry ) {
194 printout(ALWAYS, m_name,
"%s%s Executing GEOMETRY test %s%s", line, line, line, line);
196 checkVolumeTree(m_det, pv);
197 count_geo = m_geo_counters;
198 count_geo_sens = m_sens_counters;
199 total += count_geo_sens;
201 m_sens_counters.reset();
202 m_geo_counters.reset();
205 if ( check_volmgr ) {
210 printout(ALWAYS, m_name,
"%s%s Executing VOLUME MANAGER test %s%s", line, line, line, line);
211 chain.emplace_back(pv);
212 m_volMgr = description.volumeManager();
213 if ( !m_volMgr.isValid() ) {
214 printout(ERROR, m_name,
"Volume manager is not instantiated. Required for test!");
217 if ( pv.
volume() != description.worldVolume() ) {
220 m_sens_counters.reset();
221 m_current_detector = m_det;
222 checkManagerVolumeTree(m_det, pv, std::move(ids), chain, 1, depth);
223 count_volmgr_place = m_place_counters;
224 count_volmgr_sens = m_sens_counters;
225 total += count_volmgr_place;
226 total += count_volmgr_sens;
227 m_place_counters.reset();
228 m_sens_counters.reset();
231 if ( check_structure ) {
232 printout(count_struct.errors > 0 ? ERROR : ALWAYS,
233 m_name,
"+++ %s: Checked %10ld structure elements. Num.Errors:%6ld (structure test)",
234 tag_fail(count_struct.errors), count_struct.elements, count_struct.errors);
236 if ( check_geometry ) {
237 if ( check_sensitive ) {
238 printout(count_geo_sens.errors > 0 ? ERROR : ALWAYS,
239 m_name,
"+++ %s: Checked %10ld sensitive elements. Num.Errors:%6ld (geometry test)",
240 tag_fail(count_geo_sens.errors), count_geo_sens.elements, count_geo_sens.errors);
242 printout(count_geo.errors > 0 ? ERROR : ALWAYS,
243 m_name,
"+++ %s: Checked %10ld placements. Num.Errors:%6ld (geometry test)",
244 tag_fail(count_geo.errors), count_geo.elements, count_geo.errors);
246 if ( check_volmgr ) {
247 if ( check_sensitive ) {
248 printout(count_volmgr_sens.errors > 0 ? ERROR : ALWAYS,
249 m_name,
"+++ %s: Checked %10ld sensitive elements. Num.Errors:%6ld (phys.VolID test)",
250 tag_fail(count_volmgr_sens.errors), count_volmgr_sens.elements, count_volmgr_sens.errors);
252 printout(count_volmgr_place.errors > 0 ? ERROR : ALWAYS,
253 m_name,
"+++ %s: Checked %10ld sensitive placements. Num.Errors:%6ld (phys.VolID test)",
254 tag_fail(count_volmgr_place.errors), count_volmgr_sens.elements, count_volmgr_place.errors);
256 printout(ALWAYS, m_name,
"+++ %s: Checked a total of %11ld elements. Num.Errors:%6ld (Some elements checked twice)",
257 tag_fail(total.errors), total.elements, total.errors);
262 bool det_valid =
true;
263 bool parent_valid =
true;
264 bool place_valid =
true;
265 bool det_place_valid =
true;
266 bool vol_valid =
true;
267 auto nerrs = m_struct_counters.errors;
268 const char* de_path = detector.
path().c_str();
271 printout(ERROR, m_name,
"Invalid DetElement placement: %s", de_path);
272 ++m_struct_counters.errors;
275 if ( detector.
path() != path ) {
276 printout(ERROR, m_name,
"Invalid DetElement [path mismatch]: %s <> %s",
277 de_path, path.c_str());
278 ++m_struct_counters.errors;
281 printout(ERROR, m_name,
"Invalid DetElement [No parent]: %s", de_path);
282 ++m_struct_counters.errors;
283 parent_valid =
false;
286 printout(ERROR, m_name,
"Invalid DetElement [No placement]: %s", de_path);
287 ++m_struct_counters.errors;
288 det_place_valid =
false;
291 printout(ERROR, m_name,
"Invalid DetElement [No volume]: %s", de_path);
292 ++m_struct_counters.errors;
296 printout(ERROR, m_name,
"Invalid DetElement [Mismatched placement]: %s", de_path);
297 ++m_struct_counters.errors;
298 det_place_valid =
false;
300 auto count = ++m_structure_elements[detector];
303 printout(ERROR, m_name,
"DetElement %s parent: %s is placed %ld times! Only single placement allowed.",
304 de_path, par.
isValid() ? par.
path().c_str() :
"", m_structure_elements[detector]);
305 ++m_struct_counters.errors;
309 printout(ERROR, m_name,
"Invalid DetElement [No ideal alignment]: %s", de_path);
310 ++m_struct_counters.errors;
314 printout(ERROR, m_name,
"Invalid DetElement [No survey alignment]: %s", de_path);
315 ++m_struct_counters.errors;
319 if ( matrix.IsIdentity() ) {
322 printout(nerrs != m_struct_counters.errors ? ERROR : INFO, m_name,
323 "DetElement %s [%s] parent: %s placement: %s [%s] volume: %s",
324 path.c_str(), yes_no(det_valid), yes_no(parent_valid), yes_no(det_place_valid),
325 yes_no(place_valid), yes_no(vol_valid));
326 return nerrs == m_struct_counters.errors;
331 auto nerrs = m_struct_counters.errors;
333 printout(ERROR, m_name,
"Invalid DetElement seen: %s", path.c_str());
334 ++m_struct_counters.errors;
337 bool is_world = detector == detector.
world();
339 checkDetElement(path, detector, pv);
341 for (
const auto& c : detector.
children() ) {
346 m_current_detector = de;
349 printout(ERROR, m_name,
"Invalid DetElement [Parent mismatch]: %s", de.
path().c_str());
350 printout(ERROR, m_name,
" apparent parent: %s structural parent: %s",
352 ++m_struct_counters.errors;
355 checkDetElementTree(path +
"/" + c.first, de, de.
placement());
357 return nerrs == m_struct_counters.errors;
363 ++m_geo_counters.elements;
366 printout(ERROR, m_name,
"Invalid DetElement [Invalid handle]");
367 ++m_geo_counters.errors;
371 printout(ERROR, m_name,
"Invalid PlacedVolume [Invalid handle] DetElement: %s", e.
path().c_str());
372 ++m_geo_counters.errors;
377 printout(ERROR, m_name,
"Invalid Volume [Invalid handle] DetElement: %s", e.
path().c_str());
378 ++m_geo_counters.errors;
384 ++m_sens_counters.elements;
386 printout(ERROR, m_name,
"Invalid SensitiveDetector DetElement: %s", e.
path().c_str());
387 ++m_sens_counters.errors;
391 printout(ERROR, m_name,
"Inconsistent sensitive detectors for DetElement: %s", e.
path().c_str());
392 ++m_sens_counters.errors;
399 const TGeoNode* current = pv.
ptr();
400 TObjArray* nodes = current->GetNodes();
401 int num_children = nodes ? nodes->GetEntriesFast() : 0;
402 bool is_world = detector == description.
world();
405 checkSingleVolume(detector, pv);
407 for(
int i=0; i < num_children; ++i) {
408 TGeoNode* node = (TGeoNode*)nodes->At(i);
413 m_current_detector = de;
414 get_current_sensitive_detector();
418 for (
const auto& c : detector.
children() ) {
419 if ( c.second.placement() == place ) {
424 checkVolumeTree(de, place);
434 std::stringstream err, log;
440 ++m_place_counters.elements;
443 vid = m_current_iddesc.encode(child_ids);
444 top_sdet = m_volMgr.lookupDetector(vid);
445 det_elem = m_volMgr.lookupDetElement(vid);
446 mgr_ctxt = m_volMgr.lookupContext(vid);
449 PlacedVolume det_place = m_volMgr.lookupDetElementPlacement(vid);
450 ++m_sens_counters.elements;
451 if ( !ignore_detector && pv.
ptr() != det_place.
ptr() ) {
452 err <<
"VolumeMgrTest: Wrong placement "
453 <<
" got " << det_place.
name() <<
" (" << (
void*)det_place.
ptr() <<
")"
454 <<
" instead of " << pv.
name() <<
" (" << (
void*)pv.
ptr() <<
") "
455 <<
" vid:" << volumeID(vid);
456 ++m_place_counters.errors;
458 else if ( top_sdet.
ptr() != detector.
ptr() ) {
459 top_sdet = m_volMgr.lookupDetector(vid);
460 err <<
"VolumeMgrTest: Wrong associated sub-detector element vid=" << volumeID(vid)
461 <<
" got " << top_sdet.
path() <<
" (" << (
void*)top_sdet.
ptr() <<
") "
462 <<
" instead of " << detector.
path() <<
" (" << (
void*)detector.
ptr() <<
")"
463 <<
" vid:" << volumeID(vid);
464 ++m_place_counters.errors;
468 err <<
"VolumeMgrTest: Wrong associated detector element vid=" << volumeID(vid)
469 <<
" got " << det_elem.
path() <<
" (" << (
void*)det_elem.
ptr() <<
") "
470 <<
" instead of " << detector.
path() <<
" (" << (
void*)detector.
ptr() <<
")"
471 <<
" vid:" << volumeID(vid);
472 ++m_place_counters.errors;
474 else if ( top_sdet.
ptr() != m_det.ptr() ) {
475 err <<
"VolumeMgrTest: Wrong associated detector "
476 <<
" vid:" << volumeID(vid);
477 ++m_place_counters.errors;
482 err <<
"Lookup " << pv.
name() <<
" id:" << volumeID(vid)
483 <<
" path:" << detector.
path() <<
" error:" << ex.what();
484 ++m_place_counters.errors;
489 log <<
"Volume:" << std::setw(50) << std::left << pv.
name();
492 log <<
" IDDesc:" << (
char*)(dsc.
ptr() == m_current_iddesc.ptr() ?
"OK " :
"BAD");
493 if ( dsc.
ptr() != m_current_iddesc.ptr() ) ++m_place_counters.errors;
496 log << std::setw(11) <<
" ";
498 id_desc = m_current_iddesc.
str(vid);
500 <<
" vid:" << volumeID(vid)
502 if ( !err.str().empty() ) {
503 printout(ERROR, m_det.name(),err.str()+
" "+log.str());
507 id_desc = m_current_iddesc.str(det_elem.
volumeID());
508 printout(INFO, m_det.name(),log.str());
509 printout(INFO, m_det.name(),
" Elt:%-64s vid:%s %s Parent-OK:%3s",
510 det_elem.
path().c_str(),volumeID(det_elem.
volumeID()).c_str(),
517 for (
size_t i = chain.size()-1; i > 0; --i) {
519 const TGeoMatrix* mat = chain[i]->GetMatrix();
520 trafo.MultiplyLeft(mat);
522 for (
size_t i = chain.size(); i > 0; --i) {
523 const TGeoMatrix* mat = chain[i-1]->GetMatrix();
524 if ( printLevel() <= INFO ) {
525 ::printf(
"Placement [%d] VolID:%s\t\t",
int(i),chain[i-1].volIDs().str().c_str());
529 det_elem = m_volMgr.lookupDetElement(vid);
530 if ( printLevel() <= INFO ) {
531 ::printf(
"Computed Trafo (from placements):\t\t");
533 ::printf(
"DetElement Trafo: %s [%s]\t\t",
534 det_elem.
path().c_str(),volumeID(det_elem.
volumeID()).c_str());
536 ::printf(
"VolumeMgr Trafo: %s [%s]\t\t",det_elem.
path().c_str(),volumeID(vid).c_str());
537 m_volMgr.worldTransformation(m_mapping,vid).Print();
541 if ( 0 == mgr_ctxt ) {
542 printout(ERROR, m_det.name(),
"VOLUME_MANAGER FAILED: Could not find entry for vid:%s.",
543 volumeID(vid).c_str());
544 ++m_place_counters.errors;
550 printout(ERROR, m_det.name(),
"DETELEMENT_PERSISTENCY FAILED: World transformation have DIFFERET pointer!");
551 ++m_place_counters.errors;
554 if ( !ignore_detector ) {
561 printout(ERROR, m_det.name(),
"DETELEMENT_PLACEMENT FAILED: World transformation DIFFER.");
562 ++m_place_counters.errors;
565 printout(INFO, m_det.name(),
"DETELEMENT_PLACEMENT: PASSED. All matrices equal: %s",
566 volumeID(vid).c_str());
575 printout(ERROR, m_det.name(),
"VOLUME_PLACEMENT FAILED: World transformation DIFFER.");
576 ++m_place_counters.errors;
579 printout(INFO, m_det.name(),
"VOLUME_PLACEMENT: PASSED. All matrices equal: %s",
580 volumeID(vid).c_str());
587 err <<
"Matrix " << pv.
name() <<
" id:" << volumeID(vid)
588 <<
" path:" << detector.
path() <<
" error:" << ex.what();
589 ++m_place_counters.errors;
597 size_t depth,
size_t mx_depth)
599 if ( depth <= mx_depth ) {
600 const TGeoNode* current = pv.
ptr();
601 TObjArray* nodes = current->GetNodes();
602 int num_children = nodes ? nodes->GetEntriesFast() : 0;
603 bool is_world = detector == description.
world();
605 for(
int i=0; i<num_children; ++i) {
606 TGeoNode* node = (TGeoNode*)nodes->At(i);
609 Chain child_chain(chain);
613 for (
const auto& c : detector.
children() ) {
614 if ( c.second.placement() == place ) {
619 m_current_detector = de;
620 get_current_sensitive_detector();
623 child_chain.emplace_back(place);
624 child_ids.
insert(child_ids.end(), place.volIDs().begin(), place.volIDs().end());
625 checkManagerSingleVolume(de, place, child_ids, child_chain);
626 checkManagerVolumeTree(de, place, std::move(child_ids), child_chain, depth+1, mx_depth);
634 "DD4hep_DetectorCheck -option [-option] \n"
635 " -help Print this help message \n"
636 " -name <subdetector name> Name of the subdetector to be checked \n"
637 " \"ALL\" or \"all\": loop over known subdetectors\n"
638 " \"world\" start from the mother of all... \n"
639 " -structure Check structural tree consistency \n"
640 " -geometry Check geometry tree consistency \n"
641 " -sensitve Check consistency between detector and volume \n"
642 " settings of sensitive detectors. \n"
643 " -volmgr Check volume manager entries against volIDs of \n"
644 " sensitive volume placements. \n\n"
645 " NOTE: Option requires proper PhysVolID setup \n"
646 " of the sensitive volume placements ! \n"
647 " -ignore_detector Ignore DetElement placement check for -volmgr \n"
649 std::cout <<
"Arguments: " << std::endl;
650 for(
int iarg=0; iarg<argc;++iarg) {
651 std::cout <<
"Argument[" << iarg <<
"] = " << argv[iarg] << std::endl;
657 long DetectorCheck::run(
Detector& description,
int argc,
char** argv) {
660 bool geometry =
false;
661 bool structure =
false;
662 bool sensitive =
false;
663 bool placements =
false;
664 bool ignore_de =
false;
665 printout(ALWAYS,
"DetectorCheck",
"++ Processing plugin...");
666 for(
int iarg=0; iarg<argc;++iarg) {
667 if ( argv[iarg] == 0 )
break;
668 if ( ::strncasecmp(argv[iarg],
"-name",4) == 0 && (iarg+1) < argc )
670 else if ( ::strncasecmp(argv[iarg],
"-structure",4) == 0 )
672 else if ( ::strncasecmp(argv[iarg],
"-placements",4) == 0 )
674 else if ( ::strncasecmp(argv[iarg],
"-volmgr",4) == 0 )
676 else if ( ::strncasecmp(argv[iarg],
"-geometry",4) == 0 )
678 else if ( ::strncasecmp(argv[iarg],
"-sensitive",4) == 0 )
680 else if ( ::strncasecmp(argv[iarg],
"-ignore_detelement",4) == 0 )
682 else if ( ::strncasecmp(argv[iarg],
"-help",4) == 0 )
687 if ( argc == 0 )
help(argc, argv);
688 if ( !name.empty() ) {
689 DetectorCheck test(description);
690 if ( name ==
"all" || name ==
"All" || name ==
"ALL" ) {
692 printout(INFO,
"DetectorCheck",
"++ Processing subdetector: %s",
det.second.name());
693 test.check_structure = structure;
694 test.check_placements = placements;
695 test.check_volmgr = volmgr;
696 test.check_geometry = geometry;
697 test.check_sensitive = sensitive;
698 test.ignore_detector = ignore_de;
699 test.execute(
det.second, 9999);
705 printout(INFO,
"DetectorCheck",
"++ Processing subdetector: %s",name.c_str());
706 test.check_structure = structure;
707 test.check_placements = placements;
708 test.check_volmgr = volmgr;
709 test.check_geometry = geometry;
710 test.check_sensitive = sensitive;
711 test.ignore_detector = ignore_de;
712 test.execute(
det, 9999);
721 const char* args[] = {
"-name", argc > 0 ? argv[0] :
"world",
"-structure",
"-geometry",
"-volmgr", 0 };
722 return DetectorCheck::run(description, 6, (
char**)args);