diff --git a/CMakeLists.txt b/CMakeLists.txt index fca24c6e761abcab28ce38333eff1c7b6d62cb47..706f9a4d2101615b6da538cc79521fe6b677e72a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,40 +2,31 @@ # Set up the project. cmake_minimum_required( VERSION 3.1 ) -project( "GeoModelIO" VERSION 3.1.3 LANGUAGES CXX ) +project( "GeoModelIO" VERSION 3.2.0 LANGUAGES CXX ) -# Set default build options. -set( CMAKE_BUILD_TYPE "Release" CACHE STRING "CMake build mode to use" ) -set( CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard used for the build" ) -set( CMAKE_CXX_EXTENSIONS FALSE CACHE BOOL "(Dis)allow using GNU extensions" ) -set( CMAKE_CXX_STANDARD_REQUIRED TRUE CACHE BOOL - "Require the specified C++ standard for the build" ) - -# Print Build Info on screen -include(cmake/BuildTypeInfo.cmake) - -# WARNING! -# Building in 'Debug' mode with Clang (macOS) makes the GeoModelRead library -# crash when running with multiple threads. -# ( Details: https://its.cern.ch/jira/browse/ATLASDD-38 ) -# Thus, if we are on macOS and the build type has been set to 'Debug', -# we send a warning message to the user -if ( APPLE AND ${CMAKE_BUILD_TYPE} STREQUAL "Debug") - message(STATUS "**********") - message(STATUS "WARNING!! You chose to build in 'Debug' mode. However, on macOS (where you appear to run), this makes multi-threaded GeoModelRead crash. Therefore, if you need debug symbols, please build with 'RelWithDebInfo'. (Details: https://its.cern.ch/jira/browse/ATLASDD-38)") - message(STATUS "**********") -endif() +# --------------------------- # Make the module directory visible to CMake. list( APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ) -# Project's dependencies. -find_package( GeoModelCore REQUIRED ) -find_package( Qt5 COMPONENTS Core Sql REQUIRED ) +# Set build and C++ default options +include ( configure_cpp_options ) + +# Set optional definitions to steer the source code +include( configure_definitions) # Use the GNU install directory names. include( GNUInstallDirs ) +# Print Build Info on screen +include( PrintBuildInfo ) + +# --------------------------- + +# Project's dependencies. +find_package( GeoModelCore 3.2.0 REQUIRED ) +find_package( Qt5 COMPONENTS Core Sql REQUIRED ) + # Set sub-packages to build. add_subdirectory( GeoModelErrorHandler ) add_subdirectory( GeoModelDBManager ) @@ -73,5 +64,27 @@ install( COMPONENT Development DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} ) + +# Export targets from the *build* tree for use by outside projects. +# +# INFO: +# Thus, this project can be used by other projects without being installed. +# This is useful for development: you can build a child projet without an +# install path. +# For example: +# while generating GeoModel projects with CMake as Xcode project on macOS, +# after having built GeoModelCore, you can use that to build GeoModelIO without +# the need of an installation step, by simply prepend 'GeoModelCore_DIR' as this: +# $ GeoModelCore_DIR=../b_core/CMakeFiles cmake -G Xcode ../../GeoModelIO +# which is useful when generating projects +# that needs to be built with Xcode. Xcode in fact does not handle well the install +# folder generated by CMake: sometimes the content of that is not updated when +# updating the code, which results in keeping running the same old code even if +# we modified our source code. +export(EXPORT ${PROJECT_NAME}-export NAMESPACE "${PROJECT_NAME}::" FILE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake) +export(TARGETS GeoModelErrorHandler GeoModelDBManager TFPersistification GeoModelRead GeoModelWrite NAMESPACE "${PROJECT_NAME}::" FILE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Targets.cmake) + + + # Set up the packaging of the project using CPack. include( GeoModelIOPackaging ) diff --git a/GeoModelDBManager/GeoModelDBManager/GMDBManager.h b/GeoModelDBManager/GeoModelDBManager/GMDBManager.h index 440327c91b381bfa78efcc9e05f08f65e5334571..d5a49139dd4a16f48dd37f566f914ec313da964e 100644 --- a/GeoModelDBManager/GeoModelDBManager/GMDBManager.h +++ b/GeoModelDBManager/GeoModelDBManager/GMDBManager.h @@ -16,6 +16,7 @@ // include C++ #include <iostream> +#include <unordered_map> /** * \class GMDBManager @@ -160,21 +161,34 @@ public: QString getDBFilePath(); QStringList getRootPhysVol(); + std::vector<std::string> getRootPhysVolStd(); QStringList getItem(QString geoType, unsigned int id); QStringList getItem(unsigned int tableId, unsigned int id); - QStringList getItemFromTableName(QString tableName, unsigned int id); + + QStringList getItemFromTableName(QString tableName, unsigned int id); + std::vector<std::string> getItemFromTableNameStd(QString tableName, unsigned int id); + QStringList getItemAndType(unsigned int tableId, unsigned int id); + std::vector<std::string> getItemAndTypeStd(unsigned int tableId, unsigned int id); QString getNodeTypeFromTableId(unsigned int id); + std::string getNodeTypeFromTableIdStd(unsigned int id); QMap<unsigned int, QStringList> getVPhysVolChildren(const unsigned int id, const QString nodeType, const unsigned int copyN); /// methods to dump the DB QHash<QString, QMap<unsigned int, QStringList>> getChildrenTable(); + std::vector<std::vector<std::string>> getChildrenTableStd(); + QHash<unsigned int, QStringList> getTableFromNodeType(QString nodeType); + std::vector<std::vector<std::string>> getTableFromNodeTypeStd(std::string nodeType); + QHash<unsigned int, QString> getAll_TableIDsNodeTypes(); QHash<QString, unsigned int> getAll_NodeTypesTableIDs(); + std::unordered_map<unsigned int, std::string> getAll_TableIDsNodeTypesStd(); + std::unordered_map<std::string, unsigned int> getAll_NodeTypesTableIDsStd(); + private: @@ -188,11 +202,14 @@ private: QStringList getTableColNamesFromDB(QString tableName) const; QString getTableNameFromTableId(unsigned int tabId); + std::string getTableNameFromTableIdStd(unsigned int tabId); + QVariant getTableIdFromNodeType(QString nodeType); void storeNodeType(QString nodeType, QString tableName); QString getTableNameFromNodeType(QString nodeType); QSqlQuery selectAllFromTable(QString tableName) const; + QSqlQuery selectAllFromTableSortBy(QString tableName, std::string sortColumn="") const; void storeTableColumnNames(QStringList input); diff --git a/GeoModelDBManager/src/GMDBManager.cpp b/GeoModelDBManager/src/GMDBManager.cpp index 4f8f2e071a3342e4ee728887c67e0f088bca57b3..3db7b6fc936f51048aa7ce0319bc6148e376162b 100644 --- a/GeoModelDBManager/src/GMDBManager.cpp +++ b/GeoModelDBManager/src/GMDBManager.cpp @@ -10,6 +10,8 @@ #include <QSqlDriver> #include <QDebug> +#include <stdlib.h> /* exit, EXIT_FAILURE */ + static std::string dbversion = "0.3.0"; // added GeoElement support (Sep 2019) @@ -55,7 +57,6 @@ GMDBManager::GMDBManager(const QString &path) : m_dbpath(path), m_dbIsOK(false), GMDBManager::~GMDBManager() { - // JFB commented: qDebug() << "GMDBManager: destructor"; if (m_db.isOpen()) { m_db.close(); @@ -72,85 +73,69 @@ bool GMDBManager::isOpen() const void GMDBManager::printAllMaterials() const { - // JFB commented: qDebug() << "printAllMaterials()"; printAllRecords("Materials"); } void GMDBManager::printAllElements() const { - // JFB commented: qDebug() << "printAllElements()"; printAllRecords("Elements"); } void GMDBManager::printAllShapes() const { - // JFB commented: qDebug() << "printAllShapes()"; printAllRecords("Shapes"); } void GMDBManager::printAllSerialDenominators() const { - // JFB commented: qDebug() << "printAllShapes()"; printAllRecords("SerialDenominators"); } void GMDBManager::printAllLogVols() const { - // JFB commented: qDebug() << "printAllLogVols()"; printAllRecords("LogVols"); } void GMDBManager::printAllPhysVols() const { - // JFB commented: qDebug() << "printAllPhysVols()"; printAllRecords("PhysVols"); } void GMDBManager::printAllFullPhysVols() const { - // JFB commented: qDebug() << "printAllFullPhysVols()"; printAllRecords("FullPhysVols"); } void GMDBManager::printAllSerialTransformers() const { - // JFB commented: qDebug() << "printAllSerialTransformers()"; printAllRecords("SerialTransformers"); } void GMDBManager::printAllFunctions() const { - // JFB commented: qDebug() << "printAllFunctions()"; printAllRecords("Functions"); } void GMDBManager::printAllTransforms() const { - // JFB commented: qDebug() << "printAllTransforms()"; printAllRecords("Transforms"); } void GMDBManager::printAllAlignableTransforms() const { - // JFB commented: qDebug() << "printAllAlignableTransforms()"; printAllRecords("AlignableTransforms"); } void GMDBManager::printAllNameTags() const { - // JFB commented: qDebug() << "printAllNameTags()"; printAllRecords("NameTags"); } void GMDBManager::printAllChildrenPositions() const { - // JFB commented: qDebug() << "printAllChildrenPositions()"; printAllRecords("ChildrenPositions"); } void GMDBManager::printAllNodeTypes() const { - // JFB commented: qDebug() << "printAllNodeTypes()"; printAllRecords("GeoNodesTypes"); } void GMDBManager::printRootVolumeId() const { - // JFB commented: qDebug() << "printRootVolumeId()"; printAllRecords("RootVolume"); } void GMDBManager::printDBVersion() const { - // JFB commented: qDebug() << "printDBVersion()"; printAllRecords("dbversion"); } @@ -214,6 +199,33 @@ QHash<unsigned int, QStringList> GMDBManager::getTableFromNodeType(QString nodeT return records; } +std::vector<std::vector<std::string>> GMDBManager::getTableFromNodeTypeStd(std::string nodeType) +{ + + QString tableName = getTableNameFromNodeType(QString::fromStdString(nodeType)); + + // QHash<unsigned int, QStringList> records; + // QStringList nodeParams; + std::vector<std::vector<std::string>> records; + std::vector<std::string> nodeParams; + + int nCols = (m_tableNames[tableName]).size(); + + QSqlQuery query = selectAllFromTable(tableName); // sorted by ID + while (query.next()) { + nodeParams.clear(); +// unsigned int nodeId = query.value(0).toUInt(); + + for( int ii=0; ii<nCols; ++ii) { + // nodeParams << query.value(ii).toString(); + nodeParams.push_back( query.value(ii).toString().toStdString() ); + } + // records[nodeId] = nodeParams; + records.push_back(nodeParams); + } +return records; +} + void GMDBManager::showError(const QSqlError &err) const { @@ -860,6 +872,22 @@ QStringList GMDBManager::getItemAndType(unsigned int tableId, unsigned int id) return results; } +std::vector<std::string> GMDBManager::getItemAndTypeStd(unsigned int tableId, unsigned int id) +{ + std::vector<std::string> results; + + QString tableName = getTableNameFromTableId(tableId); + std::string nodeType = getNodeTypeFromTableIdStd(tableId); + std::vector<std::string> item = getItemFromTableNameStd(tableName, id); + + // add the type + results.push_back( nodeType ); + // add the item + results.insert(results.end(), item.begin(), item.end()); + + return results; + +} QStringList GMDBManager::getItemFromTableName(QString tableName, unsigned int id) @@ -895,6 +923,41 @@ QStringList GMDBManager::getItemFromTableName(QString tableName, unsigned int id return params; } +std::vector<std::string> GMDBManager::getItemFromTableNameStd(QString tableName, unsigned int id) +{ + if (m_deepDebug) qDebug() << "GMDBManager::getItemFromTableName(tableName, id)"<< tableName << QString::number(id); + /* + * 2. Get the object from DB + */ + // prepare a SQL string with the right table name + QString queryStr = QString("SELECT * FROM %1 WHERE id = (?)").arg(tableName); + // prepare the query + QSqlQuery q; + if (!q.prepare( queryStr )) { + showError(q.lastError()); +// return QStringList(); + exit(EXIT_FAILURE); + } + q.addBindValue(id); + q.exec(); + + std::vector<std::string> params; + + // get the number of columns of the DB table + int nCols = (m_tableNames[tableName]).size(); + + while (q.next()) { + + for( int ii=0; ii<nCols; ++ii) + params.push_back( q.value(ii).toString().toStdString() ); + } + if (params.size()==0) { + qWarning() << "WARNING!!" << "Item" << id << "does not exist in table" << tableName << "!!"; + } + return params; +} + + // get the list of children for a single GeoVPhysVol (i.e., GeoPhysVol or GeoFullPhysVol) QMap<unsigned int, QStringList> GMDBManager::getVPhysVolChildren(const unsigned int id, const QString nodeType, const unsigned int parentCopyNumber) { @@ -962,14 +1025,52 @@ QHash<QString, QMap<unsigned int, QStringList>> GMDBManager::getChildrenTable() QString key = parentId + ":" + parentTable + ":" + parentCopyNumber; - for( int ii=0; ii<nCols; ++ii) + for( int ii=0; ii<nCols; ++ii) { childParams << q.value(ii).toString(); + } all_children[key][childPos] = childParams; } return all_children; } +// Get all parent-children data from the database in one go +std::vector<std::vector<std::string>> GMDBManager::getChildrenTableStd() +{ +// std::cout << "GMDBManager::getChildrenTableStd()\n"; // debug + + QSqlQuery q; + QString queryStr = QString("SELECT * FROM ChildrenPositions ORDER BY parentTable, parentId, parentCopyNumber, position"); + if (!q.prepare(queryStr)) { + showError(q.lastError()); + } + q.exec(); + + std::vector<std::vector<std::string>> all_children; // to store all children + std::vector<std::string> childParams; // to temporarily store the children parameters + + // get the number of columns of the DB table + int nCols = m_tableNames["ChildrenPositions"].size(); + // std::cout << "num of columns in childrenPos table" << nCols << std::endl; // debug + + // loop over all children's positions stored in the DB + while (q.next()) { + + childParams.clear(); + + for( int ii=0; ii<nCols; ++ii) { + childParams.push_back( q.value(ii).toString().toStdString() ); + } + +// // debug +// if(childParams[2]=="8920") +// std::cout << "CoolingTube parent:"; print childParams; + + all_children.push_back(childParams); + } + return all_children; +} + QVariant GMDBManager::getTableIdFromNodeType(QString nodeType) @@ -1015,7 +1116,6 @@ QString GMDBManager::getTableNameFromNodeType(QString nodeType) // TODO: this and other methods could take data from in-memory maps, without asking to the DB all the times QString GMDBManager::getTableNameFromTableId(unsigned int tabId) { - // JFB commented: qDebug() << "GMDBManager::getTableNameFromTableId()"; QSqlQuery q; if (!q.prepare(QLatin1String("SELECT tableName FROM GeoNodesTypes WHERE id = (?)"))) { showError(q.lastError()); @@ -1032,10 +1132,27 @@ QString GMDBManager::getTableNameFromTableId(unsigned int tabId) return tableName; } +std::string GMDBManager::getTableNameFromTableIdStd(unsigned int tabId) +{ + QSqlQuery q; + if (!q.prepare(QLatin1String("SELECT tableName FROM GeoNodesTypes WHERE id = (?)"))) { + showError(q.lastError()); +// return QString(); + exit(EXIT_FAILURE); + } + q.addBindValue(tabId); + q.exec(); + + std::string tableName; + while (q.next()) { + tableName = q.value(0).toString().toStdString(); + } + + return tableName; +} QString GMDBManager::getNodeTypeFromTableId(unsigned int tabId) { - // JFB commented: qDebug() << "GMDBManager::getNodeTypeFromTableId()"; QSqlQuery q; if (!q.prepare(QLatin1String("SELECT nodeType FROM GeoNodesTypes WHERE id = (?)"))) { showError(q.lastError()); @@ -1047,12 +1164,28 @@ QString GMDBManager::getNodeTypeFromTableId(unsigned int tabId) QString nodeType; while (q.next()) { nodeType = q.value(0).toString(); - // JFB commented: qDebug() << "tableName:" << nodeType; } return nodeType; } - +std::string GMDBManager::getNodeTypeFromTableIdStd(unsigned int tabId) +{ + QSqlQuery q; + if (!q.prepare(QLatin1String("SELECT nodeType FROM GeoNodesTypes WHERE id = (?)"))) { + showError(q.lastError()); + // return QString(); + exit(EXIT_FAILURE); + } + q.addBindValue(tabId); + q.exec(); + + std::string nodeType; + while (q.next()) { + nodeType = q.value(0).toString().toStdString(); + } + + return nodeType; +} QHash<unsigned int, QString> GMDBManager::getAll_TableIDsNodeTypes() { @@ -1073,6 +1206,25 @@ QHash<unsigned int, QString> GMDBManager::getAll_TableIDsNodeTypes() return output; } +std::unordered_map<unsigned int, std::string> GMDBManager::getAll_TableIDsNodeTypesStd() +{ + // JFB commented: qDebug() << "GMDBManager::getAll_TableIDsNodeTypes()"; + +// QHash<unsigned int, QString> output; + std::unordered_map<unsigned int, std::string> output; + + QSqlQuery q = selectAllFromTable("GeoNodesTypes"); + + unsigned int id; + QString nodeType; + // QString tableName; + while (q.next()) { + id = q.value(0).toUInt(); + nodeType = q.value(1).toString(); + output[id] = nodeType.toStdString(); + } + return output; +} QHash<QString, unsigned int> GMDBManager::getAll_NodeTypesTableIDs() { @@ -1093,23 +1245,70 @@ QHash<QString, unsigned int> GMDBManager::getAll_NodeTypesTableIDs() return output; } - -QSqlQuery GMDBManager::selectAllFromTable(QString tableName) const +std::unordered_map<std::string, unsigned int> GMDBManager::getAll_NodeTypesTableIDsStd() { - QSqlQuery q; +// QHash<QString, unsigned int> output; + std::unordered_map<std::string, unsigned int> output; + + QSqlQuery q = selectAllFromTable("GeoNodesTypes"); + + unsigned int id; + QString nodeType; + // QString tableName; + while (q.next()) { + id = q.value(0).toUInt(); + nodeType = q.value(1).toString(); + output[nodeType.toStdString()] = id; + } + return output; +} - QString queryStr = QString("SELECT * FROM %1"); - queryStr = queryStr.arg(tableName); - if (!q.prepare(queryStr)) { - showError(q.lastError()); - return QSqlQuery(); - } - q.exec(); - return q; +QSqlQuery GMDBManager::selectAllFromTable(QString tableName) const +{ +// QSqlQuery q; +// +// QString queryStr = QString("SELECT * FROM %1 ORDER BY id"); +// queryStr = queryStr.arg(tableName); +// +// if (!q.prepare(queryStr)) { +// showError(q.lastError()); +// return QSqlQuery(); +// } +// q.exec(); +// return q; + + return selectAllFromTableSortBy(tableName, "id"); +} + +QSqlQuery GMDBManager::selectAllFromTableSortBy(QString tableName, std::string sortColumn) const +{ + QSqlQuery q; + + if ("" == sortColumn || 0 == sortColumn.size()) { + sortColumn = "id"; + } + QString qsortColumn = QString::fromStdString(sortColumn); + + QStringList args; + args << tableName << qsortColumn; + + QString queryStr = QString("SELECT * FROM %1 ORDER BY %2"); +// queryStr = queryStr.arg(tableName); + for (int i=0; i < args.size(); ++i) { + queryStr = queryStr.arg( args.at(i) ); + } + + if (!q.prepare(queryStr)) { + showError(q.lastError()); + return QSqlQuery(); + } + q.exec(); + return q; } + bool GMDBManager::initDB() { // JFB commented: qDebug() << "GMDBManager::initDB()"; @@ -1215,7 +1414,7 @@ bool GMDBManager::createTables() // PhysVols table geoNode = "GeoPhysVol"; tableName = "PhysVols"; - tab << tableName << "id" << "logvol" << "parent"; // FIXME: remove "parent" field, it is not used anymore + tab << tableName << "id" << "logvol" << "parent"; // FIXME: remove "parent" field, it is not used anymore and it's not reliable since it's not using the tableID. storeTableColumnNames(tab); tab.clear(); m_childType_tableName[geoNode] = tableName; // store type-table relation @@ -1548,6 +1747,21 @@ QStringList GMDBManager::getRootPhysVol() return getItemAndType(typeId.toUInt(), id.toUInt()); } +std::vector<std::string> GMDBManager::getRootPhysVolStd() +{ + // get the ID of the ROOT vol from the table "RootVolume" + QSqlQuery query = selectAllFromTable("RootVolume"); + + unsigned int id; + unsigned int typeId; + while (query.next()) { + id = query.value(1).toUInt(); + typeId = query.value(2).toUInt(); + } + return getItemAndTypeStd(typeId, id); +} + + QString GMDBManager::getDBFilePath() { return m_dbpath; diff --git a/GeoModelRead/CMakeLists.txt b/GeoModelRead/CMakeLists.txt index e436dffdd7a035f830d0eaf564cddeea6700da1d..a06cf38fbe04ce6c8bbb772b06339bce1e25671f 100644 --- a/GeoModelRead/CMakeLists.txt +++ b/GeoModelRead/CMakeLists.txt @@ -23,9 +23,10 @@ set_target_properties( GeoModelRead PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR} ) if( "${CMAKE_BUILD_TYPE}" STREQUAL "Release" OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo" ) - target_compile_definitions( GeoModelRead PUBLIC -DQT_NO_DEBUG_OUTPUT ) + target_compile_definitions( GeoModelRead PUBLIC QT_NO_DEBUG_OUTPUT ) endif() + # Install the library. install( TARGETS GeoModelRead EXPORT ${PROJECT_NAME}-export diff --git a/GeoModelRead/GeoModelRead/ReadGeoModel.h b/GeoModelRead/GeoModelRead/ReadGeoModel.h index 114575fb94225e88d41a0ce8fc4571fc110a15c3..5d0baa9b37af07fde2629227be44e481e03ab85c 100644 --- a/GeoModelRead/GeoModelRead/ReadGeoModel.h +++ b/GeoModelRead/GeoModelRead/ReadGeoModel.h @@ -4,22 +4,54 @@ * Created on: May 20, 2016 * Author: Riccardo Maria BIANCHI <riccardo.maria.bianchi@cern.ch> * - * major updates: Feb 2019 rbianchi + * major updates: + * - 2019 Feb, R.M.Bianchi + * - 2020 May, R.M.Bianchi */ #ifndef GeoModelRead_ReadGeoModel_H_ #define GeoModelRead_ReadGeoModel_H_ + +// **************************************************************** +// *** PERSISTIFICATION SETTINGS FOR GeoModel BOOLEAN SHAPES ****** +// **************************************************************** +// Note: +// -- The instructions below are needed +// -- to correctly persistify/restore boolean shapes. +// -- They must be declared before including GeoModelKernel files. +// **************************************************************** +// This variable is used by GeoModel boolean shapes to switch ON +// the persistification mode +#define _GeoShapePersistification_On_ +// fwd declaration of namespace::class we want to use to persistify +// the GeoModel boolean shapes +namespace GeoModelIO { class ReadGeoModel; } +// the 'Persistifier' class name is used by GeoModel boolean shapes +// to grant access to friend classes +typedef GeoModelIO::ReadGeoModel Persistifier; +// **************************************************************** +// **************************************************************** + + +// local includes #include "GeoModelDBManager/GMDBManager.h" #include "GeoModelKernel/GeoXF.h" // C++ includes #include <string> #include <set> +#include <tuple> +#include <vector> +#include <deque> +#include <unordered_map> +#include <unordered_set> + // FWD declarations class GeoVPhysVol; class GeoPhysVol; +class GeoFullPhysVol; class GeoLogVol; class GeoShape; class GeoMaterial; @@ -30,11 +62,19 @@ class GeoAlignableTransform; class GeoTransform; class GeoSerialTransformer; class GeoGraphNode; +class GeoShapeSubtraction; +class GeoBox; +// namespaces using namespace GeoGenfun; -using namespace GeoXF; +//using namespace GeoXF; + -typedef const Function & TRANSFUNCTION; +// type definitions +typedef const GeoXF::Function & TRANSFUNCTION; +// containers for boolean shapes' information +typedef std::tuple<unsigned int/*shape ID*/, GeoShape*, unsigned int/*opA ID*/, unsigned int/*opB ID*/> tuple_shapes_boolean_info; +typedef std::vector<tuple_shapes_boolean_info> type_shapes_boolean_info; namespace GeoModelIO { @@ -49,54 +89,124 @@ public: private: - std::string getEnvVar( std::string const & key ) const; + void buildAllShapes(); + void buildAllElements(); + void buildAllMaterials(); + void buildAllLogVols(); + void buildAllPhysVols(); + void buildAllFullPhysVols(); + // void buildAllFunctions(); // FIXME: + void buildAllTransforms(); + void buildAllAlignableTransforms(); + void buildAllSerialDenominators(); + void buildAllSerialTransformers(); + void buildAllNameTags(); - GeoPhysVol* buildGeoModelByCalls(); - GeoPhysVol* buildGeoModelOneGo(); + - void loopOverAllChildren(QStringList keys); - void loopOverAllChildrenInBunches(); - void processParentChildren(const QString &parentKey); - void processChild(GeoVPhysVol* parentVol, bool &isRootVolume, const QStringList &child); + GeoPhysVol* buildGeoModelPrivate(); + + GeoBox* buildDummyShape(); + + void loopOverAllChildrenInBunches(); + void loopOverAllChildrenRecords(std::vector<std::vector<std::string>> records); + void processParentChild(const std::vector<std::string> &parentchild); GeoPhysVol* getRootVolume(); - GeoVPhysVol* parseChildren(GeoVPhysVol* vol, QMap<unsigned int, QStringList> children, int depth = 0); - GeoVPhysVol* parseVPhysVol(QStringList values, QString nodeType, int depth = 0); - GeoVPhysVol* buildVPhysVol(QString id, QString tableId, QString copyNumber); - GeoVPhysVol* buildNewVPhysVol(QString id, QString tableId, QString copyN); + GeoVPhysVol* buildVPhysVolInstance(const unsigned int id, const unsigned int tableId, const unsigned int copyNumber); + GeoVPhysVol* buildVPhysVol(const unsigned int id, const unsigned int tableId, unsigned int logVol_ID=0); + + GeoLogVol* buildLogVol(const unsigned int id); + GeoShape* buildShape(const unsigned int id, type_shapes_boolean_info* shapes_info_sub); + GeoMaterial* buildMaterial(const unsigned id); + GeoElement* buildElement(const unsigned int id); + GeoAlignableTransform* buildAlignableTransform(const unsigned int id); + GeoTransform* buildTransform(const unsigned int id); + GeoSerialTransformer* buildSerialTransformer(const unsigned int id); + TRANSFUNCTION buildFunction(const unsigned int id); + + + void checkNodePtr(GeoGraphNode* nodePtr, std::string varName="", std::string funcName="", std::string funcSignature="");//TODO: to be moved to an utility class + void volAddHelper(GeoVPhysVol* vol, GeoGraphNode* volChild); + + // methods for shapes + std::string getShapeType(const unsigned int shapeId); + bool isShapeOperator(const unsigned int shapeId); + bool isShapeOperator(const std::string type); + bool isShapeBoolean(const unsigned int shapeId); + bool isShapeBoolean(const std::string type); + void createBooleanShapeOperands(type_shapes_boolean_info* shapes_info_sub); + std::pair<unsigned int, unsigned int> getBooleanShapeOperands(const unsigned int shape); + GeoShape* addEmptyBooleanShapeForCompletion(const unsigned int shapeID, type_shapes_boolean_info* shapes_info_sub); + GeoShape* getBooleanReferencedShape(const unsigned int shapeID, type_shapes_boolean_info* shapes_info_sub); + + + // caching methods + bool isBuiltShape(const unsigned int id); + void storeBuiltShape(const unsigned int, GeoShape* node); + GeoShape* getBuiltShape(const unsigned int id); - GeoLogVol* buildLogVol(QString id); - GeoShape* buildShape(QString id); - GeoMaterial* buildMaterial(QString id); - GeoElement* buildElement(QString id); - GeoSerialDenominator* parseSerialDenominator(QStringList values); - GeoSerialDenominator* buildSerialDenominator(QString id); - GeoAlignableTransform* parseAlignableTransform(QStringList values); - GeoAlignableTransform* buildAlignableTransform(QString id); - GeoTransform* parseTransform(QStringList values); - GeoTransform* buildTransform(QString id); - GeoSerialTransformer* parseSerialTransformer(QStringList values); - GeoSerialTransformer* buildSerialTransformer(QString id); - TRANSFUNCTION parseFunction(const std::string& expr); - TRANSFUNCTION buildFunction(QString id); - GeoNameTag* parseNameTag(QStringList values); - GeoNameTag* buildNameTag(QString id); + bool isBuiltTransform(const unsigned int id); + void storeBuiltTransform(const unsigned int, GeoTransform* node); + GeoTransform* getBuiltTransform(const unsigned int id); + + bool isBuiltAlignableTransform(const unsigned int id); + void storeBuiltAlignableTransform(const unsigned int, GeoAlignableTransform* node); + GeoAlignableTransform* getBuiltAlignableTransform(const unsigned int id); - GeoShape* getShape(QString id); + void storeVPhysVol(const unsigned int id, const unsigned int tableId, const unsigned int copyNumber, GeoGraphNode* node); + GeoGraphNode* getVPhysVol(const unsigned int id, const unsigned int tableId, const unsigned int copyNumber); - void volAddHelper(GeoVPhysVol* vol, GeoGraphNode* volChild); + bool isBuiltLog(const unsigned int id); + void storeBuiltLog(const unsigned int id, GeoLogVol* nodePtr); + GeoLogVol* getBuiltLog(const unsigned int id); - bool isNodeBuilt(const QString id, const QString tableId, const QString copyNumber); - GeoGraphNode* getNode(const QString id, const QString tableId, const QString copyNumber); - void storeNode(const QString id, const QString tableId, const QString copyNumber, GeoGraphNode* node); + bool isBuiltMaterial(const unsigned int id); + void storeBuiltMaterial(const unsigned int id, GeoMaterial* nodePtr); + GeoMaterial* getBuiltMaterial(const unsigned int id); - void checkInputString(QString input); + bool isBuiltElement(const unsigned int id); + void storeBuiltElement(const unsigned int id, GeoElement* nodePtr); + GeoElement* getBuiltElement(const unsigned int id); + bool isBuiltFunction(const unsigned int id); + void storeBuiltFunction(const unsigned int id, GeoXF::Function* nodePtr); + GeoXF::Function* getBuiltFunction(const unsigned int id); + + bool isBuiltPhysVol(const unsigned int id); + void storeBuiltPhysVol(const unsigned int id, GeoPhysVol* nodePtr); + GeoPhysVol* getBuiltPhysVol(const unsigned int id); + + bool isBuiltFullPhysVol(const unsigned int id); + void storeBuiltFullPhysVol(const unsigned int id, GeoFullPhysVol* nodePtr); + GeoFullPhysVol* getBuiltFullPhysVol(const unsigned int id); + + bool isBuiltSerialDenominator(const unsigned int id); + void storeBuiltSerialDenominator(const unsigned int id, GeoSerialDenominator* nodePtr); + GeoSerialDenominator* getBuiltSerialDenominator(const unsigned int id); + + bool isBuiltNameTag(const unsigned int id); + void storeBuiltNameTag(const unsigned int id, GeoNameTag* nodePtr); + GeoNameTag* getBuiltNameTag(const unsigned int id); + + bool isBuiltSerialTransformer(const unsigned int id); + void storeBuiltSerialTransformer(const unsigned int id, GeoSerialTransformer* nodePtr); + GeoSerialTransformer* getBuiltSerialTransformer(const unsigned int id); + + + // Utility functions + std::string getEnvVar( std::string const & key ) const; + std::vector<std::string> splitString(const std::string& s, char delimiter); void printTrf(GeoTrf::Transform3D t); - void printTransformationValues(QStringList t); - QList<double> convertQstringListToDouble(QStringList listin); + void printStdVectorStrings(std::vector<std::string> vec); //TODO: move it to utility class + + // void printTransformationValues(QStringList t); // TODO: move to a Qt utility class + // QList<double> convertQstringListToDouble(QStringList listin); // TODO: move to a Qt utility class + // std::vector<std::string> toStdVectorStrings(QStringList qlist); // TODO: move to a Qt utility class + // QStringList toQStringList(std::vector<std::string> vec); // TODO: move to a Qt utility class + // void checkInputString(QString input); // TODO: move to an utility class // input arguments @@ -104,34 +214,52 @@ private: GMDBManager* m_dbManager; bool m_deepDebug; bool m_debug; + bool m_timing; bool m_runMultithreaded; - unsigned int m_runMultithreaded_nThreads; + int m_runMultithreaded_nThreads; // callback handles unsigned long* m_progress; - // data containers - QHash<QString, QMap<unsigned int, QStringList>> m_allchildren; // key = "parentId:parentTable", item = list of children parameters, inserted by child position - QHash<unsigned int, QStringList> m_physVols; - QHash<unsigned int, QStringList> m_fullPhysVols; - QHash<unsigned int, QStringList> m_logVols; - QHash<unsigned int, QStringList> m_shapes; - QHash<unsigned int, QStringList> m_materials; - QHash<unsigned int, QStringList> m_elements; - QHash<unsigned int, QStringList> m_transforms; - QHash<unsigned int, QStringList> m_alignableTransforms; - QHash<unsigned int, QStringList> m_serialDenominators; - QHash<unsigned int, QStringList> m_serialTransformers; - QHash<unsigned int, QStringList> m_functions; - QHash<unsigned int, QStringList> m_nameTags; - - QHash<unsigned int, QString> m_tableid_tableName; - - QStringList m_root_vol_data; - - QHash<QString, GeoGraphNode*> m_memMap; - - std::set<std::string> m_unknown_shapes; + //! containers to store the list of GeoModel nodes coming from the DB + std::vector<std::vector<std::string>> m_physVols; + std::vector<std::vector<std::string>> m_fullPhysVols; + std::vector<std::vector<std::string>> m_transforms; + std::vector<std::vector<std::string>> m_alignableTransforms; + std::vector<std::vector<std::string>> m_serialDenominators; + std::vector<std::vector<std::string>> m_serialTransformers; + std::vector<std::vector<std::string>> m_nameTags; + std::vector<std::vector<std::string>> m_logVols; + 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_functions; + std::vector<std::vector<std::string>> m_allchildren; + + std::unordered_map<unsigned int, std::string> m_tableID_toTableName; // to look for node's type name starting from a table ID + std::unordered_map<std::string, unsigned int> m_tableName_toTableID; // to look for table ID starting from node's type name + + +// QStringList m_root_vol_data; + std::vector<std::string> m_root_vol_data; + + //! memory chaches + std::vector<GeoPhysVol*> m_memMapPhysVols; + std::vector<GeoFullPhysVol*> m_memMapFullPhysVols; + std::vector<GeoTransform*> m_memMapTransforms; + std::vector<GeoAlignableTransform*> m_memMapAlignableTransforms; + std::vector<GeoSerialDenominator*> m_memMapSerialDenominators; + std::vector<GeoSerialTransformer*> m_memMapSerialTransformers; + std::vector<GeoNameTag*> m_memMapNameTags; + std::vector<GeoLogVol*> m_memMapLogVols; + std::vector<GeoMaterial*> m_memMapMaterials; + std::vector<GeoElement*> m_memMapElements; + // std::vector<GeoXF::Function*> m_memMapFunctions; // FIXME: + std::unordered_map<unsigned int, GeoShape*> m_memMapShapes; // we need keys, because shapes are not built following the ID order + std::unordered_map<std::string, GeoGraphNode*> m_memMap; // we need keys, to keep track of the volume's copyNumber + + //! container to store unknown shapes + std::set<std::string> m_unknown_shapes; }; diff --git a/GeoModelRead/src/ReadGeoModel.cpp b/GeoModelRead/src/ReadGeoModel.cpp index 96427878528ebfb692bee7e98611df3c4665bf56..1423ba3b2a2ac549777e9fd4bb04391177c95b44 100644 --- a/GeoModelRead/src/ReadGeoModel.cpp +++ b/GeoModelRead/src/ReadGeoModel.cpp @@ -8,8 +8,10 @@ * - Feb 2019, R.M.Bianchi * - Mar 2020, R.M.Bianchi * - Mar 2020, boudreau + * - May 2020, R.M.Bianchi */ + // local includes #include "GeoModelRead/ReadGeoModel.h" @@ -61,23 +63,22 @@ // TODO: we should get rid of VP1Base::VP1Msg dependency, since GeoModelRead should not depend on VP1 packages. Maybe we can move VP1Msg to a standalone package. //#include "VP1Base/VP1Msg.h" -// Qt includes -#include <QDebug> -#include <QString> - // C++ includes -#include <stdlib.h> /* exit, EXIT_FAILURE */ -#include <vector> /* exit, EXIT_FAILURE */ +#include <stdlib.h> /* exit, EXIT_FAILURE */ #include <stdexcept> #include <future> #include <mutex> -#include <chrono> -#include <cstdlib> /* std::getenv */ +#include <chrono> /* system_clock */ +#include <ctime> /* std::time */ +#include <cstdlib> /* std::getenv */ +#include <vector> +#include <unordered_map> +#include <unordered_set> +#include <thread> -// mutexes for the multi-threading mode -std::mutex muxStore; -std::mutex muxGet; +// mutexes for synchronized access to containers and output streams in multi-threading mode +std::mutex muxVPhysVol; std::mutex muxCout; @@ -85,23 +86,27 @@ using namespace GeoGenfun; using namespace GeoXF; - - namespace GeoModelIO { -ReadGeoModel::ReadGeoModel(GMDBManager* db, unsigned long* progress) : m_progress(nullptr), m_deepDebug(false), m_debug(false), m_runMultithreaded(true), m_runMultithreaded_nThreads(0) +ReadGeoModel::ReadGeoModel(GMDBManager* db, unsigned long* progress) : m_deepDebug(false), + m_debug(false), m_timing(false), m_runMultithreaded(false), + m_runMultithreaded_nThreads(0), m_progress(nullptr) { - qDebug() << "===> DumpGeoModelAction: constructor"; - - #ifdef GEOMODELREAD_DEEP_DEBUG - m_deepDebug = true; - std::cout << "You defined the GEOMODELREAD_DEEP_DEBUG variable, so you will see a verbose output." << std::endl; - #endif - #ifdef GEOMODELREAD_DEBUG - m_debug = true; - std::cout << "You defined the GEOMODELREAD_DEBUG variable, so you will see a verbose output." << std::endl; - #endif - // m_deepDebug = true; + // Check if the user asked for running in serial or multi-threading mode + if ( "" != getEnvVar("GEOMODEL_ENV_IO_DEBUG")) { + m_debug = true; + std::cout << "You defined the GEOMODEL_IO_DEBUG variable, so you will see a verbose output." << std::endl; + } + // Check if the user asked for running in serial or multi-threading mode + if ( "" != getEnvVar("GEOMODEL_ENV_IO_DEBUG_VERBOSE")) { + m_deepDebug = true; + std::cout << "You defined the GEOMODEL_IO_DEBUG_VERBOSE variable, so you will see a verbose output." << std::endl; + } + // Check if the user asked for running in serial or multi-threading mode + if ( "" != getEnvVar("GEOMODEL_ENV_IO_TIMING")) { + m_timing = true; + std::cout << "You defined the GEOMODEL_IO_TIMING variable, so you will see a timing measurement in the output." << std::endl; + } if ( progress != nullptr) { m_progress = progress; @@ -110,38 +115,43 @@ ReadGeoModel::ReadGeoModel(GMDBManager* db, unsigned long* progress) : m_progres // set the geometry file m_dbManager = db; if (m_dbManager->isOpen()) { - if (m_deepDebug) qDebug() << "OK! Database is open!"; + if (m_deepDebug) std::cout << "OK! Database is open!"; } else { - qWarning() << "ERROR!! Database is NOT open!"; + std::cout << "ERROR!! Database is NOT open!"; return; } - // Check if the user asked for running in multi-threading mode - if( "1" == getEnvVar("GEOMODEL_GEOMODELIO_NOT_MULTITHREADED") ) { - std::cout << "\nYou set the GEOMODEL_GEOMODELIO_NOT_MULTITHREADED to '1', so we will run GeoModelIO in serial mode, without using worker threads. If you experience problems, please ask to the GeoModel developers ( --> geomodel-developers<at>cern.ch )\n\n"; - m_runMultithreaded = false; + // Check if the user asked for running in serial or multi-threading mode + if ( "" != getEnvVar("GEOMODEL_ENV_IO_NTHREADS")) + { + int nThreads = std::stoi(getEnvVar("GEOMODEL_ENV_IO_NTHREADS")); + if (nThreads == 0) { + std::cout << "\nYou set the GEOMODEL_ENV_IO_NTHREADS to " << nThreads << "; thus, GeoModelIO will be run in serial mode." << std::endl; + m_runMultithreaded_nThreads = nThreads; + m_runMultithreaded = false; + } else if( nThreads > 0 ) { + std::cout << "\nYou set the GEOMODEL_ENV_IO_NTHREADS to " << nThreads << "; thus, GeoModelIO will use that number of worker threads." << std::endl; + m_runMultithreaded_nThreads = nThreads; + m_runMultithreaded = true; + } else if (nThreads == -1) { + std::cout << "\nYou set the GEOMODEL_ENV_IO_NTHREADS to " << nThreads << "; thus, GeoModelIO will use the number of threads supported by the platform." << std::endl; + m_runMultithreaded_nThreads = nThreads; + m_runMultithreaded = true; } - // Check if the user asked for running in multi-threading mode - if ( "" != getEnvVar("GEOMODEL_GEOMODELIO_NTHREADS")) { - unsigned int nThreads = std::stoi(getEnvVar("GEOMODEL_GEOMODELIO_NTHREADS")); - if( nThreads > 0 ) { - std::cout << "\nYou set the GEOMODEL_GEOMODELIO_NTHREADS to " << nThreads << "; thus, that number of worker threads will be used." << std::endl; - m_runMultithreaded_nThreads = nThreads; - } else if (nThreads == 0) { - std::cout << "\nYou set the GEOMODEL_GEOMODELIO_NTHREADS to " << nThreads << "; thus, GeoModelIO will be run in serial mode." << std::endl; - m_runMultithreaded_nThreads = nThreads; - m_runMultithreaded = false; - } - } - - + } + // if the user did not specify the number of threads, then we just use the hardware-supported number of threads on the platform + else { + m_runMultithreaded_nThreads = -1; + m_runMultithreaded = true; + } } ReadGeoModel::~ReadGeoModel() { - // TODO Auto-generated destructor stub + // FIXME: some cleaning...?? } + std::string ReadGeoModel::getEnvVar( std::string const & key ) const { char * val = std::getenv( key.c_str() ); @@ -150,14 +160,13 @@ std::string ReadGeoModel::getEnvVar( std::string const & key ) const GeoPhysVol* ReadGeoModel::buildGeoModel() { - if (m_deepDebug) qDebug() << "ReadGeoModel::buildGeoModel()"; + if (m_deepDebug) std::cout << "ReadGeoModel::buildGeoModel()" << std::endl; - // return buildGeoModelByCalls(); - GeoPhysVol* rootVolume = buildGeoModelOneGo(); + GeoPhysVol* rootVolume = buildGeoModelPrivate(); // warn the user if there are unknown/unhalded shapes if (m_unknown_shapes.size() > 0) { - qWarning() << "\tWARNING!! There were unknwon shapes:"; + std::cout << "\tWARNING!! There were unknwon shapes:" << std::endl; for ( auto it = m_unknown_shapes.begin(); it != m_unknown_shapes.end(); it++ ) { std::cout << "\t\t---> " << *it << std::endl; } @@ -168,256 +177,487 @@ GeoPhysVol* ReadGeoModel::buildGeoModel() } -GeoPhysVol* ReadGeoModel::buildGeoModelOneGo() +GeoPhysVol* ReadGeoModel::buildGeoModelPrivate() { - if (m_deepDebug) qDebug() << "ReadGeoModel::buildGeoModelOneGo()"; - - // get all objects from the DB - m_physVols = m_dbManager->getTableFromNodeType("GeoPhysVol"); - m_fullPhysVols = m_dbManager->getTableFromNodeType("GeoFullPhysVol"); - m_logVols = m_dbManager->getTableFromNodeType("GeoLogVol"); - m_shapes = m_dbManager->getTableFromNodeType("GeoShape"); - m_materials = m_dbManager->getTableFromNodeType("GeoMaterial"); - m_elements = m_dbManager->getTableFromNodeType("GeoElement"); - m_functions = m_dbManager->getTableFromNodeType("Function"); - m_serialDenominators = m_dbManager->getTableFromNodeType("GeoSerialDenominator"); - m_serialTransformers = m_dbManager->getTableFromNodeType("GeoSerialTransformer"); - m_alignableTransforms = m_dbManager->getTableFromNodeType("GeoAlignableTransform"); - m_transforms = m_dbManager->getTableFromNodeType("GeoTransform"); - m_nameTags = m_dbManager->getTableFromNodeType("GeoNameTag"); - // qDebug() << "physVols: " << _physVols; - // qDebug() << "fullPhysVols: " << _fullPhysVols; - - // get DB metadata - m_tableid_tableName = m_dbManager->getAll_TableIDsNodeTypes(); - - // get the children table from DB - m_allchildren = m_dbManager->getChildrenTable(); - // qDebug() << "all children from DB:" << _allchildren; - + // *** get all data from the DB *** + + // get all GeoModel nodes from the DB + m_logVols = m_dbManager->getTableFromNodeTypeStd("GeoLogVol"); + m_shapes = m_dbManager->getTableFromNodeTypeStd("GeoShape"); + m_materials = m_dbManager->getTableFromNodeTypeStd("GeoMaterial"); + m_elements = m_dbManager->getTableFromNodeTypeStd("GeoElement"); + m_functions = m_dbManager->getTableFromNodeTypeStd("Function"); + m_physVols = m_dbManager->getTableFromNodeTypeStd("GeoPhysVol"); + m_fullPhysVols = m_dbManager->getTableFromNodeTypeStd("GeoFullPhysVol"); + m_transforms = m_dbManager->getTableFromNodeTypeStd("GeoTransform"); + m_alignableTransforms = m_dbManager->getTableFromNodeTypeStd("GeoAlignableTransform"); + m_serialDenominators = m_dbManager->getTableFromNodeTypeStd("GeoSerialDenominator"); + m_serialTransformers = m_dbManager->getTableFromNodeTypeStd("GeoSerialTransformer"); + m_nameTags = m_dbManager->getTableFromNodeTypeStd("GeoNameTag"); + + // get the children table from DB + m_allchildren = m_dbManager->getChildrenTableStd(); + // get the root volume data - m_root_vol_data = m_dbManager->getRootPhysVol(); - - // return loopOverAllChildren(); - - loopOverAllChildrenInBunches(); +// m_root_vol_data = m_dbManager->getRootPhysVol(); + m_root_vol_data = m_dbManager->getRootPhysVolStd(); + + // get DB metadata + m_tableID_toTableName = m_dbManager->getAll_TableIDsNodeTypesStd(); + m_tableName_toTableID = m_dbManager->getAll_NodeTypesTableIDsStd(); + + + // *** build all nodes *** + + // parallel mode: + + if (m_runMultithreaded) { + std::cout << "Building nodes concurrently..." << std::endl; + std::thread t2(&ReadGeoModel::buildAllElements, this); +// std::thread t7(&ReadGeoModel::buildAllFunctions, this); + + std::thread t8(&ReadGeoModel::buildAllTransforms, this); + std::thread t9(&ReadGeoModel::buildAllAlignableTransforms, this); + std::thread t10(&ReadGeoModel::buildAllSerialDenominators, this); + std::thread t11(&ReadGeoModel::buildAllNameTags, this); + + t8.join(); // ok, all Transforms have been built + t9.join(); // ok, all AlignableTransforms have been built + // needs Transforms and AlignableTransforms for Shift boolean shapes + std::thread t1(&ReadGeoModel::buildAllShapes, this); + + t2.join(); // ok, all Elements have been built + // needs Elements + std::thread t3(&ReadGeoModel::buildAllMaterials, this); + + t1.join(); // ok, all Shapes have been built + t3.join(); // ok, all Materials have been built + // needs Shapes and Materials + std::thread t4(&ReadGeoModel::buildAllLogVols, this); + + t4.join(); // ok, all LogVols have been built + // needs LogVols + std::thread t5(&ReadGeoModel::buildAllPhysVols, this); + std::thread t6(&ReadGeoModel::buildAllFullPhysVols, this); + + t5.join(); // ok, all PhysVols have been built + t6.join(); // ok, all FullPhysVols have been built +// t7.join(); // ok, all Functions have been built + // needs Functions, PhysVols, FullPhysVols + std::thread t12(&ReadGeoModel::buildAllSerialTransformers, this); + + t10.join(); // ok, all SerialDenominators have been built + t11.join(); // ok, all NameTags have been built + t12.join(); // ok, all SerialTransformers have been built + } + // serial mode: + else { + std::cout << "Building nodes serially..." << std::endl; + buildAllElements(); + // buildAllFunctions(); + buildAllTransforms(); + buildAllAlignableTransforms(); + buildAllSerialDenominators(); + buildAllNameTags(); + buildAllShapes(); + buildAllMaterials(); + buildAllLogVols(); + buildAllPhysVols(); + buildAllFullPhysVols(); + buildAllSerialTransformers(); + } + loopOverAllChildrenInBunches(); return getRootVolume(); - } //---------------------------------------- -// Serial loop over parents' children -void ReadGeoModel::loopOverAllChildren(QStringList keys) +// loop over parent-child relationship data + void ReadGeoModel::loopOverAllChildrenRecords(std::vector<std::vector<std::string>> records) { - - int nChildrenRecords = keys.size(); - + + int nChildrenRecords = records.size(); + if (m_debug || m_deepDebug) { muxCout.lock(); - std::cout << "Thread " << std::this_thread::get_id() << " - processing " << nChildrenRecords << " keys..." << std::endl; - muxCout.unlock(); + std::cout << "Thread " << std::this_thread::get_id() << " - processing " << nChildrenRecords << " keys..." << std::endl; + muxCout.unlock(); } - - // loop over parents' keys. - // It returns a list of children with positions (sorted by position) - - // Get Start Time - std::chrono::system_clock::time_point start = std::chrono::system_clock::now(); - - foreach (const QString &parentKey, keys ) { - // qWarning() << "parentKey" << parentKey; - processParentChildren(parentKey); - } // end loop over parent's childrenPositions records - - // Get End Time - auto end = std::chrono::system_clock::now(); - auto diff = std::chrono::duration_cast < std::chrono::seconds > (end - start).count(); - - if (m_debug || m_deepDebug) { + + // 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_debug || m_deepDebug) { muxCout.lock(); - std::cout << "Thread " << std::this_thread::get_id() << " -- Time Taken to process " << nChildrenRecords << " children = " << diff << " Seconds" << std::endl; - muxCout.unlock(); + std::cout << "Time Taken to process " << nChildrenRecords << " parent-child relationships = " << diff << " Seconds" << std::endl; + muxCout.unlock(); } } -//---------------------------------------- -// Parallel loop over parents' children -void ReadGeoModel::loopOverAllChildrenInBunches() -{ - int nChildrenRecords = m_allchildren.size(); - if (m_debug) std::cout << "number of children to process: " << nChildrenRecords << std::endl; - - // If we have a few children, then process them serially - if ( !(m_runMultithreaded) || nChildrenRecords <= 500) - { - loopOverAllChildren(m_allchildren.keys()); - } - // ...otherwise, lets spawn some threads to process them in bunches, parallelly! - else { - // Get Start Time - std::chrono::system_clock::time_point start = std::chrono::system_clock::now(); - - unsigned int nThreadsPlatform = std::thread::hardware_concurrency(); - unsigned int nThreads = nThreadsPlatform * 2; - if (m_debug) std::cout << "INFO - You are running with multiple threads. On this platform, " << nThreadsPlatform << " concurrent threads are supported. Using " << nThreads << " threads.\n"; - - unsigned int nBunches = nChildrenRecords / nThreads; - if (m_debug) 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 ) { - - unsigned int start = nBunches * bb; - int len = nBunches; - if ( bb == (nThreads - 1) ) len = -1; // '-1' means for mid() all the elements starting from 'start' - - if (m_debug || m_deepDebug) { - muxCout.lock(); - std::cout << "Thread " << bb+1 << " - Start: " << start << ", len: " << len << " ['len=-1' = all remaining items]" << std::endl; - muxCout.unlock(); - } - - QStringList bunch = QStringList((m_allchildren.keys()).mid(start,len)); - if (m_debug || m_deepDebug) { - muxCout.lock(); - std::cout << "'bunch' size: " << bunch.size() << std::endl; - muxCout.unlock(); - } - - // loopOverAllChildren(bunch); - futures.push_back( std::async(std::launch::async, &ReadGeoModel::loopOverAllChildren, this, bunch) ); - } - - // wait for all async calls to complete - //retrieve and print the value stored in the future - muxCout.lock(); - if (m_debug) std::cout << "Waiting for the threads to finish..." << std::flush; - muxCout.unlock(); - for(auto &e : futures) { - e.wait(); - } - muxCout.lock(); - if (m_debug) std::cout << "Done!\n"; - muxCout.unlock(); - - // Get End Time - auto 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; +//! Iterate over the list of shapes, build them all, and store their pointers +void ReadGeoModel::buildAllShapes() +{ + if (m_debug) std::cout << "Building all shapes...\n"; + size_t nSize = m_shapes.size(); + m_memMapShapes.reserve( nSize*2 ); // TODO: check if *2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int shapeID = std::stoi(m_shapes[ii][0]); + type_shapes_boolean_info shapes_info_sub; // tuple to store the boolean shapes to complete at a second stage + buildShape(shapeID, &shapes_info_sub); + createBooleanShapeOperands(&shapes_info_sub); + } + if (nSize>0) std::cout << "All " << nSize << " Shapes have been built!\n"; } -void ReadGeoModel::processParentChildren(const QString &parentKey) +//! Iterate over the list of GeoSerialDenominator nodes, build them all, and store their pointers +void ReadGeoModel::buildAllSerialDenominators() { - if (m_deepDebug) qDebug() << "\n" << "parent: " << parentKey << ':' << m_allchildren.value(parentKey) << "[parentId, parentType, parentCopyNumber, childPos, childType, childId, childCopyN]"; - - // get the parent's details - QStringList parentKeyItems = parentKey.split(":"); - QString parentId = parentKeyItems[0]; - QString parentTableId = parentKeyItems[1]; - QString parentCopyN = parentKeyItems[2]; - if (m_deepDebug) qDebug() << "parent ID:" << parentId << ", parent table ID:" << parentTableId << ", parent copy number:" << parentCopyN; - - bool isRootVolume = false; - if (parentId == "NULL") { - isRootVolume = true; - } - - GeoVPhysVol* parentVol = nullptr; - - // build or get parent volume. - // Using the parentCopyNumber here, to get a given instance of the parent volume - if (!isRootVolume) { - if (m_deepDebug) qDebug() << "get the parent volume..."; - parentVol = buildVPhysVol( parentId, parentTableId, parentCopyN); - } - - // get the parent's children - QMap<unsigned int, QStringList> children = m_allchildren.value(parentKey); - // qWarning() << "children" << children; - - // loop over children, sorted by child position automatically - // "id", "parentId", "parentTable", "parentCopyNumber", "position", "childTable", "childId", "childCopyNumber" - if (m_deepDebug) qDebug() << "parent volume has " << children.size() << "children. Looping over them..."; - foreach(QStringList child, children) { - processChild(parentVol, isRootVolume, child); - } // loop over all children + if (m_debug) std::cout << "Building all SerialDenominator nodes...\n"; + size_t nSize = m_serialDenominators.size(); + m_memMapSerialDenominators.reserve( nSize*2 ); // TODO: check if *2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int nodeID = std::stoi(m_serialDenominators[ii][0]); + const std::string baseName = m_serialDenominators[ii][1]; + GeoSerialDenominator* nodePtr = new GeoSerialDenominator(baseName); + storeBuiltSerialDenominator(nodeID, nodePtr); + } + if (nSize>0) std::cout << "All " << nSize << " SerialDenominators have been built!\n"; +} + +//! Iterate over the list of GeoSerialDenominator nodes, build them all, and store their pointers +void ReadGeoModel::buildAllNameTags() +{ + if (m_debug) std::cout << "Building all NameTag nodes...\n"; + size_t nSize = m_nameTags.size(); + m_memMapNameTags.reserve( nSize*2 ); // TODO: check if *2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int nodeID = std::stoi(m_nameTags[ii][0]); + const std::string baseName = m_nameTags[ii][1]; + GeoNameTag* nodePtr = new GeoNameTag(baseName); + storeBuiltNameTag(nodeID, nodePtr); + } + if (nSize>0) std::cout << "All " << nSize << " NameTags have been built!\n"; +} + +//! Iterate over the list of nodes, build them all, and store their pointers +void ReadGeoModel::buildAllElements() +{ + if (m_debug) std::cout << "Building all Elements...\n"; + size_t nSize = m_elements.size(); + m_memMapElements.reserve( nSize*2 ); // TODO: check if *2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int nodeID = std::stoi(m_elements[ii][0]); + buildElement(nodeID); // nodes' IDs start from 1 + } + if (nSize>0) std::cout << "All " << nSize << " Elements have been built!\n"; +} + +//! Iterate over the list of nodes, build them all, and store their pointers +void ReadGeoModel::buildAllMaterials() +{ + if (m_debug) std::cout << "Building all Materials...\n"; + size_t nSize = m_materials.size(); + m_memMapMaterials.reserve( nSize*2 ); // TODO: check if *2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int nodeID = std::stoi(m_materials[ii][0]); + buildMaterial(nodeID); // nodes' IDs start from 1 + } + if (nSize>0) std::cout << "All " << nSize << " Materials have been built!\n"; +} + +//! Iterate over the list of nodes, build them all, and store their pointers +void ReadGeoModel::buildAllLogVols() +{ + if (m_debug) std::cout << "Building all LogVols...\n"; + size_t nSize = m_logVols.size(); + m_memMapLogVols.reserve( nSize*2 ); // TODO: check if *2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int nodeID = std::stoi(m_logVols[ii][0]); + buildLogVol(nodeID); + } + if (nSize>0) std::cout << "All " << nSize << " LogVols have been built!\n"; } + //// FIXME: TODO: move to an utility class +//void printQHashQstringUInt(QHash<QString, unsigned int> qq) { +// QHash<QString, unsigned int>::const_iterator i = qq.constBegin(); +// while (i != qq.constEnd()) { +// std::cout << i.key().toStdString() << ": " << i.value() << std::endl; +// } +//} -// void ReadGeoModel::processChild(GeoVPhysVol* parentVol, bool& isRootVolume, const QStringList &child, std::mutex &mux) -void ReadGeoModel::processChild(GeoVPhysVol* parentVol, bool& isRootVolume, const QStringList &child) +//! Iterate over the list of nodes, build them all, and store their pointers +void ReadGeoModel::buildAllPhysVols() { - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "ReadGeoModel::processChild() - child:" << child; - muxCout.unlock(); + if (m_debug) std::cout << "Building all PhysVols...\n"; + if (m_physVols.size() == 0) { + std::cout << "ERROR!!! No input PhysVols found! Exiting..." << std::endl; + exit(EXIT_FAILURE); + } + const unsigned int tableID = m_tableName_toTableID["GeoPhysVol"]; + size_t nSize = m_physVols.size(); + m_memMapPhysVols.reserve( nSize*2 ); // TODO: check if *2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int volID = std::stoi(m_physVols[ii][0]); + const unsigned int logVolID = std::stoi(m_physVols[ii][1]); + // std::cout << "building PhysVol n. " << volID << " (logVol: " << logVolID << ")" << std::endl; + buildVPhysVol(volID, tableID, logVolID); + } + if (nSize>0) std::cout << "All " << nSize << " PhysVols have been built!\n"; +} + +//! Iterate over the list of nodes, build them all, and store their pointers +void ReadGeoModel::buildAllFullPhysVols() +{ + if (m_debug) std::cout << "Building all FullPhysVols...\n"; + const unsigned int tableID = m_tableName_toTableID["GeoFullPhysVol"]; + size_t nSize = m_fullPhysVols.size(); + m_memMapFullPhysVols.reserve( nSize*2 ); // TODO: check if *2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int volID = std::stoi(m_fullPhysVols[ii][0]); + const unsigned int logVolID = std::stoi(m_fullPhysVols[ii][1]); + // std::cout << "building PhysVol n. " << volID << " (logVol: " << logVolID << ")" << std::endl; + buildVPhysVol(volID, tableID, logVolID); + } + if (nSize>0) std::cout << "All " << nSize << " FullPhysVols have been built!\n"; +} + +//! Iterate over the list of GeoAlignableTransforms nodes, build them all, and store their pointers +void ReadGeoModel::buildAllAlignableTransforms() +{ + if (m_debug) std::cout << "Building all AlignableTransforms...\n"; + size_t nSize = m_alignableTransforms.size(); + m_memMapAlignableTransforms.reserve( nSize*2 ); // TODO: check if *2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int volID = std::stoi(m_alignableTransforms[ii][0]); + buildAlignableTransform(volID); + } + if (nSize>0) std::cout << "All " << nSize << " AlignableTransforms have been built!\n"; +} + +//! Iterate over the list of GeoTransforms nodes, build them all, and store their pointers +void ReadGeoModel::buildAllTransforms() +{ + if (m_debug) std::cout << "Building all Transforms...\n"; + size_t nSize = m_transforms.size(); + m_memMapTransforms.reserve( nSize*2 ); // TODO: check if *2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int volID = std::stoi(m_transforms[ii][0]); + buildTransform(volID); } + if (nSize>0) std::cout << "All " << nSize << " Transforms have been built!\n"; +} - if (child.length() < 8) { - std::cout << "ERROR!!! Probably you are using an old geometry file..." << std::endl; - exit(EXIT_FAILURE); - } +//! Iterate over the list of GeoTransforms nodes, build them all, and store their pointers +void ReadGeoModel::buildAllSerialTransformers() +{ + if (m_debug) std::cout << "Building all SerialTransformers...\n"; + size_t nSize = m_serialTransformers.size(); + m_memMapSerialTransformers.reserve( nSize*2 ); // TODO: check if 2 is good or redundant... + for (unsigned int ii=0; ii<nSize; ++ii) { + const unsigned int volID = std::stoi(m_serialTransformers[ii][0]); + buildSerialTransformer(volID); + } + if (nSize>0) std::cout << "All " << nSize << " SerialTransformers have been built!\n"; +} + +// //! Iterate over the list of nodes, build them all, and store their pointers +// void ReadGeoModel::buildAllFunctions() +// { +// if (m_debug) std::cout << "Building all Functions...\n"; +//// if (m_serialTransformers.size() == 0) { +//// std::cout << "ERROR!!! input SerialTransformers are empty! Exiting..." << std::endl; +//// exit(EXIT_FAILURE); +//// } +//// size_t nSize = m_functions.size(); +//// m_memMapFunctions.reserve( nSize*2 ); // TODO: check if 2 is good or redundant... +//// for (unsigned int ii=0; ii<nSize; ++ii) { +//// buildFunction(ii+1); // nodes' IDs start from 1 +//// } +// QHash<unsigned int, QStringList>::const_iterator i = m_functions.constBegin(); +// while (i != m_functions.constEnd()) { +// unsigned int id = i.key(); +// buildFunction(id); +// ++i; +// } +// std::cout << "All Functions have been built!\n"; +// } - // build or get child node - QString childTableId = child[5]; - QString childId = child[6]; - QString childCopyN = child[7]; - QString childNodeType = m_tableid_tableName[childTableId.toUInt()]; - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "childTableId:" << childTableId << ", type:" << childNodeType << ", childId:" << childId; - muxCout.unlock(); +void ReadGeoModel::loopOverAllChildrenInBunches() +{ + int nChildrenRecords = m_allchildren.size(); + if (m_debug) 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) + { + 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_debug || m_deepDebug) { + // 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_debug || m_deepDebug) 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_debug || m_deepDebug) 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; + if ( bb == (nThreads - 1) ) { // last bunch + bunch = std::vector<std::vector<std::string>>(first, m_allchildren.end()); +// bunch = bunchIn; + } else { // all bunches but last one + bunch = std::vector<std::vector<std::string>>(first, last); + } + + if (m_debug || m_deepDebug) { + muxCout.lock(); + std::cout << "Thread " << bb+1 << " - Start: " << start << ", len: " << len << " ['len=-1' = all remaining items]" << std::endl; + muxCout.unlock(); + } + + + if (m_debug || m_deepDebug) { + 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_debug || m_deepDebug) std::cout << "Waiting for the threads to finish...\n" << std::flush; + for(auto &e : futures) { + e.wait(); + } + if (m_debug || m_deepDebug) std::cout << "Done!\n"; + + if (m_timing || m_debug || m_deepDebug) { + // 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; } - if (childNodeType.isEmpty()) { - qWarning("ERROR!!! childNodeType is empty!!! Aborting..."); - exit(EXIT_FAILURE); - } + void ReadGeoModel::processParentChild(const std::vector<std::string> &parentchild) + { + // 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 = std::stoi(parentchild[1]); + const unsigned int parentTableId = std::stoi(parentchild[2]); + const unsigned int parentCopyN = std::stoi(parentchild[3]); + + // get the child's position in the parent's children list + const unsigned int position = std::stoi(parentchild[4]); + + // get the child's details + const unsigned int childTableId = std::stoi(parentchild[5]); + const unsigned int childId = std::stoi(parentchild[6]); + const unsigned int childCopyN = std::stoi(parentchild[7]); + +// std::string childNodeType = m_tableID_toTableName[childTableId].toStdString(); + std::string childNodeType = m_tableID_toTableName[childTableId]; + + if ( "" == childNodeType || 0 == childNodeType.size()) { + std::cout << "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 + parentVol = dynamic_cast<GeoVPhysVol*>( buildVPhysVolInstance(parentId, parentTableId, parentCopyN) ); + std::string parentName = parentVol->getLogVol()->getName(); + if (childNodeType == "GeoPhysVol") { - GeoVPhysVol* childNode = dynamic_cast<GeoPhysVol*>(buildVPhysVol(childId, childTableId, childCopyN)); - if (!isRootVolume) volAddHelper(parentVol, childNode); + GeoPhysVol* childNode = dynamic_cast<GeoPhysVol*>(buildVPhysVolInstance(childId, childTableId, childCopyN)); + volAddHelper(parentVol, childNode); } else if (childNodeType == "GeoFullPhysVol") { - GeoVPhysVol* childNode = dynamic_cast<GeoFullPhysVol*>(buildVPhysVol(childId, childTableId, childCopyN)); - if (!isRootVolume) volAddHelper(parentVol, childNode); + GeoFullPhysVol* childNode = dynamic_cast<GeoFullPhysVol*>(buildVPhysVolInstance(childId, childTableId, childCopyN)); + volAddHelper(parentVol, childNode); } else if (childNodeType == "GeoSerialDenominator") { - GeoSerialDenominator* childNode = buildSerialDenominator(childId); - if (!isRootVolume) volAddHelper(parentVol, childNode); + GeoSerialDenominator* childNode = getBuiltSerialDenominator(childId); + volAddHelper(parentVol, childNode); } else if (childNodeType == "GeoAlignableTransform") { - GeoAlignableTransform* childNode = buildAlignableTransform(childId); - if (!isRootVolume) volAddHelper(parentVol, childNode); + GeoAlignableTransform* childNode = getBuiltAlignableTransform(childId); + volAddHelper(parentVol, childNode); } else if (childNodeType == "GeoTransform") { - GeoTransform* childNode = buildTransform(childId); - if (!isRootVolume) volAddHelper(parentVol, childNode); + GeoTransform* childNode = getBuiltTransform(childId); + volAddHelper(parentVol, childNode); } else if (childNodeType == "GeoSerialTransformer") { - GeoSerialTransformer* childNode = buildSerialTransformer(childId); - if (!isRootVolume) volAddHelper(parentVol, childNode); + GeoSerialTransformer* childNode = getBuiltSerialTransformer(childId); + volAddHelper(parentVol, childNode); } else if (childNodeType == "GeoNameTag") { - GeoNameTag* childNode = buildNameTag(childId); - if (!isRootVolume) volAddHelper(parentVol, childNode); - } + GeoNameTag* childNode = getBuiltNameTag(childId); + volAddHelper(parentVol, childNode); + } else { - QString msg = "[" + childNodeType + "]" + QString(" ==> ERROR!!! - The conversion for this type of child node needs to be implemented, still!!!"); - qFatal("%s", msg.toLatin1().constData()); + 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__); + checkNodePtr(volChild, "volChild", __func__, __PRETTY_FUNCTION__); if (dynamic_cast<GeoPhysVol*>(vol)) { GeoPhysVol* volume = dynamic_cast<GeoPhysVol*>(vol); volume->add(volChild); @@ -427,250 +667,301 @@ void ReadGeoModel::volAddHelper(GeoVPhysVol* vol, GeoGraphNode* volChild) } } - -void ReadGeoModel::checkInputString(QString input) +//TODO: to be moved to an utility class +void ReadGeoModel::checkNodePtr(GeoGraphNode* nodePtr, std::string varName, std::string funcName, std::string funcSignature) { - if (input.isEmpty() || input.isNull() || input == "NULL") { - qWarning() << "ERROR!!! Input QString is empty or equal to 'NULL'!!! Aborting..."; - exit(1); - } + if (!nodePtr) { + muxCout.lock(); + std::cout << "ERROR! A pointer to a GeoGraphNode instance is NULL {"<<varName<<" "<<funcName<<" "<<"["<<funcSignature<<"]}. Exiting..." << std::endl; + exit(EXIT_FAILURE); + muxCout.unlock(); + } } + // TODO: move it to an utility class +//void ReadGeoModel::checkInputString(QString input) +//{ +// if (input.isEmpty() || input.isNull() || input == "NULL") { +// std::cout << "ERROR!!! Input QString is empty or equal to 'NULL'!!! Aborting..." << std::endl; +// exit(1); +// } +//} + // Instantiate a PhysVol and get its children -GeoVPhysVol* ReadGeoModel::buildVPhysVol(QString id, QString tableId, QString copyN) +GeoVPhysVol* ReadGeoModel::buildVPhysVolInstance(const unsigned int id, const unsigned int tableId, const unsigned int copyN) { if (m_deepDebug) { muxCout.lock(); - qDebug() << "ReadGeoModel::buildVPhysVol()" << id << tableId << copyN; + std::cout << "ReadGeoModel::buildVPhysVolInstance() - " << id << ", " << tableId << ", " << copyN << std::endl; muxCout.unlock(); } - checkInputString(id); - checkInputString(tableId); - - // if previously built, return that // TODO: apparently this is only used for VPhysVol... check that, because it should be used for all nodes, otherwise we create new nodes in memory even if we have a copy of them already... - if (isNodeBuilt(id, tableId, copyN)) { + // A - if the instance has been previously built, return that + if ( nullptr != getVPhysVol(id, tableId, copyN)) { +// if (isVPhysVolBuilt(id, tableId, copyN)) { if (m_deepDebug) { muxCout.lock(); - qDebug() << "getting the volume from memory..."; + std::cout << "getting the instance volume from memory..." << std::endl; muxCout.unlock(); } - return dynamic_cast<GeoVPhysVol*>(getNode(id, tableId, copyN)); + return dynamic_cast<GeoVPhysVol*>(getVPhysVol(id, tableId, copyN)); } - - GeoVPhysVol* vol = nullptr; - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "building a new volume..."; - muxCout.unlock(); + + // B - if not built already, then get the actual volume, + // which should be already built by now, + // get the logVol from it and build a new VPhysVol instance in the heap; + // then, associate a copy number to it, + // and store the new instance into the cache. + GeoVPhysVol* vol = nullptr; + bool volFound = true; + if (1==tableId) { + if(isBuiltPhysVol(id)) + vol = new GeoPhysVol( getBuiltPhysVol(id)->getLogVol() ); + else + volFound = false; } - vol = buildNewVPhysVol(id, tableId, copyN); - // if (m_deepDebug) std::cout << "--> built a new volume: " << vol << std::endl; + else if (2==tableId) { + if(isBuiltFullPhysVol(id)) + vol = new GeoFullPhysVol( getBuiltFullPhysVol(id)->getLogVol() ); + else + volFound = false; + } + if (!volFound) { + std::cout << "ERROR! VPhysVol not found! It should be already built, by now. Exiting...\n"; + exit(EXIT_FAILURE); + } + storeVPhysVol(id, tableId, copyN, vol); + return vol; } - -// Build a new VPhysVol volume and store it -GeoVPhysVol* ReadGeoModel::buildNewVPhysVol(QString id, QString tableId, QString copyN) +//! Build the actual VPhysVol (GeoPhysVol or GeoFullPhysVol), which will then be used to create +//! instances of that volume by using the `copyNumber`. +//! Here, however, we do not need to specify +//! any copyNumber, because the actual GeoPVPhysVol is the same for all copy instances. +GeoVPhysVol* ReadGeoModel::buildVPhysVol(const unsigned int id, const unsigned int tableId, unsigned int /*defaults to "0"*/ logVol_ID) { - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "ReadGeoModel::buildNewVPhysVol() - building a new volume..."; - muxCout.unlock(); - } - - QString nodeType = m_tableid_tableName[tableId.toUInt()]; - // get the parent volume parameters - // here we do not need to use copyN, since the actual volume is the same for all instances - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "\tget the parent..."; - muxCout.unlock(); - } - QStringList values; - if (nodeType == "GeoPhysVol") - values = m_physVols[id.toUInt()]; - else if (nodeType == "GeoFullPhysVol") - values = m_fullPhysVols[id.toUInt()]; + if (m_deepDebug) { muxCout.lock(); std::cout << "ReadGeoModel::buildVPhysVol() - " << id << ", " << tableId << std::endl; muxCout.unlock(); } + std::string nodeType = m_tableID_toTableName[tableId]; - QString volId = values[0]; - QString logVolId = values[1]; + bool errorType = false; - if (m_deepDebug) { - muxCout.lock(); - // std::cout << "Waiting..." << std::flush; - qDebug() << "\tPhysVol-ID:" << volId; - qDebug() << "\tPhysVol-LogVol:" << logVolId; - qDebug() << "\tnodeType:" << nodeType; - muxCout.unlock(); - } + // get the actual VPhysVol volume, if built already + if ( nodeType == "GeoPhysVol" && isBuiltPhysVol(id) ) { + if (m_deepDebug) { muxCout.lock(); std::cout << "getting the actual PhysVol from cache...\n"; ; muxCout.unlock(); } + return getBuiltPhysVol(id); + } else if ( nodeType == "GeoFullPhysVol" && isBuiltFullPhysVol(id)){ + if (m_deepDebug) { muxCout.lock(); std::cout << "getting the actual FullPhysVol from cache...\n"; ; muxCout.unlock(); } + return getBuiltFullPhysVol(id); + } + + // if not built already, then get its parameters and build it now + if (logVol_ID==0) { + // get the volume's parameters + std::vector<std::string> values; + if (nodeType == "GeoPhysVol") + values = m_physVols[id]; + else if (nodeType == "GeoFullPhysVol") + values = m_fullPhysVols[id]; + + logVol_ID = std::stoi(values[1]); + } // GET LOGVOL - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "\tget LogVol..."; - muxCout.unlock(); + GeoLogVol* logVol = getBuiltLog(logVol_ID); + if (!logVol) { + std::cout << "ERROR!!! LogVol is NULL!" << std::endl; +// exit(EXIT_FAILURE); } - GeoLogVol* logVol = buildLogVol(logVolId); // TODO: For PhysVols we first check if we created the vol in memory already; but I'm not sure about other nodes. Check it... // a pointer to the VPhysVol GeoVPhysVol* vol = nullptr; // BUILD THE PHYSVOL OR THE FULLPHYSVOL - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "\tbuild the VPhysVol..."; - muxCout.unlock(); + if (nodeType == "GeoPhysVol") { + GeoPhysVol* pVol = new GeoPhysVol(logVol); + storeBuiltPhysVol(id, pVol); + vol = pVol; } - if (nodeType == "GeoPhysVol") - vol = new GeoPhysVol(logVol); - else if (nodeType == "GeoFullPhysVol") - vol = new GeoFullPhysVol(logVol); - else { - muxCout.lock(); - qWarning() << "ERROR!!! Unkonwn node type!! : " << nodeType; - muxCout.unlock(); + else if (nodeType == "GeoFullPhysVol") { + GeoFullPhysVol* fpVol = new GeoFullPhysVol(logVol); + storeBuiltFullPhysVol(id, fpVol); + vol = fpVol; } + else + errorType = true; - // storing the address of the newly built node - if (m_deepDebug) { + if (errorType) { muxCout.lock(); - qDebug() << "\tstoring the VPhysVol..."; + std::cout << "ERROR! Unkonwn node type. Exiting..." <<std::endl; muxCout.unlock(); + exit(EXIT_FAILURE); } - storeNode(id, tableId, copyN, vol); - return vol; + return vol; } + + // Get the root volume GeoPhysVol* ReadGeoModel::getRootVolume() { if (m_deepDebug) { muxCout.lock(); - qDebug() << "ReadGeoModel::getRootVolume()"; + std::cout << "ReadGeoModel::getRootVolume()" << std::endl; muxCout.unlock(); } - QString id = m_root_vol_data[1]; - QString tableId = m_root_vol_data[2]; - QString copyNumber = "1"; // the Root volume has only one copy by definition - return dynamic_cast<GeoPhysVol*>(buildVPhysVol(id, tableId, copyNumber)); + const unsigned int id = std::stoi(m_root_vol_data[1]); // TODO: GetRoot() should return integers instead of strings... + const unsigned int tableId = std::stoi(m_root_vol_data[2]); + const unsigned int copyNumber = 1; // the Root volume has only one copy by definition + GeoPhysVol* root = dynamic_cast<GeoPhysVol*>(buildVPhysVolInstance(id, tableId, copyNumber)); + checkNodePtr(root, "root", __func__, __PRETTY_FUNCTION__); + return root; } -GeoMaterial* ReadGeoModel::buildMaterial(QString id) +GeoMaterial* ReadGeoModel::buildMaterial(const unsigned int id) { - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "ReadGeoModel::buildMaterial()"; - muxCout.unlock(); + if ( isBuiltMaterial(id) ){ + return getBuiltMaterial(id); } - QStringList values = m_materials[id.toUInt()]; if (m_deepDebug) { muxCout.lock(); - qDebug() << "mat values=" << values; + std::cout << "ReadGeoModel::buildMaterial()" << std::endl; muxCout.unlock(); } - QString matId = values[0]; - QString matName = values[1]; - double matDensity = values[2].toDouble(); - QString matElements = values[3]; + std::vector<std::string> values = m_materials[id-1]; + +// if (m_deepDebug) { +// muxCout.lock(); +// std::cout << "mat values=" << values << std::endl; +// muxCout.unlock(); +// } + 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]; if (m_deepDebug) { muxCout.lock(); - qDebug() << "\tMaterial - ID:" << matId - << ", name:" << matName - << ", density:" << matDensity - << " ( " << matDensity / (SYSTEM_OF_UNITS::g/SYSTEM_OF_UNITS::cm3) << "[g/cm3] )" - << ", elements:" << matElements; + std::cout << "\tMaterial - ID:" << matId + << ", name:" << matName + << ", density:" << matDensity + << " ( " << matDensity / (SYSTEM_OF_UNITS::g/SYSTEM_OF_UNITS::cm3) << "[g/cm3] )" + << ", elements:" << matElements; muxCout.unlock(); } - GeoMaterial* mat = new GeoMaterial(matName.toStdString(), matDensity); + GeoMaterial* mat = new GeoMaterial(matName, matDensity); if (matElements.size() > 0) { // get parameters from DB string - QStringList elements = matElements.split(";"); - foreach( QString par, elements) { + const std::vector<std::string> elements = splitString(matElements, ';'); + for( auto& par : elements) { if (m_deepDebug) { muxCout.lock(); - qDebug() << "par:" << par; + std::cout << "par:" << par; muxCout.unlock(); } - QStringList vars = par.split(":"); - QString elId = vars[0]; - double elFraction = vars[1].toDouble(); - - GeoElement* el = buildElement(elId); - + std::vector<std::string> vars = splitString(par, ':'); + const unsigned int elId = std::stoi(vars[0]); + double elFraction = std::stod(vars[1]); +// GeoElement* el = buildElement(elId); + GeoElement* el = getBuiltElement(elId); mat->add(el, elFraction); } mat->lock(); } + storeBuiltMaterial(id, mat); return mat; } -GeoElement* ReadGeoModel::buildElement(QString id) +GeoElement* ReadGeoModel::buildElement(const unsigned int id) { + if( isBuiltElement(id) ) { + return getBuiltElement(id); + } + if (m_deepDebug) { muxCout.lock(); - qDebug() << "ReadGeoModel::buildElement()"; + std::cout << "ReadGeoModel::buildElement()" << std::endl; muxCout.unlock(); } if (m_elements.size() == 0) qFatal("ERROR! 'm_elements' is empty! Did you load the 'Elements' table? \n\t ==> Aborting..."); - QStringList values = m_elements[id.toUInt()]; + std::vector<std::string> values = m_elements[id-1]; - QString elId = values[0]; - QString elName = values[1]; - QString elSymbol = values[2]; - double elZ = values[3].toDouble(); - double elA = values[4].toDouble(); + 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]); if (m_deepDebug) { muxCout.lock(); - qDebug() << "\tElement - ID:" << elId - << ", name:" << elName - << ", symbol:" << elSymbol - << ", Z:" << elZ - << ", A:" << elA - << " ( " << elA / (SYSTEM_OF_UNITS::g/SYSTEM_OF_UNITS::mole) << "[g/mole] )"; + std::cout << "\tElement - ID:" << elId + << ", name:" << elName + << ", symbol:" << elSymbol + << ", Z:" << elZ + << ", A:" << elA + << " ( " << elA / (SYSTEM_OF_UNITS::g/SYSTEM_OF_UNITS::mole) << "[g/mole] )" + << std::endl; muxCout.unlock(); } - return new GeoElement(elName.toStdString(), elSymbol.toStdString(), elZ, elA); + GeoElement* elem = new GeoElement(elName, elSymbol, elZ, elA); + storeBuiltElement(id, elem); + return elem; +} + + +std::string ReadGeoModel::getShapeType(const unsigned int shapeId) +{ + if (shapeId > m_shapes.size()) { + std::cout << "ERROR!! Shape ID is larger than the container size. Exiting..." << std::endl; + exit(EXIT_FAILURE); + } + std::vector<std::string> paramsShape = m_shapes[ shapeId-1 ];//remember: shapes' IDs start from 1 + std::string type = paramsShape[1]; + return type; } + -GeoShape* ReadGeoModel::buildShape(QString shapeId) + +/// Recursive function, to build GeoShape nodes +GeoShape* ReadGeoModel::buildShape(const unsigned int shapeId, type_shapes_boolean_info* shapes_info_sub) { + if (isBuiltShape(shapeId)) { + return getBuiltShape(shapeId); + } + if (m_deepDebug) { muxCout.lock(); - std::cout << "ReadGeoModel::buildShape()" << std::endl; + std::cout << "ReadGeoModel::buildShape()" << std::endl; muxCout.unlock(); } // try // TODO: implement try/catch // { - QStringList paramsShape = m_shapes[ shapeId.toUInt() ]; - - QString id = paramsShape[0]; - QString type = paramsShape[1]; - QString parameters = paramsShape[2]; + std::vector<std::string> paramsShape = m_shapes[ shapeId-1 ]; // remember: nodes' IDs start from 1 - if (m_deepDebug) { - muxCout.lock(); - std::cout << "\tShape-ID:" << id.toStdString() << ", Shape-type:" << type.toStdString(); - muxCout.unlock(); - } +// const unsigned int id = std::stoi(paramsShape[0]); // unused + std::string type = paramsShape[1]; + std::string parameters = paramsShape[2]; + // Get shape's parameters from the stored string. + // This will be interpreted differently according to the shape. + std::vector<std::string> shapePars = splitString(parameters, ';'); + + GeoShape* shape = nullptr; if (type == "Box") { // shape parameters @@ -678,16 +969,15 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double YHalfLength = 0.; double ZHalfLength = 0.; // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "XHalfLength") XHalfLength = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "YHalfLength") YHalfLength = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "ZHalfLength") ZHalfLength = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + if (varName == "XHalfLength") XHalfLength = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "YHalfLength") YHalfLength = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "ZHalfLength") ZHalfLength = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; } - return new GeoBox(XHalfLength, YHalfLength, ZHalfLength); + shape = new GeoBox(XHalfLength, YHalfLength, ZHalfLength); } else if (type == "Cons") { // shape parameters @@ -699,21 +989,20 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double SPhi = 0.; double DPhi = 0.; // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - // qWarning() << "varValue Cons:" << varValue; - if (varName == "RMin1") RMin1 = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "RMin2") RMin2 = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "RMax1") RMax1 = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "RMax2") RMax2 = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "DZ") DZ = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "SPhi") SPhi = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "DPhi") DPhi = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + // std::cout << "varValue Cons:" << varValue; + if (varName == "RMin1") RMin1 = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "RMin2") RMin2 = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "RMax1") RMax1 = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "RMax2") RMax2 = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "DZ") DZ = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "SPhi") SPhi = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "DPhi") DPhi = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; } - return new GeoCons (RMin1, RMin2, RMax1, RMax2, DZ, SPhi, DPhi); + shape = new GeoCons (RMin1, RMin2, RMax1, RMax2, DZ, SPhi, DPhi); } else if (type == "Torus") { // Member Data: @@ -731,18 +1020,17 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double SPhi = 0.; double DPhi = 0.; // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "Rmin") Rmin = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Rmax") Rmax = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Rtor") Rtor = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "SPhi") SPhi = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "DPhi") DPhi = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + if (varName == "Rmin") Rmin = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Rmax") Rmax = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Rtor") Rtor = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "SPhi") SPhi = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "DPhi") DPhi = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; } - return new GeoTorus (Rmin, Rmax, Rtor, SPhi, DPhi); + shape = new GeoTorus (Rmin, Rmax, Rtor, SPhi, DPhi); } else if (type == "Para") { // shape parameters @@ -753,19 +1041,18 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double Theta = 0.; double Phi = 0.; // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "XHalfLength") XHalfLength = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "YHalfLength") YHalfLength = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "ZHalfLength") ZHalfLength = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Alpha") Alpha = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Theta") Theta = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Phi") Phi = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + if (varName == "XHalfLength") XHalfLength = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "YHalfLength") YHalfLength = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "ZHalfLength") ZHalfLength = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Alpha") Alpha = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Theta") Theta = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Phi") Phi = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; } - return new GeoPara (XHalfLength, YHalfLength, ZHalfLength, Alpha, Theta, Phi); + shape = new GeoPara (XHalfLength, YHalfLength, ZHalfLength, Alpha, Theta, Phi); } else if (type == "Pcon") { // shape parameters @@ -773,14 +1060,11 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double DPhi = 0.; unsigned int NZPlanes = 0; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - bool error = 0; - QString par; - QStringList vars; - QString varName; - QString varValue; + std::string par; + std::vector<std::string> vars; + std::string varName; + std::string varValue; GeoPcon* pcon = nullptr; @@ -791,12 +1075,12 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) // get the three first GeoPcon parameters: the SPhi and DPhi angles, plus the number of Z planes for( int it=0; it < 3; it++) { par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "SPhi") SPhi = varValue.toDouble(); - if (varName == "DPhi") DPhi = varValue.toDouble(); - if (varName == "NZPlanes") NZPlanes = varValue.toDouble(); + if (varName == "SPhi") SPhi = std::stod(varValue); + if (varName == "DPhi") DPhi = std::stod(varValue); + if (varName == "NZPlanes") NZPlanes = std::stoi(varValue); } // build the basic GeoPcon shape pcon = new GeoPcon(SPhi, DPhi); @@ -805,36 +1089,35 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) for (int it=3; it < sizePars; it++) { par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - // qInfo() << "it:" << it << "par:" << par << "varName:" << varName << "varValue:" << varValue; if (varName == "ZPos") { - double zpos = varValue.toDouble(); + double zpos = std::stod(varValue); double rmin=0., rmax=0.; it++; // go to next variable par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "ZRmin") rmin = varValue.toDouble(); + if (varName == "ZRmin") rmin = std::stod(varValue); else error = 1; it++; // go to next variable par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "ZRmax") rmax = varValue.toDouble(); + if (varName == "ZRmax") rmax = std::stod(varValue); else error = 1; if(error) { muxCout.lock(); - qWarning() << "ERROR! GeoPcon 'ZRmin' and 'ZRmax' values are not at the right place! --> " << shapePars; + // FIXME: std::cout << "ERROR! GeoPcon 'ZRmin' and 'ZRmax' values are not at the right place! --> " << shapePars << std::endl; muxCout.unlock(); } @@ -843,7 +1126,7 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) } else { error = 1; muxCout.lock(); - qWarning() << "ERROR! GeoPcon 'ZPos' value is not at the right place! --> " << shapePars; + // FIXME: std::cout << "ERROR! GeoPcon 'ZPos' value is not at the right place! --> " << shapePars << std::endl; muxCout.unlock(); } } @@ -852,19 +1135,22 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) if( pcon->getNPlanes() != NZPlanes) { error = 1; muxCout.lock(); - qWarning() << "ERROR! GeoPcon number of planes: " << QString::number(pcon->getNPlanes()) << " is not equal to the original size! --> " << shapePars; + std::cout << "ERROR! GeoPcon number of planes: " << pcon->getNPlanes() << " is not equal to the original size! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } if(!pcon->isValid()) { error = 1; muxCout.lock(); - qWarning() << "ERROR! GeoPcon shape is not valid!! -- input: " << shapePars; + std::cout << "ERROR! GeoPcon shape is not valid!! -- input: "; + printStdVectorStrings(shapePars); muxCout.unlock(); } } // end if (size>3) else { muxCout.lock(); - qWarning() << "ERROR!! GeoPcon has no Z planes!! --> shape input parameters: " << shapePars; + std::cout << "ERROR!! GeoPcon has no Z planes!! --> shape input parameters: "; + printStdVectorStrings(shapePars); muxCout.unlock(); error = 1; } @@ -876,7 +1162,7 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) exit(EXIT_FAILURE); } - return pcon; + shape = pcon; } else if (type == "Pgon") { // shape parameters @@ -887,14 +1173,11 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) bool error = false; GeoPgon* pgon = nullptr; - QString par; - QStringList vars; - QString varName; - QString varValue; - - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - // qInfo() << "shapePars: " << shapePars; // debug + + std::string par; + std::vector<std::string> vars; + std::string varName; + std::string varValue; int sizePars = shapePars.size(); // check if we have more than 3 parameters @@ -903,14 +1186,14 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) // get the first four GeoPgon parameters: the SPhi and DPhi angles, plus the number of Z planes for( int it=0; it < 4; it++) { par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; // qInfo() << "vars: " << vars; // for debug only - if (varName == "SPhi") SPhi = varValue.toDouble(); - if (varName == "DPhi") DPhi = varValue.toDouble(); - if (varName == "NSides") NSides = varValue.toUInt();// * SYSTEM_OF_UNITS::mm; - if (varName == "NZPlanes") NZPlanes = varValue.toDouble(); + if (varName == "SPhi") SPhi = std::stod(varValue); + if (varName == "DPhi") DPhi = std::stod(varValue); + if (varName == "NSides") NSides = std::stoi(varValue); + if (varName == "NZPlanes") NZPlanes = std::stoi(varValue); } // build the basic GeoPgon shape @@ -920,36 +1203,36 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) for (int it=4; it < sizePars; it++) { par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - // qInfo() << "it:" << it << "par:" << par << "varName:" << varName << "varValue:" << varValue; if (varName == "ZPos") { - double zpos = varValue.toDouble(); + double zpos = std::stod(varValue); double rmin=0., rmax=0.; it++; // go to next variable par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "ZRmin") rmin = varValue.toDouble(); + if (varName == "ZRmin") rmin = std::stod(varValue); else error = 1; it++; // go to next variable par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "ZRmax") rmax = varValue.toDouble(); + if (varName == "ZRmax") rmax = std::stod(varValue); else error = 1; if(error) { muxCout.lock(); - qWarning() << "ERROR! GeoPgon 'ZRmin' and 'ZRmax' values are not at the right place! --> " << shapePars; + std::cout << "ERROR! GeoPgon 'ZRmin' and 'ZRmax' values are not at the right place! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } @@ -958,7 +1241,8 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) } else { error = 1; muxCout.lock(); - qWarning() << "ERROR! GeoPgon 'ZPos' value is not at the right place! --> " << shapePars; + std::cout << "ERROR! GeoPgon 'ZPos' value is not at the right place! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } } @@ -967,19 +1251,22 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) if( pgon->getNPlanes() != NZPlanes) { error = 1; muxCout.lock(); - qWarning() << "ERROR! GeoPgon number of planes: " << QString::number(pgon->getNPlanes()) << " is not equal to the original size! --> " << shapePars; + std::cout << "ERROR! GeoPgon number of planes: " << pgon->getNPlanes() << " is not equal to the original size! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } if(!pgon->isValid()) { error = 1; muxCout.lock(); - qWarning() << "ERROR! GeoPgon shape is not valid!! -- input: " << shapePars; + std::cout << "ERROR! GeoPgon shape is not valid!! -- input: "; + printStdVectorStrings(shapePars); muxCout.unlock(); } } // end if (size>3) else { muxCout.lock(); - qWarning() << "ERROR!! GeoPgon has no Z planes!! --> shape input parameters: " << shapePars; + std::cout << "ERROR!! GeoPgon has no Z planes!! --> shape input parameters: "; + printStdVectorStrings(shapePars); muxCout.unlock(); error = 1; } @@ -989,7 +1276,7 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) muxCout.unlock(); exit(EXIT_FAILURE); } - return pgon; + shape = pgon; } else if (type == "GenericTrap") { // shape parameters @@ -998,14 +1285,11 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) GeoGenericTrapVertices Vertices; bool error = false; GeoGenericTrap* gTrap = nullptr; - QString par; - QStringList vars; - QString varName; - QString varValue; - - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - // qInfo() << "shapePars: " << shapePars; // debug + + std::string par; + std::vector<std::string> vars; + std::string varName; + std::string varValue; int sizePars = shapePars.size(); // check if we have more than 3 parameters @@ -1014,12 +1298,12 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) // get the two GeoGenericTrap parameters: the ZHalfLength plus the number of vertices for( int it=0; it < 2; it++) { par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; // qInfo() << "vars: " << vars; // for debug only - if (varName == "ZHalfLength") ZHalfLength = varValue.toDouble(); - if (varName == "NVertices") NVertices = varValue.toUInt(); + if (varName == "ZHalfLength") ZHalfLength = std::stod(varValue); + if (varName == "NVertices") NVertices = std::stoi(varValue); } @@ -1028,23 +1312,22 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) for (int it=2; it < NVertices; it++) { par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - // qInfo() << "it:" << it << "par:" << par << "varName:" << varName << "varValue:" << varValue; if (varName == "X") { - double x = varValue.toDouble(); + double x = std::stod(varValue); it++; // go to next variable par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; if (varName == "Y") { - double y = varValue.toDouble(); + double y = std::stod(varValue); Vertices.push_back(GeoTwoVector(x,y)); } else { @@ -1052,13 +1335,15 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) } if(error) { muxCout.lock(); - qWarning() << "ERROR! GeoGenericTrap 'X' and 'Y' values are not at the right place! --> " << shapePars; + std::cout << "ERROR! GeoGenericTrap 'X' and 'Y' values are not at the right place! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } } else { error = 1; muxCout.lock(); - qWarning() << "ERROR! GeoGenericTrap 'ZPos' value is not at the right place! --> " << shapePars; + std::cout << "ERROR! GeoGenericTrap 'ZPos' value is not at the right place! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } } @@ -1072,7 +1357,10 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) gTrap = new GeoGenericTrap(ZHalfLength,Vertices); } // end if (size>3) else { - qWarning() << "ERROR!! GeoGenericTrap has no Z vertices!! --> shape input parameters: " << shapePars; + muxCout.lock(); + std::cout << "ERROR!! GeoGenericTrap has no Z vertices!! --> shape input parameters: "; + printStdVectorStrings(shapePars); + muxCout.unlock(); error = 1; } if(error) { @@ -1081,10 +1369,9 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) muxCout.unlock(); exit(EXIT_FAILURE); } - return gTrap; + shape = gTrap; } else if (type == "SimplePolygonBrep") { - //qInfo() << "Reading-in: SimplePolygonBrep: "; // debug // shape parameters double DZ = 0.; unsigned int NVertices = 0; @@ -1093,14 +1380,11 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) bool error = 0; GeoSimplePolygonBrep* sh = nullptr; - QString par; - QStringList vars; - QString varName; - QString varValue; - - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - //qInfo() << "shapePars: " << shapePars; // debug + + std::string par; + std::vector<std::string> vars; + std::string varName; + std::string varValue; int sizePars = shapePars.size(); // check if we have more than 2 parameters @@ -1109,15 +1393,14 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) // get the first two GeoSimplePolygonBrep parameters: DZ and the number of vertices. for( int it=0; it < 2; it++) { par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - // qInfo() << "vars: " << vars; // for debug only - if (varName == "DZ") DZ = varValue.toDouble(); - if (varName == "NVertices") NVertices = varValue.toDouble(); + if (varName == "DZ") DZ = std::stod(varValue); + if (varName == "NVertices") NVertices = std::stoi(varValue); //else if (varName == "NVertices") NVertices = varValue.toDouble(); //else error = 1; - //if(error) qWarning() << "ERROR! GeoSimplePolygonBrep parameters are not correctly stored! -->" << vars; + //if(error) std::cout << "ERROR! GeoSimplePolygonBrep parameters are not correctly stored! -->" << vars; } // build the basic GeoSimplePolygonBrep shape @@ -1127,24 +1410,25 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) for (int it=2; it < sizePars; it++) { par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "xV") xV = varValue.toDouble(); + if (varName == "xV") xV = std::stod(varValue); else error = 1; it++; // go to next variable (they come in pairs) par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "yV") yV = varValue.toDouble(); + if (varName == "yV") yV = std::stod(varValue); else error = 1; if(error) { muxCout.lock(); - qWarning() << "ERROR! GeoSimplePolygonBrep 'xVertex' and 'yVertex' values are not at the right place! --> " << shapePars; + std::cout << "ERROR! GeoSimplePolygonBrep 'xVertex' and 'yVertex' values are not at the right place! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } @@ -1155,19 +1439,22 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) if( sh->getNVertices() != NVertices) { error = 1; muxCout.lock(); - qWarning() << "ERROR! GeoSimplePolygonBrep number of planes: " << QString::number(sh->getNVertices()) << " is not equal to the original size! --> " << shapePars; + std::cout << "ERROR! GeoSimplePolygonBrep number of planes: " << sh->getNVertices() << " is not equal to the original size! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } if(!sh->isValid()) { error = 1; muxCout.lock(); - qWarning() << "ERROR! GeoSimplePolygonBrep shape is not valid!! -- input: " << shapePars; + std::cout << "ERROR! GeoSimplePolygonBrep shape is not valid!! -- input: "; + printStdVectorStrings(shapePars); muxCout.unlock(); } } // end if (size>3) else { muxCout.lock(); - qWarning() << "ERROR!! GeoSimplePolygonBrep has no vertices!! --> shape input parameters: " << shapePars; + std::cout << "ERROR!! GeoSimplePolygonBrep has no vertices!! --> shape input parameters: "; + printStdVectorStrings(shapePars); muxCout.unlock(); error = 1; } @@ -1177,31 +1464,24 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) muxCout.unlock(); exit(EXIT_FAILURE); } - return sh; + shape = sh; } else if (type == "TessellatedSolid") { // Tessellated pars example: "nFacets=1;TRI;vT=ABSOLUTE;nV=3;xV=0;yV=0;zV=1;xV=0;yV=1;zV=0;xV=1;yV=0;zV=0" - qInfo() << "Reading-in: TessellatedSolid: "; // debug + std::cout << "Reading-in: TessellatedSolid: "; // debug // Facet type - QString facetType = ""; + std::string facetType = ""; // shape parameters unsigned int nFacets = 0; bool error = 0; GeoTessellatedSolid* sh = nullptr; - QString par; - QStringList vars; - QString varName; - QString varValue; - - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - if (m_deepDebug) { - muxCout.lock(); - qInfo() << "shapePars: " << shapePars; // debug - muxCout.unlock(); - } + + std::string par; + std::vector<std::string> vars; + std::string varName; + std::string varValue; int sizePars = shapePars.size(); // check if we have at least 13 parameters, @@ -1211,15 +1491,15 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) // get the first parameter par = shapePars[0]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "nFacets") nFacets = varValue.toInt(); + if (varName == "nFacets") nFacets = std::stoi(varValue); else { muxCout.lock(); qWarning("ERROR!! - GeoTessellatedSolid - nFacets is not defined!!"); muxCout.unlock(); - error = true; // TODO: check "error.h" functionalities and replace with that if useful + error = true; // TODO: check "error.h" functionalities and replace with that, if useful } // build the basic GeoTessellatedSolid shape @@ -1232,7 +1512,7 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) { // get facet type par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; if (varName == "QUAD") { facetType = "QUAD"; @@ -1242,7 +1522,7 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) } else { muxCout.lock(); - qWarning() << "ERROR!! - GeoTessellatedSolid - Facet type is not defined! [got: '" << varName << "']"; + std::cout << "ERROR!! - GeoTessellatedSolid - Facet type is not defined! [got: '" << varName << "']" << std::endl; muxCout.unlock(); error = true; } @@ -1252,7 +1532,7 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) // get the type of the vertexes composing the facet bool isAbsolute = true; par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; if (varName == "vT") { @@ -1260,7 +1540,7 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) else if (varValue == "RELATIVE") isAbsolute = false; else { muxCout.lock(); - qWarning() << "ERROR! - GeoTessellatedSolid - Vertex type not defined!"; + std::cout << "ERROR! - GeoTessellatedSolid - Vertex type not defined!" << std::endl; muxCout.unlock(); error=true; } @@ -1271,24 +1551,23 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) unsigned int nVertexes = 0; par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "nV") nVertexes = varValue.toUInt(); + if (varName == "nV") nVertexes = std::stoi(varValue); else { muxCout.lock(); - qWarning() << "ERROR! - GeoTessellatedSolid - nVertices not defined!"; + std::cout << "ERROR! - GeoTessellatedSolid - nVertices not defined!" << std::endl; muxCout.unlock(); error=true; } - // qDebug() << "it:" << it; // if we get a QUAD ==> GeoQuadrangularFacet if (facetType=="QUAD") { muxCout.lock(); - qDebug() << "Handling a QUAD facet..."; + std::cout << "Handling a QUAD facet..." << std::endl; muxCout.unlock(); // to store the 4 vertices of the GeoQuadrangularFacet auto vV = std::vector<std::unique_ptr<GeoFacetVertex>>{}; @@ -1302,13 +1581,13 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double xV=0.; par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "xV") xV = varValue.toDouble(); + if (varName == "xV") xV = std::stod(varValue); else { muxCout.lock(); - qWarning() << "ERROR! Got '" << varName << "' instead of 'xV'!"; + std::cout << "ERROR! Got '" << varName << "' instead of 'xV'!" << std::endl; muxCout.unlock(); error = 1; } @@ -1317,13 +1596,13 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double yV=0.; par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "yV") yV = varValue.toDouble(); + if (varName == "yV") yV = std::stod(varValue); else { muxCout.lock(); - qWarning() << "ERROR! Got '" << varName << "' instead of 'yV'!"; + std::cout << "ERROR! Got '" << varName << "' instead of 'yV'!" << std::endl; muxCout.unlock(); error = 1; } @@ -1332,20 +1611,21 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double zV=0.; par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "zV") zV = varValue.toDouble(); + if (varName == "zV") zV = std::stod(varValue); else { muxCout.lock(); - qWarning() << "ERROR! Got '" << varName << "' instead of 'zV'!"; + std::cout << "ERROR! Got '" << varName << "' instead of 'zV'!" << std::endl; muxCout.unlock(); error = 1; } if(error) { muxCout.lock(); - qWarning() << "ERROR! GeoTessellatedSolid 'xV', 'yV', and 'zV' values are not at the right place! --> " << shapePars; + std::cout << "ERROR! GeoTessellatedSolid 'xV', 'yV', and 'zV' values are not at the right place! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } @@ -1361,50 +1641,51 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) else if (facetType=="TRI") { muxCout.lock(); - qDebug() << "Handling a TRI facet..."; + std::cout << "Handling a TRI facet..." << std::endl; muxCout.unlock(); -// std::vector<GeoFacetVertex*> vV(3, 0); // to store the 3 vertices of the GeoTriangularFacet + // std::vector<GeoFacetVertex*> vV(3, 0); // to store the 3 vertices of the GeoTriangularFacet auto vV = std::vector<std::unique_ptr<GeoFacetVertex>>{}; // we look for 3 vertices for GeoTriangularFacet; // for each of them, we get 3 coordinates -// vertStart = it; + // vertStart = it; for (unsigned int iV=0; iV<3; ++iV) { it++; // advance to the first of the facet's vertices' coordinates double xV=0.; par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "xV") xV = varValue.toDouble(); + if (varName == "xV") xV = std::stod(varValue); else error = 1; it++; // go to the next coordinate double yV=0.; par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "yV") yV = varValue.toDouble(); + if (varName == "yV") yV = std::stod(varValue); else error = 1; it++; // go to the next coordinate double zV=0.; par = shapePars[it]; - vars = par.split("="); + vars = splitString(par, '='); varName = vars[0]; varValue = vars[1]; - if (varName == "zV") zV = varValue.toDouble(); + if (varName == "zV") zV = std::stod(varValue); else error = 1; if(error) { muxCout.lock(); - qWarning() << "ERROR! GeoTessellatedSolid 'xV', 'yV', and 'zV' values are not at the right place! --> " << shapePars; + std::cout << "ERROR! GeoTessellatedSolid 'xV', 'yV', and 'zV' values are not at the right place! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } @@ -1424,20 +1705,22 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) if( sh->getNumberOfFacets() != nFacets) { error = 1; muxCout.lock(); - qWarning() << "ERROR! GeoTessellatedSolid number of facets: " << QString::number(sh->getNumberOfFacets()) << " is not equal to the original size! --> " << shapePars; + std::cout << "ERROR! GeoTessellatedSolid number of facets: " << sh->getNumberOfFacets() << " is not equal to the original size! --> "; + printStdVectorStrings(shapePars); muxCout.unlock(); } /* * TODO: uncomment it, when the isValid() method will be implemented for GeoTessellatedSolid if(!sh->isValid()) { error = 1; - qWarning() << "ERROR! GeoTessellatedSolid shape is not valid!! -- input: " << shapePars; + std::cout << "ERROR! GeoTessellatedSolid shape is not valid!! -- input: " << shapePars; } */ } // END OF if (size>13) else { muxCout.lock(); - qWarning() << "ERROR!! GeoTessellatedSolid has no facets!! --> shape input parameters: " << shapePars; + std::cout << "ERROR!! GeoTessellatedSolid has no facets!! --> shape input parameters: "; + printStdVectorStrings(shapePars); muxCout.unlock(); error = 1; } @@ -1447,7 +1730,7 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) muxCout.unlock(); exit(EXIT_FAILURE); } - return sh; + shape = sh; } else if (type == "Trap") { @@ -1463,25 +1746,24 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double Dxdyndzp = 0.; double Dxdypdzp = 0.; double Angleydzp = 0.; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "ZHalfLength") ZHalfLength = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Theta") Theta = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Phi") Phi = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Dydzn") Dydzn = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Dxdyndzn") Dxdyndzn = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Dxdypdzn") Dxdypdzn = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Angleydzn") Angleydzn = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Dydzp") Dydzp = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Dxdyndzp") Dxdyndzp = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Dxdypdzp") Dxdypdzp = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "Angleydzp") Angleydzp = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; + // get parameters + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + if (varName == "ZHalfLength") ZHalfLength = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Theta") Theta = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Phi") Phi = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Dydzn") Dydzn = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Dxdyndzn") Dxdyndzn = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Dxdypdzn") Dxdypdzn = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Angleydzn") Angleydzn = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Dydzp") Dydzp = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Dxdyndzp") Dxdyndzp = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Dxdypdzp") Dxdypdzp = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "Angleydzp") Angleydzp = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; } - return new GeoTrap (ZHalfLength, Theta, Phi, Dydzn, Dxdyndzn, Dxdypdzn, Angleydzn, Dydzp, Dxdyndzp, Dxdypdzp, Angleydzp); + shape = new GeoTrap (ZHalfLength, Theta, Phi, Dydzn, Dxdyndzn, Dxdypdzn, Angleydzn, Dydzp, Dxdyndzp, Dxdypdzp, Angleydzp); } else if (type == "Trd") { // shape constructor parameters @@ -1490,37 +1772,35 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double YHalfLength1 = 0.; double YHalfLength2 = 0.; double ZHalfLength = 0.; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - // qWarning() << "varValue:" << varValue; - if (varName == "XHalfLength1") XHalfLength1 = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "XHalfLength2") XHalfLength2 = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "YHalfLength1") YHalfLength1 = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "YHalfLength2") YHalfLength2 = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "ZHalfLength") ZHalfLength = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; + // get parameters + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + // std::cout << "varValue:" << varValue; + if (varName == "XHalfLength1") XHalfLength1 = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "XHalfLength2") XHalfLength2 = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "YHalfLength1") YHalfLength1 = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "YHalfLength2") YHalfLength2 = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "ZHalfLength") ZHalfLength = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; } - return new GeoTrd (XHalfLength1, XHalfLength2, YHalfLength1, YHalfLength2, ZHalfLength); + shape = new GeoTrd (XHalfLength1, XHalfLength2, YHalfLength1, YHalfLength2, ZHalfLength); } else if (type == "Tube") { // shape parameters double RMin = 0.; double RMax = 0.; double ZHalfLength = 0.; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "RMin") RMin = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "RMax") RMax = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "ZHalfLength") ZHalfLength = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; + // get parameters + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + if (varName == "RMin") RMin = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "RMax") RMax = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "ZHalfLength") ZHalfLength = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; } - return new GeoTube(RMin, RMax, ZHalfLength); + shape = new GeoTube(RMin, RMax, ZHalfLength); } else if (type == "Tubs") { // shape parameters @@ -1529,143 +1809,209 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) double ZHalfLength = 0.; double SPhi = 0.; double DPhi = 0.; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "RMin") RMin = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "RMax") RMax = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "ZHalfLength") ZHalfLength = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "SPhi") SPhi = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - if (varName == "DPhi") DPhi = varValue.toDouble();// * SYSTEM_OF_UNITS::mm; - } - return new GeoTubs (RMin, RMax, ZHalfLength, SPhi, DPhi); - } - else if (type == "Intersection") { - // shape parameters - unsigned int opA = 0; - unsigned int opB = 0; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "opA") opA = varValue.toUInt(); - if (varName == "opB") opB = varValue.toUInt(); + // get parameters + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + if (varName == "RMin") RMin = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "RMax") RMax = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "ZHalfLength") ZHalfLength = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "SPhi") SPhi = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; + if (varName == "DPhi") DPhi = std::stod(varValue);// * SYSTEM_OF_UNITS::mm; } - // get the referenced shape - const GeoShape* shapeA = buildShape( QString::number(opA) ); - const GeoShape* shapeB = buildShape( QString::number(opB) ); - // build and return the GeoShapeShift instance - return new GeoShapeIntersection(shapeA, shapeB); + shape = new GeoTubs (RMin, RMax, ZHalfLength, SPhi, DPhi); } else if (type == "Shift") { // shape parameters - unsigned int shapeId = 0; + unsigned int shapeOpId = 0; unsigned int transfId = 0; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "A") shapeId = varValue.toUInt(); - if (varName == "X") transfId = varValue.toUInt(); - } - // get the referenced shape - const GeoShape* shapeA = buildShape( QString::number(shapeId) ); - // get the referenced Transform - QStringList transPars = m_transforms[transfId]; - if (m_deepDebug) qDebug() << "child:" << transPars; - GeoTransform* transf = parseTransform(transPars); - const GeoTrf::Transform3D transfX = transf->getTransform(); - transf->unref(); // delete the transf from the heap - - // qWarning() << "GeoShift:"; - // printTrf(transfX); - - // build and return the GeoShapeShift instance - return new GeoShapeShift(shapeA, transfX); - } - else if (type == "Subtraction") { - // shape parameters - unsigned int opA = 0; - unsigned int opB = 0; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "opA") opA = varValue.toUInt(); - if (varName == "opB") opB = varValue.toUInt(); + // get parameters + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + if (varName == "A") shapeOpId = std::stoi(varValue); + if (varName == "X") transfId = std::stoi(varValue); } + if (shapeOpId == 0 || transfId == 0) { + std::cout << "ERROR! Shift shape - input operand shapes' IDs are empty! (shapeId: " << shapeOpId << ", transfId:" << transfId << ")" << std::endl; + exit(EXIT_FAILURE); + } - if (opA == 0 || opB == 0) { - std::cout << "ERROR! Subtraction shape - input shape IDs are empty! (opA: " << opA << ", opB:" << opB << ")" << std::endl; + // if both operands are built already, + // then get them from cache, + // and build the operator shape with them, + if ( isBuiltShape(shapeOpId) && isBuiltTransform(transfId) ) { + const GeoShape* shapeOp = getBuiltShape(shapeOpId); + const GeoTransform* transf = getBuiltTransform(transfId); + // TODO: here we create a fake GeoTransform to get a GeoTrf::Transform3D. + // TODO: ==> Perhaps we could keep a table for bare GeoTrf::Transform3D transforms used in GeoShift nodes. + GeoTrf::Transform3D transfX = transf->getTransform(); + transf->unref(); // delete the transf from the heap, because we don't need the node, only the bare transformation matrix + GeoShapeShift* shapeNew = new GeoShapeShift(shapeOp, transfX); + storeBuiltShape(shapeId, shapeNew); + shape = shapeNew; + } + // otherwise, build the operands + else { + + // TODO: IMPORTANT!!! --> check how the transf used in shift are saved into the DB, because they are bare transf and not GeoTransform nodes... I fear thay are wrongly stored... + + // first, check if the transform is built; + // if so, use that; + // if not, build the transform + + // get the referenced Transform, then get the bare transform matrix from it + GeoTransform* transf = nullptr; + GeoTrf::Transform3D transfX; + if (isBuiltTransform(transfId)) { + transf = getBuiltTransform(transfId); + } else { + transf = buildTransform(transfId); + } + // TODO: here we create a fake GeoTransform to get a GeoTrf::Transform3D. + // TODO: ==> Perhaps we could keep a table for bare GeoTrf::Transform3D transforms used in GeoShift nodes. + transfX = transf->getTransform(); + transf->unref(); // delete the transf from the heap, because we don't need the node, only the bare transformation matrix + + // then, check the type of the operand shape + bool isAOperator = isShapeOperator( shapeOpId ); + + // if operand shape is simple/actual shape (i.e., not boolean/operator), + // then build it, + // then build the boolean shape with that + if ( !isAOperator ) { + const GeoShape* shapeOp = buildShape( shapeOpId, shapes_info_sub ); + + if ( shapeOp == nullptr || transf == nullptr ) { + std::cout << "ERROR!!! Shift - shapeOp or transfX are NULL! Exiting..." << std::endl; exit(EXIT_FAILURE); } + GeoShapeShift* shapeNew = new GeoShapeShift(shapeOp, transfX); + shape = shapeNew; + } + // ...otherwise, build the Shift operator shape without operands + // and save the needed pieces of information to build the actual + // operands and shape later. + else { + GeoShapeShift* shapeNew = new GeoShapeShift(); + tuple_shapes_boolean_info tt (shapeId, shapeNew, shapeOpId, transfId); + shapes_info_sub->push_back(tt); //! Push the information about the new boolean shape at the end of the very same container we are iterating over + shape = shapeNew; + } + } + } + else if (type == "Subtraction" || type == "Union" || type == "Intersection") { - // get the referenced shapes - QString shAId = QString::number(opA); - const GeoShape* shapeA = buildShape( shAId ); - - QString shBId = QString::number(opB); - const GeoShape* shapeB = buildShape( shBId ); - - if ( shapeA == NULL) { - std::cout << "ERROR!!! shapeA is NULL!" << std::endl; - exit(EXIT_FAILURE); - } - - // std::cout << "Subtraction -- " << "opA: " << opA << ", opB: " << opB << ", shapeA: " << shapeA << ", shapeB: " << shapeB << std::endl; + // Check what shapes are subtracted/united/intersected: + // - If they are actual shapes, build them and return + // - If they are boolean/operator shapes, then store the shape for later and continue - // build and return the GeoShapeSubtraction instance - return new GeoShapeSubtraction(shapeA, shapeB); - } - else if (type == "Union") { - // shape parameters + // shape's operands unsigned int opA = 0; unsigned int opB = 0; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "opA") opA = varValue.toUInt(); - if (varName == "opB") opB = varValue.toUInt(); + // get parameters + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + if (varName == "opA") opA = std::stoi(varValue); + if (varName == "opB") opB = std::stoi(varValue); } - if (opA == 0 || opB == 0) { - muxCout.lock(); - std::cout << "ERROR! 'GeoUnion' shape - opA or opB have not been properly intialized!" << std::endl; - muxCout.unlock(); + if (opA == 0 || opB == 0) { + std::cout << "ERROR! Subtraction/Union/Intersection shape - input operand shapes' IDs are empty! (opA: " << opA << ", opB:" << opB << ")" << std::endl; + exit(EXIT_FAILURE); + } + + // if both operands are built already, + // then get them from cache, + // and build the operator with them + if ( isBuiltShape(opA) && isBuiltShape(opB) ) { + // std::cout << "both operand shapes are built, build the operator shape..." << std::endl; + GeoShape* shapeNew = nullptr; + const GeoShape* shapeA = getBuiltShape(opA); + const GeoShape* shapeB = getBuiltShape(opB); + if ("Subtraction" == type) { + shapeNew = new GeoShapeSubtraction(shapeA, shapeB); + } + else if ("Union" == type) { + shapeNew = new GeoShapeUnion(shapeA, shapeB); + } + else if ("Intersection" == type) { + shapeNew = new GeoShapeIntersection(shapeA, shapeB); + } + shape = shapeNew; + } + // otherwise, build the operand shapes... + else { + + // first check the operands' type + bool isAOperator = isShapeOperator( opA ); + bool isBOperator = isShapeOperator( opB ); + + // if both are simple/actual shapes (i.e., not booleans), + // then build them, then build the boolean shape with them, and store that. + if ( !isAOperator && !isBOperator) { + const GeoShape* shapeA = buildShape( opA, shapes_info_sub ); + const GeoShape* shapeB = buildShape( opB, shapes_info_sub ); + if ( shapeA == NULL || shapeB == NULL ) { + std::cout << "ERROR!!! shapeA or shapeB are NULL!" << std::endl; exit(EXIT_FAILURE); } - // get the referenced shape - const GeoShape* shapeA = buildShape( QString::number(opA) ); - const GeoShape* shapeB = buildShape( QString::number(opB) ); - // build and return the GeoShapeShift instance + + GeoShape* shapeNew = nullptr; + if ("Subtraction" == type) { + shapeNew = new GeoShapeSubtraction(shapeA, shapeB); + } + else if ("Union" == type) { + shapeNew = new GeoShapeUnion(shapeA, shapeB); + } + else if ("Intersection" == type) { + shapeNew = new GeoShapeIntersection(shapeA, shapeB); + } + + shape = shapeNew; + } + // ...otherwise, build the Subtraction operator shape without operands + // and save the needed pieces of information to build the actual + // operands and shape later. + else { + GeoShape* shapeNew = nullptr; + if ( "Subtraction" == type ) { + shapeNew = new GeoShapeSubtraction; + } else if ( "Union" == type ) { + shapeNew = new GeoShapeUnion; + } else if ( "Intersection" == type ) { + shapeNew = new GeoShapeIntersection; + } -// std::cout << "Union -- " << "opA: " << opA << ", opB: " << opB << ", shapeA: " << shapeA << ", shapeB: " << shapeB << std::endl; - return new GeoShapeUnion(shapeA, shapeB); + tuple_shapes_boolean_info tt (shapeId, shapeNew, opA, opB); + shapes_info_sub->push_back(tt); //! Push the information about the new boolean shape at the end of the very same container we are iterating over + + shape = shapeNew; + } + } } //LAr custom shape else if(type == "CustomShape") { std::string name = ""; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - // qWarning() << "shapePars: " << shapePars << shapePars.size() << shapePars.isEmpty(); - if ( shapePars.size() > 0 && ((shapePars.filter("=")).size() > 0) ) // this complex test is needed to handle null strings + // check parameters + // this complex test is needed to handle null strings in the DB records + bool okPars = false; + for( auto& par : shapePars) { + std::string str = par.substr( par.find("=") ); // get from "=" to the end + if (str.size() > 0) okPars = true; + } + //if ( shapePars.size() > 0 && ((shapePars.filter("=")).size() > 0) ) // this complex test is needed to handle null strings + if ( shapePars.size() > 0 && okPars ) { - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "name") name = varValue.toStdString(); + // get parameters + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + if (varName == "name") name = varValue; } } else { muxCout.lock(); @@ -1674,21 +2020,28 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) muxCout.unlock(); exit(EXIT_FAILURE); } - return new GeoUnidentifiedShape("LArCustomShape",name); + shape = new GeoUnidentifiedShape("LArCustomShape",name); } else if (type=="UnidentifiedShape") { std::string name = ""; std::string asciiData = ""; - // get parameters from DB string - QStringList shapePars = parameters.split(";"); - if ( shapePars.size() > 0 && ((shapePars.filter("=")).size() > 0) ) // this complex test is needed to handle null strings + // check parameters + // this complex test is needed to handle null strings in the DB records + bool okPars = false; + for( auto& par : shapePars) { + std::string str = par.substr( par.find("=") ); // get from "=" to the end + if (str.size() > 0) okPars = true; + } + //if ( shapePars.size() > 0 && ((shapePars.filter("=")).size() > 0) ) // this complex test is needed to handle null strings + if ( shapePars.size() > 0 && okPars ) { - foreach( QString par, shapePars) { - QStringList vars = par.split("="); - QString varName = vars[0]; - QString varValue = vars[1]; - if (varName == "name") name = varValue.toStdString(); - if (varName == "asciiData") asciiData=varValue.toStdString(); + // get parameters + for( auto& par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + if (varName == "name") name = varValue; + if (varName == "asciiData") asciiData = varValue; } } else { // throw std::invalid_argument("UnidentifiedShape parameters' list is empty!!"); @@ -1697,16 +2050,20 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) muxCout.unlock(); exit(EXIT_FAILURE); } - return new GeoUnidentifiedShape(name,asciiData); + shape = new GeoUnidentifiedShape(name,asciiData); } else { - // QString msg = "WARNING!! - Shape '" + type + "' not implemented yet!!! Returning a dummy cube."; - // qWarning(msg.toStdString().c_str()); - m_unknown_shapes.insert(type.toStdString()); // save unknwon shapes for later warning message - return new GeoBox(30.0*SYSTEM_OF_UNITS::cm, 30*SYSTEM_OF_UNITS::cm, 30*SYSTEM_OF_UNITS::cm); // FIXME: bogus shape. Use actual shape! + m_unknown_shapes.insert(type); // save unknwon shapes for later warning message + shape = buildDummyShape(); } + //! store into the cache the shape we have just built, + //! for later use when referenced by another node + storeBuiltShape(shapeId, shape); + + return shape; // FIXME: do we still need the return? probably not, because we now store all the shapes that have been built in this method + // } // catch (std::invalid_argument& e) @@ -1719,70 +2076,374 @@ GeoShape* ReadGeoModel::buildShape(QString shapeId) } -GeoLogVol* ReadGeoModel::buildLogVol(QString logVolId) + +// TODO: move to an untilities file/class +void printTuple(tuple_shapes_boolean_info tuple) { - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "ReadGeoModel::buildLogVol()"; - muxCout.unlock(); + std::apply([](auto&&... args) { ( (std::cout << args << ", "), ...); }, tuple); // needs C++17 + std::cout << std::endl; +} +// TODO: move to an untilities file/class +void inspectListShapesToBuild(type_shapes_boolean_info list) +{ + for (auto tuple : list) { + printTuple(tuple); + std::cout << std::endl; } +} - // get logVol properties from the DB - QStringList values = m_logVols[logVolId.toUInt()]; - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "buildLogVol() - params:" << values; - muxCout.unlock(); + +void ReadGeoModel::createBooleanShapeOperands(type_shapes_boolean_info* shapes_info_sub) +{ + if (shapes_info_sub->size() == 0) return; + + // debug + // std::cout << "\ncreateBooleanShapeOperands() - start..." << std::endl; + // inspectListShapesToBuild(shapes_info_sub); + + // Iterate over the list. The size may be incremented while iterating (therefore, we cannot use iterators) + for (type_shapes_boolean_info::size_type ii = 0; ii < shapes_info_sub->size(); ++ii) + { + // get the tuple containing the data about the operand shapes to build + tuple_shapes_boolean_info tuple = (*shapes_info_sub)[ii]; + // std::cout << "tuple: "; printTuple(tuple); // debug + + // Initializing variables for unpacking + unsigned int shapeID = 0; //std::get<0>(tuple); + GeoShape* boolShPtr = nullptr; //std::get<1>(tuple); + unsigned int idA = 0; //std::get<2>(tuple); + unsigned int idB = 0; //std::get<3>(tuple); + + // use 'tie' to unpack the tuple values into separate variables + std::tie(shapeID, boolShPtr, idA, idB) = tuple; + + if (shapeID == 0 || boolShPtr == nullptr || idA == 0 || idB == 0) { + muxCout.lock(); + std::cout << "ERROR! Boolean/Operator shape - shape is NULL or operands' IDs are not defined! (shapeID: " << shapeID << ", idA: " << idA << ", idB:" << idB << ")" << std::endl; + muxCout.unlock(); + exit(EXIT_FAILURE); + } + + if (isShapeBoolean(shapeID)) { + + GeoShape* shapeA = nullptr; + GeoShape* shapeB = nullptr; + + // if both operands are built already... + if ( isBuiltShape(idA) && isBuiltShape(idB) ) { + // then build the operator shape... + shapeA = getBuiltShape(idA); + shapeB = getBuiltShape(idB); //TODO: customize for Shift as well + } else { + // otherwise, build the operand shapes + shapeA = getBooleanReferencedShape(idA, shapes_info_sub); + shapeB = getBooleanReferencedShape(idB, shapes_info_sub); + } + // Now, assign the new shapes to the boolean shape we're building + if (dynamic_cast<GeoShapeIntersection*>(boolShPtr)) { + GeoShapeIntersection* ptr = dynamic_cast<GeoShapeIntersection*>(boolShPtr); + ptr->m_opA = shapeA; + ptr->m_opB = shapeB; + ptr->m_opA->ref(); + ptr->m_opB->ref(); + } + else if (dynamic_cast<GeoShapeSubtraction*>(boolShPtr)) { + GeoShapeSubtraction* ptr = dynamic_cast<GeoShapeSubtraction*>(boolShPtr); + ptr->m_opA = shapeA; + ptr->m_opB = shapeB; + ptr->m_opA->ref(); + ptr->m_opB->ref(); + } + else if (dynamic_cast<GeoShapeUnion*>(boolShPtr)) { + GeoShapeUnion* ptr = dynamic_cast<GeoShapeUnion*>(boolShPtr); + ptr->m_opA = shapeA; + ptr->m_opB = shapeB; + ptr->m_opA->ref(); + ptr->m_opB->ref(); + } + else{ + // TODO: move to standard error message for all instances + std::cout << "ERROR!!! shape is not boolean/operator! Write to 'geomodel-developers@cern.ch'. Exiting..." << std::endl; + exit(EXIT_FAILURE); + } + } else if ("Shift" == getShapeType(shapeID)) { + + GeoShape* opShape = nullptr; + GeoTrf::Transform3D shiftX; + GeoTransform* shiftTransf = nullptr; // TODO: remove the need for a temp GeoTransform, store the bare transforms as well... + + // if both operands are built already... + if ( isBuiltShape(idA) && isBuiltTransform(idB) ) { + // then build the operator shape... + opShape = getBuiltShape(idA); + shiftTransf = getBuiltTransform(idB); + } else { + // otherwise, build the operand shapes + opShape = getBooleanReferencedShape(idA, shapes_info_sub); + shiftTransf = buildTransform(idB); + } + shiftX = shiftTransf->getTransform(); + shiftTransf->unref(); // delete from heap, we only needed to get the bare transform // TODO: remove that need, store the bare transforms as well... + + if (dynamic_cast<GeoShapeShift*>(boolShPtr)) { + GeoShapeShift* ptr = dynamic_cast<GeoShapeShift*>(boolShPtr); + ptr->m_op = opShape; + ptr->m_shift = shiftX; + ptr->m_op->ref(); + } + else { + std::cout << "ERROR!!! shape is not a Shift operator! Exiting..." << std::endl; + exit(EXIT_FAILURE); + } + } else { + std::cout << "ERROR! Undefined operator shape! This part of the code should not be reached! Exiting..." << std::endl; + exit(EXIT_FAILURE); + } + // then, store the now completed shape and continue to the next item + storeBuiltShape(shapeID, boolShPtr); + } +} + + + +GeoShape* ReadGeoModel::getBooleanReferencedShape(const unsigned int shapeID, type_shapes_boolean_info* shapes_info_sub) +{ + if (0 == shapeID) { + std::cout << "ERROR!! ShapeID = 0!" << std::endl; + exit(EXIT_FAILURE); + } + + GeoShape* shape; + // if A is built already, then take it from cache + if (isBuiltShape(shapeID)) { + if (m_deepDebug) std::cout << "operandA is built, taking it from cache..." << std::endl; // debug + shape = getBuiltShape(shapeID); + } else { + // if not built and not a boolean shape, then build it + if (!isShapeOperator(shapeID)) { + if (m_deepDebug) std::cout << "operandA is not built and not an operator, build it..." << std::endl; // debug + shape = buildShape( shapeID, shapes_info_sub ); + if ( shape == NULL ) { + std::cout << "ERROR!!! shape is NULL!" << std::endl; + exit(EXIT_FAILURE); + } + } + // if A is a boolean shape, then create an empty shape, + // store it for later completion, and use that + else { + if (m_deepDebug) std::cout << "operandA is not built and it is an operator, add it to build it later..." << std::endl; // debug + shape = addEmptyBooleanShapeForCompletion(shapeID, shapes_info_sub); + } } - // build the LogVol - QString logVolName = values[1]; +// inspectListShapesToBuild(m_shapes_info_sub); // debug + return shape; +} - // GET LOGVOL SHAPE - QString shapeId = values[2]; - GeoShape* shape = buildShape(shapeId); - // GET LOGVOL MATERIAL - QString matId = values[3]; - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "buildLogVol() - material Id:" << matId; - muxCout.unlock(); + +GeoShape* ReadGeoModel::addEmptyBooleanShapeForCompletion(const unsigned int shapeID, type_shapes_boolean_info* shapes_info_sub) +{ + // get the operands' IDs, + // build an empty instance of the appropriate boolean/operator shape, + // and store all of that together, by appending to this same container, + // so it will be visited at a later time during this very same loop + std::pair<unsigned int, unsigned int> ops = getBooleanShapeOperands(shapeID); + unsigned int opA = ops.first; + unsigned int opB = ops.second; + + // get the type and create an appropriate empty shape + std::string shType = getShapeType(shapeID); + GeoShape* shape = nullptr; + if (shType == "Intersection"){ + shape = new GeoShapeIntersection(); + } else if (shType == "Shift") { + shape = new GeoShapeShift(); + } else if (shType == "Subtraction") { + shape = new GeoShapeSubtraction(); + } else if (shType == "Union") { + shape = new GeoShapeUnion(); } - GeoMaterial* mat = buildMaterial(matId); - return new GeoLogVol(logVolName.toStdString(), shape, mat); + tuple_shapes_boolean_info tt (shapeID, shape, opA, opB); + shapes_info_sub->push_back(tt); //! Push the information about the new boolean shape at the end of the very same container we are iterating over + return shape; } -GeoSerialDenominator* ReadGeoModel::buildSerialDenominator(QString id) +// TODO: move this to utility class/file +std::vector<std::string> ReadGeoModel::splitString(const std::string& s, const char delimiter) +{ + std::vector<std::string> tokens; + std::string token; + std::istringstream tokenStream(s); + while (std::getline(tokenStream, token, delimiter)) + { + tokens.push_back(token); + } + return tokens; +} + +// // TODO: move this to utility class/file +//QStringList ReadGeoModel::toQStringList(std::vector<std::string> vec) +// { +// QStringList ll; +// for ( auto& str : vec ) { +// ll << QString::fromStdString(str); +// } +// return ll; +// } + +//// TODO: move this to utility class/file +//std::vector<std::string> ReadGeoModel::toStdVectorStrings(QStringList qlist) +//{ +// std::vector<std::string> vec; +// foreach(QString qstr, qlist) { +// vec.push_back(qstr.toStdString()); +// } +// return vec; +//} +// TODO: move this to utility class/file +void ReadGeoModel::printStdVectorStrings(std::vector<std::string> vec) { + for ( const auto& str : vec) { + std::cout << str << " "; + } + std::cout << std::endl; + return; +} + + +std::pair<unsigned int, unsigned int> ReadGeoModel::getBooleanShapeOperands(const unsigned int shapeID) +{ + std::pair<unsigned int, unsigned int> pair; + +// std::vector<std::string> paramsShape = toStdVectorStrings(m_shapes[ shapeID ]); + std::vector<std::string> paramsShape = m_shapes[ shapeID-1 ];// remember: shapes' IDs start from 1 + +// unsigned int id = std::stoi(paramsShape[0]); //! the ID of the boolean/operator shape + std::string type = paramsShape[1]; //! the GeoModel type of the shape + std::string parameters = paramsShape[2]; //! the parameters defining the shape, coming from the DB + + //! The Subtraction boolean shape has two operands, here we store their IDs + unsigned int opA = 0; + unsigned int opB = 0; + // get parameters from DB string + std::vector<std::string> shapePars = splitString( parameters, ';' ); + // std::cout << "shapePars size: " << shapePars.size() << std::endl; // debug only + + for (auto par : shapePars) { + std::vector<std::string> vars = splitString(par, '='); + std::string varName = vars[0]; + std::string varValue = vars[1]; + // std::cout << "vars ==> " << vars[0] << ", " << vars[1] << std::endl; // debug + if ( isShapeBoolean(type) ) { + if (varName == "opA") opA = std::stoi(varValue); + if (varName == "opB") opB = std::stoi(varValue); + } else if ("Shift" == type) { + if (varName == "A") opA = std::stoi(varValue); + if (varName == "X") opB = std::stoi(varValue); + } else { + std::cout << "ERROR!!! This type of shape cannot be handled here: " << type << ", --> Exiting..." << std::endl; + exit(EXIT_FAILURE); + } + } + if (opA == 0 || opB == 0) { + std::cout << "ERROR! Intersection/Subtraction/Union/Shift shape - operands' IDs are empty! (opA: " << opA << ", opB:" << opB << ")" << std::endl; + exit(EXIT_FAILURE); + } + pair.first = opA; + pair.second = opB; + + return pair; +} + + +bool ReadGeoModel::isShapeOperator(const unsigned int shapeId) +{ + return isShapeOperator( getShapeType(shapeId) ); +} +bool ReadGeoModel::isShapeOperator(const std::string type) +{ + std::unordered_set<std::string> opShapes = {"Intersection", "Shift", "Subtraction", "Union"}; + return (opShapes.find(type) != opShapes.end()); +} + +bool ReadGeoModel::isShapeBoolean(const unsigned int shapeId) +{ + return isShapeBoolean( getShapeType(shapeId) ); +} +bool ReadGeoModel::isShapeBoolean(const std::string type) +{ + std::unordered_set<std::string> opShapes = {"Intersection", "Subtraction", "Union"}; + return (opShapes.find(type) != opShapes.end()); +} + + + + +GeoBox* ReadGeoModel::buildDummyShape() { + return new GeoBox(30.0*SYSTEM_OF_UNITS::cm, 30*SYSTEM_OF_UNITS::cm, 30*SYSTEM_OF_UNITS::cm); +} + + +GeoLogVol* ReadGeoModel::buildLogVol(const unsigned int id) +{ + + if (isBuiltLog(id)) { + return getBuiltLog(id); + } + if (m_deepDebug) { muxCout.lock(); - qDebug() << "ReadGeoModel::buildSerialDenominator()"; + std::cout << "ReadGeoModel::buildLogVol()" << std::endl; muxCout.unlock(); } - return parseSerialDenominator( m_serialDenominators[id.toUInt()] ); -} -GeoSerialDenominator* ReadGeoModel::parseSerialDenominator(QStringList values) -{ + // get logVol properties from the DB + std::vector<std::string> values = m_logVols[id-1]; if (m_deepDebug) { muxCout.lock(); - qDebug() << "ReadGeoModel::parseSerialDenominator()"; + //FIXME: std::cout << "buildLogVol() - params:" << values << std::endl; muxCout.unlock(); } - QString id = values[0]; - QString baseName = values[1]; + + // get the parameters to build the GeoLogVol node + std::string logVolName = values[1]; + + // build the referenced GeoShape node + const unsigned int shapeId = std::stoi(values[2]); +// type_shapes_boolean_info shapes_info_sub; // tuple to store the boolean shapes to complete at a second stage +// GeoShape* shape = buildShape(shapeId, &shapes_info_sub); + // now, create the missing operand shapes for boolean/operator shapes +// createBooleanShapeOperands(&shapes_info_sub); + GeoShape* shape = getBuiltShape(shapeId); + if(!shape) { + std::cout << "ERROR!! While building a LogVol, Shape is NULL! Exiting..." <<std::endl; + exit(EXIT_FAILURE); + } + + // build the referenced GeoMaterial node + const unsigned int matId = std::stoi(values[3]); if (m_deepDebug) { muxCout.lock(); - qDebug() << "\tparseSerialDenominator() - ID:" << id << ", base-name:" << baseName; + std::cout << "buildLogVol() - material Id:" << matId << std::endl; muxCout.unlock(); } - return new GeoSerialDenominator(baseName.toStdString()); +// GeoMaterial* mat = buildMaterial(matId); + GeoMaterial* mat = getBuiltMaterial(matId); + if(!mat) { + std::cout << "ERROR!! While building a LogVol, Material is NULL! Exiting..." <<std::endl; + exit(EXIT_FAILURE); + } + + GeoLogVol* logPtr = new GeoLogVol(logVolName, shape, mat); + storeBuiltLog(id, logPtr); + return logPtr; } + // TODO: this should be moved to an Utilities class! void ReadGeoModel::printTrf(GeoTrf::Transform3D t) { muxCout.lock(); @@ -1806,64 +2467,53 @@ void ReadGeoModel::printTrf(GeoTrf::Transform3D t) { muxCout.unlock(); } -// TODO: should go in a QtUtils header-only class, to be used in other packages -QList<double> ReadGeoModel::convertQstringListToDouble(QStringList listin) { - QList<double> listout; - foreach (const QString &s, listin) { - listout.append(s.toDouble()); - } - return listout; -} - -void ReadGeoModel::printTransformationValues(QStringList values) { - QList<double> t = convertQstringListToDouble(values); - muxCout.lock(); - std::cout << "transformation input values: " << std::endl; - qWarning() << "[[" << t[0] << "," << t[1] << "," << t[2] << "][" - << t[3] << "," << t[4] << "," << t[5] << "][" - << t[6] << "," << t[7] << "," << t[8] << "][" - << t[9] << "," << t[10] << "," << t[11] << "]]"; - muxCout.unlock(); -} - - -GeoAlignableTransform* ReadGeoModel::buildAlignableTransform(QString id) +//// TODO: should go in a QtUtils header-only class, to be used in other packages +//QList<double> ReadGeoModel::convertQstringListToDouble(QStringList listin) { +// QList<double> listout; +// foreach (const QString &s, listin) { +// listout.append(s.toDouble()); +// } +// return listout; +//} + +//// TODO: move it to an utility class +//void ReadGeoModel::printTransformationValues(QStringList values) { +// QList<double> t = convertQstringListToDouble(values); +// muxCout.lock(); +// std::cout << "transformation input values: " << std::endl; +// std::cout << "[[" << t[0] << "," << t[1] << "," << t[2] << "][" +// << t[3] << "," << t[4] << "," << t[5] << "][" +// << t[6] << "," << t[7] << "," << t[8] << "][" +// << t[9] << "," << t[10] << "," << t[11] << "]]" << std::endl; +// muxCout.unlock(); +//} + + +GeoAlignableTransform* ReadGeoModel::buildAlignableTransform(const unsigned int id) { - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "ReadGeoModel::buildAlignableTransform()"; - muxCout.unlock(); + if (isBuiltAlignableTransform(id)) { + return getBuiltAlignableTransform(id); } - return parseAlignableTransform( m_alignableTransforms[id.toUInt()] ); -} - -GeoAlignableTransform* ReadGeoModel::parseAlignableTransform(QStringList values) -{ - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "ReadGeoModel::parseAlignableTransform()"; - muxCout.unlock(); - } - - QString id = values.takeFirst(); // it pops out the first element, leaving the other items in the list - // printTransformationValues(values); // DEBUG + + std::vector<std::string> values = m_alignableTransforms[id-1]; // nodes' IDs start from 1 + values.erase(values.begin()); // remove the first element, that is the 'id', leaving the other items in the list // get the 12 matrix elements - double xx = values[0].toDouble(); - double xy = values[1].toDouble(); - double xz = values[2].toDouble(); + double xx = std::stod(values[0]); + double xy = std::stod(values[1]); + double xz = std::stod(values[2]); - double yx = values[3].toDouble(); - double yy = values[4].toDouble(); - double yz = values[5].toDouble(); + double yx = std::stod(values[3]); + double yy = std::stod(values[4]); + double yz = std::stod(values[5]); - double zx = values[6].toDouble(); - double zy = values[7].toDouble(); - double zz = values[8].toDouble(); + double zx = std::stod(values[6]); + double zy = std::stod(values[7]); + double zz = std::stod(values[8]); - double dx = values[9].toDouble(); - double dy = values[10].toDouble(); - double dz = values[11].toDouble(); + double dx = std::stod(values[9]); + double dy = std::stod(values[10]); + double dz = std::stod(values[11]); GeoTrf::Transform3D txf; // build the rotation matrix with the first 9 elements @@ -1885,47 +2535,38 @@ GeoAlignableTransform* ReadGeoModel::parseAlignableTransform(QStringList values) txf(2,3)=dz; // printTrf(txf); // DEBUG - return new GeoAlignableTransform(txf); + GeoAlignableTransform* tr = new GeoAlignableTransform(txf); + storeBuiltAlignableTransform(id, tr); + return tr; } -GeoTransform* ReadGeoModel::buildTransform(QString id) -{ - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "ReadGeoModel::buildTransform()"; - muxCout.unlock(); - } - return parseTransform( m_transforms[id.toUInt()] ); -} - -GeoTransform* ReadGeoModel::parseTransform(QStringList values) +GeoTransform* ReadGeoModel::buildTransform(const unsigned int id) { - muxCout.lock(); - if (m_deepDebug) qDebug() << "ReadGeoModel::parseTransform()"; - if (m_deepDebug) qDebug() << "values:" << values; - muxCout.unlock(); - - QString id = values.takeFirst(); // it pops out the first element, the 'id', leaving the other items in the list - // printTransformationValues(values); // DEBUG - - // get the 12 matrix elements - double xx = values[0].toDouble(); - double xy = values[1].toDouble(); - double xz = values[2].toDouble(); - - double yx = values[3].toDouble(); - double yy = values[4].toDouble(); - double yz = values[5].toDouble(); - - double zx = values[6].toDouble(); - double zy = values[7].toDouble(); - double zz = values[8].toDouble(); - - double dx = values[9].toDouble(); - double dy = values[10].toDouble(); - double dz = values[11].toDouble(); - + if (isBuiltTransform(id)) { + return getBuiltTransform(id); + } + + std::vector<std::string> values = m_transforms[id-1]; // nodes' IDs start from 1 + values.erase(values.begin()); // remove the first element, that is the 'id', leaving the other items in the list + + // get the 12 matrix elements + double xx = std::stod(values[0]); + double xy = std::stod(values[1]); + double xz = std::stod(values[2]); + + double yx = std::stod(values[3]); + double yy = std::stod(values[4]); + double yz = std::stod(values[5]); + + double zx = std::stod(values[6]); + double zy = std::stod(values[7]); + double zz = std::stod(values[8]); + + double dx = std::stod(values[9]); + double dy = std::stod(values[10]); + double dz = std::stod(values[11]); + GeoTrf::Transform3D txf; // build the rotation matrix with the first 9 elements txf(0,0)=xx; @@ -1946,149 +2587,282 @@ GeoTransform* ReadGeoModel::parseTransform(QStringList values) txf(2,3) = dz; // printTrf(txf); // DEBUG - return new GeoTransform(txf); + GeoTransform* tr = new GeoTransform(txf); + storeBuiltTransform(id, tr); + return tr; } -GeoSerialTransformer* ReadGeoModel::buildSerialTransformer(QString nodeId) +GeoSerialTransformer* ReadGeoModel::buildSerialTransformer(const unsigned int id) { muxCout.lock(); - if (m_deepDebug) qDebug() << "ReadGeoModel::buildSerialTransformer()"; - muxCout.unlock(); - - QStringList values = m_serialTransformers[nodeId.toUInt()]; - muxCout.lock(); - if (m_deepDebug) qDebug() << "values:" << values; + if (m_deepDebug) std::cout << "ReadGeoModel::buildSerialTransformer()" << std::endl; muxCout.unlock(); - // std::cout <<"ST * " << values[0].toStdString() << " " << values[1].toStdString() << " " << values[2].toStdString() << std::endl; - muxCout.lock(); - if (m_deepDebug) qDebug() << "buildSerialTransformer() - ST * " << values[0] << ", " << values[1] << ", " << values[2] << ", " << values[3] << ", " << values[4]; - muxCout.unlock(); - - QString id = values[0]; - QString functionId = values[1]; - QString physVolId = values[2]; - QString physVolTableIdStr = values[3]; - QString copies = values[4]; - - unsigned int physVolTableId = physVolTableIdStr.toUInt(); + const unsigned int nodeID = id-1; // nodes' IDs start from 1 + std::vector<std::string> values = m_serialTransformers[nodeID]; - QString physVolType = m_tableid_tableName[physVolTableId]; + const unsigned int functionId = std::stoi(values[1]); + const unsigned int physVolId = std::stoi(values[2]); + const unsigned int physVolTableId = std::stoi(values[3]); + const unsigned int copies = std::stoi(values[4]); - muxCout.lock(); - if (m_deepDebug) qDebug() << "\tbuildSerialTransformer() - ID:" << id << ", functionId:" << functionId << ", physVolId:" << physVolId << ", physVolTableId:" << physVolTableId << ", copies:" << copies; - muxCout.unlock(); - - // GET FUNCTION - muxCout.lock(); - if (m_deepDebug) qDebug() << "\tbuildSerialTransformer() - function Id:" << functionId; - muxCout.unlock(); + // GET THE REFERENCED FUNCTION TRANSFUNCTION func = buildFunction(functionId); - // GET PHYSVOL - muxCout.lock(); - if (m_deepDebug) qDebug() << "\tbuildSerialTransformer() - referenced physVol - Id:" << physVolId << ", type:" << physVolType << "tableId:" << physVolTableIdStr; - muxCout.unlock(); - const GeoVPhysVol* physVol = buildVPhysVol(physVolId, physVolTableIdStr, "1"); // we use "1" as default copyNumber: taking the first copy of the VPhysVol as the referenced volume - - muxCout.lock(); - if (m_deepDebug) qDebug() << "\tbuildSerialTransformer() - physVol:" << physVol << ", function:" << &func; - muxCout.unlock(); + // GET THE REFERENCED VPHYSVOL + const GeoVPhysVol* vphysVol = buildVPhysVolInstance(physVolId, physVolTableId, 1); // we use "1" as default copyNumber: taking the first copy of the VPhysVol as the referenced volume // get PhysVol or FullPhysVol pointer and return the SerialTransformer - if (dynamic_cast<const GeoFullPhysVol*>(physVol)) { - const GeoFullPhysVol* vol = dynamic_cast<const GeoFullPhysVol*>(physVol); - return new GeoSerialTransformer(vol, &func, copies.toUInt() ); + if (dynamic_cast<const GeoFullPhysVol*>(vphysVol)) { + const GeoFullPhysVol* vol = dynamic_cast<const GeoFullPhysVol*>(vphysVol); + GeoSerialTransformer* nodePtr = new GeoSerialTransformer(vol, &func, copies ); + storeBuiltSerialTransformer(id, nodePtr); + return nodePtr; } - const GeoPhysVol* vol = dynamic_cast<const GeoPhysVol*>(physVol); - return new GeoSerialTransformer(vol, &func, copies.toUInt() ); - + const GeoPhysVol* vol = dynamic_cast<const GeoPhysVol*>(vphysVol); + GeoSerialTransformer* nodePtr = new GeoSerialTransformer(vol, &func, copies ); + storeBuiltSerialTransformer(id, nodePtr); + return nodePtr; } -TRANSFUNCTION ReadGeoModel::buildFunction(QString id) +TRANSFUNCTION ReadGeoModel::buildFunction(const unsigned int id) { - muxCout.lock(); - if (m_deepDebug) qDebug() << "ReadGeoModel::buildFunction()"; - muxCout.unlock(); - QStringList values = m_functions[id.toUInt()]; - return parseFunction( values[1].toStdString() ); -} - -TRANSFUNCTION ReadGeoModel::parseFunction(const std::string& expr) -{ - muxCout.lock(); - if (m_deepDebug) qDebug() << "ReadGeoModel::parseFunction(const std::string& expr)"; - if (m_deepDebug) qDebug() << "expression: " << QString::fromStdString(expr); - muxCout.unlock(); + /* FIXME: see below + if( isBuiltFunction(id) ) { + GeoXF::Function* fPtr = getBuiltFunction(id); + TRANSFUNCTION tf = (*fPtr); // Remember: "typedef const Function & TRANSFUNCTION" + return tf; + } + */ - if (expr.empty()) { - qFatal("FATAL ERROR!! Function expression is empty!! Aborting..."); - abort(); + std::string expr = m_functions[id-1][1]; // nodes' IDs start from 1 + + if (0==expr.size()) { + muxCout.lock(); + std::cout << "FATAL ERROR!! Function expression is empty!! Aborting..." << std::endl; + muxCout.unlock(); + exit(EXIT_FAILURE); } - muxCout.lock(); - if (m_deepDebug) qDebug() << "expression:" << QString::fromStdString(expr); - muxCout.unlock(); TransFunctionInterpreter interpreter; TFPTR func=interpreter.interpret( expr ); - // if (m_deepDebug) qDebug() << "expression interpreted"; - return *(func.release()); // make func returns a pointer to the managed object and releases the ownership, then get the object dereferencing the pointer + TRANSFUNCTION tf = *(func.release()); // make func returns a pointer to the managed object and releases the ownership, then get the object dereferencing the pointer + + /* FIXME: At the moment, enabling this cache makes the app crash at the end, when calling the destructor. I suspect because the pointers are not correct and so removing them throws an error. + // Get a non-const pointer to the Function object, + // and store that into the cache. + // Remember: "typedef const Function & TRANSFUNCTION" + const GeoXF::Function* fPtrConst = &tf; + GeoXF::Function* fPtr = const_cast <GeoXF::Function*>(fPtrConst); + storeBuiltFunction(id, fPtr); + */ + + return tf; } -GeoNameTag* ReadGeoModel::buildNameTag(QString id) // TODO: Remove the "parseABC" methods, put everything into "buildABC". The "parseABC" are there for historical reasons. -{ - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "ReadGeoModel::buildNameTag()"; - muxCout.unlock(); - } - return parseNameTag( m_nameTags[id.toUInt()] ); +// --- methods for caching GeoShape nodes --- +bool ReadGeoModel::isBuiltShape(const unsigned int id) +{ + return ( ! (m_memMapShapes.find(id) == m_memMapShapes.end()) ); } - -GeoNameTag* ReadGeoModel::parseNameTag(QStringList values) +void ReadGeoModel::storeBuiltShape(const unsigned int id, GeoShape* nodePtr) { - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "ReadGeoModel::parseNameTag()"; - muxCout.unlock(); - } - QString id = values[0]; - QString name = values[1]; - if (m_deepDebug) { - muxCout.lock(); - qDebug() << "nameTag:" << name; - muxCout.unlock(); - } - return new GeoNameTag(name.toStdString()); + m_memMapShapes[id] = nodePtr; } - - -bool ReadGeoModel::isNodeBuilt(const QString id, const QString tableId, const QString copyNumber) +GeoShape* ReadGeoModel::getBuiltShape(const unsigned int id) +{ + return m_memMapShapes[id]; // this is a map, and 'id' is the key +} + +// --- methods for caching GeoLogVol nodes --- +bool ReadGeoModel::isBuiltLog(const unsigned int id) +{ + return (id <= m_memMapLogVols.size()); // vector: we exploit the fact that we built the vols ordered by their IDs +} +void ReadGeoModel::storeBuiltLog(const unsigned int id, GeoLogVol* nodePtr) +{ + m_memMapLogVols.push_back(nodePtr); +} +GeoLogVol* ReadGeoModel::getBuiltLog(const unsigned int id) +{ + return m_memMapLogVols[id-1]; // nodes' IDs start from 1 +} + +// --- methods for caching GeoPhysVol nodes --- +bool ReadGeoModel::isBuiltPhysVol(const unsigned int id) +{ + return (id <= m_memMapPhysVols.size()); // vector: we exploit the fact that we built the vols ordered by their IDs +} +void ReadGeoModel::storeBuiltPhysVol(const unsigned int id, GeoPhysVol* nodePtr) +{ + m_memMapPhysVols.push_back(nodePtr); +} +GeoPhysVol* ReadGeoModel::getBuiltPhysVol(const unsigned int id) { - std::lock_guard<std::mutex> lk(muxStore); - // qDebug() << "ReadGeoModel::isNodeBuilt(): " << id << tableId << copyNumber; - QString key = id + ":" + tableId + ":" + copyNumber; - return m_memMap.contains(key); + return m_memMapPhysVols[id-1]; // nodes' IDs start from 1 } + +// --- methods for caching GeoFullPhysVol nodes --- +bool ReadGeoModel::isBuiltFullPhysVol(const unsigned int id) +{ + return (id <= m_memMapFullPhysVols.size()); // vector: we exploit the fact that we built the vols ordered by their IDs +} +void ReadGeoModel::storeBuiltFullPhysVol(const unsigned int id, GeoFullPhysVol* nodePtr) +{ + m_memMapFullPhysVols.push_back(nodePtr); // vector, we store them in the order of IDs +} +GeoFullPhysVol* ReadGeoModel::getBuiltFullPhysVol(const unsigned int id) +{ + return m_memMapFullPhysVols[id-1]; // nodes' IDs start from 1 -GeoGraphNode* ReadGeoModel::getNode(const QString id, const QString tableId, const QString copyN) +} + +// --- methods for caching GeoMaterial nodes --- +bool ReadGeoModel::isBuiltMaterial(const unsigned int id) +{ + return (id <= m_memMapMaterials.size()); +} +void ReadGeoModel::storeBuiltMaterial(const unsigned int id, GeoMaterial* nodePtr) +{ + m_memMapMaterials.push_back(nodePtr); +} +GeoMaterial* ReadGeoModel::getBuiltMaterial(const unsigned int id) +{ + return m_memMapMaterials[id-1]; +} + +// --- methods for caching GeoElement nodes --- +bool ReadGeoModel::isBuiltElement(const unsigned int id) +{ + return (id <= m_memMapElements.size()); +} +void ReadGeoModel::storeBuiltElement(const unsigned int id, GeoElement* nodePtr) +{ + m_memMapElements.push_back(nodePtr); +} +GeoElement* ReadGeoModel::getBuiltElement(const unsigned int id) +{ + return m_memMapElements[id-1]; +} + +// --- methods for caching GeoTransform nodes --- +bool ReadGeoModel::isBuiltTransform(const unsigned int id) { - std::lock_guard<std::mutex> lk(muxGet); - if (m_deepDebug) qDebug() << "ReadGeoModel::getNode(): " << id << tableId << copyN; - QString key = id + ":" + tableId + ":" + copyN; - return m_memMap[key]; + return (id <= m_memMapTransforms.size()); // vector: we exploit the fact that we built the vols ordered by their IDs } +void ReadGeoModel::storeBuiltTransform(const unsigned int id, GeoTransform* nodePtr) +{ + m_memMapTransforms.push_back(nodePtr); // vector, we store them in the order of IDs -void ReadGeoModel::storeNode(const QString id, const QString tableId, const QString copyN, GeoGraphNode* node) +} +GeoTransform* ReadGeoModel::getBuiltTransform(const unsigned int id) +{ + return m_memMapTransforms[id-1]; // nodes' IDs start from 1 +} + +// --- methods for caching GeoAlignableTransform nodes --- +bool ReadGeoModel::isBuiltAlignableTransform(const unsigned int id) { - std::lock_guard<std::mutex> lk(muxStore); - if (m_deepDebug) qDebug() << "ReadGeoModel::storeNode(): " << id << tableId << copyN << node; - QString key = id + ":" + tableId + ":" + copyN; - m_memMap[key] = node; - if (m_deepDebug) qDebug() << "Store done."; + return (id <= m_memMapAlignableTransforms.size()); // vector: we exploit the fact that we built the vols ordered by their IDs +} +void ReadGeoModel::storeBuiltAlignableTransform(const unsigned int id, GeoAlignableTransform* nodePtr) +{ + m_memMapAlignableTransforms.push_back(nodePtr); // vector, we store them in the order of IDs + +} +GeoAlignableTransform* ReadGeoModel::getBuiltAlignableTransform(const unsigned int id) +{ + return m_memMapAlignableTransforms[id-1]; // nodes' IDs start from 1 + +} + +// --- methods for caching GeoSerialDenominator nodes --- +bool ReadGeoModel::isBuiltSerialDenominator(const unsigned int id) +{ + return (id <= m_memMapSerialDenominators.size()); // vector: we exploit the fact that we built the vols ordered by their IDs +} +void ReadGeoModel::storeBuiltSerialDenominator(const unsigned int id, GeoSerialDenominator* nodePtr) +{ + m_memMapSerialDenominators.push_back(nodePtr); // vector, we store them in the order of IDs + +} +GeoSerialDenominator* ReadGeoModel::getBuiltSerialDenominator(const unsigned int id) +{ + return m_memMapSerialDenominators[id-1]; // nodes' IDs start from 1 + +} + +// --- methods for caching GeoNameTag nodes --- +bool ReadGeoModel::isBuiltNameTag(const unsigned int id) +{ + return (id <= m_memMapNameTags.size()); // vector: we exploit the fact that we built the vols ordered by their IDs +} +void ReadGeoModel::storeBuiltNameTag(const unsigned int id, GeoNameTag* nodePtr) +{ + m_memMapNameTags.push_back(nodePtr); // vector, we store them in the order of IDs + +} +GeoNameTag* ReadGeoModel::getBuiltNameTag(const unsigned int id) +{ + return m_memMapNameTags[id-1]; // vector, but nodes' IDs start from 1 + +} + +// --- methods for caching GeoSerialDenominator nodes --- +bool ReadGeoModel::isBuiltSerialTransformer(const unsigned int id) +{ + return (id <= m_memMapSerialTransformers.size()); // vector: we exploit the fact that we built the vols ordered by their IDs +} +void ReadGeoModel::storeBuiltSerialTransformer(const unsigned int id, GeoSerialTransformer* nodePtr) +{ + m_memMapSerialTransformers.push_back(nodePtr); // vector, we store them in the order of IDs + +} +GeoSerialTransformer* ReadGeoModel::getBuiltSerialTransformer(const unsigned int id) +{ + return m_memMapSerialTransformers[id-1]; // remember: nodes' IDs start from 1 + +} + /* FIXME: + // --- methods for caching Functions nodes --- + bool ReadGeoModel::isBuiltFunction(const unsigned int id) + { + return (id <= m_memMapFunctions.size()); // vector: we exploit the fact that we built the vols ordered by their IDs + } + void ReadGeoModel::storeBuiltFunction(const unsigned int id, GeoXF::Function* nodePtr) + { + m_memMapFunctions.push_back(nodePtr); // vector, we store them in the order of IDs + + } + GeoXF::Function* ReadGeoModel::getBuiltFunction(const unsigned int id) + { + return m_memMapFunctions[id-1]; // remember: nodes' IDs start from 1 + } +*/ + +// --- methods for caching GeoPhysVol/GeoFullPhysVol nodes --- +std::string getVPhysVolKey(const unsigned int id, const unsigned int tableId, const unsigned int copyNumber) +{ + std::string key = std::to_string(id) + ":" + std::to_string(tableId) + ":" + std::to_string(copyNumber); + return key; +} +void ReadGeoModel::storeVPhysVol(const unsigned int id, const unsigned int tableId, const unsigned int copyN, GeoGraphNode* nodePtr) +{ + std::lock_guard<std::mutex> lk(muxVPhysVol); + std::string key = getVPhysVolKey(id, tableId, copyN); + m_memMap[key] = nodePtr; +} +GeoGraphNode* ReadGeoModel::getVPhysVol(const unsigned int id, const unsigned int tableId, const unsigned int copyN) +{ + std::lock_guard<std::mutex> lk(muxVPhysVol); + std::string key = getVPhysVolKey(id, tableId, copyN); + if (m_memMap.find(key) == m_memMap.end()) { + return nullptr; // if volume is not found in cache + } + return m_memMap[key]; } } /* namespace GeoModelIO */ diff --git a/GeoModelWrite/src/WriteGeoModel.cpp b/GeoModelWrite/src/WriteGeoModel.cpp index 75f47429ba9677e2ac4e06158ed8dc183f4b983f..3fbf52f155ec9a87c37c0d17f963068feb8486f7 100644 --- a/GeoModelWrite/src/WriteGeoModel.cpp +++ b/GeoModelWrite/src/WriteGeoModel.cpp @@ -127,8 +127,8 @@ void WriteGeoModel::handleVPhysVolObjects(const GeoVPhysVol* vol) // check the volume position in the geometry tree GeoNodePath* path = getPath(); unsigned int len = path->getLength(); - const GeoVPhysVol* tailVol = path->getTail(); - const GeoVPhysVol* headVol = path->getHead(); +// const GeoVPhysVol* tailVol = path->getTail(); +// const GeoVPhysVol* headVol = path->getHead(); const GeoVPhysVol* upperVol = nullptr; if (len > 1) upperVol = path->getItem(len-2); // item(len-1) is THIS volume ==> the length is updated when visiting a VPhysVol node @@ -186,7 +186,7 @@ void WriteGeoModel::handleVPhysVolObjects(const GeoVPhysVol* vol) if (doGetParentNode) { - bool isShared = vol->isShared(); +// bool isShared = vol->isShared(); //JFB Commented out: qDebug() << "is this node shared?" << isShared; //if (isShared) @@ -212,7 +212,7 @@ void WriteGeoModel::handleVPhysVolObjects(const GeoVPhysVol* vol) } // counting children - unsigned int nChildren = vol->getNChildVols(); +// unsigned int nChildren = vol->getNChildVols(); //JFB Commented out: qDebug() << "number of child physical volumes:" << nChildren; //JFB Commented out: qDebug() << "[number of PhysVol and SerialTransformer child nodes:" << vol->getNChildVolAndST() << "]"; diff --git a/README.md b/README.md index 591824a64af48ba829aee4291a2f954c44a870e1..a4ff7dc752b1131cc38704c301245ec8d13cb490 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,36 @@ sudo apt-get update sudo apt install geomodel-io-dev +---- + +# Steering the library behavior + +## Number of threads + +At run time, you can choose to run in serial mode by set this environment variable before running any code built on top of `GeoModelIO`: + +``` +export GEOMODEL_ENV_IO_NTHREADS=0 +``` + +You can also choose the number of threads for GeoModelIO to use during I/O operations. For example, if you want to use 8 threads, just set: + +``` +export GEOMODEL_ENV_IO_NTHREADS=8 +``` + +You can also ask `GeoModelIO` to use exactly the number of threads supported by your platform by passing `-1` to the variable, as here below. On a quad-core CPU offering 8 native threads, 8 threads will be used by `GeoModelIO`. + +``` +export GEOMODEL_ENV_IO_NTHREADS=-1 +``` + + ---- # Build -If you want to work on the library code, you have to build it by yourself. +If you want to work on the library code, or if you want to use the latest version of the code, you have to build it by yourself. First, install or build the [GeoModelCore](https://gitlab.cern.ch/GeoModelDev/GeoModelCore) dependency, by following [its instructions](https://gitlab.cern.ch/GeoModelDev/GeoModelCore/blob/master/README.md). @@ -46,10 +71,41 @@ cmake ../GeoModelIO make ``` +## Build options + +### Timing information + +#### GEOMODEL_IO_TIMING + +You can ask `GeoModelIO` to print timing information, by setting the `GEOMODEL_IO_TIMING` variable while configuring the build with CMake; for example, by using: + +``` +cmake -DGEOMODEL_IO_TIMING=ON ../GeoModelIO/ +``` + +### Debug information + +#### GEOMODEL_IO_DEBUG + +You can ask `GeoModelIO` to print debug information, by setting the `GEOMODEL_IO_DEBUG` variable while configuring the build with CMake: + +``` +cmake -DGEOMODEL_IO_DEBUG=ON ../GeoModelIO/ +``` + +#### GEOMODEL_IO_DEEP_DEBUG + +You can ask `GeoModelIO` to print even more debug information, by setting the `GEOMODEL_IO_DEEP_DEBUG` variable while configuring the build with CMake: + +``` +cmake -DGEOMODEL_IO_DEEP_DEBUG=ON ../GeoModelIO/ +``` + + ## Local 'install' path -If you want to build a custom version of the library to be used to develop client code, -you can create a self-contained area by defining a local `install` path; +If you want to build a custom version of the library to be used to develop client code, +you can create a self-contained area by defining a local `install` path; you can do it by adding the option `-DCMAKE_INSTALL_PREFIX=../install` when running CMake: ``` diff --git a/cmake/BuildTypeInfo.cmake b/cmake/PrintBuildInfo.cmake similarity index 92% rename from cmake/BuildTypeInfo.cmake rename to cmake/PrintBuildInfo.cmake index 7c954dbaead9cadc5b579270d1c28c7f4f791c62..86819e67bbdc491b7bed97b6d34f86c6c0316e36 100644 --- a/cmake/BuildTypeInfo.cmake +++ b/cmake/PrintBuildInfo.cmake @@ -1,15 +1,15 @@ # # Utility snippet to set a default build type if none was specified by the user # at command line. -# +# # Source: https://blog.kitware.com/cmake-and-the-default-build-type/ by M.D.Hanwell # # Set a default build type if none was specified set(default_build_type "Release") -# If the source folder seems to contain a .git folder, +# If the source folder seems to contain a .git folder, # then we assume the user is developing the code and -# we set a more useful type +# we set a more useful type if(EXISTS "${CMAKE_SOURCE_DIR}/.git") set(default_build_type "RelWithDebInfo") endif() diff --git a/cmake/configure_cpp_options.cmake b/cmake/configure_cpp_options.cmake new file mode 100644 index 0000000000000000000000000000000000000000..2eb341268ea0b307660ac4065c054254faa2a856 --- /dev/null +++ b/cmake/configure_cpp_options.cmake @@ -0,0 +1,23 @@ + +# +# Set build options and C++ standards and options +# +# This file sets up +# +# CMAKE_BUILD_TYPE +# CMAKE_CXX_STANDARD +# CMAKE_CXX_EXTENSIONS +# CMAKE_CXX_STANDARD_REQUIRED +# +# The options can be overridden at configuration time by using, e.g.: +# `cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_STANDARD=14 ../GeoModelIO` +# on the command line. +# + + +# Set default build options. +set( CMAKE_BUILD_TYPE "Release" CACHE STRING "CMake build mode to use" ) +set( CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard used for the build" ) +set( CMAKE_CXX_EXTENSIONS FALSE CACHE BOOL "(Dis)allow using GNU extensions" ) +set( CMAKE_CXX_STANDARD_REQUIRED TRUE CACHE BOOL + "Require the specified C++ standard for the build" ) diff --git a/cmake/configure_definitions.cmake b/cmake/configure_definitions.cmake new file mode 100644 index 0000000000000000000000000000000000000000..765624adc6d0cb91b7ad9cf19018d07cb2ff754e --- /dev/null +++ b/cmake/configure_definitions.cmake @@ -0,0 +1,29 @@ + +# +# Check for options to switch various optional functionalities +# in the source code +# +# This file sets up +# +# GEOMODEL_IO_TIMING +# GEOMODEL_IO_DEBUG +# GEOMODEL_IO_DEEP_DEBUG +# +# +# The options can be specified at configuration time by using, e.g.: +# `cmake -DGEOMODEL_IO_TIMING=ON` +# on the command line. +# + +IF ( DEFINED GEOMODEL_IO_TIMING ) + message( STATUS "You asked to print timing information... (`-DGEOMODEL_IO_TIMING=${GEOMODEL_IO_TIMING}`)") + ADD_DEFINITIONS(-DGEOMODEL_IO_TIMING) +ENDIF() +IF ( DEFINED GEOMODEL_IO_DEBUG ) + message( STATUS "You asked to print debug information... (`-DGEOMODEL_IO_DEBUG=${GEOMODEL_IO_DEBUG}`)") + ADD_DEFINITIONS(-DGEOMODEL_IO_DEBUG) +ENDIF() +IF ( DEFINED GEOMODEL_IO_DEBUG_VERBOSE ) + message( STATUS "You asked to print verbose debug information... (`-DGEOMODEL_IO_DEBUG_VERBOSE=${GEOMODEL_IO_DEBUG_VERBOSE}`)") + ADD_DEFINITIONS(-DGEOMODEL_IO_DEBUG_VERBOSE) +ENDIF() diff --git a/scripts/test_geomodelio_application.sh b/scripts/test_geomodelio_application.sh new file mode 100755 index 0000000000000000000000000000000000000000..65501340fc75fc81b6c696df1ccca571cd75fd3b --- /dev/null +++ b/scripts/test_geomodelio_application.sh @@ -0,0 +1,142 @@ +#! /bin/bash +# test_geomodelio_application.sh +# + +# RUN GEOMODELIO-BASED APPLICATION N TIMES AND PARSE THE TIME/MEMORY OUTPUT +# +# You should have a GeoModelIO-based application compiled. +# For example, you can use GeoModelExamples/HelloGeoRead. +# After compiling that, you will have a `helloGeoRead` executable in the +# build folder. Just source this script passing the executable name as +# parameter. +# Optional arguments: +# - max number of concurrent threads as the second parameter [default = 8] +# - number of repetitions per test as the third parameter [default = 5] +# - `time` application to use [default = `/usr/bin/time`] --> TODO: parsing is for GNU time at the moment! +# +# For example: +# +# source ../GeoModelIO/scripts/time_geomodelio_app.sh helloGeoRead 4 10 +# +# will test `helloGeoRead` with up to 4 threads, 10 times each +# +# The output of all tests will be saved in an `output.txt` file. +# +# On macOS, we suggest to install GNU time with Homebrew (http:brew.sh): +# +# brew install gtime +# +# Then, you can use `gtime` as timing application by passing that as +# the fourth parameter: +# +# source ../GeoModelIO/scripts/time_geomodelio_app.sh helloGeoRead 4 10 gtime +# +# +# ----------------- +# +# On Linux, use `/usr/bin/time` instead of the shell built-in `time` function. +# Then capture the `stderr` into a separate file from the `stdout` of your +# application command. +# Since the elapsed real time is the first word of time's output, +# that's easy to grab with `awk`. +# +# `grep` has a `-q` switch so no need to redirect to `/dev/null` to avoid +# cluttering the output with the output of `grep`. +# +# TODO: +# GNU time does not allow to use milliseconds, needed for quick commands +# +# +# Sources: +# - https://unix.stackexchange.com/a/533451/245009 (parse `time` output) +# - https://askubuntu.com/a/29379/830120 {get return value of a command} +# - https://unix.stackexchange.com/a/246934/245009 (use of `|| break 2` to stop loops inside a bash script with 'ctrl-c') +# - https://stackoverflow.com/a/16040609/320369 +# - https://stackoverflow.com/a/10959179/320369 +# - https://stackoverflow.com/a/2013573/320369 +# - https://bash.cyberciti.biz/guide/Create_usage_messages +# - https://www.cyberciti.biz/faq/unix-linux-iterate-over-a-variable-range-of-numbers-in-bash/ +# - https://stackoverflow.com/a/6002329/320369 +# - https://unix.stackexchange.com/a/122848/245009 +# - https://stackoverflow.com/a/39458935/320369 +# - https://stackoverflow.com/a/22190928/320369 (awk - set line number) +# - https://stackoverflow.com/a/8467449/320369 (printf / echo) +# - https://stackoverflow.com/a/2440685/320369 ('read' line from file) +# - https://stackoverflow.com/a/4181721/320369 (concatenate strings - I) +# - https://stackoverflow.com/a/2250199/320369 (concatenate strings - II) +# - https://stackoverflow.com/a/20915874/320369 (long, complete answer about getting command output & logfiles in general) +# - https://github.com/torvalds/linux/blob/master/tools/memory-model/scripts/cmplitmushist.sh +# + +# Set the executable to test +EXEC=$1 # first command line argument + +# Verify the type of input and number of value +# Display an error message if the executable to test was not passed and +# exit the shell script with a status of 1 using exit 1 command. +[ $# -eq 0 ] && { echo "Usage: $0 executable [nThreadsMin] [nThreadsMax] [nTimesPerTest]"; exit 1; } + + +# Variables. Assign default values if not specified by the user +NTHREADS_MIN=${2:-2} # default value: up to 8 threads +NTHREADS_MAX=${3:-8} # default value: up to 8 threads +MTIMES=${4:-5} # default value: 5 repetitions for each test +# APP=${4:-"/usr/bin/time"} # default timing application: /usr/bin/time +APP=${5:-"gtime"} # TODO: output parsing is only for GNU tinme at the moment + +function eval_command() { + "$@"; +} + +# init the output file +HEADER="ThreadsN TestN Timestamp TimeUser[s] TimeElapsed[s] MaxMem[Kb] Status" +printf "$HEADER\n" > output.txt + +echo "Testing the application ${1} with $NTHREADS_MIN-$NTHREADS_MAX concurrent threads, $MTIMES, using the '$APP' timing application." +# Test up to N concurrent threads +for (( n=$NTHREADS_MIN; n<=$NTHREADS_MAX; n++ )) +do + export GEOMODEL_ENV_IO_NTHREADS=$n; + echo "Using # worker threads: $n (0 means 'serial mode') - [The 'GEOMODEL_ENV_IO_NTHREADS' env var has been set to: $GEOMODEL_ENV_IO_NTHREADS]" + + ## run command M times + for (( m=1; m<=$MTIMES; m++ )) + do + # launch the actual command, through an 'eval function' + eval_command $APP $EXEC > log.out 2>time.out || break 2 + # get the return value of the command, to check if it succeeded + return_value=$? + + time_user_str=$(awk 'NR==1{print $1}' time.out) + time_elapsed_str=$(awk 'NR==1{print $3}' time.out) + mem_max_str=$(awk 'NR==1{print $6}' time.out) + + time_user=$(awk -F'user' '{print $1}' <<< "$time_user_str") + time_elapsed=$(awk -F'elapsed' '{print $1}' <<< "$time_elapsed_str") + mem_max=$(awk -F'maxresident' '{print $1}' <<< "$mem_max_str") + + date_str=$(date) + date_epoch=$(date +%s) + + OUT_VERBOSE="threads $n - test $m - $date_str ($date_epoch) - user $time_user [s] elapsed $time_elapsed [s] maxmem $mem_max [Kb]" + OUT="$n $m $date_epoch $time_user $time_elapsed $mem_max" + + # test the success status of the executable + if [ $return_value -ne 0 ]; then + echo "Return code was not zero but $retval --> executable probably failed" + FLAG="Failed" + else + FLAG="Succeeded" + fi + + # concatenate the test results and the global statement + OUT_VERBOSE+=" $FLAG" + OUT="$OUT $FLAG" + + + # print on screen and save to file + printf "$OUT_VERBOSE\n" + printf "$OUT\n" >> output.txt + + done +done