Skip to content
Snippets Groups Projects
Geant4VolumeManager.cpp 9.2 KiB
Newer Older
// $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"

// C/C++ include files
#include <sstream>

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;
    typedef map<VolumeID,Geant4Mapping::PlacementPath> Registries;
    /// Reference to the LCDD instance
    LCDD& m_lcdd;
    /// Set of already added entries
    Registries m_entries;
    /// Reference to Geant4 translation information
    Geant4GeometryInfo& m_geo;

    /// Default constructor
    Populator(LCDD& lcdd, Geant4GeometryInfo& g)
        : m_lcdd(lcdd), m_geo(g) {
    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, "Geant4VolumeManager", "++ 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, "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);
	PlacedVolume placement(daughter);
	if ( placement.data() ) {
          scanPhysicalVolume(daughter, ids, sd, chain);
        }
    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);
      Registries::const_iterator i = m_entries.find(code);
      if (i == 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()) {
	  stringstream log;
          printout(ERROR, "Geant4VolumeManager", "populate: Severe error: Duplicated Geant4 path!!!!");
	  for(Geant4Mapping::PlacementPath::const_iterator j=path.begin(); j!=path.end(); ++j)  {
	    log << "/" << (*j)->GetName();
	  }
	  printout(ERROR, "Geant4VolumeManager", "         Geant4 path exists in at least 2 instances: %s", log.str().c_str());
        }
        m_geo.g4Paths[path] = code;
        m_entries.insert(make_pair(code,path));
	stringstream log;
        printout(ERROR, "Geant4VolumeManager", "populate: Severe error: Duplicated Volume entry: %X", code);
	const Geant4Mapping::PlacementPath& path = (*i).second;
	for(Geant4Mapping::PlacementPath::const_iterator j=path.begin(); j!=path.end(); ++j)  {
	  log << "/" << (*j)->GetName();
	}
        printout(ERROR, "Geant4VolumeManager", "         Geant4 path: %s", log.str().c_str());
      }
    }
  };
}

/// 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);
  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) {
  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;
  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);
  throw runtime_error(format("Geant4VolumeManager", "Attempt to lookup invalid TGeo placement [Null-Pointer]"));
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])
    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;
    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);