Merge pull request #1989 from wito/tablemanager-exceptions

Implements OpmInputError in TableManager
This commit is contained in:
Joakim Hove 2020-10-05 17:02:42 +02:00 committed by GitHub
commit be898b5534
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 251 additions and 103 deletions

View File

@ -19,9 +19,9 @@
#ifndef OPM_ERROR_HPP
#define OPM_ERROR_HPP
#include <optional>
#include <stdexcept>
#include <string>
#include <vector>
#include <opm/common/OpmLog/KeywordLocation.hpp>
@ -66,10 +66,15 @@ public:
OpmInputError("Error at line {line} in file {file} - keyword: {keyword} has invalid argument {}", invalid_argument);
*/
OpmInputError(const std::string& msg_fmt, const KeywordLocation& loc) :
m_what { OpmInputError::format(msg_fmt, loc) },
location { loc }
{}
template<typename ... Args>
OpmInputError(const std::string& reason, const KeywordLocation& location, const Args& ...furtherLocations)
: locations { location, furtherLocations... }
, m_what {
locations.size() == 1
? formatSingle(reason, locations[0])
: formatMultiple(reason, locations)
}
{ }
/*
Allows for the initialisation of an OpmInputError from another exception.
@ -83,17 +88,13 @@ public:
} catch (const Opm::OpmInputError&) {
throw;
} catch (const std::exception& e) {
std::throw_with_nested(Opm::OpmInputError(location, e));
std::throw_with_nested(Opm::OpmInputError(e, location));
}
*/
OpmInputError(const KeywordLocation& loc, const std::exception& e) :
m_what { OpmInputError::formatException(loc, e) },
location { loc }
{}
OpmInputError(const std::string& msg) :
m_what(msg)
{}
OpmInputError(const std::exception& error, const KeywordLocation& location)
: locations { location }
, m_what { formatException(error, locations[0]) }
{ }
const char * what() const throw()
{
@ -102,15 +103,18 @@ public:
static std::string format(const std::string& msg_format, const KeywordLocation& loc);
static std::string formatException(const KeywordLocation& loc, const std::exception& e);
private:
std::string m_what;
// The location member is here for debugging; depending on the msg_fmt
// passed in the constructor we might not have captured all the information
// in the location argument passed to the constructor.
std::optional<KeywordLocation> location;
std::vector<KeywordLocation> locations;
std::string m_what;
static std::string formatException(const std::exception& e, const KeywordLocation& loc);
static std::string formatSingle(const std::string& reason, const KeywordLocation&);
static std::string formatMultiple(const std::string& reason, const std::vector<KeywordLocation>&);
};
}

View File

