diff --git a/DDAlign/CMakeLists.txt b/DDAlign/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2313286bb1711b45d0b3e259f9510adc74e4715f --- /dev/null +++ b/DDAlign/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR) +#--------------------------- +set( PackageName DDAlign ) + +#---add additional packages here-------------------------------------------------- +find_package( DD4hep ) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${DD4hep_ROOT}/cmake ) +include( DD4hep ) + +find_package( ROOT REQUIRED ) +#---Includedirs ------------------------------------------------------------------ +include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/include + ${DD4hep_INCLUDE_DIRS} + ${ROOT_INCLUDE_DIR} ) + +#---DD4hepAlign library -------------------------------------------------------------- +file(GLOB sources src/*.cpp) +add_library(DD4hepAlign SHARED ${sources}) +target_link_libraries(DD4hepAlign ${DD4hep_LIBRARIES} DD4hepCore ${ROOT_LIBRARIES} ${ROOT_COMPONENT_LIBRARIES}) +SET( CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic -Wno-long-long") +if(DD4HEP_USE_XERCESC) + add_definitions(-DDD4HEP_USE_XERCESC) +else() + add_definitions(-DDD4HEP_USE_TINYXML) +endif() +SET_TARGET_PROPERTIES( DD4hepAlign PROPERTIES VERSION ${DD4hep_VERSION} SOVERSION ${DD4hep_SOVERSION}) +#---DD4hepAlign rootmap -------------------------------------------------------------- +dd4hep_generate_rootmap(DD4hepAlign) + +install(DIRECTORY include/DDAlign + DESTINATION include + PATTERN ".svn" EXCLUDE ) + +install(TARGETS DD4hepAlign + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ) +# to do: add corresponding uninstall... +#------------------------------------------------------- diff --git a/DDAlign/include/DDAlign/AlignmentCache.h b/DDAlign/include/DDAlign/AlignmentCache.h new file mode 100644 index 0000000000000000000000000000000000000000..55842e7c11034a0e84dda664710d67b58b590f9d --- /dev/null +++ b/DDAlign/include/DDAlign/AlignmentCache.h @@ -0,0 +1,107 @@ +// $Id: Geant4Setup.cpp 578 2013-05-17 22:33:09Z markus.frank $ +//==================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +#ifndef DD4HEP_ALIGNMENT_ALIGNMENTCACHE_H +#define DD4HEP_ALIGNMENT_ALIGNMENTCACHE_H + +// Framework include files +#include "DD4hep/Alignment.h" +#include "DDAlign/AlignmentStack.h" + +/* + * DD4hep namespace declaration + */ +namespace DD4hep { + + /* + * Alignment namespace declaration + */ + namespace Geometry { + + /// Forward declarations + class AlignmentOperator; + class AlignmentCache; + class AlignmentStack; + class LCDD; + + /** @class AlignmentCache AlignmentCache.h AlignmentCache.h + * + * Class caching all known alignment operations for one LCDD instance. + * Internally the instances are fragmented to subdetectors defined + * by the next-to-top level detector elements. + * + * + * @author M.Frank + * @version 1.0 + */ + class AlignmentCache { + friend class LCDD; + friend class AlignmentOperator; + + public: + typedef AlignmentStack::StackEntry Entry; + typedef std::map<unsigned int, TGeoPhysicalNode*> Cache; + typedef std::map<std::string,AlignmentCache*> SubdetectorAlignments; + + protected: + LCDD& m_lcdd; + /// Cache of subdetectors + SubdetectorAlignments m_detectors; + /// The subdetector specific map of alignments caches + Cache m_cache; + /// Branchg name: If it is not the main tree instance, the name of the subdetector + std::string m_sdPath; + /// The length of the branch name to optimize lookups.... + size_t m_sdPathLen; + /// Reference count + int m_refCount; + /// Flag to indicate the top instance + bool m_top; + + protected: + /// Default constructor + AlignmentCache(LCDD& lcdd, const std::string& sdPath, bool top); + /// Default destructor + virtual ~AlignmentCache(); + /// Retrieve branch cache by name. If not present it will be created + AlignmentCache* subdetectorAlignments(const std::string& name); + + /// Population entry: Apply a complete stack of ordered alignments to the geometry structure + void apply(AlignmentStack& stack); + /// Apply a vector of SD entries of ordered alignments to the geometry structure + void apply(const std::vector<Entry*> &changes); + /// Add a new entry to the cache. The key is the placement path + bool insert(Alignment alignment); + + public: + /// Create and install a new instance tree + static void install(LCDD& lcdd); + /// Unregister and delete a tree instance + static void uninstall(LCDD& lcdd); + + /// Add reference count + int addRef(); + /// Release object. If reference count goes to NULL, automatic deletion is triggered. + int release(); + /// Access the section name + const std::string& name() const { return m_sdPath; } + /// Open a new transaction stack (Note: only one stack allowed!) + void openTransaction(); + /// Close existing transaction stack and apply all alignments + void closeTransaction(); + /// Retrieve the cache section corresponding to the path of an entry. + AlignmentCache* section(const std::string& path_name) const; + /// Retrieve an alignment entry by its lacement path + Alignment get(const std::string& path) const; + /// Return all entries matching a given path. Careful: Expensive operaton! + std::vector<Alignment> matches(const std::string& path_match, bool exclude_exact=false) const; + }; + + } /* End namespace Geometry */ +} /* End namespace DD4hep */ +#endif /* DD4HEP_ALIGNMENT_ALIGNMENTCACHE_H */ diff --git a/DDAlign/include/DDAlign/AlignmentOperators.h b/DDAlign/include/DDAlign/AlignmentOperators.h new file mode 100644 index 0000000000000000000000000000000000000000..1d5b59b27b320c5336452dd5603fd34d32373350 --- /dev/null +++ b/DDAlign/include/DDAlign/AlignmentOperators.h @@ -0,0 +1,89 @@ +// $Id: Geant4Setup.cpp 578 2013-05-17 22:33:09Z markus.frank $ +//==================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +#ifndef DD4HEP_ALIGNMENT_ALIGNMENTOPERATORS_H +#define DD4HEP_ALIGNMENT_ALIGNMENTOPERATORS_H + +// Framework include files +#include "DD4hep/Alignment.h" +#include "DDAlign/AlignmentCache.h" + +/* + * DD4hep namespace declaration + */ +namespace DD4hep { + + /* + * Alignment namespace declaration + */ + namespace Geometry { + + /// Forward declarations + class LCDD; + + + /** @class AlignmentOperator AlignmentOperators.h DDAlign/AlignmentOperators.h + * + * + * + * @author M.Frank + * @version 1.0 + */ + struct AlignmentOperator { + typedef AlignmentStack::StackEntry Entry; + typedef AlignmentCache::Cache Cache; + typedef std::vector<Entry*> Entries; + typedef std::map<std::string,std::pair<TGeoPhysicalNode*,Entry*> > Nodes; + AlignmentCache& cache; + Nodes& nodes; + public: + AlignmentOperator(AlignmentCache& c, Nodes& n) : cache(c), nodes(n) {} + void insert(Alignment alignment) const; + }; + + /** @class AlignmentSelector AlignmentOperators.h DDAlign/AlignmentOperators.h + * + * + * + * @author M.Frank + * @version 1.0 + */ + struct AlignmentSelector : public AlignmentOperator { + public: + const Entries& entries; + AlignmentSelector(AlignmentCache& c, Nodes& n, const Entries& e) : AlignmentOperator(c,n), entries(e) {} + ~AlignmentSelector() { } + const AlignmentSelector& reset() const { nodes.clear(); return *this; } + void operator()(const AlignmentCache::Cache::value_type& e) const; + void operator()(Entry* e) const; + }; + + template <typename T> struct AlignmentActor : public AlignmentOperator { + public: + AlignmentActor(AlignmentCache& c, Nodes& n) : AlignmentOperator(c,n) { init(); } + void init() {} + void operator()(Nodes::value_type& e) const; + }; + + namespace DDAlign_standard_operations { + struct node_print; + struct node_reset; + struct node_align; + struct node_delete; + } + + // Specializations + template <> void AlignmentActor<DDAlign_standard_operations::node_print>::init(); + template <> void AlignmentActor<DDAlign_standard_operations::node_print>::operator() (Nodes::value_type& n) const; + template <> void AlignmentActor<DDAlign_standard_operations::node_delete>::operator()(Nodes::value_type& n) const; + template <> void AlignmentActor<DDAlign_standard_operations::node_reset>::operator() (Nodes::value_type& n) const; + template <> void AlignmentActor<DDAlign_standard_operations::node_align>::operator() (Nodes::value_type& n) const; + + } /* End namespace Geometry */ +} /* End namespace DD4hep */ +#endif /* DD4HEP_ALIGNMENT_ALIGNMENTOPERATORS_H */ diff --git a/DDAlign/include/DDAlign/AlignmentStack.h b/DDAlign/include/DDAlign/AlignmentStack.h new file mode 100644 index 0000000000000000000000000000000000000000..87bb3101118b52253da09e54e99a57254325423b --- /dev/null +++ b/DDAlign/include/DDAlign/AlignmentStack.h @@ -0,0 +1,128 @@ +// $Id: Geant4Setup.cpp 578 2013-05-17 22:33:09Z markus.frank $ +//==================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +#ifndef DD4HEP_ALIGNMENT_ALIGNMENTSTACK_H +#define DD4HEP_ALIGNMENT_ALIGNMENTSTACK_H + +// Framework include files +#include "DD4hep/Detector.h" +#include "DD4hep/Objects.h" + +// C/C++ include files +#include <memory> + +/* + * DD4hep namespace declaration + */ +namespace DD4hep { + + /* + * Alignment namespace declaration + */ + namespace Geometry { + + /** @class AlignmentStack AlignmentStack.h AlignmentStack.h + * + * + * @author M.Frank + * @version 1.0 + */ + class AlignmentStack { + public: + enum { + OVERLAP_DEFINED = 1<<0, + MATRIX_DEFINED = 1<<1, + CHECKOVL_DEFINED = 1<<2, + CHECKOVL_VALUE = 1<<3, + RESET_VALUE = 1<<4, + RESET_CHILDREN = 1<<5, + ____LLLAST = 1<<31 + } Flags; + + struct StackEntry { + DetElement detector; + Transform3D transform; + std::string path; + double overlap; + int flag; + + /// Fully initializing constructor + StackEntry(const DetElement& p, const std::string& placement, const Transform3D& t, double ov, int flg); + /// Constructor with partial initialization + StackEntry(DetElement element, bool rst=true, bool rst_children=true); + /// Constructor with partial initialization + StackEntry(DetElement element, const Transform3D& trafo, bool rst=true, bool rst_children=true); + /// Constructor with partial initialization + StackEntry(DetElement element, const Position& translation, bool rst=true, bool rst_children=true); + /// Constructor with partial initialization + StackEntry(DetElement element, const RotationZYX& rot, bool rst=true, bool rst_children=true); + /// Constructor with partial initialization + StackEntry(DetElement element, const Position& translation, const RotationZYX& rot, bool rst=true, bool rst_children=true); + /// Copy constructor + StackEntry(const StackEntry& e); + /// Default destructor + ~StackEntry() {} + bool checkFlag(int mask) const { return (flag&mask) == mask; } + bool checkOverflow() const { return checkFlag(CHECKOVL_DEFINED); } + bool overflowValue() const { return checkFlag(CHECKOVL_VALUE); } + bool checkOverlap() const { return checkFlag(OVERLAP_DEFINED); } + bool hasMatrix() const { return checkFlag(MATRIX_DEFINED); } + bool needsReset() const { return checkFlag(RESET_VALUE); } + bool resetChildren() const { return checkFlag(RESET_CHILDREN); } + + /// Attach transformation object + StackEntry& setTransformation(const Transform3D& trafo); + /// Instruct entry to ignore the transformation + StackEntry& clearTransformation(); + /// Set flag to reset the entry to it's ideal geometrical position + StackEntry& setReset(bool new_value=true); + /// Set flag to reset the entry's children to their ideal geometrical position + StackEntry& setResetChildren(bool new_value=true); + /// Set flag to check overlaps + StackEntry& setOverlapCheck(bool new_value=true); + /// Set the precision for the overlap check (otherwise the default is 0.001 cm) + StackEntry& setOverlapPrecision(double precision=0.001); + }; + typedef std::map<std::string, StackEntry*> Stack; + + protected: + /// The subdetector specific map of alignments caches + Stack m_stack; + + /// Default constructor + AlignmentStack(); + + public: + + /// Default destructor. Careful with this one: + virtual ~AlignmentStack(); + /// Static client accessor + static AlignmentStack& get(); + /// Create an alignment stack instance. The creation of a second instance will be refused. + static void create(); + /// Check existence of alignment stack + static bool exists(); + /// Add a new entry to the cache. The key is the placement path + static bool insert(const std::string& full_path, StackEntry* new_entry); + /// Add a new entry to the cache. The key is the placement path. The placement path must be set in the entry + static bool insert(StackEntry* new_entry); + /// Clear data content and remove the slignment stack + void release(); + /// Access size of the alignment stack + size_t size() const { return m_stack.size(); } + /// Add a new entry to the cache. The key is the placement path + bool add(StackEntry* new_entry); + /// Retrieve an alignment entry of the current stack + std::auto_ptr<StackEntry> pop(); + /// Get all path entries to be aligned. Note: transient! + std::vector<const StackEntry*> entries() const; + }; + + } /* End namespace Geometry */ +} /* End namespace DD4hep */ +#endif /* DD4HEP_ALIGNMENT_ALIGNMENTSTACK_H */ diff --git a/DDAlign/include/DDAlign/AlignmentTags.h b/DDAlign/include/DDAlign/AlignmentTags.h new file mode 100644 index 0000000000000000000000000000000000000000..2f20da4620f026125c316342ab466310f90f3ec0 --- /dev/null +++ b/DDAlign/include/DDAlign/AlignmentTags.h @@ -0,0 +1,42 @@ +// $Id: XMLTags.h 889 2013-11-14 15:55:39Z markus.frank@cern.ch $ +//==================================================================== +// AIDA Detector description implementation +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +#ifndef DD4HEP_ALIGNMENT_ALIGNMENT_TAGS_H +#define DD4HEP_ALIGNMENT_ALIGNMENT_TAGS_H + +// Framework include files +#include "XML/XMLElements.h" +#ifndef UNICODE +#define UNICODE(x) extern const Tag_t Unicode_##x +#endif + +// Unicode tags known to the alignment section +namespace DD4hep { + namespace XML { + UNICODE(DetectorAlignment); + UNICODE(subdetectors); + UNICODE(subdetector); + UNICODE(detelements); + UNICODE(detelement); + UNICODE(subpath); + UNICODE(path); + UNICODE(pivot); + UNICODE(reset); + UNICODE(placement); + UNICODE(reset_children); + UNICODE(open_transaction); + UNICODE(close_transaction); + UNICODE(check_overlaps); + } +} + +#undef UNICODE // Do not miss this one! + +#include "XML/XMLTags.h" + +#endif /* DD4HEP_ALIGNMENT_ALIGNMENT_TAGS_H */ diff --git a/DDAlign/include/DDAlign/AlignmentWriter.h b/DDAlign/include/DDAlign/AlignmentWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..45b61fac18c6f0107d838766f96ff51b1ddf6f5c --- /dev/null +++ b/DDAlign/include/DDAlign/AlignmentWriter.h @@ -0,0 +1,65 @@ +// $Id: LCDDConverter.h 889 2013-11-14 15:55:39Z markus.frank@cern.ch $ +//==================================================================== +// AIDA Detector description implementation +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +#ifndef DD4HEP_DDALIGN_ALIGNMENTWRITER_H +#define DD4HEP_DDALIGN_ALIGNMENTWRITER_H + +// Framework include files +#include "XML/XMLElements.h" +#include "DD4hep/Detector.h" + +/* + * DD4hep namespace declaration + */ +namespace DD4hep { + + /* + * Geomentry namespace declaration + */ + namespace Geometry { + + // Forward declarations + class LCDD; + class AlignmentCache; + + /** @class AlignmentWriter AlignmentWriter.cpp AlignmentWriter.cpp + * + * Geometry converter from DD4hep to Geant 4. + * + * @author M.Frank + * @version 1.0 + */ + class AlignmentWriter { + protected: + /// Reference to detector description + LCDD& m_lcdd; + /// Reference to the alignment cache + AlignmentCache* m_cache; + + /// Add single alignment node to the XML document + void addNode(XML::Element elt, Alignment a) const; + + public: + /// Initializing Constructor + AlignmentWriter(LCDD& lcdd); + /// Standard destructor + virtual ~AlignmentWriter(); + + /// Dump one full DetElement subtree into a newly created document + XML::Document dump(DetElement element, bool enable_transactions=false) const; + /// Scan one DetElement structure and return an XML element containing the alignment in this subtree. + XML::Element scan(XML::Document doc, DetElement element) const; + /// Create the element corresponding to one single detector element without children + XML::Element createElement(XML::Document doc, DetElement element) const; + /// Write the XML document structure to a file. + long write(XML::Document doc, const std::string& output) const; + }; + } // End namespace XML +} // End namespace DD4hep +#endif // DD4HEP_DDALIGN_ALIGNMENTWRITER_H + diff --git a/DDAlign/include/DDAlign/DetectorAlignment.h b/DDAlign/include/DDAlign/DetectorAlignment.h new file mode 100644 index 0000000000000000000000000000000000000000..055d1bbbad94db5776f6d4b514b433d2d358a493 --- /dev/null +++ b/DDAlign/include/DDAlign/DetectorAlignment.h @@ -0,0 +1,76 @@ +// $Id: Readout.h 951 2013-12-16 23:37:56Z Christian.Grefe@cern.ch $ +//==================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +#ifndef DD4HEP_GEOMETRY_DETECTORALIGNMENT_H +#define DD4HEP_GEOMETRY_DETECTORALIGNMENT_H + +// Framework include files +#include "DD4hep/Detector.h" + +// Forward declarations +class TGeoHMatrix; + +/* + * DD4hep namespace declaration + */ +namespace DD4hep { + + /* + * Geometry namespace declaration + */ + namespace Geometry { + + class DetElement; + + /** @class Alignment Readoutn.h DD4hep/Alignment.h + * + * @author M.Frank + * @version 1.0 + */ + class DetectorAlignment : public DetElement { + protected: + public: + /// Initializing constructor + DetectorAlignment(DetElement e); + /// Collect all placements from the detector element up to the world volume + void collectNodes(std::vector<PlacedVolume>& nodes); + /// Access to the alignment block + Alignment alignment() const; + /// Alignment entries for lower level volumes, which are NOT attached to daughter DetElements + std::vector<Alignment>& volumeAlignments(); + /// Alignment entries for lower level volumes, which are NOT attached to daughter DetElements + const std::vector<Alignment>& volumeAlignments() const; + + /** @DetElement alignment: Calls to align the detector element itself */ + /// Align the PhysicalNode of the placement of the detector element (translation only) + Alignment align(const Position& pos, bool check = false, double overlap = 0.001); + /// Align the PhysicalNode of the placement of the detector element (rotation only) + Alignment align(const RotationZYX& rot, bool check = false, double overlap = 0.001); + /// Align the PhysicalNode of the placement of the detector element (translation + rotation) + Alignment align(const Position& pos, const RotationZYX& rot, bool check = false, double overlap = 0.001); + /// Align the physical node according to a generic Transform3D + Alignment align(const Transform3D& tr, bool check = false, double overlap = 0.001); + /// Align the physical node according to a generic TGeo matrix + Alignment align(TGeoHMatrix* matrix, bool check = false, double overlap = 0.001); + + /** @Volume alignment: Calls to align the volumes within on detector element */ + /// Align the PhysicalNode of the placement of the detector element (translation only) + Alignment align(const std::string& volume_path, const Position& pos, bool check = false, double overlap = 0.001); + /// Align the PhysicalNode of the placement of the detector element (rotation only) + Alignment align(const std::string& volume_path, const RotationZYX& rot, bool check = false, double overlap = 0.001); + /// Align the PhysicalNode of the placement of the detector element (translation + rotation) + Alignment align(const std::string& volume_path, const Position& pos, const RotationZYX& rot, bool check = false, double overlap = 0.001); + /// Align the physical node according to a generic Transform3D + Alignment align(const std::string& volume_path, const Transform3D& tr, bool check = false, double overlap = 0.001); + /// Align the physical node according to a generic TGeo matrix + Alignment align(const std::string& volume_path, TGeoHMatrix* matrix, bool check = false, double overlap = 0.001); + }; + + } /* End namespace Geometry */ +} /* End namespace DD4hep */ +#endif /* DD4HEP_GEOMETRY_DETECTORALIGNMENT_H */ diff --git a/DDAlign/src/AlignmentCache.cpp b/DDAlign/src/AlignmentCache.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49b39b5cdc9bcd10d98c1ad481e6cae20e7df19c --- /dev/null +++ b/DDAlign/src/AlignmentCache.cpp @@ -0,0 +1,256 @@ +// $Id: Geant4Setup.cpp 578 2013-05-17 22:33:09Z markus.frank $ +//==================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +// Framework include files +#include "DD4hep/LCDD.h" +#include "DD4hep/Printout.h" +#include "DD4hep/DetectorTools.h" +#include "DDAlign/AlignmentCache.h" +#include "DDAlign/AlignmentOperators.h" + +// ROOT include files +#include "TGeoManager.h" + +// C/C++ include files +#include <stdexcept> + +using namespace std; +using namespace DD4hep; +using namespace DD4hep::Geometry; +using namespace DD4hep::Geometry::DDAlign_standard_operations; + +typedef AlignmentStack::StackEntry Entry; + +namespace { + /// one-at-time hash function + inline unsigned int hash32(const char* key) { + unsigned int hash = 0; + const char* k = key; + for (; *k; k++) { + hash += *k; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); hash += (hash << 15); + return hash; + } + +} + +DetElement _detector(DetElement child) { + if ( child.isValid() ) { + DetElement p(child.parent()); + if ( p.isValid() && !p.parent().isValid() ) + return child; + else if ( !p.isValid() ) // World detector element... + return child; + return _detector(p); + } + throw runtime_error("DD4hep: DetElement cannot determine detector parent [Invalid handle]"); +} + +/// Default constructor +AlignmentCache::AlignmentCache(LCDD& lcdd, const std::string& sdPath, bool top) + : m_lcdd(lcdd), m_sdPath(sdPath), m_sdPathLen(sdPath.length()), m_refCount(1), m_top(top) +{ +} + +/// Default destructor +AlignmentCache::~AlignmentCache() { + int nentries = (int)m_cache.size(); + int nsect = (int)m_detectors.size(); + releaseObjects(m_detectors)(); + m_cache.clear(); + printout(INFO,"AlignmentCache", + "Destroy cache for subdetector %s [%d section(s), %d entrie(s)]", + m_sdPath.c_str(),nsect,nentries); +} + +/// Add reference count +int AlignmentCache::addRef() { + return ++m_refCount; +} + +/// Release object. If reference count goes to NULL, automatic deletion is triggered. +int AlignmentCache::release() { + int value = --m_refCount; + if ( value == 0 ) { + delete this; + } + return value; +} + +/// Add a new entry to the cache. The key is the placement path +bool AlignmentCache::insert(Alignment alignment) { + TGeoPhysicalNode* pn = alignment.ptr(); + unsigned int index = hash32(pn->GetName()+m_sdPathLen); + Cache::const_iterator i = m_cache.find(index); + printout(ALWAYS,"AlignmentCache","Section: %s adding entry: %s", + name().c_str(),alignment->GetName()); + if ( i == m_cache.end() ) { + m_cache[index] = pn; + return true; + } + return false; +} + +/// Retrieve the cache section corresponding to the path of an entry. +AlignmentCache* AlignmentCache::section(const std::string& path_name) const { + size_t idx, idq; + if ( path_name[0] != '/' ) { + return section(m_lcdd.world().placementPath()+'/'+path_name); + } + else if ( (idx=path_name.find('/',1)) == string::npos ) { + return (m_sdPath == path_name.c_str()+1) ? (AlignmentCache*)this : 0; + } + else if ( m_detectors.empty() ) { + return 0; + } + if ( (idq=path_name.find('/',idx+1)) != string::npos ) --idq; + string path = path_name.substr(idx+1,idq-idx); + SubdetectorAlignments::const_iterator j = m_detectors.find(path); + return (j==m_detectors.end()) ? 0 : (*j).second; +} + +/// Retrieve an alignment entry by its lacement path +Alignment AlignmentCache::get(const std::string& path_name) const { + size_t idx, idq; + unsigned int index = hash32(path_name.c_str()+m_sdPathLen); + Cache::const_iterator i = m_cache.find(index); + if ( i != m_cache.end() ) { + return Alignment((*i).second); + } + else if ( m_detectors.empty() ) { + return Alignment(0); + } + else if ( path_name[0] != '/' ) { + return get(m_lcdd.world().placementPath()+'/'+path_name); + } + else if ( (idx=path_name.find('/',1)) == string::npos ) { + // Escape: World volume and not found in cache --> not present + return Alignment(0); + } + if ( (idq=path_name.find('/',idx+1)) != string::npos ) --idq; + string path = path_name.substr(idx+1,idq-idx); + SubdetectorAlignments::const_iterator j = m_detectors.find(path); + if ( j != m_detectors.end() ) return (*j).second->get(path_name); + return Alignment(0); +} + +/// Return all entries matching a given path. +vector<Alignment> AlignmentCache::matches(const string& match, bool exclude_exact) const { + vector<Alignment> result; + AlignmentCache* c = section(match); + if ( c ) { + size_t len = match.length(); + result.reserve(c->m_cache.size()); + for(Cache::const_iterator i=c->m_cache.begin(); i!=c->m_cache.end();++i) { + const Cache::value_type& v = *i; + const char* n = v.second->GetName(); + if ( 0 == ::strncmp(n,match.c_str(),len) ) { + if ( exclude_exact && len == ::strlen(n) ) continue; + result.push_back(Alignment(v.second)); + } + } + } + return result; +} + +/// Open a new transaction stack (Note: only one stack allowed!) +void AlignmentCache::openTransaction() { + /// Check if transaction already present. If not, open, else issue an error + if ( !AlignmentStack::exists() ) { + AlignmentStack::create(); + return; + } + string msg = "Request to open a second alignment transaction stack -- not allowed!"; + string err = format("Alignment<alignment>",msg); + printout(FATAL,"AlignmentCache",msg); + throw runtime_error(err); +} + +/// Close existing transaction stack and apply all alignments +void AlignmentCache::closeTransaction() { + /// Check if transaction is open. If yes, close it and apply alignment stack. + /// If no transaction is active, ignore the staement, but issue a warning. + if ( AlignmentStack::exists() ) { + TGeoManager& mgr = m_lcdd.manager(); + mgr.UnlockGeometry(); + apply(AlignmentStack::get()); + AlignmentStack::get().release(); + printout(INFO,"AlignmentCache","Alignments were applied. Refreshing physical nodes...."); + mgr.LockGeometry(); + mgr.GetCurrentNavigator()->ResetAll(); + mgr.GetCurrentNavigator()->BuildCache(); + mgr.RefreshPhysicalNodes(); + return; + } + printout(WARNING,"Alignment<alignment>", + "Request to close a non-existing alignmant transaction."); +} + +/// Create and install a new instance tree +void AlignmentCache::install(LCDD& lcdd) { + AlignmentCache* cache = lcdd.extension<AlignmentCache>(false); + if ( !cache ) { + lcdd.addExtension<AlignmentCache>(new AlignmentCache(lcdd,"world",true)); + } +} + +/// Unregister and delete a tree instance +void AlignmentCache::uninstall(LCDD& lcdd) { + if ( lcdd.extension<AlignmentCache>(false) ) { + lcdd.removeExtension<AlignmentCache>(true); + } +} + +/// Retrieve branch cache by name. If not present it will be created +AlignmentCache* AlignmentCache::subdetectorAlignments(const std::string& name) { + SubdetectorAlignments::const_iterator i = m_detectors.find(name); + if ( i == m_detectors.end() ) { + AlignmentCache* ptr = new AlignmentCache(m_lcdd,name,false); + m_detectors.insert(make_pair(name,ptr)); + return ptr; + } + return (*i).second; +} + +/// Apply a complete stack of ordered alignments to the geometry structure +void AlignmentCache::apply(AlignmentStack& stack) { + typedef map<TNamed*,vector<Entry*> > sd_entries_t; + sd_entries_t all; + while(stack.size() > 0) { + Entry* e = stack.pop().release(); + DetElement det = _detector(e->detector); + all[det.ptr()].push_back(e); + } + for(sd_entries_t::iterator i=all.begin(); i!=all.end(); ++i) { + DetElement det(Ref_t((*i).first)); + AlignmentCache* sd_cache = subdetectorAlignments(det.placement().name()); + sd_cache->apply( (*i).second ); + (*i).second.clear(); + } +} + + +/// Apply a vector of SD entries of ordered alignments to the geometry structure +void AlignmentCache::apply(const vector<Entry*>& changes) { + typedef std::map<std::string,std::pair<TGeoPhysicalNode*,Entry*> > Nodes; + typedef vector<Entry*> Changes; + Nodes nodes; + + AlignmentSelector selector(*this,nodes,changes); + for_each(m_cache.begin(),m_cache.end(),selector.reset()); + for_each(nodes.begin(),nodes.end(),AlignmentActor<node_print>(*this,nodes)); + for_each(nodes.begin(),nodes.end(),AlignmentActor<node_reset>(*this,nodes)); + + for_each(changes.begin(),changes.end(),selector.reset()); + for_each(nodes.begin(),nodes.end(),AlignmentActor<node_align>(*this,nodes)); + for_each(nodes.begin(),nodes.end(),AlignmentActor<node_delete>(*this,nodes)); +} diff --git a/DDAlign/src/AlignmentOperators.cpp b/DDAlign/src/AlignmentOperators.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe73c627de507c62058bfe8af5808d8a28c4f2d9 --- /dev/null +++ b/DDAlign/src/AlignmentOperators.cpp @@ -0,0 +1,196 @@ +// $Id: Geant4Setup.cpp 578 2013-05-17 22:33:09Z markus.frank $ +//==================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +// Framework include files +#include "DD4hep/LCDD.h" +#include "DD4hep/Printout.h" +#include "DDAlign/AlignmentOperators.h" +#include "DDAlign/DetectorAlignment.h" + +// C/C++ include files +#include <stdexcept> + +using namespace std; +using namespace DD4hep; +using namespace DD4hep::Geometry; + +void AlignmentOperator::insert(Alignment alignment) const { + if ( !cache.insert(alignment) ) { + // Error + } +} + +void AlignmentSelector::operator()(Entries::value_type e) const { + TGeoPhysicalNode* pn = 0; + nodes.insert(make_pair(e->path,make_pair(pn,e))); +} + +void AlignmentSelector::operator()(const Cache::value_type& e) const { + TGeoPhysicalNode* pn = e.second; + for(Entries::const_iterator j=entries.begin(); j != entries.end(); ++j) { + Entries::value_type e = (*j); + if ( e->needsReset() || e->hasMatrix() ) { + const char* p = pn->GetName(); + bool reset_children = e->resetChildren(); + if ( reset_children && ::strstr(p,e->path.c_str()) == p ) { + nodes.insert(make_pair(p,make_pair(pn,e))); + break; + } + else if ( e->path == p ) { + nodes.insert(make_pair(p,make_pair(pn,e))); + break; + } + } + } +} + +template <> void AlignmentActor<DDAlign_standard_operations::node_print>::init() { + printout(ALWAYS,"AlignmentCache","++++++++++++++++++++++++ Summary ++++++++++++++++++++++++"); +} + +template <> void AlignmentActor<DDAlign_standard_operations::node_print>::operator()(Nodes::value_type& n) const { + TGeoPhysicalNode* p = n.second.first; + Entry* e = n.second.second; + printout(ALWAYS,"AlignmentCache","Need to reset entry:%s - %s [needsReset:%s, hasMatrix:%s]", + p->GetName(),e->path.c_str(),yes_no(e->needsReset()),yes_no(e->hasMatrix())); +} + +template <> void AlignmentActor<DDAlign_standard_operations::node_delete>::operator()(Nodes::value_type& n) const { + delete n.second.second; + n.second.second = 0; +} + +template <> void AlignmentActor<DDAlign_standard_operations::node_reset>::operator()(Nodes::value_type& n) const { + TGeoPhysicalNode* p = n.second.first; + //Entry* e = n.second.second; + string np; + if ( p->IsAligned() ) { + for (Int_t i=0, nLvl=p->GetLevel(); i<=nLvl; i++) { + TGeoNode* node = p->GetNode(i); + TGeoMatrix* mm = node->GetMatrix(); // Node's relative matrix + np += string("/")+node->GetName(); + if ( !mm->IsIdentity() && i > 0 ) { // Ignore the 'world', is identity anyhow + Alignment a = cache.get(np); + if ( a.isValid() ) { + printout(ALWAYS,"AlignmentActor<reset>","Correct path:%s leaf:%s",p->GetName(),np.c_str()); + TGeoHMatrix* glob = p->GetMatrix(i-1); + if ( a.isValid() && i!=nLvl ) { + *mm = *(a->GetOriginalMatrix()); + } + else if ( i==nLvl ) { + TGeoHMatrix* hm = dynamic_cast<TGeoHMatrix*>(mm); + TGeoMatrix* org = p->GetOriginalMatrix(); + hm->SetTranslation(org->GetTranslation()); + hm->SetRotation(org->GetRotationMatrix()); + } + *glob *= *mm; + } + } + } + } +} + +template <> void AlignmentActor<DDAlign_standard_operations::node_align>::operator()(Nodes::value_type& n) const { + Entry& e = *n.second.second; + bool check = e.checkOverflow(); + bool overlap = e.checkOverlap(); + bool has_matrix = e.hasMatrix(); + DetElement det = e.detector; + bool valid = det.alignment().isValid(); + string det_placement = det.placementPath(); + + if ( !valid && !has_matrix ) { + cout << "++++ SKIP ALIGNMENT: ++++ " << e.path + << " DE:" << det_placement + << " Valid:" << yes_no(valid) + << " Matrix:" << yes_no(has_matrix) << endl; + /* */ + return; + } + + cout << "++++ " << e.path + << " DE:" << det_placement + << " Valid:" << yes_no(valid) + << " Matrix:" << yes_no(has_matrix) + << endl; + /* */ + // Need to care about optional arguments 'check_overlaps' and 'overlap' + DetectorAlignment ad(det); + Alignment alignment; + bool is_not_volume = e.path == det_placement; + if ( check && overlap ) { + alignment = is_not_volume + ? ad.align(e.transform, e.overflowValue(), e.overlap) + : ad.align(e.path, e.transform, e.overflowValue(), e.overlap); + } + else if ( check ) { + alignment = is_not_volume + ? ad.align(e.transform, e.overflowValue()) + : ad.align(e.path, e.transform, e.overflowValue()); + } + else { + alignment = is_not_volume ? ad.align(e.transform) : ad.align(e.path, e.transform); + } + if ( alignment.isValid() ) { + insert(alignment); + return; + } + throw runtime_error("Failed to apply alignment for "+e.path); +} + +#if 0 + void alignment_reset_dbg(const string& path, const Alignment& a) { + TGeoPhysicalNode* n = a.ptr(); + cout << " +++++++++++++++++++++++++++++++ " << path << endl; + cout << " +++++ Misaligned physical node: " << endl; + n->Print(); + string np; + if ( n->IsAligned() ) { + for (Int_t i=0; i<=n->GetLevel(); i++) { + TGeoMatrix* mm = n->GetNode(i)->GetMatrix(); + np += "/"; + np += n->GetNode(i)->GetName(); + if ( mm->IsIdentity() ) continue; + if ( i == 0 ) continue; + + TGeoHMatrix* glob = n->GetMatrix(i-1); + NodeMap::const_iterator j=original_matrices.find(np); + if ( j != original_matrices.end() && i!=n->GetLevel() ) { + cout << " +++++ Patch Level: " << i << np << endl; + *mm = *((*j).second); + } + else { + if ( i==n->GetLevel() ) { + cout << " +++++ Level: " << i << np << " --- Original matrix: " << endl; + n->GetOriginalMatrix()->Print(); + cout << " +++++ Level: " << i << np << " --- Local matrix: " << endl; + mm->Print(); + TGeoHMatrix* hm = dynamic_cast<TGeoHMatrix*>(mm); + hm->SetTranslation(n->GetOriginalMatrix()->GetTranslation()); + hm->SetRotation(n->GetOriginalMatrix()->GetRotationMatrix()); + cout << " +++++ Level: " << i << np << " --- New local matrix" << endl; + mm->Print(); + } + else { + cout << " +++++ Level: " << i << np << " --- Keep matrix " << endl; + mm->Print(); + } + } + cout << " +++++ Level: " << i << np << " --- Global matrix: " << endl; + glob->Print(); + *glob *= *mm; + cout << " +++++ Level: " << i << np << " --- New global matrix: " << endl; + glob->Print(); + } + } + cout << "\n\n\n +++++ physical node (full): " << np << endl; + n->Print(); + cout << " +++++ physical node (global): " << np << endl; + n->GetMatrix()->Print(); + } +#endif diff --git a/DDAlign/src/AlignmentParser.cpp b/DDAlign/src/AlignmentParser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9de3872c48c7872dba5dfada4c5481afc6dd4723 --- /dev/null +++ b/DDAlign/src/AlignmentParser.cpp @@ -0,0 +1,372 @@ +// $Id: Geant4Setup.cpp 578 2013-05-17 22:33:09Z markus.frank $ +//==================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== + +// Framework include files +#include "DD4hep/LCDD.h" +#include "DD4hep/Printout.h" +#include "XML/Conversions.h" +#include "XML/XMLElements.h" +#include "XML/DocumentHandler.h" +#include "DD4hep/DetectorTools.h" +#include "DD4hep/DetFactoryHelper.h" + +#include "DDAlign/AlignmentTags.h" +#include "DDAlign/AlignmentStack.h" +#include "DDAlign/AlignmentCache.h" + +// C/C++ include files +#include <stdexcept> + +namespace DD4hep { + + namespace { + + /** @class AlignmentTransaction + * + * Manage alignment transaction to the cache for a given LCDD instance + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ + struct AlignmentTransaction { + /// Internal flag to remember transaction contexts + bool flag; + /// Reference to the current LCDD instance + lcdd_t& lcdd; + /// Reference to the alignment cache + Geometry::AlignmentCache* m_cache; + + /// Default constructor + AlignmentTransaction(lcdd_t& l, xml_h e) : lcdd(l) { + flag = e.hasChild(_U(close_transaction)); + /// First check if a transaction is to be opened + m_cache = lcdd.extension<Geometry::AlignmentCache>(); + m_cache->addRef(); + if ( e.hasChild(_U(open_transaction)) ) { + m_cache->openTransaction(); + } + } + /// Default destructor + ~AlignmentTransaction() { + /// Last check if a transaction is to be closed + if ( flag ) { + lcdd.extension<Geometry::AlignmentCache>()->closeTransaction(); + } + m_cache->release(); + } + }; + /// Some utility class to specialize the convetrers: + class alignment; + class detelement; + class include_file; + class volume; + class rotation; + class position; + class pivot; + class transform3d; + } + + /// Forward declarations for all specialized converters + template <> void Converter<pivot>::operator()(xml_h seq) const; + template <> void Converter<position>::operator()(xml_h seq) const; + template <> void Converter<rotation>::operator()(xml_h seq) const; + template <> void Converter<transform3d>::operator()(xml_h seq) const; + + template <> void Converter<volume>::operator()(xml_h seq) const; + template <> void Converter<alignment>::operator()(xml_h seq) const; + template <> void Converter<detelement>::operator()(xml_h seq) const; + template <> void Converter<include_file>::operator()(xml_h seq) const; +} + +using namespace std; +using namespace DD4hep; +using namespace DD4hep::Geometry; + +/** Convert rotation objects + * + * <rotation x="0.5" y="0" z="0"/> + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ +template <> void Converter<rotation>::operator()(xml_h e) const { + xml_comp_t r(e); + RotationZYX* v = (RotationZYX*)param; + v->SetComponents(r.z(), r.y(), r.x()); + printout(INFO,"Alignment<rotation>", + " Rotation: x=%9.3f y=%9.3f z=%9.3f phi=%7.4f psi=%7.4f theta=%7.4f", + r.x(), r.y(), r.z(), v->Phi(), v->Psi(), v->Theta()); +} + +/** Convert position objects + * + * <position x="0.5" y="0" z="0"/> + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ +template <> void Converter<position>::operator()(xml_h e) const { + xml_comp_t p(e); + Position* v = (Position*)param; + v->SetXYZ(p.x(), p.y(), p.z()); + printout(INFO,"Alignment<position>"," Position: x=%9.3f y=%9.3f z=%9.3f", + v->X(), v->Y(), v->Z()); +} + +/** Convert pivot objects + * + * <pivot x="0.5" y="0" z="0"/> + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ +template <> void Converter<pivot>::operator()(xml_h e) const { + xml_comp_t p(e); + double x,y,z; + Translation3D* v = (Translation3D*)param; + v->SetXYZ(x=p.x(), y=p.y(), z=p.z()); + printout(INFO,"Alignment<pivot>"," Pivot: x=%9.3f y=%9.3f z=%9.3f",x,y,z); +} + +/** Convert transform3d objects + * + * A generic alignment transformation is defined by + * - a translation in 3D space identified in XML as a + * <position/> element + * - a rotation in 3D space around a pivot point specified in XML by + * 2 elements: the <rotation/> and the <pivot/> element. + * The specification of any of the elements is optional: + * - The absence of a translation implies the origine (0,0,0) + * - The absence of a pivot point implies the origine (0,0,0) + * - The absence of a rotation implies the identity rotation. + * Any supplied pivot point in this case is ignored. + * + * <xx> + * <position x="0" y="0" z="0.0001*mm"/> + * <rotation x="0" y="0" z="0"/> + * <pivot x="0" y="0" z="100"/> + * </xx> + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ +template <> void Converter<transform3d>::operator()(xml_h e) const { + typedef pair<bool,Transform3D> Data; + Position pos; + RotationZYX rot; + Translation3D piv; + xml_h child_rot, child_pos, child_piv; + Data* tr = (Data*)param; + + if ( (child_pos=e.child(_U(position),false)) ) + Converter<position>(lcdd,&pos)(child_pos); + if ( (child_rot=e.child(_U(rotation),false)) ) { + Converter<rotation>(lcdd,&rot)(child_rot); + if ( (child_piv=e.child(_U(pivot),false)) ) + Converter<pivot>(lcdd,&piv)(child_piv); + } + tr->first = true; + if ( child_rot && child_pos && child_piv ) + tr->second = Transform3D(Translation3D(pos)*piv*rot*(piv.Inverse())); + else if ( child_rot && child_pos ) + tr->second = Transform3D(rot,pos); + else if ( child_rot && child_piv ) + tr->second = Transform3D(piv*rot*(piv.Inverse())); + else if ( child_rot ) + tr->second = Transform3D(rot); + else if ( child_pos ) + tr->second = Transform3D(pos); + else { + tr->first = false; + } +} + +typedef AlignmentStack::StackEntry StackEntry; + +/** Convert volume objects + * + * <volume subpath="layer4_0"> + * <position x="0" y="0" z="0"/> + * <rotation x="0.5" y="0.1" z="0.2"/> + * <pivot x="0" y="0" z="100"/> + * <volume> + * ... + * <volume> + * </volume> + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ +template <> void Converter<volume>::operator()(xml_h e) const { + pair<bool,Transform3D> trafo; + pair<DetElement,string>* elt = (pair<DetElement,string>*)param; + string subpath = e.attr<string>(_U(path)); + bool reset = e.hasAttr(_U(reset)) ? e.attr<bool>(_U(reset)) : true; + bool reset_dau = e.hasAttr(_U(reset_children)) ? e.attr<bool>(_U(reset_children)) : true; + bool check = e.hasAttr(_U(check_overlaps)); + bool check_val = check ? e.attr<bool>(_U(check_overlaps)) : false; + bool overlap = e.hasAttr(_U(overlap)); + double ovl = overlap ? e.attr<double>(_U(overlap)) : 0.001; + string eltPlacement = elt->first.placementPath(); + string placementPath = subpath[0]=='/' ? subpath : eltPlacement + "/" + subpath; + + printout(INFO,"Alignment<volume>"," path:%s placement:%s reset:%s children:%s", + subpath.c_str(), placementPath.c_str(), yes_no(reset), yes_no(reset_dau)); + + Converter<transform3d>(lcdd,&trafo)(e); + int flags = 0; + if ( overlap ) flags |= AlignmentStack::OVERLAP_DEFINED; + if ( trafo.first ) flags |= AlignmentStack::MATRIX_DEFINED; + if ( reset ) flags |= AlignmentStack::RESET_VALUE; + if ( reset_dau ) flags |= AlignmentStack::RESET_CHILDREN; + if ( check ) flags |= AlignmentStack::CHECKOVL_DEFINED; + if ( check_val ) flags |= AlignmentStack::CHECKOVL_VALUE; + + StackEntry* entry = new StackEntry(elt->first,placementPath,trafo.second,ovl,flags); + AlignmentStack::insert(entry); + pair<DetElement,string> vol_param(elt->first,subpath); + xml_coll_t(e,_U(volume)).for_each(Converter<volume>(lcdd,&vol_param)); +} + +/** Convert detelement objects + * + * Function entry expects as a parameter a valid DetElement handle + * pointing to the subdetector, which detector elements should be + * realigned. + * + * <detelement path="/world/TPC/TPC_SideA/TPC_SideA_sector02"> + * <position x="0" y="0" z="0"/> + * <rotation x="0.5" y="0" z="0"/> + * <pivot x="0" y="0" z="100"/> + * </detelement> + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ +template <> void Converter<detelement>::operator()(xml_h e) const { + DetElement det = param ? *(DetElement*)param : DetElement(); + string path = e.attr<string>(_U(path)); + bool check = e.hasAttr(_U(check_overlaps)); + bool check_val = check ? e.attr<bool>(_U(check_overlaps)) : false; + bool reset = e.hasAttr(_U(reset)) ? e.attr<bool>(_U(reset)) : true; + bool reset_dau = e.hasAttr(_U(reset_children)) ? e.attr<bool>(_U(reset_children)) : true; + bool overlap = e.hasAttr(_U(overlap)); + double ovl = overlap ? e.attr<double>(_U(overlap)) : 0.001; + DetElement elt = Geometry::DetectorTools::findElement(det,path); + string placementPath = elt.isValid() ? elt.placementPath() : string("-----"); + + printout(INFO,"Alignment<detelement>","path:%s [%s] placement:%s reset:%s children:%s", + path.c_str(), + elt.isValid() ? elt.path().c_str() : "-----", + placementPath.c_str(), + yes_no(reset), yes_no(reset_dau)); + + if ( !elt.isValid() ) { + string err = "DD4hep: DetElement "+det.path()+" has no child:"+path+" [No such child]"; + throw runtime_error(err); + } + + pair<bool,Transform3D> trafo; + Converter<transform3d>(lcdd,&trafo)(e); + int flags = 0; + if ( overlap ) flags |= AlignmentStack::OVERLAP_DEFINED; + if ( check ) flags |= AlignmentStack::CHECKOVL_DEFINED; + if ( trafo.first ) flags |= AlignmentStack::MATRIX_DEFINED; + if ( reset ) flags |= AlignmentStack::RESET_VALUE; + if ( reset_dau ) flags |= AlignmentStack::RESET_CHILDREN; + if ( check_val ) flags |= AlignmentStack::CHECKOVL_VALUE; + + StackEntry* entry = new StackEntry(elt,placementPath,trafo.second,ovl,flags); + AlignmentStack::insert(entry); + + pair<DetElement,string> vol_param(elt,""); + xml_coll_t(e,_U(volume)).for_each(Converter<volume>(lcdd,&vol_param)); + xml_coll_t(e,_U(detelement)).for_each(Converter<detelement>(lcdd,&elt)); + xml_coll_t(e,_U(include)).for_each(Converter<include_file>(lcdd,&elt)); +} + +/** Convert detelement_include objects + * + * <detelement path="/world/TPC/TPC_SideA/TPC_SideA_sector02"> + * <position x="0" y="0" z="0"/> + * <rotation x="0.5" y="0" z="0"/> + * </detelement> + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ +template <> void Converter<include_file>::operator()(xml_h element) const { + xml_h node = XML::DocumentHandler().load(element, element.attr_value(_U(ref))).root(); + string tag = node.tag(); + if ( tag == "alignment" ) + Converter<alignment>(lcdd,param)(node); + else if ( tag == "detelement" ) + Converter<detelement>(lcdd,param)(node); + else if ( tag == "subdetectors" || tag == "detelements" ) + xml_coll_t(node,_U(detelements)).for_each(Converter<detelement>(lcdd,param)); + else + throw runtime_error("Undefined tag name in XML structure:"+tag+" XML parsing abandoned."); +} + +/** Convert alignment objects + * + * <alignment> + * <detelement path="TPC"> + * ... + * </detelement> + * </alignment> + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ +template <> void Converter<alignment>::operator()(xml_h e) const { + AlignmentTransaction tr(lcdd, e); + DetElement top = param ? *(DetElement*)param : lcdd.world(); + + /// Now we process all allowed elements within the alignment tag: + /// <detelement/>, <detelements/>, <subdetectors/> and <include/> + xml_coll_t(e,_U(detelement)).for_each(Converter<detelement>(lcdd,&top)); + xml_coll_t(e,_U(detelements)).for_each(_U(detelement),Converter<detelement>(lcdd,&top)); + xml_coll_t(e,_U(subdetectors)).for_each(_U(detelement),Converter<detelement>(lcdd,&top)); + xml_coll_t(e,_U(include)).for_each(Converter<include_file>(lcdd,&top)); +} + +/** Basic entry point to read alignment files + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ +static long setup_Alignment(lcdd_t& lcdd, const xml_h& e) { + AlignmentCache::install(lcdd); + (DD4hep::Converter<DD4hep::alignment>(lcdd))(e); + return 1; +} +DECLARE_XML_DOC_READER(alignment,setup_Alignment) + +/** Basic entry point to install the alignment cache in a LCDD instance + * + * @author M.Frank + * @version 1.0 + * @date 01/04/2014 + */ +static long install_Alignment(lcdd_t& lcdd, int, char**) { + AlignmentCache::install(lcdd); + return 1; +} +DECLARE_APPLY(DD4hepAlignmentInstall,install_Alignment) diff --git a/DDAlign/src/AlignmentParser.cpp.save b/DDAlign/src/AlignmentParser.cpp.save new file mode 100644 index 0000000000000000000000000000000000000000..df7d53a8442443bcbabf59d11ebe5faffdb87eec --- /dev/null +++ b/DDAlign/src/AlignmentParser.cpp.save @@ -0,0 +1,486 @@ +// $Id: Geant4Setup.cpp 578 2013-05-17 22:33:09Z markus.frank $ +//==================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +// Framework include files +#include "DD4hep/LCDD.h" +#include "DD4hep/Printout.h" +#include "XML/Conversions.h" +#include "XML/XMLElements.h" +#include "XML/DocumentHandler.h" +#include "DD4hep/MatrixHelpers.h" +#include "DD4hep/DetectorTools.h" +#include "DD4hep/DetFactoryHelper.h" +#include "DD4hep/DetectorAlignment.h" + +#include "DDAlign/AlignmentTags.h" +#include "TGeoManager.h" +#include "Math/Rotation3D.h" +#include "Math/Translation3D.h" + +using namespace std; +using namespace DD4hep; +typedef DD4hep::Geometry::DetectorAlignment DetectorAlignment; +typedef DD4hep::Geometry::DetectorTools DetectorTools; +typedef DD4hep::Geometry::RotationZYX RotationZYX; +typedef DD4hep::Geometry::Rotation3D Rotation3D; +typedef DD4hep::Geometry::Position Position; +typedef DD4hep::Geometry::Transform3D Transform3D; +typedef ROOT::Math::Translation3D Translation3D; +typedef DD4hep::Geometry::DetElement DetElement; +typedef DD4hep::Geometry::Alignment Alignment; + +typedef DD4hep::Geometry::LCDD lcdd_t; + +namespace DD4hep { + + + /** @class AlignmentCache AlignmentCache.h AlignmentCache.h + * + */ + class AlignmentCache { + protected: + typedef std::map<unsigned int, TGeoPhysicalNode*> Cache; + /// The subdetector specific map of alignments caches + Cache m_cache; + std::string m_sdPath; + size_t m_sdPathLen; + public: + /// Default constructor + AlignmentCache(const std::string& sdPath); + /// Default destructor + virtual ~AlignmentCache(); + /// Add a new entry to the cache. The key is the placement path + bool insert(Alignment alignment); + /// Retrieve an alignment entry by its lacement path + Alignment get(const std::string& path) const; + }; + + /// one-at-time hash function + static unsigned int hash32(const char* key) { + unsigned int hash = 0; + const char* k = key; + for (; *k; k++) { + hash += *k; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); hash += (hash << 15); + return hash; + } + + /// Default constructor + AlignmentCache::AlignmentCache(const std::string& sdPath) + : m_sdPath(sdPath), m_sdPathLen(sdPath.length()) + { + } + + /// Default destructor + AlignmentCache::~AlignmentCache() { + m_cache.clear(); + } + + /// Add a new entry to the cache. The key is the placement path + bool AlignmentCache::insert(Alignment alignment) { + TGeoPhysicalNode* pn = alignment.ptr(); + unsigned int index = hash32(pn->GetName()+m_sdPathLen); + Cache::const_iterator i = m_cache.find(index); + if ( i == m_cache.end() ) { + m_cache[index] = pn; + return true; + } + return false; + } + + /// Retrieve an alignment entry by its lacement path + Alignment AlignmentCache::get(const std::string& path) const { + unsigned int index = hash32(path.c_str()+m_sdPathLen); + Cache::const_iterator i = m_cache.find(index); + return Alignment(i == m_cache.end() ? 0 : (*i).second); + } + + AlignmentCache* subdetectorAlignments(const std::string& name) { + typedef map<string,AlignmentCache*> SubdetectorAlignments; + SubdetectorAlignments c; + SubdetectorAlignments::const_iterator i = c.find(name); + if ( i == c.end() ) { + AlignmentCache* ptr = new AlignmentCache(name); + c.insert(make_pair(name,ptr)); + return ptr; + } + return (*i).second; + } + + namespace { + class alignment; + class subdetector; + class detelement; + class include_file; + class volume; + class rotation; + class position; + class pivot; + class transform3d; + } + template <> void Converter<alignment>::operator()(xml_h seq) const; + + namespace { + using namespace Geometry; +#if 0 + void alignment_reset_dbg(const string& path, const Alignment& a) { + TGeoPhysicalNode* n = a.ptr(); + cout << " +++++++++++++++++++++++++++++++ " << path << endl; + cout << " +++++ Misaligned physical node: " << endl; + n->Print(); + string np; + if ( n->IsAligned() ) { + for (Int_t i=0; i<=n->GetLevel(); i++) { + TGeoMatrix* mm = n->GetNode(i)->GetMatrix(); + np += "/"; + np += n->GetNode(i)->GetName(); + if ( mm->IsIdentity() ) continue; + if ( i == 0 ) continue; + + TGeoHMatrix* glob = n->GetMatrix(i-1); + NodeMap::const_iterator j=original_matrices.find(np); + if ( j != original_matrices.end() && i!=n->GetLevel() ) { + cout << " +++++ Patch Level: " << i << np << endl; + *mm = *((*j).second); + } + else { + if ( i==n->GetLevel() ) { + cout << " +++++ Level: " << i << np << " --- Original matrix: " << endl; + n->GetOriginalMatrix()->Print(); + cout << " +++++ Level: " << i << np << " --- Local matrix: " << endl; + mm->Print(); + TGeoHMatrix* hm = dynamic_cast<TGeoHMatrix*>(mm); + hm->SetTranslation(n->GetOriginalMatrix()->GetTranslation()); + hm->SetRotation(n->GetOriginalMatrix()->GetRotationMatrix()); + cout << " +++++ Level: " << i << np << " --- New local matrix" << endl; + mm->Print(); + } + else { + cout << " +++++ Level: " << i << np << " --- Keep matrix " << endl; + mm->Print(); + } + } + cout << " +++++ Level: " << i << np << " --- Global matrix: " << endl; + glob->Print(); + *glob *= *mm; + cout << " +++++ Level: " << i << np << " --- New global matrix: " << endl; + glob->Print(); + } + } + cout << "\n\n\n +++++ physical node (full): " << np << endl; + n->Print(); + cout << " +++++ physical node (global): " << np << endl; + n->GetMatrix()->Print(); + } +#endif + + void alignment_reset(AlignmentCache* cache, const string& path, const Alignment& a) { + TGeoPhysicalNode* pn = a.ptr(); + string np; + if ( pn->IsAligned() ) { + for (Int_t i=0, nLvl=pn->GetLevel(); i<=nLvl; i++) { + TGeoNode* node = pn->GetNode(i); + TGeoMatrix* mm = node->GetMatrix(); // Node's relative matrix + np += string("/")+node->GetName(); + if ( !mm->IsIdentity() && i > 0 ) { // Ignore the 'world', is identity anyhow + TGeoHMatrix* glob = pn->GetMatrix(i-1); + Alignment a = cache->get(np); + if ( a.isValid() && i!=nLvl ) { + *mm = *(a->GetOriginalMatrix()); + } + else if ( i==nLvl ) { + TGeoHMatrix* hm = dynamic_cast<TGeoHMatrix*>(mm); + TGeoMatrix* org = pn->GetOriginalMatrix(); + hm->SetTranslation(org->GetTranslation()); + hm->SetRotation(org->GetRotationMatrix()); + } + *glob *= *mm; + } + } + } + } + } + + + + /** Convert rotation objects + * + * <rotation x="0.5" y="0" z="0"/> + */ + template <> void Converter<rotation>::operator()(xml_h e) const { + xml_comp_t r(e); + RotationZYX* v = (RotationZYX*)param; + v->SetComponents(r.z(), r.y(), r.x()); + printout(INFO,"Alignment<rotation>", + " Rotation: x=%9.3f y=%9.3f z=%9.3f phi=%7.4f psi=%7.4f theta=%7.4f", + r.x(), r.y(), r.z(), v->Phi(), v->Psi(), v->Theta()); + } + + /** Convert position objects + * + * <position x="0.5" y="0" z="0"/> + */ + template <> void Converter<position>::operator()(xml_h e) const { + xml_comp_t p(e); + Position* v = (Position*)param; + v->SetXYZ(p.x(), p.y(), p.z()); + printout(INFO,"Alignment<position>"," Position: x=%9.3f y=%9.3f z=%9.3f", + v->X(), v->Y(), v->Z()); + } + + /** Convert pivot objects + * + * <pivot x="0.5" y="0" z="0"/> + */ + template <> void Converter<pivot>::operator()(xml_h e) const { + xml_comp_t p(e); + double x,y,z; + Translation3D* v = (Translation3D*)param; + v->SetXYZ(x=p.x(), y=p.y(), z=p.z()); + printout(INFO,"Alignment<pivot>"," Pivot: x=%9.3f y=%9.3f z=%9.3f",x,y,z); + } + + /** Convert transform3d objects + * + * A generic alignment transformation is defined by + * - a translation in 3D space identified in XML as a + * <position/> element + * - a rotation in 3D space around a pivot point specified in XML by + * 2 elements: the <rotation/> and the <pivot/> element. + * The specification of any of the elements is optional: + * - The absence of a translation implies the origine (0,0,0) + * - The absence of a pivot point implies the origine (0,0,0) + * - The absence of a rotation implies the identity rotation. + * Any supplied pivot point in this case is ignored. + * + * <xx> + * <position x="0" y="0" z="0.0001*mm"/> + * <rotation x="0" y="0" z="0"/> + * <pivot x="0" y="0" z="100"/> + * </xx> + * + */ + template <> void Converter<transform3d>::operator()(xml_h e) const { + typedef pair<bool,Transform3D> Data; + Position pos; + RotationZYX rot; + Translation3D piv; + xml_h child_rot, child_pos, child_piv; + Data* tr = (Data*)param; + + if ( (child_pos=e.child(_U(position),false)) ) + Converter<position>(lcdd,&pos)(child_pos); + if ( (child_rot=e.child(_U(rotation),false)) ) { + Converter<rotation>(lcdd,&rot)(child_rot); + if ( (child_piv=e.child(_U(pivot),false)) ) + Converter<pivot>(lcdd,&piv)(child_piv); + } + tr->first = true; + if ( child_rot && child_pos && child_piv ) + tr->second = Transform3D(Translation3D(pos)*piv*rot*(piv.Inverse())); + else if ( child_rot && child_pos ) + tr->second = Transform3D(rot,pos); + else if ( child_rot && child_piv ) + tr->second = Transform3D(piv*rot*(piv.Inverse())); + else if ( child_rot ) + tr->second = Transform3D(rot); + else if ( child_pos ) + tr->second = Transform3D(pos); + else { + tr->first = false; + } + } + + /** Convert volume objects + * + * <volume subpath="layer4_0"> + * <position x="0" y="0" z="0"/> + * <rotation x="0.5" y="0.1" z="0.2"/> + * <pivot x="0" y="0" z="100"/> + * <volume> + * ... + * </volume> + * </volume> + */ + template <> void Converter<volume>::operator()(xml_h e) const { + pair<bool,Transform3D> trafo; + pair<DetElement,string>* elt = (pair<DetElement,string>*)param; + string subpath = e.attr<string>(_U(path)); + bool reset = e.hasAttr(_U(reset)) ? e.attr<bool>(_U(reset)) : false; + + printout(INFO,"Alignment<volume>"," path:%s / %s reset:%s", + elt->first.path().c_str(),subpath.c_str(), reset ? "YES" : "NO"); + Converter<transform3d>(lcdd,&trafo)(e); + + pair<DetElement,string> vol_param(elt->first,subpath); + xml_coll_t(e,_U(volume)).for_each(Converter<volume>(lcdd,&vol_param)); + } + + /** Convert detelement objects + * + * Function entry expects as a parameter a valid DetElement handle + * pointing to the subdetector, which detector elements should be + * realigned. + * + * <detelement path="/world/TPC/TPC_SideA/TPC_SideA_sector02"> + * <position x="0" y="0" z="0"/> + * <rotation x="0.5" y="0" z="0"/> + * <pivot x="0" y="0" z="100"/> + * </detelement> + */ + template <> void Converter<detelement>::operator()(xml_h e) const { + xml_comp_t node(e); + string path = node.attr<string>(_U(path)); + bool check = node.hasAttr(_U(check_overlaps)); + bool overlap = node.hasAttr(_U(overlap)); + bool reset = node.hasAttr(_U(reset)) ? node.attr<bool>(_U(reset)) : false; + DetElement det(param ? (TNamed*)param : 0); + DetElement elt = DetectorTools::findElement(det,path); + + if ( !elt.isValid() ) { + string err = "DD4hep: DetElement "+det.path()+" has no child:"+path+" [No such child]"; + throw runtime_error(err); + } + + string placementPath = elt.isValid() ? elt.placementPath() : string("-----"); + printout(INFO,"Alignment<detelement>","path:%s [%s] placement:%s reset:%s", + path.c_str(), + elt.isValid() ? elt.path().c_str() : "-----", + placementPath.c_str(), + reset ? "YES" : "NO"); + + if ( reset && elt.alignment().isValid() ) { + alignment_reset(det, placementPath, elt.alignment()); + } + + int sc = 0; + pair<bool,Transform3D> trafo; + Converter<transform3d>(lcdd,&trafo)(e); + if ( trafo.first ) { + DetectorAlignment alignment(elt); + // Need to care about optional arguments 'check_overlaps' and 'overlap' + if ( check && overlap ) { + bool check_overlaps = node.attr<bool>(_U(check_overlaps)); + double overlap = node.attr<double>(_U(overlap)); + sc = alignment.align(trafo.second, check_overlaps, overlap); + } + else if ( check ) { + bool check_overlaps = node.attr<bool>(_U(check_overlaps)); + sc = alignment.align(trafo.second, check_overlaps); + } + else { + sc = alignment.align(trafo.second); + } + if ( !subDetectorAlignment(elt)->insert(alignment) ) { + // Error + } + } + pair<DetElement,string> vol_param(elt,""); + xml_coll_t(node,_U(volume)).for_each(Converter<volume>(lcdd,&vol_param)); + } + + /** Convert subdetector objects + * + * <subdetector name="TPC"> + * <detelement path="/world/TPC/TPC_SideA/TPC_SideA_sector02"> + * ... + * </detelement> + * ... + * <include ref="file-name"/> + * ... + * </subdetector> + */ + template <> void Converter<subdetector>::operator()(xml_h e) const { + xml_comp_t sd(e); + string path = sd.nameStr(); + bool reset = sd.hasAttr(_U(reset)) ? sd.attr<bool>(_U(reset)) : false; + printout(INFO,"Alignment<subdetector>","name:%s reset:%s", + path.c_str(), reset ? "YES" : "NO"); + DetElement elt = DetectorTools::findElement(lcdd.world(),path); + if ( !elt.isValid() ) { + throw runtime_error("DD4hep: The subdetector "+path+" is not present [Invalid Handle]"); + } + string placementPath = elt.placementPath(); + if ( reset && elt.alignment().isValid() ) { + alignment_reset(elt, placementPath, elt.alignment()); + } + xml_coll_t(sd,_U(detelement)).for_each(Converter<detelement>(lcdd,&elt)); + xml_coll_t(sd,_U(include)).for_each(Converter<include_file>(lcdd,&elt)); + } + + /** Convert detelement_inlclude objects + * + * <detelement path="/world/TPC/TPC_SideA/TPC_SideA_sector02"> + * <position x="0" y="0" z="0"/> + * <rotation x="0.5" y="0" z="0"/> + * </detelement> + */ + template <> void Converter<include_file>::operator()(xml_h element) const { + xml_h node = XML::DocumentHandler().load(element, element.attr_value(_U(ref))).root(); + string tag = node.tag(); + if ( tag == "alignment" ) { + Converter<alignment>(lcdd,param)(node); + } + else if ( tag == "subdetector" ) { + Converter<subdetector>(lcdd,param)(node); + } + else if ( tag == "detelement" ) { + Converter<detelement>(lcdd,0)(node); + } + else if ( tag == "subdetectors" ) { + xml_coll_t(node,_U(subdetector)).for_each(Converter<subdetector>(lcdd,param)); + } + else if ( tag == "detelements" ) { + xml_coll_t(node,_U(detelement)).for_each(Converter<detelement>(lcdd,0)); + } + } + + template <> void Converter<alignment>::operator()(xml_h seq) const { + xml_elt_t compact(seq); + xml_coll_t(compact,_U(subdetectors)).for_each(_U(subdetector),Converter<subdetector>(lcdd,param)); + xml_coll_t(compact,_U(include)).for_each(Converter<include_file>(lcdd,param)); + } +} + +static long setup_Alignment(lcdd_t& lcdd, const xml_h& e) { + TGeoManager* mgr = gGeoManager; + if ( 1 || e.hasAttr("print") ) { + TObjArray* pn = mgr->GetListOfPhysicalNodes(); + TIter next(pn); + int count = 0; + typedef TGeoPhysicalNode PN; + for (PN *p=(PN*)next(); p; p=(PN*)next(), ++count) { + //p->Print(); + } + printout(INFO,"Alignment","Found %d physical nodes registered to the geometyry manager",count); + } +#if 0 + if ( original_matrices.size() > 0 ) { + const NodeMap& nm = original_matrices; + for(NodeMap::const_iterator i=nm.begin(); i!=nm.end();++i) { + cout << "Nodemap: " << (*i).first << endl; + //(*i).second->Print(); + } + //mgr->UnlockGeometry(); + //mgr->ClearPhysicalNodes(kTRUE); + //mgr->RefreshPhysicalNodes(); + //return 1; + } +#endif + (Converter<alignment>(lcdd))(e); + printout(INFO,"Alignment","Refreshing physical nodes...."); + mgr->GetCurrentNavigator()->ResetAll(); + mgr->GetCurrentNavigator()->BuildCache(); + mgr->RefreshPhysicalNodes(); + return 1; +} +DECLARE_XML_DOC_READER(alignment,setup_Alignment) diff --git a/DDAlign/src/AlignmentStack.cpp b/DDAlign/src/AlignmentStack.cpp new file mode 100644 index 0000000000000000000000000000000000000000..db5f3656c6454ccc21a067da969dcb5947a75366 --- /dev/null +++ b/DDAlign/src/AlignmentStack.cpp @@ -0,0 +1,213 @@ +// $Id: Geant4Setup.cpp 578 2013-05-17 22:33:09Z markus.frank $ +//==================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +// Framework include files +#include "DD4hep/LCDD.h" +#include "DD4hep/Objects.h" +#include "DD4hep/Printout.h" +#include "DDAlign/AlignmentStack.h" + +using namespace std; +using namespace DD4hep; +using namespace DD4hep::Geometry; + +static AlignmentStack* s_alignStack = 0; + + +/// Fully initializing constructor +AlignmentStack::StackEntry::StackEntry(const DetElement& p, const std::string& placement, const Transform3D& t, double ov, int f) + : detector(p), transform(t), path(placement), overlap(ov), flag(f) +{ +} + +/// Constructor with partial initialization +AlignmentStack::StackEntry::StackEntry(DetElement element, bool rst, bool rst_children) + : detector(element), transform(), overlap(0.001), flag(0) +{ + if ( rst ) flag |= RESET_VALUE; + if ( rst_children ) flag |= RESET_CHILDREN; + if ( detector.isValid() ) path = detector.placementPath(); +} + +/// Constructor with partial initialization +AlignmentStack::StackEntry::StackEntry(DetElement element, const Transform3D& trafo, bool rst, bool rst_children) + : detector(element), transform(trafo), overlap(0.001), flag(0) +{ + flag |= MATRIX_DEFINED; + if ( rst ) flag |= RESET_VALUE; + if ( rst_children ) flag |= RESET_CHILDREN; + if ( detector.isValid() ) path = detector.placementPath(); +} + +/// Constructor with partial initialization +AlignmentStack::StackEntry::StackEntry(DetElement element, const Position& translation, bool rst, bool rst_children) +: detector(element), transform(translation), overlap(0.001), flag(0) +{ + flag |= MATRIX_DEFINED; + if ( rst ) flag |= RESET_VALUE; + if ( rst_children ) flag |= RESET_CHILDREN; + if ( detector.isValid() ) path = detector.placementPath(); +} + +/// Constructor with partial initialization +AlignmentStack::StackEntry::StackEntry(DetElement element, const RotationZYX& rot, bool rst, bool rst_children) +: detector(element), transform(rot), overlap(0.001), flag(0) +{ + flag |= MATRIX_DEFINED; + if ( rst ) flag |= RESET_VALUE; + if ( rst_children ) flag |= RESET_CHILDREN; + if ( detector.isValid() ) path = detector.placementPath(); +} + +/// Constructor with partial initialization +AlignmentStack::StackEntry::StackEntry(DetElement element, const Position& translation, const RotationZYX& rot, bool rst, bool rst_children) + : detector(element), transform(rot,translation), overlap(0.001), flag(0) +{ + flag |= MATRIX_DEFINED; + if ( rst ) flag |= RESET_VALUE; + if ( rst_children ) flag |= RESET_CHILDREN; + if ( detector.isValid() ) path = detector.placementPath(); +} + +/// Copy constructor +AlignmentStack::StackEntry::StackEntry(const StackEntry& e) +: detector(e.detector), transform(e.transform), path(e.path), overlap(e.overlap), flag(e.flag) +{ +} + +/// Attach transformation object +AlignmentStack::StackEntry& AlignmentStack::StackEntry::setTransformation(const Transform3D& trafo) { + flag |= MATRIX_DEFINED; + transform = trafo; + return *this; +} + +/// Instruct entry to ignore the transformation +AlignmentStack::StackEntry& AlignmentStack::StackEntry::clearTransformation() { + flag &= ~MATRIX_DEFINED; + transform = Transform3D(); + return *this; +} + +/// Set flag to reset the entry to it's ideal geometrical position +AlignmentStack::StackEntry& AlignmentStack::StackEntry::setReset(bool new_value) { + new_value ? (flag |= RESET_VALUE) : (flag &= ~RESET_VALUE); + return *this; +} + + +/// Set flag to reset the entry's children to their ideal geometrical position +AlignmentStack::StackEntry& AlignmentStack::StackEntry::setResetChildren(bool new_value) { + new_value ? (flag |= RESET_CHILDREN) : (flag &= ~RESET_CHILDREN); + return *this; +} + + +/// Set flag to check overlaps +AlignmentStack::StackEntry& AlignmentStack::StackEntry::setOverlapCheck(bool new_value) { + new_value ? (flag |= CHECKOVL_DEFINED) : (flag &= ~CHECKOVL_DEFINED); + return *this; +} + + +/// Set the precision for the overlap check (otherwise the default is 0.001 cm) +AlignmentStack::StackEntry& AlignmentStack::StackEntry::setOverlapPrecision(double precision) { + flag |= CHECKOVL_DEFINED; + flag |= CHECKOVL_VALUE; + overlap = precision; + return *this; +} + +/// Default constructor +AlignmentStack::AlignmentStack() +{ +} + +/// Default destructor +AlignmentStack::~AlignmentStack() { + destroyObjects(m_stack)(); +} + +/// Static client accessor +AlignmentStack& AlignmentStack::get() { + if ( s_alignStack ) return *s_alignStack; + throw runtime_error("AlignmentStack> Stack not allocated -- may not be retrieved!"); +} + +/// Create an alignment stack instance. The creation of a second instance will be refused. +void AlignmentStack::create() { + if ( s_alignStack ) { + throw runtime_error("AlignmentStack> Stack already allocated. Multiple copies are not allowed!"); + } + s_alignStack = new AlignmentStack(); +} + +/// Check existence of alignment stack +bool AlignmentStack::exists() { + return s_alignStack != 0; +} + +/// Clear data content and remove the slignment stack +void AlignmentStack::release() { + if ( s_alignStack ) { + delete s_alignStack; + s_alignStack = 0; + return; + } + throw runtime_error("AlignmentStack> Attempt to delete non existing stack."); +} + +/// Add a new entry to the cache. The key is the placement path +bool AlignmentStack::insert(const std::string& full_path, StackEntry* entry) { + if ( entry && !full_path.empty() ) { + entry->path = full_path; + return get().add(entry); + } + throw runtime_error("AlignmentStack> Attempt to apply an invalid alignment entry."); +} + +/// Add a new entry to the cache. The key is the placement path +bool AlignmentStack::insert(StackEntry* entry) { + return get().add(entry); +} + +/// Add a new entry to the cache. The key is the placement path +bool AlignmentStack::add(StackEntry* entry) { + if ( entry && !entry->path.empty() ) { + Stack::const_iterator i = m_stack.find(entry->path); + if ( i == m_stack.end() ) { + m_stack.insert(make_pair(entry->path,entry)); + return true; + } + throw runtime_error("AlignmentStack> The entry with path "+entry->path+ + " cannot be re-aligned twice in one transaction."); + } + throw runtime_error("AlignmentStack> Attempt to apply an invalid alignment entry."); +} + +/// Retrieve an alignment entry of the current stack +auto_ptr<AlignmentStack::StackEntry> AlignmentStack::pop() { + Stack::iterator i = m_stack.begin(); + if ( i != m_stack.end() ) { + StackEntry* e = (*i).second; + m_stack.erase(i); + return auto_ptr<StackEntry>(e); + } + throw runtime_error("AlignmentStack> Alignment stack is empty. " + "Cannot pop entries - check size first!"); +} + +/// Get all pathes to be aligned +vector<const AlignmentStack::StackEntry*> AlignmentStack::entries() const { + vector<const StackEntry*> result; + result.reserve(m_stack.size()); + for(Stack::const_iterator i=m_stack.begin(); i != m_stack.end(); ++i) + result.push_back((*i).second); + return result; +} + diff --git a/DDAlign/src/AlignmentTags.cpp b/DDAlign/src/AlignmentTags.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6278743ab71ea9256f88f61326101a2788b17630 --- /dev/null +++ b/DDAlign/src/AlignmentTags.cpp @@ -0,0 +1,15 @@ +// $Id: XMLTags.h 889 2013-11-14 15:55:39Z markus.frank@cern.ch $ +//==================================================================== +// AIDA Detector description implementation +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== + +// Framework include files +#include "XML/XMLTags.h" + +// Define unicode tags +#define UNICODE(x) DECLARE_UNICODE_TAG(x) +#include "DDAlign/AlignmentTags.h" diff --git a/DDAlign/src/AlignmentWriter.cpp b/DDAlign/src/AlignmentWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a87c54894afc6cade3631f1eb763e3527df3f1b1 --- /dev/null +++ b/DDAlign/src/AlignmentWriter.cpp @@ -0,0 +1,176 @@ +// $Id: LCDDConverter.h 889 2013-11-14 15:55:39Z markus.frank@cern.ch $ +//==================================================================== +// AIDA Detector description implementation +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== + +// Framework includes +#include "DDAlign/AlignmentWriter.h" + +#include "DD4hep/Printout.h" +#include "DD4hep/MatrixHelpers.h" +#include "DD4hep/DetectorTools.h" +#include "DD4hep/DetFactoryHelper.h" +#include "XML/DocumentHandler.h" +#include "DDAlign/AlignmentTags.h" +#include "DDAlign/AlignmentCache.h" +#include "DDAlign/DetectorAlignment.h" + +#include "TGeoMatrix.h" + +// C/C++ include files +#include <stdexcept> + +using namespace DD4hep::Geometry; +using namespace DD4hep; +using namespace std; + +/// Initializing Constructor +AlignmentWriter::AlignmentWriter(LCDD& lcdd) + : m_lcdd(lcdd) +{ + m_cache = lcdd.extension<Geometry::AlignmentCache>(); +} + +/// Standard destructor +AlignmentWriter::~AlignmentWriter() { + if ( m_cache ) m_cache->release(); +} + +/// Create the element corresponding to one single detector element without children +XML::Element AlignmentWriter::createElement(XML::Document doc, DetElement element) const { + XML::Element e(0), placement(0), elt = XML::Element(doc,_U(detelement)); + string path = element.placementPath(); + Alignment a = element.alignment(); + DetectorAlignment det(element); + const vector<Alignment>& va = det.volumeAlignments(); + + elt.setAttr(_U(path),element.path()); + if ( a.isValid() ) { + addNode(elt,a); + } + for(vector<Alignment>::const_iterator i=va.begin(); i!=va.end();++i) { + e = XML::Element(doc,_U(volume)); + e.setAttr(_U(path),(*i)->GetName()); + addNode(e,*i); + elt.append(e); + } + return elt; +} + +/// Add single alignment node to the XML document +void AlignmentWriter::addNode(XML::Element elt, Alignment a) const { + TGeoNode* n = a->GetNode(); + TGeoHMatrix mat(a->GetOriginalMatrix()->Inverse()); + mat.Multiply(n->GetMatrix()); + const Double_t* t = mat.GetTranslation(); + XML::Element placement = XML::Element(elt.document(),_U(comment)); + placement.setAttr(_U(placement),a->GetName()); + elt.append(placement); + + printout(INFO,"AlignmentWriter","Write Delta constants for %s",a->GetName()); + //mat.Print(); + if ( fabs(t[0]) > numeric_limits<double>::epsilon() || + fabs(t[1]) > numeric_limits<double>::epsilon() || + fabs(t[2]) > numeric_limits<double>::epsilon() ) { + XML::Element e = XML::Element(elt.document(),_U(position)); + e.setAttr(_U(x),t[0]); + e.setAttr(_U(y),t[1]); + e.setAttr(_U(z),t[2]); + elt.append(e); + } + if ( mat.IsRotation() ) { + XYZAngles rot = _XYZangles(&mat); + if ( fabs(rot.X()) > numeric_limits<double>::epsilon() || + fabs(rot.Y()) > numeric_limits<double>::epsilon() || + fabs(rot.Z()) > numeric_limits<double>::epsilon() ) { + + XML::Element e = XML::Element(elt.document(),_U(rotation)); + // Don't know why the angles have the wrong sign.... + rot *= -1; + e.setAttr(_U(x),rot.X()); + e.setAttr(_U(y),rot.Y()); + e.setAttr(_U(z),rot.Z()); + elt.append(e); + } + } +} + +/// Scan a DetElement subtree and add on the fly the XML entries +XML::Element AlignmentWriter::scan(XML::Document doc, DetElement element) const { + XML::Element elt(0); + if ( element.isValid() ) { + const DetElement::Children& c = element.children(); + Alignment alignment = element.alignment(); + if ( alignment.isValid() ) elt = createElement(doc,element); + for (DetElement::Children::const_iterator i = c.begin(); i != c.end(); ++i) { + XML::Element daughter = scan(doc, (*i).second); + if ( daughter ) { + (elt ? (elt) : (elt=createElement(doc,element))).append(daughter); + } + } + } + return elt; +} + +/// Dump the tree content into a XML document structure +XML::Document AlignmentWriter::dump(DetElement top, bool enable_transactions) const { + const char comment[] = "\n" + " +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" + " ++++ DD4hep generated alignment file using the ++++\n" + " ++++ DD4hep Detector description XML generator. ++++\n" + " ++++ ++++\n" + " ++++ Parser:" + XML_IMPLEMENTATION_TYPE + " ++++\n" + " ++++ ++++\n" + " ++++ M.Frank CERN/LHCb ++++\n" + " +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n "; + XML::DocumentHandler docH; + XML::Document doc = docH.create("alignment", comment); + XML::Element elt(0), elements(0), root = doc.root(); + root.append(elements = XML::Element(doc, _U(detelements))); + if ( enable_transactions ) root.append(XML::Element(doc,_U(open_transaction))); + if ( (elt=scan(doc,top)) ) elements.append(elt); + if ( enable_transactions ) root.append(XML::Element(doc,_U(close_transaction))); + return doc; +} + +/// Write the XML document structure to a file. +long AlignmentWriter::write(XML::Document doc, const string& output) const { + XML::DocumentHandler docH; + return docH.output(doc, output); +} + +static long create_alignment_file(LCDD& lcdd, int argc, char** argv) { + DetElement top; + string output, path = "/world"; + bool enable_transactions = false; + for(int i=1; i<argc;++i) { + if ( argv[i][0]=='-' || argv[i][0]=='/' ) { + const char* p = ::strchr(argv[i],'='); + if ( p && strncmp(argv[i]+1,"-output",7)==0 ) + output = p+1; + else if ( p && strncmp(argv[i]+1,"-path",5)==0 ) + path = p+1; + else if ( strncmp(argv[i]+1,"-transactions",5)==0 ) + enable_transactions = true; + else + throw runtime_error("AlignmentWriter: Invalid argument:"+string(argv[i])); + } + } + printout(ALWAYS,"AlignmentWriter", + "++++ Writing DD4hep alignment constants of the \"%s\" DetElement tree to file \"%s\"", + path.c_str(), output.c_str()); + top = DetectorTools::findElement(lcdd.world(),path); + if ( top.isValid() ) { + AlignmentWriter wr(lcdd); + return wr.write(wr.dump(top,enable_transactions), output); + } + throw runtime_error("AlignmentWriter: Invalid top level element name:"+path); +} + +DECLARE_APPLY(DDAlignmentWriter, create_alignment_file) diff --git a/DDAlign/src/DetectorAlignment.cpp b/DDAlign/src/DetectorAlignment.cpp new file mode 100644 index 0000000000000000000000000000000000000000..000a73072f0fe6f6c104ef47a09d7cf692d35e5a --- /dev/null +++ b/DDAlign/src/DetectorAlignment.cpp @@ -0,0 +1,180 @@ +// $Id: Readout.cpp 985 2014-01-30 13:50:10Z markus.frank@cern.ch $ +//==================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------- +// +// Author : M.Frank +// +//==================================================================== +#include "DDAlign/DetectorAlignment.h" +#include "DD4hep/DetectorTools.h" +#include "DD4hep/InstanceCount.h" +#include "DD4hep/MatrixHelpers.h" +#include "DD4hep/Printout.h" + +// ROOT include files +#include "TGeoMatrix.h" +#include "TGeoManager.h" + +using namespace std; +using namespace DD4hep; +using namespace DD4hep::Geometry; + +typedef DetectorTools::ElementPath ElementPath; +typedef DetectorTools::PlacementPath PlacementPath; +typedef vector<pair<int,DetElement> > LevelElements; +namespace { + + Alignment _align(const Alignment& a, TGeoHMatrix* transform, bool check, double overlap) { + TGeoPhysicalNode* n = a.ptr(); + if ( n ) { + TGeoMatrix* mm = n->GetNode()->GetMatrix(); + printout(INFO,"Alignment","DELTA matrix of %s",n->GetName()); + transform->Print(); + /* + printout(INFO,"Alignment","OLD matrix of %s",n->GetName()); + mm->Print(); + */ + transform->MultiplyLeft(mm); // orig * delta + n->Align(transform, 0, check, overlap); + /* + printout(INFO,"Alignment","Apply new relative matrix mother to daughter:"); + transform->Print(); + transform->MultiplyLeft(mm); // orig * delta + printout(INFO,"Alignment","With deltas %s ....",n->GetName()); + transform->Print(); + n->Align(transform, 0, check, overlap); + printout(INFO,"Alignment","NEW matrix of %s",n->GetName()); + n->GetNode()->GetMatrix()->Print(); + + Position local, global = a.toGlobal(local); + cout << "Local:" << local << " Global: " << global + << " and back:" << a.globalToLocal(global) << endl; + */ + return Alignment(n); + } + throw runtime_error("DD4hep: Cannot align non existing physical node. [Invalid Handle]"); + } + + Alignment& _alignment(const DetectorAlignment& det) { + DetElement::Object& e = det._data(); + if ( !e.alignment.isValid() ) { + string path = DetectorTools::placementPath(det); + //cout << "Align path:" << path << endl; + e.alignment = Alignment(path); + } + return e.alignment; + } + void _dumpParentElements(DetectorAlignment& det, LevelElements& elements) { + PlacementPath nodes; + ElementPath det_nodes; + DetectorTools::placementPath(det,nodes); + DetectorTools::elementPath(det,det_nodes); + /// cout << "Placement path:"; + int level = 0; + PlacementPath::const_reverse_iterator j=nodes.rbegin(); + ElementPath::const_reverse_iterator k=det_nodes.rbegin(); + for(; j!=nodes.rend(); ++j, ++level) { + //cout << "(" << level << ") " << (void*)((*j).ptr()) + // << " " << string((*j)->GetName()) << " "; + if ( ::strcmp((*j).ptr()->GetName(),(*k).placement().ptr()->GetName()) ) { + //cout << "[DE]"; + elements.push_back(make_pair(level,*k)); + ++k; + } + else { + //elements.push_back(make_pair(level,DetElement())); + } + //cout << " "; + } + //cout << endl; + } +} + +/// Initializing constructor +DetectorAlignment::DetectorAlignment(DetElement e) + : DetElement(e) +{ +} + + +/// Collect all placements from the detector element up to the world volume +void DetectorAlignment::collectNodes(vector<PlacedVolume>& nodes) { + DetectorTools::placementPath(*this,nodes); +} + +/// Access to the alignment block +Alignment DetectorAlignment::alignment() const { + return _alignment(*this); +} + +/// Alignment entries for lower level volumes, which are NOT attached to daughter DetElements +std::vector<Alignment>& DetectorAlignment::volumeAlignments() { + return _data().volume_alignments; +} + +/// Alignment entries for lower level volumes, which are NOT attached to daughter DetElements +const std::vector<Alignment>& DetectorAlignment::volumeAlignments() const { + return _data().volume_alignments; +} + +/// Align the PhysicalNode of the placement of the detector element (translation only) +Alignment DetectorAlignment::align(const Position& pos, bool check, double overlap) { + return align(_transform(pos),check,overlap); +} + +/// Align the PhysicalNode of the placement of the detector element (rotation only) +Alignment DetectorAlignment::align(const RotationZYX& rot, bool check, double overlap) { + return align(_transform(rot),check,overlap); +} + +/// Align the PhysicalNode of the placement of the detector element (translation + rotation) +Alignment DetectorAlignment::align(const Position& pos, const RotationZYX& rot, bool check, double overlap) { + return align(_transform(pos,rot),check,overlap); +} + +/// Align the physical node according to a generic Transform3D +Alignment DetectorAlignment::align(const Transform3D& transform, bool check, double overlap) { + return align(_transform(transform),check,overlap); +} + +/// Align the physical node according to a generic TGeo matrix +Alignment DetectorAlignment::align(TGeoHMatrix* matrix, bool check, double overlap) { + return _align(_alignment(*this),matrix,check,overlap); +} + +/// Align the PhysicalNode of the placement of the detector element (translation only) +Alignment DetectorAlignment::align(const string& path, const Position& pos, bool check, double overlap) { + return align(path,_transform(pos),check,overlap); +} + +/// Align the PhysicalNode of the placement of the detector element (rotation only) +Alignment DetectorAlignment::align(const string& path, const RotationZYX& rot, bool check, double overlap) { + return align(path,_transform(rot),check,overlap); +} + +/// Align the PhysicalNode of the placement of the detector element (translation + rotation) +Alignment DetectorAlignment::align(const string& path, const Position& pos, const RotationZYX& rot, bool check, double overlap) { + return align(path,_transform(pos,rot),check,overlap); +} + +/// Align the physical node according to a generic Transform3D +Alignment DetectorAlignment::align(const string& path, const Transform3D& transform, bool check, double overlap) { + return align(path,_transform(transform),check,overlap); +} + +/// Align the physical node according to a generic TGeo matrix +Alignment DetectorAlignment::align(const string& path, TGeoHMatrix* matrix, bool check, double overlap) { + if ( path.empty() ) + return _align(_alignment(*this),matrix,check,overlap); + else if ( path == placementPath() ) + return _align(_alignment(*this),matrix,check,overlap); + else if ( path[0] == '/' ) { + Alignment a(path); + volumeAlignments().push_back(a); + return _align(a,matrix,check,overlap); + } + Alignment a(placementPath()+'/'+path); + volumeAlignments().push_back(a); + return _align(a,matrix,check,overlap); +}