From 36280e888f84915a6a296fa55a0d26d06eff9bcf Mon Sep 17 00:00:00 2001
From: Markus Frank <Markus.Frank@cern.ch>
Date: Wed, 11 Mar 2020 11:31:44 +0100
Subject: [PATCH] Finalize CAD volume plugin

---
 DDCAD/include/DDCAD/ASSIMPReader.h            |   2 +-
 DDCAD/include/DDCAD/InputReader.h             |   2 +-
 DDCAD/src/ASSIMPReader.cpp                    |  10 +-
 DDCAD/src/plugins/CADPlugins.cpp              | 145 ++++++++++++++-
 DDCore/include/DD4hep/DDTest.h                | 176 ++++++++++++++++++
 DDCore/src/plugins/ShapePlugins.cpp           |  14 +-
 examples/ClientTests/compact/CheckShape.xml   |  19 +-
 .../DDCAD/compact/Check_Shape_MS3D_jeep.xml   |   2 +-
 .../DDCAD/compact/Check_Shape_OBJ_spider.xml  |   2 +-
 examples/DDCAD/compact/Check_Shape_Test1.xml  |   2 +-
 .../DDCAD/compact/MultiShape_MS3D_jeep.xml    |  52 ++++++
 11 files changed, 403 insertions(+), 23 deletions(-)
 create mode 100644 DDCore/include/DD4hep/DDTest.h
 create mode 100644 examples/DDCAD/compact/MultiShape_MS3D_jeep.xml

diff --git a/DDCAD/include/DDCAD/ASSIMPReader.h b/DDCAD/include/DDCAD/ASSIMPReader.h
index 8f87caac2..c1c71e330 100644
--- a/DDCAD/include/DDCAD/ASSIMPReader.h
+++ b/DDCAD/include/DDCAD/ASSIMPReader.h
@@ -31,7 +31,7 @@ namespace dd4hep {
       virtual ~ASSIMPReader() = default;
       /// Read input file
       virtual std::vector<std::unique_ptr<TGeoTessellated> >
-        read(const std::string& source)  const  override;      
+      read(const std::string& source, double unit_Length)  const  override;      
     };
     
   }        /* End namespace cad                      */
diff --git a/DDCAD/include/DDCAD/InputReader.h b/DDCAD/include/DDCAD/InputReader.h
index f7ecaa573..33047cfab 100644
--- a/DDCAD/include/DDCAD/InputReader.h
+++ b/DDCAD/include/DDCAD/InputReader.h
@@ -37,7 +37,7 @@ namespace dd4hep {
       virtual ~InputReader();
       /// Read input file
       virtual std::vector<std::unique_ptr<TGeoTessellated> >
-        read(const std::string& source)  const  = 0;
+      read(const std::string& source, double unit_length)  const  = 0;
     };
     
   }        /* End namespace cad                      */
diff --git a/DDCAD/src/ASSIMPReader.cpp b/DDCAD/src/ASSIMPReader.cpp
index d6ff10966..4b7ffd606 100644
--- a/DDCAD/src/ASSIMPReader.cpp
+++ b/DDCAD/src/ASSIMPReader.cpp
@@ -28,7 +28,7 @@ using namespace dd4hep::cad;
 
 /// Read input file
 vector<unique_ptr<TGeoTessellated> >
