From e600c2373ce1884ea6f2dd3a342f58eaa894f0c9 Mon Sep 17 00:00:00 2001
From: Andrei Gheata <Andrei.Gheata@cern.ch>
Date: Mon, 29 Apr 2019 17:03:39 +0200
Subject: [PATCH] Added support for material constant properties, including
 GDML persistency.

---
 geom/gdml/inc/TGDMLWrite.h     |  2 ++
 geom/gdml/src/TGDMLParse.cxx   | 43 +++++++++++++++++++++++++------
 geom/gdml/src/TGDMLWrite.cxx   | 29 +++++++++++++++++++++
 geom/geom/inc/TGeoManager.h    |  1 +
 geom/geom/inc/TGeoMaterial.h   | 14 ++++++++--
 geom/geom/src/TGeoManager.cxx  | 18 +++++++++++++
 geom/geom/src/TGeoMaterial.cxx | 47 +++++++++++++++++++++++++++++++++-
 7 files changed, 143 insertions(+), 11 deletions(-)

diff --git a/geom/gdml/inc/TGDMLWrite.h b/geom/gdml/inc/TGDMLWrite.h
index 7b19e943562..d0f008aa507 100644
--- a/geom/gdml/inc/TGDMLWrite.h
+++ b/geom/gdml/inc/TGDMLWrite.h
@@ -140,6 +140,7 @@ private:
    TString          ExtractSolid(TGeoShape* volShape);     //adds <shape> to <solids>
    void             ExtractVolumes(TGeoVolume* volume);    //result <volume> node...  + corresp. shape
    void             ExtractMatrices(TObjArray *matrices);  //adds <matrix> to <define>
+   void             ExtractConstants(TGeoManager *geom);   //adds <constant> to <define>
    void             ExtractOpticalSurfaces(TObjArray *surfaces); //adds <opticalsurface> to <solids>
    void             ExtractSkinSurfaces(TObjArray *surfaces);    //adds <skinsurface> to <structure>
    void             ExtractBorderSurfaces(TObjArray *surfaces);  //adds <bordersurface> to <structure>
@@ -206,6 +207,7 @@ private:
    XMLNodePointer_t CreatePositionN(const char * name, Xyz position, const char * type = "position", const char * unit = "cm");
    XMLNodePointer_t CreateRotationN(const char * name, Xyz rotation, const char * type = "rotation", const char * unit = "deg");
    XMLNodePointer_t CreateMatrixN(TGDMLMatrix const *matrix);
+   XMLNodePointer_t CreateConstantN(const char *name, Double_t value);
    TGeoCompositeShape* CreateFakeCtub(TGeoCtub * geoShape);  //create fake cut tube as intersection
 
    //check name (2nd parameter) whether it is in the list (1st parameter)
diff --git a/geom/gdml/src/TGDMLParse.cxx b/geom/gdml/src/TGDMLParse.cxx
index 189d6c0abb4..45b92c9f789 100644
--- a/geom/gdml/src/TGDMLParse.cxx
+++ b/geom/gdml/src/TGDMLParse.cxx
@@ -471,7 +471,9 @@ XMLNodePointer_t TGDMLParse::ConProcess(TXMLEngine* gdml, XMLNodePointer_t node,
    // name = TString::Format("%s_%s", name.Data(), fCurrentFile);
    //}
 
-   fconsts[name.Data()] = Value(value);
+   Double_t val = Value(value);
+   fconsts[name.Data()] = val;
+   gGeoManager->AddProperty(name.Data(), val);
 
    return node;
 }