@ -16,6 +16,9 @@
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <numeric>
#include <utility>
#include <fmt/format.h>
@ -23,12 +26,26 @@
namespace Opm {
std::string OpmInputError::formatException(const KeywordLocation& loc, const std::exception& e) {
const std::string defaultMessage { R"(Problem parsing keyword {{keyword}}
In {{file}} line {{line}}.
namespace {
template<typename ... Args>
std::string formatImpl(const std::string& msg_format, const KeywordLocation& loc, const Args& ...arguments) {
return fmt::format(msg_format,
arguments...,
fmt::arg("keyword", loc.keyword),
fmt::arg("file", loc.filename),
fmt::arg("line", loc.lineno)
);
}
}
std::string OpmInputError::formatException(const std::exception& e, const KeywordLocation& loc) {
const std::string defaultMessage { R"(Problem with keyword {keyword}
In {file} line {line}.
Internal error: {})" } ;
return format(fmt::format(defaultMessage, e.what()), loc);
return formatImpl(defaultMessage, loc, e.what());
}
/*
@ -39,11 +56,32 @@ Internal error: {})" } ;
the fmtlib dependendcy will be imposed on downstream modules.
*/
std::string OpmInputError::format(const std::string& msg_format, const KeywordLocation& loc) {
return fmt::format(msg_format,
fmt::arg("keyword", loc.keyword),
fmt::arg("file", loc.filename),
fmt::arg("line", loc.lineno)
);
return formatImpl(msg_format, loc);
}
std::string OpmInputError::formatSingle(const std::string& reason, const KeywordLocation& location) {
const std::string defaultMessage { R"(Problem with keyword {keyword}
In {file} line {line}
{})" } ;
return formatImpl(defaultMessage, location, reason);
}
namespace {
std::string locationStringLine(const KeywordLocation& loc) {
return OpmInputError::format("\n {keyword} in {file}, line {line}", loc);
}
}
std::string OpmInputError::formatMultiple(const std::string& reason, const std::vector<KeywordLocation>& locations) {
std::vector<std::string> locationStrings;
std::transform(locations.begin(), locations.end(), std::back_inserter(locationStrings), &locationStringLine);
const std::string messages { std::accumulate(locationStrings.begin(), locationStrings.end(), std::string {}) } ;
return fmt::format(R"(Problem with keywords {}
{})", messages, reason);
}
}

View File

@ -19,6 +19,8 @@
#include <algorithm>
#include <unordered_map>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/A.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/B.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/C.hpp>
@ -859,7 +861,7 @@ void FieldProps::handle_operation(const DeckKeyword& keyword, Box box) {
continue;
}
//throw std::out_of_range("The keyword: " + target_kw + " is not supported");
throw OpmInputError("Operation keyword " + keyword.name() + " does not support the keyword " + target_kw, keyword.location());
}
}

View File

@ -1767,9 +1767,11 @@ namespace {
} catch (const OpmInputError&) {
throw;
} catch (const std::exception& e) {
OpmLog::error(OpmInputError::formatException(handlerContext.keyword.location(), e));
const OpmInputError opm_error { e, handlerContext.keyword.location() } ;
throw;
OpmLog::error(opm_error.what());
std::throw_with_nested(opm_error);
}
return true;

View File

