From 5dafa5aa26f0486666493cdabfad6c58b8177754 Mon Sep 17 00:00:00 2001
From: Riccardo Maria Bianchi <riccardo.maria.bianchi@cern.ch>
Date: Thu, 3 Oct 2024 16:20:40 +0200
Subject: [PATCH] Add support for EllipticalTube shape in I/O

---
 .../GeoModelDBManager/src/GMDBManager.cpp     |  23 +++-
 GeoModelIO/GeoModelIOHelpers/CMakeLists.txt   |   5 +
 .../tests/test_io_shape_EllipticalTube.cpp    | 120 ++++++++++++++++++
 GeoModelIO/GeoModelRead/CMakeLists.txt        |   2 +-
 .../GeoModelRead/GeoModelRead/ReadGeoModel.h  |   6 +
 .../src/BuildGeoShapes_EllipticalTube.cpp     |  36 ++++++
 .../src/BuildGeoShapes_EllipticalTube.h       |  28 ++++
 GeoModelIO/GeoModelRead/src/ReadGeoModel.cpp  |  48 ++++++-
 .../GeoModelWrite/WriteGeoModel.h             |   7 +
 .../GeoModelWrite/src/WriteGeoModel.cpp       |  20 ++-
 10 files changed, 286 insertions(+), 9 deletions(-)
 create mode 100644 GeoModelIO/GeoModelIOHelpers/tests/test_io_shape_EllipticalTube.cpp
 create mode 100644 GeoModelIO/GeoModelRead/src/BuildGeoShapes_EllipticalTube.cpp
 create mode 100644 GeoModelIO/GeoModelRead/src/BuildGeoShapes_EllipticalTube.h