@@ -1275,8 +1277,9 @@ XMLNodePointer_t TGDMLParse::MatProcess(TXMLEngine* gdml, XMLNodePointer_t node,
 
    TGeoManager* mgr = gGeoManager;
    TGeoElementTable* tab_ele = mgr->GetElementTable();
-   TList properties;
+   TList properties, constproperties;
    properties.SetOwner();
+   constproperties.SetOwner();
    // We have to assume the media are monotonic increasing starting with 1
    static int medid = mgr->GetListOfMedia()->GetSize()+1;
    XMLNodePointer_t child = gdml->GetChild(node);
@@ -1314,9 +1317,15 @@ XMLNodePointer_t TGDMLParse::MatProcess(TXMLEngine* gdml, XMLNodePointer_t node,
                else if(tempattr == "ref") {
                   property->SetTitle(gdml->GetAttrValue(attr));
                   TGDMLMatrix *matrix = fmatrices[property->GetTitle()];
-                  if (!matrix)
-                     Error("MatProcess", "Reference matrix %s for material %s not found", property->GetTitle(), name.Data());
-                  properties.Add(property);
+                  if (matrix) properties.Add(property);
+                  else {
+                     Bool_t error = 0;
+                     gGeoManager->GetProperty(property->GetTitle(), &error);
+                     if (error)
+                        Error("MatProcess", "Reference %s for material %s not found", property->GetTitle(), name.Data());
+                     else
+                        constproperties.Add(property);
+                  }
                }
                attr = gdml->GetNextAttr(attr);
             }
@@ -1373,6 +1382,12 @@ XMLNodePointer_t TGDMLParse::MatProcess(TXMLEngine* gdml, XMLNodePointer_t node,
          while ((property = (TNamed*)next()))
             mat->AddProperty(property->GetName(), property->GetTitle());
       }
+      if (constproperties.GetSize()) {
+         TNamed *property;
+         TIter next(&constproperties);
+         while ((property = (TNamed*)next()))
+            mat->AddConstProperty(property->GetName(), property->GetTitle());
+      }
       mixflag = 0;
       //Note: Object(name, title) corresponds to Element(formula, name)
       TGeoElement* mat_ele = tab_ele->FindElement(mat_name);
@@ -1405,9 +1420,15 @@ XMLNodePointer_t TGDMLParse::MatProcess(TXMLEngine* gdml, XMLNodePointer_t node,
                else if(tempattr == "ref") {
                   property->SetTitle(gdml->GetAttrValue(attr));
                   TGDMLMatrix *matrix = fmatrices[property->GetTitle()];
-                  if (!matrix)
-                     Error("MatProcess", "Reference matrix %s for material %s not found", property->GetTitle(), name.Data());
-                  properties.Add(property);
+                  if (matrix) properties.Add(property);
+                  else {
+                     Bool_t error = 0;
+                     gGeoManager->GetProperty(property->GetTitle(), &error);
+                     if (error)
+                        Error("MatProcess", "Reference %s for material %s not found", property->GetTitle(), name.Data());
+                     else
+                        constproperties.Add(property);
+                  }
                }
                attr = gdml->GetNextAttr(attr);
             }
@@ -1495,6 +1516,12 @@ XMLNodePointer_t TGDMLParse::MatProcess(TXMLEngine* gdml, XMLNodePointer_t node,
          while ((property = (TNamed*)next()))
             mix->AddProperty(property->GetName(), property->GetTitle());
       }
+      if (constproperties.GetSize()) {
+         TNamed *property;
+         TIter next(&constproperties);
+         while ((property = (TNamed*)next()))
+            mix->AddConstProperty(property->GetName(), property->GetTitle());
+      }
       Int_t natoms;
       Double_t weight;
 
diff --git a/geom/gdml/src/TGDMLWrite.cxx b/geom/gdml/src/TGDMLWrite.cxx
index f6758caa5f1..958d45be607 100644
--- a/geom/gdml/src/TGDMLWrite.cxx
+++ b/geom/gdml/src/TGDMLWrite.cxx
@@ -354,6 +354,7 @@ void TGDMLWrite::WriteGDMLfile(TGeoManager * geomanager,
    time_t startT, endT;
    startT = time(NULL);
    ExtractMatrices(geomanager->GetListOfGDMLMatrices());
+   ExtractConstants(geomanager);
    fMaterialsNode = ExtractMaterials(materialsLst);
 
    Info("WriteGDMLfile", "Extracting volumes");
@@ -402,6 +403,22 @@ void TGDMLWrite::ExtractMatrices(TObjArray* matrixList)
    }
 }
 
