diff --git a/src/coreComponents/codingUtilities/StringUtilities.hpp b/src/coreComponents/codingUtilities/StringUtilities.hpp index 8bd9c805d42..39cacf9b30e 100644 --- a/src/coreComponents/codingUtilities/StringUtilities.hpp +++ b/src/coreComponents/codingUtilities/StringUtilities.hpp @@ -207,6 +207,25 @@ array1d< T > fromStringToArray( string const & str ) template< typename T > string toMetricPrefixString( T const & value ); +/** + * @brief Compute the length of a constant string at compile-time. + */ +// TODO c++17: this function is to remove in favor of std::string_view +constexpr size_t cstrlen( char const * const str ) +{ + if( str ) + { + char const * ptr = str; + for(; *ptr != '\0'; ++ptr ) + {} + return ptr - str; + } + else + { + return 0; + } +} + } // namespace stringutilities } // namespace geos diff --git a/src/coreComponents/common/Logger.cpp b/src/coreComponents/common/Logger.cpp index 024230d1af8..56c20135d56 100644 --- a/src/coreComponents/common/Logger.cpp +++ b/src/coreComponents/common/Logger.cpp @@ -19,10 +19,42 @@ // Source includes #include "Logger.hpp" #include "Path.hpp" +#include "codingUtilities/StringUtilities.hpp" namespace geos { +/** + * @brief Insert an exception message in another one. + * @param originalMsg original exception message (i.e. thrown from LVARRAY_THROW or GEOSX_THROW) + * @param msgToInsert message to insert at the top of the originalMsg + */ +std::string InsertExMsg( std::string const & originalMsg, std::string const & msgToInsert ) +{ + std::string newMsg( originalMsg ); + + size_t insertPos = 0; + // for readability purposes, we try to insert the message after the "***** Rank N: " or after "***** " instead of at the top. + static auto constexpr rankLogStart = "***** Rank "; + static auto constexpr rankLogEnd = ": "; + static auto constexpr simpleLogStart = "***** "; + if( ( insertPos = newMsg.find( rankLogStart ) ) != std::string::npos ) + { + insertPos = newMsg.find( rankLogEnd, insertPos + stringutilities::cstrlen( rankLogStart ) ) + + stringutilities::cstrlen( rankLogEnd ); + } + else if( ( insertPos = newMsg.find_last_of( simpleLogStart ) ) != std::string::npos ) + { + insertPos += stringutilities::cstrlen( simpleLogStart ); + } + newMsg.insert( insertPos, msgToInsert ); + return newMsg; +} + +InputError::InputError( std::exception const & subException, std::string const & msgToInsert ): + std::runtime_error( InsertExMsg( subException.what(), msgToInsert ) ) +{} + namespace logger { diff --git a/src/coreComponents/common/Logger.hpp b/src/coreComponents/common/Logger.hpp index d9a28f13ad2..cb69fdfb2df 100644 --- a/src/coreComponents/common/Logger.hpp +++ b/src/coreComponents/common/Logger.hpp @@ -476,6 +476,13 @@ struct InputError : public std::runtime_error InputError( char const * const what ): std::runtime_error( what ) {} + + /** + * @brief Construct an InputError from an underlying exception. + * @param subException An exception to base this new one on. + * @param msgToInsert The error message. It will be inserted into the one inside of subException. + */ + InputError( std::exception const & subException, std::string const & msgToInsert ); }; /** diff --git a/src/coreComponents/dataRepository/Group.cpp b/src/coreComponents/dataRepository/Group.cpp index a49340791a0..0ce9da03dbf 100644 --- a/src/coreComponents/dataRepository/Group.cpp +++ b/src/coreComponents/dataRepository/Group.cpp @@ -127,7 +127,7 @@ string Group::getPath() const { // In the Conduit node heirarchy everything begins with 'Problem', we should change it so that // the ProblemManager actually uses the root Conduit Node but that will require a full rebaseline. - string const noProblem = getConduitNode().path().substr( std::strlen( dataRepository::keys::ProblemManager ) - 1 ); + string const noProblem = getConduitNode().path().substr( stringutilities::cstrlen( dataRepository::keys::ProblemManager ) ); return noProblem.empty() ? "/" : noProblem; } @@ -267,6 +267,32 @@ string Group::dumpInputOptions() const return rval; } +string Group::dumpSubGroupsNames() const +{ + if( numSubGroups() == 0 ) + { + return getName() + " has no children."; + } + else + { + return "The children of " + getName() + " are: " + + "{ " + stringutilities::join( getSubGroupsNames(), ", " ) + " }"; + } +} + +string Group::dumpWrappersNames() const +{ + if( numWrappers() == 0 ) + { + return getName() + " has no wrappers."; + } + else + { + return "The wrappers of " + getName() + " are: " + + "{ " + stringutilities::join( getWrappersNames(), ", " ) + " }"; + } +} + void Group::deregisterGroup( string const & name ) { GEOS_ERROR_IF( !hasGroup( name ), "Group " << name << " doesn't exist." ); @@ -610,8 +636,10 @@ Group const & Group::getBaseGroupByPath( string const & path ) const break; } } - GEOS_ERROR_IF( !foundTarget, - "Could not find the specified path from the starting group." ); + GEOS_THROW_IF( !foundTarget, + "Could not find the specified path start.\n"<< + "Specified path is " << path, + std::domain_error ); } string::size_type currentPosition; @@ -650,5 +678,21 @@ PyTypeObject * Group::getPythonType() const { return geos::python::getPyGroupType(); } #endif +std::vector< string > Group::getSubGroupsNames() const +{ + std::vector< string > childrenNames; + childrenNames.reserve( numSubGroups() ); + forSubGroups( [&]( Group const & subGroup ){ childrenNames.push_back( subGroup.getName() ); } ); + return childrenNames; +} + +std::vector< string > Group::getWrappersNames() const +{ + std::vector< string > wrappersNames; + wrappersNames.reserve( numWrappers() ); + forWrappers( [&]( WrapperBase const & wrapper ){ wrappersNames.push_back( wrapper.getName() ); } ); + return wrappersNames; +} + } /* end namespace dataRepository */ } /* end namespace geos */ diff --git a/src/coreComponents/dataRepository/Group.hpp b/src/coreComponents/dataRepository/Group.hpp index d2dcb489b7a..8c46f4fb2e6 100644 --- a/src/coreComponents/dataRepository/Group.hpp +++ b/src/coreComponents/dataRepository/Group.hpp @@ -165,6 +165,16 @@ class Group */ string dumpInputOptions() const; + /** + * @brief @return a comma separated string containing all sub groups name. + */ + string dumpSubGroupsNames() const; + + /** + * @brief @return a comma separated string containing all wrappers name. + */ + string dumpWrappersNames() const; + ///@} //START_SPHINX_INCLUDE_REGISTER_GROUP @@ -323,7 +333,11 @@ class Group T & getGroup( KEY const & key ) { Group * const child = m_subGroups[ key ]; - GEOS_THROW_IF( child == nullptr, "Group " << getPath() << " doesn't have a child " << key, std::domain_error ); + GEOS_THROW_IF( child == nullptr, + "Group " << getPath() << " has no child named " << key << std::endl + << dumpSubGroupsNames(), + std::domain_error ); + return dynamicCast< T & >( *child ); } @@ -334,7 +348,11 @@ class Group T const & getGroup( KEY const & key ) const { Group const * const child = m_subGroups[ key ]; - GEOS_THROW_IF( child == nullptr, "Group " << getPath() << " doesn't have a child " << key, std::domain_error ); + GEOS_THROW_IF( child == nullptr, + "Group " << getPath() << " has no child named " << key << std::endl + << dumpSubGroupsNames(), + std::domain_error ); + return dynamicCast< T const & >( *child ); } @@ -380,6 +398,11 @@ class Group */ localIndex numSubGroups() const { return m_subGroups.size(); } + /** + * @return An array containing all sub groups keys + */ + std::vector< string > getSubGroupsNames() const; + /** * @brief Check whether a sub-group exists. * @param name the name of sub-group to search for @@ -1047,7 +1070,11 @@ class Group WrapperBase const & getWrapperBase( KEY const & key ) const { WrapperBase const * const wrapper = m_wrappers[ key ]; - GEOS_THROW_IF( wrapper == nullptr, "Group " << getPath() << " doesn't have a child " << key, std::domain_error ); + GEOS_THROW_IF( wrapper == nullptr, + "Group " << getPath() << " has no wrapper named " << key << std::endl + << dumpWrappersNames(), + std::domain_error ); + return *wrapper; } @@ -1058,7 +1085,11 @@ class Group WrapperBase & getWrapperBase( KEY const & key ) { WrapperBase * const wrapper = m_wrappers[ key ]; - GEOS_THROW_IF( wrapper == nullptr, "Group " << getPath() << " doesn't have a child " << key, std::domain_error ); + GEOS_THROW_IF( wrapper == nullptr, + "Group " << getPath() << " has no wrapper named " << key << std::endl + << dumpWrappersNames(), + std::domain_error ); + return *wrapper; } @@ -1090,6 +1121,11 @@ class Group indexType numWrappers() const { return m_wrappers.size(); } + /** + * @return An array containing all wrappers keys + */ + std::vector< string > getWrappersNames() const; + ///@} /** @@ -1244,6 +1280,7 @@ class Group /** * @brief Return the path of this Group in the data repository. + * Starts with '/' followed by the hierarchy of the children of the "Problem" in which the Group is. * @return The path of this group in the data repository. */ string getPath() const; diff --git a/src/coreComponents/fieldSpecification/FieldSpecificationBase.cpp b/src/coreComponents/fieldSpecification/FieldSpecificationBase.cpp index c8eacb7d5bf..521eff2dc5e 100644 --- a/src/coreComponents/fieldSpecification/FieldSpecificationBase.cpp +++ b/src/coreComponents/fieldSpecification/FieldSpecificationBase.cpp @@ -97,7 +97,14 @@ FieldSpecificationBase::getCatalog() void FieldSpecificationBase::setMeshObjectPath( Group const & meshBodies ) { - m_meshObjectPaths = std::make_unique< MeshObjectPath >( m_objectPath, meshBodies ); + try + { + m_meshObjectPaths = std::make_unique< MeshObjectPath >( m_objectPath, meshBodies ); + } + catch( InputError const & e ) + { + throw InputError( e, getName() + " has a wrong objectPath: " + m_objectPath + "\n" ); + } } diff --git a/src/coreComponents/fileIO/timeHistory/HistoryCollectionBase.cpp b/src/coreComponents/fileIO/timeHistory/HistoryCollectionBase.cpp index e5ccb1ef02b..ba32826c9ae 100644 --- a/src/coreComponents/fileIO/timeHistory/HistoryCollectionBase.cpp +++ b/src/coreComponents/fileIO/timeHistory/HistoryCollectionBase.cpp @@ -84,108 +84,128 @@ bool HistoryCollectionBase::execute( real64 const time_n, dataRepository::Group const * HistoryCollectionBase::getTargetObject( DomainPartition const & domain, string const & objectPath ) const { - // absolute objectPaths can be used to target anything in the data repo that is packable - if( objectPath[0] == '/' ) + try { - return &Group::getGroupByPath( objectPath ); - } - else // relative objectPaths use relative lookup identical to fieldSpecification to make xml input spec easier - { - std::vector< string > targetTokens = stringutilities::tokenize( objectPath, "/" ); - localIndex targetTokenLength = LvArray::integerConversion< localIndex >( targetTokens.size() ); - - dataRepository::Group const * targetGroup = nullptr; - int const numMeshBodies = domain.getMeshBodies().numSubGroups(); - - - if( numMeshBodies==1 ) + // absolute objectPaths can be used to target anything in the data repo that is packable + if( objectPath[0] == '/' ) { - string const singleMeshBodyName = domain.getMeshBody( 0 ).getName(); - if( targetTokens[0] != singleMeshBodyName ) - { - ++targetTokenLength; - targetTokens.insert( targetTokens.begin(), singleMeshBodyName ); - } + return &Group::getGroupByPath( objectPath ); } - else + else // relative objectPaths use relative lookup identical to fieldSpecification to make xml input spec easier { - bool bodyFound = false; - domain.forMeshBodies( [&]( MeshBody const & meshBody ) - { - if( meshBody.getName()==targetTokens[0] ) - { - bodyFound=true; - } - } ); - - GEOS_ERROR_IF( !bodyFound, - GEOS_FMT( "MeshBody ({}) is specified, but not found.", - targetTokens[0] ) ); - } + std::vector< string > targetTokens = stringutilities::tokenize( objectPath, "/" ); + localIndex targetTokenLength = LvArray::integerConversion< localIndex >( targetTokens.size() ); + dataRepository::Group const * targetGroup = nullptr; + int const numMeshBodies = domain.getMeshBodies().numSubGroups(); - string const meshBodyName = targetTokens[0]; - MeshBody const & meshBody = domain.getMeshBody( meshBodyName ); - - // set mesh level in path - localIndex const numMeshLevels = meshBody.getMeshLevels().numSubGroups(); - if( numMeshLevels==1 ) - { - string const singleMeshLevelName = meshBody.getMeshLevels().getGroup< MeshLevel >( 0 ).getName(); - if( targetTokens[1] != singleMeshLevelName ) + if( numMeshBodies==1 ) { - ++targetTokenLength; - targetTokens.insert( targetTokens.begin()+1, singleMeshLevelName ); + string const singleMeshBodyName = domain.getMeshBody( 0 ).getName(); + if( targetTokens[0] != singleMeshBodyName ) + { + ++targetTokenLength; + targetTokens.insert( targetTokens.begin(), singleMeshBodyName ); + } } else { - bool levelFound = false; - meshBody.forMeshLevels( [&]( MeshLevel const & meshLevel ) + bool bodyFound = false; + domain.forMeshBodies( [&]( MeshBody const & meshBody ) { - if( meshLevel.getName()==targetTokens[1] ) + if( meshBody.getName()==targetTokens[0] ) { - levelFound=true; + bodyFound=true; } } ); - GEOS_ERROR_IF( !levelFound, - GEOS_FMT( "MeshLevel ({}) is specified, but not found.", - targetTokens[1] ) ); + GEOS_ERROR_IF( !bodyFound, + GEOS_FMT( "MeshBody ({}) is specified, but not found.", + targetTokens[0] ) ); } - } - else if( !meshBody.getMeshLevels().hasGroup< MeshLevel >( targetTokens[1] ) ) - { - //GEOS_LOG_RANK_0( "In TimeHistoryCollection.hpp, Mesh Level Discretization not specified, " - // "using baseDiscretizationString()." ); - string const baseMeshLevelName = MeshBody::groupStructKeys::baseDiscretizationString(); - ++targetTokenLength; - targetTokens.insert( targetTokens.begin()+1, baseMeshLevelName ); - } - string meshLevelName = targetTokens[1]; - MeshLevel const & meshLevel = meshBody.getMeshLevel( meshLevelName ); - targetGroup = &meshLevel; + string const meshBodyName = targetTokens[0]; + MeshBody const & meshBody = domain.getMeshBody( meshBodyName ); - if( targetTokens[2]== MeshLevel::groupStructKeys::elemManagerString() ) - { - ElementRegionManager const & elemRegionManager = meshLevel.getElemManager(); - string const elemRegionName = targetTokens[3]; - ElementRegionBase const & elemRegion = elemRegionManager.getRegion( elemRegionName ); - string const elemSubRegionName = targetTokens[4]; - ElementSubRegionBase const & elemSubRegion = elemRegion.getSubRegion( elemSubRegionName ); - targetGroup = &elemSubRegion; - } - else - { - for( localIndex pathLevel = 2; pathLevel < targetTokenLength; ++pathLevel ) + // set mesh level in path + localIndex const numMeshLevels = meshBody.getMeshLevels().numSubGroups(); + if( numMeshLevels==1 ) { - targetGroup = targetGroup->getGroupPointer( targetTokens[pathLevel] ); + string const singleMeshLevelName = meshBody.getMeshLevels().getGroup< MeshLevel >( 0 ).getName(); + if( targetTokens[1] != singleMeshLevelName ) + { + ++targetTokenLength; + targetTokens.insert( targetTokens.begin()+1, singleMeshLevelName ); + } + else + { + bool levelFound = false; + meshBody.forMeshLevels( [&]( MeshLevel const & meshLevel ) + { + if( meshLevel.getName()==targetTokens[1] ) + { + levelFound=true; + } + } ); + + GEOS_ERROR_IF( !levelFound, + GEOS_FMT( "MeshLevel ({}) is specified, but not found.", + targetTokens[1] ) ); + } + } + else if( !meshBody.getMeshLevels().hasGroup< MeshLevel >( targetTokens[1] ) ) + { + //GEOSX_LOG_RANK_0( "In TimeHistoryCollection.hpp, Mesh Level Discretization not specified, " + // "using baseDiscretizationString()." ); + + string const baseMeshLevelName = MeshBody::groupStructKeys::baseDiscretizationString(); + ++targetTokenLength; + targetTokens.insert( targetTokens.begin()+1, baseMeshLevelName ); + } + + string meshLevelName = targetTokens[1]; + MeshLevel const & meshLevel = meshBody.getMeshLevel( meshLevelName ); + targetGroup = &meshLevel; + + + if( targetTokens[2]== MeshLevel::groupStructKeys::elemManagerString() ) + { + ElementRegionManager const & elemRegionManager = meshLevel.getElemManager(); + string const elemRegionName = targetTokens[3]; + ElementRegionBase const & elemRegion = elemRegionManager.getRegion( elemRegionName ); + string const elemSubRegionName = targetTokens[4]; + ElementSubRegionBase const & elemSubRegion = elemRegion.getSubRegion( elemSubRegionName ); + targetGroup = &elemSubRegion; + } + else + { + for( localIndex pathLevel = 2; pathLevel < targetTokenLength; ++pathLevel ) + { + dataRepository::Group const * const childGroup = targetGroup->getGroupPointer( targetTokens[pathLevel] ); + if( childGroup != nullptr ) + { + targetGroup=childGroup; + } + else + { + string const targetTokensStr = stringutilities::join( targetTokens.begin(), + targetTokens.begin()+pathLevel, + '/' ); + GEOS_THROW( targetTokens[pathLevel] << " not found in path " << + objectPath << std::endl << targetGroup->dumpSubGroupsNames(), + std::domain_error ); + } + } } + return targetGroup; } - return targetGroup; + } + catch( std::domain_error const & e ) + { + throw InputError( e, getName() + " has a wrong objectPath: " + objectPath + "\n" ); } } diff --git a/src/coreComponents/mesh/ElementRegionBase.hpp b/src/coreComponents/mesh/ElementRegionBase.hpp index 673ea6fe33b..825044d7fac 100644 --- a/src/coreComponents/mesh/ElementRegionBase.hpp +++ b/src/coreComponents/mesh/ElementRegionBase.hpp @@ -135,6 +135,7 @@ class ElementRegionBase : public ObjectManagerBase * @tparam KEY_TYPE The type of the key used to lookup the subregion. * @param key The key to the subregion. * @return A reference to the subregion + * @throw std::domain_error if the the requested sub-region doesn't exist. */ template< typename SUBREGIONTYPE=ElementSubRegionBase, typename KEY_TYPE=void > SUBREGIONTYPE const & getSubRegion( KEY_TYPE const & key ) const diff --git a/src/coreComponents/mesh/ElementRegionManager.hpp b/src/coreComponents/mesh/ElementRegionManager.hpp index 4b9c7bd3663..e53e1545bb8 100644 --- a/src/coreComponents/mesh/ElementRegionManager.hpp +++ b/src/coreComponents/mesh/ElementRegionManager.hpp @@ -225,6 +225,7 @@ class ElementRegionManager : public ObjectManagerBase * @brief Get a element region. * @param key The key of element region, either name or number. * @return Reference to const T. + * @throw std::domain_error if the requested region doesn't exist. */ template< typename T=ElementRegionBase, typename KEY_TYPE=void > T const & getRegion( KEY_TYPE const & key ) const @@ -236,6 +237,7 @@ class ElementRegionManager : public ObjectManagerBase * @brief Get a element region. * @param key The key of the element region, either name or number. * @return Reference to T. + * @throw std::domain_error if the requested region doesn't exist. */ template< typename T=ElementRegionBase, typename KEY_TYPE=void > T & getRegion( KEY_TYPE const & key ) diff --git a/src/coreComponents/mesh/MeshObjectPath.cpp b/src/coreComponents/mesh/MeshObjectPath.cpp index 59886ec04ee..6cdaf6bfe98 100644 --- a/src/coreComponents/mesh/MeshObjectPath.cpp +++ b/src/coreComponents/mesh/MeshObjectPath.cpp @@ -80,14 +80,15 @@ MeshObjectPath::fillPathTokens( string const & path, int objectIndex = findObjectIndex(); - GEOS_ERROR_IF( objectIndex==-1, - GEOS_FMT( "Path ({}) does not contain a valid object type. " - "Must contain one of ({},{},{},{})", + GEOS_THROW_IF( objectIndex==-1, + GEOS_FMT( "Path {} does not contain a valid object type. " + "It must contain one of the following: {}, {}, {}, {}", path, MeshLevel::groupStructKeys::nodeManagerString(), MeshLevel::groupStructKeys::edgeManagerString(), MeshLevel::groupStructKeys::faceManagerString(), - MeshLevel::groupStructKeys::elemManagerString() ) ); + MeshLevel::groupStructKeys::elemManagerString() ), + InputError ); // No MeshBody or MeshLevels were specified. add all of them if( objectIndex==0 ) @@ -110,24 +111,36 @@ MeshObjectPath::fillPathTokens( string const & path, { pathTokens.insert( pathTokens.begin(), "{*}" ); - string existingMeshBodyAndLevel; + // searching if the mesh level exists bool levelNameFound = false; meshBodies.forSubGroups< MeshBody >( [&]( MeshBody const & meshBody ) { - existingMeshBodyAndLevel += meshBody.getName() + ": "; meshBody.forMeshLevels( [&]( MeshLevel const & meshLevel ) { - existingMeshBodyAndLevel += meshLevel.getName() + ", "; - levelNameFound = ( unidentifiedName==meshLevel.getName() ) ? true : levelNameFound; + levelNameFound |= ( unidentifiedName==meshLevel.getName() ); } ); - existingMeshBodyAndLevel += "/n"; } ); - GEOS_ERROR_IF( !levelNameFound, - GEOS_FMT( "Path ({}) specifies an invalid MeshBody or MeshLevel. ", - "existing MeshBodies: MeshLevels /n", - path, - existingMeshBodyAndLevel ) ); + if( !levelNameFound ) + { + string existingMeshBodiesAndLevels; + meshBodies.forSubGroups< MeshBody >( [&]( MeshBody const & meshBody ) + { + std::vector< string > meshLevelsNames; + existingMeshBodiesAndLevels += " MeshBody "+meshBody.getName() + ": { "; + meshBody.forMeshLevels( [&]( MeshLevel const & meshLevel ) + { + meshLevelsNames.push_back( meshLevel.getName() ); + } ); + existingMeshBodiesAndLevels += stringutilities::join( meshLevelsNames, ", " ) + " }\n"; + } ); + + GEOS_THROW( GEOS_FMT( "Path {0} specifies an invalid MeshBody or MeshLevel. ", + "existing MeshBodies: \n{1}\n", + path, + existingMeshBodiesAndLevels ), + InputError ); + } pathTokens.insert( pathTokens.begin()+1, unidentifiedName ); } } @@ -136,11 +149,13 @@ MeshObjectPath::fillPathTokens( string const & path, objectIndex = findObjectIndex(); size_t targetTokenLength = pathTokens.size(); - GEOS_ERROR_IF_NE_MSG( objectIndex, 2, - "Filling of MeshBody and/or MeshLevel in path has failed. Object Index should be 2" ); + GEOS_THROW_IF_NE_MSG( objectIndex, 2, + "Filling of MeshBody and/or MeshLevel in path has failed. Object Index should be 2", + InputError ); - GEOS_ERROR_IF( targetTokenLength < 2, - "Filling of MeshBody and/or MeshLevel in path has failed. targetTokenLength should be greater than 2" ); + GEOS_THROW_IF( targetTokenLength < 2, + "Filling of MeshBody and/or MeshLevel in path has failed. targetTokenLength should be greater than 2", + InputError ); // now we need to fill in any missing region/subregion specifications. @@ -189,12 +204,16 @@ void processTokenRecursive( dataRepository::Group const & parentGroup, NODETYPE & node, CALLBACK && cbfunc ) { - array1d< string > namesInRepository; + std::vector< string > namesInRepository; parentGroup.forSubGroups< TYPE >( [&]( TYPE const & group ) { namesInRepository.emplace_back( group.getName() ); } ); + GEOS_THROW_IF( namesInRepository.empty(), + GEOS_FMT( "{0} doesn't have any children.", parentGroup.getName()), + InputError ); + for( string const & inputEntry : stringutilities::tokenize( pathToken, " " ) ) { bool foundMatch = false; @@ -212,12 +231,13 @@ void processTokenRecursive( dataRepository::Group const & parentGroup, } } - GEOS_ERROR_IF( !foundMatch, - GEOS_FMT( "Specified name ({0}) did not find a match with a object in group ({1}). " - "Objects that are present in ({1}) are:\n{2}", - inputEntry, + GEOS_THROW_IF( !foundMatch, + GEOS_FMT( "{0} doesn't have a child named {1}.\n" + "{0} have the following children: {{ {2} }}", parentGroup.getName(), - stringutilities::join( namesInRepository, ", " ) ) ); + inputEntry, + stringutilities::join( namesInRepository, ", " ) ), + InputError ); } } diff --git a/src/coreComponents/mesh/MeshObjectPath.hpp b/src/coreComponents/mesh/MeshObjectPath.hpp index 573bfd915bd..b8124d1c9df 100644 --- a/src/coreComponents/mesh/MeshObjectPath.hpp +++ b/src/coreComponents/mesh/MeshObjectPath.hpp @@ -62,6 +62,7 @@ class MeshObjectPath * * @param path The path string * @param meshBodies The Group that contains all MeshBody objects + * @throw InputError when the input path is wrong. */ MeshObjectPath( string const path, dataRepository::Group const & meshBodies ); diff --git a/src/coreComponents/mesh/unitTests/testMeshObjectPath.cpp b/src/coreComponents/mesh/unitTests/testMeshObjectPath.cpp index d76015a3749..d5021b24c01 100644 --- a/src/coreComponents/mesh/unitTests/testMeshObjectPath.cpp +++ b/src/coreComponents/mesh/unitTests/testMeshObjectPath.cpp @@ -280,7 +280,7 @@ TEST( testMeshObjectPath, invalidMeshBody ) Group const & meshBodies = testMesh.meshBodies(); { string const path = "*/level2/ElementRegions"; - EXPECT_DEATH_IF_SUPPORTED( MeshObjectPath meshObjectPath( path, meshBodies ), ".*" ); + ASSERT_THROW( MeshObjectPath meshObjectPath( path, meshBodies ), InputError ); } } @@ -291,7 +291,7 @@ TEST( testMeshObjectPath, invalidMeshLevel ) Group const & meshBodies = testMesh.meshBodies(); { string const path = "*/*/ElementRegions/{region2}"; - EXPECT_DEATH_IF_SUPPORTED( MeshObjectPath meshObjectPath( path, meshBodies ), ".*" ); + ASSERT_THROW( MeshObjectPath meshObjectPath( path, meshBodies ), InputError ); } } @@ -301,7 +301,7 @@ TEST( testMeshObjectPath, invalidMeshRegion ) Group const & meshBodies = testMesh.meshBodies(); { string const path = "*/*/ElementRegions/*/subreg2"; - EXPECT_DEATH_IF_SUPPORTED( MeshObjectPath meshObjectPath( path, meshBodies ), ".*" ); + ASSERT_THROW( MeshObjectPath meshObjectPath( path, meshBodies ), InputError ); } } diff --git a/src/coreComponents/unitTests/dataRepositoryTests/CMakeLists.txt b/src/coreComponents/unitTests/dataRepositoryTests/CMakeLists.txt index dc372d7d1ef..3d88175d1fb 100644 --- a/src/coreComponents/unitTests/dataRepositoryTests/CMakeLists.txt +++ b/src/coreComponents/unitTests/dataRepositoryTests/CMakeLists.txt @@ -8,7 +8,8 @@ set( dataRepository_tests testRestartExtended.cpp testPacking.cpp testWrapperHelpers.cpp - ) + testGroupPath.cpp + ) set( dependencyList gtest ) diff --git a/src/coreComponents/unitTests/dataRepositoryTests/testGroupPath.cpp b/src/coreComponents/unitTests/dataRepositoryTests/testGroupPath.cpp new file mode 100644 index 00000000000..20761ea22c7 --- /dev/null +++ b/src/coreComponents/unitTests/dataRepositoryTests/testGroupPath.cpp @@ -0,0 +1,129 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +// Source includes +#include "mainInterface/ProblemManager.hpp" +#include "mainInterface/initialization.hpp" +#include "mainInterface/GeosxState.hpp" + +// TPL includes +#include +#include + +// Tests the Group::getGroup() and getPath() methods +TEST( testGroupPath, testGlobalPaths ) +{ + using namespace geos; + using namespace dataRepository; + + char const * xmlInput = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + + std::vector< string > const groupPaths{ + "/Mesh/mesh1", + "/domain/MeshBodies/mesh1/meshLevels/Level0/ElementRegions/elementRegionsGroup/Region2", + "/domain/Constitutive/shale", + "/Events/solverApplications", + "/NumericalMethods/FiniteElements/FE1", + "/Solvers/lagsolve", + }; + + ProblemManager & problem = getGlobalState().getProblemManager(); + problem.parseInputString( xmlInput ); + + for( string const & path : groupPaths ) + { + Group const & group = problem.getGroupByPath( path ); + ASSERT_STREQ( path.c_str(), group.getPath().c_str() ); + } + + // test for a wrong path given to getGroupByPath() + bool trowHappened = false; + try + { + problem.getGroupByPath( "/Mesh/mesh2" ); + } + catch( const std::domain_error & e ) + { + static constexpr auto expectedMsg = "***** Controlling expression (should be false): child == nullptr\n" + "***** Rank 0: Group /Mesh has no child named mesh2\n" + "The children of Mesh are: { mesh1 }"; + // checks if the exception contains the expected message + ASSERT_TRUE( string( e.what() ).find( expectedMsg ) != string::npos ); + trowHappened = true; + } + // checks if the exception has been thrown as expected + ASSERT_TRUE( trowHappened ); +} + +int main( int argc, char * * argv ) +{ + ::testing::InitGoogleTest( &argc, argv ); + + geos::GeosxState state( geos::basicSetup( argc, argv, false ) ); + + int const result = RUN_ALL_TESTS(); + + geos::basicCleanup(); + + return result; +}