diff --git a/GeoModelIO/GeoModelDBManager/src/GMDBManager.cpp b/GeoModelIO/GeoModelDBManager/src/GMDBManager.cpp
index 0fe998d67..1a00001f6 100644
--- a/GeoModelIO/GeoModelDBManager/src/GMDBManager.cpp
+++ b/GeoModelIO/GeoModelDBManager/src/GMDBManager.cpp
@@ -120,6 +120,7 @@ void GMDBManager::printAllElements() const { printAllRecords("Elements"); }
 
 void GMDBManager::printAllShapes() const { 
     printAllRecords("Shapes_Box"); 
+    printAllRecords("Shapes_EllipticalTube"); 
     printAllRecords("Shapes_Tube"); 
     printAllRecords("Shapes_Cons"); 
     printAllRecords("Shapes_Para");
@@ -1760,6 +1761,26 @@ bool GMDBManager::createTables() {
     }
     tab.clear();
     
+    // Shapes-EllipticalTube table
+    // ID, XHalfLength, YHalfLength, ZHalfLength
+    geoNode = "GeoEllipticalTube";
+    tableName = "Shapes_EllipticalTube";
+    m_childType_tableName[geoNode] = tableName;
+    tab.push_back(tableName);
+    tab.push_back("id");
+    tab.push_back("computedVolume");
+    tab.push_back("XHalfLength");
+    tab.push_back("YHalfLength");
+    tab.push_back("ZHalfLength");
+    storeTableColumnNames(tab);
+    queryStr = fmt::format(
+        "create table {0}({1} integer primary key, {2} real, {3} real, {4} real, {5} real )",
+        tab[0], tab[1], tab[2], tab[3], tab[4], tab[5]);
+    if (0 == (rc = execQuery(queryStr))) {
+        storeNodeType(geoNode, tableName);
+    }
+    tab.clear();
+    
     // Shapes-Tube table
     geoNode = "GeoTube";
     tableName = "Shapes_Tube";
@@ -2103,7 +2124,7 @@ bool GMDBManager::createTables() {
     }
     tab.clear();
 
-    // Shapes-Box table
+    // Shapes-GeoUnidentifiedShape table
     // ID, XHalfLength, YHalfLength, ZHalfLength
     geoNode = "GeoUnidentifiedShape";
     tableName = "Shapes_UnidentifiedShape";
diff --git a/GeoModelIO/GeoModelIOHelpers/CMakeLists.txt b/GeoModelIO/GeoModelIOHelpers/CMakeLists.txt
index 48dbc3ba4..ac5927ef2 100644
--- a/GeoModelIO/GeoModelIOHelpers/CMakeLists.txt
+++ b/GeoModelIO/GeoModelIOHelpers/CMakeLists.txt
@@ -59,3 +59,8 @@ add_executable(test_io_shapes_unidentifiedshape tests/test_io_UnidentifiedShape.
 target_link_libraries( test_io_shapes_unidentifiedshape GeoModelIO::GeoModelDBManager GeoModelCore::GeoModelHelpers GeoModelCore::GeoModelKernel GeoModelIO::GeoModelIOHelpers)
 add_test(NAME test_IO_Shapes_UnidentifiedShape
          COMMAND test_io_shapes_unidentifiedshape)
+
+add_executable(test_io_shapes_ellipticaltube tests/test_io_shape_EllipticalTube.cpp)
+target_link_libraries( test_io_shapes_ellipticaltube GeoModelIO::GeoModelDBManager GeoModelCore::GeoModelHelpers GeoModelCore::GeoModelKernel GeoModelIO::GeoModelIOHelpers)
+add_test(NAME test_IO_Shapes_EllipticalTube
+         COMMAND test_io_shapes_ellipticaltube)
diff --git a/GeoModelIO/GeoModelIOHelpers/tests/test_io_shape_EllipticalTube.cpp b/GeoModelIO/GeoModelIOHelpers/tests/test_io_shape_EllipticalTube.cpp
new file mode 100644
index 000000000..6d22ff13b
--- /dev/null
+++ b/GeoModelIO/GeoModelIOHelpers/tests/test_io_shape_EllipticalTube.cpp
@@ -0,0 +1,120 @@
+// Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+
+/*
+ * This example tests the I/O of GeoEllipticalTube.
+ *
+ *  Author:     Riccardo Maria BIANCHI @ CERN
+ *  Created on: Oct, 2024
+ */
+
+// GeoModel includes
+#include "GeoModelIOHelpers/GMTests_IO.h"
+#include "GeoModelKernel/GeoEllipticalTube.h"
+#include "GeoModelHelpers/defineWorld.h"
+
+// C++ includes
+#include <iostream>
+
+// Units
+#include "GeoModelKernel/Units.h"
+#define SYSTEM_OF_UNITS \
+    GeoModelKernelUnits // so we will get, e.g., 'GeoModelKernelUnits::cm'
+
+int main(int argc, char *argv[])
+{
+// Define the units
+#define gr SYSTEM_OF_UNITS::gram
+#define mole SYSTEM_OF_UNITS::mole
+
+    // get the World volume,
+    // we build it outside the class for convenience only
+    GeoIntrusivePtr<GeoPhysVol> world{createGeoWorld()};
+
+    // Define elements used in this example:
+    GeoElement *elAluminum = new GeoElement("Aluminum", "Al", 13, 26 * gr / mole);
+
+    // Define materials used in this example:
+    double densityOfAluminum = 2.7; // g/cm^3
+    GeoMaterial *matAluminum = new GeoMaterial("Aluminum", densityOfAluminum);
+    matAluminum->add(elAluminum, 1.0);
+    matAluminum->lock();
+
+
+      // Add a test GeoEllipticalTube shape
+      const double halfX = 5.0 * SYSTEM_OF_UNITS::cm;
+      const double halfY = 30.0 * SYSTEM_OF_UNITS::cm;
+      const double halfZ = 30.0 * SYSTEM_OF_UNITS::cm;
+  GeoEllipticalTube *sh = new GeoEllipticalTube(halfX, halfY, halfZ);                                                                          
+  GeoLogVol *lV = new GeoLogVol("ellipticaltube", sh, matAluminum);                    
+  GeoPhysVol *pV = new GeoPhysVol(lV);                                           
+  GeoNameTag *nT = new GeoNameTag("Shape-EllipticalTube");                                    
+  world->add(nT);                                                                           
+  world->add(pV);
+
+    // write to the test DB
+    std::string testDB = "test_io_shape_EllipticalTube.db";
+    unsigned loglevel = 2;
+    const bool forceDelete = true;
+    GeoModelIO::IO::saveToDB(world, testDB, loglevel, forceDelete);
+
+    // load from the test DB
+    const GeoVPhysVol *world2 = GeoModelIO::IO::loadDB(testDB);
+
+    // get the child volume, then the shape from its logVol
+    GeoIntrusivePtr<const GeoVPhysVol> childVol = world2->getChildVol(0);
+    const GeoEllipticalTube *shape = dynamic_cast<const GeoEllipticalTube *>(childVol->getLogVol()->getShape());
+
+
+    // data to test
+    std::vector<double> test_data_stored;
+    std::vector<double> test_data_restored;
+    std::vector<std::string> test_label;
+
+    test_data_stored.push_back(halfX);
+    test_data_restored.push_back(shape->getXHalfLength());
+    test_label.push_back("shape->getXHalfLength()");
+
+    bool test = true;
+    unsigned int ii{0};
+    for (const auto& num : test_data_stored) {
+        if (test_data_stored != test_data_restored)
+        {
+            std::cout << "\nERROR!!! The restored '" << test_label[ii] << "' is different from the stored version!!!" << std::endl;
+            std::cout << "Stored data: " << num << std::endl;
+            std::cout << "Restored data: " << test_data_restored[ii] << std::endl;
+            test = false;
+        }
+    }
+
+
+
+    if (shape->getXHalfLength() != halfX)
+    {
+        std::cout << "\nERROR!!! The restored 'getXHalfLength' is different from the stored version!!!" << std::endl;
+        std::cout << "Stored data: " << halfX << std::endl;
+        std::cout << "Restored data: " << shape->getXHalfLength() << std::endl;
+        test = false;
+    }
+    if (shape->getYHalfLength() != halfY)
+    {
+        std::cout << "\nERROR!!! The restored 'getYHalfLength' is different from the stored version!!!" << std::endl;
+        std::cout << "Stored data: " << halfY << std::endl;
+        std::cout << "Restored data: " << shape->getYHalfLength() << std::endl;
+        test = false;
+    }
+    if (shape->getZHalfLength() != halfZ)
+    {
+        std::cout << "\nERROR!!! The restored 'getZHalfLength' is different from the stored version!!!" << std::endl;
+        std::cout << "Stored data: " << halfZ << std::endl;
+        std::cout << "Restored data: " << shape->getZHalfLength() << std::endl;
+        test = false;
+    }
+    
+
+    // Return 0 if all OK! :-)
+    // Return 1 otherwise... :-(
+    if (test)
+        return 0;
+    return 1;
+    // ----------------
+}
diff --git a/GeoModelIO/GeoModelRead/CMakeLists.txt b/GeoModelIO/GeoModelRead/CMakeLists.txt
index 5ae4e4fb1..1c4b09d77 100644
--- a/GeoModelIO/GeoModelRead/CMakeLists.txt
+++ b/GeoModelIO/GeoModelRead/CMakeLists.txt
@@ -21,7 +21,7 @@ source_group( "src" FILES ${SOURCES} )
 set_target_properties( GeoModelRead 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( GeoModelIO::GeoModelRead ALIAS GeoModelRead )
diff --git a/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.h b/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.h
index 39aaf8051..794bf2332 100644
--- a/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.h
+++ b/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.h
@@ -67,8 +67,10 @@ class GeoSerialTransformer;
 class GeoGraphNode;
 class GeoShapeSubtraction;
 class GeoBox;
+class GeoEllipticalTube;
 
 class BuildGeoShapes_Box;
+class BuildGeoShapes_EllipticalTube;
 class BuildGeoShapes_Tube;
 class BuildGeoShapes_Cons;
 class BuildGeoShapes_Para;
@@ -159,6 +161,7 @@ class ReadGeoModel {
     };
     unsigned long getNShapes() { return m_shapes.size(); }; // TODO: to be removed later
     unsigned long getNShapes_Box() { return m_shapes_Box.size(); };
+    unsigned long getNShapes_EllipticalTube() { return m_shapes_EllipticalTube.size(); };
     unsigned long getNShapes_Cons() { return m_shapes_Cons.size(); };
     unsigned long getNShapes_Para() { return m_shapes_Para.size(); };
     unsigned long getNShapes_Pcon() { return m_shapes_Pcon.size(); };
@@ -180,6 +183,7 @@ class ReadGeoModel {
    private:
     void buildAllShapes(); // TODO: OLD METHOD, TO BE REMOVED WHEN READY
     void buildAllShapes_Box();
+    void buildAllShapes_EllipticalTube();
     void buildAllShapes_Tube();
     void buildAllShapes_Cons();
     void buildAllShapes_Para();
@@ -380,6 +384,7 @@ class ReadGeoModel {
     //! builders
     // std::unique_ptr<BuildGeoShapes_Box> m_builderShape_Box;
     BuildGeoShapes_Box* m_builderShape_Box{};
+    BuildGeoShapes_EllipticalTube* m_builderShape_EllipticalTube{};
     BuildGeoShapes_Tube* m_builderShape_Tube{};
     BuildGeoShapes_Cons* m_builderShape_Cons{};
     BuildGeoShapes_Para* m_builderShape_Para{};
@@ -416,6 +421,7 @@ class ReadGeoModel {
 
     // containers to store shapes' parameters
     DBRowsList m_shapes_Box;
+    DBRowsList m_shapes_EllipticalTube;
     DBRowsList m_shapes_Tube;
     DBRowsList m_shapes_Cons;
     DBRowsList m_shapes_Para;
diff --git a/GeoModelIO/GeoModelRead/src/BuildGeoShapes_EllipticalTube.cpp b/GeoModelIO/GeoModelRead/src/BuildGeoShapes_EllipticalTube.cpp
new file mode 100644
index 000000000..25f8d328e
--- /dev/null
+++ b/GeoModelIO/GeoModelRead/src/BuildGeoShapes_EllipticalTube.cpp
@@ -0,0 +1,36 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+/*
+ * Created on: May 7, 2024
+ * Author: Riccardo Maria BIANCHI <riccardo.maria.bianchi@cern.ch>
+ */
+
+#include "BuildGeoShapes_EllipticalTube.h"
+
+#include "GeoModelKernel/GeoEllipticalTube.h"
+#include "GeoModelHelpers/variantHelpers.h"
+
+#include <vector>
+#include <iostream>
+
+void BuildGeoShapes_EllipticalTube::buildShape(const DBRowEntry row)
+{
+    // === get shape numeric data from the DB row
+    // shape ID
+    const unsigned shapeId = GeoModelHelpers::variantHelper::getFromVariant_Int(row[0], "EllipticalTube:shapeID");
+    // shape volume
+    const double shapeVolume = GeoModelHelpers::variantHelper::getFromVariant_Double(row[1], "EllipticalTube:shapeVolume");
+    // shape parameters
+    const double XHalfLength = GeoModelHelpers::variantHelper::getFromVariant_Double(row[2], "EllipticalTube:XHalfLength");
+    const double YHalfLength = GeoModelHelpers::variantHelper::getFromVariant_Double(row[3], "EllipticalTube:YHalfLength");
+    const double ZHalfLength = GeoModelHelpers::variantHelper::getFromVariant_Double(row[4], "EllipticalTube:ZHalfLength");
+
+    GeoEllipticalTube *shape = new GeoEllipticalTube(XHalfLength, YHalfLength, ZHalfLength);
+
+    storeBuiltShape(shapeId, shape);
+
+    // return shape;
+    return;
+}
diff --git a/GeoModelIO/GeoModelRead/src/BuildGeoShapes_EllipticalTube.h b/GeoModelIO/GeoModelRead/src/BuildGeoShapes_EllipticalTube.h
new file mode 100644
index 000000000..39491304b
--- /dev/null
+++ b/GeoModelIO/GeoModelRead/src/BuildGeoShapes_EllipticalTube.h
@@ -0,0 +1,28 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+/*
+ * BuildGeoShapes_EllipticalTube.h
+ *
+ * Created on: Oct 3, 2024
+ * Author: Riccardo Maria BIANCHI <riccardo.maria.bianchi@cern.ch>
+ *
+ */
+
+#ifndef GEOMODELREAD_BUILDGEOSHAPES_EllipticalTube_H
+#define GEOMODELREAD_BUILDGEOSHAPES_EllipticalTube_H
+
+#include "BuildGeoShapes.h"
+
+#include <vector>
+#include <variant>
+
+class BuildGeoShapes_EllipticalTube : public BuildGeoShapes
+{
+public:
+  BuildGeoShapes_EllipticalTube(const unsigned size):BuildGeoShapes("EllipticalTube", size){};
+  void buildShape(const DBRowEntry row) override;
+};
+
+#endif
diff --git a/GeoModelIO/GeoModelRead/src/ReadGeoModel.cpp b/GeoModelIO/GeoModelRead/src/ReadGeoModel.cpp
index c380fa008..9411ab234 100644
--- a/GeoModelIO/GeoModelRead/src/ReadGeoModel.cpp
+++ b/GeoModelIO/GeoModelRead/src/ReadGeoModel.cpp
@@ -25,10 +25,13 @@
  *              https://gitlab.cern.ch/GeoModelDev/GeoModel/-/issues/39
  *  - May 2024, R.M.Bianchi <riccardo.maria.bianchi@cern.ch>
  *              Major re-write: moved to the new DB schema based on numeric data
+ *  - Oct 2024 - Riccardo Maria Bianchi, <riccardo.maria.bianchi@cern.ch>,                                  *              Support for the EllipticalTube shape was added. 
+ *
  */
 
 // local includes
 #include "BuildGeoShapes_Box.h"
+#include "BuildGeoShapes_EllipticalTube.h"
 #include "BuildGeoShapes_Tube.h"
 #include "BuildGeoShapes_Cons.h"
 #include "BuildGeoShapes_Para.h"
@@ -67,6 +70,7 @@
 
 // GeoModel shapes
 #include "GeoModelKernel/GeoBox.h"
+#include "GeoModelKernel/GeoEllipticalTube.h"
 #include "GeoModelKernel/GeoCons.h"
 #include "GeoModelKernel/GeoGenericTrap.h"
 #include "GeoModelKernel/GeoPara.h"
@@ -184,9 +188,6 @@ ReadGeoModel::ReadGeoModel(GMDBManager* db, unsigned long* progress)
     m_dbManager->loadGeoNodeTypesAndBuildCache();
     m_dbManager->createTableDataCaches();
 
-    // prepare builders
-    // m_builderShape_Box = std::make_unique<BuildGeoShapes_Box>();
-
     // Check if the user asked for running in serial or multi-threading mode
     if ("" != GeoStrUtils::getEnvVar("GEOMODEL_ENV_IO_NTHREADS")) {
         int nThreads = std::stoi(GeoStrUtils::getEnvVar("GEOMODEL_ENV_IO_NTHREADS"));
@@ -227,6 +228,7 @@ ReadGeoModel::ReadGeoModel(GMDBManager* db, unsigned long* progress)
 
 ReadGeoModel::~ReadGeoModel() {
     delete m_builderShape_Box;
+    delete m_builderShape_EllipticalTube;
     delete m_builderShape_Tube;
     delete m_builderShape_Pcon;
     delete m_builderShape_Cons;
@@ -240,6 +242,7 @@ ReadGeoModel::~ReadGeoModel() {
     delete m_builderShape_GenericTrap;
     delete m_builderShape_UnidentifiedShape;
     m_builderShape_Box = nullptr;
+    m_builderShape_EllipticalTube = nullptr;
     m_builderShape_Tube = nullptr;
     m_builderShape_Pcon = nullptr;
     m_builderShape_Cons = nullptr;
@@ -309,6 +312,7 @@ void ReadGeoModel::loadDB() {
 
     // shapes from the new DB schema
     m_shapes_Box = m_dbManager->getTableFromNodeType_VecVecData("GeoBox");
+    m_shapes_EllipticalTube = m_dbManager->getTableFromNodeType_VecVecData("GeoEllipticalTube");
     m_shapes_Tube = m_dbManager->getTableFromNodeType_VecVecData("GeoTube");
     m_shapes_Cons = m_dbManager->getTableFromNodeType_VecVecData("GeoCons");
     m_shapes_Para = m_dbManager->getTableFromNodeType_VecVecData("GeoPara");
@@ -397,6 +401,7 @@ GeoVPhysVol* ReadGeoModel::buildGeoModelPrivate() {
         std::thread t26(&ReadGeoModel::buildAllShapes_UnidentifiedShape, this);
         std::thread t27(&ReadGeoModel::buildAllShapes_Torus, this);
         std::thread t28(&ReadGeoModel::buildAllShapes_GenericTrap, this);
+        std::thread t29(&ReadGeoModel::buildAllShapes_EllipticalTube, this);
         
 
         t2.join();  // ok, all Elements have been built
@@ -418,6 +423,7 @@ GeoVPhysVol* ReadGeoModel::buildGeoModelPrivate() {
         t26.join();  // ok, all Shapes-UnidentifiedShape have been built
         t27.join();  // ok, all Shapes-Torus have been built
         t28.join();  // ok, all Shapes-GenericTrap have been built
+        t29.join();  // ok, all Shapes-EllipticalTube have been built
 
 	    // Build boolean shapes and shape operators,
         // this needs Shapes to be built
@@ -459,6 +465,7 @@ GeoVPhysVol* ReadGeoModel::buildGeoModelPrivate() {
         buildAllNameTags();
         // buildAllShapes();
         buildAllShapes_Box();
+        buildAllShapes_EllipticalTube();
         buildAllShapes_Tube();
         buildAllShapes_Pcon();
         buildAllShapes_Pgon();
@@ -636,6 +643,29 @@ void ReadGeoModel::buildAllShapes_Box()
         std::cout << "All " << nSize << " Shapes-Box have been built!\n";
     }
 }
+//! Iterate over the list of GeoEllipticalTube shape nodes, build them all, 
+//! and store their pointers
+void ReadGeoModel::buildAllShapes_EllipticalTube()
+{
+    if (m_loglevel >= 1) {
+        std::cout << "Building all shapes -- EllipticalTube ...\n";
+    }
+
+    // create a builder and reserve size of memory map
+    size_t nSize = m_shapes_EllipticalTube.size();
+    m_builderShape_EllipticalTube = new BuildGeoShapes_EllipticalTube(nSize);
+
+    // loop over the DB rows and build the shapes
+    for (const auto &row : m_shapes_EllipticalTube)
+    {
+        // GeoModelIO::CppHelper::printStdVectorVariants(row); // DEBUG MSG
+        m_builderShape_EllipticalTube->buildShape(row);
+    }
+    // m_builderShape_EllipticalTube->printBuiltShapes(); // DEBUG MSG
+    if (nSize > 0) {
+        std::cout << "All " << nSize << " Shapes-EllipticalTube have been built!\n";
+    }
+}
 //! Iterate over the list of GeoTube shape nodes, build them all, 
 //! and store their pointers
 void ReadGeoModel::buildAllShapes_Tube()
@@ -3210,7 +3240,7 @@ return (!(m_memMapShapes_Union.find(id) == m_memMapShapes_Union.end()));
 }
 // --- methods for caching GeoShape nodes ---
 bool ReadGeoModel::isBuiltShape(std::string_view shapeType, const unsigned shapeId) {
-const std::set<std::string> shapesNewDB{"Box", "Tube", "Pcon", "Cons", "Para", "Pgon", "Trap", "Trd", "Tubs", "Torus", "TwistedTrap", "SimplePolygonBrep", "GenericTrap", "Shift", "Subtraction", "Intersection", "Union"};
+const std::set<std::string> shapesNewDB{"Box", "EllipticalTube", "Tube", "Pcon", "Cons", "Para", "Pgon", "Trap", "Trd", "Tubs", "Torus", "TwistedTrap", "SimplePolygonBrep", "GenericTrap", "Shift", "Subtraction", "Intersection", "Union"};
     // get shape parameters
     if (std::count(shapesNewDB.begin(), shapesNewDB.end(), shapeType))
     {
@@ -3218,6 +3248,10 @@ const std::set<std::string> shapesNewDB{"Box", "Tube", "Pcon", "Cons", "Para", "
         {
             return m_builderShape_Box->isBuiltShape(shapeId);
         }
+        else if ("EllipticalTube" == shapeType)
+        {
+            return m_builderShape_EllipticalTube->isBuiltShape(shapeId);
+        }
         else if ("Tube" == shapeType)
         {
             return m_builderShape_Tube->isBuiltShape(shapeId);
@@ -3321,7 +3355,7 @@ void ReadGeoModel::storeBuiltShapeOperators_Union(const unsigned id, GeoShape* n
 GeoShape *ReadGeoModel::getBuiltShape(const unsigned shapeId, std::string_view shapeType)
 {
 
-    const std::set<std::string> shapesNewDB{"Box", "Tube", "Pcon", "Cons", "Para", "Pgon", "Trap", "Trd", "Tubs", "Torus", "TwistedTrap", "SimplePolygonBrep", "GenericTrap", "Shift", "Intersection", "Subtraction", "Union", "UnidentifiedShape"};
+    const std::set<std::string> shapesNewDB{"Box", "EllipticalTube", "Tube", "Pcon", "Cons", "Para", "Pgon", "Trap", "Trd", "Tubs", "Torus", "TwistedTrap", "SimplePolygonBrep", "GenericTrap", "Shift", "Intersection", "Subtraction", "Union", "UnidentifiedShape"};
     // get shape parameters
     if (std::count(shapesNewDB.begin(), shapesNewDB.end(), shapeType))
     {
@@ -3329,6 +3363,10 @@ GeoShape *ReadGeoModel::getBuiltShape(const unsigned shapeId, std::string_view s
         {
             return m_builderShape_Box->getBuiltShape(shapeId);
         }
+        else if ("EllipticalTube" == shapeType)
+        {
+            return m_builderShape_EllipticalTube->getBuiltShape(shapeId);
+        }
         else if ("Tube" == shapeType)
         {
             return m_builderShape_Tube->getBuiltShape(shapeId);
diff --git a/GeoModelIO/GeoModelWrite/GeoModelWrite/WriteGeoModel.h b/GeoModelIO/GeoModelWrite/GeoModelWrite/WriteGeoModel.h
index 71aff6afc..66d5d1be9 100644
--- a/GeoModelIO/GeoModelWrite/GeoModelWrite/WriteGeoModel.h
+++ b/GeoModelIO/GeoModelWrite/GeoModelWrite/WriteGeoModel.h
@@ -20,6 +20,11 @@
  *              Also added methods to get the numbers of visited nodes.
  * - Feb 2023 - R.M.Bianchi <riccardo.maria.bianchi@cern.ch>
  *              Added 'setLoglevel' method, to steer output messages
+// - May 2024 - Riccardo Maria Bianchi, <riccardo.maria.bianchi@cern.ch>,
+//              Major change: we move to the new DB schema that uses numerical data
+//              instead of strings/TEXT
+// - Oct 2024 - Riccardo Maria Bianchi, <riccardo.maria.bianchi@cern.ch>,
+//              Support for the EllipticalTube shape was added.
  */
 
 #ifndef GeoModelWrite_WriteGeoModel_H
@@ -152,6 +157,7 @@ class WriteGeoModel : public GeoNodeAction {
     };
     unsigned long getNShapes() { return m_shapes.size(); }; // TODO: to be removed later
     unsigned long getNShapes_Box() { return m_shapes_Box.size(); };
+    unsigned long getNShapes_EllipticalTube() { return m_shapes_EllipticalTube.size(); };
     unsigned long getNShapes_Cons() { return m_shapes_Cons.size(); };
     unsigned long getNShapes_Para() { return m_shapes_Para.size(); };
     unsigned long getNShapes_Pcon() { return m_shapes_Pcon.size(); };
@@ -390,6 +396,7 @@ class WriteGeoModel : public GeoNodeAction {
     DBRowsList m_materials_Data;
 
     DBRowsList m_shapes_Box;
+    DBRowsList m_shapes_EllipticalTube;
     DBRowsList m_shapes_Tube;
     DBRowsList m_shapes_Cons;
     DBRowsList m_shapes_Para;
diff --git a/GeoModelIO/GeoModelWrite/src/WriteGeoModel.cpp b/GeoModelIO/GeoModelWrite/src/WriteGeoModel.cpp
index 87732d22d..1220c2662 100644
--- a/GeoModelIO/GeoModelWrite/src/WriteGeoModel.cpp
+++ b/GeoModelIO/GeoModelWrite/src/WriteGeoModel.cpp
@@ -21,8 +21,11 @@
 // - Nov 2023 - R.M.Bianchi <riccardo.maria.bianchi@cern.ch>
 //              Updated to use the AlignableTransform 'default position', 
 //              which does not include alignment constants
-// - May 2024 - Major change: we move to the new DB schema that uses numerical data
+// - May 2024 - Riccardo Maria Bianchi, <riccardo.maria.bianchi@cern.ch>,
+//              Major change: we move to the new DB schema that uses numerical data
 //              instead of strings/TEXT
+// - Oct 2024 - Riccardo Maria Bianchi, <riccardo.maria.bianchi@cern.ch>,
+//              Support for the EllipticalTube shape was added.
 //
 
 // local includes
@@ -39,6 +42,7 @@
 #include "GeoModelKernel/GeoNodePath.h"
 // GeoModelKernel shapes
 #include "GeoModelKernel/GeoBox.h"
+#include "GeoModelKernel/GeoEllipticalTube.h"
 #include "GeoModelKernel/GeoCons.h"
 #include "GeoModelKernel/GeoGenericTrap.h"
 #include "GeoModelKernel/GeoPara.h"
@@ -700,7 +704,7 @@ std::pair<std::string, unsigned> WriteGeoModel::storeShape(const GeoShape* shape
     // LArCustomShape is deprecated.  Write it out as a GeoUnidentifiedShape;
     if (shapeType == "CustomShape") shapeType = "UnidentifiedShape";
 
-    const std::set<std::string> shapesNewDB{"Box", "Tube", "Cons", "Para", "Trap", "Trd", "Tubs", "Torus", "TwistedTrap", "Pcon", "Pgon", "SimplePolygonBrep", "GenericTrap", "Intersection", "Shift", "Subtraction", "Union", "UnidentifiedShape"};
+    const std::set<std::string> shapesNewDB{"Box", "EllipticalTube", "Tube", "Cons", "Para", "Trap", "Trd", "Tubs", "Torus", "TwistedTrap", "Pcon", "Pgon", "SimplePolygonBrep", "GenericTrap", "Intersection", "Shift", "Subtraction", "Union", "UnidentifiedShape"};
 
     // get shape parameters
     if (shapesNewDB.count(shapeType))
@@ -1000,6 +1004,13 @@ WriteGeoModel::getShapeParametersV(const GeoShape *shape, const bool data)
         shapePars.push_back(box->getYHalfLength());
         shapePars.push_back(box->getZHalfLength());
     }
+    else if ("EllipticalTube" == shapeType)
+    {
+        const GeoEllipticalTube *box = dynamic_cast<const GeoEllipticalTube *>(shape);
+        shapePars.push_back(box->getXHalfLength());
+        shapePars.push_back(box->getYHalfLength());
+        shapePars.push_back(box->getZHalfLength());
+    }
     else if ("Tube" == shapeType)
     {
         const GeoTube *tube = dynamic_cast<const GeoTube *>(shape);
@@ -1964,6 +1975,10 @@ unsigned int WriteGeoModel::addShape(const std::string &type,
     {
         container = &m_shapes_Box;
     }
+    else if ("EllipticalTube" == type)
+    {
+        container = &m_shapes_EllipticalTube;
+    }
     else if ("Tube" == type)
     {
         container = &m_shapes_Tube;
@@ -2155,6 +2170,7 @@ void WriteGeoModel::saveToDB(std::vector<GeoPublisher*>& publishers) {
 
     m_dbManager->addListOfRecords("GeoShape", m_shapes); // OLD version, with shape's parameters as strings
     m_dbManager->addListOfRecords("GeoBox", m_shapes_Box); // new version, with shape's parameters as numbers
+    m_dbManager->addListOfRecords("GeoEllipticalTube", m_shapes_EllipticalTube); // new version, with shape's parameters as numbers
     m_dbManager->addListOfRecords("GeoTube", m_shapes_Tube); // new version, with shape's parameters as numbers
     m_dbManager->addListOfRecords("GeoCons", m_shapes_Cons); // new version, with shape's parameters as numbers
     m_dbManager->addListOfRecords("GeoPara", m_shapes_Para); // new version, with shape's parameters as numbers
-- 
GitLab