@ -31,12 +31,15 @@
#include <opm/common/OpmLog/LogUtil.hpp>
#include <opm/common/OpmLog/StreamLog.hpp>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/A.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/E.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/M.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/O.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/P.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/R.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/S.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/T.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/V.hpp>
@ -569,12 +572,17 @@ namespace Opm {
// the TEMPVD (E300) and RTEMPVD (E300 + E100) keywords are
// synonymous, but we want to provide only a single cannonical
// API here, so we jump through some small hoops...
if (deck.hasKeyword("TEMPVD") && deck.hasKeyword("RTEMPVD"))
throw std::invalid_argument("The TEMPVD and RTEMPVD tables are mutually exclusive!");
else if (deck.hasKeyword("TEMPVD"))
initSimpleTableContainer<RtempvdTable>(deck, "TEMPVD", "RTEMPVD", m_eqldims.getNumEquilRegions());
else if (deck.hasKeyword("RTEMPVD"))
initSimpleTableContainer<RtempvdTable>(deck, "RTEMPVD", "RTEMPVD" , m_eqldims.getNumEquilRegions());
const bool
hasTEMPVD { deck.hasKeyword<ParserKeywords::TEMPVD>() } ,
hasRTEMPVD { deck.hasKeyword<ParserKeywords::RTEMPVD>() } ;
if (hasTEMPVD && hasRTEMPVD) {
throw OpmInputError("The TEMPVD and RTEMPVD tables are mutually exclusive.", deck.getKeyword<ParserKeywords::TEMPVD>().location(), deck.getKeyword<ParserKeywords::RTEMPVD>().location());
} else if (hasTEMPVD) {
initSimpleTableContainer<RtempvdTable>(deck, "TEMPVD", "RTEMPVD", m_eqldims.getNumEquilRegions());
} else if (hasRTEMPVD) {
initSimpleTableContainer<RtempvdTable>(deck, "RTEMPVD", "RTEMPVD", m_eqldims.getNumEquilRegions());
}
}
@ -621,8 +629,11 @@ namespace Opm {
const auto& tableKeyword = deck.getKeyword(keywordName);
if (tableKeyword.size() > 2) {
std::string msg = "The Parser does currently NOT support the alternating record schema used in PLYSHLOG";
throw std::invalid_argument( msg );
const std::string reason {
"The Parser does currently NOT support the alternating record schema used in PLYSHLOG"
} ;
throw OpmInputError(reason, tableKeyword.location());
}
for (size_t tableIdx = 0; tableIdx < tableKeyword.size(); tableIdx += 2) {
@ -655,9 +666,13 @@ namespace Opm {
if (m_plymwinjTables.find(table_number) == m_plymwinjTables.end()) {
m_plymwinjTables.insert(std::make_pair(table_number, std::move(table)));
} else {
throw std::invalid_argument("Duplicated table number "
+ std::to_string(table_number)
+ " for keyword PLYMWINJ found");
const std::string reason {
"Duplicated table number " +
std::to_string(table_number) +
" for keyword PLYMWINJ found"
} ;
throw OpmInputError(reason, keyword.location());
}
}
}
@ -681,9 +696,13 @@ namespace Opm {
if (m_skprwatTables.find(table_number) == m_skprwatTables.end()) {
m_skprwatTables.insert(std::make_pair(table_number, std::move(table)));
} else {
throw std::invalid_argument("Duplicated table number "
+ std::to_string(table_number)
+ " for keyword SKPRWAT found");
const std::string reason {
"Duplicated table number " +
std::to_string(table_number) +
" for keyword SKPRWAT found"
} ;
throw OpmInputError(reason, keyword.location());
}
}
}
@ -707,9 +726,13 @@ namespace Opm {
if (m_skprpolyTables.find(table_number) == m_skprpolyTables.end()) {
m_skprpolyTables.insert(std::make_pair(table_number, std::move(table)));
} else {
throw std::invalid_argument("Duplicated table number "
+ std::to_string(table_number)
+ " for keyword SKPRPOLY found");
const std::string reason {
"Duplicated table number " +
std::to_string(table_number) +
" for keyword SKPRPOLY found"
} ;
throw OpmInputError(reason, keyword.location());
}
}
}
@ -775,9 +798,13 @@ namespace Opm {
bool isDirectional = deck.hasKeyword<ParserKeywords::RKTRMDIR>();
if (isDirectional) {
const std::string msg = "RKTRMDIR is in the deck. Flow does not support directional rock compaction mulipliers. \n"
"Make sure that your ROCKTAB table only has 3 columns";
throw std::invalid_argument(msg);
const auto& keyword = deck.getKeyword<ParserKeywords::RKTRMDIR>();
const std::string reason {
"RKTRMDIR is in the deck. Flow does not support directional rock compaction mulipliers.\n"
"Make sure that your ROCKTAB table only has 3 columns)"
} ;
throw OpmInputError(reason, keyword.location());
}
bool useStressOption = false;
@ -786,10 +813,12 @@ namespace Opm {
const auto& rockoptsRecord = rockoptsKeyword.getRecord(0);
const auto& item = rockoptsRecord.getItem<ParserKeywords::ROCKOPTS::METHOD>();
useStressOption = (item.getTrimmedString(0) == "STRESS");
}
if (useStressOption) {
const std::string msg = "STRESS option is set in ROCKOPTS. Flow does not support stress option in rock compaction mulipliers";
throw std::invalid_argument(msg);
if (useStressOption) {
const std::string reason { "STRESS option is set in ROCKOPTS. Flow does not support stress option in rock compaction mulipliers" } ;
throw OpmInputError(reason, rockoptsKeyword.location());
}
}
for (size_t tableIdx = 0; tableIdx < rocktabKeyword.size(); ++tableIdx) {

View File

@ -162,10 +162,8 @@ namespace Opm {
// make sure the error object does not terminate the application
// when it goes out of scope.
errors.clear();
if (location)
throw OpmInputError(msg_fmt, *location);
else
throw OpmInputError(msg_fmt, {});
throw OpmInputError(msg_fmt, location.value_or(KeywordLocation{}));
}
if (action == InputError::EXIT1) {

View File

@ -934,10 +934,11 @@ bool parseState( ParserState& parserState, const Parser& parser ) {
same exception without updating the what() message of the
exception.
*/
const OpmInputError opm_error { e, rawKeyword->location() } ;
OpmLog::error(OpmInputError::formatException(rawKeyword->location(), e));
OpmLog::error(opm_error.what());
throw;
std::throw_with_nested(opm_error);
}
} else {
const std::string msg = "The keyword " + rawKeyword->getKeywordName() + " is not recognized - ignored";

View File

@ -31,6 +31,7 @@
#include <boost/filesystem.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
@ -2219,3 +2220,76 @@ ADDREG
BOOST_CHECK_THROW( FieldPropsManager(deck, Phases{true, true, true}, grid, TableManager()), std::logic_error );
}
}
BOOST_AUTO_TEST_CASE(OPERATION_VALID_KEYWORD) {
std::string deck_string1 = R"(
GRID
PORO
200*0.15 /
REGIONS
FIPNUM
200*1 /
MAXVALUE
POROX 0.25 /
/
)";
std::string deck_string2 = R"(
GRID
PORO
200*0.15 /
REGIONS
FIPNUM
200*1 /
MAXVALUE
TRANX 0.25 /
TRANY 0.25 /
TRANZ 0.25 /
/
)";
std::string deck_string3 = R"(
GRID
PORO
200*0.15 /
REGIONS
FIPXYZ
200*1 /
MAXVALUE
FIPXYZ 5 /
/
)";
EclipseGrid grid(10,10, 2);
Deck deck1 = Parser{}.parseString(deck_string1);
Deck deck2 = Parser{}.parseString(deck_string2);
Deck deck3 = Parser{}.parseString(deck_string3);
// This case should throw because we refer to unsupported keyword POROX
BOOST_CHECK_THROW( FieldPropsManager(deck1, Phases{true, true, true}, grid, TableManager()), OpmInputError );
// This is legitimate and should not throw; however the keywords TRANX,
// TRANY and TRANZ are not supported in the normal sense and some special
// casing will be required.
BOOST_CHECK_NO_THROW( FieldPropsManager(deck2, Phases{true, true, true}, grid, TableManager()));
// This is legitimate and should not throw; however the FIPXYZ is an
// autogenerated keyword and (maybe ??) not supported in the normal sense
// and some special casing might be required.
BOOST_CHECK_NO_THROW( FieldPropsManager(deck3, Phases{true, true, true}, grid, TableManager()));
}

