File indexing completed on 2026-10-02 08:10:33
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include <DD4hep/Printout.h>
0016 #include <DD4hep/Volumes.h>
0017 #include <DD4hep/DetElement.h>
0018 #include <DD4hep/DetectorTools.h>
0019 #include <DD4hep/VolumeManager.h>
0020 #include <DD4hep/detail/VolumeManagerInterna.h>
0021 #include <DDG4/Geant4VolumeManager.h>
0022 #include <DDG4/Geant4TouchableHandler.h>
0023 #include <DDG4/Geant4Mapping.h>
0024
0025
0026 #include <G4VTouchable.hh>
0027 #include <G4LogicalVolume.hh>
0028 #include <G4VPhysicalVolume.hh>
0029
0030
0031 #include <set>
0032 #include <stdexcept>
0033 #include <string>
0034 #include <utility>
0035 #include <vector>
0036
0037
0038
0039 #ifdef VOLMGR_HAVE_DEBUG_INFO
0040
0041 namespace dd4hep {
0042
0043 namespace sim {
0044
0045 class Geant4GeometryInfo::DebugInfo {
0046 public:
0047 typedef std::vector<const G4VPhysicalVolume*> Geant4PlacementPath;
0048 std::map<Geant4PlacementPath, Placement> g4Paths;
0049 };
0050
0051 }
0052 }
0053 #endif
0054
0055
0056
0057 namespace dd4hep {
0058
0059 namespace sim {
0060
0061 void _print_volumeid(const std::string& tag, const IDDescriptor& iddesc, DDSegmentation::VolumeID volID);
0062
0063 }
0064 }
0065
0066 using namespace dd4hep::sim;
0067 using namespace dd4hep;
0068
0069 #include <DDG4/Geant4AssemblyVolume.h>
0070 using VolIDDescriptor = std::pair<VolumeID,std::vector<std::pair<const BitFieldElement*, VolumeID> > >;
0071
0072 namespace {
0073
0074
0075
0076
0077
0078
0079
0080 struct Populator {
0081
0082 typedef std::vector<const TGeoNode*> Chain;
0083
0084 typedef std::set<VolumeID> Registries;
0085
0086
0087 const Detector& m_detDesc;
0088
0089 Registries m_entries;
0090
0091 Geant4GeometryInfo& m_geo;
0092
0093 long m_debug { 0 };
0094
0095
0096 Populator(const Detector& description, Geant4GeometryInfo& g, long dbg)
0097 : m_detDesc(description), m_geo(g), m_debug(dbg)
0098 {
0099 #ifdef VOLMGR_HAVE_DEBUG_INFO
0100 if ( nullptr == g.g4DebugInfo ) {
0101 g.g4DebugInfo = new Geant4GeometryInfo::DebugInfo();
0102 }
0103 #endif
0104 }
0105
0106 ~Populator() {
0107 #ifdef VOLMGR_HAVE_DEBUG_INFO
0108 if ( g.g4DebugInfo ) {
0109 delete g.g4DebugInfo;
0110 g.g4DebugInfo = nullptr;
0111 }
0112 #endif
0113 }
0114
0115
0116
0117 void populate(DetElement e) {
0118 const DetElement::Children& children = e.children();
0119 m_entries.clear();
0120 for( const auto& i : children ) {
0121 DetElement de = i.second;
0122 PlacedVolume pv = de.placement();
0123 if( pv.isValid() ) {
0124 Chain chain;
0125 SensitiveDetector sd;
0126 PlacedVolume::VolIDs ids;
0127 m_entries.clear();
0128 chain.emplace_back(m_detDesc.world().placement().ptr());
0129 scanPhysicalVolume(pv.ptr(), std::move(ids), sd, chain);
0130 continue;
0131 }
0132 printout(WARNING, "Geant4VolumeManager",
0133 "++ Detector element %s of type %s has no placement.",
0134 de.name(), de.type().c_str());
0135 }
0136
0137 for( const auto& pv : m_geo.g4Placements ) {
0138 if( pv.second->IsParameterised() )
0139 m_geo.g4Parameterised[pv.second] = pv.first;
0140 if( pv.second->IsReplicated() )
0141 m_geo.g4Replicated[pv.second] = pv.first;
0142 }
0143 m_entries.clear();
0144 }
0145
0146
0147 void scanPhysicalVolume(const TGeoNode* node, PlacedVolume::VolIDs ids, SensitiveDetector& sd, Chain& chain) {
0148 PlacedVolume pv = node;
0149 Volume vol = pv.volume();
0150 PlacedVolume::VolIDs pv_ids = pv.volIDs();
0151
0152 chain.emplace_back(node);
0153 ids.PlacedVolume::VolIDs::Base::insert(ids.end(), pv_ids.begin(), pv_ids.end());
0154 if( vol.isSensitive() ) {
0155 sd = vol.sensitiveDetector();
0156 if( sd.readout().isValid() ) {
0157 add_entry(sd, node, ids, chain);
0158 }
0159 else {
0160 printout(WARNING, "Geant4VolumeManager",
0161 "populate: Strange constellation volume %s is sensitive, but has no readout! sd:%p", pv.volume().name(),
0162 sd.ptr());
0163 }
0164 }
0165 for( Int_t idau = 0, ndau = node->GetNdaughters(); idau < ndau; ++idau ) {
0166 TGeoNode* daughter = node->GetDaughter(idau);
0167 PlacedVolume placement(daughter);
0168 if( placement.data() ) {
0169 scanPhysicalVolume(daughter, ids, sd, chain);
0170 }
0171 }
0172 chain.pop_back();
0173 }
0174
0175 void add_entry(SensitiveDetector sd, const TGeoNode* n, const PlacedVolume::VolIDs& ids, const Chain& nodes) {
0176 Chain control;
0177 Volume vol;
0178 Readout rdout = sd.readout();
0179 IDDescriptor iddesc = rdout.idSpec();
0180 VolumeID code = iddesc.encode(ids);
0181 PrintLevel print_action = (m_debug&Geant4VolumeManager::PRINT_ACTION) ? ALWAYS : m_geo.printLevel;
0182 PrintLevel print_chain = (m_debug&Geant4VolumeManager::PRINT_CHAIN) ? ALWAYS : m_geo.printLevel;
0183 PrintLevel print_res = (m_debug&Geant4VolumeManager::PRINT_RESULT) ? ALWAYS : m_geo.printLevel;
0184 bool print_nodes = (m_debug&Geant4VolumeManager::PRINT_NODES) ? true : false;
0185 Geant4TouchableHandler::Geant4PlacementPath path;
0186 Registries::const_iterator i = m_entries.find(code);
0187
0188 printout(print_action,"Geant4VolumeManager","+++ Add path:%s vid:%016X",
0189 detail::tools::placementPath(nodes, false).c_str(), code);
0190
0191 if( i == m_entries.end() ) {
0192 path.reserve(nodes.size());
0193 for( Chain::const_reverse_iterator k = nodes.rbegin(), kend=nodes.rend(); k != kend; ++k ) {
0194 const TGeoNode* node = *(k);
0195 auto g4pit = m_geo.g4Placements.find(node);
0196 if( g4pit != m_geo.g4Placements.end() ) {
0197 G4VPhysicalVolume* phys = g4pit->second;
0198 if( phys->IsParameterised() ) {
0199 PlacedVolume pv(n);
0200 PlacedVolumeExtension* ext = pv.data();
0201 if( nullptr == ext->params->field ) {
0202 ext->params->field = iddesc.field(ext->volIDs.at(0).first);
0203 }
0204 }
0205 path.emplace_back(phys);
0206 printout(print_chain, "Geant4VolumeManager",
0207 "+++ Chain: Node OK: %s [%s]", node->GetName(), phys->GetName().c_str());
0208 continue;
0209 }
0210 control.insert(control.begin(),node);
0211 vol = Volume(node->GetVolume());
0212 auto iVolImp = m_geo.g4VolumeImprints.find(vol);
0213 if ( iVolImp != m_geo.g4VolumeImprints.end() ) {
0214 for(const auto& imp : iVolImp->second ) {
0215 const auto& c = imp.first;
0216 if ( c.size() <= control.size() && control == c ) {
0217 path.emplace_back(imp.second);
0218 printout(print_chain, "Geant4VolumeManager", "+++ Chain: Node OK: %s %s -> %s",
0219 node->GetName(), detail::tools::placementPath(c,false).c_str(),
0220 imp.second->GetName().c_str());
0221 control.clear();
0222 break;
0223 }
0224 }
0225 }
0226 }
0227 if ( control.empty() ) {
0228 printout(print_res, "Geant4VolumeManager", "+++ Encoded Volume IDs:%s",
0229 detail::tools::toString(iddesc,ids,code).c_str());
0230 path.erase(path.begin()+path.size()-1);
0231 printout(print_res, "Geant4VolumeManager", "+++ Map %016X to Geant4 Path:%s",
0232 (void*)code, Geant4TouchableHandler::placementPath(path).c_str());
0233 auto hash = detail::hash64(&path[0], path.size()*sizeof(path[0]));
0234 bool missing_hash_path = m_geo.g4Paths.find(hash) == m_geo.g4Paths.end();
0235 #ifdef VOLMGR_HAVE_DEBUG_INFO
0236 {
0237 bool missing_real_path = m_geo.g4DebugInfo->g4Paths.find(path) == m_geo.g4DebugInfo->g4Paths.end();
0238 if ( missing_real_path != missing_hash_path ) {
0239 if ( !path.empty() )
0240 printout(ERROR,"Geant4VolumeManager"," New G4 path: %s", Geant4TouchableHandler::placementPath(path).c_str());
0241 if ( !nodes.empty() )
0242 printout(ERROR,"Geant4VolumeManager"," TGeo path: %s", detail::tools::placementPath(nodes,false).c_str());
0243 printout(ERROR,"Geant4VolumeManager", " Offend.VolIDs: %s", detail::tools::toString(iddesc,ids,code).c_str());
0244 }
0245 if ( missing_real_path ) {
0246 Geant4GeometryInfo::PlacementFlags opt;
0247 opt.flags.parametrised = path.front()->IsParameterised() ? 1 : 0;
0248 opt.flags.replicated = path.front()->IsReplicated() ? 1 : 0;
0249 m_geo.g4DebugInfo->g4Paths[path] = { code, opt.value };
0250 }
0251 }
0252 #endif
0253 if ( missing_hash_path ) {
0254 Geant4GeometryInfo::PlacementFlags opt;
0255 opt.flags.parametrised = path.front()->IsParameterised() ? 1 : 0;
0256 opt.flags.replicated = path.front()->IsReplicated() ? 1 : 0;
0257 m_geo.g4Paths[hash] = { code, opt.value };
0258 if( m_debug&Geant4VolumeManager::PRINT_VOLIDS ) {
0259 std::string idstr = iddesc.str(code);
0260 printout(ALWAYS, "Geant4VolumeManager",
0261 "+++ Decoded Volume IDs: %016llX -> %s", code, idstr.c_str());
0262 }
0263 m_entries.emplace(code);
0264 return;
0265 }
0266
0267 if ( !path.empty() && (path.front()->IsParameterised() || path.front()->IsReplicated()) ) {
0268 return;
0269 }
0270 printout(ERROR, "Geant4VolumeManager", "populate: Severe error: Duplicated Geant4 path!!!! %s %s",
0271 " [THIS SHOULD NEVER HAPPEN]", Geant4TouchableHandler::placementPath(path).c_str());
0272 goto Err;
0273 }
0274 printout(INFO, "Geant4VolumeManager", "Control block has still %d entries:%s",
0275 int(control.size()), detail::tools::placementPath(control,true).c_str());
0276 print_nodes = true;
0277 goto Err;
0278 }
0279 else {
0280
0281 if ( !path.empty() && (path.front()->IsParameterised() || path.front()->IsReplicated()) ) {
0282 return;
0283 }
0284 }
0285 printout(ERROR, "Geant4VolumeManager", "populate: Severe error: Duplicated Volume entry: 0x%X"
0286 " [THIS SHOULD NEVER HAPPEN]", code);
0287
0288 Err:
0289 if ( i != m_entries.end() )
0290 printout( ERROR,"Geant4VolumeManager"," Known G4 path: %s", Geant4TouchableHandler::placementPath(path).c_str() );
0291 if ( !path.empty() )
0292 printout( ERROR,"Geant4VolumeManager"," New G4 path: %s", Geant4TouchableHandler::placementPath(path).c_str() );
0293 if ( !nodes.empty() ) {
0294 printout( ERROR,"Geant4VolumeManager"," TGeo path: %s", detail::tools::placementPath(nodes,false).c_str() );
0295 if( print_nodes ) {
0296 std::string node_path;
0297 for( std::size_t in=0; in<nodes.size(); ++in ) {
0298 PlacedVolume pv(nodes[in]);
0299 node_path += "/";
0300 node_path += pv.name();
0301 printout( ALWAYS,"Geant4VolumeManager", " TGeo Node[%ld]: %s [%p] Volids: '%s'",
0302 in, node_path.c_str(), (void*)nodes[in], pv.volIDs().str().c_str() );
0303 }
0304 }
0305 }
0306 printout( ERROR,"Geant4VolumeManager", " Offend.VolIDs: %s", detail::tools::toString(iddesc,ids,code).c_str() );
0307 throw std::runtime_error("Failed to populate Geant4 volume manager!");
0308 }
0309 };
0310 }
0311
0312
0313 Geant4VolumeManager::Geant4VolumeManager(const Detector& description, Geant4GeometryInfo* info, long debug)
0314 : Handle<Geant4GeometryInfo>(info) {
0315 if( info && info->valid ) {
0316 if( !info->has_volmgr ) {
0317 Populator p(description, *info, debug);
0318 printout( ALWAYS, "Geant4VolumeManager", "+++ Populating Geant4 volume manager.");
0319 p.populate(description.world());
0320 printout( ALWAYS, "Geant4VolumeManager",
0321 "+++ Geant4 volume manager populated with %ld sensitive path entries.",
0322 info->g4Paths.size() );
0323 if( debug&PRINT_ENTRIES ) {
0324 int count = 0;
0325 VolumeManager volmgr = description.volumeManager();
0326 for( auto it=info->g4Paths.begin(); it != info->g4Paths.end(); ++it, ++count ) {
0327 VolumeID volid = it->second.volumeID;
0328 VolumeManagerContext* context = volmgr.lookupContext(volid);
0329 if( context ) {
0330 std::string path = context->element.path();
0331 PlacedVolume plac = context->volumePlacement();
0332 SensitiveDetector sens = plac.volume().sensitiveDetector();
0333 std::string idstr = sens.idSpec().str(volid);
0334 printout( ALWAYS, "Geant4VolumeManager", "%8d: %016X %s -> %s",
0335 count, volid, path.c_str(), plac.name());
0336 printout(ALWAYS, "Geant4VolumeManager", "%8s %16s %s", "", "", idstr.c_str());
0337 }
0338 else {
0339 printout( ERROR, "Geant4VolumeManager",
0340 "Missing volume manager entry: volume ID %016X", volid);
0341 }
0342 }
0343 }
0344 info->has_volmgr = true;
0345 }
0346 return;
0347 }
0348 except("Geant4VolumeManager", "Attempt populate from invalid Geant4 geometry info [Invalid-Info]");
0349 }
0350
0351
0352 std::vector<const G4VPhysicalVolume*>
0353 Geant4VolumeManager::placementPath(const G4VTouchable* touchable, bool exception) const {
0354 Geant4TouchableHandler handler(touchable);
0355 return handler.placementPath(exception);
0356 }
0357
0358
0359 bool Geant4VolumeManager::checkValidity() const {
0360 if( !isValid() ) {
0361 except("Geant4VolumeManager", "Attempt to use invalid Geant4 volume manager [Invalid-Handle]");
0362 }
0363 else if( !ptr()->valid ) {
0364 except("Geant4VolumeManager", "Attempt to use invalid Geant4 geometry info [Invalid-Info]");
0365 }
0366 return true;
0367 }
0368
0369 namespace {
0370 std::string debug_status(const Geant4VolumeManager* mgr) {
0371 char text[256];
0372 auto* p = mgr->ptr();
0373 if ( p ) {
0374 ::snprintf(text, sizeof(text), "==> #path entries: %ld valid: %s has_volmgr: %s",
0375 p->g4Paths.size(), yes_no(p->valid), yes_no(p->has_volmgr));
0376 return { text };
0377 }
0378 return { "Invalid handle to Geant4GeometryInfo" };
0379 }
0380 }
0381
0382
0383 VolumeID Geant4VolumeManager::volumeID(const G4VTouchable* touchable) const {
0384 Geant4TouchableHandler handler(touchable);
0385 std::vector<const G4VPhysicalVolume*> path = handler.placementPath();
0386 if( !isValid() ) {
0387 printout(INFO, "Geant4VolumeManager", "+++ INVALID Geant4VolumeManager handle.");
0388 return NonExisting;
0389 }
0390 else if( !ptr()->valid ) {
0391 printout(INFO, "Geant4VolumeManager", "+++ INVALID Geant4VolumeManager [Not initialized]");
0392 return NonExisting;
0393 }
0394 else if( path.empty() ) {
0395 printout(INFO, "Geant4VolumeManager", "+++ EMPTY volume Geant4 Path: %s",
0396 Geant4TouchableHandler::placementPath(path).c_str());
0397 return NonExisting;
0398 }
0399 else {
0400 uint64_t hash = detail::hash64(&path[0], sizeof(path[0])*path.size());
0401 auto i = ptr()->g4Paths.find(hash);
0402 if( i != ptr()->g4Paths.end() ) {
0403 const auto& e = (*i).second;
0404 VolumeID volid = e.volumeID;
0405
0406 if( e.flags == 0 ) {
0407 return volid;
0408 }
0409 const auto& paramterised = ptr()->g4Parameterised;
0410 const auto& replicated = ptr()->g4Replicated;
0411
0412 for( std::size_t j=0; j < path.size(); ++j ) {
0413 const auto* phys = path[j];
0414 if( phys->IsParameterised() ) {
0415 int copy_no = touchable->GetCopyNumber(j);
0416 const auto it = paramterised.find(phys);
0417 if( it != paramterised.end() ) {
0418
0419
0420 const auto* field = (*it).second.data()->params->field;
0421 volid |= IDDescriptor::encode(field, copy_no);
0422 continue;
0423 }
0424 except("Geant4VolumeManager",
0425 "Error Geant4VolumeManager::volumeID(const G4VTouchable* touchable)");
0426 }
0427 else if( phys->IsReplicated() ) {
0428 int copy_no = touchable->GetCopyNumber(j);
0429 const auto it = replicated.find(phys);
0430 if( it != replicated.end() ) {
0431 const auto* field = (*it).second.data()->params->field;
0432 volid |= IDDescriptor::encode(field, copy_no);
0433 continue;
0434 }
0435 except("Geant4VolumeManager",
0436 "Error Geant4VolumeManager::volumeID(const G4VTouchable* touchable)");
0437 }
0438 }
0439 return volid;
0440 }
0441 if( !path[0] ) {
0442 printout(INFO, "Geant4VolumeManager", "+++ Bad Geant4 volume path: \'%s\' [invalid path] %s",
0443 Geant4TouchableHandler::placementPath(path).c_str(), debug_status(this).c_str());
0444 return InvalidPath;
0445 }
0446 else if( !path[0]->GetLogicalVolume()->GetSensitiveDetector() ) {
0447 printout(DEBUG, "Geant4VolumeManager", "+++ Bad Geant4 volume path: \'%s\' [insensitive] %s",
0448 Geant4TouchableHandler::placementPath(path).c_str(), debug_status(this).c_str());
0449 return Insensitive;
0450 }
0451 printout(INFO, "Geant4VolumeManager",
0452 "+++ Bad Geant4 volume path: \'%s\' [missing entry] %s",
0453 Geant4TouchableHandler::placementPath(path).c_str(), debug_status(this).c_str());
0454 return NonExisting;
0455 }
0456 printout(INFO, "Geant4VolumeManager", "+++ Bad Geant4 volume path: \'%s\' %s",
0457 Geant4TouchableHandler::placementPath(path).c_str(), yes_no(path.empty()));
0458 return NonExisting;
0459 }
0460
0461
0462 void Geant4VolumeManager::volumeDescriptor(const std::vector<const G4VPhysicalVolume*>& path,
0463 VolIDDescriptor& vol_desc) const
0464 {
0465 vol_desc.second.clear();
0466 vol_desc.first = NonExisting;
0467 if( !path.empty() && checkValidity() ) {
0468 auto hash = detail::hash64(&path[0], sizeof(path[0])*path.size());
0469 auto i = ptr()->g4Paths.find(hash);
0470 if( i != ptr()->g4Paths.end() ) {
0471 VolumeID vid = (*i).second.volumeID;
0472 G4LogicalVolume* lvol = path[0]->GetLogicalVolume();
0473 if( lvol->GetSensitiveDetector() ) {
0474 const auto* node = path[0];
0475 const auto& pm = ptr()->g4Placements;
0476 for( const auto& ipm : pm ) {
0477 if ( ipm.second == node ) {
0478 PlacedVolume pv = ipm.first;
0479 SensitiveDetector sd = pv.volume().sensitiveDetector();
0480 IDDescriptor dsc = sd.readout().idSpec();
0481 vol_desc.first = vid;
0482 dsc.decodeFields(vid, vol_desc.second);
0483 return;
0484 }
0485 }
0486 }
0487 vol_desc.first = Insensitive;
0488 return;
0489 }
0490 if( !path[0] )
0491 vol_desc.first = InvalidPath;
0492 else if( !path[0]->GetLogicalVolume()->GetSensitiveDetector() )
0493 vol_desc.first = Insensitive;
0494 else
0495 vol_desc.first = NonExisting;
0496 }
0497 }
0498
0499
0500 void Geant4VolumeManager::volumeDescriptor(const G4VTouchable* touchable,
0501 VolIDDescriptor& vol_desc) const {
0502 volumeDescriptor(placementPath(touchable), vol_desc);
0503 }