Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:17:30

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/Shapes.h>
0016 #include <DD4hep/Volumes.h>
0017 #include <DD4hep/Plugins.h>
0018 #include <DD4hep/Printout.h>
0019 #include <DD4hep/Detector.h>
0020 #include <DD4hep/DD4hepUnits.h>
0021 #include <DD4hep/PropertyTable.h>
0022 #include <DD4hep/DetectorTools.h>
0023 #include <DD4hep/detail/ShapesInterna.h>
0024 #include <DD4hep/detail/ObjectsInterna.h>
0025 #include <DD4hep/detail/DetectorInterna.h>
0026 
0027 #include <DDG4/Geant4Field.h>
0028 #include <DDG4/Geant4Helpers.h>
0029 #include <DDG4/Geant4Converter.h>
0030 #include <DDG4/Geant4UserLimits.h>
0031 #include <DDG4/Geant4AssemblyVolume.h>
0032 #include <DDG4/Geant4PlacementParameterisation.h>
0033 #include "Geant4ShapeConverter.h"
0034 
0035 // ROOT includes
0036 #include <TClass.h>
0037 #include <TTimeStamp.h>
0038 #include <TGeoBoolNode.h>
0039 
0040 // Geant4 include files
0041 #include <G4Version.hh>
0042 #include <G4VisAttributes.hh>
0043 #include <G4PVParameterised.hh>
0044 #include <G4ProductionCuts.hh>
0045 #include <G4VUserRegionInformation.hh>
0046 
0047 #include <G4Box.hh>
0048 #include <G4Tubs.hh>
0049 #include <G4Ellipsoid.hh>
0050 #include <G4UnionSolid.hh>
0051 #include <G4ReflectedSolid.hh>
0052 #include <G4SubtractionSolid.hh>
0053 #include <G4IntersectionSolid.hh>
0054 #include <G4VSensitiveDetector.hh>
0055 
0056 #include <G4Region.hh>
0057 #include <G4Element.hh>
0058 #include <G4Isotope.hh>
0059 #include <G4Material.hh>
0060 #include <G4UserLimits.hh>
0061 #include <G4RegionStore.hh>
0062 #include <G4FieldManager.hh>
0063 #include <G4LogicalVolume.hh>
0064 #include <G4OpticalSurface.hh>
0065 #include <G4ReflectionFactory.hh>
0066 #include <G4LogicalSkinSurface.hh>
0067 #include <G4ElectroMagneticField.hh>
0068 #include <G4LogicalBorderSurface.hh>
0069 #include <G4MaterialPropertiesTable.hh>
0070 #if G4VERSION_NUMBER >= 1040
0071 #include <G4MaterialPropertiesIndex.hh>
0072 #endif
0073 #include <G4ScaledSolid.hh>
0074 #include <CLHEP/Units/SystemOfUnits.h>
0075 
0076 // C/C++ include files
0077 #include <iostream>
0078 #include <iomanip>
0079 #include <sstream>
0080 #include <limits>
0081 
0082 namespace units = dd4hep;
0083 using namespace dd4hep::sim;
0084 using namespace dd4hep;
0085 
0086 namespace {
0087 
0088   static constexpr const double CM_2_MM = (CLHEP::centimeter/dd4hep::centimeter);
0089   static constexpr const char* GEANT4_TAG_IGNORE = "Geant4-ignore";
0090   static constexpr const char* GEANT4_TAG_PLUGIN = "Geant4-plugin";
0091   static constexpr const char* GEANT4_TAG_BIRKSCONSTANT    = "BirksConstant";
0092   static constexpr const char* GEANT4_TAG_MEE              = "MeanExcitationEnergy";
0093   static constexpr const char* GEANT4_TAG_ENE_PER_ION_PAIR = "MeanEnergyPerIonPair";
0094 
0095   static std::string indent = "";
0096 
0097   template <typename O, typename C, typename F> void handleRefs(const O* o, const C& c, F pmf) {
0098     for (typename C::const_iterator i = c.begin(); i != c.end(); ++i) {
0099       //(o->*pmf)((*i)->GetName(), *i);
0100       (o->*pmf)("", *i);
0101     }
0102   }
0103 
0104   template <typename O, typename C, typename F> void handle(const O* o, const C& c, F pmf) {
0105     for (typename C::const_iterator i = c.begin(); i != c.end(); ++i) {
0106       (o->*pmf)((*i)->GetName(), *i);
0107     }
0108   }
0109 
0110   template <typename O, typename F> void handleArray(const O* o, const TObjArray* c, F pmf) {
0111     TObjArrayIter arr(c);
0112     for(TObject* i = arr.Next(); i; i=arr.Next())
0113       (o->*pmf)(i);
0114   }
0115 
0116   template <typename O, typename C, typename F> void handleMap(const O* o, const C& c, F pmf) {
0117     for (typename C::const_iterator i = c.begin(); i != c.end(); ++i)
0118       (o->*pmf)((*i).first, (*i).second);
0119   }
0120 
0121   template <typename O, typename C, typename F> void handleRMap(const O* o, const C& c, F pmf) {
0122     for (typename C::const_reverse_iterator i = c.rbegin(); i != c.rend(); ++i)  {
0123       //cout << "Handle RMAP [ " << (*i).first << " ]" << std::endl;
0124       handle(o, i->second, pmf);
0125     }
0126   }
0127   template <typename O, typename C, typename F> void handleRMap_(const O* o, const C& c, F pmf) {
0128     for (typename C::const_iterator i = c.begin(); i != c.end(); ++i)  {
0129       const auto& cc = (*i).second;
0130       for (const auto& j : cc)   {
0131         (o->*pmf)(j);
0132       }
0133     }
0134   }
0135 
0136   std::string make_NCName(const std::string& in)   {
0137     std::string res = detail::str_replace(in, "/", "_");
0138     res = detail::str_replace(res, "#", "_");
0139     return res;
0140   }
0141 
0142   bool is_left_handed(const TGeoMatrix* m)   {
0143     const Double_t* r = m->GetRotationMatrix();
0144     if ( r )    {
0145       Double_t det =
0146         r[0]*r[4]*r[8] + r[3]*r[7]*r[2] + r[6]*r[1]*r[5] -
0147         r[2]*r[4]*r[6] - r[5]*r[7]*r[0] - r[8]*r[1]*r[3];
0148       return det < 0e0;
0149     }
0150     return false;
0151   }
0152 
0153   class G4UserRegionInformation : public G4VUserRegionInformation {
0154   public:
0155     Region region;
0156     double threshold;
0157     bool   storeSecondaries;
0158     G4UserRegionInformation()
0159       : threshold(0.0), storeSecondaries(false) {
0160     }
0161     virtual ~G4UserRegionInformation() {
0162     }
0163     virtual void Print() const {
0164       if (region.isValid())
0165         printout(DEBUG, "Region", "Name:%s", region.name());
0166     }
0167   };
0168 
0169   std::pair<double,double> g4PropertyConversion(int index)   {
0170 #if G4VERSION_NUMBER >= 1040
0171     switch(index)  {
0172     case kRINDEX:                         return std::make_pair(CLHEP::keV/units::keV, 1.0);
0173     case kREFLECTIVITY:                   return std::make_pair(CLHEP::keV/units::keV, 1.0);
0174     case kREALRINDEX:                     return std::make_pair(CLHEP::keV/units::keV, 1.0);
0175     case kIMAGINARYRINDEX:                return std::make_pair(CLHEP::keV/units::keV, 1.0);
0176     case kEFFICIENCY:                     return std::make_pair(CLHEP::keV/units::keV, 1.0);
0177     case kTRANSMITTANCE:                  return std::make_pair(CLHEP::keV/units::keV, 1.0);
0178     case kSPECULARLOBECONSTANT:           return std::make_pair(CLHEP::keV/units::keV, 1.0);
0179     case kSPECULARSPIKECONSTANT:          return std::make_pair(CLHEP::keV/units::keV, 1.0);
0180     case kBACKSCATTERCONSTANT:            return std::make_pair(CLHEP::keV/units::keV, 1.0);
0181     case kGROUPVEL:                       return std::make_pair(CLHEP::keV/units::keV, (CLHEP::m/CLHEP::s)/(units::m/units::s));  // meter/second
0182     case kMIEHG:                          return std::make_pair(CLHEP::keV/units::keV, CLHEP::m/units::m);
0183     case kRAYLEIGH:                       return std::make_pair(CLHEP::keV/units::keV, CLHEP::m/units::m);  // ??? says its a length
0184     case kWLSCOMPONENT:                   return std::make_pair(CLHEP::keV/units::keV, 1.0);
0185     case kWLSABSLENGTH:                   return std::make_pair(CLHEP::keV/units::keV, CLHEP::m/units::m);
0186     case kABSLENGTH:                      return std::make_pair(CLHEP::keV/units::keV, CLHEP::m/units::m);
0187 #if G4VERSION_NUMBER >= 1100
0188     case kWLSCOMPONENT2:                  return std::make_pair(CLHEP::keV/units::keV, 1.0);
0189     case kWLSABSLENGTH2:                  return std::make_pair(CLHEP::keV/units::keV, CLHEP::m/units::m);
0190     case kSCINTILLATIONCOMPONENT1:        return std::make_pair(CLHEP::keV/units::keV, units::keV/CLHEP::keV);
0191     case kSCINTILLATIONCOMPONENT2:        return std::make_pair(CLHEP::keV/units::keV, units::keV/CLHEP::keV);
0192     case kSCINTILLATIONCOMPONENT3:        return std::make_pair(CLHEP::keV/units::keV, units::keV/CLHEP::keV);
0193 #else
0194     case kFASTCOMPONENT:                  return std::make_pair(CLHEP::keV/units::keV, 1.0);
0195     case kSLOWCOMPONENT:                  return std::make_pair(CLHEP::keV/units::keV, 1.0);
0196 #endif
0197     case kPROTONSCINTILLATIONYIELD:       return std::make_pair(CLHEP::keV/units::keV, units::keV/CLHEP::keV); // Yields: 1/energy
0198     case kDEUTERONSCINTILLATIONYIELD:     return std::make_pair(CLHEP::keV/units::keV, units::keV/CLHEP::keV);
0199     case kTRITONSCINTILLATIONYIELD:       return std::make_pair(CLHEP::keV/units::keV, units::keV/CLHEP::keV);
0200     case kALPHASCINTILLATIONYIELD:        return std::make_pair(CLHEP::keV/units::keV, units::keV/CLHEP::keV);
0201     case kIONSCINTILLATIONYIELD:          return std::make_pair(CLHEP::keV/units::keV, units::keV/CLHEP::keV);
0202     case kELECTRONSCINTILLATIONYIELD:     return std::make_pair(CLHEP::keV/units::keV, units::keV/CLHEP::keV);
0203     default:
0204       break;
0205     }
0206     printout(FATAL,"Geant4Converter", "+++ Cannot convert material property with index: %d", index);
0207 #else
0208     printout(FATAL,"Geant4Converter", "+++ Cannot convert material property with index: %d [Need Geant4 > 10.03]", index);
0209 #endif
0210     return std::make_pair(0e0,0e0);
0211   }
0212 
0213   double g4ConstPropertyConversion(int index)   {
0214 #if G4VERSION_NUMBER >= 1040
0215     switch(index)   {
0216     case kSURFACEROUGHNESS:            return CLHEP::m/units::m;                             // Length
0217     case kISOTHERMAL_COMPRESSIBILITY:  return (CLHEP::m3/CLHEP::keV)/(units::m3/CLHEP::keV); // Volume/Energy
0218     case kRS_SCALE_FACTOR:             return 1.0;  // ??
0219     case kWLSMEANNUMBERPHOTONS:        return 1.0;  // ??
0220     case kWLSTIMECONSTANT:             return CLHEP::second/units::second;                   // Time
0221     case kMIEHG_FORWARD:               return 1.0;
0222     case kMIEHG_BACKWARD:              return 1.0;
0223     case kMIEHG_FORWARD_RATIO:         return 1.0;
0224     case kSCINTILLATIONYIELD:          return units::keV/CLHEP::keV;                         // Energy
0225     case kRESOLUTIONSCALE:             return 1.0;
0226     case kFERMIPOT:                    return CLHEP::keV/units::keV;                         // Energy
0227     case kDIFFUSION:                   return 1.0;
0228     case kSPINFLIP:                    return 1.0;
0229     case kLOSS:                        return 1.0;  // ??
0230     case kLOSSCS:                      return CLHEP::barn/units::barn;  // ??
0231     case kABSCS:                       return CLHEP::barn/units::barn;  // ??
0232     case kSCATCS:                      return CLHEP::barn/units::barn;  // ??
0233     case kMR_NBTHETA:                  return 1.0;
0234     case kMR_NBE:                      return 1.0;
0235     case kMR_RRMS:                     return 1.0;  // ??
0236     case kMR_CORRLEN:                  return CLHEP::m/units::m;                             // Length
0237     case kMR_THETAMIN:                 return 1.0;
0238     case kMR_THETAMAX:                 return 1.0;
0239     case kMR_EMIN:                     return CLHEP::keV/units::keV;                         // Energy
0240     case kMR_EMAX:                     return CLHEP::keV/units::keV;                         // Energy
0241     case kMR_ANGNOTHETA:               return 1.0;
0242     case kMR_ANGNOPHI:                 return 1.0;
0243     case kMR_ANGCUT:                   return 1.0;
0244 
0245 #if G4VERSION_NUMBER >= 1100
0246     case kSCINTILLATIONTIMECONSTANT1:  return CLHEP::second/units::second;                   // Time
0247     case kSCINTILLATIONTIMECONSTANT2:  return CLHEP::second/units::second;                   // Time
0248     case kSCINTILLATIONTIMECONSTANT3:  return CLHEP::second/units::second;                   // Time
0249     case kSCINTILLATIONRISETIME1:      return CLHEP::second/units::second;                   // Time
0250     case kSCINTILLATIONRISETIME2:      return CLHEP::second/units::second;                   // Time
0251     case kSCINTILLATIONRISETIME3:      return CLHEP::second/units::second;                   // Time
0252     case kSCINTILLATIONYIELD1:         return 1.0;
0253     case kSCINTILLATIONYIELD2:         return 1.0;
0254     case kSCINTILLATIONYIELD3:         return 1.0;
0255     case kPROTONSCINTILLATIONYIELD1:   return 1.0;
0256     case kPROTONSCINTILLATIONYIELD2:   return 1.0;
0257     case kPROTONSCINTILLATIONYIELD3:   return 1.0;
0258     case kDEUTERONSCINTILLATIONYIELD1: return 1.0;
0259     case kDEUTERONSCINTILLATIONYIELD2: return 1.0;
0260     case kDEUTERONSCINTILLATIONYIELD3: return 1.0;
0261     case kALPHASCINTILLATIONYIELD1:    return 1.0;
0262     case kALPHASCINTILLATIONYIELD2:    return 1.0;
0263     case kALPHASCINTILLATIONYIELD3:    return 1.0;
0264     case kIONSCINTILLATIONYIELD1:      return 1.0;
0265     case kIONSCINTILLATIONYIELD2:      return 1.0;
0266     case kIONSCINTILLATIONYIELD3:      return 1.0;
0267     case kELECTRONSCINTILLATIONYIELD1: return 1.0;
0268     case kELECTRONSCINTILLATIONYIELD2: return 1.0;
0269     case kELECTRONSCINTILLATIONYIELD3: return 1.0;
0270 #else
0271     case kFASTTIMECONSTANT:            return CLHEP::second/units::second;                   // Time
0272     case kFASTSCINTILLATIONRISETIME:   return CLHEP::second/units::second;                   // Time
0273     case kSLOWTIMECONSTANT:            return CLHEP::second/units::second;                   // Time
0274     case kSLOWSCINTILLATIONRISETIME:   return CLHEP::second/units::second;                   // Time
0275     case kYIELDRATIO:                  return 1.0;
0276 #endif
0277     default:
0278       break;
0279     }
0280     printout(FATAL,"Geant4Converter", "+++ Cannot convert CONST material property with index: %d", index);
0281 #else
0282     printout(FATAL,"Geant4Converter", "+++ Cannot convert material property with index: %d [Need Geant4 > 10.03]", index);
0283 #endif
0284     return 0.0;
0285   }
0286 }
0287 
0288 /// Initializing Constructor
0289 Geant4Converter::Geant4Converter(const Detector& description_ref)
0290   : Geant4Mapping(description_ref), checkOverlaps(true) {
0291   this->Geant4Mapping::init();
0292   m_propagateRegions = true;
0293   outputLevel = PrintLevel(printLevel() - 1);
0294 }
0295 
0296 /// Initializing Constructor
0297 Geant4Converter::Geant4Converter(const Detector& description_ref, PrintLevel level)
0298   : Geant4Mapping(description_ref), outputLevel(level)  {
0299   this->Geant4Mapping::init();
0300   m_propagateRegions = true;
0301 }
0302 
0303 /// Standard destructor
0304 Geant4Converter::~Geant4Converter() {
0305 }
0306 
0307 /// Handle the conversion of isotopes
0308 void* Geant4Converter::handleIsotope(const std::string& /* name */, const TGeoIsotope* iso) const {
0309   G4Isotope* g4i = data().g4Isotopes[iso];
0310   if ( !g4i )  {
0311     double a_conv = (CLHEP::g / CLHEP::mole);
0312     g4i = new G4Isotope(iso->GetName(), iso->GetZ(), iso->GetN(), iso->GetA()*a_conv);
0313     printout(debugElements ? ALWAYS : outputLevel,
0314              "Geant4Converter", "++ Created G4 Isotope %s from data: Z=%d N=%d A=%.3f [g/mole]",
0315              iso->GetName(), iso->GetZ(), iso->GetN(), iso->GetA());
0316     data().g4Isotopes[iso] = g4i;
0317   }
0318   return g4i;
0319 }
0320 
0321 /// Handle the conversion of elements
0322 void* Geant4Converter::handleElement(const std::string& name, const Atom element) const {
0323   G4Element* g4e = data().g4Elements[element];
0324   if ( !g4e ) {
0325     PrintLevel lvl = debugElements ? ALWAYS : outputLevel;
0326     if (element->GetNisotopes() > 0) {
0327       g4e = new G4Element(name, element->GetTitle(), element->GetNisotopes());
0328       for (int i = 0, n = element->GetNisotopes(); i < n; ++i) {
0329         TGeoIsotope* iso = element->GetIsotope(i);
0330         G4Isotope* g4iso = (G4Isotope*)handleIsotope(iso->GetName(), iso);
0331         g4e->AddIsotope(g4iso, element->GetRelativeAbundance(i));
0332       }
0333     }
0334     else {
0335       // This adds in Geant4 the natural isotopes, which we normally do not want. We want to steer it outselves.
0336       double a_conv = (CLHEP::g / CLHEP::mole);
0337       g4e = new G4Element(element->GetTitle(), name, element->Z(), element->A()*a_conv);
0338       printout(lvl, "Geant4Converter", "++ Created G4 Isotope %s from data: Z=%d N=%d A=%.3f [g/mole]",
0339                element->GetName(), element->Z(), element->N(), element->A());
0340     }
0341     std::stringstream str;
0342     str << (*g4e) << std::endl;
0343     printout(lvl, "Geant4Converter", "++ Created G4 element %s", str.str().c_str());
0344     data().g4Elements[element] = g4e;
0345   }
0346   return g4e;
0347 }
0348 
0349 /// Dump material in GDML format to output stream
0350 void* Geant4Converter::handleMaterial(const std::string& name, Material medium) const {
0351   Geant4GeometryInfo& info = data();
0352   G4Material*         mat  = info.g4Materials[medium];
0353   if ( !mat )  {
0354     PrintLevel    lvl      = debugMaterials ? ALWAYS : outputLevel;
0355     TGeoMaterial* material = medium->GetMaterial();
0356     G4State       state    = kStateUndefined;
0357     double        density  = material->GetDensity() * (CLHEP::gram / CLHEP::cm3);
0358     if ( density < 1e-25 )
0359       density = 1e-25;
0360     switch ( material->GetState() ) {
0361     case TGeoMaterial::kMatStateSolid:
0362       state = kStateSolid;
0363       break;
0364     case TGeoMaterial::kMatStateLiquid:
0365       state = kStateLiquid;
0366       break;
0367     case TGeoMaterial::kMatStateGas:
0368       state = kStateGas;
0369       break;
0370     default:
0371     case TGeoMaterial::kMatStateUndefined:
0372       state = kStateUndefined;
0373       break;
0374     }
0375     printout(lvl,"Geant4Material","+++ Setting up material %s", name.c_str());
0376     if ( material->IsMixture() )  {
0377       double A_total = 0.0;
0378       double W_total = 0.0;
0379       TGeoMixture* mix = (TGeoMixture*) material;
0380       int    nElements = mix->GetNelements();
0381       mat = new G4Material(name, density, nElements, state, 
0382                            material->GetTemperature(), material->GetPressure());
0383       for (int i = 0; i < nElements; ++i)  {
0384         A_total += (mix->GetAmixt())[i];
0385         W_total += (mix->GetWmixt())[i];
0386       }
0387       for (int i = 0; i < nElements; ++i) {
0388         TGeoElement* e = mix->GetElement(i);
0389         G4Element* g4e = (G4Element*) handleElement(e->GetName(), Atom(e));
0390         if (!g4e) {
0391           printout(ERROR, name, 
0392                    "Missing element component %s for material %s. A=%f W=%f", 
0393                    e->GetName(), mix->GetName(), A_total, W_total);
0394         }
0395         //mat->AddElement(g4e, (mix->GetAmixt())[i] / A_total);
0396         mat->AddElement(g4e, (mix->GetWmixt())[i] / W_total);
0397       }
0398     }
0399     else {
0400       double z = material->GetZ(), a = material->GetA();
0401       if ( z < 1.0000001 ) z = 1.0;
0402       if ( a < 0.5000001 ) a = 1.0;
0403       mat = new G4Material(name, z, a, density, state, 
0404                            material->GetTemperature(), material->GetPressure());
0405     }
0406 
0407     std::string plugin_name { };
0408     double value = 0e0;
0409     double ionisation_mee = -2e100;
0410     double ionisation_birks_constant = -2e100;
0411     double ionisation_ene_per_ion_pair = -2e100;
0412 
0413     /// Attach the material properties if any
0414     G4MaterialPropertiesTable* tab = 0;
0415     TListIter propIt(&material->GetProperties());
0416     for(TObject* obj=propIt.Next(); obj; obj = propIt.Next())  {
0417       std::string       exc_str;
0418       TNamed*      named  = (TNamed*)obj;
0419       TGDMLMatrix* matrix = info.manager->GetGDMLMatrix(named->GetTitle());
0420       const char*  cptr   = ::strstr(matrix->GetName(), GEANT4_TAG_IGNORE);
0421       if ( nullptr != cptr )   {
0422         printout(INFO,name,"++ Ignore property %s [%s]. Not Suitable for Geant4.",
0423                  matrix->GetName(), matrix->GetTitle());
0424         continue;
0425       }
0426       cptr = ::strstr(matrix->GetTitle(), GEANT4_TAG_IGNORE);
0427       if ( nullptr != cptr )   {
0428         printout(INFO,name,"++ Ignore property %s [%s]. Not Suitable for Geant4.",
0429                  matrix->GetName(), matrix->GetTitle());
0430         continue;
0431       }
0432       Geant4GeometryInfo::PropertyVector* v =
0433         (Geant4GeometryInfo::PropertyVector*)handleMaterialProperties(matrix);
0434       if ( nullptr == v )   {
0435         except("Geant4Converter", "++ FAILED to create G4 material %s [Cannot convert property:%s]",
0436                material->GetName(), named->GetName());
0437       }
0438       if ( nullptr == tab )  {
0439         tab = new G4MaterialPropertiesTable();
0440         mat->SetMaterialPropertiesTable(tab);
0441       }
0442       int idx = -1;
0443       try   {
0444         idx = tab->GetPropertyIndex(named->GetName());
0445       }
0446       catch(const std::exception& e)   {
0447         exc_str = e.what();
0448         idx = -1;
0449       }
0450       catch(...)   {
0451         idx = -1;
0452       }
0453       if ( idx < 0 )   {
0454         printout(ERROR, "Geant4Converter",
0455                  "++ UNKNOWN Geant4 Property: %-20s %s [IGNORED]",
0456                  exc_str.c_str(), named->GetName());
0457         continue;
0458       }
0459       // We need to convert the property from TGeo units to Geant4 units
0460       auto conv = g4PropertyConversion(idx);
0461       std::vector<double> bins(v->bins), vals(v->values);
0462       for(std::size_t i=0, count=bins.size(); i<count; ++i)
0463         bins[i] *= conv.first, vals[i] *= conv.second;
0464 
0465       G4MaterialPropertyVector* vec =
0466         new G4MaterialPropertyVector(&bins[0], &vals[0], bins.size());
0467       tab->AddProperty(named->GetName(), vec);
0468       printout(lvl, name, "++      Property: %-20s [%ld x %ld] -> %s ",
0469                named->GetName(), matrix->GetRows(), matrix->GetCols(), named->GetTitle());
0470       for(std::size_t i=0, count=v->bins.size(); i<count; ++i)
0471         printout(lvl, name, "  Geant4: %s %8.3g [MeV]  TGeo: %8.3g [GeV] Conversion: %8.3g",
0472                  named->GetName(), bins[i], v->bins[i], conv.first);
0473     }
0474 
0475     /// Attach the material properties if any
0476     TListIter cpropIt(&material->GetConstProperties());
0477     for(TObject* obj=cpropIt.Next(); obj; obj = cpropIt.Next())  {
0478       std::string  exc_str;
0479       Bool_t     err = kFALSE;
0480       TNamed*  named = (TNamed*)obj;
0481 
0482       const char*  cptr = ::strstr(named->GetName(), GEANT4_TAG_IGNORE);
0483       if ( nullptr != cptr )   {
0484         printout(INFO, name, "++ Ignore CONST property %s [%s].",
0485                  named->GetName(), named->GetTitle());
0486         continue;
0487       }
0488       cptr = ::strstr(named->GetTitle(), GEANT4_TAG_IGNORE);
0489       if ( nullptr != cptr )   {
0490         printout(INFO, name,"++ Ignore CONST property %s [%s].",
0491                  named->GetName(), named->GetTitle());
0492         continue;
0493       }
0494       cptr = ::strstr(named->GetName(), GEANT4_TAG_PLUGIN);
0495       if ( nullptr != cptr )   {
0496         printout(INFO, name, "++ Ignore CONST property %s [%s]  --> Plugin.",
0497                  named->GetName(), named->GetTitle());
0498         plugin_name = named->GetTitle();
0499         continue;
0500       }
0501       cptr = ::strstr(named->GetName(), GEANT4_TAG_BIRKSCONSTANT);
0502       if ( nullptr != cptr )   {
0503         err = kFALSE;
0504         value = material->GetConstProperty(GEANT4_TAG_BIRKSCONSTANT,&err);
0505         if ( err == kFALSE ) ionisation_birks_constant = value * (CLHEP::mm/CLHEP::MeV)/(units::mm/units::MeV);
0506         continue;
0507       }
0508       cptr = ::strstr(named->GetName(), GEANT4_TAG_MEE);
0509       if ( nullptr != cptr )   {
0510         err = kFALSE;
0511         value = material->GetConstProperty(GEANT4_TAG_MEE, &err);
0512         if ( err == kFALSE ) ionisation_mee = value * (CLHEP::MeV/units::MeV);
0513         continue;
0514       }
0515       cptr = ::strstr(named->GetName(), GEANT4_TAG_ENE_PER_ION_PAIR);
0516       if ( nullptr != cptr )   {
0517         err = kFALSE;
0518         value = material->GetConstProperty(GEANT4_TAG_ENE_PER_ION_PAIR,&err);
0519         if ( err == kFALSE ) ionisation_ene_per_ion_pair = value * (CLHEP::MeV/units::MeV);
0520         continue;
0521       }
0522 
0523       err = kFALSE;
0524       value = info.manager->GetProperty(named->GetTitle(), &err);
0525       if ( err != kFALSE )   {
0526         except(name,
0527                "++ FAILED to create G4 material %s [Cannot convert const property: %s]",
0528                material->GetName(), named->GetName());
0529       }
0530       if ( nullptr == tab )  {
0531         tab = new G4MaterialPropertiesTable();
0532         mat->SetMaterialPropertiesTable(tab);
0533       }
0534       int idx = -1;
0535       try   {
0536         idx = tab->GetConstPropertyIndex(named->GetName());
0537       }
0538       catch(const std::exception& e)   {
0539         exc_str = e.what();
0540         idx = -1;
0541       }
0542       catch(...)   {
0543         idx = -1;
0544       }
0545       if ( idx < 0 )   {
0546         printout(ERROR, name,
0547                  "++ UNKNOWN Geant4 CONST Property: %-20s %s [IGNORED]",
0548                  exc_str.c_str(), named->GetName());
0549         continue;
0550       }
0551       // We need to convert the property from TGeo units to Geant4 units
0552       double conv = g4ConstPropertyConversion(idx);
0553       printout(lvl, name, "++      CONST Property: %-20s %g ", named->GetName(), value);
0554       tab->AddConstProperty(named->GetName(), value * conv);
0555     }
0556     //
0557     // Set Birk's constant if it was supplied in the material table of the TGeoMaterial
0558     auto* ionisation = mat->GetIonisation();
0559     std::stringstream str;
0560     str << (*mat);
0561     if ( ionisation )   {
0562       if ( ionisation_birks_constant > 0e0 )   {
0563         ionisation->SetBirksConstant(ionisation_birks_constant);
0564       }
0565       if ( ionisation_mee > -1e100 )   {
0566         ionisation->SetMeanExcitationEnergy(ionisation_mee);
0567       }
0568       if ( ionisation_ene_per_ion_pair > 0e0 )   {
0569         ionisation->SetMeanEnergyPerIonPair(ionisation_ene_per_ion_pair);
0570       }
0571       str << "          log(MEE): " << std::setprecision(4) << ionisation->GetLogMeanExcEnergy();
0572       if ( ionisation_birks_constant > 0e0 )
0573         str << "  Birk's constant: " << std::setprecision(4) << ionisation->GetBirksConstant() << " [mm/MeV]";
0574       if ( ionisation_ene_per_ion_pair > 0e0 )
0575         str << "  Mean Energy Per Ion Pair: " << std::setprecision(4) << ionisation->GetMeanEnergyPerIonPair()/CLHEP::eV << " [eV]";
0576     }
0577     else  {
0578       str << "          No ionisation parameters availible.";
0579     }
0580     printout(lvl, name, "++ Created G4 material %s", str.str().c_str());
0581 
0582     if ( !plugin_name.empty() )    {
0583       // Call plugin to create extended material if requested
0584       Detector* det = const_cast<Detector*>(&m_detDesc);
0585       G4Material* extended_mat = PluginService::Create<G4Material*>(plugin_name, det, medium, mat);
0586       if ( !extended_mat )   {
0587         except("G4Cnv::material["+name+"]","++ FATAL Failed to call plugin to create material.");
0588       }
0589       mat = extended_mat;
0590     }
0591     info.g4Materials[medium] = mat;
0592   }
0593   return mat;
0594 }
0595 
0596 /// Dump solid in GDML format to output stream
0597 void* Geant4Converter::handleSolid(const std::string& name, const TGeoShape* shape) const {
0598   G4VSolid* solid = nullptr;
0599   if ( shape ) {
0600     if ( nullptr != (solid = data().g4Solids[shape]) )   {
0601       return solid;
0602     }
0603     TClass*    isa = shape->IsA();
0604     PrintLevel lvl = debugShapes ? ALWAYS : outputLevel;
0605     if (isa == TGeoShapeAssembly::Class()) {
0606       // Assemblies have no corresponding 'shape' in Geant4. Ignore the shape translation.
0607       // It does not harm, since this 'shape' is never accessed afterwards.
0608       data().g4Solids[shape] = solid = convertShape<TGeoShapeAssembly>(shape);
0609       return solid;
0610     }
0611     else if (isa == TGeoBBox::Class())
0612       solid = convertShape<TGeoBBox>(shape);
0613     else if (isa == TGeoTube::Class())
0614       solid = convertShape<TGeoTube>(shape);
0615     else if (isa == TGeoTubeSeg::Class())
0616       solid = convertShape<TGeoTubeSeg>(shape);
0617     else if (isa == TGeoCtub::Class())
0618       solid = convertShape<TGeoCtub>(shape);
0619     else if (isa == TGeoEltu::Class())
0620       solid = convertShape<TGeoEltu>(shape);
0621     else if (isa == TwistedTubeObject::Class())
0622       solid = convertShape<TwistedTubeObject>(shape);
0623     else if (isa == TGeoTrd1::Class())
0624       solid = convertShape<TGeoTrd1>(shape);
0625     else if (isa == TGeoTrd2::Class())
0626       solid = convertShape<TGeoTrd2>(shape);
0627     else if (isa == TGeoHype::Class())
0628       solid = convertShape<TGeoHype>(shape);
0629     else if (isa == TGeoXtru::Class())
0630       solid = convertShape<TGeoXtru>(shape);
0631     else if (isa == TGeoPgon::Class())
0632       solid = convertShape<TGeoPgon>(shape);
0633     else if (isa == TGeoPcon::Class())
0634       solid = convertShape<TGeoPcon>(shape);
0635     else if (isa == TGeoCone::Class())
0636       solid = convertShape<TGeoCone>(shape);
0637     else if (isa == TGeoConeSeg::Class())
0638       solid = convertShape<TGeoConeSeg>(shape);
0639     else if (isa == TGeoParaboloid::Class())
0640       solid = convertShape<TGeoParaboloid>(shape);
0641     else if (isa == TGeoSphere::Class())
0642       solid = convertShape<TGeoSphere>(shape);
0643     else if (isa == TGeoTorus::Class())
0644       solid = convertShape<TGeoTorus>(shape);
0645     else if (isa == TGeoTrap::Class())
0646       solid = convertShape<TGeoTrap>(shape);
0647     else if (isa == TGeoArb8::Class()) 
0648       solid = convertShape<TGeoArb8>(shape);
0649     else if (isa == TGeoPara::Class())
0650       solid = convertShape<TGeoPara>(shape);
0651     else if (isa == TGeoTessellated::Class()) 
0652       solid = convertShape<TGeoTessellated>(shape);
0653     else if (isa == TGeoScaledShape::Class())  {
0654       TGeoScaledShape* sh   = (TGeoScaledShape*) shape;
0655       TGeoShape*       sol  = sh->GetShape();
0656       if ( sol->IsA() == TGeoShapeAssembly::Class() )  {
0657         return solid;
0658       }
0659       const double*    vals = sh->GetScale()->GetScale();
0660       G4Scale3D        scal(vals[0], vals[1], vals[2]);
0661       G4VSolid* g4solid = (G4VSolid*)handleSolid(sol->GetName(), sol);
0662       if ( scal.xx()>0e0 && scal.yy()>0e0 && scal.zz()>0e0 )
0663         solid = new G4ScaledSolid(sh->GetName(), g4solid, scal);
0664       else
0665         solid = new G4ReflectedSolid(g4solid->GetName()+"_refl", g4solid, scal);
0666     }
0667     else if ( isa == TGeoCompositeShape::Class() )   {
0668       const TGeoCompositeShape* sh = (const TGeoCompositeShape*) shape;
0669       const TGeoBoolNode* boolean = sh->GetBoolNode();
0670       TGeoBoolNode::EGeoBoolType oper = boolean->GetBooleanOperator();
0671       TGeoMatrix* matrix = boolean->GetRightMatrix();
0672       G4VSolid* left  = (G4VSolid*) handleSolid(name + "_left", boolean->GetLeftShape());
0673       G4VSolid* right = (G4VSolid*) handleSolid(name + "_right", boolean->GetRightShape());
0674       
0675       if (!left) {
0676         except("Geant4Converter","++ No left Geant4 Solid present for composite shape: %s",name.c_str());
0677       }
0678       if (!right) {
0679         except("Geant4Converter","++ No right Geant4 Solid present for composite shape: %s",name.c_str());
0680       }
0681 
0682       TGeoShape* ls = boolean->GetLeftShape();
0683       TGeoShape* rs = boolean->GetRightShape();
0684       if (strcmp(ls->ClassName(), "TGeoScaledShape") == 0 &&
0685           strcmp(rs->ClassName(), "TGeoBBox") == 0) {
0686         if (strcmp(((TGeoScaledShape *)ls)->GetShape()->ClassName(), "TGeoSphere") == 0) {
0687           if (oper == TGeoBoolNode::kGeoIntersection) {
0688             TGeoScaledShape* lls = (TGeoScaledShape *)ls;
0689             TGeoBBox* rrs = (TGeoBBox*)rs;
0690             double sx     = lls->GetScale()->GetScale()[0];
0691             double sy     = lls->GetScale()->GetScale()[1];
0692             double radius = ((TGeoSphere *)lls->GetShape())->GetRmax();
0693             double dz     = rrs->GetDZ();
0694             double zorig  = rrs->GetOrigin()[2];
0695             double zcut2  = dz + zorig;
0696             double zcut1  = 2 * zorig - zcut2;
0697             solid = new G4Ellipsoid(name,
0698                                     sx * radius * CM_2_MM,
0699                                     sy * radius * CM_2_MM,
0700                                     radius * CM_2_MM,
0701                                     zcut1 * CM_2_MM,
0702                                     zcut2 * CM_2_MM);
0703             data().g4Solids[shape] = solid;
0704             return solid;
0705           }
0706         }
0707       }
0708 
0709       if ( matrix->IsRotation() ) {
0710         G4Transform3D transform;
0711         g4Transform(matrix, transform);
0712         if (oper == TGeoBoolNode::kGeoSubtraction)
0713           solid = new G4SubtractionSolid(name, left, right, transform);
0714         else if (oper == TGeoBoolNode::kGeoUnion)
0715           solid = new G4UnionSolid(name, left, right, transform);
0716         else if (oper == TGeoBoolNode::kGeoIntersection)
0717           solid = new G4IntersectionSolid(name, left, right, transform);
0718       }
0719       else {
0720         const Double_t *t = matrix->GetTranslation();
0721         G4ThreeVector transform(t[0] * CM_2_MM, t[1] * CM_2_MM, t[2] * CM_2_MM);
0722         if (oper == TGeoBoolNode::kGeoSubtraction)
0723           solid = new G4SubtractionSolid(name, left, right, 0, transform);
0724         else if (oper == TGeoBoolNode::kGeoUnion)
0725           solid = new G4UnionSolid(name, left, right, 0, transform);
0726         else if (oper == TGeoBoolNode::kGeoIntersection)
0727           solid = new G4IntersectionSolid(name, left, right, 0, transform);
0728       }
0729     }
0730 
0731     if ( !solid )
0732       except("Geant4Converter","++ Failed to handle unknown solid shape: %s of type %s",
0733              name.c_str(), isa->GetName());
0734     printout(lvl,"Geant4Converter","++ Successessfully converted shape [%p] of type:%s to %s.",
0735              solid,isa->GetName(),typeName(typeid(*solid)).c_str());
0736     data().g4Solids[shape] = solid;
0737   }
0738   return solid;
0739 }
0740 
0741 /// Dump logical volume in GDML format to output stream
0742 void* Geant4Converter::handleVolume(const std::string& name, const TGeoVolume* volume) const {
0743   Volume _v(volume);
0744   Geant4GeometryInfo& info = data();
0745   PrintLevel lvl = debugVolumes ? ALWAYS : outputLevel;
0746   Geant4GeometryMaps::VolumeMap::const_iterator volIt = info.g4Volumes.find(volume);
0747   if ( _v.testFlagBit(Volume::VETO_SIMU) )  {
0748     printout(lvl, "Geant4Converter", "++ Volume %s not converted [Veto'ed for simulation]",volume->GetName());
0749     return nullptr;
0750   }
0751   else if (volIt == info.g4Volumes.end() ) {
0752     const char*  vnam = volume->GetName();
0753     TGeoMedium*  med  = volume->GetMedium();
0754     Solid        sh   = volume->GetShape();
0755     bool         is_assembly = sh->IsA() == TGeoShapeAssembly::Class() || volume->IsAssembly();
0756 
0757     printout(lvl, "Geant4Converter", "++ Convert Volume %-32s: %p %s/%s assembly:%s",
0758              vnam, volume, sh.type(), _v.type(), yes_no(is_assembly));
0759     if ( is_assembly ) {
0760       return nullptr;
0761     }
0762     Region        reg      = _v.region();
0763     LimitSet      lim      = _v.limitSet();
0764     VisAttr       vis      = _v.visAttributes();
0765     G4Region*     g4region = reg.isValid() ? info.g4Regions[reg] : nullptr;
0766     G4UserLimits* g4limits = lim.isValid() ? info.g4Limits[lim]  : nullptr;
0767     G4VSolid*     g4solid  = (G4VSolid*)   handleSolid(sh->GetName(), sh);
0768     G4Material*   g4medium = (G4Material*) handleMaterial(med->GetName(), Material(med));
0769     /// Check all pre-conditions
0770     if ( !g4solid )   {
0771       except("G4Converter","++ No Geant4 Solid present for volume: %s", vnam);
0772     }
0773     else if ( !g4medium )   {
0774       except("G4Converter","++ No Geant4 material present for volume: %s", vnam);
0775     }
0776     else if ( reg.isValid() && !g4region )  {
0777       except("G4Cnv::volume["+name+"]"," ++ Failed to access Geant4 region %s.", reg.name());
0778     }
0779     else if ( lim.isValid() && !g4limits )  {
0780       except("G4Cnv::volume["+name+"]","++ FATAL Failed to access Geant4 user limits %s.", lim.name());
0781     }
0782     else if ( g4limits )   {
0783       printout(lvl, "Geant4Converter", "++ Volume     + Apply LIMITS settings: %-24s to volume %s.",
0784                lim.name(), vnam);
0785     }
0786 
0787     G4LogicalVolume* g4vol = nullptr;
0788     if ( _v.hasProperties() && !_v.getProperty(GEANT4_TAG_PLUGIN,"").empty() )   {
0789       Detector* det = const_cast<Detector*>(&m_detDesc); 
0790       std::string plugin = _v.getProperty(GEANT4_TAG_PLUGIN,"");
0791       g4vol = PluginService::Create<G4LogicalVolume*>(plugin, det, _v, g4solid, g4medium);
0792       if ( !g4vol )    {
0793         except("G4Cnv::volume["+name+"]","++ FATAL Failed to call plugin to create logical volume.");
0794       }
0795     }
0796     else  {
0797       g4vol = new G4LogicalVolume(g4solid, g4medium, vnam, nullptr, nullptr, nullptr);
0798     }
0799 
0800     if ( g4limits )   {
0801       g4vol->SetUserLimits(g4limits);
0802     }
0803     if ( g4region )   {
0804       PrintLevel plevel = (debugVolumes||debugRegions||debugLimits) ? ALWAYS : outputLevel;
0805       printout(plevel, "Geant4Converter", "++ Volume     + Apply REGION settings: %-24s to volume %s.",
0806                reg.name(), vnam);
0807       // Handle the region settings for the world volume seperately.
0808       // Geant4 does NOT WANT any regions assigned to the workd volume.
0809       // The world's region is created in the G4RunManagerKernel!
0810       if ( _v == m_detDesc.worldVolume() )   {
0811         const char* wrd_nam = "DefaultRegionForTheWorld";
0812         const char* src_nam = g4region->GetName().c_str();
0813         auto* world_region  = G4RegionStore::GetInstance()->GetRegion(wrd_nam, false);
0814         if ( auto* cuts = g4region->GetProductionCuts() )   {
0815           world_region->SetProductionCuts(cuts);
0816           printout(plevel, "Geant4Converter",
0817                    "++ Volume %s Region: %s. Apply production cuts from %s", 
0818                    vnam, wrd_nam, src_nam);
0819         }
0820         if ( auto* lims = g4region->GetUserLimits() )   {
0821           world_region->SetUserLimits(lims);
0822           printout(plevel, "Geant4Converter",
0823                    "++ Volume %s Region: %s. Apply user limits from %s", 
0824                    vnam, wrd_nam, src_nam);
0825         }
0826       }
0827       else   {
0828         g4vol->SetRegion(g4region);
0829         g4region->AddRootLogicalVolume(g4vol);
0830       }
0831     }
0832     G4VisAttributes* g4vattr = vis.isValid()
0833       ? (G4VisAttributes*)handleVis(vis.name(), vis) : nullptr;
0834     if ( g4vattr )   {
0835       g4vol->SetVisAttributes(g4vattr);
0836     }
0837     info.g4Volumes[volume] = g4vol;
0838     printout(lvl, "Geant4Converter",
0839              "++ Volume     + %s converted: %p ---> G4: %p", vnam, volume, g4vol);
0840   }
0841   return nullptr;
0842 }
0843 
0844 /// Dump logical volume in GDML format to output stream
0845 void* Geant4Converter::collectVolume(const std::string& /* name */, const TGeoVolume* volume) const {
0846   Geant4GeometryInfo& info = data();
0847   Volume              _v(volume);
0848   Region              reg = _v.region();
0849   LimitSet            lim = _v.limitSet();
0850   SensitiveDetector   det = _v.sensitiveDetector();
0851   bool              world = (volume == m_detDesc.worldVolume().ptr());
0852 
0853   if ( !world )   {
0854     if ( lim.isValid() )
0855       info.limits[lim].insert(volume);
0856     if ( reg.isValid() )
0857       info.regions[reg].insert(volume);
0858     if ( det.isValid() )
0859       info.sensitives[det].insert(volume);
0860   }
0861   return (void*)volume;
0862 }
0863 
0864 /// Dump volume placement in GDML format to output stream
0865 void* Geant4Converter::handleAssembly(const std::string& name, const TGeoNode* node) const {
0866   TGeoVolume* mot_vol = node->GetVolume();
0867   PrintLevel lvl = debugVolumes ? ALWAYS : outputLevel;
0868   if ( mot_vol->IsA() != TGeoVolumeAssembly::Class() )    {
0869     return nullptr;
0870   }
0871   Volume _v(mot_vol);
0872   if ( _v.testFlagBit(Volume::VETO_SIMU) )  {
0873     printout(lvl, "Geant4Converter", "++ AssemblyNode %s not converted [Veto'ed for simulation]",node->GetName());
0874     return nullptr;
0875   }
0876   Geant4GeometryInfo& info = data();
0877   Geant4AssemblyVolume* g4 = info.g4AssemblyVolumes[node];
0878   if ( g4 )  {
0879     printout(ALWAYS, "Geant4Converter", "+++ Assembly: **** : Re-using existing assembly: %s",node->GetName());
0880   }
0881   if ( !g4 )  {
0882     g4 = new Geant4AssemblyVolume();
0883     for(Int_t i=0; i < mot_vol->GetNdaughters(); ++i)   {
0884       TGeoNode*     dau     = mot_vol->GetNode(i);
0885       TGeoVolume*   dau_vol = dau->GetVolume();
0886       TGeoMatrix*   tr      = dau->GetMatrix();
0887       G4Transform3D transform;
0888 
0889       g4Transform(tr, transform);
0890       if ( is_left_handed(tr) )   {
0891         G4Scale3D     scale;
0892         G4Rotate3D    rot;
0893         G4Translate3D trans;
0894         transform.getDecomposition(scale, rot, trans);
0895         printout(debugReflections ? ALWAYS : lvl, "Geant4Converter",
0896                  "++ Placing reflected ASSEMBLY. dau:%s to mother %s "
0897                  "Tr:x=%8.1f y=%8.1f z=%8.1f   Scale:x=%4.2f y=%4.2f z=%4.2f",
0898                  dau_vol->GetName(), mot_vol->GetName(),
0899                  transform.dx(), transform.dy(), transform.dz(),
0900                  scale.xx(), scale.yy(), scale.zz());
0901       }
0902 
0903       if ( dau_vol->IsA() == TGeoVolumeAssembly::Class() )  {
0904         Geant4GeometryMaps::AssemblyMap::iterator ia = info.g4AssemblyVolumes.find(dau);
0905         if ( ia == info.g4AssemblyVolumes.end() )  {
0906           printout(FATAL, "Geant4Converter", "+++ Invalid child assembly at %s : %d  parent: %s child:%s",
0907                    __FILE__, __LINE__, name.c_str(), dau->GetName());
0908           delete g4;
0909           return nullptr;
0910         }
0911         g4->placeAssembly(dau, (*ia).second, transform);
0912         printout(lvl, "Geant4Converter", "+++ Assembly: AddPlacedAssembly %p: dau:%s "
0913                  "to mother %s Tr:x=%8.3f y=%8.3f z=%8.3f",
0914                  (void*)dau_vol, dau_vol->GetName(), mot_vol->GetName(),
0915                  transform.dx(), transform.dy(), transform.dz());
0916       }
0917       else   {
0918         Geant4GeometryMaps::VolumeMap::iterator iv = info.g4Volumes.find(dau_vol);
0919         if ( iv == info.g4Volumes.end() )  {
0920           printout(FATAL,"Geant4Converter", "+++ Invalid child volume at %s : %d  parent: %s child:%s",
0921                    __FILE__, __LINE__, name.c_str(), dau->GetName());
0922           except("Geant4Converter", "+++ Invalid child volume at %s : %d  parent: %s child:%s",
0923                  __FILE__, __LINE__, name.c_str(), dau->GetName());
0924         }
0925         g4->placeVolume(dau,(*iv).second, transform);
0926         printout(lvl, "Geant4Converter", "+++ Assembly: AddPlacedVolume %p: dau:%s "
0927                  "to mother %s Tr:x=%8.3f y=%8.3f z=%8.3f",
0928                  (void*)dau_vol, dau_vol->GetName(), mot_vol->GetName(),
0929                  transform.dx(), transform.dy(), transform.dz());
0930       }
0931     }
0932     info.g4AssemblyVolumes[node] = g4;
0933   }
0934   return g4;
0935 }
0936 
0937 /// Dump volume placement in GDML format to output stream
0938 void* Geant4Converter::handlePlacement(const std::string& name, const TGeoNode* node) const {
0939   Geant4GeometryInfo& info = data();
0940   PrintLevel lvl = debugPlacements ? ALWAYS : outputLevel;
0941   Geant4GeometryMaps::PlacementMap::const_iterator g4it = info.g4Placements.find(node);
0942   G4VPhysicalVolume* g4 = (g4it == info.g4Placements.end()) ? 0 : (*g4it).second;
0943   TGeoVolume* vol = node->GetVolume();
0944   Volume _v(vol);
0945 
0946   if ( _v.testFlagBit(Volume::VETO_SIMU) )  {
0947     printout(lvl, "Geant4Converter", "++ Placement %s not converted [Veto'ed for simulation]",node->GetName());
0948     return nullptr;
0949   }
0950   //g4 = nullptr;
0951   if ( !g4 ) {
0952     TGeoVolume* mot_vol = node->GetMotherVolume();
0953     TGeoMatrix* tr = node->GetMatrix();
0954     if ( !tr ) {
0955       except("Geant4Converter",
0956              "++ Attempt to handle placement without transformation:%p %s of type %s vol:%p",
0957              node, node->GetName(), node->IsA()->GetName(), vol);
0958     }
0959     else if (nullptr == vol) {
0960       except("Geant4Converter", "++ Unknown G4 volume:%p %s of type %s ptr:%p",
0961              node, node->GetName(), node->IsA()->GetName(), vol);
0962     }
0963     else {
0964       int           copy               = node->GetNumber();
0965       bool          node_is_reflected  = is_left_handed(tr);
0966       bool          node_is_assembly   = vol->IsA() == TGeoVolumeAssembly::Class();
0967       bool          mother_is_assembly = mot_vol ? mot_vol->IsA() == TGeoVolumeAssembly::Class() : false;
0968       G4Transform3D transform;
0969       Geant4GeometryMaps::VolumeMap::const_iterator volIt = info.g4Volumes.find(mot_vol);
0970 
0971       g4Transform(tr, transform);
0972       if ( mother_is_assembly )   {
0973         //
0974         // Mother is an assembly:
0975         // Nothing to do here, because:
0976         // -- placed volumes were already added before in "handleAssembly"
0977         // -- imprint cannot be made, because this requires a logical volume as a mother
0978         //
0979         printout(lvl, "Geant4Converter", "+++ Assembly: **** : dau:%s "
0980                  "to mother %s Tr:x=%8.3f y=%8.3f z=%8.3f",
0981                  vol->GetName(), mot_vol->GetName(),
0982                  transform.dx(), transform.dy(), transform.dz());
0983         return nullptr;
0984       }
0985       G4Scale3D        scale;
0986       G4Rotate3D       rot;
0987       G4Translate3D    trans;
0988       transform.getDecomposition(scale, rot, trans);
0989       if ( node_is_assembly )   {
0990         //
0991         // Node is an assembly:
0992         // Imprint the assembly. The mother MUST already be transformed.
0993         //
0994         printout(lvl, "Geant4Converter", "++ Assembly: makeImprint: dau:%-12s %s in mother %-12s "
0995                  "Tr:x=%8.1f y=%8.1f z=%8.1f   Scale:x=%4.2f y=%4.2f z=%4.2f",
0996                  node->GetName(), node_is_reflected ? "(REFLECTED)" : "",
0997                  mot_vol ? mot_vol->GetName() : "<unknown>",
0998                  transform.dx(), transform.dy(), transform.dz(),
0999                  scale.xx(), scale.yy(), scale.zz());
1000         Geant4AssemblyVolume* ass = (Geant4AssemblyVolume*)info.g4AssemblyVolumes[node];
1001         Geant4AssemblyVolume::Chain chain;
1002         chain.emplace_back(node);
1003         ass->imprint(*this, node, chain, ass, (*volIt).second, transform, checkOverlaps);
1004         return nullptr;
1005       }
1006       else if ( node != info.manager->GetTopNode() && volIt == info.g4Volumes.end() )  {
1007         //throw std::logic_error("Geant4Converter: Invalid mother volume found!");
1008       }
1009       PlacedVolume pv(node);
1010       const auto*  pv_data = pv.data();
1011       G4LogicalVolume* g4vol = info.g4Volumes[vol];
1012       G4LogicalVolume* g4mot = info.g4Volumes[mot_vol];
1013       G4PhysicalVolumesPair pvPlaced  { nullptr, nullptr };
1014 
1015       if ( pv_data && pv_data->params && (pv_data->params->flags&Volume::REPLICATED) )   {
1016         EAxis  axis = kUndefined;
1017         double width = 0e0, offset = 0e0;
1018         auto flags = pv_data->params->flags;
1019         auto count = pv_data->params->trafo1D.second;
1020         auto start = pv_data->params->start.Translation().Vect();
1021         auto delta = pv_data->params->trafo1D.first.Translation().Vect();
1022 
1023         if ( flags&Volume::X_axis )
1024         { axis = kXAxis; width = delta.X(); offset = start.X(); }
1025         else if ( flags&Volume::Y_axis )
1026         { axis = kYAxis; width = delta.Y(); offset = start.Y(); }
1027         else if ( flags&Volume::Z_axis )
1028         { axis = kZAxis; width = delta.Z(); offset = start.Z(); }
1029         else
1030           except("Geant4Converter",
1031                  "++ Replication around unknown axis is not implemented. flags: %16X", flags);
1032         printout(INFO,"Geant4Converter","++ Replicate: Axis: %ld Count: %ld offset: %f width: %f",
1033                  axis, count, offset, width);
1034         auto* g4pv = new G4PVReplica(name,      // its name
1035                                      g4vol,     // its logical volume
1036                                      g4mot,     // its mother (logical) volume
1037                                      axis,      // its replication axis
1038                                      count,     // Number of replicas
1039                                      width,     // Distance between 2 replicas
1040                                      offset);   // Placement offset in axis direction
1041         pvPlaced = { g4pv, nullptr };
1042 #if 0
1043         pvPlaced =
1044           G4ReflectionFactory::Instance()->Replicate(name,      // its name
1045                                                      g4vol,     // its logical volume
1046                                                      g4mot,     // its mother (logical) volume
1047                                                      axis,      // its replication axis
1048                                                      count,     // Number of replicas
1049                                                      width,     // Distance between 2 replicas
1050                                                      offset);   // Placement offset in axis direction
1051         /// Update replica list to avoid additional conversions...
1052         auto* g4pv = pvPlaced.second ? pvPlaced.second : pvPlaced.first;
1053 #endif
1054         for( auto& handle : pv_data->params->placements )
1055           info.g4Placements[handle.ptr()] = g4pv;
1056       }
1057       else if ( pv_data && pv_data->params )   {
1058         auto*  g4par = new Geant4PlacementParameterisation(pv);
1059         auto*  g4pv  = new G4PVParameterised(name,              // its name
1060                                              g4vol,             // its logical volume
1061                                              g4mot,             // its mother (logical) volume
1062                                              g4par->axis(),     // its replication axis
1063                                              g4par->count(),    // Number of replicas
1064                                              g4par);            // G4 parametrization
1065         pvPlaced = { g4pv, nullptr };
1066         /// Update replica list to avoid additional conversions...
1067         for( auto& handle : pv_data->params->placements )
1068           info.g4Placements[handle.ptr()] = g4pv;
1069       }
1070       else    {
1071         pvPlaced =
1072           G4ReflectionFactory::Instance()->Place(transform,     // no rotation
1073                                                  name,          // its name
1074                                                  g4vol,         // its logical volume
1075                                                  g4mot,         // its mother (logical) volume
1076                                                  false,         // no boolean operations
1077                                                  copy,          // its copy number
1078                                                  checkOverlaps);
1079       }
1080       printout(debugReflections||debugPlacements ? ALWAYS : lvl, "Geant4Converter",
1081                "++ Place %svolume %-12s in mother %-12s "
1082                "Tr:x=%8.1f y=%8.1f z=%8.1f   Scale:x=%4.2f y=%4.2f z=%4.2f",
1083                node_is_reflected ? "REFLECTED " : "", _v.name(),
1084                mot_vol ? mot_vol->GetName() : "<unknown>",
1085                transform.dx(), transform.dy(), transform.dz(),
1086                scale.xx(), scale.yy(), scale.zz());
1087       // First 2 cases can be combined.
1088       // Leave them separated for debugging G4ReflectionFactory for now...
1089       if ( node_is_reflected  && !pvPlaced.second )
1090         return info.g4Placements[node] = pvPlaced.first;
1091       else if ( !node_is_reflected && !pvPlaced.second )
1092         return info.g4Placements[node] = pvPlaced.first;
1093       // Now deal with valid pvPlaced.second ...
1094       if ( node_is_reflected )
1095         return info.g4Placements[node] = pvPlaced.first;
1096       else if ( !node_is_reflected )
1097         return info.g4Placements[node] = pvPlaced.first;
1098       g4 = pvPlaced.second ? pvPlaced.second : pvPlaced.first;
1099     }
1100     info.g4Placements[node] = g4;
1101     printout(ERROR, "Geant4Converter", "++ DEAD code. Should not end up here!");
1102   }
1103   return g4;
1104 }
1105 
1106 /// Convert the geometry type region into the corresponding Geant4 object(s).
1107 void* Geant4Converter::handleRegion(Region region, const std::set<const TGeoVolume*>& /* volumes */) const {
1108   G4Region* g4 = data().g4Regions[region];
1109   if ( !g4 ) {
1110     PrintLevel lvl = debugRegions ? ALWAYS : outputLevel;
1111     Region r = region;
1112     g4 = new G4Region(region.name());
1113 
1114     // create region info with storeSecondaries flag
1115     if( not r.wasThresholdSet() and r.storeSecondaries() ) {
1116       throw std::runtime_error("G4Region: StoreSecondaries is True, but no explicit threshold set:");
1117     }
1118     printout(lvl, "Geant4Converter", "++ Setting up region: %s", r.name());
1119     G4UserRegionInformation* info = new G4UserRegionInformation();
1120     info->region = r;
1121     info->threshold = r.threshold()*CLHEP::MeV/units::MeV;
1122     info->storeSecondaries = r.storeSecondaries();
1123     g4->SetUserInformation(info);
1124 
1125     printout(lvl, "Geant4Converter", "++ Converted region settings of:%s.", r.name());
1126     std::vector < std::string > &limits = r.limits();
1127     G4ProductionCuts* cuts = 0;
1128     // set production cut
1129     if( not r.useDefaultCut() ) {
1130       cuts = new G4ProductionCuts();
1131       cuts->SetProductionCut(r.cut()*CLHEP::mm/units::mm);
1132       printout(lvl, "Geant4Converter", "++ %s: Using default cut: %f [mm]",
1133                r.name(), r.cut()*CLHEP::mm/units::mm);
1134     }
1135     for( const auto& nam : limits )  {
1136       LimitSet ls = m_detDesc.limitSet(nam);
1137       if ( ls.isValid() ) {
1138         const LimitSet::Set& cts = ls.cuts();
1139         for (const auto& c : cts )   {
1140           int pid = 0;
1141           if ( c.particles == "*" ) pid = -1;
1142           else if ( c.particles == "e-"     ) pid = idxG4ElectronCut;
1143           else if ( c.particles == "e+"     ) pid = idxG4PositronCut;
1144           else if ( c.particles == "e[+-]"  ) pid = -idxG4PositronCut-idxG4ElectronCut;
1145           else if ( c.particles == "e[-+]"  ) pid = -idxG4PositronCut-idxG4ElectronCut;
1146           else if ( c.particles == "gamma"  ) pid = idxG4GammaCut;
1147           else if ( c.particles == "proton" ) pid = idxG4ProtonCut;
1148           else throw std::runtime_error("G4Region: Invalid production cut particle-type:" + c.particles);
1149           if ( !cuts ) cuts = new G4ProductionCuts();
1150           if ( pid == -(idxG4PositronCut+idxG4ElectronCut) )  {
1151             cuts->SetProductionCut(c.value*CLHEP::mm/units::mm, idxG4PositronCut);
1152             cuts->SetProductionCut(c.value*CLHEP::mm/units::mm, idxG4ElectronCut);
1153           }
1154           else  {
1155             cuts->SetProductionCut(c.value*CLHEP::mm/units::mm, pid);
1156           }
1157           printout(lvl, "Geant4Converter", "++ %s: Set cut  [%s/%d] = %f [mm]",
1158                    r.name(), c.particles.c_str(), pid, c.value*CLHEP::mm/units::mm);
1159         }
1160         bool found = false;
1161         const auto& lm = data().g4Limits;
1162         for (const auto& j : lm )   {
1163           if (nam == j.first->GetName()) {
1164             g4->SetUserLimits(j.second);
1165             printout(lvl, "Geant4Converter", "++ %s: Set limits %s to region type %s",
1166                      r.name(), nam.c_str(), j.second->GetType().c_str());
1167             found = true;
1168             break;
1169           }
1170         }
1171         if ( found )   {
1172           continue;
1173         }
1174       }
1175       except("Geant4Converter", "++ G4Region: Failed to resolve limitset: " + nam);
1176     }
1177     /// Assign cuts to region if they were created
1178     if ( cuts ) g4->SetProductionCuts(cuts);
1179     data().g4Regions[region] = g4;
1180   }
1181   return g4;
1182 }
1183 
1184 /// Convert the geometry type LimitSet into the corresponding Geant4 object(s).
1185 void* Geant4Converter::handleLimitSet(LimitSet limitset, const std::set<const TGeoVolume*>& /* volumes */) const {
1186   G4UserLimits* g4 = data().g4Limits[limitset];
1187   if ( !g4 ) {
1188     PrintLevel lvl = debugLimits || debugRegions ? ALWAYS : outputLevel;
1189     struct LimitPrint  {
1190       const LimitSet& ls;
1191       LimitPrint(const LimitSet& lset) : ls(lset) {}
1192       const LimitPrint& operator()(const std::string& pref, const Geant4UserLimits::Handler& h)  const {
1193         if ( !h.particleLimits.empty() )  {
1194           printout(ALWAYS,"Geant4Converter",
1195                    "+++ LimitSet: Explicit Limit %s.%s applied for particles:",ls.name(), pref.c_str());
1196           for(const auto& p : h.particleLimits)
1197             printout(ALWAYS,"Geant4Converter","+++ LimitSet:    Particle type: %-18s PDG: %-6d : %f",
1198                      p.first->GetParticleName().c_str(), p.first->GetPDGEncoding(), p.second);
1199         }
1200         else if ( h.defaultValue > std::numeric_limits<double>::epsilon() )  {
1201           printout(ALWAYS,"Geant4Converter",
1202                    "+++ LimitSet: Implicit Limit %s.%s for wildcard particles: %f",
1203                    ls.name(), pref.c_str(), float(h.defaultValue));
1204         }
1205         return *this;
1206       }
1207     };
1208     Geant4UserLimits* limits = new Geant4UserLimits(limitset);
1209     g4 = limits;
1210     printout(lvl, "Geant4Converter",
1211              "++ Successfully converted LimitSet: %s [%ld cuts, %ld limits]",
1212              limitset.name(), limitset.cuts().size(), limitset.limits().size());
1213     if ( debugRegions || debugLimits )    {
1214       LimitPrint print(limitset);
1215       print("maxTime",    limits->maxTime)
1216         ("minEKine",      limits->minEKine)
1217         ("minRange",      limits->minRange)
1218         ("maxStepLength", limits->maxStepLength)
1219         ("maxTrackLength",limits->maxTrackLength);
1220     }
1221     data().g4Limits[limitset] = g4;
1222   }
1223   return g4;
1224 }
1225 
1226 /// Convert the geometry visualisation attributes to the corresponding Geant4 object(s).
1227 void* Geant4Converter::handleVis(const std::string& /* name */, VisAttr attr) const {
1228   Geant4GeometryInfo& info = data();
1229   G4VisAttributes*    g4   = info.g4Vis[attr];
1230   if ( !g4 ) {
1231     float red = 0, green = 0, blue = 0;
1232     int   style = attr.lineStyle();
1233     attr.rgb(red, green, blue);
1234     g4 = new G4VisAttributes(attr.visible(), G4Colour(red, green, blue, attr.alpha()));
1235     //g4->SetLineWidth(attr->GetLineWidth());
1236     g4->SetDaughtersInvisible(!attr.showDaughters());
1237     if ( style == VisAttr::SOLID ) {
1238       g4->SetLineStyle(G4VisAttributes::unbroken);
1239       g4->SetForceWireframe(false);
1240       g4->SetForceSolid(true);
1241     }
1242     else if ( style == VisAttr::WIREFRAME || style == VisAttr::DASHED ) {
1243       g4->SetLineStyle(G4VisAttributes::dashed);
1244       g4->SetForceSolid(false);
1245       g4->SetForceWireframe(true);
1246     }
1247     info.g4Vis[attr] = g4;
1248   }
1249   return g4;
1250 }
1251 
1252 /// Handle the geant 4 specific properties
1253 void Geant4Converter::handleProperties(Detector::Properties& prp) const {
1254   std::map < std::string, std::string > processors;
1255   static int s_idd = 9999999;
1256   for( const auto& [nam, vals] : prp ) {
1257     if ( nam.substr(0, 6) == "geant4" ) {
1258       auto id_it = vals.find("id");
1259       std::string id = (id_it == vals.end()) ? _toString(++s_idd,"%d") : (*id_it).second;
1260       processors.emplace(id, nam);
1261     }
1262   }
1263   for( const auto& p : processors ) {
1264     const GeoHandler* hdlr = this;
1265     const Detector::PropertyValues& vals = prp[p.second];
1266     auto iter = vals.find("type");
1267     if ( iter != vals.end() )  {
1268       std::string type = iter->second;
1269       std::string tag  = type + "_Geant4_action";
1270       Detector* det = const_cast<Detector*>(&m_detDesc);
1271       long      res = PluginService::Create<long>(tag, det, hdlr, &vals);
1272       if ( 0 == res ) {
1273         throw std::runtime_error("Failed to locate plugin to interprete files of type"
1274                                  " \"" + tag + "\" - no factory:" + type);
1275       }
1276       res = *(long*)res;
1277       if ( res != 1 ) {
1278         throw std::runtime_error("Failed to invoke the plugin " + tag + " of type " + type);
1279       }
1280       printout(outputLevel, "Geant4Converter", "+++++ Executed Successfully Geant4 setup module *%s*.", type.c_str());
1281       continue;
1282     }
1283     printout(outputLevel, "Geant4Converter", "+++++ FAILED to execute Geant4 setup module *%s*.", p.second.c_str());    
1284   }
1285 }
1286 
1287 /// Convert the geometry type material into the corresponding Geant4 object(s).
1288 void* Geant4Converter::handleMaterialProperties(TObject* mtx) const    {
1289   Geant4GeometryInfo& info   = data();
1290   TGDMLMatrix*        matrix = (TGDMLMatrix*)mtx;
1291   const char*         cptr   = ::strstr(matrix->GetName(), GEANT4_TAG_IGNORE);
1292   Geant4GeometryInfo::PropertyVector* g4 = info.g4OpticalProperties[matrix];
1293 
1294   if ( nullptr != cptr )   {  // Check if the property should not be passed to Geant4
1295     printout(INFO,"Geant4MaterialProperties","++ Ignore property %s [%s].",
1296              matrix->GetName(), matrix->GetTitle());         
1297     return nullptr;
1298   }
1299   cptr = ::strstr(matrix->GetTitle(), GEANT4_TAG_IGNORE);
1300   if ( nullptr != cptr )   {  // Check if the property should not be passed to Geant4
1301     printout(INFO,"Geant4MaterialProperties","++ Ignore property %s [%s].",
1302              matrix->GetName(), matrix->GetTitle());
1303     return nullptr;
1304   }
1305   
1306   if ( !g4 )  {
1307     PrintLevel lvl = debugMaterials ? ALWAYS : outputLevel;
1308     g4 = new Geant4GeometryInfo::PropertyVector();
1309     std::size_t rows = matrix->GetRows();
1310     g4->name    = matrix->GetName();
1311     g4->title   = matrix->GetTitle();
1312     g4->bins.reserve(rows);
1313     g4->values.reserve(rows);
1314     for( std::size_t i=0; i<rows; ++i )   {
1315       g4->bins.emplace_back(matrix->Get(i,0)  /*   *CLHEP::eV/units::eV   */);
1316       g4->values.emplace_back(matrix->Get(i,1));
1317     }
1318     printout(lvl, "Geant4Converter",
1319              "++ Successfully converted material property:%s : %s [%ld rows]",
1320              matrix->GetName(), matrix->GetTitle(), rows);
1321     info.g4OpticalProperties[matrix] = g4;
1322   }
1323   return g4;
1324 }
1325 
1326 static G4OpticalSurfaceFinish geant4_surface_finish(TGeoOpticalSurface::ESurfaceFinish f)   {
1327 #define TO_G4_FINISH(x)  case TGeoOpticalSurface::kF##x : return x;
1328   switch(f)   {
1329     TO_G4_FINISH(polished);              // smooth perfectly polished surface
1330     TO_G4_FINISH(polishedfrontpainted);  // smooth top-layer (front) paint
1331     TO_G4_FINISH(polishedbackpainted);   // same is 'polished' but with a back-paint
1332  
1333     TO_G4_FINISH(ground);                // rough surface
1334     TO_G4_FINISH(groundfrontpainted);    // rough top-layer (front) paint
1335     TO_G4_FINISH(groundbackpainted);     // same as 'ground' but with a back-paint
1336 
1337     TO_G4_FINISH(polishedlumirrorair);   // mechanically polished surface, with lumirror
1338     TO_G4_FINISH(polishedlumirrorglue);  // mechanically polished surface, with lumirror & meltmount
1339     TO_G4_FINISH(polishedair);           // mechanically polished surface
1340     TO_G4_FINISH(polishedteflonair);     // mechanically polished surface, with teflon
1341     TO_G4_FINISH(polishedtioair);        // mechanically polished surface, with tio paint
1342     TO_G4_FINISH(polishedtyvekair);      // mechanically polished surface, with tyvek
1343     TO_G4_FINISH(polishedvm2000air);     // mechanically polished surface, with esr film
1344     TO_G4_FINISH(polishedvm2000glue);    // mechanically polished surface, with esr film & meltmount
1345 
1346     TO_G4_FINISH(etchedlumirrorair);     // chemically etched surface, with lumirror
1347     TO_G4_FINISH(etchedlumirrorglue);    // chemically etched surface, with lumirror & meltmount
1348     TO_G4_FINISH(etchedair);             // chemically etched surface
1349     TO_G4_FINISH(etchedteflonair);       // chemically etched surface, with teflon
1350     TO_G4_FINISH(etchedtioair);          // chemically etched surface, with tio paint
1351     TO_G4_FINISH(etchedtyvekair);        // chemically etched surface, with tyvek
1352     TO_G4_FINISH(etchedvm2000air);       // chemically etched surface, with esr film
1353     TO_G4_FINISH(etchedvm2000glue);      // chemically etched surface, with esr film & meltmount
1354 
1355     TO_G4_FINISH(groundlumirrorair);     // rough-cut surface, with lumirror
1356     TO_G4_FINISH(groundlumirrorglue);    // rough-cut surface, with lumirror & meltmount
1357     TO_G4_FINISH(groundair);             // rough-cut surface
1358     TO_G4_FINISH(groundteflonair);       // rough-cut surface, with teflon
1359     TO_G4_FINISH(groundtioair);          // rough-cut surface, with tio paint
1360     TO_G4_FINISH(groundtyvekair);        // rough-cut surface, with tyvek
1361     TO_G4_FINISH(groundvm2000air);       // rough-cut surface, with esr film
1362     TO_G4_FINISH(groundvm2000glue);      // rough-cut surface, with esr film & meltmount
1363 
1364     // for DAVIS model
1365     TO_G4_FINISH(Rough_LUT);             // rough surface
1366     TO_G4_FINISH(RoughTeflon_LUT);       // rough surface wrapped in Teflon tape
1367     TO_G4_FINISH(RoughESR_LUT);          // rough surface wrapped with ESR
1368     TO_G4_FINISH(RoughESRGrease_LUT);    // rough surface wrapped with ESR and coupled with opical grease
1369     TO_G4_FINISH(Polished_LUT);          // polished surface
1370     TO_G4_FINISH(PolishedTeflon_LUT);    // polished surface wrapped in Teflon tape
1371     TO_G4_FINISH(PolishedESR_LUT);       // polished surface wrapped with ESR
1372     TO_G4_FINISH(PolishedESRGrease_LUT); // polished surface wrapped with ESR and coupled with opical grease
1373     TO_G4_FINISH(Detector_LUT);          // polished surface with optical grease
1374   default:
1375     printout(ERROR,"Geant4Surfaces","++ Unknown finish style: %d [%s]. Assume polished!",
1376              int(f), TGeoOpticalSurface::FinishToString(f));
1377     return polished;
1378   }
1379 #undef TO_G4_FINISH
1380 }
1381 
1382 static G4SurfaceType geant4_surface_type(TGeoOpticalSurface::ESurfaceType t)   {
1383 #define TO_G4_TYPE(x)  case TGeoOpticalSurface::kT##x : return x;
1384   switch(t)   {
1385     TO_G4_TYPE(dielectric_metal);      // dielectric-metal interface
1386     TO_G4_TYPE(dielectric_dielectric); // dielectric-dielectric interface
1387     TO_G4_TYPE(dielectric_LUT);        // dielectric-Look-Up-Table interface
1388     TO_G4_TYPE(dielectric_LUTDAVIS);   // dielectric-Look-Up-Table DAVIS interface
1389     TO_G4_TYPE(dielectric_dichroic);   // dichroic filter interface
1390     TO_G4_TYPE(firsov);                // for Firsov Process
1391     TO_G4_TYPE(x_ray);                  // for x-ray mirror process
1392   default:
1393     printout(ERROR,"Geant4Surfaces","++ Unknown surface type: %d [%s]. Assume dielectric_metal!",
1394              int(t), TGeoOpticalSurface::TypeToString(t));
1395     return dielectric_metal;
1396   }
1397 #undef TO_G4_TYPE
1398 }
1399 
1400 static G4OpticalSurfaceModel geant4_surface_model(TGeoOpticalSurface::ESurfaceModel surfMod)   {
1401 #define TO_G4_MODEL(x)  case TGeoOpticalSurface::kM##x : return x;
1402   switch(surfMod)   {
1403     TO_G4_MODEL(glisur);   // original GEANT3 model
1404     TO_G4_MODEL(unified);  // UNIFIED model
1405     TO_G4_MODEL(LUT);      // Look-Up-Table model
1406     TO_G4_MODEL(DAVIS);    // DAVIS model
1407     TO_G4_MODEL(dichroic); // dichroic filter
1408   default:
1409     printout(ERROR,"Geant4Surfaces","++ Unknown surface model: %d [%s]. Assume glisur!",
1410              int(surfMod), TGeoOpticalSurface::ModelToString(surfMod));
1411     return glisur;
1412   }
1413 #undef TO_G4_MODEL
1414 }
1415 
1416 /// Convert the optical surface to Geant4
1417 void* Geant4Converter::handleOpticalSurface(TObject* surface) const    {
1418   TGeoOpticalSurface* optSurf    = (TGeoOpticalSurface*)surface;
1419   Geant4GeometryInfo& info = data();
1420   G4OpticalSurface*   g4   = info.g4OpticalSurfaces[optSurf];
1421   if ( !g4 ) {
1422     G4SurfaceType          type   = geant4_surface_type(optSurf->GetType());
1423     G4OpticalSurfaceModel  model  = geant4_surface_model(optSurf->GetModel());
1424     G4OpticalSurfaceFinish finish = geant4_surface_finish(optSurf->GetFinish());
1425     std::string name = make_NCName(optSurf->GetName());
1426     PrintLevel lvl = debugSurfaces ? ALWAYS : DEBUG;
1427     g4 = new G4OpticalSurface(name, model, finish, type, optSurf->GetValue());
1428     g4->SetSigmaAlpha(optSurf->GetSigmaAlpha());
1429     g4->SetPolish(optSurf->GetPolish());
1430 
1431     printout(lvl, "Geant4Converter",
1432              "++ Created OpticalSurface: %-18s type:%s model:%s finish:%s SigmaAlphs: %.3e Polish: %.3e",
1433              optSurf->GetName(),
1434              TGeoOpticalSurface::TypeToString(optSurf->GetType()),
1435              TGeoOpticalSurface::ModelToString(optSurf->GetModel()),
1436              TGeoOpticalSurface::FinishToString(optSurf->GetFinish()),
1437              optSurf->GetSigmaAlpha(), optSurf->GetPolish());
1438     ///
1439     /// Convert non-scalar properties from GDML tables
1440     G4MaterialPropertiesTable* tab = nullptr;
1441     TListIter itp(&optSurf->GetProperties());
1442     for(TObject* obj = itp.Next(); obj; obj = itp.Next())  {
1443       std::string exc_str;
1444       TNamed*      named  = (TNamed*)obj;
1445       TGDMLMatrix* matrix = info.manager->GetGDMLMatrix(named->GetTitle());
1446       const char*  cptr   = ::strstr(matrix->GetName(), GEANT4_TAG_IGNORE);
1447       if ( nullptr != cptr )  // Check if the property should not be passed to Geant4
1448         continue;
1449 
1450       if ( nullptr == tab )  {
1451         tab = new G4MaterialPropertiesTable();
1452         g4->SetMaterialPropertiesTable(tab);
1453       }
1454 
1455       Geant4GeometryInfo::PropertyVector* v =
1456         (Geant4GeometryInfo::PropertyVector*)handleMaterialProperties(matrix);
1457       if ( !v )  {  // Error!
1458         except("Geant4OpticalSurface","++ Failed to convert opt.surface %s. Property table %s is not defined!",
1459                optSurf->GetName(), named->GetTitle());
1460       }
1461       int idx = -1;
1462       try   {
1463         idx = tab->GetPropertyIndex(named->GetName());
1464       }
1465       catch(const std::exception& e)   {
1466         exc_str = e.what();
1467       }
1468       catch(...)   {
1469       }
1470       if ( idx < 0 )   {
1471         printout(ERROR, "Geant4Converter",
1472                  "++ UNKNOWN Geant4 Property: %-20s %s [IGNORED]",
1473                  exc_str.c_str(), named->GetName());
1474         continue;
1475       }
1476       // We need to convert the property from TGeo units to Geant4 units
1477       auto conv = g4PropertyConversion(idx);
1478       std::vector<double> bins(v->bins), vals(v->values);
1479       for(std::size_t i=0, count=v->bins.size(); i<count; ++i)
1480         bins[i] *= conv.first, vals[i] *= conv.second;
1481       G4MaterialPropertyVector* vec = new G4MaterialPropertyVector(&bins[0], &vals[0], bins.size());
1482       tab->AddProperty(named->GetName(), vec);
1483       
1484       printout(lvl, "Geant4Converter",
1485                "++       Property: %-20s [%ld x %ld] -->  %s",
1486                named->GetName(), matrix->GetRows(), matrix->GetCols(), named->GetTitle());
1487       for(std::size_t i=0, count=v->bins.size(); i<count; ++i)
1488         printout(lvl, named->GetName(),
1489                  "  Geant4: %8.3g [MeV]  TGeo: %8.3g [GeV] Conversion: %8.3g",
1490                  bins[i], v->bins[i], conv.first);
1491     }
1492     ///
1493     /// Convert scalar properties
1494 #if ROOT_VERSION_CODE >= ROOT_VERSION(6,31,1)
1495     TListIter itc(&optSurf->GetConstProperties());
1496     for(TObject* obj = itc.Next(); obj; obj = itc.Next())  {
1497       std::string  exc_str;
1498       TNamed* named  = (TNamed*)obj;
1499       const char* cptr = ::strstr(named->GetName(), GEANT4_TAG_IGNORE);
1500       if ( nullptr != cptr )   {
1501         printout(INFO, name, "++ Ignore CONST property %s [%s].",
1502                  named->GetName(), named->GetTitle());
1503         continue;
1504       }
1505       cptr = ::strstr(named->GetTitle(), GEANT4_TAG_IGNORE);
1506       if ( nullptr != cptr )   {
1507         printout(INFO, name,"++ Ignore CONST property %s [%s].",
1508                  named->GetName(), named->GetTitle());
1509         continue;
1510       }
1511       Bool_t   err = kFALSE;
1512       Double_t value = info.manager->GetProperty(named->GetTitle(),&err);
1513       if ( err != kFALSE )   {
1514         except(name,
1515                "++ FAILED to create G4 material %s [Cannot convert const property: %s]",
1516                optSurf->GetName(), named->GetName());
1517       }
1518       if ( nullptr == tab )  {
1519         tab = new G4MaterialPropertiesTable();
1520         g4->SetMaterialPropertiesTable(tab);
1521       }
1522       int idx = -1;
1523       try   {
1524         idx = tab->GetConstPropertyIndex(named->GetName());
1525       }
1526       catch(const std::exception& e)   {
1527         exc_str = e.what();
1528       }
1529       catch(...)   {
1530       }
1531       if ( idx < 0 )   {
1532         printout(ERROR, name,
1533                  "++ UNKNOWN Geant4 CONST Property: %-20s %s [IGNORED]",
1534                  exc_str.c_str(), named->GetName());
1535         continue;
1536       }
1537       // We need to convert the property from TGeo units to Geant4 units
1538       double conv = g4ConstPropertyConversion(idx);
1539       printout(lvl, name, "++      CONST Property: %-20s %g * %g --> %g ",
1540                named->GetName(), value, conv, value * conv);
1541       tab->AddConstProperty(named->GetName(), value * conv);
1542     }
1543 #endif  // ROOT_VERSION >= 6.31.1
1544     info.g4OpticalSurfaces[optSurf] = g4;
1545   }
1546   return g4;
1547 }
1548 
1549 /// Convert the skin surface to Geant4
1550 void* Geant4Converter::handleSkinSurface(TObject* surface) const   {
1551   TGeoSkinSurface*    surf = (TGeoSkinSurface*)surface;
1552   Geant4GeometryInfo& info = data();
1553   G4LogicalSkinSurface* g4 = info.g4SkinSurfaces[surf];
1554   if ( !g4 ) {
1555     G4OpticalSurface* optSurf  = info.g4OpticalSurfaces[OpticalSurface(surf->GetSurface())];
1556     G4LogicalVolume*  v = info.g4Volumes[surf->GetVolume()];
1557     std::string name = make_NCName(surf->GetName());
1558     g4 = new G4LogicalSkinSurface(name, v, optSurf);
1559     printout(debugSurfaces ? ALWAYS : DEBUG, "Geant4Converter",
1560              "++ Created SkinSurface: %-18s  optical:%s",
1561              surf->GetName(), surf->GetSurface()->GetName());
1562     info.g4SkinSurfaces[surf] = g4;
1563   }
1564   return g4;
1565 }
1566 
1567 /// Convert the border surface to Geant4
1568 void* Geant4Converter::handleBorderSurface(TObject* surface) const   {
1569   TGeoBorderSurface*    surf = (TGeoBorderSurface*)surface;
1570   Geant4GeometryInfo&   info = data();
1571   G4LogicalBorderSurface* g4 = info.g4BorderSurfaces[surf];
1572   if ( !g4 ) {
1573     G4OpticalSurface*  optSurf = info.g4OpticalSurfaces[OpticalSurface(surf->GetSurface())];
1574     G4VPhysicalVolume* n1 = info.g4Placements[surf->GetNode1()];
1575     G4VPhysicalVolume* n2 = info.g4Placements[surf->GetNode2()];
1576     std::string name = make_NCName(surf->GetName());
1577     g4 = new G4LogicalBorderSurface(name, n1, n2, optSurf);
1578     printout(debugSurfaces ? ALWAYS : DEBUG, "Geant4Converter",
1579              "++ Created BorderSurface: %-18s  optical:%s",
1580              surf->GetName(), surf->GetSurface()->GetName());
1581     info.g4BorderSurfaces[surf] = g4;
1582   }
1583   return g4;
1584 }
1585 
1586 /// Convert the geometry type SensitiveDetector into the corresponding Geant4 object(s).
1587 void Geant4Converter::printSensitive(SensitiveDetector sens_det, const std::set<const TGeoVolume*>& /* volumes */) const {
1588   Geant4GeometryInfo&          info = data();
1589   std::set<const TGeoVolume*>& volset = info.sensitives[sens_det];
1590   SensitiveDetector            sd = sens_det;
1591   std::stringstream str;
1592 
1593   printout(INFO, "Geant4Converter", "++ SensitiveDetector: %-18s %-20s Hits:%-16s", sd.name(), ("[" + sd.type() + "]").c_str(),
1594            sd.hitsCollection().c_str());
1595   str << "                    | " << "Cutoff:" << std::setw(6) << std::left
1596       << sd.energyCutoff() << std::setw(5) << std::right << volset.size()
1597       << " volumes ";
1598   if (sd.region().isValid())
1599     str << " Region:" << std::setw(12) << std::left << sd.region().name();
1600   if (sd.limits().isValid())
1601     str << " Limits:" << std::setw(12) << std::left << sd.limits().name();
1602   str << ".";
1603   printout(INFO, "Geant4Converter", str.str().c_str());
1604 
1605   for (const auto i : volset )  {
1606     std::map<Volume, G4LogicalVolume*>::iterator v = info.g4Volumes.find(i);
1607     if ( v != info.g4Volumes.end() )   {
1608       G4LogicalVolume* vol = (*v).second;
1609       str.str("");
1610       str << "                                   | " << "Volume:" << std::setw(24) << std::left << vol->GetName() << " "
1611           << vol->GetNoDaughters() << " daughters.";
1612       printout(INFO, "Geant4Converter", str.str().c_str());
1613     }
1614   }
1615 }
1616 
1617 std::string printSolid(G4VSolid* sol) {
1618   std::stringstream str;
1619   if (typeid(*sol) == typeid(G4Box)) {
1620     const G4Box* b = (G4Box*) sol;
1621     str << "++ Box: x=" << b->GetXHalfLength() << " y=" << b->GetYHalfLength() << " z=" << b->GetZHalfLength();
1622   }
1623   else if (typeid(*sol) == typeid(G4Tubs)) {
1624     const G4Tubs* t = (const G4Tubs*) sol;
1625     str << " Tubs: Ri=" << t->GetInnerRadius() << " Ra=" << t->GetOuterRadius() << " z/2=" << t->GetZHalfLength() << " Phi="
1626         << t->GetStartPhiAngle() << "..." << t->GetDeltaPhiAngle();
1627   }
1628   return str.str();
1629 }
1630 
1631 /// Print G4 placement
1632 void* Geant4Converter::printPlacement(const std::string& name, const TGeoNode* node) const {
1633   Geant4GeometryInfo& info = data();
1634   G4VPhysicalVolume*  g4   = info.g4Placements[node];
1635   G4LogicalVolume*    vol  = info.g4Volumes[node->GetVolume()];
1636   G4LogicalVolume*    mot  = info.g4Volumes[node->GetMotherVolume()];
1637   G4VSolid*           sol  = vol->GetSolid();
1638   G4ThreeVector       tr   = g4->GetObjectTranslation();
1639   G4VSensitiveDetector* sd = vol->GetSensitiveDetector();
1640   if ( !sd )  {
1641     return g4;
1642   }
1643   std::stringstream str;
1644   str << "G4Cnv::placement: + " << name << " No:" << node->GetNumber() << " Vol:" << vol->GetName() << " Solid:"
1645       << sol->GetName();
1646   printout(outputLevel, "G4Placement", str.str().c_str());
1647   str.str("");
1648   str << "                  |" << " Loc: x=" << tr.x() << " y=" << tr.y() << " z=" << tr.z();
1649   printout(outputLevel, "G4Placement", str.str().c_str());
1650   printout(outputLevel, "G4Placement", printSolid(sol).c_str());
1651   str.str("");
1652   str << "                  |" << " Ndau:" << vol->GetNoDaughters()
1653       << " physvols." << " Mat:" << vol->GetMaterial()->GetName()
1654       << " Mother:" << (char*) (mot ? mot->GetName().c_str() : "---");
1655   printout(outputLevel, "G4Placement", str.str().c_str());
1656   str.str("");
1657   str << "                  |" << " SD:" << sd->GetName();
1658   printout(outputLevel, "G4Placement", str.str().c_str());
1659   return g4;
1660 }
1661 
1662 /// Create geometry conversion
1663 Geant4Converter& Geant4Converter::create(DetElement top) {
1664   typedef std::map<const TGeoNode*, std::vector<TGeoNode*> > _DAU;
1665   TTimeStamp start;
1666   _DAU daughters;
1667   Geant4GeometryInfo& geo = this->init();
1668   World wrld = top.world();
1669 
1670   m_data->clear();
1671   m_set_data->clear();
1672   m_daughters = &daughters;
1673   geo.manager = &wrld.detectorDescription().manager();
1674   this->collect(top, geo);
1675   this->checkOverlaps = false;
1676   // We do not have to handle defines etc.
1677   // All positions and the like are not really named.
1678   // Hence, start creating the G4 objects for materials, solids and log volumes.
1679   handleArray(this, geo.manager->GetListOfGDMLMatrices(), &Geant4Converter::handleMaterialProperties);
1680   handleArray(this, geo.manager->GetListOfOpticalSurfaces(), &Geant4Converter::handleOpticalSurface);
1681   
1682   handle(this,     geo.volumes, &Geant4Converter::collectVolume);
1683   handle(this,     geo.solids,  &Geant4Converter::handleSolid);
1684   printout(outputLevel, "Geant4Converter", "++ Handled %ld solids.", geo.solids.size());
1685   handleRefs(this, geo.vis,     &Geant4Converter::handleVis);
1686   printout(outputLevel, "Geant4Converter", "++ Handled %ld visualization attributes.", geo.vis.size());
1687   handleMap(this,  geo.limits,  &Geant4Converter::handleLimitSet);
1688   printout(outputLevel, "Geant4Converter", "++ Handled %ld limit sets.", geo.limits.size());
1689   handleMap(this,  geo.regions, &Geant4Converter::handleRegion);
1690   printout(outputLevel, "Geant4Converter", "++ Handled %ld regions.", geo.regions.size());
1691   handle(this,     geo.volumes, &Geant4Converter::handleVolume);
1692   printout(outputLevel, "Geant4Converter", "++ Handled %ld volumes.", geo.volumes.size());
1693   handleRMap(this, *m_data,     &Geant4Converter::handleAssembly);
1694   // Now place all this stuff appropriately
1695   //handleRMap(this, *m_data,     &Geant4Converter::handlePlacement);
1696   std::map<int, std::vector<const TGeoNode*> >::const_reverse_iterator i = m_data->rbegin();
1697   for ( ; i != m_data->rend(); ++i )  {
1698     for ( const TGeoNode* node : i->second )  {
1699       this->handlePlacement(node->GetName(), node);
1700     }
1701   }
1702   /// Handle concrete surfaces
1703   handleArray(this, geo.manager->GetListOfSkinSurfaces(),   &Geant4Converter::handleSkinSurface);
1704   handleArray(this, geo.manager->GetListOfBorderSurfaces(), &Geant4Converter::handleBorderSurface);
1705   //==================== Fields
1706   handleProperties(m_detDesc.properties());
1707   if ( printSensitives )  {
1708     handleMap(this, geo.sensitives, &Geant4Converter::printSensitive);
1709   }
1710   if ( printPlacements )  {
1711     handleRMap(this, *m_data, &Geant4Converter::printPlacement);
1712   }
1713 
1714   m_daughters = nullptr;
1715   geo.setWorld(top.placement().ptr());
1716   geo.valid = true;
1717   TTimeStamp stop;
1718   printout(INFO, "Geant4Converter",
1719            "+++  Successfully converted geometry to Geant4. [%7.3f seconds]",
1720            stop.AsDouble()-start.AsDouble() );
1721   return *this;
1722 }