View File

@ -305,7 +305,7 @@ BOOST_AUTO_TEST_CASE(TESTGuideRateLINCOM) {
/* The 'COMB' target mode is not supported */
BOOST_CHECK_THROW(create_schedule(input), std::logic_error);
BOOST_CHECK_THROW(create_schedule(input), std::exception);
}
BOOST_AUTO_TEST_CASE(TESTGuideRate) {

View File

@ -94,7 +94,7 @@ NODEPROP
/
)";
BOOST_CHECK_THROW( make_schedule(deck_string), std::invalid_argument);
BOOST_CHECK_THROW( make_schedule(deck_string), std::exception);
}
@ -128,7 +128,7 @@ NODEPROP
/
)";
BOOST_CHECK_THROW( make_schedule(deck_string), std::invalid_argument);
BOOST_CHECK_THROW( make_schedule(deck_string), std::exception);
}
BOOST_AUTO_TEST_CASE(INVALID_VFP_NODE) {
@ -161,7 +161,7 @@ NODEPROP
/
)";
BOOST_CHECK_THROW( make_schedule(deck_string), std::invalid_argument);
BOOST_CHECK_THROW( make_schedule(deck_string), std::exception);
}
BOOST_AUTO_TEST_CASE(OK) {

View File

@ -848,9 +848,6 @@ BOOST_AUTO_TEST_CASE(OPM_ERROR) {
OpmInputError error1("Error", location);
OpmInputError error4("{keyword}:{line}:{keyword}", location);
BOOST_CHECK_EQUAL(error1.what(), "Error");
BOOST_CHECK_EQUAL(error4.what(), "kw:100:kw");
/*
This test is meant to emulate the typical parsing process, the blocks here
in the main test function represent main() in the simulator and the main

View File

@ -2125,7 +2125,7 @@ FIELD
section in order to provoke the exception; if at some stage the opm parser
treats section stricter this test might fail due to that reason instead.
*/
BOOST_CHECK_THROW( Parser{}.parseString(deck_string), std::invalid_argument);
BOOST_CHECK_THROW( Parser{}.parseString(deck_string), OpmInputError);
}
BOOST_AUTO_TEST_CASE(ParseRSConstT) {

View File

@ -2169,7 +2169,7 @@ BOOST_AUTO_TEST_CASE( complump_less_than_1 ) {
TableManager table ( deck );
FieldPropsManager fp( deck, Phases{true, true, true}, grid, table);
Runspec runspec (deck);
BOOST_CHECK_THROW( Schedule( deck , grid, fp, runspec, python), std::invalid_argument );
BOOST_CHECK_THROW( Schedule( deck , grid, fp, runspec, python), std::exception );
}
BOOST_AUTO_TEST_CASE( complump ) {

View File

@ -506,7 +506,7 @@ UDQ
/
)";
BOOST_CHECK_THROW( make_schedule(input), std::invalid_argument);
BOOST_CHECK_THROW( make_schedule(input), std::exception);
}

View File

@ -205,11 +205,11 @@ BOOST_AUTO_TEST_CASE(WlistInvalid) {
"10 AUG 2007 /\n"
"/\n";
BOOST_CHECK_THROW( createSchedule(wlist_invalid_well), std::invalid_argument);
BOOST_CHECK_THROW( createSchedule(wlist_invalid_well), std::invalid_argument);
BOOST_CHECK_THROW( createSchedule(wlist_invalid_action), std::invalid_argument);
BOOST_CHECK_THROW( createSchedule(wlist_invalid_list1), std::invalid_argument);
BOOST_CHECK_THROW( createSchedule(wlist_invalid_list2), std::invalid_argument);
BOOST_CHECK_THROW( createSchedule(wlist_invalid_well), std::exception);
BOOST_CHECK_THROW( createSchedule(wlist_invalid_well), std::exception);
BOOST_CHECK_THROW( createSchedule(wlist_invalid_action), std::exception);
BOOST_CHECK_THROW( createSchedule(wlist_invalid_list1), std::exception);
BOOST_CHECK_THROW( createSchedule(wlist_invalid_list2), std::exception);
}
BOOST_AUTO_TEST_CASE(Wlist) {

View File

@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE(TestOilInjector) {
TableManager table ( deck );
FieldPropsManager fp(deck, Phases{true, true, true}, grid, table);
Runspec runspec(deck);
BOOST_CHECK_THROW (Schedule(deck , grid , fp, runspec, python), std::invalid_argument);
BOOST_CHECK_THROW (Schedule(deck , grid , fp, runspec, python), std::exception);
}
BOOST_AUTO_TEST_CASE(TestWaterInjector) {
@ -233,5 +233,5 @@ BOOST_AUTO_TEST_CASE(TestWaterInjector) {
TableManager table ( deck );
FieldPropsManager fp(deck, Phases{true, true, true}, grid, table);
Runspec runspec(deck);
BOOST_CHECK_THROW (Schedule(deck, grid , fp, runspec, python), std::invalid_argument);
BOOST_CHECK_THROW (Schedule(deck, grid , fp, runspec, python), std::exception);
}

View File

@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE(WCONPROD_Missing_DATA) {
FieldPropsManager fp(deck, Phases{true, true, true}, grid, table);
Runspec runspec (deck);
auto python = std::make_shared<Python>();
BOOST_CHECK_THROW( Schedule(deck, grid , fp, runspec, python) , std::invalid_argument );
BOOST_CHECK_THROW( Schedule(deck, grid , fp, runspec, python) , std::exception );
}
@ -125,7 +125,7 @@ BOOST_AUTO_TEST_CASE(WellTestRefDepth) {
const auto& well4 = sched.getWellatEnd("W_4");
BOOST_CHECK_EQUAL( well1.getRefDepth() , grid.getCellDepth( 29 , 36 , 0 ));
BOOST_CHECK_EQUAL( well2.getRefDepth() , 100 );
BOOST_CHECK_THROW( well4.getRefDepth() , std::invalid_argument );
BOOST_CHECK_THROW( well4.getRefDepth() , std::exception );
}

View File

@ -49,38 +49,18 @@ BOOST_AUTO_TEST_CASE(positional) {
BOOST_CHECK_EQUAL(formatted, expected);
}
BOOST_AUTO_TEST_CASE(exception) {
const std::string expected { R"(Problem parsing keyword MXUNSUPP
In FILENAME.DAT line 42.
Internal error: Runtime Error)" };
const std::string formatted { Opm::OpmInputError::formatException(location, std::runtime_error("Runtime Error")) } ;
BOOST_CHECK_EQUAL(formatted, expected);
}
BOOST_AUTO_TEST_CASE(exception_reason) {
const std::string expected { R"(Problem parsing keyword MXUNSUPP
In FILENAME.DAT line 42.
Internal error: Runtime Error)" };
const std::string formatted { Opm::OpmInputError::formatException(location, std::runtime_error("Runtime Error")) } ;
BOOST_CHECK_EQUAL(formatted, expected);
}
BOOST_AUTO_TEST_CASE(exception_init) {
const std::string expected { R"(Problem parsing keyword MXUNSUPP
const std::string expected { R"(Problem with keyword MXUNSUPP
In FILENAME.DAT line 42.
Internal error: Runtime Error)" };
const std::string formatted { Opm::OpmInputError(location, std::runtime_error("Runtime Error")).what() } ;
const std::string formatted { Opm::OpmInputError(std::runtime_error("Runtime Error"), location).what() } ;
BOOST_CHECK_EQUAL(formatted, expected);
}
BOOST_AUTO_TEST_CASE(exception_nest) {
const std::string expected { R"(Problem parsing keyword MXUNSUPP
const std::string expected { R"(Problem with keyword MXUNSUPP
In FILENAME.DAT line 42.
Internal error: Runtime Error)" };
@ -88,9 +68,32 @@ Internal error: Runtime Error)" };
try {
throw std::runtime_error("Runtime Error");
} catch (const std::exception& e) {
std::throw_with_nested(Opm::OpmInputError(location, e));
std::throw_with_nested(Opm::OpmInputError(e, location));
}
} catch (const Opm::OpmInputError& opm_error) {
BOOST_CHECK_EQUAL(opm_error.what(), expected);
}
}
const Opm::KeywordLocation location2 { "MZUNSUPP", "FILENAME.DAT", 45 } ;
BOOST_AUTO_TEST_CASE(exception_multi_1) {
const std::string expected { R"(Problem with keyword MXUNSUPP
In FILENAME.DAT line 42
Runtime Error)" } ;
const std::string formatted { Opm::OpmInputError("Runtime Error", location).what() } ;
BOOST_CHECK_EQUAL(formatted, expected);
}
BOOST_AUTO_TEST_CASE(exception_multi_2) {
const std::string expected { R"(Problem with keywords
MXUNSUPP in FILENAME.DAT, line 42
MZUNSUPP in FILENAME.DAT, line 45
Runtime Error)" } ;
const std::string formatted { Opm::OpmInputError("Runtime Error", location, location2).what() } ;
BOOST_CHECK_EQUAL(formatted, expected);
}