From 9eda807f7510e2457c2b563bfa116768505f69cc Mon Sep 17 00:00:00 2001
From: Riccardo Maria Bianchi <riccardo.maria.bianchi@cern.ch>
Date: Thu, 13 Jun 2024 01:07:00 +0200
Subject: [PATCH] Move Elements and Materials tables to the new DB schema,
 adapt I/O, add safety checks on DB

---
 .../GeoModelHelpers/variantHelpers.h          |   6 +-
 ...ep1_create_store_geo_and_publish_nodes.cpp |   7 +
 .../step2_read_geo_and_published_nodes.cpp    |   4 +-
 .../GeoModelDBManager/GMDBManager.h           |   9 +-
 .../GeoModelDBManager/src/GMDBManager.cpp     | 107 ++++--
 .../GeoModelIOHelpers/GMIO.h                  |  25 +-
 .../GeoModelRead/GeoModelRead/ReadGeoModel.h  |  20 +-
 .../GeoModelRead/ReadGeoModel.tpp             |   4 +-
 GeoModelIO/GeoModelRead/src/ReadGeoModel.cpp  | 363 +++++++++++++++---
 .../GeoModelWrite/WriteGeoModel.h             |  26 +-
 .../GeoModelWrite/src/WriteGeoModel.cpp       | 118 ++++--
 11 files changed, 507 insertions(+), 182 deletions(-)

diff --git a/GeoModelCore/GeoModelHelpers/GeoModelHelpers/variantHelpers.h b/GeoModelCore/GeoModelHelpers/GeoModelHelpers/variantHelpers.h
index 325994b64..6c298fb15 100644
--- a/GeoModelCore/GeoModelHelpers/GeoModelHelpers/variantHelpers.h
+++ b/GeoModelCore/GeoModelHelpers/GeoModelHelpers/variantHelpers.h
@@ -55,7 +55,7 @@ namespace GeoModelHelpers {
             }
             catch (std::bad_variant_access const &ex)
             {
-                std::cout << ex.what() << ": '" << logMsg << "'  is not a 'string'!\n";
+                std::cout << ex.what() << ": '" << logMsg << "'  is not a 'string'! " << getFromVariant_Type(record) << "\n";
             }
             return ret;
         }
@@ -70,7 +70,7 @@ namespace GeoModelHelpers {
             }
             catch (std::bad_variant_access const &ex)
             {
-                std::cout << ex.what() << ": '" << logMsg << "'  is not a '" << type << "'!\n";
+                std::cout << ex.what() << ": '" << logMsg << "'  is not a '" << type << "'! " << getFromVariant_Type(record) << "\n";
             }
             return ret;
         }
@@ -84,7 +84,7 @@ namespace GeoModelHelpers {
             }
             catch (std::bad_variant_access const &ex)
             {
-                std::cout << ex.what() << ": '" << logMsg << "'  is not a '" << type << "'!\n";
+                std::cout << ex.what() << ": '" << logMsg << "'  is not a '" << type << "'! " << getFromVariant_Type(record) << "\n";
             }
             return ret;
         }
diff --git a/GeoModelExamples/HelloToy/step1_create_store_geo_and_publish_nodes.cpp b/GeoModelExamples/HelloToy/step1_create_store_geo_and_publish_nodes.cpp
index f99792ae8..26af27678 100644
--- a/GeoModelExamples/HelloToy/step1_create_store_geo_and_publish_nodes.cpp
+++ b/GeoModelExamples/HelloToy/step1_create_store_geo_and_publish_nodes.cpp
@@ -367,6 +367,13 @@ int main(int argc, char *argv[])
   toyPhys->add(nSimplePolygonBrep);
   toyPhys->add(pSimplePolygonBrep);
 
+  // Add a test *shared* GeoSimplePolygonBrep shape
+  GeoLogVol *lSimplePolygonBrep2 = new GeoLogVol("SimplePolygonBrepShared", sSimplePolygonBrep, steel);
+  GeoPhysVol *pSimplePolygonBrep2 = new GeoPhysVol(lSimplePolygonBrep2);
+  GeoNameTag *nSimplePolygonBrep2 = new GeoNameTag("Shape-SimplePolygonBrepShared");
+  toyPhys->add(nSimplePolygonBrep2);
+  toyPhys->add(pSimplePolygonBrep2);
+
   // Add a test GeoShift boolean shape:
   // a shift of a box
   GeoShapeShift* sShift = new GeoShapeShift(sPass, GeoTrf::TranslateZ3D(50*SYSTEM_OF_UNITS::cm));
diff --git a/GeoModelExamples/HelloToy/step2_read_geo_and_published_nodes.cpp b/GeoModelExamples/HelloToy/step2_read_geo_and_published_nodes.cpp
index e9baf8e5d..654d70c3d 100644
--- a/GeoModelExamples/HelloToy/step2_read_geo_and_published_nodes.cpp
+++ b/GeoModelExamples/HelloToy/step2_read_geo_and_published_nodes.cpp
@@ -154,9 +154,9 @@ int main(int argc, char *argv[])
   std::map<std::string, GeoAlignableTransform*> mapAXF = readInGeo.getPublishedNodes<std::string, GeoAlignableTransform*>("HelloToyExample");
 
   //check for a table we know doesn't exist
-  if (db->checkTable("PublishedFullPhysVols_HelloToyExample")) std::cout<<"We find the table that we expected - good!"<<std::endl;
+  if (db->checkTableFromDB("PublishedFullPhysVols_HelloToyExample")) std::cout<<"We find the table that we expected - good!"<<std::endl;
   else std::cout<<"Uh oh, we don't find the expected table - bad!"<<std::endl;
-  if(!db->checkTable("PublishedFullPhysVols_ByeByeToyExample")) std::cout<<"We don't find the table that we didn't expect - good!"<<std::endl;
+  if(!db->checkTableFromDB("PublishedFullPhysVols_ByeByeToyExample")) std::cout<<"We don't find the table that we didn't expect - good!"<<std::endl;
   else std::cout<<"Uh oh, we found a table which doesn't exist - bad!"<<std::endl;  
 
   //Now test via the specific accessors with additional checks
