// $Id: Geant4VolumeManager.cpp 513 2013-04-05 14:31:53Z gaede $ //==================================================================== // AIDA Detector description implementation for LCD //-------------------------------------------------------------------- // // Author : M.Frank // //==================================================================== // Framework include files #include "DD4hep/Printout.h" #include "DD4hep/Volumes.h" #include "DD4hep/Detector.h" #include "DDG4/Geant4VolumeManager.h" #include "DDG4/Geant4Mapping.h" // Geant4 include files #include "G4VTouchable.hh" #include "G4LogicalVolume.hh" #include "G4VPhysicalVolume.hh" using namespace DD4hep::Simulation; using namespace DD4hep::Geometry; using namespace DD4hep; using namespace std; typedef Geant4GeometryInfo::PathMap PathMap; typedef Geant4GeometryInfo::PlacementMap PlacementMap; namespace { struct Populator { typedef vector<const TGeoNode*> Chain; typedef DD4hep::Geometry::LCDD LCDD; typedef DD4hep::Geometry::Readout Readout; typedef DD4hep::Geometry::DetElement DetElement; /// Reference to the LCDD instance LCDD& m_lcdd; /// Set of already added entries set<VolumeID> m_entries; /// Reference to Geant4 translation information Geant4GeometryInfo& m_geo; /// Default constructor Populator(LCDD& lcdd, Geant4GeometryInfo& g) : m_lcdd(lcdd), m_geo(g) { } /// Populate the Volume manager void populate(DetElement e) { const DetElement::Children& c = e.children(); for (DetElement::Children::const_iterator i = c.begin(); i != c.end(); ++i) { DetElement de = (*i).second; PlacedVolume pv = de.placement(); if (pv.isValid()) { Chain chain; SensitiveDetector sd; PlacedVolume::VolIDs ids; m_entries.clear(); scanPhysicalVolume(pv.ptr(), ids, sd, chain); continue; } printout(WARNING, "VolumeManager", "++ Detector element %s of type %s has no placement.", de.name(), de.type().c_str()); } } /// Scan a single physical volume and look for sensitive elements below void scanPhysicalVolume(const TGeoNode* node, PlacedVolume::VolIDs ids, SensitiveDetector& sd, Chain& chain) { PlacedVolume pv = Ref_t(node); Volume vol = pv.volume(); PlacedVolume::VolIDs pv_ids = pv.volIDs(); chain.push_back(node); ids.PlacedVolume::VolIDs::Base::insert(ids.end(), pv_ids.begin(), pv_ids.end()); if (vol.isSensitive()) { sd = vol.sensitiveDetector(); if (sd.readout().isValid()) { add_entry(sd, node, ids, chain); } else { printout(WARNING, "VolumeManager", "populate: Strange constellation volume %s is sensitive, but has no readout! sd:%p", pv.volume().name(), sd.ptr()); } } for (Int_t idau = 0, ndau = node->GetNdaughters(); idau < ndau; ++idau) { TGeoNode* daughter = node->GetDaughter(idau); PlacedVolume placement(daughter); if ( placement.data() ) { scanPhysicalVolume(daughter, ids, sd, chain); } } chain.pop_back(); } void add_entry(SensitiveDetector sd, const TGeoNode* /* n */, const PlacedVolume::VolIDs& ids, const Chain& nodes) { Readout ro = sd.readout(); IDDescriptor iddesc = ro.idSpec(); VolumeID code = iddesc.encode(ids); if (m_entries.find(code) == m_entries.end()) { Geant4Mapping::PlacementPath path; path.reserve(nodes.size()); for (Chain::const_reverse_iterator i = nodes.rbegin(); i != nodes.rend(); ++i) { const TGeoNode* node = *i; PlacementMap::const_iterator g4pit = m_geo.g4Placements.find(node); if (g4pit != m_geo.g4Placements.end()) { path.push_back((*g4pit).second); } } if (m_geo.g4Paths.find(path) != m_geo.g4Paths.end()) { printout(ERROR, "VolumeManager", "populate: Severe error: Duplicated Geant4 path!!!!"); } m_geo.g4Paths[path] = code; m_entries.insert(code); } else { printout(ERROR, "VolumeManager", "populate: Severe error: Duplicated Volume entry: %X", code); } } }; } /// Initializing constructor. The tree will automatically be built if possible Geant4VolumeManager::Geant4VolumeManager(LCDD& lcdd, Geant4GeometryInfo* info) : Base(info), m_isValid(false) { if (info && info->valid && info->g4Paths.empty()) { Populator p(lcdd, *info); p.populate(lcdd.world()); return; } throw runtime_error(format("Geant4VolumeManager", "Attempt populate from invalid Geant4 geometry info [Invalid-Info]")); } /// Helper: Generate placement path from touchable object Geant4VolumeManager::PlacementPath Geant4VolumeManager::placementPath(const G4VTouchable* touchable, bool exception) const { Geant4Mapping::PlacementPath path; if (touchable) { for (int i = 0, n = touchable->GetHistoryDepth(); i < n; ++i) { G4VPhysicalVolume* pv = touchable->GetVolume(i); path.push_back(pv); } } else if (exception) { throw runtime_error(format("Geant4VolumeManager", "Attempt to use invalid Geant4 touchable [Invalid-Touchable]")); } return path; } /// Check the validity of the information before accessing it. bool Geant4VolumeManager::checkValidity() const { if (m_isValid) { return true; } else if (!isValid()) { throw runtime_error(format("Geant4VolumeManager", "Attempt to use invalid Geant4 volume manager [Invalid-Handle]")); } else if (!ptr()->valid) { throw runtime_error(format("Geant4VolumeManager", "Attempt to use invalid Geant4 geometry info [Invalid-Info]")); } m_isValid = true; return m_isValid; } /// Accessor to resolve G4 placements G4VPhysicalVolume* Geant4VolumeManager::placement(const TGeoNode* node) const { if (node && checkValidity()) { const PlacementMap& m = ptr()->g4Placements; PlacementMap::const_iterator i = m.find(node); if (i != m.end()) return (*i).second; return 0; } throw runtime_error(format("Geant4VolumeManager", "Attempt to use invalid Geant4 volume manager [Invalid-Handle]")); } /// Accessor to resolve geometry placements PlacedVolume Geant4VolumeManager::placement(const G4VPhysicalVolume* node) const { if (node && checkValidity()) { const PlacementMap& m = ptr()->g4Placements; for (PlacementMap::const_iterator i = m.begin(); i != m.end(); ++i) { if ((*i).second == node) return PlacedVolume((*i).first); } return PlacedVolume(0); } throw runtime_error(format("Geant4VolumeManager", "Attempt to lookup invalid TGeo placement [Null-Pointer]")); } /// Access CELLID by placement path VolumeID Geant4VolumeManager::volumeID(const PlacementPath& path) const { if (!path.empty() && checkValidity()) { const PathMap& m = ptr()->g4Paths; PathMap::const_iterator i = m.find(path); if (i != m.end()) return (*i).second; if (!path[0]) return InvalidPath; else if (!path[0]->GetLogicalVolume()->GetSensitiveDetector()) return Insensitive; return NonExisting; } return NonExisting; } /// Access CELLID by Geant4 touchable object VolumeID Geant4VolumeManager::volumeID(const G4VTouchable* touchable) const { return volumeID(placementPath(touchable)); } /// Accessfully decoded volume fields by placement path void Geant4VolumeManager::volumeDescriptor(const PlacementPath& path, VolIDDescriptor& vol_desc) const { vol_desc.second.clear(); vol_desc.first = NonExisting; if (!path.empty() && checkValidity()) { const PathMap& m = ptr()->g4Paths; PathMap::const_iterator i = m.find(path); if (i != m.end()) { VolumeID vid = (*i).second; G4LogicalVolume* lvol = path[0]->GetLogicalVolume(); if (lvol->GetSensitiveDetector()) { PlacedVolume pv = placement(path[0]); Geometry::SensitiveDetector sd = pv.volume().sensitiveDetector(); Geometry::IDDescriptor dsc = sd.readout().idSpec(); vol_desc.first = vid; dsc.decodeFields(vid, vol_desc.second); return; } vol_desc.first = Insensitive; return; } if (!path[0]) vol_desc.first = InvalidPath; else if (!path[0]->GetLogicalVolume()->GetSensitiveDetector()) vol_desc.first = Insensitive; else vol_desc.first = NonExisting; } } /// Access fully decoded volume fields by Geant4 touchable object void Geant4VolumeManager::volumeDescriptor(const G4VTouchable* touchable, VolIDDescriptor& vol_desc) const { volumeDescriptor(placementPath(touchable), vol_desc); }