Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-03 07:54:37

0001 //==========================================================================
0002 //  AIDA Detector description implementation 
0003 //--------------------------------------------------------------------------
0004 // Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
0005 // All rights reserved.
0006 //
0007 // For the licensing terms see $DD4hepINSTALL/LICENSE.
0008 // For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
0009 //
0010 // Author     : M.Frank
0011 //
0012 //==========================================================================
0013 
0014 // Framework include files
0015 #include <DD4hep/Detector.h>
0016 #include <DD4hep/Printout.h>
0017 #include <DD4hep/InstanceCount.h>
0018 #include <DD4hep/MatrixHelpers.h>
0019 #include <DD4hep/detail/ObjectsInterna.h>
0020 
0021 // ROOT include files
0022 #include <TROOT.h>
0023 #include <TClass.h>
0024 #include <TColor.h>
0025 #include <TGeoShape.h>
0026 #include <TGeoVolume.h>
0027 #include <TGeoNode.h>
0028 #include <TGeoMatrix.h>
0029 #include <TGeoMedium.h>
0030 
0031 #include <TGeoVoxelFinder.h>
0032 #include <TGeoShapeAssembly.h>
0033 #include <TGeoScaledShape.h>
0034 #include <TMap.h>
0035 
0036 // C/C++ include files
0037 #include <climits>
0038 #include <iostream>
0039 #include <stdexcept>
0040 #include <sstream>
0041 #include <iomanip>
0042 
0043 using namespace dd4hep;
0044 
0045 /*
0046  *  The section below uses the new ROOT features using user extensions to volumes
0047  *  and placements. Once this is common, the above mechanism should be thrown away....
0048  *
0049  *  M.Frank
0050  */
0051 
0052 typedef TGeoNode                geo_node_t;
0053 typedef TGeoVolume              geo_volume_t;
0054 typedef TGeoVolumeAssembly      geo_assembly_t;
0055 
0056 /// Enable ROOT persistency
0057 ClassImp(VolumeExtension)
0058 ClassImp(PlacedVolumeExtension)
0059 ClassImp(PlacedVolumeExtension::Parameterisation)
0060 
0061 namespace {
0062 
0063   static constexpr double s_rotation_test_limit = 1e-12;
0064   static bool s_verifyCopyNumbers = true;
0065 
0066   template <typename T> typename T::Object* _userExtension(const T& v)  {
0067     typedef typename T::Object O;
0068     O* o = (O*)(v.ptr()->GetUserExtension());
0069     return o;
0070   }
0071 
0072   TGeoVolume* _createTGeoVolume(const std::string& name, TGeoShape* s, TGeoMedium* m)  {
0073     geo_volume_t* e = new geo_volume_t(name.c_str(),s,m);
0074     e->SetUserExtension(new Volume::Object());
0075     return e;
0076   }
0077   TGeoVolume* _createTGeoVolumeAssembly(const std::string& name)  {
0078     geo_assembly_t* e = new geo_assembly_t(name.c_str()); // It is important to use the correct constructor!!
0079     e->SetUserExtension(new Assembly::Object());
0080     return e;
0081   }
0082   TGeoVolumeMulti* _createTGeoVolumeMulti(const std::string& name, TGeoMedium* medium)  {
0083     TGeoVolumeMulti* e = new TGeoVolumeMulti(name.c_str(), medium);
0084     e->SetUserExtension(new VolumeMulti::Object());
0085     return e;
0086   }
0087   PlacedVolume::Object* _data(const PlacedVolume& v) {
0088     PlacedVolume::Object* o = _userExtension(v);
0089     if (o) return o;
0090     except("PlacedVolume::_data", "+++ Attempt to access invalid handle of type: PlacedVolume");
0091     return nullptr;
0092   }
0093   /// Accessor to the data part of the Volume
0094   Volume::Object* _data(const Volume& v, bool throw_exception = true) {
0095     //if ( v.ptr() && v.ptr()->IsA() == TGeoVolume::Class() ) return v.data<Volume::Object>();
0096     Volume::Object* o = _userExtension(v);
0097     if (o)
0098       return o;
0099     else if (!throw_exception)
0100       return nullptr;
0101     except("Volume::_data", "+++ Attempt to access invalid handle of type: Volume");
0102     return nullptr;
0103   }
0104 
0105   class VolumeImport   {
0106   public:
0107     void setShapeTitle(TGeoVolume* vol)   {
0108       if ( vol )   {
0109         TGeoShape* sh = vol->GetShape();
0110         std::string tag = get_shape_tag(sh);
0111         sh->SetTitle(tag.c_str());
0112       }
0113     }
0114 
0115   public:
0116     /// Callback for simple imports. Simple add a user extension
0117     void operator()(TGeoVolume* v)   {
0118       TClass* c = v->IsA();
0119       if ( !v->GetUserExtension() )   {
0120         if ( c == geo_volume_t::Class() )
0121           v->SetUserExtension(new Volume::Object());
0122         else if  ( c == geo_assembly_t::Class() )
0123           v->SetUserExtension(new Assembly::Object());
0124         else if  ( c == TGeoVolumeMulti::Class() )
0125           v->SetUserExtension(new VolumeMulti::Object());
0126         else
0127           except("dd4hep","VolumeImport: Unknown TGeoVolume sub-class: %s",v->IsA()->GetName());
0128       }
0129       if  ( c == TGeoVolumeMulti::Class() )  {
0130         TGeoVolumeMulti* mv = (TGeoVolumeMulti*)v;
0131         for( int i=0, n=mv->GetNvolumes(); i<n; ++i )   {
0132           TGeoVolume* vol = mv->GetVolume(i);
0133           this->setShapeTitle(vol);
0134           (*this)(vol);
0135         }
0136       }
0137       for( Int_t i=0; i<v->GetNdaughters(); ++i )  {
0138         geo_node_t* pv = v->GetNode(i);
0139         if ( !pv->GetUserExtension() )
0140           pv->geo_node_t::SetUserExtension(new PlacedVolume::Object());
0141         (*this)(pv->GetVolume());
0142       }
0143     }
0144     /// Callback for clone imports, where the user extension should be copied
0145     void operator()(TGeoVolume* new_v, TGeoVolume* old_v, SensitiveDetector sd, int set_bit)   {
0146       if ( !new_v || !old_v )  {
0147         except("dd4hep","VolumeImport: ERROR: The refected volume is INVALID!");        
0148       }
0149       else if ( !old_v->GetUserExtension() )   {
0150         except("dd4hep","VolumeImport: ERROR: Reflection of non-dd4hep volume %s",new_v->IsA()->GetName());
0151       }
0152       else if ( !new_v->GetUserExtension() )   {
0153         TClass* c = new_v->IsA();
0154         Volume old_vol(old_v);
0155         Volume new_vol(new_v);
0156         if ( sd.isValid() && old_vol.isSensitive() )   {
0157           new_vol.setSensitiveDetector(sd);
0158         }
0159         if ( c == geo_volume_t::Class() )  {
0160           Volume::Object *new_e, *old_e = (Volume::Object*)_data(old_vol);
0161           old_e->reflected = new_v;
0162           new_v->SetUserExtension(new_e = new Volume::Object(*old_e)); 
0163           new_e->reflected = old_v;
0164         }
0165         else if  ( c == geo_assembly_t::Class() )  {
0166           Assembly::Object *new_e, *old_e = (Assembly::Object*)_data(old_vol);
0167           old_e->reflected = new_v;
0168           new_v->SetUserExtension(new_e = new Assembly::Object(*old_e));
0169           new_e->reflected = old_v;
0170         }
0171         else if  ( c == TGeoVolumeMulti::Class() )   {
0172           VolumeMulti::Object *new_e, *old_e = (VolumeMulti::Object*)_data(old_vol);
0173           TGeoVolumeMulti* new_mv = (TGeoVolumeMulti*)new_v;
0174           TGeoVolumeMulti* old_mv = (TGeoVolumeMulti*)old_v;
0175           new_v->SetUserExtension(new_e = new VolumeMulti::Object(*old_e));
0176           old_e->reflected = new_v;
0177           new_e->reflected = old_v;
0178           for(int i=0, n=new_mv->GetNvolumes(); i<n; ++i)  {
0179             TGeoVolume* vol = new_mv->GetVolume(i);
0180             this->setShapeTitle(vol);
0181             (*this)(vol, old_mv->GetVolume(i), sd, set_bit);
0182           }
0183         }
0184         else
0185           except("dd4hep","VolumeImport: Unknown TGeoVolume sub-class: %s",new_v->IsA()->GetName());
0186 
0187         new_vol.setSensitiveDetector(old_vol.sensitiveDetector());
0188         new_vol.setVisAttributes(old_vol.visAttributes());
0189         new_vol.setLimitSet(old_vol.limitSet());
0190         new_vol.setRegion(old_vol.region());
0191 
0192         if ( set_bit >= 0 ) new_vol.setFlagBit(set_bit);
0193         for(Int_t i=0; i<new_v->GetNdaughters(); ++i)  {
0194           geo_node_t* pv = new_v->GetNode(i);
0195           geo_node_t* ov = old_v->GetNode(i);
0196           if ( !pv->GetUserExtension() )   {
0197             auto* e = (PlacedVolume::Object*)ov->geo_node_t::GetUserExtension();
0198             pv->geo_node_t::SetUserExtension(new PlacedVolume::Object(*e));
0199           }
0200           (*this)(pv->GetVolume(), ov->GetVolume(), sd, set_bit);
0201         }
0202       }
0203     }
0204   };
0205 
0206   TGeoVolume* MakeReflection(TGeoVolume* v, const char* newname=0)  {
0207     static TMap map(100);
0208     TGeoVolume* vol = (TGeoVolume*)map.GetValue(v);
0209     if ( vol ) {
0210       if (newname && newname[0]) v->SetName(newname);
0211       return vol;
0212     }
0213     vol = v->CloneVolume();
0214     if (!vol) {
0215       printout(ERROR,"MakeReflection", "Cannot clone volume %s\n", v->GetName());
0216       return nullptr;
0217     }
0218     map.Add(v, vol);
0219     std::string nam;
0220     if (newname && newname[0])  {
0221       nam = newname;
0222       vol->SetName(newname);
0223     }
0224     else   {
0225       nam = v->GetName();
0226       vol->SetName((nam+"_refl").c_str());
0227     }
0228     delete vol->GetNodes();
0229     vol->SetNodes(nullptr);
0230     vol->SetBit(TGeoVolume::kVolumeImportNodes, kFALSE);
0231     v->CloneNodesAndConnect(vol);
0232     // The volume is now properly cloned, but with the same shape.
0233     // Reflect the shape (if any) and connect it.
0234     if (v->GetShape())   {
0235       TGeoScale* scale = new TGeoScale( 1., 1.,-1.);
0236       TGeoShape* reflected_shape =
0237         TGeoScaledShape::MakeScaledShape((nam+"_shape_refl").c_str(), v->GetShape(), scale);
0238       vol->SetShape(reflected_shape);
0239       //vol->SetShape(v->GetShape());
0240     }
0241     // Reflect the daughters.
0242     Int_t nd = vol->GetNdaughters();
0243     if ( !nd ) return vol;
0244     TGeoVolume *new_vol;
0245     if ( !vol->GetFinder() ) {
0246       for (Int_t i=0; i < nd; i++) {
0247         TGeoNodeMatrix* node  = (TGeoNodeMatrix*)vol->GetNode(i);
0248         TGeoMatrix*     local = node->GetMatrix();
0249         //         printf("%s before\n", node->GetName());
0250         //         local->Print();
0251         Bool_t      reflected = local->IsReflection();
0252         TGeoMatrix* local_cloned = new TGeoCombiTrans(*local);
0253         local_cloned->RegisterYourself();
0254         node->SetMatrix(local_cloned);
0255         if (!reflected) {
0256           // We need to reflect only the translation and propagate to daughters.
0257           // H' = Sz * H * Sz
0258           local_cloned->ReflectZ(kTRUE);
0259           local_cloned->ReflectZ(kFALSE);
0260           //            printf("%s after\n", node->GetName());
0261           //            node->GetMatrix()->Print();
0262           new_vol = MakeReflection(node->GetVolume());
0263           node->SetVolume(new_vol);
0264           continue;
0265         }
0266         // The next daughter is already reflected, so reflect on Z everything and stop
0267         local_cloned->ReflectZ(kTRUE); // rot + tr
0268         //         printf("%s already reflected... After:\n", node->GetName());
0269         //         node->GetMatrix()->Print();
0270       }
0271       if ( vol->GetVoxels() ) vol->GetVoxels()->Voxelize();
0272       return vol;
0273     }
0274     // Volume is divided, so we have to reflect the division.
0275     //   printf("   ... divided %s\n", fFinder->ClassName());
0276     TGeoPatternFinder *new_finder = v->GetFinder()->MakeCopy(kTRUE);
0277     if (!new_finder) {
0278       printout(ERROR,"MakeReflection", "Could not copy finder for volume %s", v->GetName());
0279       return nullptr;
0280     }
0281     new_finder->SetVolume(vol);
0282     vol->SetFinder(new_finder);
0283     TGeoNodeOffset *nodeoff;
0284     new_vol = 0;
0285     for (Int_t i=0; i<nd; i++) {
0286       nodeoff = (TGeoNodeOffset*)vol->GetNode(i);
0287       nodeoff->SetFinder(new_finder);
0288       new_vol = MakeReflection(nodeoff->GetVolume());
0289       nodeoff->SetVolume(new_vol);
0290     }
0291     return vol;
0292   }
0293 
0294   int get_copy_number(TGeoVolume* par)    {
0295     TObjArray* a = par ? par->GetNodes() : 0;
0296     int copy_nr = (a ? a->GetEntries() : 0);
0297     return copy_nr;
0298   }
0299 
0300 }
0301 
0302 /// Perform scan
0303 void ReflectionBuilder::execute()  const   {
0304   TGeoIterator next(detector.manager().GetTopVolume());
0305   bool print_active = isActivePrintLevel(DEBUG);
0306   TGeoNode *node;
0307   while ( (node=next()) ) {
0308     TGeoMatrix* matrix = node->GetMatrix();
0309     if (matrix->IsReflection()) {
0310       if ( print_active )  {
0311         printout(INFO,"ReflectionBuilder","Reflection matrix:");
0312         matrix->Print();
0313       }
0314       Volume vol(node->GetVolume());
0315       TGeoMatrix* mclone = new TGeoCombiTrans(*matrix);
0316       mclone->RegisterYourself();
0317       // Reflect just the rotation component
0318       //mclone->ReflectZ(kFALSE, kTRUE);
0319       if ( print_active )  {
0320         printout(INFO,"ReflectionBuilder","CLONE matrix:");
0321         mclone->Print();
0322       }
0323       TGeoNodeMatrix* nodematrix = (TGeoNodeMatrix*)node;
0324       nodematrix->SetMatrix(mclone);
0325       if ( print_active )  {
0326         printout(INFO,"ReflectionBuilder","Reflected volume: %s ", vol.name());
0327       }
0328       Volume refl = vol.reflect(vol.sensitiveDetector());
0329       node->SetVolume(refl.ptr());
0330     }
0331   }
0332 }
0333 
0334 /// Default constructor
0335 PlacedVolume::Processor::Processor()   {
0336 }
0337 
0338 /// Default destructor
0339 PlacedVolume::Processor::~Processor()   {
0340 }
0341 
0342 /// Increase ref count
0343 PlacedVolumeExtension::Parameterisation* PlacedVolumeExtension::Parameterisation::addref()  {
0344   ++refCount;
0345   return this;
0346 }
0347 /// Decrease ref count
0348 void PlacedVolumeExtension::Parameterisation::release()  {
0349   --refCount;
0350   if ( 0 == refCount ) delete this;
0351 }
0352 
0353 /// Default constructor
0354 PlacedVolumeExtension::PlacedVolumeExtension()
0355   : TGeoExtension(), volIDs()
0356 {
0357   magic = magic_word();
0358   INCREMENT_COUNTER;
0359 }
0360 
0361 /// Default move
0362 PlacedVolumeExtension::PlacedVolumeExtension(PlacedVolumeExtension&& c)
0363   : TGeoExtension(c), magic(std::move(c.magic)), refCount(0), volIDs(std::move(c.volIDs)) {
0364   INCREMENT_COUNTER;
0365 }
0366 
0367 /// Copy constructor
0368 PlacedVolumeExtension::PlacedVolumeExtension(const PlacedVolumeExtension& c)
0369   : TGeoExtension(), magic(c.magic), refCount(0), volIDs() {
0370   INCREMENT_COUNTER;
0371   volIDs = c.volIDs;
0372 }
0373 
0374 /// Default destructor
0375 PlacedVolumeExtension::~PlacedVolumeExtension() {
0376   if ( this->params ) this->params->release();
0377   DECREMENT_COUNTER;
0378 }
0379 
0380 /// Move assignment
0381 PlacedVolumeExtension& PlacedVolumeExtension::operator=(PlacedVolumeExtension&& copy)  {
0382   magic  = std::move(copy.magic);
0383   params = std::move(copy.params);
0384   volIDs = std::move(copy.volIDs);
0385   return *this;
0386 }
0387 /// Assignment operator
0388 PlacedVolumeExtension& PlacedVolumeExtension::operator=(const PlacedVolumeExtension& copy) {
0389   magic  = copy.magic;
0390   params = copy.params;
0391   volIDs = copy.volIDs;
0392   return *this;
0393 }
0394 
0395 /// TGeoExtension overload: Method called whenever requiring a pointer to the extension
0396 TGeoExtension* PlacedVolumeExtension::Grab()   {
0397   ++this->refCount;
0398 #ifdef ___print_vols
0399   else  cout << "Placement grabbed....." << endl;
0400 #endif
0401   return this;
0402 }
0403 
0404 /// TGeoExtension overload: Method called always when the pointer to the extension is not needed anymore
0405 void PlacedVolumeExtension::Release() const  {
0406   PlacedVolumeExtension* ext = const_cast<PlacedVolumeExtension*>(this);
0407   --ext->refCount;
0408   if ( 0 == ext->refCount ) delete ext;
0409 }
0410 
0411 /// Lookup volume ID
0412 std::vector<PlacedVolumeExtension::VolID>::const_iterator
0413 PlacedVolumeExtension::VolIDs::find(const std::string& name) const {
0414   for (Base::const_iterator i = this->Base::begin(); i != this->Base::end(); ++i)
0415     if (name == (*i).first)
0416       return i;
0417   return this->end();
0418 }
0419 
0420 /// Insert a new value into the volume ID container
0421 std::pair<std::vector<PlacedVolumeExtension::VolID>::iterator, bool>
0422 PlacedVolumeExtension::VolIDs::insert(const std::string& name, int value) {
0423   Base::iterator i = this->Base::begin();
0424   for (; i != this->Base::end(); ++i)
0425     if (name == (*i).first)
0426       break;
0427   //
0428   if (i != this->Base::end()) {
0429     return make_pair(i, false);
0430   }
0431   i = this->Base::emplace(this->Base::end(), name, value);
0432   return make_pair(i, true);
0433 }
0434 
0435 /// String representation for debugging
0436 std::string PlacedVolumeExtension::VolIDs::str()  const   {
0437   std::stringstream str;
0438   str << std::hex;
0439   for(const auto& i : *this )   {
0440     str << i.first << "=" << std::setw(4) << std::right
0441         << std::setfill('0') << i.second << std::setfill(' ') << " ";
0442   }
0443   return str.str();
0444 }
0445 
0446 /// Check if placement is properly instrumented
0447 PlacedVolume::Object* PlacedVolume::data() const   {
0448   PlacedVolume::Object* o = _userExtension(*this);
0449   return o;
0450 }
0451 
0452 /// Access the object type from the class information
0453 const char* PlacedVolume::type() const   {
0454   return m_element ? m_element->IsA()->GetName() : "UNKNOWN-PlacedVolume";
0455 }
0456 
0457 /// Access the copy number of this placement within its mother
0458 int PlacedVolume::copyNumber() const   {
0459   return m_element ? m_element->GetNumber() : -1;
0460 }
0461 
0462 /// Volume material
0463 Material PlacedVolume::material() const {
0464   return Material(m_element ? m_element->GetMedium() : 0);
0465 }
0466 
0467 /// Logical volume of this placement
0468 Volume PlacedVolume::volume() const {
0469   return Volume(m_element ? m_element->GetVolume() : 0);
0470 }
0471 
0472 /// Parent volume (envelope)
0473 Volume PlacedVolume::motherVol() const {
0474   return Volume(m_element ? m_element->GetMotherVolume() : 0);
0475 }
0476 
0477 /// Number of daughters placed in this volume
0478 std::size_t PlacedVolume::num_daughters()  const   {
0479   return m_element ? m_element->GetNdaughters() : 0;
0480 }
0481 
0482 /// Access the daughter by index
0483 PlacedVolume PlacedVolume::daughter(std::size_t which)  const   {
0484   if ( m_element )  {
0485     if ( which < (std::size_t)m_element->GetNdaughters() )   {
0486       return m_element->GetDaughter(which);
0487     }
0488     except("Volume","+++ Access daughter %ld of %s [Has only %d daughters]",
0489        which, m_element->GetName(), m_element->GetNdaughters());
0490   }
0491   except("Volume","+++ Cannot access daughters of a non-existing volume!");
0492   return nullptr;
0493 }
0494 
0495 /// Access to the volume IDs
0496 const PlacedVolume::VolIDs& PlacedVolume::volIDs() const {
0497   return _data(*this)->volIDs;
0498 }
0499 
0500 /// Add identifier
0501 PlacedVolume& PlacedVolume::addPhysVolID(const std::string& nam, int value) {
0502   auto* o = _data(*this);
0503   if ( !o->params )   {
0504     o->volIDs.emplace_back(nam, value);
0505     return *this;
0506   }
0507   if ( value > 0 )    {
0508     except("PlacedVolume",
0509        "+++ addPhysVolID(%s): parameterised volumes only accept '0' is volID."
0510        "These automatically get overwritten with the copy number!",
0511        ptr()->GetName());
0512   }
0513   if ( !o->volIDs.empty() )    {
0514     except("PlacedVolume",
0515        "+++ addPhysVolID(%s): parameterised volumes can only host 1 physical volume ID."
0516        " vol id '%s' is already defined!", ptr()->GetName(), o->volIDs[0].first.c_str());
0517   }
0518   for(PlacedVolume pv : o->params->placements)  {
0519     auto* p = _data(pv);
0520     p->volIDs.emplace_back(nam, pv->GetNumber());
0521   }
0522   return *this;
0523 }
0524 
0525 /// Translation vector within parent volume
0526 const TGeoMatrix& PlacedVolume::matrix()  const    {
0527   if ( !isValid() )  {
0528     except("PlacedVolume","+++ matrix: Failed to access invalid PlacedVolume! [Invalid handle]");
0529   }
0530   return *(m_element->GetMatrix());  
0531 }
0532 
0533 /// Translation vector within parent volume
0534 Position PlacedVolume::position()  const    {
0535   const double* ptr = matrix().GetTranslation();
0536   return Position(ptr[0],ptr[1],ptr[2]);
0537 }
0538 
0539 /// String dump
0540 std::string PlacedVolume::toString() const {
0541   std::stringstream str;
0542   Object* obj = _data(*this);
0543   str << m_element->GetName() << ":  vol='" << m_element->GetVolume()->GetName()
0544       << "' mat:'" << m_element->GetMatrix()->GetName()
0545       << "' volID[" << obj->volIDs.size() << "] ";
0546   for (VolIDs::const_iterator i = obj->volIDs.begin(); i != obj->volIDs.end(); ++i)
0547     str << (*i).first << "=" << (*i).second << "  ";
0548   str << std::ends;
0549   return str.str();
0550 }
0551 
0552 /// Default constructor
0553 VolumeExtension::VolumeExtension()
0554 : TGeoExtension(), region(), limits(), vis(), sens_det()   {
0555   INCREMENT_COUNTER;
0556 }
0557 
0558 /// Default destructor
0559 VolumeExtension::~VolumeExtension() {
0560   detail::deletePtr(properties);
0561   region.clear();
0562   limits.clear();
0563   vis.clear();
0564   sens_det.clear();
0565   DECREMENT_COUNTER;
0566 }
0567 
0568 /// Copy constructor
0569 VolumeExtension::VolumeExtension(const VolumeExtension& c)   {
0570   this->copy(c);
0571 }
0572 
0573 /// Copy assignment
0574 VolumeExtension& VolumeExtension::operator=(const VolumeExtension& c)   {
0575   this->copy(c);
0576   return *this;
0577 }
0578 
0579 /// Copy the object
0580 void VolumeExtension::copy(const VolumeExtension& c) {
0581   if ( this != &c )   {
0582     magic      = c.magic;
0583     region     = c.region;
0584     limits     = c.limits;
0585     vis        = c.vis;
0586     sens_det   = c.sens_det;
0587     referenced = c.referenced;
0588     detail::deletePtr(properties);
0589     if ( c.properties )    {
0590       properties = new TList();
0591       properties->SetOwner();
0592       TIter next(properties);
0593       TNamed *property;
0594       while ((property = (TNamed*)next())) properties->Add(new TNamed(*property));
0595     }
0596   }
0597 }
0598 
0599 /// TGeoExtension overload: Method called whenever requiring a pointer to the extension
0600 TGeoExtension* VolumeExtension::Grab()  {
0601   VolumeExtension* ext = const_cast<VolumeExtension*>(this);
0602   ++ext->refCount;
0603 #ifdef ___print_vols
0604   if ( ext->sens_det.isValid() )
0605     cout << "Volume grabbed with valid sensitive detector....." << endl;
0606   else
0607     cout << "Volume grabbed....." << endl;
0608 #endif
0609   return ext;
0610 }
0611 
0612 /// TGeoExtension overload: Method called always when the pointer to the extension is not needed anymore
0613 void VolumeExtension::Release() const  {
0614   VolumeExtension* ext = const_cast<VolumeExtension*>(this);
0615   --ext->refCount;
0616   if ( 0 == ext->refCount )   {
0617 #ifdef ___print_vols
0618     cout << "Volume deleted....." << endl;
0619 #endif
0620     delete ext;
0621   }
0622   else  {
0623 #ifdef ___print_vols
0624     cout << "VolumeExtension::Release::refCount:" << ext->refCount << endl;
0625 #endif
0626   }
0627 }
0628 
0629 /// Constructor to be used when creating a new geometry tree.
0630 Volume::Volume(const std::string& nam) {
0631   m_element = _createTGeoVolume(nam,0,0);
0632 }
0633 
0634 /// Constructor to be used when creating a new geometry tree.
0635 Volume::Volume(const std::string& nam, const std::string& title) {
0636   m_element = _createTGeoVolume(nam,0,0);
0637   m_element->SetTitle(title.c_str());
0638 }
0639 
0640 /// Constructor to be used when creating a new geometry tree. Also sets materuial and solid attributes
0641 Volume::Volume(const std::string& nam, const Solid& sol, const Material& mat) {
0642   m_element = _createTGeoVolume(nam, sol.ptr(), mat.ptr());
0643 }
0644 
0645 /// Constructor to be used when creating a new geometry tree. Also sets materuial and solid attributes
0646 Volume::Volume(const std::string& nam, const std::string& title, const Solid& sol, const Material& mat) {
0647   m_element = _createTGeoVolume(nam, sol.ptr(), mat.ptr());
0648   m_element->SetTitle(title.c_str());
0649 }
0650 
0651 /// Set flag to enable copy number checks when inserting new nodes
0652 void Volume::enableCopyNumberCheck(bool value)    {
0653   s_verifyCopyNumbers = value;
0654 }
0655 
0656 /// Check if placement is properly instrumented
0657 Volume::Object* Volume::data() const   {
0658   Volume::Object* o = _userExtension(*this);
0659   return o;
0660 }
0661 
0662 /// Access the object type from the class information
0663 const char* Volume::type() const   {
0664   return m_element ? m_element->IsA()->GetName() : "UNKNOWN-Volume";
0665 }
0666 
0667 /// Create a reflected volume tree. The reflected volume has left-handed coordinates
0668 Volume Volume::reflect()  const   {
0669   return this->reflect(this->sensitiveDetector());
0670 }
0671     
0672 /// Create a reflected volume tree. The reflected volume has left-handed coordinates
0673 Volume Volume::reflect(SensitiveDetector sd)  const   {
0674   if ( m_element )   {
0675     VolumeImport imp;
0676     Object* o = data();
0677     if ( !o->reflected.isValid() )  {
0678       TGeoVolume* vol = MakeReflection(m_element);
0679       imp(vol, m_element, sd, Volume::REFLECTED);
0680       o->reflected = vol;
0681     }
0682     return o->reflected;
0683   }
0684   except("dd4hep","Volume: Attempt to reflect an invalid Volume handle.");
0685   return *this;
0686 }
0687 
0688 /// If we import volumes from external sources, we have to attach the extensions to the tree
0689 Volume& Volume::import()    {
0690   if ( m_element )   {
0691     VolumeImport imp;
0692     imp(m_element);
0693     return *this;
0694   }
0695   except("dd4hep","Volume: Attempt to import an invalid Volume handle.");
0696   return *this;
0697 }
0698 
0699 /// Set user flags in bit-field
0700 void Volume::setFlagBit(unsigned int bit)   {
0701   if ( bit <= 31 )   {
0702     data()->flags |= 1<<bit;
0703     return;
0704   }
0705   except("Volume","+++ Volume flag bit outsize range [0...31]: %d",bit);
0706 }
0707 
0708 /// Test the user flag bit
0709 bool Volume::testFlagBit(unsigned int bit)   const    {
0710   if ( bit <= 31 )   {
0711     return (data()->flags & 1<<bit) != 0;
0712   }
0713   except("Volume","+++ Volume flag bit outsize range [0...31]: %d",bit);
0714   return false; // Anyhow never called. Only to satisfy the compiler.
0715 }    
0716 
0717 /// Test if this volume was reflected
0718 bool Volume::isReflected()   const    {
0719   return testFlagBit(REFLECTED);
0720 }
0721 
0722 /// Test if this volume is an assembly structure
0723 bool Volume::isAssembly()   const   {
0724   return m_element ? m_element->IsAssembly() : false;
0725 }    
0726 
0727 /// Set the smartless option for G4 voxelization. Returns previous value
0728 unsigned char Volume::setSmartlessValue(unsigned char new_value)  {
0729   Object* obj = _data(*this);
0730   unsigned char tmp = obj->smartLess;
0731   obj->smartLess = new_value;
0732   return tmp;
0733 }
0734 
0735 /// access the smartless option for G4 voxelization
0736 unsigned char Volume::smartlessValue()  const  {
0737   return _data(*this)->smartLess;
0738 }
0739 
0740 /// Divide volume into subsections (See the ROOT manuloa for details)
0741 Volume Volume::divide(const std::string& divname, int iaxis, int ndiv,
0742                       double start, double step, int numed, const char* option)   {
0743   TGeoVolume* p = m_element;
0744   if ( p )  {
0745     TGeoVolume* mvp = p->Divide(divname.c_str(), iaxis, ndiv, start, step, numed, option);
0746     if ( mvp )   {
0747       VolumeImport imp;
0748       imp(mvp);
0749       return VolumeMulti(mvp);
0750     }
0751     except("dd4hep","Volume: Failed to divide volume %s -> %s [Invalid result]",
0752            p->GetName(), divname.c_str());
0753   }
0754   except("dd4hep","Volume: Attempt to divide an invalid logical volume.");
0755   return nullptr;
0756 }
0757 
0758 PlacedVolume _addNode(TGeoVolume* par, TGeoVolume* daughter, int id, TGeoMatrix* transform) {
0759   TGeoVolume* parent = par;
0760   if ( !parent )   {
0761     except("dd4hep","Volume: Attempt to assign daughters to an invalid physical parent volume.");
0762   }
0763   else if ( !daughter )   {
0764     except("dd4hep","Volume: Attempt to assign an invalid physical daughter volume.");
0765   }
0766   else if ( !transform )   {
0767     except("dd4hep","Volume: Attempt to place volume without placement matrix.");
0768   }
0769   if ( transform != detail::matrix::_identity() ) {
0770     std::string nam = std::string(daughter->GetName()) + "_placement";
0771     transform->SetName(nam.c_str());
0772   }
0773   TGeoShape* shape = daughter->GetShape();
0774   // Need to fix the daughter's BBox of assemblies, if the BBox was not calculated....
0775   if ( shape->IsA() == TGeoShapeAssembly::Class() )  {
0776     TGeoShapeAssembly* as = (TGeoShapeAssembly*)shape;
0777     as->NeedsBBoxRecompute();
0778     as->ComputeBBox();
0779   }
0780   const Double_t* r = transform->GetRotationMatrix();
0781   if ( r )   {
0782     Double_t test_rot = r[0] + r[4] + r[8] - 3.0;
0783     if ( TMath::Abs(test_rot) < s_rotation_test_limit )
0784       transform->ResetBit(TGeoMatrix::kGeoRotation);
0785     else
0786       transform->SetBit(TGeoMatrix::kGeoRotation);
0787 
0788     if ( transform->IsRotation() )   {
0789       Double_t det =
0790         r[0]*r[4]*r[8] + r[3]*r[7]*r[2] + r[6]*r[1]*r[5] -
0791         r[2]*r[4]*r[6] - r[5]*r[7]*r[0] - r[8]*r[1]*r[3];
0792       /// We have a left handed matrix (determinant < 0). This is a reflection!
0793       if ( det < 0e0 )   {
0794         transform->SetBit(TGeoMatrix::kGeoReflection);
0795         printout(DEBUG, "PlacedVolume",
0796                  "REFLECTION: (x.Cross(y)).Dot(z): %8.3g Parent: %s [%s] Daughter: %s [%s]",
0797                  det, par->GetName(), par->IsA()->GetName(),
0798                  daughter->GetName(), daughter->IsA()->GetName());
0799       }
0800     }
0801   }
0802   geo_node_t* n {nullptr};
0803   TString nam_id = TString::Format("%s_%d", daughter->GetName(), id);
0804   if ( s_verifyCopyNumbers )   {
0805     n = static_cast<geo_node_t*>(parent->GetNode(nam_id));
0806     if ( n != 0 )  {
0807       printout(ERROR,"PlacedVolume","++ Attempt to place already exiting node %s",(const char*)nam_id);
0808     }
0809   }
0810   /* n = */ parent->AddNode(daughter, id, transform);
0811   //n = static_cast<geo_node_t*>(parent->GetNode(nam_id));
0812   n = static_cast<geo_node_t*>(parent->GetNodes()->Last());
0813   if ( nam_id != n->GetName() )   {
0814     printout(ERROR,"PlacedVolume","++ FAILED to place node %s",(const char*)nam_id);
0815   }
0816   PlacedVolume::Object* extension = new PlacedVolume::Object();
0817   n->geo_node_t::SetUserExtension(extension);
0818   return PlacedVolume(n);
0819 }
0820 
0821 PlacedVolume _addNode(TGeoVolume* par, Volume daughter, int copy_nr, const Rotation3D& rot3D)   {
0822   TGeoRotation r;
0823   double elements[9];
0824   rot3D.GetComponents(elements);
0825   r.SetMatrix(elements);
0826   auto matrix = std::make_unique<TGeoCombiTrans>(TGeoTranslation(0,0,0),r);
0827   return _addNode(par, daughter, copy_nr, matrix.release());
0828 }
0829 
0830 PlacedVolume _addNode(TGeoVolume* par, Volume daughter, int copy_nr, const Transform3D& tr)   {
0831   TGeoRotation r;
0832   double elements[9];
0833   Position   pos3D;
0834   Rotation3D rot3D;
0835   tr.GetRotation(rot3D);
0836   tr.GetTranslation(pos3D);
0837   rot3D.GetComponents(elements);
0838   r.SetMatrix(elements);
0839   auto matrix = std::make_unique<TGeoCombiTrans>(TGeoTranslation(pos3D.x(), pos3D.y(), pos3D.z()),r);
0840   return _addNode(par, daughter, copy_nr, matrix.release());
0841 }
0842 
0843 /// Place daughter volume with generic TGeo matrix
0844 PlacedVolume Volume::placeVolume(const Volume& volume, TGeoMatrix* tr) const    {
0845   return _addNode(m_element, volume, get_copy_number(m_element), tr);
0846 }
0847 
0848 /// Place daughter volume with generic TGeo matrix
0849 PlacedVolume Volume::placeVolume(const Volume& volume, int copy_nr, TGeoMatrix* tr) const    {
0850   return _addNode(m_element, volume, copy_nr, tr);
0851 }
0852 
0853 /// Place daughter volume according to generic Transform3D
0854 PlacedVolume Volume::placeVolume(const Volume& volume, const Transform3D& trans) const {
0855   return _addNode(m_element, volume, get_copy_number(m_element), trans);
0856 }
0857 
0858 /// Place daughter volume. The position and rotation are the identity
0859 PlacedVolume Volume::placeVolume(const Volume& volume) const {
0860   return _addNode(m_element, volume, get_copy_number(m_element), detail::matrix::_identity());
0861 }
0862 
0863 /// Place un-rotated daughter volume at the given position.
0864 PlacedVolume Volume::placeVolume(const Volume& volume, const Position& pos) const {
0865   return _addNode(m_element, volume, get_copy_number(m_element), detail::matrix::_translation(pos));
0866 }
0867 
0868 /// Place rotated daughter volume. The position is automatically the identity position
0869 PlacedVolume Volume::placeVolume(const Volume& volume, const RotationZYX& rot) const {
0870   return _addNode(m_element, volume, get_copy_number(m_element), detail::matrix::_rotationZYX(rot));
0871 }
0872 
0873 /// Place rotated daughter volume. The position is automatically the identity position
0874 PlacedVolume Volume::placeVolume(const Volume& volume, const Rotation3D& rot) const {
0875   return _addNode(m_element, volume, get_copy_number(m_element), rot);
0876 }
0877 
0878 /// Place daughter volume according to generic Transform3D
0879 PlacedVolume Volume::placeVolume(const Volume& volume, int copy_no, const Transform3D& trans) const {
0880   return _addNode(m_element, volume, copy_no, trans);
0881 }
0882 
0883 /// Place daughter volume. The position and rotation are the identity
0884 PlacedVolume Volume::placeVolume(const Volume& volume, int copy_no) const {
0885   return _addNode(m_element, volume, copy_no, detail::matrix::_identity());
0886 }
0887 
0888 /// Place un-rotated daughter volume at the given position.
0889 PlacedVolume Volume::placeVolume(const Volume& volume, int copy_no, const Position& pos) const {
0890   return _addNode(m_element, volume, copy_no, detail::matrix::_translation(pos));
0891 }
0892 
0893 /// Place rotated daughter volume. The position is automatically the identity position
0894 PlacedVolume Volume::placeVolume(const Volume& volume, int copy_no, const RotationZYX& rot) const {
0895   return _addNode(m_element, volume, copy_no, detail::matrix::_rotationZYX(rot));
0896 }
0897 
0898 /// Place rotated daughter volume. The position is automatically the identity position
0899 PlacedVolume Volume::placeVolume(const Volume& volume, int copy_no, const Rotation3D& rot) const {
0900   return _addNode(m_element, volume, copy_no, rot);
0901 }
0902 
0903 /// 1D volume replication implementation
0904 PlacedVolume Volume::replicate(const Volume entity, ReplicationAxis axis,
0905                    size_t count, double inc, double start)
0906 {
0907   Transform3D offset(1e0,  0e0,  0e0,  axis == X_axis ? start : 0e0,
0908              0e0,  1e0,  0e0,  axis == Y_axis ? start : 0e0,
0909              0e0,  0e0,  1e0,  axis == Z_axis ? start : 0e0);
0910   Transform3D tr(1e0,  0e0,  0e0,  axis == X_axis ? inc : 0e0,
0911          0e0,  1e0,  0e0,  axis == Y_axis ? inc : 0e0,
0912          0e0,  0e0,  1e0,  axis == Z_axis ? inc : 0e0);
0913   PlacedVolume pv = paramVolume1D(offset, entity, count, tr);
0914   auto* data = pv.data();
0915   data->params->flags = axis | REPLICATED;
0916   return pv;
0917 }
0918 
0919 /// Constructor to be used when creating a new parameterised volume object
0920 PlacedVolume Volume::paramVolume1D(Volume entity, size_t count, const Position& inc)   {
0921   return paramVolume1D(Transform3D(), entity, count, Transform3D(inc));
0922 }
0923 
0924 /// Constructor to be used when creating a new parameterised volume object
0925 PlacedVolume Volume::paramVolume1D(Volume entity, size_t count, const RotationZYX& inc)  {
0926   return paramVolume1D(Transform3D(), entity, count, Transform3D(inc));
0927 }
0928 
0929 /// Constructor to be used when creating a new parameterised volume object
0930 PlacedVolume Volume::paramVolume1D(Volume entity, size_t count, const Transform3D& inc)   {
0931   return paramVolume1D(Transform3D(), entity, count, inc);
0932 }
0933 
0934 /// Constructor to be used when creating a new parameterised volume object
0935 PlacedVolume Volume::paramVolume1D(const Transform3D& start,
0936                    Volume entity,
0937                    size_t count,
0938                    const Transform3D& trafo)
0939 {
0940   Transform3D tr(start);
0941   PlacedVolume pv =
0942     _addNode(m_element, entity, get_copy_number(m_element), detail::matrix::_transform(tr));
0943   auto* data = pv.data();
0944   if ( pv->GetNdaughters() > 1 )   {
0945     except("Volume","paramVolume1D: Mother %s has too many daughters: %ld "
0946        "Parameterized volumes may only have one single daughter!",
0947        ptr()->GetName(), pv.volume()->GetNdaughters());
0948   }
0949   data->params = new PlacedVolumeExtension::Parameterisation();
0950   data->params->addref();
0951   data->params->flags = PARAMETERIZED;
0952   data->params->start = start;
0953   data->params->trafo1D.first  = trafo;
0954   data->params->trafo1D.second = count;
0955   data->params->trafo2D.second = 0;
0956   data->params->trafo3D.second = 0;
0957   data->params->placements.emplace_back(pv);
0958   for(size_t i=1; i < count; ++i)    {
0959     tr *= trafo;
0960     PlacedVolume ppv =
0961       _addNode(m_element, entity, get_copy_number(m_element), detail::matrix::_transform(tr));
0962     data->params->placements.emplace_back(ppv);
0963     ppv.data()->params = data->params->addref();
0964   }
0965   return pv;
0966 }
0967 
0968 /// Constructor to be used when creating a new parameterised volume object
0969 PlacedVolume Volume::paramVolume2D(Volume entity,
0970                    size_t count_1,
0971                    const Transform3D& trafo_1,
0972                    size_t count_2,
0973                    const Transform3D& trafo_2)
0974 {
0975   return paramVolume2D(Transform3D(), entity, count_1, trafo_1, count_2, trafo_2);
0976 }
0977 
0978 /// Constructor to be used when creating a new parameterised volume object
0979 PlacedVolume Volume::paramVolume2D(const Transform3D& start,
0980                    Volume entity,
0981                    size_t count_1,
0982                    const Position& pos_1,
0983                    size_t count_2,
0984                    const Position& pos_2)
0985 {
0986   return paramVolume2D(start, entity, count_1, Transform3D(pos_1), count_2, Transform3D(pos_2));
0987 }
0988 
0989 /// Constructor to be used when creating a new parameterised volume object
0990 PlacedVolume Volume::paramVolume2D(Volume entity,
0991                    size_t count_1,
0992                    const Position& pos_1,
0993                    size_t count_2,
0994                    const Position& pos_2)
0995 {
0996   return paramVolume2D(Transform3D(), entity, count_1, Transform3D(pos_1), count_2, Transform3D(pos_2));
0997 }
0998 
0999 /// Constructor to be used when creating a new parameterised volume object
1000 PlacedVolume Volume::paramVolume2D(const Transform3D& start,
1001                    Volume entity,
1002                    size_t count_1,
1003                    const Transform3D& trafo_1,
1004                    size_t count_2,
1005                    const Transform3D& trafo_2)
1006 {
1007   PlacedVolume pv =
1008     _addNode(m_element, entity, get_copy_number(m_element), detail::matrix::_transform(start));
1009   auto* data = pv.data();
1010   if ( pv->GetNdaughters() > 1 )   {
1011     except("Volume","paramVolume1D: Mother %s has too many daughters: %ld "
1012        "Parameterized volumes may only have one single daughter!",
1013        ptr()->GetName(), pv.volume()->GetNdaughters());
1014   }
1015   data->params = new PlacedVolumeExtension::Parameterisation();
1016   data->params->addref();
1017   data->params->flags = PARAMETERIZED;
1018   data->params->start = start;
1019   data->params->trafo1D.first  = trafo_1;
1020   data->params->trafo1D.second = count_1;
1021   data->params->trafo2D.first  = trafo_2;
1022   data->params->trafo2D.second = count_2;
1023   data->params->trafo3D.second = 0;
1024   data->params->placements.emplace_back(pv);
1025   Transform3D tr2(start);
1026   for(size_t j=0; j < count_2; ++j)    {
1027     Transform3D tr1 = tr2;
1028     for(size_t i = 0; i < count_1; ++i)    {
1029       if ( !( i == 0 && j == 0 ) )   {
1030     PlacedVolume ppv =
1031       _addNode(m_element, entity, get_copy_number(m_element), detail::matrix::_transform(tr1));
1032     data->params->placements.emplace_back(ppv);
1033     ppv.data()->params = data->params->addref();
1034       }
1035       tr1 *= trafo_1;
1036     }
1037     tr2 *= trafo_2;
1038   }
1039   return pv;
1040 }
1041 
1042 /// Constructor to be used when creating a new parameterised volume object
1043 PlacedVolume Volume::paramVolume3D(const Transform3D& start,
1044                    Volume entity,
1045                    size_t count_1,
1046                    const Position& pos_1,
1047                    size_t count_2,
1048                    const Position& pos_2,
1049                    size_t count_3,
1050                    const Position& pos_3)
1051 {
1052   return paramVolume3D(start, entity, 
1053                count_1, Transform3D(pos_1),
1054                count_2, Transform3D(pos_2),
1055                count_3, Transform3D(pos_3));
1056 }
1057 
1058 /// Constructor to be used when creating a new parameterised volume object
1059 PlacedVolume Volume::paramVolume3D(Volume entity,
1060                    size_t count_1,
1061                    const Position& pos_1,
1062                    size_t count_2,
1063                    const Position& pos_2,
1064                    size_t count_3,
1065                    const Position& pos_3)
1066 {
1067   return paramVolume3D(Transform3D(), entity,
1068                count_1, Transform3D(pos_1),
1069                count_2, Transform3D(pos_2),
1070                count_3, Transform3D(pos_3));
1071 }
1072 
1073 /// Constructor to be used when creating a new parameterised volume object
1074 PlacedVolume Volume::paramVolume3D(const Transform3D& start,
1075                    Volume entity,
1076                    size_t count_1,
1077                    const Transform3D& trafo_1,
1078                    size_t count_2,
1079                    const Transform3D& trafo_2,
1080                    size_t count_3,
1081                    const Transform3D& trafo_3)
1082 {
1083   PlacedVolume pv =
1084     _addNode(m_element, entity, get_copy_number(m_element), detail::matrix::_transform(start));
1085   auto* data = pv.data();
1086   if ( pv->GetNdaughters() > 1 )   {
1087     except("Volume","paramVolume1D: Mother %s has too many daughters: %ld "
1088        "Parameterized volumes may only have one single daughter!",
1089        ptr()->GetName(), pv.volume()->GetNdaughters());
1090   }
1091   data->params = new PlacedVolumeExtension::Parameterisation();
1092   data->params->addref();
1093   data->params->flags = PARAMETERIZED;
1094   data->params->start = start;
1095   data->params->trafo1D.first  = trafo_1;
1096   data->params->trafo1D.second = count_1;
1097   data->params->trafo2D.first  = trafo_2;
1098   data->params->trafo2D.second = count_2;
1099   data->params->trafo3D.first  = trafo_3;
1100   data->params->trafo3D.second = count_3;
1101   data->params->placements.emplace_back(pv);
1102   Transform3D tr3(start);
1103   for(size_t k=0; k < count_3; ++k)    {
1104     Transform3D tr2 = tr3;
1105     for(size_t j=0; j < count_2; ++j)    {
1106       Transform3D tr1 = tr2;
1107       for(size_t i = 0; i < count_1; ++i)    {
1108     if ( !( i == 0 && j == 0 && k == 0 ) )   {
1109       PlacedVolume ppv =
1110         _addNode(m_element, entity, get_copy_number(m_element), detail::matrix::_transform(tr1));
1111       data->params->placements.emplace_back(ppv);
1112       ppv.data()->params = data->params->addref();
1113     }
1114     tr1 *= trafo_1;
1115       }
1116       tr2 *= trafo_2;
1117     }
1118     tr3 *= trafo_3;
1119   }
1120   return pv;
1121 }
1122 
1123 /// Set the volume's option value
1124 const Volume& Volume::setOption(const std::string& opt) const {
1125   if ( isValid() )   {
1126     m_element->SetOption(opt.c_str());
1127     return *this;
1128   }
1129   throw std::runtime_error("dd4hep: Attempt to access invalid handle of type: PlacedVolume");
1130 }
1131 
1132 /// Access the volume's option value
1133 std::string Volume::option() const {
1134   return m_element->GetOption();
1135 }
1136 
1137 /// Set the volume's material
1138 const Volume& Volume::setMaterial(const Material& mat) const {
1139   if (mat.isValid()) {
1140     TGeoMedium* medium = mat._ptr<TGeoMedium>();
1141     if (medium) {
1142       m_element->SetMedium(medium);
1143       return *this;
1144     }
1145     throw std::runtime_error("dd4hep: Volume: Medium " + std::string(mat.name()) + " is not registered with geometry manager.");
1146   }
1147   throw std::runtime_error("dd4hep: Volume: Attempt to assign invalid material.");
1148 }
1149 
1150 /// Access to the Volume material
1151 Material Volume::material() const {
1152   return Material(m_element->GetMedium());
1153 }
1154 
1155 /// Set Visualization attributes to the volume
1156 const Volume& Volume::setVisAttributes(const VisAttr& attr) const {
1157   if ( attr.isValid() ) {
1158     VisAttr::Object* vis = attr.data<VisAttr::Object>();
1159     TColor* col = vis->color;
1160     if ( col )   {
1161       int draw_style = vis->drawingStyle;
1162       int line_style = vis->lineStyle;
1163       int col_num    = col->GetNumber();
1164       int col_tr_num = vis->colortr->GetNumber();
1165       m_element->SetVisibility(vis->visible ? kTRUE : kFALSE);
1166       m_element->SetVisContainers(kTRUE);
1167       m_element->SetVisDaughters(vis->showDaughters ? kTRUE : kFALSE);
1168       printout(DEBUG,"setVisAttributes",
1169                "Set color %3d transparent(alpha:%.3f): %3d [%02X,%02X,%02X] DrawingStyle:%9s LineStyle:%6s for volume %s",
1170                col_num, vis->alpha, col_tr_num,
1171                int(255*col->GetRed()),
1172                int(255*col->GetGreen()),
1173                int(255*col->GetBlue()),
1174                draw_style == VisAttr::SOLID ? "Solid" : "Wireframe",
1175                line_style == VisAttr::SOLID ? "Solid" : "Dashed",
1176                name()
1177                );
1178       m_element->SetLineWidth(10);
1179       m_element->SetLineColor(col_num);
1180       m_element->SetFillColor(col_tr_num);
1181       if (draw_style == VisAttr::SOLID) {
1182         m_element->SetFillStyle(1001);   // Root: solid
1183 
1184 #if ROOT_VERSION_CODE >= ROOT_VERSION(6,29,0)
1185         // Set directly transparency to the volume, NOT to the material as for ROOT < 6.29
1186         m_element->ResetTransparency(Char_t((1.0-vis->alpha)*100));
1187 #else
1188         // As suggested by Valentin Volkl https://sft.its.cern.ch/jira/browse/DDFORHEP-20
1189         //
1190         // According to https://root.cern.ch/phpBB3/viewtopic.php?t=2309#p66013
1191         // a transparency>50 will make a volume invisible in the normal pad.
1192         // Hence: possibly restrict transparency to a maximum of 50.
1193         //        but let's see first how this behaves.
1194         m_element->SetTransparency(Char_t((1.0-vis->alpha)*100));
1195 #endif
1196       }
1197       else {
1198         printout(DEBUG,"setVisAttributes","Set to wireframe vis:%s",name());
1199         m_element->SetLineColor(kBlack);
1200         m_element->SetFillColor(0);
1201         m_element->SetFillStyle(0);      // Root: hollow
1202       }
1203       if (line_style == VisAttr::SOLID)  // Root line style: 1=solid, 2=dash, 3=dot, 4=dash-dot.
1204         m_element->SetLineStyle(1);
1205       else if (line_style == VisAttr::DASHED)
1206         m_element->SetLineStyle(2);
1207       else
1208         m_element->SetLineStyle(line_style);
1209     }
1210     else   {
1211       except("Volume","setVisAttributes: encountered valid, but badly initialized visattr: %s",attr.name());
1212     }
1213   }
1214   Volume::Object* o = _userExtension(*this);
1215   if ( o ) o->vis = attr;
1216   return *this;
1217 }
1218 
1219 /// Set Visualization attributes to the volume
1220 const Volume& Volume::setVisAttributes(const Detector& description, const std::string& nam) const {
1221   if (!nam.empty()) {
1222     VisAttr attr = description.visAttributes(nam);
1223     setVisAttributes(attr);
1224   }
1225   return *this;
1226 }
1227 
1228 /// Attach attributes to the volume
1229 const Volume& Volume::setAttributes(const Detector& description, const std::string& rg, const std::string& ls, const std::string& vis) const {
1230   if (!rg.empty())
1231     setRegion(description.region(rg));
1232   if (!ls.empty())
1233     setLimitSet(description.limitSet(ls));
1234   setVisAttributes(description, vis);
1235   return *this;
1236 }
1237 
1238 /// Access the visualisation attributes
1239 VisAttr Volume::visAttributes() const {
1240   Object* o = _data(*this, false);
1241   if (o) return o->vis;
1242   return VisAttr();
1243 }
1244 
1245 /// Set the volume's solid shape
1246 const Volume& Volume::setSolid(const Solid& sol) const {
1247   m_element->SetShape(sol);
1248   return *this;
1249 }
1250 
1251 /// Access to Solid (Shape)
1252 Solid Volume::solid() const {
1253   return Solid((*this)->GetShape());
1254 }
1255 
1256 /// Access the bounding box of the volume (if available)
1257 Box Volume::boundingBox() const {
1258   Box box = this->solid();
1259   if ( box.isValid() )   {
1260     return box;
1261   }
1262   else if ( !isValid() )   {
1263     except("dd4hep","Volume: Cannot access the bounding box of an invalid volume [Invalid Handle]!");
1264   }
1265   except("dd4hep","Volume: Cannot access the bounding box an object of type: %s shape: %s",
1266          this->ptr()->IsA()->GetName(), this->ptr()->GetShape()->IsA()->GetName());
1267   return box;
1268 }
1269 
1270 /// Set the regional attributes to the volume
1271 const Volume& Volume::setRegion(const Detector& description, const std::string& nam) const {
1272   if (!nam.empty()) {
1273     return setRegion(description.region(nam));
1274   }
1275   return *this;
1276 }
1277 
1278 /// Set the regional attributes to the volume
1279 const Volume& Volume::setRegion(const Region& obj) const {
1280   _data(*this)->region = obj;
1281   return *this;
1282 }
1283 
1284 /// Access to the handle to the region structure
1285 Region Volume::region() const {
1286   return _data(*this)->region;
1287 }
1288 
1289 /// Set the limits to the volume
1290 const Volume& Volume::setLimitSet(const Detector& description, const std::string& nam) const {
1291   if (!nam.empty()) {
1292     return setLimitSet(description.limitSet(nam));
1293   }
1294   return *this;
1295 }
1296 
1297 /// Set the limits to the volume
1298 const Volume& Volume::setLimitSet(const LimitSet& obj) const {
1299   _data(*this)->limits = obj;
1300   return *this;
1301 }
1302 
1303 /// Access to the limit set
1304 LimitSet Volume::limitSet() const {
1305   return _data(*this)->limits;
1306 }
1307 
1308 /// Assign the sensitive detector structure
1309 const Volume& Volume::setSensitiveDetector(const SensitiveDetector& obj) const {
1310   //cout << "Setting sensitive detector '" << obj.name() << "' to volume:" << ptr() << " " << name() << endl;
1311   _data(*this)->sens_det = obj;
1312   return *this;
1313 }
1314 
1315 /// Access to the handle to the sensitive detector
1316 Ref_t Volume::sensitiveDetector() const {
1317   const Object* o = _data(*this);
1318   return o->sens_det;
1319 }
1320 
1321 /// Accessor if volume is sensitive (ie. is attached to a sensitive detector)
1322 bool Volume::isSensitive() const {
1323   return _data(*this)->sens_det.isValid();
1324 }
1325 
1326 /// Check for existence of properties
1327 bool Volume::hasProperties()  const   {
1328   return _data(*this)->properties != nullptr;
1329 }
1330 
1331 /// Add Volume property (name-value pair)
1332 void Volume::addProperty(const std::string& nam, const std::string& val) const  {
1333   auto* o = _data(*this);
1334   if ( !o->properties )   {
1335     o->properties = new TList();
1336     o->properties->SetOwner();
1337   }
1338   TNamed *prop = (TNamed*)o->properties->FindObject(nam.c_str());
1339   if ( !prop )   {
1340     o->properties->Add(new TNamed(nam.c_str(), val.c_str()));
1341     return;
1342   }
1343   except("Volume::addProperty", "Volume: '%s' Property '%s' is already set!",
1344      ptr()->GetName(), nam.c_str());
1345 }
1346 
1347 /// Access property value. Returns default_value if the property is not present
1348 std::string Volume::getProperty(const std::string& nam, const std::string& default_val)   const {
1349   const auto* o = _data(*this);
1350   if ( !o->properties )   {
1351     return default_val;
1352   }
1353   TNamed *prop = (TNamed*)o->properties->FindObject(nam.c_str());
1354   if ( prop ) return prop->GetTitle();
1355   return default_val;
1356 }
1357 
1358 /// Constructor to be used when creating a new assembly object
1359 Assembly::Assembly(const std::string& nam) {
1360   m_element = _createTGeoVolumeAssembly(nam);
1361 }
1362 
1363 /// Constructor to be used when creating a new multi-volume object
1364 VolumeMulti::VolumeMulti(const std::string& nam, Material mat) {
1365   m_element = _createTGeoVolumeMulti(nam, mat.ptr());
1366 }
1367 
1368 /// Import volume from pointer as a result of Solid->Divide()
1369 void VolumeMulti::verifyVolumeMulti()   {
1370   if ( m_element )  {
1371     // This will lead to an exception if the type is not TGeoVolumeMulti
1372     TGeoVolumeMulti* multi = detail::safe_cast<TGeoVolumeMulti>::cast(m_element);
1373     if ( multi )  {
1374       import();
1375       return;
1376     }
1377     // Force a bad cast exception
1378     Handle<TGeoVolumeMulti> handle(m_element);
1379     if ( handle.isValid() )  {}
1380   }
1381 }
1382 
1383 /// Output mesh vertices to string
1384 std::string dd4hep::toStringMesh(PlacedVolume place, int prec)   {
1385   Volume       vol   = place->GetVolume();
1386   TGeoMatrix*  mat   = place->GetMatrix();
1387   Solid        sol   = vol.solid();
1388   std::stringstream os;
1389   struct _numbering {
1390     double adjust(double value)  const   {
1391       if ( std::abs(value) < TGeoShape::Tolerance() )
1392         return 0.0;
1393       return value/dd4hep::cm;
1394     }
1395   } _vertex;
1396 
1397   if ( vol->IsA() == TGeoVolumeAssembly::Class() )    {
1398     for(int i=0; i<vol->GetNdaughters(); ++i)  {
1399       os << toStringMesh(vol->GetNode(i), prec) << std::endl;
1400     }
1401     return os.str();
1402   }
1403 
1404   // Prints shape parameters
1405   int nvert = 0, nsegs = 0, npols = 0;
1406   sol->GetMeshNumbers(nvert, nsegs, npols);
1407   Double_t* points = new Double_t[3*nvert];
1408   sol->SetPoints(points);
1409 
1410   os << std::setw(16) << std::left << sol->IsA()->GetName()
1411      << " " << nvert << " Mesh-points:" << std::endl;
1412   os << std::setw(16) << std::left << sol->IsA()->GetName() << " " << sol->GetName()
1413      << " N(mesh)=" << sol->GetNmeshVertices()
1414      << "  N(vert)=" << nvert << "  N(seg)=" << nsegs << "  N(pols)=" << npols << std::endl;
1415     
1416   for(int i=0; i<nvert; ++i)   {
1417     Double_t* p = points + 3*i;
1418     Double_t global[3], local[3] = {p[0], p[1], p[2]};
1419     mat->LocalToMaster(local, global);
1420     os << std::setw(16) << std::left << sol->IsA()->GetName() << " " << std::setw(3) << std::left << i
1421        << " Local  ("  << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(local[0])
1422        << ", "         << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(local[1])
1423        << ", "         << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(local[2])
1424        << ") Global (" << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(global[0])
1425        << ", "         << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(global[1])
1426        << ", "         << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(global[2])
1427        << ")" << std::endl;
1428   }
1429   Box box = sol;
1430   const Double_t* org = box->GetOrigin();
1431   os << std::setw(16) << std::left << sol->IsA()->GetName()
1432      << " Bounding box: "
1433      << " dx="        << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(box->GetDX())
1434      << " dy="        << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(box->GetDY())
1435      << " dz="        << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(box->GetDZ())
1436      << " Origin: x=" << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(org[0])
1437      << " y="         << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(org[1])
1438      << " z="         << std::setw(7) << std::setprecision(prec) << std::fixed << std::right << _vertex.adjust(org[2])
1439      << std::endl;
1440   
1441   /// -------------------- DONE --------------------
1442   delete [] points;
1443   return os.str();
1444 }
1445