diff --git a/GeoModelIO/GeoModelDBManager/GeoModelDBManager/GMDBManager.h b/GeoModelIO/GeoModelDBManager/GeoModelDBManager/GMDBManager.h
index c08cd5f0e..10c7091a3 100644
--- a/GeoModelIO/GeoModelDBManager/GeoModelDBManager/GMDBManager.h
+++ b/GeoModelIO/GeoModelDBManager/GeoModelDBManager/GMDBManager.h
@@ -292,12 +292,12 @@ class GMDBManager {
 
     std::vector<std::vector<std::string>> getTableFromNodeType_String(
         std::string nodeType);
-    std::vector<std::vector<std::variant<int, long, float, double, std::string>>> getTableFromNodeType_VecVecData(
+    DBRowsList getTableFromNodeType_VecVecData(
         std::string nodeType);
     
-    std::vector<std::variant<int, long, float, double, std::string>> getTableFromTableName_VecData(
+    DBRowEntry getTableFromTableName_VecData(
         std::string tableName);
-    std::vector<std::vector<std::variant<int, long, float, double, std::string>>> getTableFromTableName_VecVecData(
+    DBRowsList getTableFromTableName_VecVecData(
         std::string tableName);
     // specializations
     std::vector<double> getTableFromTableName_VectorDouble(std::string tableName);
@@ -312,7 +312,7 @@ class GMDBManager {
 
     //! Test if a given table exists
     //! This requires the *full* table name (i.e. prefix_suffix)
-    bool checkTable(std::string tableName) const;
+    bool checkTableFromDB(std::string tableName) const;
 
     //! Test if a table has been loaded from a DB, that is it exists in the cache
     bool checkTableFromCache(const std::string_view tableName) const;
@@ -382,6 +382,7 @@ class GMDBManager {
     void storeTableColumnNames(std::vector<std::string> input);
 
     std::vector<std::string> getTableColumnNames(const std::string &tableName);
+    bool hasTableBeenCreatedInDB(const std::string_view tableName);
 
     int getTableColIndex(const std::string &tableName,
                          const std::string &colName);
diff --git a/GeoModelIO/GeoModelDBManager/src/GMDBManager.cpp b/GeoModelIO/GeoModelDBManager/src/GMDBManager.cpp
index cda6e99cd..ecdb197df 100644
--- a/GeoModelIO/GeoModelDBManager/src/GMDBManager.cpp
+++ b/GeoModelIO/GeoModelDBManager/src/GMDBManager.cpp
@@ -64,7 +64,7 @@ class GMDBManager::Imp {
     sqlite3_stmt* selectAllFromTableSortBy(const std::string_view tableName,
                                            const std::string_view sortColumn = "") const;
     sqlite3_stmt* selectAllFromTableChildrenPositions() const;
-    bool checkTable_imp(std::string tableName) const;
+    bool checkTableFromDB_imp(std::string tableName) const;
 };
 
 GMDBManager::GMDBManager(const std::string& path)
@@ -547,7 +547,8 @@ std::vector<std::vector<std::string>> GMDBManager::getTableFromNodeType_String(
     }
     else
     {
-        if (!checkTable(tableName))
+        // if (!checkTable(tableName))
+        if (!checkTableFromCache(tableName))
         {
             THROW_EXCEPTION("ERROR!!! Table name '" + tableName + "' does not exist in cache! (It has not been loaded from the DB)");
         }
@@ -556,10 +557,10 @@ std::vector<std::vector<std::string>> GMDBManager::getTableFromNodeType_String(
     return out;
 }
 
-std::vector<std::vector<std::variant<int, long, float, double, std::string>>> GMDBManager::getTableFromNodeType_VecVecData(
+DBRowsList GMDBManager::getTableFromNodeType_VecVecData(
     std::string nodeType)
 {
-    std::vector<std::vector<std::variant<int, long, float, double, std::string>>> out;
+    DBRowsList out;
     std::string tableName = getTableNameFromNodeType(nodeType);
     
     if (tableName.empty())
@@ -616,10 +617,10 @@ DBRowsList GMDBManager::getTableFromTableName_VecVecData(
     }
     return out;
 }
-std::vector<std::variant<int, long, float, double, std::string>> GMDBManager::getTableFromTableName_VecData(
+DBRowEntry GMDBManager::getTableFromTableName_VecData(
     std::string tableName)
 {
-    std::vector<std::variant<int, long, float, double, std::string>> out;
+    DBRowEntry out;
     if (tableName.empty())
     {
         std::mutex coutMutex;
@@ -797,6 +798,10 @@ bool GMDBManager::addListOfRecordsToTable(
     const std::string tableName,
     const std::vector<std::vector<std::string>> records) {
     
+    if ( !(hasTableBeenCreatedInDB(tableName)) ) {
+        THROW_EXCEPTION("ERROR!!! The DB has no '" << tableName << "' table; probably, the table has not been created in the DB.");
+    }
+
     // get table columns and format them for query
     std::string tableColString =
         "(" + GeoModelIO::CppHelper::joinVectorStrings(m_tableNames.at(tableName), ", ") + ")";
@@ -842,6 +847,11 @@ bool GMDBManager::addListOfRecordsToTable(
     const std::vector<
         std::vector<std::variant<int, long, float, double, std::string>>>
         records) {
+
+    if ( !(hasTableBeenCreatedInDB(tableName)) ) {
+        THROW_EXCEPTION("ERROR!!! The DB has no '" << tableName << "' table; probably, the table has not been created in the DB.");
+    }
+
     if (records.size() > 0) {
     // get table columns and format them for query
     std::string tableColString =
@@ -1261,7 +1271,7 @@ sqlite3_stmt* GMDBManager::Imp::selectAllFromTableSortBy(
     return st;
 }
 
-bool GMDBManager::Imp::checkTable_imp(std::string tableName) const {
+bool GMDBManager::Imp::checkTableFromDB_imp(std::string tableName) const {
     theManager->checkIsDBOpen();
     sqlite3_stmt* st = nullptr;  // SQLite statement to be returned
     int rc = -1;                 // SQLite return code
@@ -1299,6 +1309,7 @@ bool GMDBManager::initDB() {
     return tablesOK;
 }
 
+
 bool GMDBManager::checkTableFromCache(const std::string_view tableName) const
 {
     std::string tableNameStr{tableName};
@@ -1424,8 +1435,8 @@ DBRowsList GMDBManager::getPublishedAXFTable(
     return getTableRecords_VecVecData(tableName);
 }
 
-bool GMDBManager::checkTable(std::string tableName) const {
-    return m_d->checkTable_imp(tableName);
+bool GMDBManager::checkTableFromDB(std::string tableName) const {
+    return m_d->checkTableFromDB_imp(tableName);
 }
 
 // create a user-defined custom table to store the published nodes
@@ -1762,18 +1773,33 @@ bool GMDBManager::createTables() {
     tab.push_back("id");
     tab.push_back("name");
     tab.push_back("density");
-    tab.push_back("elements");
+    tab.push_back("dataStart");
+    tab.push_back("dataEnd");
     storeTableColumnNames(tab);
     queryStr = fmt::format(
-        "create table {0}({1} integer primary key, {2} varchar, {3} "
-        "varchar, "
-        "{4} varchar)",
-        tab[0], tab[1], tab[2], tab[3], tab[4]);
+        "create table {0}({1} integer primary key, {2} varchar, "
+        "{3} real, {4} integer, {5} integer)",
+        tab[0], tab[1], tab[2], tab[3], tab[4], tab[5]);
     if (0 == (rc = execQuery(queryStr))) {
         storeNodeType(geoNode, tableName);
     }
     tab.clear();
 
+    // create a table to store the numeric data used in GeoPcon shapes
+    tableName = "Materials_Data";
+    tab.push_back(tableName);
+    tab.push_back("id");
+    tab.push_back("element");
+    tab.push_back("fraction");
+    storeTableColumnNames(tab);
+    queryStr = fmt::format(
+        "create table {0}({1} integer primary key, "
+        "{2} integer not null REFERENCES Elements(id), "
+        "{3} real )",
+        tab[0], tab[1], tab[2], tab[3]);
+    rc = execQuery(queryStr);
+    tab.clear();
+
     // Elements table
     geoNode = "GeoElement";
     tableName = "Elements";
@@ -1786,32 +1812,33 @@ bool GMDBManager::createTables() {
     tab.push_back("A");
     storeTableColumnNames(tab);
     queryStr = fmt::format(
-        "create table {0}({1} integer primary key, {2} varchar, {3} "
-        "varchar, "
-        "{4} integer, {5} real)",
+        "create table {0}({1} integer primary key, {2} varchar, "
+        "{3} varchar, "
+        "{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 table
-    // geoNode = "GeoShape";
-    // tableName = "Shapes";
-    // m_childType_tableName[geoNode] = tableName;
-    // tab.push_back(tableName);
-    // tab.push_back("id");
-    // tab.push_back("type");
-    // tab.push_back("parameters");
-    // storeTableColumnNames(tab);
-    // queryStr = fmt::format(
-    //     "create table {0}({1} integer primary key, {2} varchar, {3} "
-    //     "varchar)",
-    //     tab[0], tab[1], tab[2], tab[3]);
-    // if (0 == (rc = execQuery(queryStr))) {
-    //     storeNodeType(geoNode, tableName);
-    // }
-    // tab.clear();
+    // TODO: to be removed as soson as all shapes are migrated to the new DB schema
+    // Shapes table
+    geoNode = "GeoShape";
+    tableName = "Shapes";
+    m_childType_tableName[geoNode] = tableName;
+    tab.push_back(tableName);
+    tab.push_back("id");
+    tab.push_back("type");
+    tab.push_back("parameters");
+    storeTableColumnNames(tab);
+    queryStr = fmt::format(
+        "create table {0}({1} integer primary key, {2} varchar, {3} "
+        "varchar)",
+        tab[0], tab[1], tab[2], tab[3]);
+    if (0 == (rc = execQuery(queryStr))) {
+        storeNodeType(geoNode, tableName);
+    }
+    tab.clear();
 
     // Shapes-Box table
     // ID, XHalfLength, YHalfLength, ZHalfLength
@@ -2363,6 +2390,12 @@ std::vector<std::string> GMDBManager::getTableColumnNames(
     return m_tableNames.at(tableName);
 }
 
+bool GMDBManager::hasTableBeenCreatedInDB(const std::string_view tableName) {
+    if (m_tableNames.count(std::string(tableName)) == 0) return false;
+    return true;
+}
+
+
 void GMDBManager::storeNodeType(std::string nodeType, std::string tableName) {
     checkIsDBOpen();
     std::string queryStr;
@@ -2479,7 +2512,7 @@ bool GMDBManager::storeRootVolume(const unsigned &id,
 
 // std::vector<std::string> GMDBManager::getRootPhysVol() {
 std::pair<unsigned, unsigned> GMDBManager::getRootPhysVol() {
-    // get the ID of the ROOT vol from the table "RootVolume"
+    // get the type and ID of the ROOT vol from the table "RootVolume"
     sqlite3_stmt* stmt = m_d->selectAllFromTable("RootVolume");
     // declare the data we want to fetch
     unsigned int id = 0;
@@ -2487,7 +2520,9 @@ std::pair<unsigned, unsigned> GMDBManager::getRootPhysVol() {
     // execute the statement on all rows
     int rc = -1;
     while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
-        // get the data
+        // get the data;
+        // we skip the SQLite record ID stored in column 0, 
+        // because we only have one Root volume
         id = sqlite3_column_int(stmt, 1);
         typeId = sqlite3_column_int(stmt, 2);
         // TODO: fill a cache
diff --git a/GeoModelIO/GeoModelIOHelpers/GeoModelIOHelpers/GMIO.h b/GeoModelIO/GeoModelIOHelpers/GeoModelIOHelpers/GMIO.h
index 0e4b211b5..9641055f9 100644
--- a/GeoModelIO/GeoModelIOHelpers/GeoModelIOHelpers/GMIO.h
+++ b/GeoModelIO/GeoModelIOHelpers/GeoModelIOHelpers/GMIO.h
@@ -153,21 +153,16 @@ class IO {
         // map to populate and return
         std::map<std::string, unsigned long> mmap;
         // get the number of all nodes in the DB from the DB manager
-        unsigned long nphysvols = db.getTableFromNodeType("GeoPhysVol").size();
-        unsigned long nfullphysvols =
-            db.getTableFromNodeType("GeoFullPhysVol").size();
-        unsigned long nlogvols = db.getTableFromNodeType("GeoLogVol").size();
-        unsigned long nelements = db.getTableFromNodeType("GeoElement").size();
-        unsigned long nmaterials =
-            db.getTableFromNodeType("GeoMaterial").size();
-        unsigned long nalignables =
-            db.getTableFromNodeType("GeoAlignableTransform").size();
-        unsigned long nfunctions = db.getTableFromNodeType("Function").size();
-        unsigned long nserialtransformers =
-            db.getTableFromNodeType("GeoSerialTransformer").size();
-        unsigned long nshapes = db.getTableFromNodeType("GeoShape").size();
-        unsigned long nserialdenominators =
-            db.getTableFromNodeType("GeoSerialDenominator").size();
+        unsigned long nphysvols = db.getTableFromNodeType_String("GeoPhysVol").size();
+        unsigned long nfullphysvols = db.getTableFromNodeType_String("GeoFullPhysVol").size();
+        unsigned long nlogvols = db.getTableFromNodeType_VecVecData("GeoLogVol").size();
+        unsigned long nelements = db.getTableFromNodeType_VecVecData("GeoElement").size();
+        unsigned long nmaterials = db.getTableFromNodeType_VecVecData("GeoMaterial").size();
+        unsigned long nalignables = db.getTableFromNodeType_String("GeoAlignableTransform").size();
+        unsigned long nfunctions = db.getTableFromNodeType_VecVecData("Function").size();
+        unsigned long nserialtransformers = db.getTableFromNodeType_String("GeoSerialTransformer").size();
+        unsigned long nshapes = db.getTableFromNodeType_String("GeoShape").size();
+        unsigned long nserialdenominators = db.getTableFromNodeType_String("GeoSerialDenominator").size();
         // get metadata
         unsigned long nchildrenconnections = db.getChildrenTable().size();
 
diff --git a/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.h b/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.h
index 993198e63..b1cf2ffe9 100644
--- a/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.h
+++ b/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.h
@@ -212,10 +212,13 @@ class ReadGeoModel {
 
     GeoBox* buildDummyShape();
 
-    void loopOverAllChildrenInBunches();
-    void loopOverAllChildrenRecords(
-        std::vector<std::vector<std::string>> records);
+    // void loopOverAllChildrenInBunches_String(); // OLD
+    void loopOverAllChildrenInBunches_VecVecData();
+    // void loopOverAllChildrenRecords(
+    //     std::vector<std::vector<std::string>> records);
+    void loopOverAllChildrenRecords(DBRowsList records);
     void processParentChild(const std::vector<std::string>& parentchild);
+    void processParentChild(const DBRowEntry& parentchild);
 
     GeoVPhysVol* getRootVolume();
 
@@ -398,13 +401,13 @@ class ReadGeoModel {
     std::vector<std::vector<std::string>> m_identifierTags;
     std::vector<std::vector<std::string>> m_serialTransformers;
     std::vector<std::vector<std::string>> m_nameTags;
-    // std::vector<std::vector<std::string>> m_materials;
-    std::vector<std::vector<std::string>> m_elements;
+    std::vector<std::vector<std::string>> m_materials;
+    // std::vector<std::vector<std::string>> m_elements;
     std::vector<std::vector<std::string>> m_shapes;
-    std::vector<std::vector<std::string>> m_allchildren;
 
-    DBRowsList m_materials;
+    DBRowsList m_elements;
     DBRowsList m_logVols;
+    DBRowsList m_allchildren;
 
     // containers to store shapes' parameters
     DBRowsList m_shapes_Box;
@@ -444,7 +447,8 @@ class ReadGeoModel {
         m_tableName_toTableID;  // to look for table ID starting from node's
                                 // type name
 
-    std::vector<std::string> m_root_vol_data;
+    // std::vector<std::string> m_root_vol_data;
+    std::pair<unsigned, unsigned> m_root_vol_data;
 
     //! memory chaches
     std::vector<GeoPhysVol*> m_memMapPhysVols;
diff --git a/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.tpp b/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.tpp
index 1e383e844..006813743 100644
--- a/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.tpp
+++ b/GeoModelIO/GeoModelRead/GeoModelRead/ReadGeoModel.tpp
@@ -36,13 +36,13 @@ namespace GeoModelIO {
 
         if constexpr ( std::is_same_v<GeoFullPhysVol*, N> ) {
             if(doCheckTable){ 
-                bool tableExists = m_dbManager->checkTable("PublishedFullPhysVols_"+publisherName);
+                bool tableExists = m_dbManager->checkTableFromDB("PublishedFullPhysVols_"+publisherName);
                 if(!tableExists) return mapNodes;
             }
             vecRecords = m_dbManager->getPublishedFPVTable( publisherName );
         } else if constexpr ( std::is_same_v<GeoAlignableTransform*, N> ) {
             if(doCheckTable){ 
-                bool tableExists = m_dbManager->checkTable("PublishedAlignableTransforms_"+publisherName);
+                bool tableExists = m_dbManager->checkTableFromDB("PublishedAlignableTransforms_"+publisherName);
                 if(!tableExists) return mapNodes;
             }
             vecRecords = m_dbManager->getPublishedAXFTable( publisherName );
diff --git a/GeoModelIO/GeoModelRead/src/ReadGeoModel.cpp b/GeoModelIO/GeoModelRead/src/ReadGeoModel.cpp
index 605e32432..e846708aa 100644
--- a/GeoModelIO/GeoModelRead/src/ReadGeoModel.cpp
+++ b/GeoModelIO/GeoModelRead/src/ReadGeoModel.cpp
@@ -281,21 +281,24 @@ void ReadGeoModel::loadDB() {
     std::chrono::system_clock::time_point start = std::chrono::system_clock::now();  
     // get all GeoModel nodes from the DB
     // m_shapes = m_dbManager->getTableFromNodeType("GeoShape");
-    m_materials = m_dbManager->getTableFromNodeType("GeoMaterial");
-    m_elements = m_dbManager->getTableFromNodeType("GeoElement");
-    m_physVols = m_dbManager->getTableFromNodeType("GeoPhysVol");
-    m_fullPhysVols = m_dbManager->getTableFromNodeType("GeoFullPhysVol");
-    m_transforms = m_dbManager->getTableFromNodeType("GeoTransform");
+    m_elements = m_dbManager->getTableFromNodeType_VecVecData("GeoElement");
+    GeoModelHelpers::variantHelper::printStdVectorVariants(m_elements[0]);
+    GeoModelHelpers::variantHelper::getFromVariant_Type(m_elements[0][3]);
+
+    m_materials = m_dbManager->getTableFromNodeType_String("GeoMaterial");
+    m_physVols = m_dbManager->getTableFromNodeType_String("GeoPhysVol");
+    m_fullPhysVols = m_dbManager->getTableFromNodeType_String("GeoFullPhysVol");
+    m_transforms = m_dbManager->getTableFromNodeType_String("GeoTransform");
     m_alignableTransforms =
-        m_dbManager->getTableFromNodeType("GeoAlignableTransform");
+        m_dbManager->getTableFromNodeType_String("GeoAlignableTransform");
     m_serialDenominators =
-        m_dbManager->getTableFromNodeType("GeoSerialDenominator");
+        m_dbManager->getTableFromNodeType_String("GeoSerialDenominator");
     m_serialIdentifiers =
-        m_dbManager->getTableFromNodeType("GeoSerialIdentifier");
-    m_identifierTags = m_dbManager->getTableFromNodeType("GeoIdentifierTag");
+        m_dbManager->getTableFromNodeType_String("GeoSerialIdentifier");
+    m_identifierTags = m_dbManager->getTableFromNodeType_String("GeoIdentifierTag");
     m_serialTransformers =
-        m_dbManager->getTableFromNodeType("GeoSerialTransformer");
-    m_nameTags = m_dbManager->getTableFromNodeType("GeoNameTag");
+        m_dbManager->getTableFromNodeType_String("GeoSerialTransformer");
+    m_nameTags = m_dbManager->getTableFromNodeType_String("GeoNameTag");
 
     // containers to store data that have been moved to the new DB schema
     m_functions = m_dbManager->getTableFromNodeType_VecVecData("Function");
@@ -473,7 +476,8 @@ GeoVPhysVol* ReadGeoModel::buildGeoModelPrivate() {
 
     // *** recreate all mother-daughter relatioships between nodes ***
     start = std::chrono::system_clock::now();  // timing: get start time
-    loopOverAllChildrenInBunches();
+    // loopOverAllChildrenInBunches_String(); // OLD
+    loopOverAllChildrenInBunches_VecVecData(); //NEW
     end = std::chrono::system_clock::now();  // timing: get end time
     diff =
         std::chrono::duration_cast<std::chrono::seconds>(end - start).count();
@@ -486,10 +490,47 @@ GeoVPhysVol* ReadGeoModel::buildGeoModelPrivate() {
     return getRootVolume();
 }
 
+// OLD method
+//----------------------------------------
+// loop over parent-child relationship data
+// void ReadGeoModel::loopOverAllChildrenRecords(
+//     std::vector<std::vector<std::string>> records) {
+//     int nChildrenRecords = records.size();
+
+//     if (m_loglevel >= 1) {
+//         muxCout.lock();
+//         std::cout << "\nReadGeoModel::loopOverAllChildrenRecords -- Thread "
+//                   << std::this_thread::get_id() << " - processing "
+//                   << nChildrenRecords << " keys..." << std::endl;
+//         muxCout.unlock();
+//     }
+
+//     //  // Get Start Time
+//     //  std::chrono::system_clock::time_point start =
+//     //  std::chrono::system_clock::now();
+
+//     for (auto& record : records) {
+//         processParentChild(record);
+//     }
+
+//     //  // Get End Time
+//     //  auto end = std::chrono::system_clock::now();
+//     //  auto diff = std::chrono::duration_cast < std::chrono::seconds > (end
+//     //  - start).count();
+//     //
+//     //  if (m_timing || (m_loglevel >= 1)) {
+//     //    muxCout.lock();
+//     //    std::cout << "Time Taken to process " << nChildrenRecords << "
+//     //    parent-child relationships = " << diff << " Seconds" << std::endl;
+//     //    muxCout.unlock();
+//     //  }
+// }
+
+// NEW method
 //----------------------------------------
 // loop over parent-child relationship data
 void ReadGeoModel::loopOverAllChildrenRecords(
-    std::vector<std::vector<std::string>> records) {
+    DBRowsList records) {
     int nChildrenRecords = records.size();
 
     if (m_loglevel >= 1) {
@@ -499,26 +540,9 @@ void ReadGeoModel::loopOverAllChildrenRecords(
                   << nChildrenRecords << " keys..." << std::endl;
         muxCout.unlock();
     }
-
-    //  // Get Start Time
-    //  std::chrono::system_clock::time_point start =
-    //  std::chrono::system_clock::now();
-
     for (auto& record : records) {
         processParentChild(record);
     }
-
-    //  // Get End Time
-    //  auto end = std::chrono::system_clock::now();
-    //  auto diff = std::chrono::duration_cast < std::chrono::seconds > (end
-    //  - start).count();
-    //
-    //  if (m_timing || (m_loglevel >= 1)) {
-    //    muxCout.lock();
-    //    std::cout << "Time Taken to process " << nChildrenRecords << "
-    //    parent-child relationships = " << diff << " Seconds" << std::endl;
-    //    muxCout.unlock();
-    //  }
 }
 
 //! Iterate over the list of shapes, build them all, and store their
@@ -916,7 +940,8 @@ void ReadGeoModel::buildAllElements() {
     size_t nSize = m_elements.size();
     m_memMapElements.reserve(nSize);
     for (unsigned int ii = 0; ii < nSize; ++ii) {
-        const unsigned int nodeID = std::stoi(m_elements[ii][0]);
+        // const unsigned int nodeID = std::stoi(m_elements[ii][0]);
+        const unsigned nodeID = GeoModelHelpers::variantHelper::getFromVariant_Int(m_elements[ii][0], "Element:ID");
         buildElement(nodeID);  // nodes' IDs start from 1
     }
     if (nSize > 0)
@@ -1183,7 +1208,117 @@ void ReadGeoModel::buildAllFunctions() {
 
 
 
-void ReadGeoModel::loopOverAllChildrenInBunches() {
+// void ReadGeoModel::loopOverAllChildrenInBunches_String() {
+//     int nChildrenRecords = m_allchildren.size();
+//     if (m_loglevel >= 1)
+//         std::cout << "number of children to process: " << nChildrenRecords
+//                   << std::endl;
+
+//     // If we have a few children, then process them serially
+//     // std::cout << "Running concurrently? " << m_runMultithreaded <<
+//     // std::endl;
+//     if (true)  // !(m_runMultithreaded) || nChildrenRecords <= 500) // TODO:
+//                // test if you can optimize, then revert to if()...else()
+//     {
+//         // std::cout << "Running serially...\n";
+//         loopOverAllChildrenRecords(m_allchildren);
+//     }
+//     // ...otherwise, let's spawn some threads to process them in bunches,
+//     // parallelly!
+//     else {
+//         // std::cout << "Running concurrently...\n";
+
+//         std::chrono::system_clock::time_point start, end;
+//         if (m_timing || (m_loglevel >= 1)) {
+//             // Get Start Time
+//             start = std::chrono::system_clock::now();
+//         }
+
+//         // set number of worker threads
+//         unsigned int nThreads = 0;
+//         if (m_runMultithreaded_nThreads > 0) {
+//             nThreads = m_runMultithreaded_nThreads;
+//         } else if (m_runMultithreaded_nThreads == -1) {
+//             unsigned int nThreadsPlatform = std::thread::hardware_concurrency();
+//             nThreads = nThreadsPlatform;
+//             if (m_loglevel >= 1)
+//                 std::cout << "INFO - You have asked for hardware native "
+//                              "parellelism. On this platform, "
+//                           << nThreadsPlatform
+//                           << " concurrent threads are supported. Thus, using "
+//                           << nThreads << " threads.\n";
+//         }
+
+//         unsigned int nBunches = nChildrenRecords / nThreads;
+//         if (m_loglevel >= 1)
+//             std::cout << "Processing " << nThreads << " bunches, with "
+//                       << nBunches << " children each, plus the remainder."
+//                       << std::endl;
+
+//         // a vector to store the "futures" of async calls
+//         std::vector<std::future<void>> futures;
+
+//         for (unsigned int bb = 0; bb < nThreads; ++bb) {
+//             std::vector<std::vector<std::string>> bunch;
+
+//             unsigned int start = nBunches * bb;
+//             int len = nBunches;
+//             const unsigned int stop = start + len;
+//             std::vector<std::vector<std::string>>::const_iterator first =
+//                 m_allchildren.begin() + start;
+//             std::vector<std::vector<std::string>>::const_iterator last =
+//                 m_allchildren.begin() + stop;
+//             std::vector<std::vector<std::string>>::const_iterator end =
+//                 m_allchildren.end();
+//             if (bb == (nThreads - 1)) {  // last bunch
+//                 bunch = std::vector<std::vector<std::string>>(first, end);
+//             } else {  // all bunches but last one
+//                 bunch = std::vector<std::vector<std::string>>(first, last);
+//             }
+
+//             if (m_loglevel >= 1) {
+//                 muxCout.lock();
+//                 std::cout << "Thread " << bb + 1 << " - Start: " << start
+//                           << ", len: " << len
+//                           << "   ['len=-1' = all remaining items]" << std::endl;
+//                 muxCout.unlock();
+//             }
+
+//             if (m_loglevel >= 1) {
+//                 muxCout.lock();
+//                 std::cout << "'bunch' size: " << bunch.size() << std::endl;
+//                 muxCout.unlock();
+//             }
+
+//             futures.push_back(std::async(
+//                 std::launch::async, &ReadGeoModel::loopOverAllChildrenRecords,
+//                 this, bunch));
+//         }
+
+//         // wait for all async calls to complete
+//         // retrieve and print the value stored in the 'std::future'
+//         if (m_loglevel >= 1)
+//             std::cout << "Waiting for the threads to finish...\n" << std::flush;
+//         for (auto& e : futures) {
+//             e.wait();
+//         }
+//         if (m_loglevel >= 1) std::cout << "Done!\n";
+
+//         if (m_timing || (m_loglevel >= 1)) {
+//             // Get End Time
+//             end = std::chrono::system_clock::now();
+//             auto diff =
+//                 std::chrono::duration_cast<std::chrono::seconds>(end - start)
+//                     .count();
+//             std::cout << "(Total time taken to recreate all "
+//                       << nChildrenRecords
+//                       << " mother-children relationships: " << diff
+//                       << " seconds)" << std::endl;
+//         }
+//     }
+//     return;
+// }
+void ReadGeoModel::loopOverAllChildrenInBunches_VecVecData() {
     int nChildrenRecords = m_allchildren.size();
     if (m_loglevel >= 1)
         std::cout << "number of children to process: " << nChildrenRecords
@@ -1234,21 +1369,21 @@ void ReadGeoModel::loopOverAllChildrenInBunches() {
         std::vector<std::future<void>> futures;
 
         for (unsigned int bb = 0; bb < nThreads; ++bb) {
-            std::vector<std::vector<std::string>> bunch;
+            DBRowsList bunch;
 
             unsigned int start = nBunches * bb;
             int len = nBunches;
             const unsigned int stop = start + len;
-            std::vector<std::vector<std::string>>::const_iterator first =
+            DBRowsList::const_iterator first =
                 m_allchildren.begin() + start;
-            std::vector<std::vector<std::string>>::const_iterator last =
+            DBRowsList::const_iterator last =
                 m_allchildren.begin() + stop;
-            std::vector<std::vector<std::string>>::const_iterator end =
+            DBRowsList::const_iterator end =
                 m_allchildren.end();
             if (bb == (nThreads - 1)) {  // last bunch
-                bunch = std::vector<std::vector<std::string>>(first, end);
+                bunch = DBRowsList(first, end);
             } else {  // all bunches but last one
-                bunch = std::vector<std::vector<std::string>>(first, last);
+                bunch = DBRowsList(first, last);
             }
 
             if (m_loglevel >= 1) {
@@ -1392,6 +1527,105 @@ void ReadGeoModel::processParentChild(
         exit(EXIT_FAILURE);
     }
 }
+void ReadGeoModel::processParentChild(
+    const DBRowEntry& parentchild) {
+    
+    // if (m_loglevel >= 2) {
+    //     muxCout.lock();
+    //     std::cout << "\nReadGeoModel::processParentChild()..." << std::endl;
+    //     for (auto& rec : parentchild) std::cout << rec << "-";
+    //     std::cout << std::endl;
+    //     muxCout.unlock();
+    // }
+
+    // safety check
+    if (parentchild.size() < 8) {
+        std::cout << "ERROR!!! Probably you are using an old geometry file. "
+                     "Please, get a new one. Exiting..."
+                  << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+    // get the parent's details
+    const unsigned int parentId = GeoModelHelpers::variantHelper::getFromVariant_Int(parentchild[1], "ParentChild:parentID");
+    const unsigned int parentTableId = GeoModelHelpers::variantHelper::getFromVariant_Int(parentchild[2], "ParentChild:parentID"); 
+    const unsigned int parentCopyN = GeoModelHelpers::variantHelper::getFromVariant_Int(parentchild[3], "ParentChild:parentID"); 
+
+    // get the child's position in the parent's children list
+    // const unsigned int position = parentchild[4]; // unused, at the moment
+
+    // get the child's details
+    const unsigned int childTableId = GeoModelHelpers::variantHelper::getFromVariant_Int(parentchild[5], "ParentChild:parentID"); 
+    const unsigned int childId = GeoModelHelpers::variantHelper::getFromVariant_Int(parentchild[6], "ParentChild:parentID"); 
+    const unsigned int childCopyN = GeoModelHelpers::variantHelper::getFromVariant_Int(parentchild[7], "ParentChild:parentID"); 
+
+    //    std::string childNodeType =
+    //    m_tableID_toTableName[childTableId].toStdString();
+    std::string childNodeType = m_tableID_toTableName[childTableId];
+
+    if ("" == childNodeType || 0 == childNodeType.size()) {
+        std::cout << "ReadGeoModel -- ERROR!!! childNodeType is empty!!! "
+                     "Aborting..."
+                  << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+    GeoVPhysVol* parentVol = nullptr;
+
+    // build or get parent volume.
+    // Using the parentCopyNumber here, to get a given instance of the
+    // parent volume
+    if (m_loglevel >= 2) {
+        muxCout.lock();
+        std::cout << "build/get parent volume...\n";
+        muxCout.unlock();
+    }
+    parentVol = dynamic_cast<GeoVPhysVol*>(
+        buildVPhysVolInstance(parentId, parentTableId, parentCopyN));
+    std::string parentName = parentVol->getLogVol()->getName();
+
+    if (childNodeType == "GeoPhysVol") {
+        GeoPhysVol* childNode = dynamic_cast<GeoPhysVol*>(
+            buildVPhysVolInstance(childId, childTableId, childCopyN));
+        volAddHelper(parentVol, childNode);
+    } else if (childNodeType == "GeoFullPhysVol") {
+        GeoFullPhysVol* childNode = dynamic_cast<GeoFullPhysVol*>(
+            buildVPhysVolInstance(childId, childTableId, childCopyN));
+        volAddHelper(parentVol, childNode);
+    } else if (childNodeType == "GeoSerialDenominator") {
+        GeoSerialDenominator* childNode = getBuiltSerialDenominator(childId);
+        volAddHelper(parentVol, childNode);
+    } else if (childNodeType == "GeoSerialIdentifier") {
+        GeoSerialIdentifier* childNode = getBuiltSerialIdentifier(childId);
+        volAddHelper(parentVol, childNode);
+    } else if (childNodeType == "GeoIdentifierTag") {
+        GeoIdentifierTag* childNode = getBuiltIdentifierTag(childId);
+        volAddHelper(parentVol, childNode);
+    } else if (childNodeType == "GeoAlignableTransform") {
+        GeoAlignableTransform* childNode = getBuiltAlignableTransform(childId);
+        volAddHelper(parentVol, childNode);
+    } else if (childNodeType == "GeoTransform") {
+        if (m_loglevel >= 2) {
+            muxCout.lock();
+            std::cout << "get transform child...\n";
+            muxCout.unlock();
+        }
+        GeoTransform* childNode = getBuiltTransform(childId);
+        volAddHelper(parentVol, childNode);
+    } else if (childNodeType == "GeoSerialTransformer") {
+        GeoSerialTransformer* childNode = getBuiltSerialTransformer(childId);
+        volAddHelper(parentVol, childNode);
+    } else if (childNodeType == "GeoNameTag") {
+        GeoNameTag* childNode = getBuiltNameTag(childId);
+        volAddHelper(parentVol, childNode);
+    } else {
+        std::cout << "[" << childNodeType
+                  << "] ==> ERROR!!! - The conversion for this type of child "
+                     "node needs to be implemented."
+                  << std::endl;
+        exit(EXIT_FAILURE);
+    }
+}
 
 void ReadGeoModel::volAddHelper(GeoVPhysVol* vol, GeoGraphNode* volChild) {
     checkNodePtr(vol, "vol", __func__, __PRETTY_FUNCTION__);
@@ -1601,14 +1835,12 @@ GeoVPhysVol* ReadGeoModel::getRootVolume() {
     if (m_loglevel >= 2) {
         muxCout.lock();
         std::cout << "ReadGeoModel::getRootVolume()" << std::endl;
-        std::cout << "m_root_vol_data: " << m_root_vol_data[0] << ", " << m_root_vol_data[1] << ", " << m_root_vol_data[2] << std::endl;       
+        std::cout << "m_root_vol_data: " << m_root_vol_data.first << ", " << m_root_vol_data.second << std::endl;       
         muxCout.unlock();
     }
-    const unsigned int id =
-        std::stoi(m_root_vol_data[1]);  // TODO: GeoModel GetRoot() should
-                                        // return integers instead of strings...
-    const std::string tableName = m_root_vol_data[0];
-    const unsigned int tableId = m_dbManager->getTableIdFromNodeType(tableName);
+    const unsigned tableId = m_root_vol_data.first;
+    const unsigned id = m_root_vol_data.second;
+    // const unsigned int tableId = m_dbManager->getTableIdFromNodeType(tableName);
     const unsigned int copyNumber =
         1;  // the Root volume has only one copy by definition
     GeoVPhysVol* root = buildVPhysVolInstance(id, tableId, copyNumber);
@@ -1626,18 +1858,20 @@ GeoMaterial* ReadGeoModel::buildMaterial(const unsigned int id) {
         std::cout << "ReadGeoModel::buildMaterial()" << std::endl;
         muxCout.unlock();
     }
-    // std::vector<std::string> values = m_materials[id - 1];
-    DBRowEntry values = m_materials[id - 1];
 
-    // const unsigned int matId = std::stoi(values[0]);
-    // const std::string matName = values[1];
-    // double matDensity = std::stod(values[2]);
-    // std::string matElements = values[3];
+    // OLD
+    std::vector<std::string> values = m_materials[id - 1];
+    const unsigned int matId = std::stoi(values[0]);
+    const std::string matName = values[1];
+    double matDensity = std::stod(values[2]);
+    std::string matElements = values[3];
 
-    const unsigned int matId = GeoModelHelpers::variantHelper::getFromVariant_Int(row[0], "Material:id");
-    const std::string matName = GeoModelHelpers::variantHelper::getFromVariant_String(row[0], "Material:matName");
-    const double matDensity = GeoModelHelpers::variantHelper::getFromVariant_Int(row[0], "Material:matDensity");
-    const std::string matElements = GeoModelHelpers::variantHelper::getFromVariant_String(row[0], "Material:matElements");
+    // NEW
+    // DBRowEntry values = m_materials[id - 1];
+    // const unsigned int matId = GeoModelHelpers::variantHelper::getFromVariant_Int(values[0], "Material:id");
+    // const std::string matName = GeoModelHelpers::variantHelper::getFromVariant_String(values[0], "Material:matName");
+    // const double matDensity = GeoModelHelpers::variantHelper::getFromVariant_Int(values[0], "Material:matDensity");
+    // const std::string matElements = GeoModelHelpers::variantHelper::getFromVariant_String(values[0], "Material:matElements");
 
     if (m_loglevel >= 2) {
         muxCout.lock();
@@ -1684,18 +1918,19 @@ GeoElement* ReadGeoModel::buildElement(const unsigned int id) {
         muxCout.unlock();
     }
 
-    if (m_elements.size() == 0)
-        std::cout << "ERROR! 'm_elements' is empty! Did you load the "
-                     "'Elements' table? \n\t ==> Aborting...\n"
-                  << std::endl;
+    // if (m_elements.size() == 0)
+    //     std::cout << "ERROR! 'm_elements' is empty! Did you load the "
+    //                  "'Elements' table? \n\t ==> Aborting...\n"
+    //               << std::endl;
 
-    std::vector<std::string> values = m_elements[id - 1];
+    // std::vector<std::string> values = m_elements[id - 1];
+    DBRowEntry values = m_elements[id - 1];
 
-    const unsigned int elId = std::stoi(values[0]);
-    std::string elName = values[1];
-    std::string elSymbol = values[2];
-    double elZ = std::stod(values[3]);
-    double elA = std::stod(values[4]);
+    const unsigned elId = GeoModelHelpers::variantHelper::getFromVariant_Int(values[0], "Element:ID"); //values[0];
+    std::string elName = GeoModelHelpers::variantHelper::getFromVariant_String(values[1], "Element:name"); // values[1];
+    std::string elSymbol = GeoModelHelpers::variantHelper::getFromVariant_String(values[2], "Element:symbol"); //values[2];
+    double elZ = GeoModelHelpers::variantHelper::getFromVariant_Double(values[3], "Element:Z"); //std::stod(values[3]);
+    double elA = GeoModelHelpers::variantHelper::getFromVariant_Double(values[4], "Element:A"); //std::stod(values[4]);
 
     if (m_loglevel >= 2) {
         muxCout.lock();
diff --git a/GeoModelIO/GeoModelWrite/GeoModelWrite/WriteGeoModel.h b/GeoModelIO/GeoModelWrite/GeoModelWrite/WriteGeoModel.h
index 4577344aa..51596afb3 100644
--- a/GeoModelIO/GeoModelWrite/GeoModelWrite/WriteGeoModel.h
+++ b/GeoModelIO/GeoModelWrite/GeoModelWrite/WriteGeoModel.h
@@ -174,14 +174,15 @@ class WriteGeoModel : public GeoNodeAction {
     unsigned int storeTranform(const GeoTransform *node);
 
     unsigned int storeObj(const GeoMaterial *pointer, const std::string &name,
-                          const double &density, const std::string &elements);
+                          const double &density, const DBRowsList &materialData);
     unsigned int storeObj(const GeoElement *pointer, const std::string &name,
                           const std::string &symbol, const double &elZ,
                           const double &elA);
     unsigned int storeObj(const GeoShape *pointer, const std::string &type,
                           const std::string &parameters);
     std::pair<std::string, unsigned> storeObj(const GeoShape *pointer, const std::string &type,
-                          const std::vector<std::variant<int, long, float, double, std::string>> &parameters);
+                                            DBRowEntry &parameters,
+                                            const DBRowsList &shapeData);
     unsigned int storeObj(const GeoLogVol *pointer, const std::string &name,
                           const unsigned int &shapeId, std::string_view shapeType,
                           const unsigned int &materialId);
@@ -218,30 +219,35 @@ class WriteGeoModel : public GeoNodeAction {
     unsigned int addRecord(std::vector<std::vector<std::string>> *container,
                            const std::vector<std::string> values) const;
     unsigned int addRecord(DBRowsList *container,
-                           const std::vector<std::variant<int, long, float, double, std::string>> values) const;
+                           const DBRowEntry values) const;
     
     std::pair<unsigned, unsigned> addRecordData(
         DBRowsList *container,
         const DBRowsList values) const;
 
-    unsigned int addMaterial(const std::string &name, const double &density,
-                             const std::string &elements);
     unsigned int addElement(const std::string &name, const std::string &symbol,
                             const double &elZ, const double &elA);
+    
+    unsigned int addMaterial(const std::string &name, const double &density,
+                             const unsigned &dataStart, const unsigned &dataEnd);
+    std::pair<unsigned, unsigned> addMaterialData(const DBRowsList &matElementFraction);
+
     unsigned int addNameTag(const std::string &name);
     unsigned int addAlignableTransform(const std::vector<double> &params);
     unsigned int addTransform(const std::vector<double> &params);
     unsigned int addFunction(const std::string &expression, const unsigned &dataStart, const unsigned &dataEnd);
-    unsigned int addSerialTransformer(const unsigned int &funcId,
-                                      const unsigned int &physvolId,
+    unsigned int addSerialTransformer(const unsigned &funcId,
+                                      const unsigned &physvolId,
                                       const std::string volType,
-                                      const unsigned int &copies);
+                                      const unsigned &copies);
+    
     unsigned int addShape(const std::string &type,
                           const std::string &parameters);
     unsigned int addShape(const std::string &type,
                           const std::vector<std::variant<int, long, float, double, std::string>> &parameters);
     std::pair<unsigned, unsigned> addShapeData(const std::string type,
                                        const DBRowsList &shapeData);
+    
     unsigned int addSerialDenominator(const std::string &baseName);
     unsigned int addSerialIdentifier(const int &baseId);
     unsigned int addIdentifierTag(const int &identifier);
@@ -350,7 +356,7 @@ class WriteGeoModel : public GeoNodeAction {
     // std::vector<std::vector<std::string>> m_logVols;
     std::vector<std::vector<std::string>> m_physVols;
     std::vector<std::vector<std::string>> m_fullPhysVols;
-    std::vector<std::vector<std::string>> m_materials;
+    // std::vector<std::vector<std::string>> m_materials;
     // std::vector<std::vector<std::string>> m_elements;
     std::vector<std::vector<std::string>> m_transforms;
     std::vector<std::vector<std::string>> m_alignableTransforms;
@@ -362,6 +368,8 @@ class WriteGeoModel : public GeoNodeAction {
     std::vector<std::vector<std::string>> m_shapes;
 
     DBRowsList m_elements;
+    DBRowsList m_materials;
+    DBRowsList m_materials_Data;
 
     DBRowsList m_shapes_Box;
     DBRowsList m_shapes_Tube;
diff --git a/GeoModelIO/GeoModelWrite/src/WriteGeoModel.cpp b/GeoModelIO/GeoModelWrite/src/WriteGeoModel.cpp
index 4d96e1c05..d55ccced2 100644
--- a/GeoModelIO/GeoModelWrite/src/WriteGeoModel.cpp
+++ b/GeoModelIO/GeoModelWrite/src/WriteGeoModel.cpp
@@ -706,30 +706,19 @@ std::pair<std::string, unsigned> WriteGeoModel::storeShape(const GeoShape* shape
     // get shape parameters
     if (std::count(shapesNewDB.begin(), shapesNewDB.end(), shapeType))
     {
-        std::pair<std::vector<std::variant<int, long, float, double, std::string>>,
-                  std::vector<std::vector<std::variant<int, long, float, double, std::string>>>>
+        std::pair<DBRowEntry, DBRowsList>
             shapePair = getShapeParametersV(shape);
         
-        std::vector<std::variant<int, long, float, double, std::string>> shapePars = shapePair.first;
-        std::vector<std::vector<std::variant<int, long, float, double, std::string>>> shapeData = shapePair.second;
-
-        if (shapeData.size() > 0)
-        {
-            // Store the node's additional data 
-            // (e.g., the numeric values of a Function, or the ZPlanes of a GeoPcon shape node)
-            std::pair<unsigned, unsigned> dataRows = addShapeData(shapeType, shapeData);
-            unsigned dataStart = dataRows.first;
-            unsigned dataEnd = dataRows.second;
-            shapePars.push_back(dataStart);
-            shapePars.push_back(dataEnd);
-        }
+        DBRowEntry shapePars = shapePair.first;
+        DBRowsList shapeData = shapePair.second;
+       
         // DEBUG MSGS
         // std::cout << "shape: " << shapeType << std::endl;
         // GeoModelIO::CppHelper::printStdVectorVariants(shapePars);
         // std::cout << std::endl;
 
         // store the shape in the DB and returns the ID
-        return storeObj(shape, shapeType, shapePars);
+        return storeObj(shape, shapeType, shapePars, shapeData);
 
     }
     else
@@ -747,15 +736,26 @@ std::pair<std::string, unsigned> WriteGeoModel::storeShape(const GeoShape* shape
 
 //______________________________________________________________________
 unsigned int WriteGeoModel::storeMaterial(const GeoMaterial* mat) {
-    const std::string matName = mat->getName();   // The name of the material.
-    const double matDensity = mat->getDensity();  // The density of the
-                                                  // material.
-    const unsigned int numElements = mat->getNumElements();
+    const std::string matName = mat->getName();  // The name of the material
+    const double matDensity = mat->getDensity(); // The density of the material
+    const unsigned numElements = mat->getNumElements(); // The number of elements composing the material
+
+    if (0 == numElements) {
+        THROW_EXCEPTION("ERROR!!! The material '" << matName << "' has zero elements!");
+    }
+
+    // std::string matElements;
+    // std::vector<std::string> matElementsList;
+
+    DBRowEntry matData_ElementFraction;
+    DBRowsList matData_List;
 
     // loop over the elements composing the material
-    std::string matElements;
-    std::vector<std::string> matElementsList;
     for (unsigned int i = 0; i < numElements; i++) {
+        
+        // clear the container for the row (elemnt,fraction)
+        matData_ElementFraction.clear();
+
         // Gets the i-th element.
         const GeoElement* element = mat->getElement(i);
         std::string elName = element->getName();
@@ -764,16 +764,24 @@ unsigned int WriteGeoModel::storeMaterial(const GeoMaterial* mat) {
         unsigned int elementId = storeElement(element);
 
         // Gets the fraction by weight of the i-th element
-        const std::string elementFraction =
-            CppHelper::to_string_with_precision(mat->getFraction(i));
+        const double elementFraction = mat->getFraction(i);
 
-        matElementsList.push_back(std::to_string(elementId) + ":" +
-                                  elementFraction);  // INT+string
+        // Build one row for a single element that compose 
+        // the material: (element, fraction)
+        matData_ElementFraction.push_back(elementId);
+        matData_ElementFraction.push_back(elementFraction);
+
+        // matElementsList.push_back(std::to_string(elementId) + ":" +
+        //                           elementFraction);  // INT+string
+
+        // Add the (element,fraction) 
+        // to the list of all elements for the given material
+        matData_List.push_back(matData_ElementFraction);
     }
-    matElements = joinVectorStrings(matElementsList, ";");
+    // matElements = joinVectorStrings(matElementsList, ";");
 
     // store the material in the DB and returns the ID
-    return storeObj(mat, matName, matDensity, matElements);
+    return storeObj(mat, matName, matDensity, matData_List);
 }
 
 //_______________________________________________________________________
@@ -1618,13 +1626,18 @@ void WriteGeoModel::showMemoryMap() {
 
 unsigned int WriteGeoModel::storeObj(const GeoMaterial* pointer,
                                      const std::string& name,
-                                     const double& density,
-                                     const std::string& elements) {
+                                     const double &density,
+                                     const DBRowsList &materialData) {
     std::string address = getAddressStringFromPointer(pointer);
     unsigned int materialId;
 
     if (!isAddressStored(address)) {
-        materialId = addMaterial(name, density, elements);
+        // Store the material's additional data,
+        // that is, the list of the elements that compose the material and their fraction
+        std::pair<unsigned, unsigned> dataRows = addMaterialData(materialData);
+        unsigned dataStart = dataRows.first;
+        unsigned dataEnd = dataRows.second;
+        materialId = addMaterial(name, density, dataStart, dataEnd);
         storeAddress(address, materialId);
     } else {
         materialId = getStoredIdFromAddress(address);
@@ -1664,11 +1677,26 @@ unsigned int WriteGeoModel::storeObj(const GeoShape* pointer,
 }
 std::pair<std::string, unsigned> WriteGeoModel::storeObj(const GeoShape* pointer,
                                      const std::string& shapeName,
-                                     const std::vector<std::variant<int, long, float, double, std::string>>& parameters) {
+                                     DBRowEntry& parameters,
+                                     const DBRowsList &shapeData) {
     std::string address = getAddressStringFromPointer(pointer);
 
     unsigned int shapeId;
     if (!isAddressStored(address)) {
+
+        // if the shape has additional data, store them in the DB,
+        // get the start and end rows, then add those to the shape
+        // parameters to be stored with the shape
+         if (shapeData.size() > 0)
+        {
+            // Store the node's additional data
+            // (e.g., the numeric values of a Function, or the ZPlanes of a GeoPcon shape node)
+            std::pair<unsigned, unsigned> dataRows = addShapeData(shapeName, shapeData);
+            unsigned dataStart = dataRows.first;
+            unsigned dataEnd = dataRows.second;
+            parameters.push_back(dataStart);
+            parameters.push_back(dataEnd);
+        }
         shapeId = addShape(shapeName, parameters);
         storeAddress(address, shapeId); // TODO: check if this step of storing the address and the ID is still used/needed.
     } else {
@@ -2022,17 +2050,27 @@ std::pair<unsigned, unsigned> WriteGeoModel::addShapeData(const std::string type
     std::pair<unsigned, unsigned> dataPair = addRecordData(container, shapeData);
     return dataPair;
 }
+std::pair<unsigned, unsigned> WriteGeoModel::addMaterialData(const DBRowsList& matListElementFraction) 
+{
+    DBRowsList *container = &m_materials_Data;
+    std::pair<unsigned, unsigned> dataPair = addRecordData(container, matListElementFraction);
+    return dataPair;
+}
 
 
 
 unsigned int WriteGeoModel::addMaterial(const std::string& name,
                                         const double& density,
-                                        const std::string& elements) {
-    std::vector<std::vector<std::string>>* container = &m_materials;
-    std::vector<std::string> values;
+                                        const unsigned &dataStart,
+                                        const unsigned &dataEnd) {
+    // std::vector<std::vector<std::string>>* container = &m_materials;
+    // std::vector<std::string> values;
+    DBRowsList* container = &m_materials;
+    DBRowEntry values;
     values.push_back(name);
-    values.push_back(CppHelper::to_string_with_precision(density));
-    values.push_back(elements);
+    values.push_back(density);
+    values.push_back(dataStart);
+    values.push_back(dataEnd);
     return addRecord(container, values);
 }
 
@@ -2587,8 +2625,10 @@ void WriteGeoModel::saveToDB(std::vector<GeoPublisher*>& publishers) {
     std::cout << "Saving the GeoModel tree to file: '" << m_dbpath << "'"
               << std::endl;
 
-    m_dbManager->addListOfRecords("GeoMaterial", m_materials);
     m_dbManager->addListOfRecords("GeoElement", m_elements);
+    m_dbManager->addListOfRecords("GeoMaterial", m_materials);
+    m_dbManager->addListOfRecordsToTable("Materials_Data", m_materials_Data); // new version, with list of (element,fraction) stored separately
+    
     m_dbManager->addListOfRecords("GeoNameTag", m_nameTags);
     m_dbManager->addListOfRecords("GeoAlignableTransform",
                                   m_alignableTransforms);
@@ -2604,7 +2644,7 @@ void WriteGeoModel::saveToDB(std::vector<GeoPublisher*>& publishers) {
     
     m_dbManager->addRecordsToTable("FuncExprData", m_exprData);
 
-    m_dbManager->addListOfRecords("GeoShape", m_shapes); // old version, with shape's parameters as trings
+    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("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
-- 
GitLab