Skip to content
Snippets Groups Projects
Geant4VolumeManager.cpp 15.2 KiB
Newer Older
//==========================================================================
Markus Frank's avatar
Markus Frank committed
//  AIDA Detector description implementation 
//--------------------------------------------------------------------------
Markus Frank's avatar
Markus Frank committed
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
// Author     : M.Frank
//
//==========================================================================
#include <DD4hep/Printout.h>
#include <DD4hep/Volumes.h>
#include <DD4hep/DetElement.h>
#include <DD4hep/DetectorTools.h>
#include <DD4hep/VolumeManager.h>
#include <DD4hep/detail/VolumeManagerInterna.h>
#include <DDG4/Geant4VolumeManager.h>
#include <DDG4/Geant4TouchableHandler.h>
#include <DDG4/Geant4Mapping.h>
#include <G4VTouchable.hh>
#include <G4LogicalVolume.hh>
#include <G4VPhysicalVolume.hh>
// C/C++ include files
#include <sstream>

Markus Frank's avatar
Markus Frank committed
using namespace dd4hep::sim;
using namespace dd4hep;
#include <DDG4/Geant4AssemblyVolume.h>
Markus Frank's avatar
Markus Frank committed
using VolIDDescriptor = std::pair<VolumeID,std::vector<std::pair<const BitFieldElement*, VolumeID> > >;

namespace  {
  /// Helper class to populate the Geant4 volume manager
  /**
   *  \author  M.Frank
   *  \version 1.0
   *  \ingroup DD4HEP_SIMULATION
   */
  struct Populator {
Markus Frank's avatar
Markus Frank committed

    typedef std::vector<const TGeoNode*> Chain;
    // typedef std::map<VolumeID,Geant4TouchableHandler::Geant4PlacementPath> Registries;
    typedef std::set<VolumeID> Registries;
Markus Frank's avatar
Markus Frank committed

Markus Frank's avatar
Markus Frank committed
    /// Reference to the Detector instance
Markus Frank's avatar
Markus Frank committed
    const Detector&     m_detDesc;
    /// Set of already added entries
Markus Frank's avatar
Markus Frank committed
    Registries          m_entries;
    /// Reference to Geant4 translation information
    Geant4GeometryInfo& m_geo;

    /// Default constructor
    Populator(const Detector& description, Geant4GeometryInfo& g)
      : m_detDesc(description), m_geo(g)
    {
    }

