Skip to content
Snippets Groups Projects
Geant4VolumeManager.cpp 8.28 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"

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);
	if ( dynamic_cast<const PlacedVolume::Object*>(daughter) ) {
	  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);
}