Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /DD4hep/DDCore/src/plugins/DetectorCheck.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 //==========================================================================
0002 //  AIDA Detector description implementation 
0003 //--------------------------------------------------------------------------
0004 // Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
0005 // All rights reserved.
0006 //
0007 // For the licensing terms see $DD4hepINSTALL/LICENSE.
0008 // For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
0009 //
0010 // Author     : M.Frank
0011 //
0012 //==========================================================================
0013 
0014 // Framework include files
0015 #include <DD4hep/Detector.h>
0016 #include <DD4hep/Printout.h>
0017 #include <DD4hep/Factories.h>
0018 #include <DD4hep/IDDescriptor.h>
0019 #include <DD4hep/VolumeManager.h>
0020 #include <DD4hep/DetectorTools.h>
0021 #include <DD4hep/MatrixHelpers.h>
0022 #include <DD4hep/AlignmentsNominalMap.h>
0023 #include <DD4hep/detail/VolumeManagerInterna.h>
0024 
0025 // C/C++ include files
0026 #include <stdexcept>
0027 #include <algorithm>
0028 #include <cstdlib>
0029 
0030 using namespace dd4hep;
0031 
0032 /// Namespace for the AIDA detector description toolkit
0033 namespace dd4hep { namespace detail { namespace tools  {
0034       /// Assemble the path of the PlacedVolume selection
0035       std::string elementPath(const PlacementPath& nodes, bool reverse= false);
0036     } } }
0037 
0038 namespace  {
0039 
0040   /// Tool to check the consistency of a detector setup or individual subdetectors
0041   /** Tool to check the consistency of a detector setup or individual subdetectors
0042    *
0043    *  Test the detector setup:
0044    *
0045    *   - Scan the detectore structure and check DetElements and its placements
0046    *
0047    *   - Scan the geomeytrical hierarchy and check the volumes and placement
0048    *     + optionally the sensitive setup
0049    *
0050    *   - Scan the volume hierarchy and test the volume manager by scanning 
0051    *     the sensitive volumes and checking the correct setup of the
0052    *     PhysVolIDs of the placements against the volume manager
0053    *
0054    *  Other ideas for implementing a proper detector check tool are welcome!
0055    *
0056    *  @author  M.Frank
0057    *  @version 2.0
0058    */
0059   struct DetectorCheck  {
0060     using StructureElements = std::map<DetElement, size_t>;
0061     using Chain = detail::tools::PlacementPath;
0062     using VolIDs = PlacedVolume::VolIDs;
0063 
0064     /// Helper to scan volume ids
0065     struct FND {
0066       const std::string& test;
0067       FND(const std::string& c) : test(c) {}
0068       bool operator()(const VolIDs::value_type& c) const { return c.first == test; }
0069     };
0070     struct counters  {
0071       size_t elements { 0 };
0072       size_t errors { 0 };
0073       void reset() { elements = errors = 0; }
0074       counters& operator+=(const counters& c)  {
0075         elements += c.elements;
0076         errors   += c.errors;
0077         return *this;
0078       }
0079     };
0080 
0081     Detector&            description;
0082     AlignmentsNominalMap m_mapping;
0083     DetElement           m_current_detector;
0084     SensitiveDetector    m_current_sensitive;
0085     IDDescriptor         m_current_iddesc;
0086     VolumeManager        m_volMgr;
0087     DetElement           m_det;
0088     std::string          m_name { "GeometryCheck" };
0089 
0090     counters             m_place_counters, m_sens_counters, m_geo_counters, m_struct_counters;
0091     StructureElements    m_structure_elements;
0092 
0093     bool check_structure  { false };
0094     bool check_geometry   { false };
0095     bool check_placements { false };
0096     bool check_volmgr     { false };
0097     bool check_sensitive  { false };
0098     bool ignore_detector  { false };
0099 
0100     SensitiveDetector get_current_sensitive_detector();
0101 
0102     /// Initializing constructor
0103     DetectorCheck(Detector& description);
0104     /// Default destructor
0105     virtual ~DetectorCheck() = default;
0106 
0107     /// Check single volume integrity
0108     void checkManagerSingleVolume(DetElement e, PlacedVolume pv, const VolIDs& child_ids, const Chain& chain);
0109     /// Walk through tree of volume placements
0110     void checkManagerVolumeTree(DetElement e, PlacedVolume pv, VolIDs ids, const Chain& chain, size_t depth, size_t mx_depth);
0111 
0112     /// Check single volume integrity
0113     void checkSingleVolume(DetElement e, PlacedVolume pv);
0114     /// Walk through tree of volume placements
0115     void checkVolumeTree(DetElement e, PlacedVolume pv);
0116 
0117     /// Check DetElement integrity
0118     bool checkDetElement(const std::string& path, DetElement detector, PlacedVolume pv);
0119     /// Check DetElement tree for integrity
0120     bool checkDetElementTree(const std::string& path, DetElement detector, PlacedVolume pv);
0121 
0122     void execute(DetElement sdet, size_t depth);
0123 
0124     /// Action routine to execute the test
0125     static long run(Detector& description,int argc,char** argv);
0126     static void help(int argc,char** argv);
0127   };
0128   const char* tag_fail(size_t errs)   {
0129     return errs==0 ? "PASSED" : "FAILED";
0130   }
0131 }
0132 
0133 
0134 /// Initializing constructor
0135 DetectorCheck::DetectorCheck(Detector& desc)
0136   : description(desc), m_mapping(desc.world())
0137 {
0138 }
0139 
0140 SensitiveDetector DetectorCheck::get_current_sensitive_detector()  {
0141   DetElement de = m_current_detector;
0142   m_current_sensitive = description.sensitiveDetector(de.name());
0143   m_current_iddesc = IDDescriptor();
0144   if ( m_current_sensitive.isValid() )   {
0145     m_current_iddesc = m_current_sensitive.readout().idSpec();
0146   }
0147   return m_current_sensitive;
0148 }
0149 
0150 void DetectorCheck::execute(DetElement sdet, size_t depth)   {
0151   const char* line = "============================";
0152   struct counters count_volmgr_sens, count_volmgr_place;
0153   struct counters total, count_struct;
0154   struct counters count_geo, count_geo_sens;
0155 
0156   if ( !sdet.isValid() )   {
0157     ++m_place_counters.errors;
0158     except("VolumeMgrTest", "The detector element is not known to the geometry.");
0159     return;
0160   }
0161 
0162   m_det = sdet;
0163   m_current_detector = m_det;
0164 
0165   /// Enable sensitive volume checks
0166   if ( check_sensitive || check_volmgr )   {
0167     if ( m_det == m_det.world() )  {
0168       m_current_sensitive = SensitiveDetector();
0169       m_current_iddesc = IDDescriptor();
0170     }
0171     else   {
0172       m_current_sensitive = description.sensitiveDetector(m_det.name());
0173       if ( !m_current_sensitive.isValid() )   {
0174         printout(ERROR, m_name,
0175                  "The sensitive detector of subdetector %s "
0176                  "is not known to the geometry.", m_det.name());
0177         return;
0178       }
0179       m_current_iddesc = m_current_sensitive.readout().idSpec();
0180     }
0181   }
0182   /// Execute actions:
0183   if ( check_structure )   {
0184     printout(ALWAYS, m_name, "%s%s  Executing STRUCTURE      test  %s%s", line, line, line, line);
0185     PlacedVolume pv  = m_det.placement();
0186     checkDetElementTree(m_det.path(), m_det, pv);
0187     count_struct.elements = m_structure_elements.size();
0188     count_struct.errors   = m_struct_counters.errors;
0189     total += count_struct;
0190     m_structure_elements.clear();
0191     m_struct_counters.reset();
0192   }
0193   if ( check_geometry )   {
0194     printout(ALWAYS, m_name, "%s%s  Executing GEOMETRY       test  %s%s", line, line, line, line);
0195     PlacedVolume pv  = m_det.placement();
0196     checkVolumeTree(m_det, pv);
0197     count_geo      = m_geo_counters;
0198     count_geo_sens = m_sens_counters;
0199     total += count_geo_sens;
0200     total += count_geo;
0201     m_sens_counters.reset();
0202     m_geo_counters.reset();
0203   }
0204 
0205   if ( check_volmgr )   {
0206     Chain        chain;
0207     PlacedVolume pv  = m_det.placement();
0208     VolIDs       ids;
0209 
0210     printout(ALWAYS, m_name, "%s%s  Executing VOLUME MANAGER test  %s%s", line, line, line, line);
0211     chain.emplace_back(pv);
0212     m_volMgr = description.volumeManager();
0213     if ( !m_volMgr.isValid() )    {
0214       printout(ERROR, m_name, "Volume manager is not instantiated. Required for test!");
0215       return;
0216     }
0217     if ( pv.volume() != description.worldVolume() )   {
0218       ids = pv.volIDs();
0219     }
0220     m_sens_counters.reset();
0221     m_current_detector = m_det;
0222     checkManagerVolumeTree(m_det, pv, std::move(ids), chain, 1, depth);
0223     count_volmgr_place = m_place_counters;
0224     count_volmgr_sens  = m_sens_counters;
0225     total += count_volmgr_place;
0226     total += count_volmgr_sens;
0227     m_place_counters.reset();
0228     m_sens_counters.reset();
0229   }
0230 
0231   if ( check_structure )   {
0232     printout(count_struct.errors > 0 ? ERROR : ALWAYS, 
0233              m_name, "+++ %s: Checked %10ld structure elements.   Num.Errors:%6ld (structure test)",
0234              tag_fail(count_struct.errors), count_struct.elements, count_struct.errors);
0235   }
0236   if ( check_geometry )   {
0237     if ( check_sensitive )  {
0238       printout(count_geo_sens.errors > 0 ? ERROR : ALWAYS,
0239                m_name, "+++ %s: Checked %10ld sensitive elements.   Num.Errors:%6ld (geometry test)",
0240                tag_fail(count_geo_sens.errors), count_geo_sens.elements, count_geo_sens.errors);
0241     }
0242     printout(count_geo.errors > 0 ? ERROR : ALWAYS,
0243              m_name, "+++ %s: Checked %10ld placements.           Num.Errors:%6ld (geometry test)",
0244              tag_fail(count_geo.errors), count_geo.elements, count_geo.errors);
0245   }
0246   if ( check_volmgr )   {
0247     if ( check_sensitive )  {
0248       printout(count_volmgr_sens.errors > 0 ? ERROR : ALWAYS,
0249                m_name, "+++ %s: Checked %10ld sensitive elements.   Num.Errors:%6ld (phys.VolID test)",
0250                tag_fail(count_volmgr_sens.errors), count_volmgr_sens.elements, count_volmgr_sens.errors);
0251     }
0252     printout(count_volmgr_place.errors > 0 ? ERROR : ALWAYS,
0253              m_name, "+++ %s: Checked %10ld sensitive placements. Num.Errors:%6ld (phys.VolID test)",
0254              tag_fail(count_volmgr_place.errors), count_volmgr_sens.elements, count_volmgr_place.errors);
0255   }
0256   printout(ALWAYS, m_name, "+++ %s: Checked a total of %11ld elements. Num.Errors:%6ld (Some elements checked twice)",
0257            tag_fail(total.errors), total.elements, total.errors);
0258 }
0259 
0260 /// Check DetElement integrity
0261 bool DetectorCheck::checkDetElement(const std::string& path, DetElement detector, PlacedVolume pv)   {
0262   bool det_valid = true;
0263   bool parent_valid = true;
0264   bool place_valid = true;
0265   bool det_place_valid = true;
0266   bool vol_valid = true;
0267   auto nerrs = m_struct_counters.errors;
0268   const char* de_path = detector.path().c_str();
0269 
0270   if ( !pv.isValid() )   {
0271     printout(ERROR, m_name, "Invalid DetElement placement: %s", de_path);
0272     ++m_struct_counters.errors;
0273     place_valid = false;
0274   }
0275   if ( detector.path() != path )    {
0276     printout(ERROR, m_name, "Invalid DetElement [path mismatch]: %s <> %s",
0277              de_path, path.c_str());
0278     ++m_struct_counters.errors;
0279   }
0280   if ( !detector.parent().isValid() && detector.world() != detector )   {
0281     printout(ERROR, m_name, "Invalid DetElement [No parent]:     %s", de_path);
0282     ++m_struct_counters.errors;
0283     parent_valid = false;
0284   }
0285   if ( !detector.placement().isValid() )   {
0286     printout(ERROR, m_name, "Invalid DetElement [No placement]:  %s", de_path);
0287     ++m_struct_counters.errors;
0288     det_place_valid = false;
0289   }
0290   else if ( !detector.volume().isValid() )   {
0291     printout(ERROR, m_name, "Invalid DetElement [No volume]:     %s", de_path);
0292     ++m_struct_counters.errors;
0293     vol_valid = false;
0294   }
0295   if ( detector.placement().isValid() && detector.placement() != pv )   {
0296     printout(ERROR, m_name, "Invalid DetElement [Mismatched placement]:  %s", de_path);
0297     ++m_struct_counters.errors;
0298     det_place_valid = false;
0299   }
0300   auto count = ++m_structure_elements[detector];
0301   if ( count > 1 )   {
0302     DetElement par = detector.parent();
0303     printout(ERROR, m_name, "DetElement %s parent: %s is placed %ld times! Only single placement allowed.", 
0304              de_path, par.isValid() ? par.path().c_str() : "", m_structure_elements[detector]);
0305     ++m_struct_counters.errors;
0306   }
0307   Alignment ideal = detector.nominal();
0308   if ( !ideal.isValid() )    {
0309     printout(ERROR, m_name, "Invalid DetElement [No ideal alignment]: %s", de_path);
0310     ++m_struct_counters.errors;
0311   }
0312   Alignment survey = detector.survey();
0313   if ( !survey.isValid() )    {
0314     printout(ERROR, m_name, "Invalid DetElement [No survey alignment]: %s", de_path);
0315     ++m_struct_counters.errors;
0316   }
0317   if ( ideal.isValid() )    {
0318     const TGeoHMatrix& matrix = ideal.worldTransformation();
0319     if ( matrix.IsIdentity() )  {
0320     }
0321   }
0322   printout(nerrs != m_struct_counters.errors ? ERROR : INFO, m_name, 
0323            "DetElement %s [%s] parent: %s placement: %s [%s] volume: %s",
0324            path.c_str(), yes_no(det_valid), yes_no(parent_valid), yes_no(det_place_valid),
0325            yes_no(place_valid), yes_no(vol_valid));
0326   return nerrs == m_struct_counters.errors;
0327 }
0328 
0329 /// Check DetElement tree for integrity
0330 bool DetectorCheck::checkDetElementTree(const std::string& path, DetElement detector, PlacedVolume pv)   {
0331   auto nerrs = m_struct_counters.errors;
0332   if ( !detector.isValid() )   {
0333     printout(ERROR, m_name, "Invalid DetElement seen: %s", path.c_str());
0334     ++m_struct_counters.errors;
0335     return false;
0336   }
0337   bool is_world = detector == detector.world();
0338   /// Check single DetElement object
0339   checkDetElement(path, detector, pv);
0340   /// Recurse the tree
0341   for ( const auto& c : detector.children() )   {
0342     DetElement de = c.second;
0343     if ( is_world )   {
0344       m_current_sensitive = SensitiveDetector();
0345       m_current_iddesc = IDDescriptor();
0346       m_current_detector = de;
0347     }
0348     if ( de.parent().isValid() && de.parent() != detector )    {
0349       printout(ERROR, m_name, "Invalid DetElement [Parent mismatch]:    %s", de.path().c_str());
0350       printout(ERROR, m_name, "        apparent parent: %s  structural parent: %s",
0351                de.parent().path().c_str(), detector.path().c_str());
0352       ++m_struct_counters.errors;
0353     }
0354     /// Invalid daughter elements will be detectoed in there:
0355     checkDetElementTree(path + "/" + c.first, de, de.placement());
0356   }
0357   return nerrs == m_struct_counters.errors;
0358 }
0359 
0360 /// Check single volume integrity
0361 void DetectorCheck::checkSingleVolume(DetElement e, PlacedVolume pv)   {
0362 
0363   ++m_geo_counters.elements;
0364   /// Check DetElement validity
0365   if ( !e.isValid() )   {
0366     printout(ERROR, m_name, "Invalid DetElement [Invalid handle]");
0367     ++m_geo_counters.errors;
0368   }
0369   /// Check placement validity
0370   if ( !pv.isValid() )   {
0371     printout(ERROR, m_name, "Invalid PlacedVolume [Invalid handle] DetElement: %s", e.path().c_str());
0372     ++m_geo_counters.errors;
0373   }
0374   Volume vol = pv.volume();
0375   /// Check volume validity
0376   if ( !vol.isValid() )   {
0377     printout(ERROR, m_name, "Invalid Volume [Invalid handle] DetElement: %s", e.path().c_str());
0378     ++m_geo_counters.errors;
0379     return;
0380   }
0381   /// Check sensitive settings for sensitive volumes
0382   if ( check_sensitive && vol.isSensitive() )    {
0383     SensitiveDetector sdv = vol.sensitiveDetector();
0384     ++m_sens_counters.elements;
0385     if ( !sdv.isValid() )   {
0386       printout(ERROR, m_name, "Invalid SensitiveDetector DetElement: %s", e.path().c_str());
0387       ++m_sens_counters.errors;
0388     } 
0389     SensitiveDetector sdd = get_current_sensitive_detector();
0390     if ( sdd != sdv )   {
0391       printout(ERROR, m_name, "Inconsistent sensitive detectors for DetElement: %s", e.path().c_str());
0392       ++m_sens_counters.errors;
0393     }
0394   }
0395 }
0396 
0397 /// Walk through tree of volume placements
0398 void DetectorCheck::checkVolumeTree(DetElement detector, PlacedVolume pv)   {
0399   const TGeoNode* current  = pv.ptr();
0400   TObjArray*  nodes        = current->GetNodes();
0401   int         num_children = nodes ? nodes->GetEntriesFast() : 0;
0402   bool        is_world     = detector == description.world();
0403 
0404   /// Check single volume object
0405   checkSingleVolume(detector, pv);
0406   /// Recurse the tree
0407   for(int i=0; i < num_children; ++i)   {
0408     TGeoNode* node = (TGeoNode*)nodes->At(i);
0409     PlacedVolume place(node);
0410     DetElement de = detector;
0411 
0412     if ( is_world )   {
0413       m_current_detector = de;
0414       get_current_sensitive_detector();
0415     }
0416 
0417     /// Check if there is a new parent at the next level:
0418     for ( const auto& c : detector.children() )   {
0419       if ( c.second.placement() == place )   {
0420         de = c.second;
0421         break;
0422       }
0423     }
0424     checkVolumeTree(de, place);
0425     if ( is_world )   {
0426       m_current_sensitive = SensitiveDetector();
0427       m_current_iddesc    = IDDescriptor();
0428     }
0429   }
0430 }
0431 
0432 /// Check volume integrity
0433 void DetectorCheck::checkManagerSingleVolume(DetElement detector, PlacedVolume pv, const VolIDs& child_ids, const Chain& chain)   {
0434   std::stringstream err, log;
0435   VolumeID     det_vol_id = detector.volumeID();
0436   VolumeID     vid        = det_vol_id;
0437   DetElement   top_sdet, det_elem;
0438   VolumeManagerContext* mgr_ctxt = 0;
0439   
0440   ++m_place_counters.elements;
0441 
0442   try {
0443     vid       = m_current_iddesc.encode(child_ids);
0444     top_sdet  = m_volMgr.lookupDetector(vid);
0445     det_elem  = m_volMgr.lookupDetElement(vid);
0446     mgr_ctxt  = m_volMgr.lookupContext(vid);
0447 
0448     if ( pv.volume().isSensitive() )  {
0449       PlacedVolume det_place = m_volMgr.lookupDetElementPlacement(vid);
0450       ++m_sens_counters.elements;
0451       if ( !ignore_detector && pv.ptr() != det_place.ptr() )   {
0452         err << "VolumeMgrTest: Wrong placement "
0453             << " got "        << det_place.name() << " (" << (void*)det_place.ptr() << ")"
0454             << " instead of " << pv.name()        << " (" << (void*)pv.ptr()        << ") "
0455             << " vid:" << volumeID(vid);
0456         ++m_place_counters.errors;
0457       }
0458       else if ( top_sdet.ptr() != detector.ptr() )   {
0459         top_sdet  = m_volMgr.lookupDetector(vid);
0460         err << "VolumeMgrTest: Wrong associated sub-detector element vid="  << volumeID(vid)
0461             << " got "        << top_sdet.path() << " (" << (void*)top_sdet.ptr() << ") "
0462             << " instead of " << detector.path() << " (" << (void*)detector.ptr() << ")"
0463             << " vid:" << volumeID(vid);
0464         ++m_place_counters.errors;
0465       }
0466       else if ( !detail::tools::isParentElement(detector,det_elem) )   {
0467         // This is sort of a bit wischi-waschi.... 
0468         err << "VolumeMgrTest: Wrong associated detector element vid="  << volumeID(vid)
0469             << " got "        << det_elem.path() << " (" << (void*)det_elem.ptr() << ") "
0470             << " instead of " << detector.path() << " (" << (void*)detector.ptr() << ")"
0471             << " vid:" << volumeID(vid);
0472         ++m_place_counters.errors;
0473       }
0474       else if ( top_sdet.ptr() != m_det.ptr() )   {
0475         err << "VolumeMgrTest: Wrong associated detector "
0476             << " vid:" << volumeID(vid);
0477         ++m_place_counters.errors;
0478       }
0479     }
0480   }
0481   catch(const std::exception& ex) {
0482     err << "Lookup " << pv.name() << " id:" << volumeID(vid)
0483         << " path:" << detector.path() << " error:" << ex.what();
0484     ++m_place_counters.errors;
0485   }
0486 
0487   if ( pv.volume().isSensitive() || (0 != det_vol_id) )  {
0488     std::string id_desc;
0489     log << "Volume:"  << std::setw(50) << std::left << pv.name();
0490     if ( pv.volume().isSensitive() )  {
0491       IDDescriptor dsc = SensitiveDetector(pv.volume().sensitiveDetector()).readout().idSpec();
0492       log << " IDDesc:" << (char*)(dsc.ptr() == m_current_iddesc.ptr() ? "OK " : "BAD");
0493       if ( dsc.ptr() != m_current_iddesc.ptr() ) ++m_place_counters.errors;
0494     }
0495     else  {
0496       log << std::setw(11) << " ";
0497     }
0498     id_desc = m_current_iddesc.str(vid);
0499     log << " [" << char(pv.volume().isSensitive() ? 'S' : 'N') << "] " << std::right
0500         << " vid:" << volumeID(vid)
0501         << " " << id_desc;
0502     if ( !err.str().empty() )   {
0503       printout(ERROR, m_det.name(),err.str()+" "+log.str());
0504       //throw std::runtime_error(err.str());
0505       return;
0506     }
0507     id_desc = m_current_iddesc.str(det_elem.volumeID());
0508     printout(INFO, m_det.name(),log.str());
0509     printout(INFO, m_det.name(), "  Elt:%-64s    vid:%s %s Parent-OK:%3s",
0510              det_elem.path().c_str(),volumeID(det_elem.volumeID()).c_str(),
0511              id_desc.c_str(),
0512              yes_no(detail::tools::isParentElement(detector,det_elem)));
0513 
0514     try  {
0515       if ( pv.volume().isSensitive() )  {
0516         TGeoHMatrix trafo;
0517         for (size_t i = chain.size()-1; i > 0; --i)  {
0518           //for (size_t i = 0; i<chain.size(); ++i )  {
0519           const TGeoMatrix* mat = chain[i]->GetMatrix();
0520           trafo.MultiplyLeft(mat);
0521         }
0522         for (size_t i = chain.size(); i > 0; --i)  {
0523           const TGeoMatrix* mat = chain[i-1]->GetMatrix();
0524           if ( printLevel() <= INFO )  {
0525             ::printf("Placement [%d]  VolID:%s\t\t",int(i),chain[i-1].volIDs().str().c_str());
0526             mat->Print();
0527           }
0528         }
0529         det_elem  = m_volMgr.lookupDetElement(vid);
0530         if ( printLevel() <= INFO )  {
0531           ::printf("Computed Trafo (from placements):\t\t");
0532           trafo.Print();
0533           ::printf("DetElement Trafo: %s [%s]\t\t",
0534                    det_elem.path().c_str(),volumeID(det_elem.volumeID()).c_str());
0535           det_elem.nominal().worldTransformation().Print();
0536           ::printf("VolumeMgr  Trafo: %s [%s]\t\t",det_elem.path().c_str(),volumeID(vid).c_str());
0537           m_volMgr.worldTransformation(m_mapping,vid).Print();
0538         }
0539         
0540         /// Check volume manager context
0541         if ( 0 == mgr_ctxt )  {
0542           printout(ERROR, m_det.name(), "VOLUME_MANAGER FAILED: Could not find entry for vid:%s.",
0543                    volumeID(vid).c_str());
0544           ++m_place_counters.errors;
0545         }
0546 
0547         /// Check nominal and DetElement trafos for pointer equality:
0548         if ( &det_elem.nominal().worldTransformation() != &m_volMgr.worldTransformation(m_mapping,det_elem.volumeID()) )
0549         {
0550           printout(ERROR, m_det.name(), "DETELEMENT_PERSISTENCY FAILED: World transformation have DIFFERET pointer!");
0551           ++m_place_counters.errors;
0552         }
0553 
0554         if ( !ignore_detector )   {
0555           if ( pv.ptr() == det_elem.placement().ptr() )   {
0556             // The computed transformation 'trafo' MUST be equal to:
0557             // m_volMgr.worldTransformation(vid) AND det_elem.nominal().worldTransformation()
0558             int res1 = detail::matrix::_matrixEqual(trafo, det_elem.nominal().worldTransformation());
0559             int res2 = detail::matrix::_matrixEqual(trafo, m_volMgr.worldTransformation(m_mapping,vid));
0560             if ( res1 != detail::matrix::MATRICES_EQUAL || res2 != detail::matrix::MATRICES_EQUAL )  {
0561               printout(ERROR, m_det.name(), "DETELEMENT_PLACEMENT FAILED: World transformation DIFFER.");
0562               ++m_place_counters.errors;
0563             }
0564             else  {
0565               printout(INFO, m_det.name(), "DETELEMENT_PLACEMENT: PASSED. All matrices equal: %s",
0566                        volumeID(vid).c_str());
0567             }
0568           }
0569           else  {
0570             // The computed transformation 'trafo' MUST be equal to:
0571             // m_volMgr.worldTransformation(vid)
0572             // The det_elem.nominal().worldTransformation() however is DIFFERENT!
0573             int res2 = detail::matrix::_matrixEqual(trafo, m_volMgr.worldTransformation(m_mapping,vid));
0574             if ( res2 != detail::matrix::MATRICES_EQUAL )  {
0575               printout(ERROR, m_det.name(), "VOLUME_PLACEMENT FAILED: World transformation DIFFER.");
0576               ++m_place_counters.errors;
0577             }
0578             else  {
0579               printout(INFO, m_det.name(), "VOLUME_PLACEMENT: PASSED. All matrices equal: %s",
0580                        volumeID(vid).c_str());
0581             }
0582           }
0583         }
0584       }
0585     }
0586     catch(const std::exception& ex) {
0587       err << "Matrix " << pv.name() << " id:" << volumeID(vid)
0588           << " path:" << detector.path() << " error:" << ex.what();
0589       ++m_place_counters.errors;
0590     }
0591     
0592   }
0593 }
0594 
0595 /// Walk through tree of detector elements
0596 void DetectorCheck::checkManagerVolumeTree(DetElement detector, PlacedVolume pv, VolIDs ids, const Chain& chain,
0597                                            size_t depth, size_t mx_depth)  
0598 {
0599   if ( depth <= mx_depth )  {
0600     const TGeoNode* current  = pv.ptr();
0601     TObjArray*  nodes        = current->GetNodes();
0602     int         num_children = nodes ? nodes->GetEntriesFast() : 0;
0603     bool        is_world     = detector == description.world();
0604 
0605     for(int i=0; i<num_children; ++i)   {
0606       TGeoNode* node = (TGeoNode*)nodes->At(i);
0607       PlacedVolume place(node);
0608       VolIDs child_ids(ids);
0609       Chain  child_chain(chain);
0610       DetElement de = detector;
0611       if ( is_world )  {
0612         /// Check if there is a new parent at the next level:
0613         for ( const auto& c : detector.children() )   {
0614           if ( c.second.placement() == place )   {
0615             de = c.second;
0616             break;
0617           }
0618         }
0619         m_current_detector = de;
0620         get_current_sensitive_detector();
0621       }
0622       place.access(); // Test validity
0623       child_chain.emplace_back(place);
0624       child_ids.insert(child_ids.end(), place.volIDs().begin(), place.volIDs().end());
0625       checkManagerSingleVolume(de, place, child_ids, child_chain);
0626       checkManagerVolumeTree(de, place, std::move(child_ids), child_chain, depth+1, mx_depth);
0627     }
0628   }
0629 }
0630 
0631 void DetectorCheck::help(int argc,char** argv)   {
0632   std::cout
0633     << 
0634     "DD4hep_DetectorCheck -option [-option]                                         \n"
0635     "  -help                        Print this help message                         \n"
0636     "  -name  <subdetector name>    Name of the subdetector to be checked           \n"
0637     "                               \"ALL\" or \"all\": loop over known subdetectors\n"
0638     "                               \"world\" start from the mother of all...       \n"
0639     "  -structure                   Check structural tree consistency               \n"
0640     "  -geometry                    Check geometry tree consistency                 \n"
0641     "  -sensitve                    Check consistency between detector and volume   \n"
0642     "                               settings of sensitive detectors.                \n"
0643     "  -volmgr                      Check volume manager entries against volIDs of  \n"
0644     "                               sensitive volume placements.                  \n\n"
0645     "                               NOTE: Option requires proper PhysVolID setup    \n"
0646     "                               of the sensitive volume placements !            \n"
0647     "  -ignore_detector             Ignore DetElement placement check for -volmgr   \n"
0648     << std::endl;
0649   std::cout << "Arguments: " << std::endl;
0650   for(int iarg=0; iarg<argc;++iarg)  {
0651     std::cout << "Argument[" << iarg << "]  = " << argv[iarg] << std::endl;
0652   }
0653   ::exit(EINVAL);
0654 }
0655 
0656 /// Action routine to execute the test
0657 long DetectorCheck::run(Detector& description,int argc,char** argv)    {
0658   std::string name;
0659   bool volmgr = false;
0660   bool geometry = false;
0661   bool structure = false;
0662   bool sensitive = false;
0663   bool placements = false;
0664   bool ignore_de  = false;
0665   printout(ALWAYS, "DetectorCheck", "++ Processing plugin...");
0666   for(int iarg=0; iarg<argc;++iarg)  {
0667     if ( argv[iarg] == 0 ) break;
0668     if ( ::strncasecmp(argv[iarg], "-name",4) == 0 && (iarg+1) < argc )
0669       name = argv[++iarg];
0670     else if ( ::strncasecmp(argv[iarg], "-structure",4) == 0 )
0671       structure = true;
0672     else if ( ::strncasecmp(argv[iarg], "-placements",4) == 0 )
0673       placements = true;
0674     else if ( ::strncasecmp(argv[iarg], "-volmgr",4) == 0 )
0675       volmgr = true;
0676     else if ( ::strncasecmp(argv[iarg], "-geometry",4) == 0 )
0677       geometry = true;
0678     else if ( ::strncasecmp(argv[iarg], "-sensitive",4) == 0 )
0679       sensitive = true;
0680     else if ( ::strncasecmp(argv[iarg], "-ignore_detelement",4) == 0 )
0681       ignore_de = true;
0682     else if ( ::strncasecmp(argv[iarg], "-help",4) == 0 )
0683       help(argc, argv);
0684     else
0685       help(argc, argv);
0686   }
0687   if ( argc == 0 ) help(argc, argv);
0688   if ( !name.empty() )   {
0689     DetectorCheck test(description);
0690     if ( name == "all" || name == "All" || name == "ALL" )  {
0691       for (const auto& det : description.detectors() )  {
0692         printout(INFO, "DetectorCheck", "++ Processing subdetector: %s", det.second.name());
0693         test.check_structure  = structure;
0694         test.check_placements = placements;
0695         test.check_volmgr     = volmgr;
0696         test.check_geometry   = geometry;
0697         test.check_sensitive  = sensitive;
0698         test.ignore_detector  = ignore_de;
0699         test.execute(det.second, 9999);
0700       }
0701       return 1;
0702     }
0703     DetElement det = (::strcasecmp(name.c_str(), "world") == 0)
0704       ? description.world() : description.detector(name);
0705     printout(INFO, "DetectorCheck", "++ Processing subdetector: %s",name.c_str());
0706     test.check_structure  = structure;
0707     test.check_placements = placements;
0708     test.check_volmgr     = volmgr;
0709     test.check_geometry   = geometry;
0710     test.check_sensitive  = sensitive;
0711     test.ignore_detector  = ignore_de;
0712     test.execute(det, 9999);
0713   }
0714   return 1;
0715 }
0716 
0717 DECLARE_APPLY(DD4hep_DetectorCheck,DetectorCheck::run)
0718 
0719 /// Action routine to execute the test for backwards compatibility
0720 long run_VolumeMgrTest(Detector& description,int argc, const char*const* argv)    {
0721   const char* args[] = {"-name", argc > 0 ? argv[0] : "world", "-structure", "-geometry", "-volmgr", 0 };
0722   return DetectorCheck::run(description, 6, (char**)args); 
0723 }
0724 DECLARE_APPLY(DD4hep_VolumeMgrTest,run_VolumeMgrTest)