From 256c37db9aa7ac15f63c7390135ca6d82d01aaed Mon Sep 17 00:00:00 2001
From: Markus Frank <Markus.Frank@cern.ch>
Date: Mon, 6 May 2024 20:17:20 +0200
Subject: [PATCH] Allow definition of recursively defined shapes using shape
 plugins

---
 DDCore/src/XML/Utilities.cpp        | 152 ++++++++++++++++++++--------
 DDCore/src/plugins/ShapePlugins.cpp |  14 ++-
 2 files changed, 121 insertions(+), 45 deletions(-)

diff --git a/DDCore/src/XML/Utilities.cpp b/DDCore/src/XML/Utilities.cpp
index 6ffbf2445..16bf0e264 100644
--- a/DDCore/src/XML/Utilities.cpp
+++ b/DDCore/src/XML/Utilities.cpp
@@ -14,9 +14,9 @@
 
 // Framework include files
 #include <XML/Utilities.h>
-#include <DD4hep/Printout.h>
 #include <DD4hep/Plugins.h>
 #include <DD4hep/Detector.h>
+#include <DD4hep/Printout.h>
 #include <DD4hep/DetFactoryHelper.h>
 #include <Math/Polar2D.h>
 
@@ -98,29 +98,98 @@ dd4hep::Solid dd4hep::xml::createShape(Detector& description,
 
 /// Create a volume using the plugin mechanism from the attributes of the XML element
 dd4hep::Volume dd4hep::xml::createStdVolume(Detector& description, xml::Element element)    {
-  xml_dim_t e(element);
-  if ( e.hasAttr(_U(material)) )   {
-    xml_dim_t x_s = e.child(_U(shape));
-    std::string    typ = x_s.typeStr();
-    Material  mat = description.material(e.attr<std::string>(_U(material)));
-    Solid     sol = createShape(description, typ, x_s);
-    Volume    vol("volume", sol, mat);
-    if ( e.hasAttr(_U(name)) ) vol->SetName(e.attr<std::string>(_U(name)).c_str());
-    vol.setAttributes(description,e.regionStr(),e.limitsStr(),e.visStr());
-    return vol;
+  Volume      vol;
+  int         dbg = 1;
+  xml_dim_t   elt(element);
+  std::string typ, tag = elt.tag();
+  PrintLevel  lvl   = dbg ? ALWAYS : DEBUG;
+
+  printout(lvl, "xml::createStdVolume", "++ Processing tag: %-12s", tag.c_str());
+  if ( elt.hasAttr(_U(material)) )   {
+    xml_dim_t x_s = elt.child(_U(shape));
+    Solid     sol = createShape(description, x_s.typeStr(), x_s);
+    Material  mat = description.material(elt.attr<std::string>(_U(material)));
+    vol = Volume("volume", sol, mat);
+    if ( elt.hasAttr(_U(name)) ) vol->SetName(elt.nameStr().c_str());
+    vol.setAttributes(description,elt.regionStr(),elt.limitsStr(),elt.visStr());
+    printout(lvl, "xml::createStdVolume", "++ --> name: %s vis: %s region: %s limits: %s",
+             vol.name(),
+             elt.visStr("").c_str(),
+             elt.regionStr("").c_str(),
+             elt.limitsStr("").c_str());
+    elt = x_s;
+  }
+  else if ( elt.hasAttr(_U(type)) )  {
+    typ = elt.typeStr();
+    if ( typ == "CAD_Assembly" || typ == "CAD_MultiVolume" || typ == "GenVolume" )
+      vol = xml::createVolume(description, typ, elt);
+    else if ( typ.substr(1) == "ssembly" )
+      vol = Assembly("assembly");
+    if ( elt.hasAttr(_U(name)) ) vol->SetName(elt.nameStr().c_str());
+    vol.setAttributes(description,elt.regionStr(),elt.limitsStr(),elt.visStr());
+    printout(lvl, "xml::createStdVolume", "++ --> name: %s vis: %s region: %s limits: %s",
+             vol.name(),
+             elt.visStr("").c_str(),
+             elt.regionStr("").c_str(),
+             elt.limitsStr("").c_str());
   }
-  xml_h h = e;
-  xml_attr_t a = h.attr_nothrow(_U(type));
-  if ( a )   {
-    std::string typ = h.attr<std::string>(a);
-    if ( typ.substr(1) == "ssembly" )  {
-      Assembly vol("assembly");
-      if ( e.hasAttr(_U(name)) ) vol->SetName(e.attr<std::string>(_U(name)).c_str());
-      vol.setAttributes(description,e.regionStr(),e.limitsStr(),e.visStr());
-      return vol;
+  if ( vol.isValid() )  {
+    for( xml_coll_t xv(elt, _U(volume)); xv; ++xv )    {
+      xml_dim_t xvol = xv;
+      xml_dim_t xshap = xv.child(_U(shape), false);
+      /** Trigger on:
+       *
+       *  <volume name="..." type="Assembly">
+       *  or
+       *  <volume name="..." material="Air">
+       *
+       *    <shape  ....  />
+       *    or 
+       *    <volume  ....  />
+       *
+       *    optional:
+       *    <position x="0" ... />
+       *    <rotation x="0" ... />
+       *  </volume>
+       */
+      if ( xvol && ((xvol.hasAttr(_U(material)) && xshap) || xvol.hasAttr(_U(type))) )  {
+        RotationZYX rot;
+        Position    pos;
+        xml_comp_t  x_rot = xvol.child( _U(rotation), false );
+        xml_comp_t  x_pos = xvol.child( _U(position), false );
+        Volume      child = xml::createStdVolume( description, xvol );
+        if ( x_rot ) rot = RotationZYX( x_rot.z(0),x_rot.y(0),x_rot.x(0) );
+        if ( x_pos ) pos = Position( x_pos.x(0),x_pos.y(0),x_pos.z(0) );
+        Transform3D trafo(Rotation3D(rot), pos);
+        vol.placeVolume( child, trafo );
+      }
+    }
+    for( xml_coll_t xs(elt, _U(shape)); xs; ++xs )    {
+      xml_dim_t xshap = xs;
+      /** Trigger on:
+       *  - typical for CAD multi-volume shapes
+       *
+       *  <shape name="Assembly" type="Assembly">
+       *    <shape  ....  />
+       *    <position x="0" ... />
+       *    <rotation x="0" ... />
+       *  </shape>
+       */
+      if ( xshap && xshap.hasAttr(_U(type)) )  {
+        RotationZYX rot;
+        Position    pos;
+        xml_comp_t  x_rot = xshap.child( _U(rotation), false );
+        xml_comp_t  x_pos = xshap.child( _U(position), false );
+        Volume      child = xml::createStdVolume( description, xshap );
+        if ( x_rot ) rot = RotationZYX( x_rot.z(0),x_rot.y(0),x_rot.x(0) );
+        if ( x_pos ) pos = Position( x_pos.x(0),x_pos.y(0),x_pos.z(0) );
+        Transform3D trafo(Rotation3D(rot), pos);
+        vol.placeVolume( child, trafo );
+      }
     }
+    return vol;
   }
-  dd4hep::except("xml::createVolume","Failed to create volume. No material specified!");
+  dd4hep::except("xml::createStdVolume","Failed to create volume. No material specified!");
   return Volume();
 }
 
@@ -155,29 +224,28 @@ dd4hep::Volume dd4hep::xml::createPlacedEnvelope( dd4hep::Detector& description,
   xml_det_t     x_det     = e;
   std::string   det_name  = x_det.nameStr();
   
-  xml_comp_t    x_env     =  x_det.child( dd4hep::xml::Strng_t("envelope") ) ;
+  xml_comp_t    x_env     =  x_det.child( dd4hep::xml::Strng_t("envelope") );
   xml_comp_t    x_shape   =  x_env.child( _U(shape) ); 
-  
-  
-  bool useRot = false ;
-  bool usePos = false ; 
-  Position    pos ;
-  RotationZYX rot ;
+
+  bool useRot = false;
+  bool usePos = false; 
+  Position    pos;
+  RotationZYX rot;
 
   if( x_env.hasChild( _U(position) ) ) {
-    usePos = true ;
+    usePos = true;
     xml_comp_t env_pos = x_env.position();
     pos = Position( env_pos.x(),env_pos.y(),env_pos.z() );  
   }
   if( x_env.hasChild( _U(rotation) ) ) {
-    useRot = true ;
+    useRot = true;
     xml_comp_t    env_rot     = x_env.rotation();
-    rot = RotationZYX( env_rot.z(),env_rot.y(),env_rot.x() ) ;
+    rot = RotationZYX( env_rot.z(),env_rot.y(),env_rot.x() );
   }
 
   Volume  envelope;
   if(  x_shape.typeStr() == "Assembly" ){
-    envelope = Assembly( det_name+"_assembly" ) ;
+    envelope = Assembly( det_name+"_assembly" );
   } else { 
     // ---- create a shape from the specified xml element --------
     Box  env_solid = xml_comp_t( x_shape ).createShape();
@@ -187,28 +255,28 @@ dd4hep::Volume dd4hep::xml::createPlacedEnvelope( dd4hep::Detector& description,
                      "Cannot create envelope volume : %s for detector %s.",
                      x_shape.typeStr().c_str(), det_name.c_str());
     }
-    Material      env_mat   = description.material( x_shape.materialStr() );
+    Material env_mat   = description.material( x_shape.materialStr() );
     envelope = Volume( det_name+"_envelope", env_solid, env_mat );
   }
-  PlacedVolume  env_pv  ; 
+  PlacedVolume  env_pv; 
   Volume        mother = description.pickMotherVolume(sdet);
   // ---- place the envelope into the mother volume 
   //      only specify transformations given in xml
   //      to allow for optimization 
 
   if( useRot && usePos ){
-    env_pv =  mother.placeVolume( envelope , Transform3D( rot, pos )  ) ;
+    env_pv =  mother.placeVolume( envelope , Transform3D( rot, pos )  );
   } else if( useRot ){
-    env_pv =  mother.placeVolume( envelope , rot  ) ;
+    env_pv =  mother.placeVolume( envelope , rot  );
   } else if( usePos ){
-    env_pv =  mother.placeVolume( envelope , pos  ) ;
+    env_pv =  mother.placeVolume( envelope , pos  );
   } else {
     env_pv = mother.placeVolume( envelope );
   }
 
   // ----------------------------------------------
   env_pv.addPhysVolID("system", sdet.id());
-  sdet.setPlacement( env_pv ) ;
+  sdet.setPlacement( env_pv );
   envelope.setAttributes( description,x_det.regionStr(),x_det.limitsStr(),x_env.visStr());
   return envelope;
 }
@@ -218,10 +286,10 @@ void  dd4hep::xml::setDetectorTypeFlag( dd4hep::xml::Handle_t e, dd4hep::DetElem
   std::string   det_name  = x_det.nameStr();
   
   try{
-    xml_comp_t  x_dettype = x_det.child( dd4hep::xml::Strng_t("type_flags") ) ;
-    unsigned int typeFlag = x_dettype.type() ;
-    printout(DEBUG,"Utilities","+++ setDetectorTypeFlags for detector: %s set to 0x%x", det_name.c_str(), typeFlag ) ;
-    sdet.setTypeFlag( typeFlag ) ;
+    xml_comp_t  x_dettype = x_det.child( dd4hep::xml::Strng_t("type_flags") );
+    unsigned int typeFlag = x_dettype.type();
+    printout(DEBUG,"Utilities","+++ setDetectorTypeFlags for detector: %s set to 0x%x", det_name.c_str(), typeFlag );
+    sdet.setTypeFlag( typeFlag );
   }
   catch(const std::runtime_error& err)   {
     printout(INFO,"Utilities",
diff --git a/DDCore/src/plugins/ShapePlugins.cpp b/DDCore/src/plugins/ShapePlugins.cpp
index 8646a4e87..212b1f151 100644
--- a/DDCore/src/plugins/ShapePlugins.cpp
+++ b/DDCore/src/plugins/ShapePlugins.cpp
@@ -954,7 +954,7 @@ static Ref_t create_shape(Detector& description, xml_h e, SensitiveDetector sens
     mat = description.material(x_det.child(_U(material)).attr<std::string>(_U(name)));
     printout(INFO,"TestShape","+++ Volume material is %s", mat.name());      
   }
-  for ( xml_coll_t itm(e, _U(check)); itm; ++itm, ++count )   {
+  for ( xml_coll_t itm(e, _U(check)); itm; ++itm, ++count )  {
     xml_dim_t   x_check  = itm;
     xml_comp_t  shape      (x_check.child(_U(shape)));
     xml_dim_t   pos        (x_check.child(_U(position), false));
@@ -964,13 +964,21 @@ static Ref_t create_shape(Detector& description, xml_h e, SensitiveDetector sens
     bool        reflectY = x_check.hasChild(_U(reflect_y));
     bool        reflectX = x_check.hasChild(_U(reflect_x));
     std::string shape_type = shape.typeStr();
-    Solid       solid;
     Volume      volume;
+    Solid       solid;
 
-    if ( shape_type == "CAD_Assembly" || shape_type == "CAD_MultiVolume" )   {
+    if ( shape_type == "CAD_Assembly" || shape_type == "CAD_MultiVolume" )  {
       volume = xml::createVolume(description, shape_type, shape);
       solid  = volume->GetShape();
     }
+    else if ( shape_type == "StdVolume" )  {
+      volume = xml::createStdVolume(description, shape.child(_U(volume)));
+      solid  = volume->GetShape();
+    }
+    else if ( shape_type == "GenVolume" )  {
+      volume = xml::createVolume(description, shape_type, shape.child(_U(volume)));
+      solid  = volume->GetShape();
+    }
     else   {
       solid  = xml::createShape(description, shape_type, shape);
       volume = Volume(name+_toString(count,"_vol_%d"),solid, mat);
-- 
GitLab