+////////////////////////////////////////////////////////////////////////////////
+/// Method exporting GDML matrices
+
+void TGDMLWrite::ExtractConstants(TGeoManager *geom)
+{
+   if (!geom->GetNproperties()) return;
+   XMLNodePointer_t constantN;
+   TString property;
+   Double_t value;
+   for (Int_t i = 0; i < geom->GetNproperties(); ++i) {
+      value = geom->GetProperty(i, property);
+      constantN = CreateConstantN(property.Data(), value);
+      fGdmlE->AddChild(fDefineNode, constantN);
+   }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 /// Method exporting optical surfaces
 
@@ -1804,6 +1821,18 @@ XMLNodePointer_t TGDMLWrite::CreateMatrixN(TGDMLMatrix const *matrix)
    return mainN;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+/// Creates "constant" kind of node for GDML
+
+XMLNodePointer_t TGDMLWrite::CreateConstantN(const char *name, Double_t value)
+{
+   XMLNodePointer_t mainN = fGdmlE->NewChild(0, 0, "constant", 0);
+   const TString fltPrecision = TString::Format("%%.%dg", fFltPrecision);
+   fGdmlE->NewAttr(mainN, 0, "name", name);
+   fGdmlE->NewAttr(mainN, 0, "value", TString::Format(fltPrecision.Data(), value));
+   return mainN;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 /// Creates "setup" node for GDML
 
diff --git a/geom/geom/inc/TGeoManager.h b/geom/geom/inc/TGeoManager.h
index 51fdd36c0d2..c8f40e40abd 100644
--- a/geom/geom/inc/TGeoManager.h
+++ b/geom/geom/inc/TGeoManager.h
@@ -177,6 +177,7 @@ public:
    TGeoNavigator         *AddNavigator();
    Bool_t                 AddProperty(const char *property, Double_t value);
    Double_t               GetProperty(const char *name, Bool_t *error = nullptr) const;
+   Double_t               GetProperty(size_t i, TString &name, Bool_t *error = nullptr) const;
    Int_t                  GetNproperties() const { return fProperties.size(); }
    void                   ClearOverlaps();
    void                   RegisterMatrix(const TGeoMatrix *matrix);
diff --git a/geom/geom/inc/TGeoMaterial.h b/geom/geom/inc/TGeoMaterial.h
index 5325832f10c..c6dc87e7f85 100644
--- a/geom/geom/inc/TGeoMaterial.h
+++ b/geom/geom/inc/TGeoMaterial.h
@@ -54,6 +54,7 @@ protected:
    TObject                 *fCerenkov;   // pointer to class with Cerenkov properties
    TGeoElement             *fElement;    // pointer to element composing the material
    TList                    fProperties; // user-defined properties
+   TList                    fConstProperties; // user-defined constant properties
    TGeoExtension           *fUserExtension;  //! Transient user-defined extension to materials
    TGeoExtension           *fFWExtension;    //! Transient framework-defined extension to materials
 
@@ -81,8 +82,17 @@ public:
    virtual void             FillMaterialEvolution(TObjArray *population, Double_t precision=0.001);
    // getters & setters
    bool                     AddProperty(const char *property, const char *ref);
-   const char              *GetPropertyRef(const char *property);
+   bool                     AddConstProperty(const char *property, const char *ref);
+   Int_t                    GetNproperties() const { return fProperties.GetSize(); }
+   Int_t                    GetNconstProperties() const { return fConstProperties.GetSize(); }
+   const char              *GetPropertyRef(const char *property) const;
+   const char              *GetPropertyRef(Int_t i) const { return (fProperties.At(i) ? fProperties.At(i)->GetTitle() : nullptr); }
+   Double_t                 GetConstProperty(const char *property, Bool_t *error = nullptr) const;
+   Double_t                 GetConstProperty(Int_t i, Bool_t *error = nullptr) const;
+   const char              *GetConstPropertyRef(const char *property) const;
+   const char              *GetConstPropertyRef(Int_t i) const { return (fConstProperties.At(i) ? fConstProperties.At(i)->GetTitle() : nullptr); }
    TList const             &GetProperties() const { return fProperties; }
+   TList const             &GetConstProperties() const { return fConstProperties; }
    virtual Int_t            GetByteCount() const {return sizeof(*this);}
    virtual Double_t         GetA() const       {return fA;}
    virtual Double_t         GetZ()  const      {return fZ;}
@@ -128,7 +138,7 @@ public:
 
 
 
-   ClassDef(TGeoMaterial, 6)              // base material class
+   ClassDef(TGeoMaterial, 7)              // base material class
 
 //***** Need to add classes and globals to LinkDef.h *****
 };
diff --git a/geom/geom/src/TGeoManager.cxx b/geom/geom/src/TGeoManager.cxx
index d4eaf3ae03b..066ce7e5b5c 100644
--- a/geom/geom/src/TGeoManager.cxx
+++ b/geom/geom/src/TGeoManager.cxx
@@ -776,6 +776,24 @@ Double_t TGeoManager::GetProperty(const char *property, Bool_t *error) const
    return pos->second;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+/// Get a user-defined property from a given index
+
+Double_t TGeoManager::GetProperty(size_t i, TString &name, Bool_t *error) const
+{
+   // This is a quite inefficient way to access map elements, but needed for the GDML writer to 
+   if (i >= fProperties.size()) {
+      if (error) *error = kTRUE;
+      return 0.;
+   }
+   size_t pos = 0;
+   auto it = fProperties.begin();
+   while (pos < i) { ++it; ++pos; }
+   if (error) *error = kFALSE;
+   name = (*it).first;
+   return (*it).second;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 /// Add a matrix to the list. Returns index of the matrix in list.
 
diff --git a/geom/geom/src/TGeoMaterial.cxx b/geom/geom/src/TGeoMaterial.cxx
index c3207756d29..84feabbf717 100644
--- a/geom/geom/src/TGeoMaterial.cxx
+++ b/geom/geom/src/TGeoMaterial.cxx
@@ -286,13 +286,45 @@ void TGeoMaterial::SetUserExtension(TGeoExtension *ext)
 }
 
 //_____________________________________________________________________________
-const char *TGeoMaterial::GetPropertyRef(const char *property)
+const char *TGeoMaterial::GetPropertyRef(const char *property) const
 {
    // Find reference for a given property
    TNamed *prop = (TNamed*)fProperties.FindObject(property);
    return (prop) ? prop->GetTitle() : nullptr;
 }
 
+//_____________________________________________________________________________
+const char *TGeoMaterial::GetConstPropertyRef(const char *property) const
+{
+   // Find reference for a given constant property
+   TNamed *prop = (TNamed*)fConstProperties.FindObject(property);
+   return (prop) ? prop->GetTitle() : nullptr;
+}
+
+//_____________________________________________________________________________
+Double_t TGeoMaterial::GetConstProperty(const char *property, Bool_t *err) const
+{
+   // Find reference for a given constant property
+   TNamed *prop = (TNamed*)fConstProperties.FindObject(property);
+   if (!prop) {
+      if (err) *err = kTRUE;
+      return 0.;
+   }
+   return gGeoManager->GetProperty(prop->GetTitle(), err);
+}
+
+//_____________________________________________________________________________
+Double_t TGeoMaterial::GetConstProperty(Int_t i, Bool_t *err) const
+{
+   // Find reference for a given constant property
+   TNamed *prop = (TNamed*)fConstProperties.At(i);
+   if (!prop) {
+      if (err) *err = kTRUE;
+      return 0.;
+   }
+   return gGeoManager->GetProperty(prop->GetTitle(), err);
+}
+
 //_____________________________________________________________________________
 bool TGeoMaterial::AddProperty(const char *property, const char *ref)
 {
@@ -306,6 +338,19 @@ bool TGeoMaterial::AddProperty(const char *property, const char *ref)
    return true;
 }
 
+//_____________________________________________________________________________
+bool TGeoMaterial::AddConstProperty(const char *property, const char *ref)
+{
+   fConstProperties.SetOwner();
+   if (GetConstPropertyRef(property)) {
+      Error("AddConstProperty", "Constant property %s already added to material %s",
+         property, GetName());
+      return false;
+   }
+   fConstProperties.Add(new TNamed(property, ref));
+   return true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 /// Connect framework defined extension to the material. The material "grabs" a copy,
 /// so the original object can be released by the producer. Release the previously
-- 
GitLab