diff --git a/GeoModelCore/GeoModelHelpers/src/GeoShapeUtils.cxx b/GeoModelCore/GeoModelHelpers/src/GeoShapeUtils.cxx
index 73ef9a7622a01cd6c69851a102ed6973ef4da387..413e020748911e4baa3ea5beaa79465f6be81531 100644
--- a/GeoModelCore/GeoModelHelpers/src/GeoShapeUtils.cxx
+++ b/GeoModelCore/GeoModelHelpers/src/GeoShapeUtils.cxx
@@ -24,6 +24,7 @@
 #include "GeoModelKernel/GeoGenericTrap.h"
 
 #include "GeoModelKernel/Units.h"
+#include "GeoModelHelpers/TransformSorter.h"
 
 #include <vector>
 
@@ -51,12 +52,28 @@ unsigned int countComposedShapes(const GeoShape* shape) {
 }
 
 GeoIntrusivePtr<const GeoShape> compressShift(const GeoShape* shift) {    
-    if (shift->typeID() != GeoShapeShift::getClassTypeID()) return GeoIntrusivePtr<const GeoShape>{shift};
-    const GeoShapeShift* shapeShift = dynamic_cast<const GeoShapeShift*>(shift);
-    if (shapeShift->getOp()->typeID() != GeoShapeShift::getClassTypeID()) return GeoIntrusivePtr<const GeoShape>{shift};
+    GeoIntrusivePtr<const GeoShape> retPtr{shift};
+    if (shift->typeID() != GeoShapeShift::getClassTypeID()) {
+        return retPtr;
+    }
+    
+    GeoIntrusivePtr<const GeoShapeShift> shapeShift = dynamic_pointer_cast<const GeoShapeShift>(retPtr);
+    /// If the shape shift is an Identity no need to go go further.
+    if (!GeoTrf::TransformSorter{}.compare(shapeShift->getX(), GeoTrf::Transform3D::Identity())) {
+        retPtr = shapeShift->getOp();
+    }
+    if (shapeShift->getOp()->typeID() != GeoShapeShift::getClassTypeID()) {
+        return retPtr;
+    }
     GeoIntrusivePtr<const GeoShape> subShape{compressShift(shapeShift->getOp())};
-    const GeoShapeShift* subShift = dynamic_cast<const GeoShapeShift*>(subShape.get());
-    return GeoIntrusivePtr<const GeoShape>{new GeoShapeShift(subShift->getOp(), subShift->getX() * shapeShift->getX())};
+    
+    GeoIntrusivePtr<const GeoShapeShift> subShift = dynamic_pointer_cast<const GeoShapeShift>(subShape);
+    /// Check that the GeoShape shift is actually neccesary. If not bail out
+    GeoTrf::Transform3D shiftTrf{subShift->getX() * shapeShift->getX()};
+    if (!GeoTrf::TransformSorter{}.compare(shiftTrf, GeoTrf::Transform3D::Identity())) {
+        return GeoIntrusivePtr<const GeoShape>{subShift->getOp()};
+    }
+    return make_intrusive<GeoShapeShift>(subShift->getOp(), std::move(shiftTrf));
 }
 std::vector<const GeoShape*> getBooleanComponents(const GeoShape* booleanShape) {
      std::pair<const GeoShape*, const GeoShape*> operands = getOps(booleanShape);
diff --git a/GeoModelCore/GeoModelHelpers/tests/testPhysVolSorter.cxx b/GeoModelCore/GeoModelHelpers/tests/testPhysVolSorter.cxx
index 9d22808b5ecc480988a5e3680098df945f7686c3..095ff482e334df00735d250457626ca0c8c849c3 100644
--- a/GeoModelCore/GeoModelHelpers/tests/testPhysVolSorter.cxx
+++ b/GeoModelCore/GeoModelHelpers/tests/testPhysVolSorter.cxx
@@ -20,14 +20,14 @@ int main() {
     GeoIntrusivePtr<GeoPhysVol> world{createGeoWorld()};
 
     const GeoMaterial* air = world->getLogVol()->getMaterial();
-    GeoIntrusivePtr<GeoBox> externalBox{new GeoBox(500.,500., 500.)};
-    GeoIntrusivePtr<GeoBox> internalBox{new GeoBox(100.,100., 100.)};
+    GeoIntrusivePtr<GeoBox> externalBox{make_intrusive<GeoBox>(500.,500., 500.)};
+    GeoIntrusivePtr<GeoBox> internalBox{make_intrusive<GeoBox>(100.,100., 100.)};
     auto  makeBox = [&](bool bigOne) {
-        return PVLink(new GeoPhysVol(new GeoLogVol("TestVolume", bigOne ? externalBox : internalBox, air)));
+        return make_intrusive<GeoPhysVol>(make_intrusive<GeoLogVol>("TestVolume", bigOne ? externalBox : internalBox, air));
     };
 
     auto makeFullBox = [&](bool bigOne) {
-        return PVLink(new GeoFullPhysVol(new GeoLogVol("TestFullPhysVol", bigOne ? externalBox : internalBox, air)));
+        return make_intrusive<GeoFullPhysVol>(make_intrusive<GeoLogVol>("TestFullPhysVol", bigOne ? externalBox : internalBox, air));
     };
     /// 
     PVLink extVolume = makeBox(true);
@@ -54,7 +54,7 @@ int main() {
         return EXIT_FAILURE;
     }
     extVolume = makeBox(true);
-    extVolume->add(new GeoTransform(GeoTrf::TranslateX3D(50.)));
+    extVolume->add(make_intrusive<GeoTransform>(GeoTrf::TranslateX3D(50.)));
     extVolume->add(makeBox(false));
     if (!physVolSet.insert(extVolume).second) {
         std::cerr<<"testPhysVolSorter() "<<__LINE__<<" A box with a displaced box inside is not the same as box ception "<<std::endl;
diff --git a/GeoModelTools/CMakeLists.txt b/GeoModelTools/CMakeLists.txt
index bed80fb75f6f943b19353885f3239e9281857724..1f7861e775cd44706a8c2957b54673b7bc8cf4f8 100644
--- a/GeoModelTools/CMakeLists.txt
+++ b/GeoModelTools/CMakeLists.txt
@@ -48,6 +48,7 @@ include( GNUInstallDirs )
 # Set up the build of the libraries of the project.
 add_subdirectory( GeoModelXML )
 add_subdirectory( GeoModelXMLParser )
+add_subdirectory( GeoModelXMLDumper )
 add_subdirectory( GeoModelJSONParser )
 add_subdirectory( ExpressionEvaluator )
 add_subdirectory( GMCAT )
diff --git a/GeoModelTools/GeoModelXML/GeoModelXml/GeoModelXml/GmxUtil.h b/GeoModelTools/GeoModelXML/GeoModelXml/GeoModelXml/GmxUtil.h
index e149501c677c4f71a229f77a360f80f347a525c8..9e44cd9d99e2484f3771f9798c9ed44ad813060f 100644
--- a/GeoModelTools/GeoModelXML/GeoModelXml/GeoModelXml/GmxUtil.h
+++ b/GeoModelTools/GeoModelXML/GeoModelXml/GeoModelXml/GmxUtil.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
 */
 
 #ifndef GEO_MODEL_XML_GMXUTIL_H
@@ -54,6 +54,8 @@
 #include "GeoModelXml/shape/MakeSubtraction.h"
 #include "GeoModelXml/shape/MakeShaperef.h"
 #include "GeoModelXml/shape/AddPlane.h"
+#include "GeoModelXml/shape/MakeShapeShift.h"
+
 
 #include "GeoModelXml/PositionIndex.h"
 //#include "GeoModelXml/SensitiveId.h"
@@ -114,6 +116,7 @@ public:
         MakeUnion onion; // union is reserved
         MakeSubtraction subtraction;
         MakeShaperef shaperef;
+        MakeShapeShift shapeshift;
         MakeTransformation transformation;
         MakeTransformationref transformationref;
 //
diff --git a/GeoModelTools/GeoModelXML/GeoModelXml/GeoModelXml/shape/MakeShapeShift.h b/GeoModelTools/GeoModelXML/GeoModelXml/GeoModelXml/shape/MakeShapeShift.h
new file mode 100644
index 0000000000000000000000000000000000000000..c13c214c730b63c58e0f70a3f08d1313cd142d11
--- /dev/null
+++ b/GeoModelTools/GeoModelXML/GeoModelXml/GeoModelXml/shape/MakeShapeShift.h
@@ -0,0 +1,28 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef GEO_MODEL_XML_MAKE_SHAPESHIFT_H
+#define GEO_MODEL_XML_MAKE_SHAPESHIFT_H
+#include <xercesc/util/XercesDefs.hpp>
+
+#include "GeoModelXml/Element2GeoItem.h"
+//
+//    Create and return a HepRotation3d. Caller must delete it.
+//
+
+XERCES_CPP_NAMESPACE_BEGIN
+class DOMElement;
+XERCES_CPP_NAMESPACE_END
+
+
+
+class GmxUtil;
+
+class MakeShapeShift: public Element2GeoItem {
+public:
+    MakeShapeShift() = default;
+    virtual GeoIntrusivePtr<RCBase> make(const xercesc::DOMElement *element, GmxUtil &gmxUtil) const override;
+};
+
+#endif // MAKE_ROTATION_H
diff --git a/GeoModelTools/GeoModelXML/GeoModelXml/data/geomodel_v0.dtd b/GeoModelTools/GeoModelXML/GeoModelXml/data/geomodel_v0.dtd
index dd3443183937b493945eb4d9e777814864e709fb..90bb859d8269990966d7307ba1bf640600ffc7b1 100644
--- a/GeoModelTools/GeoModelXML/GeoModelXml/data/geomodel_v0.dtd
+++ b/GeoModelTools/GeoModelXML/GeoModelXml/data/geomodel_v0.dtd
@@ -65,7 +65,7 @@
     <!ELEMENT addindex EMPTY>
         <!ATTLIST addindex name ID #REQUIRED>
 
-  <!ELEMENT shapes ((box|cons|generictrap|para|pcon|pgon|trap|tube|tubs|trd|intersection|subtraction|union|simplepolygonbrep|ellipticaltube|twistedtrap|torus)+)>
+  <!ELEMENT shapes ((box|cons|generictrap|para|pcon|pgon|trap|tube|tubs|trd|intersection|subtraction|union|simplepolygonbrep|ellipticaltube|twistedtrap|torus|shapeshift)+)>
     <!-- All shapes allowed in GeoModel manual. Same name, parameters, parameter order, but always lower case -->
 
     <!ELEMENT box EMPTY>
@@ -168,6 +168,9 @@
     <!ELEMENT intersection (shaperef, (transformation|transformationref), shaperef)>
       <!ATTLIST intersection name ID #REQUIRED>
 
+    <!ELEMENT shapeshift ( (shaperef|transformation|transformationref)+)>
+      <!ATTLIST shapeshift name ID #REQUIRED>
+
     <!ELEMENT subtraction (shaperef, (transformation|transformationref), shaperef)>
       <!ATTLIST subtraction name ID #REQUIRED>
 
@@ -268,7 +271,7 @@
     <!ATTLIST assemblyref ref    IDREF        #REQUIRED
                           zeroid (true|false) "false">
 
-  <!ELEMENT multicopy (transformation, (transform|logvolref|assemblyref))>
+  <!ELEMENT multicopy ( (transformation|transformationref), (transform|logvolref|assemblyref))>
     <!-- Removed logvol and assembly from multicopy content. There is no particular reason
          why they shouldn't be allowed there. But in practice one uses refs, and forbidding them simplifies coding
          the index stuff. If you need them, put them back. Got rid of transformref; never used. -->
diff --git a/GeoModelTools/GeoModelXML/GeoModelXml/src/GmxUtil.cxx b/GeoModelTools/GeoModelXML/GeoModelXml/src/GmxUtil.cxx
index f9e5ff1d31d488c5b1b92d1d5126818fc935449d..2445aae348daec2800166213c02b6f5b17aa8d5d 100644
--- a/GeoModelTools/GeoModelXML/GeoModelXml/src/GmxUtil.cxx
+++ b/GeoModelTools/GeoModelXML/GeoModelXml/src/GmxUtil.cxx
@@ -85,7 +85,8 @@ GmxUtil::GmxUtil(GmxInterface &gmxInterface):
     geoItemRegistry.enregister("shaperef",  &tagHandler.shaperef);
     geoItemRegistry.enregister("transformation",  &tagHandler.transformation);
     geoItemRegistry.enregister("transformationref",  &tagHandler.transformationref);
-
+    geoItemRegistry.enregister("shapeshift", &tagHandler.shapeshift);
+    
     tagHandler.addplane.gmxUtil=this;
 }
 
diff --git a/GeoModelTools/GeoModelXML/GeoModelXml/src/MakeShapeShift.cxx b/GeoModelTools/GeoModelXML/GeoModelXml/src/MakeShapeShift.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..2e6b9fae5a58266b31131104d74a43b4c001d2a2
--- /dev/null
+++ b/GeoModelTools/GeoModelXML/GeoModelXml/src/MakeShapeShift.cxx
@@ -0,0 +1,62 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+#include "GeoModelXml/shape/MakeShapeShift.h"
+
+#include "OutputDirector.h"
+#include <string>
+#include <xercesc/dom/DOM.hpp>
+
+#include "GeoModelKernel/GeoShapeShift.h"
+#include "GeoModelKernel/GeoTransform.h"
+
+#include "xercesc/util/XMLString.hpp"
+#include "GeoModelXml/GmxUtil.h"
+
+#include "GeoModelHelpers/TransformSorter.h"
+#include "GeoModelHelpers/throwExcept.h"
+#include "GeoModelHelpers/GeoShapeUtils.h"
+
+#include "xercesc/util/XMLString.hpp"
+#include "GeoModelXml/GmxUtil.h"
+
+using namespace xercesc;
+
+GeoIntrusivePtr<RCBase> MakeShapeShift::make(const xercesc::DOMElement *element, GmxUtil &gmxUtil) const {
+    
+    unsigned int elementIndex = 0;
+    GeoShape* shape{nullptr};
+    GeoTrf::Transform3D hepXf{GeoTrf::Transform3D::Identity()};
+    for (DOMNode *child = element->getFirstChild(); child != nullptr; child = child->getNextSibling()) {
+       if (child->getNodeType() != DOMNode::ELEMENT_NODE) { 
+            continue;
+       }
+      switch (elementIndex) {
+        case 0: { // First element is first shaperef
+            shape = dynamic_pointer_cast<GeoShape>(gmxUtil.tagHandler.shaperef.process(dynamic_cast<DOMElement*> (child), gmxUtil));
+            break;
+        } case 1: { // Second element is transformation or transformationref
+            char *toRelease = XMLString::transcode(child->getNodeName());
+            std::string nodeName{toRelease};
+            XMLString::release(&toRelease);
+            const GeoTransform *geoXf = (nodeName == "transformation")
+              ? dynamic_pointer_cast<const GeoTransform>( gmxUtil.tagHandler.transformation.process(dynamic_cast<DOMElement *>(child), gmxUtil))
+              : dynamic_pointer_cast<const GeoTransform>( gmxUtil.tagHandler.transformationref.process(dynamic_cast<DOMElement *>(child), gmxUtil));
+            hepXf = geoXf->getTransform();
+            break;
+        } default: // More than 3 elements?
+            THROW_EXCEPTION("Only transofrmation & geoshaperef are accepted");
+      }
+      ++elementIndex;        
+    }
+    if (!shape) {
+        THROW_EXCEPTION("No shape has been given");
+    }
+    
+    static const GeoTrf::TransformSorter sorter{};
+    if (!sorter.compare(GeoTrf::Transform3D::Identity(), hepXf)) {
+        return shape;
+    }    
+    return const_pointer_cast(cacheShape(make_intrusive<GeoShapeShift>(shape, hepXf)));
+}
+
diff --git a/GeoModelTools/GeoModelXMLDumper/CMakeLists.txt b/GeoModelTools/GeoModelXMLDumper/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d7d6408c99f18dc7385b594d3820d907171409f8
--- /dev/null
+++ b/GeoModelTools/GeoModelXMLDumper/CMakeLists.txt
@@ -0,0 +1,63 @@
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+
+# Find the header and source files.
+file( GLOB SOURCES src/*.cxx )
+file( GLOB HEADERS GeoModelXMLDumper/*.h GeoModelXMLDumper/*.icc )
+
+# Create the library.
+add_library( GeoModelXMLDumper SHARED ${HEADERS} ${SOURCES} )
+target_link_libraries( GeoModelXMLDumper PUBLIC GeoModelCore::GeoModelKernel 
+                                                GeoModelCore::GeoModelHelpers )
+target_include_directories( GeoModelXMLDumper PUBLIC
+   $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+   $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> )
+source_group( "GeoModelXMLDumper" FILES ${HEADERS} )
+source_group( "src" FILES ${SOURCES} )
+set_target_properties( GeoModelXMLDumper PROPERTIES
+   VERSION ${PROJECT_VERSION}
+   SOVERSION ${PROJECT_VERSION_MAJOR} )
+
+# Set up an alias with the same name that you would get by "finding" a pre-built
+# version of the library.
+add_library( GeoModelTools::GeoModelXMLDumper ALIAS GeoModelXMLDumper )
+
+# Install the library.
+install(TARGETS GeoModelXMLDumper
+    EXPORT ${PROJECT_NAME}-export
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+            COMPONENT Runtime
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+            COMPONENT          Runtime
+            NAMELINK_COMPONENT Development   # Requires CMake 3.12
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+            COMPONENT Development
+    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/GeoModelXMLDumper
+            COMPONENT Development
+    INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+
+install( FILES ${HEADERS}
+   DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/GeoModelXMLDumper
+   COMPONENT Development )
+
+
+
+# Declare the package's executable.
+add_executable( dumpGeoXML util/main.cxx)
+target_link_libraries( dumpGeoXML PRIVATE GeoModelCore::GeoModelKernel
+                                          GeoModelCore::GeoModelHelpers
+                                          GeoModelIO::GeoModelRead
+                                          GeoModelTools::GeoModelXMLDumper)
+
+# Tweak how debug information should be attached to the executable, in Debug
+# builds.
+if( "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" AND
+   "${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" )
+   target_compile_options( dumpGeoXML PRIVATE "-gdwarf-2" )
+endif()
+
+# Install the executable.
+install( TARGETS dumpGeoXML
+   EXPORT ${PROJECT_NAME}-export
+   RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+   COMPONENT Runtime )
diff --git a/GeoModelTools/GeoModelXMLDumper/GeoModelXMLDumper/GeoXMLDumper.h b/GeoModelTools/GeoModelXMLDumper/GeoModelXMLDumper/GeoXMLDumper.h
new file mode 100644
index 0000000000000000000000000000000000000000..f5e3dd0bf95d9683552d41375fe8676486f9afdf
--- /dev/null
+++ b/GeoModelTools/GeoModelXMLDumper/GeoModelXMLDumper/GeoXMLDumper.h
@@ -0,0 +1,93 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+#ifndef GEOMODELXMLDUMPER_GEOXMLDUMPER_H
+#define GEOMODELXMLDUMPER_GEOXMLDUMPER_H
+
+#include "GeoModelKernel/GeoVPhysVol.h"
+#include "GeoModelKernel/GeoTransform.h"
+
+#include "GeoModelKernel/GeoIntrusivePtr.h"
+#include "GeoModelHelpers/GeoShapeSorter.h"
+#include "GeoModelHelpers/GeoPhysVolSorter.h"
+#include "GeoModelHelpers/TransformSorter.h"
+
+
+#include <string>
+#include <map>
+
+/** @brief: The GeoXmlDumper class traverses the GeoModel tree and translates its content 
+ *          into plain GeoModelXML
+ * 
+*/
+
+class GeoXMLDumper {
+public:
+    GeoXMLDumper(const std::string& outDir,
+                 const std::string& systemName,
+                 const std::string& xmlLayoutFile);
+
+    bool publishWorld(PVConstLink world);
+
+
+
+
+private:
+     std::string m_outDir{};
+     std::string m_systemName{};
+     std::string m_xmlLayoutFile{};
+
+     void countVolume(PVConstLink volume);
+
+private:
+     /// @brief 
+     /// @param outStream 
+     /// @param physVol 
+     /// @param volName 
+     /// @param indentation 
+     /// @return 
+     std::string publishPhysVol(std::ostream& outStream,
+                                PVConstLink physVol,
+                                const std::string& volName,
+                                unsigned int indentation = 0);
+
+     std::string publishShape(std::ostream& shapeStream,
+                              GeoIntrusivePtr<const GeoShape> shape,
+                              const std::string& shapeName);
+
+    /// @brief  Creates the actual header file of the GeoModel description
+    /// @param ostr 
+    using topVolWithPathList = std::vector<std::pair<std::string, std::string>>;
+    void dumpGeoModelHeader(std::ostream& ostr,
+                            const topVolWithPathList& pubVolsWithFiles) const;
+    
+    void writeMaterials(std::ostream& ostr,
+                        unsigned int indentation = 0) const;
+
+
+    void dumpTransform(std::ostream& ostr,
+                       const GeoTrf::Transform3D& trans,
+                       const std::string& refName,
+                       unsigned int indentation = 0);
+
+    std::string uniqueNameTag(std::string nameTag,
+                              const std::string_view idToken);
+
+    using shapeMap = std::map<GeoIntrusivePtr<const GeoShape>, std::string, GeoShapeSorter>;
+    using physVolMap = std::map<PVConstLink, std::string, GeoPhysVolSorter>;
+    using transformMap = std::map<GeoTrf::Transform3D, std::string, GeoTrf::TransformSorter>;
+
+
+    using physVolCounter = std::map<PVConstLink, unsigned int, GeoPhysVolSorter>;    
+    using nameTagCounter = std::map<std::string, unsigned int>;
+
+    physVolMap m_physVolNameTags{};
+    shapeMap m_shapeNameTags{};
+    transformMap m_transformNameTags{};
+
+    nameTagCounter m_nameTagCounts{};
+    physVolCounter m_physVolCounts{};
+    
+};
+
+#endif
\ No newline at end of file
diff --git a/GeoModelTools/GeoModelXMLDumper/src/GeoXMLDumper.cxx b/GeoModelTools/GeoModelXMLDumper/src/GeoXMLDumper.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..f78ca6c1fbf78eeb88a74d19c43fb5dbcc867eda
--- /dev/null
+++ b/GeoModelTools/GeoModelXMLDumper/src/GeoXMLDumper.cxx
@@ -0,0 +1,539 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+#include "GeoModelXMLDumper/GeoXMLDumper.h"
+#include "GeoModelKernel/Units.h"
+#include "GeoModelKernel/GeoVolumeCursor.h"
+
+#include "GeoModelKernel/GeoShapeUnion.h"
+#include "GeoModelKernel/GeoShapeSubtraction.h"
+#include "GeoModelKernel/GeoShapeIntersection.h"
+#include "GeoModelKernel/GeoShapeShift.h"
+
+
+#include "GeoModelKernel/GeoCons.h"
+#include "GeoModelKernel/GeoBox.h"
+#include "GeoModelKernel/GeoEllipticalTube.h"
+#include "GeoModelKernel/GeoGenericTrap.h"
+#include "GeoModelKernel/GeoPara.h"
+#include "GeoModelKernel/GeoPcon.h"
+#include "GeoModelKernel/GeoPgon.h"
+#include "GeoModelKernel/GeoTorus.h"
+#include "GeoModelKernel/GeoTrap.h"
+#include "GeoModelKernel/GeoTrd.h"
+#include "GeoModelKernel/GeoTube.h"
+#include "GeoModelKernel/GeoTubs.h"
+#include "GeoModelKernel/GeoSimplePolygonBrep.h"
+
+#include "GeoModelHelpers/GeoShapeUtils.h"
+#include "GeoModelHelpers/getChildNodesWithTrf.h"
+#include "GeoModelHelpers/throwExcept.h"
+#include "GeoModelHelpers/TransformToStringConverter.h"
+#include "GeoModelHelpers/StringUtils.h"
+#include "GeoModelHelpers/FileUtils.h"
+
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <fstream>
+#include <string_view>
+#include <functional>
+
+
+using namespace GeoStrUtils;
+using namespace GeoFileUtils;
+namespace {
+    constexpr unsigned int stdIndent = 4;
+    constexpr double transTolerance = 0.1 * GeoModelKernelUnits::micrometer;
+    constexpr double toDeg = 1./ GeoModelKernelUnits::deg;
+
+    std::string shapeRefTag(const std::string& shapeName, unsigned int nWhite) {
+        std::stringstream sstr{};
+        sstr<<whiteSpaces(nWhite)<<"<shaperef ref=\""<<shapeName<<"\" />";
+        return sstr.str();
+    }
+
+    std::string fileNameBody(const std::string_view path) {
+        const unsigned slashPos = path.rfind("/") + 1;
+        return std::string{path.substr(slashPos, path.rfind(".") - slashPos)};
+    }
+    static const GeoTrf::TransformSorter transformSorter{};
+}
+
+
+GeoXMLDumper::GeoXMLDumper(const std::string& outDir,
+                           const std::string& systemName,
+                           const std::string& xmlLayoutFile):
+    m_outDir{outDir},
+    m_systemName{systemName},
+    m_xmlLayoutFile{xmlLayoutFile} {
+        if (m_outDir.size() && m_outDir[m_outDir.size() -1] != '/') {
+            m_outDir+="/";
+        }
+}
+
+void GeoXMLDumper::countVolume(PVConstLink volume){
+    ++(m_physVolCounts[volume]);
+    GeoVolumeCursor cursor{volume};
+    while (!cursor.atEnd()) {
+        countVolume(cursor.getVolume());
+        cursor.next();
+    }
+}
+
+bool GeoXMLDumper::publishWorld(PVConstLink world) {
+    if (m_outDir.empty()) {
+        std::cerr<<"Please define an output directory "<<std::endl;
+        return false;
+    }
+    /// Count how many distinct volumes are in the world and also at how many nodes each volume appears
+    std::cout<<"Traverse the GeoModel tree to count the different physical volumes "<<std::endl;
+    countVolume(world);
+    {
+        unsigned int counts{0};
+        for (const auto& [vol, c] : m_physVolCounts) counts+=c;
+        std::cout<<"Found in total "<<m_physVolCounts.size()<<" different volumes used in "<<counts<<" nodes."<<std::endl;        
+    }
+    /// If the world only has one volume get to its child
+    while (world->getNChildVols() == 1)  {
+        world = world->getChildVol(0);
+    }
+
+    GeoVolumeCursor cursor{world};
+
+    topVolWithPathList publishedTops{};
+    while (!cursor.atEnd()) {
+        PVConstLink worldNode{cursor.getVolume()};
+        const std::string out_xmlDir = m_outDir + "volumes/";
+        if (!mkdir(out_xmlDir)) {
+            std::cerr<<"Failed to create directory "<<out_xmlDir<<std::endl;
+            return false;
+        }
+        const std::string out_xmlFile = out_xmlDir + 
+                                        replaceExpInString(cursor.getName()," ", "_") + ".xml";
+
+        std::ofstream outFile{out_xmlFile};
+        if (!outFile.good()) {
+            std::cerr<<"Failed to open "<<out_xmlFile<<std::endl;
+            return false;
+        }
+        const std::string pubVolume{publishPhysVol(outFile, cursor.getVolume(),  cursor.getName())};
+        publishedTops.emplace_back(std::make_pair(out_xmlFile, pubVolume));
+        cursor.next();
+    }
+
+    const std::string out_xmlFile = m_outDir+ m_systemName +".xml";
+
+    std::ofstream outFile{out_xmlFile};
+    if (!outFile.good()) {
+        std::cerr<<"Failed to open "<<out_xmlFile<<std::endl;
+        return false;
+    } 
+    dumpGeoModelHeader(outFile, publishedTops);
+    return true;
+}
+std::string GeoXMLDumper::uniqueNameTag(std::string nameTag,
+                                        const std::string_view idToken) {
+    nameTag = replaceExpInString(nameTag, " ", "_");
+    unsigned int& nameTagCounts{m_nameTagCounts[nameTag]};
+    if (nameTagCounts> 0) {
+        nameTag += std::string{idToken}+"No"+std::to_string(nameTagCounts);
+    }
+    ++nameTagCounts;
+    return nameTag;
+}
+std::string GeoXMLDumper::publishPhysVol(std::ostream& outStream,
+                                         PVConstLink physVol,
+                                         const std::string& volName,
+                                         unsigned int indentation) {
+    
+    physVolMap::const_iterator itr = m_physVolNameTags.find(physVol);
+    if (itr != m_physVolNameTags.end()) return itr->second;
+    
+    std::string nameTag{volName};
+
+    std::stringstream shapeStream{}, childStream{};
+ 
+    /// Retrieve the children together with their transformations
+    std::vector<GeoChildNodeWithTrf> children = getChildrenWithRef(physVol, true);
+    for (const GeoChildNodeWithTrf& child : children) {
+        /// Dump the transformation
+        
+        unsigned int childIndent = indentation + stdIndent;
+
+        const bool dumpTrf{child.isAlignable || transformSorter.compare(GeoTrf::Transform3D::Identity(), child.transform)};
+
+        if (dumpTrf) {
+            childStream<<whiteSpaces(indentation + stdIndent)
+                       <<"<transform name=\""<<uniqueNameTag(child.nodeName, "VolTrf")<<"\" ";
+            if (child.isAlignable)childStream<<"alignable=\"true\" ";
+            childStream<<">"<<std::endl;
+            
+            childIndent += stdIndent;
+            dumpTransform(childStream, child.transform, child.nodeName, childIndent);           
+        }
+        if (child.nCopies > 1) {
+            childStream<<whiteSpaces(childIndent)<<"<assembly name=\""
+                       <<uniqueNameTag(child.nodeName, "Assembly")<<"\">"<<std::endl;
+            childIndent +=stdIndent;
+            childStream<<whiteSpaces(childIndent)<<"<multicopy n=\""<<child.nCopies<<"\" "
+                       <<"name=\""<<uniqueNameTag(child.nodeName, "MultiCopy")<<"\">"<<std::endl;
+            childIndent+=stdIndent;
+            dumpTransform(childStream, child.inductionRule, child.nodeName, childIndent);
+        }
+        childStream<<whiteSpaces(childIndent)<<"<logvolref ref=\""<<publishPhysVol(outStream, child.volume, child.nodeName)<<"\" />"<<std::endl;
+        if (child.nCopies > 1) {
+            childIndent-=stdIndent;
+            childStream<<whiteSpaces(childIndent)<<"</multicopy>"<<std::endl;
+            childIndent-=stdIndent;
+            childStream<<whiteSpaces(childIndent)<<"</assembly>"<<std::endl;
+        }
+        if (dumpTrf) {
+            childStream<<whiteSpaces(childIndent - stdIndent)<<"</transform>"<<std::endl;
+        }
+        childStream<<std::endl;        
+    }
+    
+    const std::string shapeName = publishShape(shapeStream, physVol->getLogVol()->getShape(), nameTag);
+    const std::string pubShapes{shapeStream.str()};
+    if (pubShapes.size()) {
+        outStream<<"<shapes>"<<std::endl;
+        outStream<<pubShapes;
+        outStream<<"</shapes>"<<std::endl;
+    }
+    nameTag = uniqueNameTag(nameTag, "logVol");
+
+    outStream<<whiteSpaces(indentation)<<"<logvol name=\""<<nameTag<<"\" ";
+    const std::string matName = replaceExpInString(physVol->getLogVol()->getMaterial()->getName(), "::", "__dColon__");
+
+    outStream<<"shape=\""<<shapeName<<"\" material=\""<<matName<<"\" ";
+    if (children.empty()) {
+        outStream<<"/>"<<std::endl;
+    } else {
+        outStream<<">"<<std::endl;
+        outStream<<childStream.str();    
+        outStream<<whiteSpaces(indentation)<<"</logvol>"<<std::endl;
+    }
+    outStream<<std::endl<<std::endl;
+ 
+    auto insert_itr = m_physVolNameTags.insert(std::make_pair(physVol, nameTag));
+    if (!insert_itr.second) {
+        THROW_EXCEPTION("Failed to cache physical volume "<<physVol.get()<<" with shape "<<printGeoShape(physVol->getLogVol()->getShape())
+                       <<" as "<<nameTag<<std::endl<<"The volume "<<insert_itr.first->first.get()<<" with shape "
+                       <<printGeoShape(insert_itr.first->first->getLogVol()->getShape())<<" as "<<insert_itr.first->second
+                       <<" is blocking the way");
+    }
+
+    return insert_itr.first->second;
+}
+
+std::string GeoXMLDumper::publishShape(std::ostream& shapeStream,
+                                       GeoIntrusivePtr<const GeoShape> shape,
+                                       const std::string& shapeName) {
+    
+    /// The Volume has been already published before refer to it
+    shapeMap::const_iterator itr = m_shapeNameTags.find(shape);
+    if (itr != m_shapeNameTags.end()) return itr->second;
+    
+    
+    std::pair<const GeoShape*, const GeoShape*> operands = getOps(shape);
+    /// The shape is a boolean volume (GeoShapeUnion, Subtraction, Intersection)
+    if (operands.first && operands.second) {
+        publishShape(shapeStream, operands.first, shapeName);
+        publishShape(shapeStream, operands.second, shapeName);
+    } 
+    /// The shape is a  GeoShapeShift 
+    else if (operands.first) {
+        /// In fact there might be several Shape shifts in a row. Collapse them into a single shift
+        /// and publish the underlying shape
+        operands = getOps(compressShift(shape));
+        publishShape(shapeStream, operands.first, shapeName);
+    }
+    
+    /// Create a unique name tag
+    const std::string nameTag{uniqueNameTag(shapeName.size() ? shapeName : shape->type(), "Shape")};
+    /// Register the shape
+    const auto insert_itr = m_shapeNameTags.insert(std::make_pair(shape, nameTag));
+    if (!insert_itr.second) {
+        THROW_EXCEPTION("The insertion of shape "<<printGeoShape(shape)<<" as "<<nameTag<<" failed."<<std::endl<<
+                        "The shape "<<printGeoShape(insert_itr.first->first)<<" as "<<insert_itr.first->second
+                        << " has been added before. ");
+    }
+
+    auto dumpParameter = [&shapeStream](const std::string_view parName, 
+                                        const double value,
+                                        bool isAngle = false) {
+        
+        shapeStream<<parName<<"=\"";
+        if (isAngle) {
+            shapeStream<<value*toDeg<<"*deg\" ";
+        } else {
+            shapeStream<<value<<"\" ";
+        }
+    };
+
+    constexpr unsigned int indentation = stdIndent;
+    const unsigned int shapeID = shape->typeID();    
+    shapeStream<<whiteSpaces(indentation);
+    if (shapeID == GeoShapeUnion::getClassTypeID() ||
+        shapeID == GeoShapeSubtraction::getClassTypeID() ||
+        shapeID == GeoShapeIntersection::getClassTypeID()) {       
+        
+        const std::string xmlField = shapeID == GeoShapeUnion::getClassTypeID() ? "union" :
+                                     shapeID == GeoShapeSubtraction::getClassTypeID()? "subtraction" : "intersection";
+        shapeStream<<"<"<<xmlField<<" name=\""<<nameTag<<"\" >"<<std::endl;
+        shapeStream<<shapeRefTag(publishShape(shapeStream, operands.first, shapeName), indentation + stdIndent)<<std::endl;
+        if (operands.second->typeID() == GeoShapeShift::getClassTypeID()) {
+            GeoIntrusivePtr<const GeoShape> collapsedShift = compressShift(operands.second);
+            const GeoShapeShift* shift = dynamic_cast<const GeoShapeShift*>(collapsedShift.get());
+            dumpTransform(shapeStream, shift->getX(), shapeName, indentation + stdIndent);
+            shapeStream<<shapeRefTag(publishShape(shapeStream, shift->getOp(), shapeName), indentation + stdIndent)<<std::endl;
+        } else{
+            shapeStream<<shapeRefTag(publishShape(shapeStream, operands.second, shapeName), indentation + stdIndent)<<std::endl;
+        }
+        shapeStream<<whiteSpaces(indentation)<<"</"<<xmlField<<">"<<std::endl;
+    } else if (shapeID == GeoShapeShift::getClassTypeID()) {
+        shapeStream<<"<shapeshift name=\""<<nameTag<<"\" >"<<std::endl;
+        GeoIntrusivePtr<const GeoShape> collapsedShift = compressShift(shape);
+        const GeoShapeShift* shift = dynamic_cast<const GeoShapeShift*>(collapsedShift.get());
+        dumpTransform(shapeStream, shift->getX(), shapeName, indentation + stdIndent);        
+        shapeStream<<shapeRefTag(publishShape(shapeStream, operands.first, shapeName), 
+                                              indentation + stdIndent)<<std::endl;
+        shapeStream<<whiteSpaces(indentation)<<"</shapeshift>"<<std::endl;
+
+    } else if (shapeID == GeoBox::getClassTypeID()) {
+        shapeStream<<"<box name=\""<<nameTag<<"\" ";
+        const GeoBox* box = dynamic_cast<const GeoBox*>(shape.get());
+        dumpParameter("xhalflength", box->getXHalfLength());
+        dumpParameter("yhalflength", box->getYHalfLength());
+        dumpParameter("zhalflength", box->getZHalfLength());
+        shapeStream<<"/>"<<std::endl;
+    } else if (shapeID == GeoCons::getClassTypeID()) {
+        const GeoCons* cons = static_cast<const GeoCons*>(shape.get());
+        shapeStream<<"<cons name=\""<<nameTag<<"\" ";
+        dumpParameter("rmin1", cons->getRMin1());
+        dumpParameter("rmin2", cons->getRMin2());
+        dumpParameter("rmax1", cons->getRMax1());
+        dumpParameter("rmax2", cons->getRMax2());
+        dumpParameter("dz", cons->getDZ());
+        dumpParameter("sphi", cons->getSPhi(), true);
+        dumpParameter("dphi", cons->getDPhi(), true);
+        shapeStream<<"/>"<<std::endl;
+    } else if (shapeID == GeoEllipticalTube::getClassTypeID()) {
+        const GeoEllipticalTube* tube = static_cast<const GeoEllipticalTube*>(shape.get());
+        shapeStream<<"<ellipticaltube name=\""<<nameTag<<"\" ";
+        dumpParameter("xhalflength", tube->getXHalfLength());
+        dumpParameter("yhalflength", tube->getYHalfLength());
+        dumpParameter("zhalflength", tube->getZHalfLength());
+        shapeStream<<"/>"<<std::endl;
+    } else if (shapeID == GeoGenericTrap::getClassTypeID()) {
+        const GeoGenericTrap* trap = static_cast<const GeoGenericTrap*>(shape.get());
+        shapeStream<<"<generictrap name=\""<<nameTag<<"\" ";
+        dumpParameter("zhalflength", trap->getZHalfLength());
+        for (unsigned int v = 0; v < std::min(8lu, trap->getVertices().size()); ++v){
+            shapeStream<<"x"<<v<<"=\""<<trap->getVertices()[v].x()<<"\" ";
+            shapeStream<<"y"<<v<<"=\""<<trap->getVertices()[v].y()<<"\" ";
+        } 
+        shapeStream<<"/>"<<std::endl;
+    } else if (shapeID == GeoPara::getClassTypeID()) {
+        const GeoPara* para = dynamic_cast<const GeoPara*>(shape.get());
+        shapeStream<<"<para name=\""<<nameTag<<"\" ";
+        dumpParameter("xhalflength", para->getXHalfLength());
+        dumpParameter("yhalflength", para->getYHalfLength());
+        dumpParameter("zhalflength", para->getZHalfLength());
+        dumpParameter("alpha", para->getAlpha(), true);
+        dumpParameter("theta", para->getTheta(), true);
+        dumpParameter("phi", para->getPhi(), true);
+        shapeStream<<"/>"<<std::endl;
+    } else if (shapeID == GeoPcon::getClassTypeID()) {
+        const GeoPcon* pcon = dynamic_cast<const GeoPcon*>(shape.get());
+        shapeStream<<"<pcon name=\""<<nameTag<<"\" ";
+        dumpParameter("sphi", pcon->getSPhi(), true);
+        dumpParameter("dphi", pcon->getSPhi(), true);
+        shapeStream<<">"<<std::endl;
+        for (unsigned int plane = 0 ; plane < pcon->getNPlanes(); ++plane) {
+            shapeStream<<whiteSpaces(indentation + stdIndent);
+            shapeStream<<"<addplane ";
+            dumpParameter("zplane", pcon->getZPlane(plane));
+            dumpParameter("rminplane", pcon->getRMinPlane(plane));            
+            dumpParameter("rmaxplane", pcon->getRMaxPlane(plane));
+            shapeStream<<"/>"<<std::endl;
+        }        
+        shapeStream<<whiteSpaces(indentation)<<"</pcon>"<<std::endl;
+    } else if (shapeID == GeoPgon::getClassTypeID()) {
+        const GeoPgon* pgon = dynamic_cast<const GeoPgon*>(shape.get());
+        shapeStream<<"<pgon name=\""<<nameTag<<"\" ";
+        dumpParameter("sphi", pgon->getSPhi(), true);
+        dumpParameter("dphi", pgon->getSPhi(), true);
+        dumpParameter("nsides", pgon->getNSides());
+        for (unsigned int plane = 0 ; plane < pgon->getNPlanes(); ++plane) {
+            shapeStream<<whiteSpaces(indentation + stdIndent);
+            shapeStream<<"<plane ";
+            dumpParameter("zplane", pgon->getZPlane(plane));
+            dumpParameter("rminplane", pgon->getRMinPlane(plane));            
+            dumpParameter("rmaxplane", pgon->getRMaxPlane(plane));
+            shapeStream<<"/>"<<std::endl;
+        }        
+        shapeStream<<whiteSpaces(indentation)<<"</pgon>"<<std::endl;
+    } else if (shapeID == GeoTorus::getClassTypeID()) {
+        shapeStream<<"<torus name=\""<<nameTag<<"\" ";
+        const GeoTorus* torus{dynamic_cast<const GeoTorus*>(shape.get())};
+        dumpParameter("rmin", torus->getRMin());
+        dumpParameter("rmax", torus->getRMax());
+        dumpParameter("rtor", torus->getRTor());
+        dumpParameter("sphi", torus->getSPhi(), true);
+        dumpParameter("dphi", torus->getDPhi(), true);
+        shapeStream<<"/>"<<std::endl;
+    } else if (shapeID == GeoTrap::getClassTypeID()) {
+        shapeStream<<"<trap name=\""<<nameTag<<"\" ";
+        const GeoTrap* trap{dynamic_cast<const GeoTrap*>(shape.get())};
+        dumpParameter("zhalflength", trap->getZHalfLength());
+        dumpParameter("theta", trap->getTheta(), true);
+        dumpParameter("phi", trap->getPhi(), true);
+        dumpParameter("dydzn", trap->getDydzn());
+        dumpParameter("dxdyndzn", trap->getDxdyndzn());
+        dumpParameter("dxdypdzn", trap->getDxdypdzn());
+        dumpParameter("angleydzn", trap->getAngleydzn(), true);
+        dumpParameter("dydzp", trap->getDydzn());
+        dumpParameter("dxdyndzp", trap->getDxdyndzp());
+        dumpParameter("dxdypdzp", trap->getDxdypdzp());
+        dumpParameter("angleydzp", trap->getAngleydzp());
+        shapeStream<<"/>"<<std::endl;
+    } else if (shapeID == GeoTrd::getClassTypeID()) {
+        shapeStream<<"<trd name=\""<<nameTag<<"\" ";
+        const GeoTrd* trd = dynamic_cast<const GeoTrd*>(shape.get());
+        dumpParameter("xhalflength1", trd->getXHalfLength1());
+        dumpParameter("xhalflength2", trd->getXHalfLength2());
+        dumpParameter("yhalflength1", trd->getYHalfLength1());
+        dumpParameter("yhalflength2", trd->getYHalfLength2());
+        dumpParameter("zhalflength", trd->getZHalfLength());
+        shapeStream<<"/>"<<std::endl;
+    } else if (shapeID == GeoTube::getClassTypeID()) {
+        const GeoTube* tube = dynamic_cast<const GeoTube*>(shape.get());
+        shapeStream<<"<tube name=\""<<nameTag<<"\" ";
+        dumpParameter("rmin", tube->getRMin());
+        dumpParameter("rmax", tube->getRMax());
+        dumpParameter("zhalflength", tube->getZHalfLength());
+        shapeStream<<"/>"<<std::endl;
+    } else if (shapeID == GeoTubs::getClassTypeID()) {
+        const GeoTubs* tube = dynamic_cast<const GeoTubs*>(shape.get());
+        shapeStream<<"<tubs name=\""<<nameTag<<"\" ";
+        dumpParameter("rmin", tube->getRMin());
+        dumpParameter("rmax", tube->getRMax());
+        dumpParameter("zhalflength", tube->getZHalfLength());
+        dumpParameter("sphi", tube->getSPhi(), true);
+        dumpParameter("dphi", tube->getDPhi(), true);
+        shapeStream<<"/>"<<std::endl;
+    } else if(shapeID == GeoSimplePolygonBrep::getClassTypeID()) {
+        const GeoSimplePolygonBrep* brep = dynamic_cast<const GeoSimplePolygonBrep*>(shape.get());
+        shapeStream<<"<simplepolygonbrep name=\""<<nameTag<<"\" ";
+        dumpParameter("zhalflength" , brep->getDZ());
+        const unsigned int N = brep->getNVertices();
+        shapeStream<<"xpoints=\""<<chainUp<double>(N, [brep](unsigned int v){return brep->getXVertex(v); }, ";")<<"\" ";
+        shapeStream<<"ypoints=\""<<chainUp<double>(N, [brep](unsigned int v){return brep->getYVertex(v); }, ";")<<"\" ";        
+        shapeStream<<"/>"<<std::endl;
+    } else {
+        THROW_EXCEPTION("The shape "<<printGeoShape(shape)<<"is not implemented.");
+    }
+    return nameTag;
+}
+
+void GeoXMLDumper::dumpTransform(std::ostream& ostr,
+                                 const GeoTrf::Transform3D& trans,
+                                 const std::string& name,
+                                 unsigned int indentation) {    
+
+
+    transformMap::const_iterator itr = m_transformNameTags.find(trans);
+    if (!name.empty() && itr != m_transformNameTags.end()) {
+        ostr<<whiteSpaces(indentation)<<"<transformationref  ref=\""<<itr->second<<"\" />"<<std::endl;
+        return;
+    }
+
+
+    std::stringstream transstr{};
+    transstr<< std::setiosflags(std::ios::fixed) << std::setprecision(3);
+    std::string nameTag{replaceExpInString(name," ","_")};
+    if (nameTag.size()) {
+        nameTag = uniqueNameTag(nameTag, "Trf");
+        auto insert_itr = m_transformNameTags.insert(std::make_pair(trans, nameTag));
+        if(!insert_itr.second) {
+            THROW_EXCEPTION("Failed to insert transformation "<<GeoTrf::toString(trans));
+        }
+    }
+    transstr<<whiteSpaces(indentation)<<"<transformation ";
+    if (nameTag.size()) transstr<<"name=\""<<nameTag<<"\" ";   
+    transstr<<">"<<std::endl;
+   
+    if (!transformSorter.compare(trans, GeoTrf::Transform3D::Identity())) {
+        transstr<<whiteSpaces(indentation + stdIndent)
+                <<"<translation x=\"0\" y=\"0\" z=\"0\" />"<<std::endl;
+    } else {
+        const GeoTrf::CoordEulerAngles angles = GeoTrf::getCoordRotationAngles(trans);
+        const GeoTrf::Vector3D translation = trans.translation();
+        /// Dump the translation
+        if (translation.dot(translation) > transTolerance) {
+            transstr<<whiteSpaces(indentation +stdIndent)<<"<translation ";
+            if (std::abs(translation.x()) > transTolerance) {
+                transstr<<"x=\""<<translation.x()<<"\" ";
+            }
+            if (std::abs(translation.y()) > transTolerance) {
+                transstr<<"y=\""<<translation.y()<<"\" ";
+            }
+            if (std::abs(translation.z()) > transTolerance) {
+                transstr<<"z=\""<<translation.z()<<"\" ";
+            }
+            transstr<<"/>"<<std::endl;
+        }
+        transstr<<std::setiosflags(std::ios::fixed) << std::setprecision(2);
+
+
+        /// Dump the rotations
+        if (angles.alpha) {
+            transstr<<whiteSpaces(indentation+stdIndent)<<"<rotation xcos=\"1\" angle=\""
+                    <<angles.alpha*toDeg<<"*deg\" />"<<std::endl;
+        }
+        if (angles.beta) {
+            transstr<<whiteSpaces(indentation+stdIndent)
+                        <<"<rotation ycos=\"1\" angle=\""<<angles.beta*toDeg<<"*deg\" />"<<std::endl;
+        }
+        if (angles.gamma) {
+            transstr<<whiteSpaces(indentation+stdIndent)
+                    <<"<rotation zcos=\"1\" angle=\""<<angles.gamma*toDeg<<"*deg\" />"<<std::endl;
+        }
+    }
+    transstr<<whiteSpaces(indentation)<<"</transformation>"<<std::endl;
+    ostr<<transstr.str();
+}
+void GeoXMLDumper::dumpGeoModelHeader(std::ostream& fileStream,
+                                      const topVolWithPathList& pubVolsWithFiles) const {
+
+    fileStream<<"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"<<std::endl;
+    fileStream<<"<!DOCTYPE geomodel SYSTEM \""<<m_xmlLayoutFile<<"\" [ "<<std::endl;
+    
+    std::vector<std::string> entities{};
+    for (const auto& [fileName, pubVolName] : pubVolsWithFiles) {
+        entities.emplace_back(fileNameBody(fileName));
+        fileStream<<whiteSpaces(stdIndent)<<"<!ENTITY "<<entities.back();
+        fileStream<<" SYSTEM \""<<fileName<<"\" >"<<std::endl;
+    }
+
+    fileStream<<"]>"<<std::endl<<std::endl<<std::endl;
+
+    fileStream<<"<geomodel name=\""<<m_systemName<<"\" version=\"1.0\" >"<<std::endl;
+    fileStream<<whiteSpaces(stdIndent)<<"<defines>"<<std::endl;
+    fileStream<<whiteSpaces(2*stdIndent)<<"<var name =\"deg\" value=\""<<GeoModelKernelUnits::deg<<"\" />"<<std::endl;
+    fileStream<<whiteSpaces(stdIndent)<<"</defines>"<<std::endl<<std::endl<<std::endl;
+
+    for (const std::string& entity : entities) {
+        fileStream<<whiteSpaces(stdIndent)<<"&"<<entity<<";"<<std::endl;
+    }
+    fileStream<<std::endl<<std::endl;
+
+    fileStream<<whiteSpaces(stdIndent)<<"<addbranch>"<<std::endl;
+    for (const auto& [fileName, pubVolName] : pubVolsWithFiles) {
+        fileStream<<whiteSpaces(2*stdIndent)<<"<logvolref ref=\""<<pubVolName<<"\" />"<<std::endl;
+    }
+    fileStream<<whiteSpaces(stdIndent)<<"</addbranch>"<<std::endl;
+    fileStream<<"</geomodel>"<<std::endl;
+}
\ No newline at end of file
diff --git a/GeoModelTools/GeoModelXMLDumper/util/main.cxx b/GeoModelTools/GeoModelXMLDumper/util/main.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3b37523d430e3e785dbe8cf3a522bf60ce530f73
--- /dev/null
+++ b/GeoModelTools/GeoModelXMLDumper/util/main.cxx
@@ -0,0 +1,105 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "GeoModelHelpers/defineWorld.h"
+#include "GeoModelKernel/GeoGeometryPluginLoader.h"
+#include "GeoModelXMLDumper/GeoXMLDumper.h"
+#include "GeoModelHelpers/FileUtils.h"
+#include "GeoModelHelpers/StringUtils.h"
+
+
+#include <vector>
+#include <string>
+#include <iostream>
+
+using namespace GeoFileUtils;
+using namespace GeoStrUtils;
+
+
+int main(int argc, char ** argv) {
+     const  std::string stdDtdFile{"geomodel_v0.dtd"};
+
+    std::vector<std::string> plugins{};
+    std::string outDir{};
+    std::string detectorName{"ForkLiftGarage"};
+    std::string xmlLayoutFile{};
+
+    for (int k =1 ; k < argc; ++k) {
+        const std::string the_arg{argv[k]};
+        if (the_arg=="-p" || the_arg=="--plugin") {
+           bool added{false};
+           while (k+1 < argc) {
+                const std::string toLoad{argv[k+1]};
+                if (toLoad[0] == '-') break;
+                plugins.push_back(toLoad);
+                ++k;
+                added = true;
+           }
+           if (!added) {
+                std::cerr<<"Please give at least one follow-up argument to "<<the_arg<<std::endl;
+                return EXIT_FAILURE;
+           }
+        } else if (the_arg == "-o" || the_arg=="--outDir"){
+            if (k+1 < argc) {
+                outDir = argv[k+1];
+                ++k;
+            } else {
+                std::cerr<<"Please give one follow-up argument to "<<the_arg<<std::endl;
+                return EXIT_FAILURE;
+            }
+        } else {
+            std::cerr<<"Unknown argument "<<the_arg<<std::endl;
+            return EXIT_FAILURE;
+        }
+    }
+    if(plugins.empty()) {
+        std::cerr<<"Please specify at least one plugin via -p / --plugin "<<std::endl;
+        return EXIT_FAILURE;
+    }
+
+    if (outDir.empty()) {
+        std::cerr<<"Please specify an output file via -o "<<std::endl;
+        return EXIT_FAILURE;
+    }
+    if (!mkdir(outDir)) {
+        std::cerr<<"Failed to create directory "<<outDir<<std::endl;
+        return EXIT_FAILURE;
+    }
+    if (xmlLayoutFile.empty()) {
+        const std::string ldEnvVar = resolveEnviromentVariables("${LD_LIBRARY_PATH}");
+        const std::vector<std::string> ldLibPaths{tokenize(ldEnvVar, ":")};
+        for (const std::string& ld_lib : ldLibPaths){
+            const std::string sharePath{ld_lib+"/../share/"};
+            if (!doesDirectoryExist(sharePath)) {
+                continue;
+            }            
+            //  
+            const std::vector<std::string> dtdPaths{findFile(sharePath, stdDtdFile)};
+            
+            if (dtdPaths.size() && !copyFile(dtdPaths[0], outDir + "/" + stdDtdFile)) {
+                return EXIT_FAILURE;
+            }
+            xmlLayoutFile = stdDtdFile;
+        }
+        if (xmlLayoutFile.empty()) {
+            return EXIT_FAILURE;
+        }
+    }
+    GeoIntrusivePtr<GeoVPhysVol> world{createGeoWorld()};
+
+    for (const std::string& load_me : plugins) {
+        std::cout<<"Load plugin "<<load_me<<std::endl;
+        GeoGeometryPluginLoader loader;
+        std::unique_ptr<GeoVGeometryPlugin> factory{loader.load(load_me)};
+        if (!factory) {
+          std::cerr << "Could not load plugin " << load_me << std::endl;
+          return EXIT_FAILURE;
+        }
+        factory->create(world);
+    }
+    GeoXMLDumper dumper{outDir, detectorName, xmlLayoutFile};
+    dumper.publishWorld(world);
+
+    return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/athena_ci/athena_build.sh b/athena_ci/athena_build.sh
index 5da73e55b1bdf5a9cad080c4e34711f62f0ca570..b2e11f57485b7c75c2f70a582075fd9f94d2df69 100755
--- a/athena_ci/athena_build.sh
+++ b/athena_ci/athena_build.sh
@@ -96,10 +96,6 @@ echo "LCG_PLATFORM: ${LCG_PLATFORM}"
 
 lsetup "views ${LCG_RELEASE} ${LCG_PLATFORM}" || true
 
-CCACHE=$(command -v ccache)
-$CCACHE -z
-
-
 export 
 
 if [ "${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}" == "main" ];then
@@ -128,12 +124,10 @@ cmake -S "${SCRIPT_DIR}/.." -B geomodel-build \
   -DCMAKE_INSTALL_PREFIX=$gm_install_dir \
   -DCMAKE_BUILD_TYPE=RelWithDebInfo \
   -DGEOMODEL_BUILD_TOOLS=ON \
-  -DCMAKE_CXX_COMPILER_LAUNCHER=$CCACHE \
 
 heading "Build GeoModel"
 
 cmake --build geomodel-build
-$CCACHE -s
 
 echo "Installing GeoModel"
 cmake --install geomodel-build > gm_install.log
@@ -155,14 +149,8 @@ popd > /dev/null
 echo
 fill_line "="
 
-echo "$CCACHE -z"
-$CCACHE -z
-
-echo "$CCACHE -C"
-$CCACHE -C
 export CMAKE_PREFIX_PATH="${gm_install_dir}:$CMAKE_PREFIX_PATH"
 
-
 heading "Configure Athena"
 
 full_package_filters=$SCRIPT_DIR/package_filters.txt
@@ -208,7 +196,6 @@ cmake "$ATHENA_SOURCE/Projects/WorkDir" \
   -DCMAKE_MAKE_PROGRAM="$NINJA" \
   -DCMAKE_CXX_FLAGS="$EXTRA_FLAGS -isystem ${gm_install_dir}/include" \
   -DATLAS_PACKAGE_FILTER_FILE="$package_filters" \
-  -DCMAKE_CXX_COMPILER_LAUNCHER=$CCACHE \
   -DCMAKE_INSTALL_PREFIX=$install_dir
 
 popd
@@ -222,15 +209,11 @@ echo "export LD_LIBRARY_PATH=\"${gm_install_dir}/lib64:\${LD_LIBRARY_PATH}\"" >>
 echo "export PATH=\"${gm_install_dir}/share:\${PATH}\"" >> athena-build/*/env_setup.sh
 echo "export ROOT_INCLUDE_PATH=\"${gm_install_dir}/include:\${ROOT_INCLUDE_PATH}\"" >> athena-build/*/env_setup.sh
 
-
-
 heading "Build Athena"
-
 if [ -z "$CI" ]; then
-  heading "Interactive mode, dropping into shell"
-  bash
+    heading "Interactive mode, dropping into shell"
+    bash
 else
-  cmake --build athena-build -- -j3
-  $CCACHE -s
-  cmake --install athena-build > athena_install.log
+    cmake --build athena-build -- -j5
+    cmake --install athena-build > athena_install.log
 fi
diff --git a/athena_ci/ci.yml b/athena_ci/ci.yml
index 5f349ddba90a8416984ec58dd07a354fc44a1a2d..545d624ed8f913d058fd4c8e514a4c6e3cf524d7 100644
--- a/athena_ci/ci.yml
+++ b/athena_ci/ci.yml
@@ -150,7 +150,7 @@ run_workflow_tests_run2_mc:
   extends: .run_base
   script:
     - cd run
-    - RunWorkflowTests_Run2.py --CI -r -w MCReco --threads 0 -e '--CA "all:True" "RDOtoRDOTrigger:False" --conditionsTag "default:OFLCOND-MC16-SDR-RUN2-11" "RDOtoRDOTrigger:OFLCOND-MC16-SDR-RUN2-08-02" --maxEvents 25' --detailed-comparison
+    - RunWorkflowTests_Run2.py --CI -r -w MCReco --threads 0 -e '--maxEvents 25 --conditionsTag OFLCOND-MC16-SDR-RUN2-12' --detailed-comparison
 
 run_workflow_tests_run3_mc:
   extends: .run_base
diff --git a/athena_ci/package_filters.txt b/athena_ci/package_filters.txt
index ae8bb3e96588217f2b797db5f44e44e649d87283..aa6c0bd86c210047d7b2de81915c8a756c53f2ab 100644
--- a/athena_ci/package_filters.txt
+++ b/athena_ci/package_filters.txt
@@ -18,6 +18,7 @@
 + DetectorDescription/GeoModel/GeoModelSvc
 + DetectorDescription/GeoModel/GeoModelUtilities
 + DetectorDescription/GeoModel/GeoSpecialShapes
++ DetectorDescription/GeoModel/DumpGeo
 + DetectorDescription/IdDictDetDescrCnv
 + DetectorDescription/ReadoutGeometryBase
 + ForwardDetectors/AFP/AFP_GeoModel
@@ -44,6 +45,7 @@
 + InnerDetector/InDetDetDescr/InDetTrackingGeometry
 + InnerDetector/InDetDetDescr/PixelGeoModel
 + InnerDetector/InDetDetDescr/PixelGeoModelXml
++ InnerDetector/InDetDetDescr/PixelReadoutDefinitions
 + InnerDetector/InDetDetDescr/PixelReadoutGeometry
 + InnerDetector/InDetDetDescr/SCT_GeoModel
 + InnerDetector/InDetDetDescr/SCT_ReadoutGeometry
@@ -110,6 +112,7 @@
 + Simulation/G4Utilities/Geo2G4
 + Simulation/G4Utilities/GeoMaterial2G4
 + Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization
++ Simulation/AtlasGeant4
 + TileCalorimeter/TileConditions
 + TileCalorimeter/TileDetDescr
 + TileCalorimeter/TileFastCaloSim
@@ -128,14 +131,3 @@
 + Tracking/TrkDetDescr/TrkDetDescrGeoModelCnv
 + Tracking/TrkDetDescr/TrkDetElementBase
 + Trigger/TrigAlgorithms/TrigL2MuonSA
-+ graphics/GeometryJiveXML
-+ graphics/VP1/VP1Base
-+ graphics/VP1/VP1Systems/VP1AODSystems
-+ graphics/VP1/VP1Systems/VP1CaloSystems
-+ graphics/VP1/VP1Systems/VP1GeometrySystems
-+ graphics/VP1/VP1Systems/VP1GuideLineSystems
-+ graphics/VP1/VP1Systems/VP1PRDSystems
-+ graphics/VP1/VP1Systems/VP1RawDataSystems
-+ graphics/VP1/VP1Systems/VP1TrackSystems
-+ graphics/VP1/VP1Systems/VP1TrackingGeometrySystems
-+ graphics/VP1/VP1Utils
diff --git a/athena_ci/patch_package_filters.txt b/athena_ci/patch_package_filters.txt
index 1af0733cac65957da6adfc15366c248b1a153f26..f5d158999179ac28adc33cf4b7ca20d8ad3100e4 100644
--- a/athena_ci/patch_package_filters.txt
+++ b/athena_ci/patch_package_filters.txt
@@ -1,3 +1,2 @@
 # These Athena packages need to be rebuilt 
 # due to patches in the `patches` directory
-
diff --git a/athena_ci/test_athena.sh b/athena_ci/test_athena.sh
index 406930c7d12502fd1b42c4606be1df0102f8759c..c3cbc378807d977129671e0022151f3641175de5 100755
--- a/athena_ci/test_athena.sh
+++ b/athena_ci/test_athena.sh
@@ -12,9 +12,6 @@ export ATHENA_REF=main
 
 _pwd=$PWD
 
-export ATHENA_SOURCE=$PWD/athena
-
-
 tmp=/tmp/ath_build
 rm -rf $tmp
 mkdir -p $tmp
diff --git a/documentation/docs/components/kernel/overview/MaterialGeometry.md b/documentation/docs/components/kernel/overview/MaterialGeometry.md
index a94751a87e126ccd999554c77da97343ef9a9d53..32280d9df11e2fb0820647b7e828dd7cdd626f51 100644
--- a/documentation/docs/components/kernel/overview/MaterialGeometry.md
+++ b/documentation/docs/components/kernel/overview/MaterialGeometry.md
@@ -1,17 +1,17 @@
 
 ## Material Geometry
 
-Material geometry consists of a set of classes that bears a large resemblance to the material geometry within GEANT4. These classes, however, are designed to take a minimal size in memory. This requirement determines the basic data structure used to hold the data for the geometry description. That structure is a graph of nodes consisting of both volumes and their properties. The tree is built directly and accessed in a way that provides users access to volumes and, simultaneously, to the properties accumulated during graph traversal that apply to the volumes. See the [Actions](/components/kernel/overview/#actions) section, below.
+Material geometry consists of a set of classes that bears a large resemblance to the material geometry within GEANT4. These classes, however, are designed to take a minimal size in memory. This requirement determines the basic data structure used to hold the data for the geometry description. That structure is a graph of nodes consisting of both volumes and their properties. The tree is built directly and accessed in a way that provides users access to volumes and, simultaneously, to the properties accumulated during graph traversal that apply to the volumes. See the [Actions](#actions) section, below.
 
-The requirement of minimizing the memory consumption has led us to foresee a system in which objects (as well as classes) in the detector description can be re-used. This is called shared instancing and is described below. It essentially means that an element, compound, volume, or entire tree of volumes may be referenced by more than one object in the detector description.   Shared instancing can make the deletion of objects difficult unless special measures are taken.  We have used reference counting in order to facilitate clean-up and make it less error prone. See the section [How Objects are Created and Destroyed](/components/kernel/overview/#how-objects-are-created-and-destroyed).
+The requirement of minimizing the memory consumption has led us to foresee a system in which objects (as well as classes) in the detector description can be re-used. This is called shared instancing and is described below. It essentially means that an element, compound, volume, or entire tree of volumes may be referenced by more than one object in the detector description.   Shared instancing can make the deletion of objects difficult unless special measures are taken.  We have used reference counting in order to facilitate clean-up and make it less error prone. See the section [How Objects are Created and Destroyed](#how-objects-are-created-and-destroyed).
 
 Before creating hierarchies of volumes representing positioned pieces of detectors, we need to create lower-level primitives, such as elements, materials, and shapes. So, we will discuss these first.
 
 ### Materials
 
-Materials are represented within the geometry kernel class library by the class [GeoMaterial](/components/kernel/reference/#geomaterial), and are built up by combining different elements, specifying each element and its fraction-by-mass.  Material constants such as the radiation length and the interaction length, as well as constants for ionization energy loss, are available through the interface but do not need to be provided to the constructor.  Instead, they are computed from the material’s element list.
+Materials are represented within the geometry kernel class library by the class [GeoMaterial](../reference/#geomaterial), and are built up by combining different elements, specifying each element and its fraction-by-mass.  Material constants such as the radiation length and the interaction length, as well as constants for ionization energy loss, are available through the interface but do not need to be provided to the constructor.  Instead, they are computed from the material’s element list.
 
-The class [GeoElement](/components/kernel/reference/#geoelement) is used to represent elements.  Their constructor requires a name, symbol, and effective Z and A. These properties can also be retrieved from the element.
+The class [GeoElement](../reference/#geoelement) is used to represent elements.  Their constructor requires a name, symbol, and effective Z and A. These properties can also be retrieved from the element.
 
 `GeoMaterial` objects are created by specifying a name and a density.  The material is “empty” until elements are added, one by one, using the `GeoMaterial::add()` method, which is overloaded so that one may provide either elements or prebuilt materials.  After all materials are added, the `GeoMaterial::lock()` method must be called, which prevents further elements or materials from being added.
 
@@ -86,7 +86,7 @@ polycone->addPlane(z2, rmin2, rmax2);
 
 This creates a polycone whose projection subtends an angle of 10 degrees between 40 degrees and 50 degrees, with planes at z=0, z=10, and z=15, with minimum and maximum radii there of (5,10), (6, 12), and (5,10).
 
-The shapes can provide their data to a client through their accessors, and in addition support several other operations. Boolean operations on shapes are possible.  They can be accomplished through Boolean operators in class [GeoShape](/components/kernel/reference/#introduction_1):
+The shapes can provide their data to a client through their accessors, and in addition support several other operations. Boolean operations on shapes are possible.  They can be accomplished through Boolean operators in class [GeoShape](../reference/#introduction_1):
 
 ```cpp
 GeoShape       * donut  = new GeoTube();
@@ -126,9 +126,9 @@ Having created elements, materials, shapes, and logical volumes, you are now rea
  * Regular Physical Volumes, designed to be small.
  * Full Physical Volumes, designed to hold in cache complete information about how the volume is located with respect to the world volume, its formatted name string and other important information.
 
-There is a common abstract [base class](/components/kernel/overview/#geomodel-kernel-overview) for all of these:  `GeoVPhysVol`.  In addition both the full physical volumes have another layer of abstraction, `GeoVFullPhysVol`. All physical volumes allow access to their children.
+There is a common abstract [base class](#geomodel-kernel-overview) for all of these:  `GeoVPhysVol`.  In addition both the full physical volumes have another layer of abstraction, `GeoVFullPhysVol`. All physical volumes allow access to their children.
 
-The concrete subclasses that you have at your disposition for detector description are called [GeoPhysVol](/components/kernel/reference/#geophysvol) and [GeoFullPhysVol](/components/kernel/reference/#geofullphysvol).  Both of these have a method to add either volumes or volume properties:
+The concrete subclasses that you have at your disposition for detector description are called [GeoPhysVol](../reference/#geophysvol) and [GeoFullPhysVol](../reference/#geofullphysvol).  Both of these have a method to add either volumes or volume properties:
 
 ```cpp
 GeoPhysVol* myVol;
@@ -150,8 +150,8 @@ The model of the raw geometry is a tree of nodes, property nodes and volume node
 Finally, we provide three mechanisms for giving names to volumes:
 
 * Do nothing.  The volume will be called "ANON".
-* Add a [GeoNameTag](/components/kernel/reference/#geonametag) object to the graph before adding a volume.  The next volume to be added will be given the `GeoNameTag`’s name.
-* Add a [GeoSerialDenominator](/components/kernel/reference/#geoserialdenominator) object to the graph before adding more volumes. The volumes will be named according to the base name of the `GeoSerialDenominator`, plus given a serial number: 0, 1, 2, 3, ...
+* Add a [GeoNameTag](../reference/#geonametag) object to the graph before adding a volume.  The next volume to be added will be given the `GeoNameTag`’s name.
+* Add a [GeoSerialDenominator](../reference/#geoserialdenominator) object to the graph before adding more volumes. The volumes will be named according to the base name of the `GeoSerialDenominator`, plus given a serial number: 0, 1, 2, 3, ...
 
 In effect this last method can be thought of as a way of parametrizing the name of the volume.
 
@@ -169,7 +169,7 @@ for(int c=0; c<myVol->getNChildVols(); ++c) {
 
 One could then iterate in a similar way over the grand children, by using a double loop.  Ultimately one would probably to visit all the volumes, whatever their depth in the tree, so probably this would call on some form of recursion.  An easy way would be to embed the small sample of code shown above in a recursive subroutine or method.  That would be fine and is conceptually simple. However, within the geometry model’s kernel, we have provided an alternate, probably better way to visit the entire tree.
 
-That mechanism involves a [GeoVolumeAction]().  A `GeoVolumeAction` is a way (for applications programmers) to obtain recursive behavior without writing any recursive routines.  It’s a class with a handler routine (`handleVPhysVol()`) which is called for each node before (or after) it is called on its children.  This can descend to an arbitrary depth in the tree.  The `GeoVolumeAction` is an abstract base class and should be subclassed by programmers to suit their needs.  Another class `TemplateVolAction` is provided as a template that one can take and modify.  To run it, one does this:
+That mechanism involves a `GeoVolumeAction`.  A `GeoVolumeAction` is a way (for applications programmers) to obtain recursive behavior without writing any recursive routines.  It’s a class with a handler routine (`handleVPhysVol()`) which is called for each node before (or after) it is called on its children.  This can descend to an arbitrary depth in the tree.  The `GeoVolumeAction` is an abstract base class and should be subclassed by programmers to suit their needs.  Another class `TemplateVolAction` is provided as a template that one can take and modify.  To run it, one does this:
 
 ```cpp
 PVConstLink myVol;
@@ -179,7 +179,7 @@ myVol->apply(tempVolAction);
 
 The `handleVPhysVol()` function within the `TemplateVolAction` is where the work is supposed to get done.  It will be invoked repeatedly, once for each node in the tree.  Within that routine, one can access the physical volume as a subroutine parameter, and information about the transformation and the path to the node through the base class for actions, `GeoVolumeAction`.  The action can be designed to run from the bottom up or from the top down.
 
-Incidentally, there is another kind of action in the library called [GeoNodeAction]().  `GeoNodeActions` visit all nodes in the geometry tree, including naming nodes, transformation nodes, and perhaps other property nodes that may be added later to the model.  Since usually an application programmer wants to see volumes and their properties, the `GeoVolumeAction` is more suited to casual users than the `GeoNodeAction`, which is considered mostly internal.  However the usage is similar, except that node actions are “exec’d” while volume actions are “applied”.  Here for example is how we can rewrite the loop over children using volume actions:
+Incidentally, there is another kind of action in the library called `GeoNodeAction`.  `GeoNodeActions` visit all nodes in the geometry tree, including naming nodes, transformation nodes, and perhaps other property nodes that may be added later to the model.  Since usually an application programmer wants to see volumes and their properties, the `GeoVolumeAction` is more suited to casual users than the `GeoNodeAction`, which is considered mostly internal.  However the usage is similar, except that node actions are “exec’d” while volume actions are “applied”.  Here for example is how we can rewrite the loop over children using volume actions:
 
 ```cpp
 PVConstLink myVol;
diff --git a/documentation/docs/components/kernel/overview/Overview.md b/documentation/docs/components/kernel/overview/Overview.md
index 446fd4014a1c468f08853cbb3416c5241775e9ac..a7d22a471ad0d48c91c02402c02a54e5b5b32b6a 100644
--- a/documentation/docs/components/kernel/overview/Overview.md
+++ b/documentation/docs/components/kernel/overview/Overview.md
@@ -1,7 +1,7 @@
 
 ## GeoModel Kernel Overview
 
-In this section we give a short overview of all of the pieces of the GeoModel Kernel. These pieces are described in detail in the kernel [Class Reference](/components/kernel/reference). In this section our goal is to describe the “big picture”.  A subset of the GeoModel kernel class tree is shown on the diagram below.
+In this section we give a short overview of all of the pieces of the GeoModel Kernel. These pieces are described in detail in the kernel [Class Reference](../reference). In this section our goal is to describe the “big picture”.  A subset of the GeoModel kernel class tree is shown on the diagram below.
 
 ```mermaid
 classDiagram
diff --git a/documentation/docs/index.md b/documentation/docs/index.md
index 4f109ec0a43b3ebed53cff147390fea5d86509a5..d1b571a6801e02f9412b41e037caf6d23169194f 100755
--- a/documentation/docs/index.md
+++ b/documentation/docs/index.md
@@ -77,7 +77,7 @@ A standalone tool to **generate geantino maps** of your newly built detector
 
 ## External Dependencies
 
-The external dependencies are minimal:
+The GeoModel Toolkit offers C++ classes that provide geometrical primitives for describing detector geometry (e.g., shapes, logical/physical volumes, transformations), as well as a set of tools for writing, accessing, handling, manipulating, dumping, restoring, visualizing, inspecting, and debugging detector geometries.
 
 - The [Eigen](https://eigen.tuxfamily.org) math library for the core packages
 
diff --git a/documentation/docs/start/install.md b/documentation/docs/start/install.md
index ac3b127fcc5d5083de324f482adbecdfff15a5a6..eb668ff46b1a7be799dd7efa48285390d7e7d844 100644
--- a/documentation/docs/start/install.md
+++ b/documentation/docs/start/install.md
@@ -49,8 +49,9 @@ If you have outdated packages, these commands will replace those packages with t
 
     The GeoModelExplorer (GMEX) application appears to crash when run on the latest version of the Ubuntu "Wayland" window manager. The problem is that the SoQt/Coin third-party packages we use in GMEX have not been ported to work to the latest Wayland yet. As far as we know, all applications that use the Coin/SoQt graphics layers as their graphics interface crash on the latest Wayland. The Coin3D community is aware of the problem but there are no fixes, yet.
 
-    We strongly suggest to use the alternative X11 window manager when running GEMX (or all other SoQt/Coin3D-based applications) on Ubuntu, for the time being.
+    We strongly suggest to use the alternative X11 window manager when running GMEX (or all other SoQt/Coin3D-based applications) on Ubuntu, for the time being.
 
+    Note also:  hardware acceleration is required for the GMEX application to work properly.  On computers with NVidia graphics, install and run the `nvidia-settings` application; go into the `PRIME Profiles` section and choose performance mode.  
 
 ## How to install GeoModel on macOS