    typedef std::pair<VolumeID, VolumeID> Encoding;
    /// Compute the encoding for a set of VolIDs within a readout descriptor
    static Encoding encoding(const IDDescriptor iddesc, const PlacedVolume::VolIDs& ids)  {
      VolumeID volume_id = 0, mask = 0;
      for( const auto& id : ids )  {
        const BitFieldElement* f = iddesc.field(id.first);
        VolumeID msk = f->mask();
        int      off = f->offset();
        VolumeID val = id.second;    // Necessary to extend volume IDs > 32 bit
        volume_id |= ((f->value(val << off) << off)&msk);
        mask      |= msk;
      }
      return std::make_pair(volume_id, mask);
    void populate(DetElement e)  {
      const DetElement::Children& c = e.children();
      for( const auto& i : c )  {
        DetElement de = i.second;
        PlacedVolume pv = de.placement();
        if( pv.isValid() )  {
          Chain chain;
          SensitiveDetector sd;
          PlacedVolume::VolIDs ids;
          m_entries.clear();
          chain.emplace_back(m_detDesc.world().placement().ptr());
Markus Frank's avatar
Markus Frank committed
          scanPhysicalVolume(pv.ptr(), std::move(ids), sd, chain);
Markus Frank's avatar
Markus Frank committed
        printout(WARNING, "Geant4VolumeManager",
                 "++ Detector element %s of type %s has no placement.",
                 de.name(), de.type().c_str());
      /// Needed to compute the cellID of parameterized volumes
Markus Frank's avatar
Markus Frank committed
      for( const auto& pv : m_geo.g4Placements )  {
        if( pv.second->IsParameterised() )
          m_geo.g4Parameterised[pv.second] = pv.first;
        if( pv.second->IsReplicated() )
          m_geo.g4Replicated[pv.second] = pv.first;
      m_entries.clear();
    }

    /// Scan a single physical volume and look for sensitive elements below
    void scanPhysicalVolume(const TGeoNode* node, PlacedVolume::VolIDs ids, SensitiveDetector& sd, Chain& chain) {
      Volume vol = pv.volume();
      PlacedVolume::VolIDs pv_ids = pv.volIDs();

      chain.emplace_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, "Geant4VolumeManager",
                   "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);
        if( placement.data() ) {
          scanPhysicalVolume(daughter, ids, sd, chain);
        }
    void add_entry(SensitiveDetector sd, const TGeoNode* n, const PlacedVolume::VolIDs& ids, const Chain& nodes) {
      Chain           control;
      Volume          vol;
      Readout         rdout   = sd.readout();
      IDDescriptor    iddesc  = rdout.idSpec();
      VolumeID        code    = iddesc.encode(ids);
      PrintLevel print_level  = m_geo.printLevel;
      PrintLevel print_action = print_level;
      PrintLevel print_chain  = print_level;
      PrintLevel print_res    = print_level;
      Geant4TouchableHandler::Geant4PlacementPath path;
      Registries::const_iterator i = m_entries.find(code);
      printout(print_action,"Geant4VolumeManager","+++ Add path:%s vid:%016X",
               detail::tools::placementPath(nodes, false).c_str(), code);
Markus Frank's avatar
Markus Frank committed
      if( i == m_entries.end() ) {
        path.reserve(nodes.size());
Markus Frank's avatar
Markus Frank committed
        for( Chain::const_reverse_iterator k = nodes.rbegin(), kend=nodes.rend(); k != kend; ++k ) {
          const TGeoNode* node = *(k);
Markus Frank's avatar
Markus Frank committed
          auto g4pit = m_geo.g4Placements.find(node);
          if( g4pit != m_geo.g4Placements.end() )  {
            G4VPhysicalVolume* phys = g4pit->second;
            if( phys->IsParameterised() )  {
              PlacedVolume pv(n);
              PlacedVolumeExtension* ext = pv.data();
Markus Frank's avatar
Markus Frank committed
              if( nullptr == ext->params->field )  {
                ext->params->field = iddesc.field(ext->volIDs.at(0).first);
              }
            }
            printout(print_chain, "Geant4VolumeManager",
                     "+++     Chain: Node OK: %s [%s]", node->GetName(), phys->GetName().c_str());
            continue;
          }
          control.insert(control.begin(),node);
          vol = Volume(node->GetVolume());
Markus Frank's avatar
Markus Frank committed
          auto iVolImp = m_geo.g4VolumeImprints.find(vol);
          if ( iVolImp != m_geo.g4VolumeImprints.end() )  {
            for(const auto& imp : iVolImp->second )  {
              const auto& c = imp.first;
              if ( c.size() <= control.size() && control == c )  {
                path.emplace_back(imp.second);
                printout(print_chain, "Geant4VolumeManager", "+++     Chain: Node OK: %s %s -> %s",
Markus Frank's avatar
Markus Frank committed
                         node->GetName(), detail::tools::placementPath(c,false).c_str(),
                         imp.second->GetName().c_str());
Markus Frank's avatar
Markus Frank committed
        if ( control.empty() )  {
          printout(print_res, "Geant4VolumeManager", "+++     Volume  IDs:%s",
                   detail::tools::toString(rdout.idSpec(),ids,code).c_str());
          path.erase(path.begin()+path.size()-1);
          printout(print_res, "Geant4VolumeManager", "+++     Map %016X to Geant4 Path:%s",
                   (void*)code, Geant4TouchableHandler::placementPath(path).c_str());
          auto hash = detail::hash64(&path[0], path.size()*sizeof(path[0]));
          if ( m_geo.g4Paths.find(hash) == m_geo.g4Paths.end() ) {
            Geant4GeometryInfo::PlacementFlags opt;
            opt.flags.parametrised = path.front()->IsParameterised() ? 1 : 0;
            opt.flags.replicated   = path.front()->IsReplicated()    ? 1 : 0;
            m_geo.g4Paths[hash]    = { code, opt.value };
            m_entries.emplace(code);
          /// This is a normal case for parametrized volumes and no error
Markus Frank's avatar
Markus Frank committed
          if ( !path.empty() && (path.front()->IsParameterised() || path.front()->IsReplicated()) )  {
          printout(ERROR, "Geant4VolumeManager", "populate: Severe error: Duplicated Geant4 path!!!! %s %s",
                   " [THIS SHOULD NEVER HAPPEN]", Geant4TouchableHandler::placementPath(path).c_str());
          goto Err;
        }
        printout(INFO, "Geant4VolumeManager", "Control block has still %d entries:%s",
                 int(control.size()), detail::tools::placementPath(control,true).c_str());
        /// This is a normal case for parametrized volumes and no error
Markus Frank's avatar
Markus Frank committed
        if ( !path.empty() && (path.front()->IsParameterised() || path.front()->IsReplicated()) )  {
      printout(ERROR, "Geant4VolumeManager", "populate: Severe error: Duplicated Volume entry: 0x%X"
        printout(ERROR,"Geant4VolumeManager"," Known G4 path: %s", Geant4TouchableHandler::placementPath(path).c_str());
Markus Frank's avatar
Markus Frank committed
      if ( !path.empty() )
        printout(ERROR,"Geant4VolumeManager"," New   G4 path: %s", Geant4TouchableHandler::placementPath(path).c_str());
Markus Frank's avatar
Markus Frank committed
      if ( !nodes.empty() )
        printout(ERROR,"Geant4VolumeManager","     TGeo path: %s", detail::tools::placementPath(nodes,false).c_str());
      printout(ERROR,"Geant4VolumeManager",  " Offend.VolIDs: %s", detail::tools::toString(rdout.idSpec(),ids,code).c_str());
      throw std::runtime_error("Failed to populate Geant4 volume manager!");
    }
  };
}

/// Initializing constructor. The tree will automatically be built if possible
Geant4VolumeManager::Geant4VolumeManager(const Detector& description, Geant4GeometryInfo* info)
Markus Frank's avatar
Markus Frank committed
  : Handle<Geant4GeometryInfo>(info)  {
  if( info && info->valid )  {
    if( !info->has_volmgr )  {
      Populator p(description, *info);
      p.populate(description.world());
      info->has_volmgr = true;
    }
  except("Geant4VolumeManager", "Attempt populate from invalid Geant4 geometry info [Invalid-Info]");
}

/// Helper: Generate placement path from touchable object
std::vector<const G4VPhysicalVolume*>
Geant4VolumeManager::placementPath(const G4VTouchable* touchable, bool exception) const  {
  Geant4TouchableHandler handler(touchable);
  return handler.placementPath(exception);
}

/// Check the validity of the information before accessing it.
bool Geant4VolumeManager::checkValidity() const  {
  if( !isValid() )  {
    except("Geant4VolumeManager", "Attempt to use invalid Geant4 volume manager [Invalid-Handle]");
  else if( !ptr()->valid )  {
    except("Geant4VolumeManager", "Attempt to use invalid Geant4 geometry info [Invalid-Info]");
}

/// Access CELLID by Geant4 touchable object
VolumeID Geant4VolumeManager::volumeID(const G4VTouchable* touchable) const  {
  Geant4TouchableHandler handler(touchable);
  std::vector<const G4VPhysicalVolume*> path = handler.placementPath();
  if( !isValid() )  {
    printout(INFO, "Geant4VolumeManager", "+++   INVALID Geant4VolumeManager handle.");
    return NonExisting;
  }
  else if( !ptr()->valid )  {
    printout(INFO, "Geant4VolumeManager", "+++   INVALID Geant4VolumeManager [Not initialized]");
    return NonExisting;
  }
  else if( path.empty() )  {
    printout(INFO, "Geant4VolumeManager", "+++   EMPTY volume Geant4 Path: %s",
	     Geant4TouchableHandler::placementPath(path).c_str());
    return NonExisting;
  }
  else  {
    uint64_t hash = detail::hash64(&path[0], sizeof(path[0])*path.size());
    auto i = ptr()->g4Paths.find(hash);
    if( i != ptr()->g4Paths.end() )  {
      VolumeID volid = e.volumeID;
      /// No parametrization or replication.
Markus Frank's avatar
Markus Frank committed
      if( e.flags == 0 )  {
        return volid;
      }
      const auto& paramterised = ptr()->g4Parameterised;
      const auto& replicated   = ptr()->g4Replicated;
      /// This is incredibly slow .... but what can I do ? Need a better idea.
Markus Frank's avatar
Markus Frank committed
      for( std::size_t j=0; j < path.size(); ++j )  {
        const auto* phys = path[j];
Markus Frank's avatar
Markus Frank committed
        if( phys->IsParameterised() )  {
          int copy_no = touchable->GetCopyNumber(j);
          const auto it = paramterised.find(phys);
Markus Frank's avatar
Markus Frank committed
          if( it != paramterised.end() )  {
            //printout(INFO,"Geant4VolumeManager",
            //         "Copy number:   %ld  <--> %ld", copy_no, long(phys->GetCopyNo()));
            const auto* field = (*it).second.data()->params->field;
            volid |= IDDescriptor::encode(field, copy_no);
            continue;
          }
          except("Geant4VolumeManager",
                 "Error  Geant4VolumeManager::volumeID(const G4VTouchable* touchable)");
Markus Frank's avatar
Markus Frank committed
        else if( phys->IsReplicated() )   {
          int copy_no = touchable->GetCopyNumber(j);
          const auto it = replicated.find(phys);
Markus Frank's avatar
Markus Frank committed
          if( it != replicated.end() )  {
            const auto* field = (*it).second.data()->params->field;
            volid |= IDDescriptor::encode(field, copy_no);
            continue;
          }
          except("Geant4VolumeManager",
                 "Error  Geant4VolumeManager::volumeID(const G4VTouchable* touchable)");
Markus Frank's avatar
Markus Frank committed
    if( !path[0] )
Markus Frank's avatar
Markus Frank committed
    else if( !path[0]->GetLogicalVolume()->GetSensitiveDetector() )
  printout(INFO, "Geant4VolumeManager", "+++   Bad Geant4 volume path: \'%s\' Path empty: %s",
           Geant4TouchableHandler::placementPath(path).c_str(), yes_no(path.empty()));
}

/// Accessfully decoded volume fields  by placement path
void Geant4VolumeManager::volumeDescriptor(const std::vector<const G4VPhysicalVolume*>& path,
Markus Frank's avatar
Markus Frank committed
                                           VolIDDescriptor& vol_desc) const
{
  vol_desc.second.clear();
  vol_desc.first = NonExisting;
Markus Frank's avatar
Markus Frank committed
  if( !path.empty() && checkValidity() )  {
    auto hash = detail::hash64(&path[0], sizeof(path[0])*path.size());
    auto i = ptr()->g4Paths.find(hash);
    if( i != ptr()->g4Paths.end() )  {
      VolumeID vid = (*i).second.volumeID;
      G4LogicalVolume* lvol = path[0]->GetLogicalVolume();
Markus Frank's avatar
Markus Frank committed
      if( lvol->GetSensitiveDetector() ) {
        const auto* node = path[0];
        const auto& pm = ptr()->g4Placements;
        for( const auto& ipm : pm )  {
          if ( ipm.second == node )  {
            PlacedVolume      pv  = ipm.first;
            SensitiveDetector sd  = pv.volume().sensitiveDetector();
            IDDescriptor      dsc = sd.readout().idSpec();
            vol_desc.first = vid;
            dsc.decodeFields(vid, vol_desc.second);
            return;
          }
        }
Markus Frank's avatar
Markus Frank committed
    if( !path[0] )
Markus Frank's avatar
Markus Frank committed
    else if( !path[0]->GetLogicalVolume()->GetSensitiveDetector() )
      vol_desc.first = Insensitive;
    else
      vol_desc.first = NonExisting;
  }
}

/// Access fully decoded volume fields by Geant4 touchable object
Markus Frank's avatar
Markus Frank committed
void Geant4VolumeManager::volumeDescriptor(const G4VTouchable* touchable,
Markus Frank's avatar
Markus Frank committed
                                           VolIDDescriptor&    vol_desc)  const  {
  volumeDescriptor(placementPath(touchable), vol_desc);