-ASSIMPReader::read(const string& source)  const  {
+ASSIMPReader::read(const string& source, double unit_length)  const  {
   vector<unique_ptr<TGeoTessellated> > result;
   unique_ptr<Assimp::Importer> importer = make_unique<Assimp::Importer>();
   int flags = aiProcess_Triangulate|aiProcess_JoinIdenticalVertices|aiProcess_CalcTangentSpace;
@@ -36,6 +36,7 @@ ASSIMPReader::read(const string& source)  const  {
   if ( !scene )  {
     except("ASSIMPReader","+++ FileNotFound: %s",source.c_str());
   }
+  double unit = unit_length;
   for (unsigned int index = 0; index < scene->mNumMeshes; index++)   {
     aiMesh* mesh = scene->mMeshes[index];
     if ( mesh->mNumFaces > 0 )   {
@@ -45,9 +46,9 @@ ASSIMPReader::read(const string& source)  const  {
       for(unsigned int i=0; i < mesh->mNumFaces; i++)  {
         const aiFace&     face = mesh->mFaces[i];
         const unsigned int* idx = face.mIndices;
-        Tessellated::Vertex_t a(v[idx[0]].x, v[idx[0]].y, v[idx[0]].z); 
-        Tessellated::Vertex_t b(v[idx[1]].x, v[idx[1]].y, v[idx[1]].z); 
-        Tessellated::Vertex_t c(v[idx[2]].x, v[idx[2]].y, v[idx[2]].z); 
+        Tessellated::Vertex_t a(v[idx[0]].x*unit, v[idx[0]].y*unit, v[idx[0]].z*unit); 
+        Tessellated::Vertex_t b(v[idx[1]].x*unit, v[idx[1]].y*unit, v[idx[1]].z*unit); 
+        Tessellated::Vertex_t c(v[idx[2]].x*unit, v[idx[2]].y*unit, v[idx[2]].z*unit); 
         shape->AddFacet(a,b,c);
 #if 0
         if ( scene->HasMaterials() )   {
@@ -65,4 +66,3 @@ ASSIMPReader::read(const string& source)  const  {
   }
   return result;
 }
-
diff --git a/DDCAD/src/plugins/CADPlugins.cpp b/DDCAD/src/plugins/CADPlugins.cpp
index bd9df0b9c..62a98425e 100644
--- a/DDCAD/src/plugins/CADPlugins.cpp
+++ b/DDCAD/src/plugins/CADPlugins.cpp
@@ -26,7 +26,8 @@ using namespace dd4hep::detail;
 static Handle<TObject> create_CAD_Shape(Detector&, xml_h e)   {
   xml_elt_t elt(e);
   string fname = elt.attr<string>(_U(ref));
-  auto shapes = cad::ASSIMPReader().read(fname);
+  double unit  = elt.hasAttr(_U(unit)) ? elt.attr<double>(_U(unit)) : dd4hep::cm;
+  auto shapes = cad::ASSIMPReader().read(fname, unit);
   if ( shapes.empty() )   {
     except("CAD_Shape","+++ CAD file: %s does not contain any "
            "understandable tessellated shapes.", fname.c_str());
@@ -56,10 +57,11 @@ static Handle<TObject> create_CAD_Shape(Detector&, xml_h e)   {
 DECLARE_XML_SHAPE(CAD_Shape__shape_constructor,create_CAD_Shape)
 
 
-static Handle<TObject> create_CAD_MultiShape(Detector&, xml_h e)   {
+static Handle<TObject> create_CAD_MultiShape_Assembly(Detector&, xml_h e)   {
   xml_elt_t elt(e);
   string fname = elt.attr<string>(_U(ref));
-  auto shapes = cad::ASSIMPReader().read(fname);
+  double unit  = elt.hasAttr(_U(unit)) ? elt.attr<double>(_U(unit)) : dd4hep::cm;
+  auto shapes = cad::ASSIMPReader().read(fname, unit);
   if ( shapes.empty() )   {
     except("CAD_Shape","+++ CAD file: %s does not contain any "
            "understandable tessellated shapes.", fname.c_str());
@@ -75,4 +77,139 @@ static Handle<TObject> create_CAD_MultiShape(Detector&, xml_h e)   {
   if ( elt.hasAttr(_U(name)) ) assembly->SetName(elt.attr<string>(_U(name)).c_str());
   return assembly;
 }
-DECLARE_XML_SHAPE(CAD_MultiShape__volume_constructor,create_CAD_MultiShape)
+DECLARE_XML_VOLUME(CAD_Assembly__volume_constructor,create_CAD_MultiShape_Assembly)
+
+
+/**
+ *   <XXX ref="file-name"  material="material-name">   
+ *     <material name="material-name"/>                        <!-- alternative: child or attr -->
+ *
+ *     Envelope:  Use special envelop shape (default: assembly)
+ *                The envelope tag must match the expected pattern of the utility
+ *                dd4hep::xml::createStdVolume(Detector& desc, xml::Element e)
+ *     <envelope name="volume-name" material="material-name">
+ *       <shape name="shape-name" type="shape-type" args....>
+ *       </shape>
+ *     </envelope>
+ *
+ *     Option 1:  No additional children. use default material 
+ *                and place all children in the origin of the envelope
+ *
+ *     Option 2:  Volume with default material
+ *     <volume name="vol-name"/>
+ *
+ *     Option 3:  Volume with non-default material
+ *     <volume name="vol-name" material="material-name"/>
+ *
+ *     Option 4:  Volume with optional placement. No position = (0,0,0), No rotation = (0,0,0)
+ *     <volume name="vol-name" material="material-name"/>
+ *       <position x="0" y="0" z="5*cm"/>
+ *       <rotation x="0" y="0" z="0.5*pi*rad"/>
+ *     </volume>
+ *
+ *     For sensitive volumes: add physical volume IDs:
+ *     <volume name="vol-name" material="material-name"/>
+ *       <physvolid name="layer" value="1"/>
+ *       <physvolid name="slice" value="10"/>
+ *     </volume>
+ *
+ *   </XXX>
+ */
+static Handle<TObject> create_CAD_Volume(Detector& dsc, xml_h e)   {
+  xml_elt_t elt(e);
+  string fname = elt.attr<string>(_U(ref));
+  double unit  = elt.attr<double>(_U(unit));
+  auto shapes = cad::ASSIMPReader().read(fname, unit);
+  if ( shapes.empty() )   {
+    except("CAD_Volume","+++ CAD file: %s does not contain any "
+           "understandable tessellated shapes.", fname.c_str());
+  }
+  Volume envelope;
+  if ( elt.hasChild(_U(envelope)) )   {
+    string   typ   = "DD4hep_StdVolume";
+    xml_h    x_env = elt.child(_U(envelope));
+    TObject* pvol  = PluginService::Create<TObject*>(typ, &dsc, &x_env);
+    envelope = dynamic_cast<TGeoVolume*>(pvol);
+    if ( !envelope.isValid() )   {
+      except("CAD_Volume",
+             "+++ Unable to determine envelope to CAD shape: %s",fname.c_str());
+    }
+  }
+  else   {
+    envelope = Assembly("envelope");
+  }
+  xml_dim_t x_envpos = elt.child(_U(position),false);
+  xml_dim_t x_envrot = elt.child(_U(rotation),false);
+  Position env_pos;
+  RotationZYX env_rot;
+  if ( x_envpos && x_envrot )   {
+    env_rot = RotationZYX(x_envrot.z(0), x_envrot.y(0), x_envrot.x(0));
+    env_pos = Position(x_envpos.x(0), x_envpos.y(0), x_envpos.z(0));
+  }
+  else if ( x_envpos )
+    env_pos = Position(x_envpos.x(0), x_envpos.y(0), x_envpos.z(0));
+  else if ( x_envrot )
+    env_rot = RotationZYX(x_envrot.z(0), x_envrot.y(0), x_envrot.x(0));
+
+  Transform3D env_trafo(env_rot, env_pos);
+  Material default_material;
+  xml_dim_t x_mat = elt.child(_U(material),false);
+  if      ( x_mat.ptr() ) default_material = dsc.material(x_mat.nameStr());
+  else if ( elt.hasAttr(_U(material)) ) default_material = dsc.material(elt.attr<string>(_U(material)));
+
+  if ( elt.hasChild(_U(volume)) )   {
+    map<int, xml_h> shape_map;
+    for (xml_coll_t c(elt,_U(volume)); c; ++c )
+      shape_map.emplace(xml_dim_t(c).id(),c);
+
+    for (size_t i=0; i < shapes.size(); ++i)   {
+      Solid       sol = shapes[i].release();
+      Material    mat = default_material;
+      auto is = shape_map.find(i);
+      if ( is == shape_map.end() )   {
+        Volume vol(_toString(int(i),"vol_%d"), sol, mat);
+        envelope.placeVolume(vol);
+      }
+      else   {
+        xml_dim_t x_vol = (*is).second;
+        xml_dim_t x_pos = x_vol.child(_U(position),false);
+        xml_dim_t x_rot = x_vol.child(_U(rotation),false);
+        string     vnam = x_vol.hasAttr(_U(name)) ? x_vol.attr<string>(_U(name)) : _toString(int(i),"vol_%d");
+
+        if ( x_vol.hasAttr(_U(material)) )  {
+          mat = dsc.material(x_vol.attr<string>(_U(material)));
+        }
+        Position    pos;
+        RotationZYX rot;
+        if ( x_pos && x_rot )   {
+          rot = RotationZYX(x_rot.z(0), x_rot.y(0), x_rot.x(0));
+          pos = Position(x_pos.x(0), x_pos.y(0), x_pos.z(0));
+        }
+        else if ( x_pos )
+          pos = Position(x_pos.x(0), x_pos.y(0), x_pos.z(0));
+        else if ( x_rot )
+          rot = RotationZYX(x_rot.z(0), x_rot.y(0), x_rot.x(0));
+      
+        Volume vol(vnam, sol, mat);
+        PlacedVolume pv = envelope.placeVolume(vol,env_trafo*Transform3D(rot, pos));
+        vol.setAttributes(dsc, x_vol.regionStr(), x_vol.limitsStr(), x_vol.visStr());
+        for (xml_coll_t cc(x_vol,_U(physvolid)); cc; ++cc )   {
+          xml_dim_t vid = cc;
+          pv.addPhysVolID(vid.nameStr(), vid.attr<int>(_U(value)));
+        }
+      }
+    }
+  }
+  else   {
+    for(size_t i=0; i < shapes.size(); ++i)   {
+      Solid solid = shapes[i].release();
+      if ( solid.isValid() )   {
+        Volume vol(_toString(int(i),"vol_%d"), solid, default_material);
+        envelope.placeVolume(vol);
+      }
+    }
+  }
+  if ( elt.hasAttr(_U(name)) ) envelope->SetName(elt.attr<string>(_U(name)).c_str());
+  return envelope;
+}
+DECLARE_XML_VOLUME(CAD_MultiVolume__volume_constructor,create_CAD_Volume)
diff --git a/DDCore/include/DD4hep/DDTest.h b/DDCore/include/DD4hep/DDTest.h
new file mode 100644
index 000000000..9c91a727b
--- /dev/null
+++ b/DDCore/include/DD4hep/DDTest.h
@@ -0,0 +1,176 @@
+#include <iostream>
+#include <sstream>
+#include <stdlib.h>
+
+namespace dd4hep{
+
+  /// Simple class for defining unit tests.
+  /**  Use in main program that is added as a test to ctest:
+   *  
+   *    DDTest test = DDTest( "example" ) ; 
+   *    test.log( "example test" );
+   *    test( "Example", "Example", "example test - string comparison " ); // this test will pass
+   *    test( "Example", "BadExample", "example test - string comparison " ); //  this test will fail
+   * 
+   * @author F.Gaede, DESY, 2014
+   * based on original version from J.Engels  
+   */
+  class DDTest{
+
+  public:
+    /// Default constructor
+    DDTest() = delete;
+
+    /// Copy constructor
+    DDTest(const DDTest& copy) = delete;
+
+    /// Assignment operator
+    DDTest& operator=(const DDTest& copy) = delete;
+    
+
+    /** Only constructor
+     */
+    DDTest( const std::string& testname, std::ostream& stream=std::cout ) :
+      _testname(testname), 
+      _out(stream), 
+      _failed(0), 
+      _passed(0), 
+      _last_test_status(false) {
+    
+      _out << std::endl << "[" << _testname << "] ";
+
+      _out << "****************************** TEST_BEGIN ******************************" << std::endl << std::endl;
+    }
+
+
+
+    /** Destructor - print summary of tests passed and failed 
+     */
+    ~DDTest(){
+
+      std::stringstream sstr ;
+
+      sstr << std::endl;
+      sstr << "[" << _testname << "] number of tests PASSED : " << _passed << std::endl ;
+      sstr << "[" << _testname << "] number of tests FAILED : " << _failed << std::endl ;
+      sstr << std::endl;
+
+      sstr << "[" << _testname << "] " ;
+      sstr << "****************************** " ;
+      sstr << ( _failed == 0 ? "TEST_PASSED" : "TEST_FAILED" ) ;
+      sstr << " ******************************" ;
+      sstr << std::endl << std::endl ;
+
+      _out << sstr.str() ;
+
+      if( _failed != 0 ) exit(1) ;
+
+    }
+
+
+    /** Operator for calling a test - test is passed if v1 == v2
+     */
+    template <class V1, class V2 >
+    void operator()(const V1& v1, const V2& v2, const std::string& name ) {
+    
+      if ( ! (v1 == v2)  ) {
+      
+        std::stringstream sstr ;
+        sstr << "  " << name<< " : [" << v1 << "] != [" << v2 <<"]" ;
+
+        error( sstr.str() ) ;
+
+      } else {
+
+        std::stringstream sstr ;
+        sstr << "  " << name<< " : [" << v1 << "] == [" << v2 <<"]" ;
+
+        pass( sstr.str() ) ;
+      }
+
+      return ;
+    }
+
+    /** Operator for calling a test - test is passed if (!c)==false 
+     */
+    template <class Cond >
+    void operator()(const Cond& c, const std::string& name ) {
+    
+      if ( ! (c)  ) {
+      
+        std::stringstream sstr ;
+        sstr << "  " << name<< " : [" << c << "] " ;
+      
+        error( sstr.str() ) ;
+      
+      } else {      
+
+        std::stringstream sstr ;
+        sstr << "  " << name<< " : [" << c  << "] " ;
+
+        pass( sstr.str() ) ;
+      }
+      return ;
+    }
+
+
+    /** Simple log message */
+    void log( const std::string& msg ){
+      _out << "[" << _testname << "] " << msg << std::endl;
+    }
+  
+  
+    /** print message when test passed */  
+    void pass( const std::string& msg ){
+    
+      _passed++;
+      _last_test_status = true ;
+
+      _out << "[" << _testname << "] test " << last_test_status() << ":  " << msg << std::endl;
+    }
+
+
+
+    /** print message when test failed */  
+    void error( const std::string& msg ){
+
+      _failed++;
+      _last_test_status = false ;
+
+      std::stringstream errmsg;
+      //    errmsg << std::endl;
+      errmsg << "[" << _testname << "] ##################### TEST_FAILED ######################" << std::endl;
+      errmsg << "[" << _testname << "] ### ERROR: " << msg << std::endl;
+      errmsg << "[" << _testname << "] ########################################################" << std::endl;
+      //  errmsg << std::endl;
+
+      _out << errmsg.str();
+
+      // also send error to stderr
+      //std::cerr << errmsg.str();
+    }
+
+    /** Fatal error ...*/
+    void fatal_error( const std::string& msg ){
+      error( msg );
+      _out << "FATAL ERROR OCCURRED, program will exit now !!" << std::endl ;
+      exit(1);
+    }
+
+    /** Return the status from the last test - either PASSED or FAILED */
+    const char* last_test_status(){
+      return ( _last_test_status ? "PASSED" : "FAILED" ) ;
+    }
+
+  private:
+
+    std::string _testname ;
+    std::ostream& _out = std::cout;
+
+    unsigned int _failed = 0;          // number of failed tests
+    unsigned int _passed = 0;          // number of passed tests
+    bool _last_test_status = false;    // true if last test succeeded, false otherwise
+  };
+
+
+} // end namespace
diff --git a/DDCore/src/plugins/ShapePlugins.cpp b/DDCore/src/plugins/ShapePlugins.cpp
index e9427fce0..6724799e3 100644
--- a/DDCore/src/plugins/ShapePlugins.cpp
+++ b/DDCore/src/plugins/ShapePlugins.cpp
@@ -541,6 +541,18 @@ static Handle<TObject> create_BooleanMulti(Detector& description, xml_h element)
 }
 DECLARE_XML_SHAPE(BooleanShape__shape_constructor,create_BooleanMulti)
 
+static Handle<TObject> create_std_volume(Detector& description, xml_h e)   {
+  return xml::createStdVolume(description, e);
+}
+DECLARE_XML_VOLUME(DD4hep_StdVolume,create_std_volume)
+
+static Handle<TObject> create_gen_volume(Detector& description, xml_h e)   {
+  xml_dim_t elt = e;
+  string    typ = elt.attr<string>(_U(type));
+  return xml::createVolume(description, typ, e);
+}
+DECLARE_XML_VOLUME(DD4hep_GenericVolume,create_gen_volume)
+
 TGeoCombiTrans* createPlacement(const Rotation3D& iRot, const Position& iTrans) {
   double elements[9];
   iRot.GetComponents(elements);
@@ -569,7 +581,7 @@ static Ref_t create_shape(Detector& description, xml_h e, Ref_t /* sens */)  {
     Volume     volume;
     string     shape_type = shape.typeStr();
 
-    if ( shape_type == "CAD_MultiShape" )   {
+    if ( shape_type == "CAD_Assembly" || shape_type == "CAD_MultiVolume" )   {
       volume = xml::createVolume(description, shape_type, shape);
       solid  = volume->GetShape();
     }
diff --git a/examples/ClientTests/compact/CheckShape.xml b/examples/ClientTests/compact/CheckShape.xml
index c15280102..12a6d2db9 100644
--- a/examples/ClientTests/compact/CheckShape.xml
+++ b/examples/ClientTests/compact/CheckShape.xml
@@ -26,14 +26,17 @@
   <display>
     <vis name="InvisibleNoDaughters"      showDaughters="false" visible="false"/>
     <vis name="InvisibleWithDaughters"    showDaughters="true"  visible="false"/>
-    <vis name="Shape1_vis_20" alpha="0.2" r="0.9" g="0.8" b="0.8" showDaughters="true" visible="true"/>
+    <vis name="Shape1_vis_20"    alpha="0.2" r="0.9" g="0.8" b="0.8" showDaughters="true" visible="true"/>
     <vis name="ShapeGray_vis_50" alpha="0.5" r="0.9" g="0.8" b="0.8" showDaughters="true" visible="true"/>
-    <vis name="Shape1_vis"    alpha="1.0" r="1" g="0" b="0" showDaughters="true" visible="true"/>
-    <vis name="Shape2_vis"    alpha="1.0" r="0" g="1" b="0" showDaughters="true" visible="true"/>
-    <vis name="Shape3_vis"    alpha="1.0" r="0" g="0" b="1" showDaughters="true" visible="true"/>
-    <vis name="Shape4_vis"    alpha="1.0" r="1" g="1" b="0" showDaughters="true" visible="true"/>
-    <vis name="Shape5_vis"    alpha="1.0" r="1" g="0" b="1" showDaughters="true" visible="true"/>
-    <vis name="Shape6_vis"    alpha="1.0" r="0" g="1" b="1" showDaughters="true" visible="true"/>
-    <vis name="Shape_grey"    alpha="1.0" r="0.4" g="0.4" b="0.4" showDaughters="true" visible="true"/>
+    <vis name="Shape0_vis"       alpha="1.0" r="0"   g="1"   b="1"   showDaughters="true" visible="true"/>
+    <vis name="Shape1_vis"       alpha="1.0" r="1"   g="0"   b="0"   showDaughters="true" visible="true"/>
+    <vis name="Shape2_vis"       alpha="1.0" r="0"   g="1"   b="0"   showDaughters="true" visible="true"/>
+    <vis name="Shape3_vis"       alpha="1.0" r="0"   g="0"   b="1"   showDaughters="true" visible="true"/>
+    <vis name="Shape4_vis"       alpha="1.0" r="1"   g="1"   b="0"   showDaughters="true" visible="true"/>
+    <vis name="Shape5_vis"       alpha="1.0" r="1"   g="0"   b="1"   showDaughters="true" visible="true"/>
+    <vis name="Shape6_vis"       alpha="1.0" r="0.5" g="0.5" b="0"   showDaughters="true" visible="true"/>
+    <vis name="Shape7_vis"       alpha="1.0" r="0"   g="0.5" b="0.5" showDaughters="true" visible="true"/>
+    <vis name="Shape8_vis"       alpha="0.5" r="0.0" g="0.4" b="0.4" showDaughters="true" visible="true"/>
+    <vis name="Shape_grey"       alpha="0.5" r="0.0" g="0.4" b="0.4" showDaughters="true" visible="true"/>
   </display>
 </lccdd>
diff --git a/examples/DDCAD/compact/Check_Shape_MS3D_jeep.xml b/examples/DDCAD/compact/Check_Shape_MS3D_jeep.xml
index ddf46cabb..5fd5ac794 100644
--- a/examples/DDCAD/compact/Check_Shape_MS3D_jeep.xml
+++ b/examples/DDCAD/compact/Check_Shape_MS3D_jeep.xml
@@ -6,7 +6,7 @@
   <detectors>
     <detector id="1" name="Shape_OBJ" type="DD4hep_TestShape_Creator">
       <check vis="Shape1_vis">
-        <shape type="CAD_MultiShape" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/models/MS3D/jeep1.ms3d"/>
+        <shape type="CAD_Assembly" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/models/MS3D/jeep1.ms3d"/>
       </check>
       <test1  type="DD4hep_Mesh_Verifier" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/ref/Ref_OBJ_spider.txt" create="CheckShape_create"/>
     </detector>
diff --git a/examples/DDCAD/compact/Check_Shape_OBJ_spider.xml b/examples/DDCAD/compact/Check_Shape_OBJ_spider.xml
index d6f421edf..6654a13f4 100644
--- a/examples/DDCAD/compact/Check_Shape_OBJ_spider.xml
+++ b/examples/DDCAD/compact/Check_Shape_OBJ_spider.xml
@@ -6,7 +6,7 @@
   <detectors>
     <detector id="1" name="Shape_OBJ" type="DD4hep_TestShape_Creator">
       <check vis="Shape1_vis">
-        <shape type="CAD_MultiShape" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/models/OBJ/spider.obj" mesh="0"/>
+        <shape type="CAD_Assembly" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/models/OBJ/spider.obj" mesh="0"/>
       </check>
       <test1  type="DD4hep_Mesh_Verifier" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/ref/Ref_OBJ_spider.txt" create="CheckShape_create"/>
     </detector>
diff --git a/examples/DDCAD/compact/Check_Shape_Test1.xml b/examples/DDCAD/compact/Check_Shape_Test1.xml
index 4ccb12eeb..6553694a2 100644
--- a/examples/DDCAD/compact/Check_Shape_Test1.xml
+++ b/examples/DDCAD/compact/Check_Shape_Test1.xml
@@ -6,7 +6,7 @@
   <detectors>
     <detector id="1" name="Shape_Collada" type="DD4hep_TestShape_Creator">
       <check vis="Shape1_vis">
-        <shape type="CAD_MultiShape" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/models/OBJ/spider.obj" mesh="0"/>
+        <shape type="CAD_Assembly" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/models/OBJ/spider.obj" mesh="0"/>
       </check>
       <test111  type="DD4hep_Mesh_Verifier" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/ref/Ref_test.txt" create="CheckShape_create"/>
       <test  type="DD4hep_Mesh_Verifier" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/ref/Ref_test.txt" create="1"/>
diff --git a/examples/DDCAD/compact/MultiShape_MS3D_jeep.xml b/examples/DDCAD/compact/MultiShape_MS3D_jeep.xml
new file mode 100644
index 000000000..5853f9842
--- /dev/null
+++ b/examples/DDCAD/compact/MultiShape_MS3D_jeep.xml
@@ -0,0 +1,52 @@
+<lccdd>
+  <includes>
+    <gdmlFile ref="../../ClientTests/compact/CheckShape.xml"/>
+  </includes>
+
+  <detectors>
+    <detector id="1" name="Shape_OBJ" type="DD4hep_TestShape_Creator">
+      <check>
+        <shape type="CAD_MultiVolume" ref="${DD4hepExamplesINSTALL}/examples/DDCAD/models/MS3D/jeep1.ms3d" unit="cm">
+
+          <!--      Envelope definition
+                    This is optional: By default the child volumes are placed in an assembly.
+          -->
+          <envelope name="Garage" material="Air" vis="Shape_grey">
+            <shape type="Box" dx="10*cm" dy="10*cm" dz="10*cm"/>
+          </envelope>
+
+          <!--      Transformation defining the positioning of all sub-volumes within the envelope  -->
+          <position x="-5*cm" y="0" z="0"/>
+          <rotation x="0" y="0" z="-pi/2*rad"/>
+
+          <!--      These are the 4 wheels  -->
+          <volume id="0" name="Wheel1" material="Iron" vis="Shape0_vis">
+            <position x="5*cm" y="5*cm" z="0"/>
+            <rotation x="0" y="0" z="-pi/2*rad"/>
+          </volume>
+          <volume id="1" name="Wheel2" material="Iron" vis="Shape1_vis">
+            <position x="2*cm" y="5*cm" z="0"/>
+            <rotation x="0" y="0" z="-pi/4*rad"/>
+          </volume>
+          <volume id="2" name="Wheel3" material="Iron" vis="Shape2_vis">
+            <position x="-5*cm" y="5*cm" z="0"/>
+            <rotation x="0" y="0" z="pi/2*rad"/>
+          </volume>
+          <volume id="3" name="Wheel4" material="Iron" vis="Shape3_vis">
+            <position x="-2*cm" y="5*cm" z="0"/>
+            <rotation x="0" y="0" z="pi/4*rad"/>
+          </volume>
+
+          <!--      This is the rest of the jeep  -->
+          <volume id="4" name="E" material="Iron" vis="Shape4_vis">
+          </volume>
+          <volume id="5" name="F" material="Iron" vis="Shape5_vis">
+          </volume>
+          <volume id="6" name="C" material="Iron" vis="Shape6_vis">
+          </volume>
+        </shape>
+      </check>
+
+    </detector>
+  </detectors>
+</lccdd>
-- 
GitLab