opm-common/tests/test_ArrayDimChecker.cpp
2020-09-29 15:37:14 +02:00

494 lines
10 KiB
C++

/*
Copyright 2019 Equinor ASA
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#define BOOST_TEST_MODULE Array_Dimension_Checker
#include <boost/test/unit_test.hpp>
#include <boost/version.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ArrayDimChecker.hpp>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
#include <iostream>
#include <string>
namespace {
Opm::Deck simCaseWellDims()
{
const auto input = std::string{ R"(RUNSPEC
TITLE
'Check Well Dimensions' /
DIMENS
20 20 15 /
OIL
WATER
METRIC
EQLDIMS
-- Defaulted
/
TABDIMS
-- Defaulted
/
WELLDIMS
-- Defaulted
/
-- ====================================================================
GRID
SPECGRID
20 20 15 1 F /
DXV
20*100.0 /
DYV
20*100.0 /
DZV
15*0.1 /
DEPTHZ
441*2000 /
PORO
6000*0.3 /
PERMX
6000*100.0 /
COPY
'PERMX' 'PERMY' /
'PERMX' 'PERMZ' /
/
MULTIPLY
'PERMZ' 0.1 /
/
-- ====================================================================
PROPS
SWOF
0 0 1 0
1 1 0 0 /
PVDO
1 1.0 0.5
800 0.99 0.51 /
PVTW
300 0.99 1.0e-6 0.25 0 /
DENSITY
850.0 1014.0 1.05 /
-- ====================================================================
SOLUTION
EQUIL
2000 300 2010 0.0 2000 10 /
-- ====================================================================
SUMMARY
ALL
-- ====================================================================
SCHEDULE
RPTRST
BASIC=5 FREQ=6 /
GRUPTREE
'G1' 'FIELD' /
'PLAT1' 'G1' /
'PLAT2' 'G1' /
'I-NORTH' 'PLAT1' /
'P-NORTH' 'PLAT1' /
'O-WEST' 'PLAT2' /
'I-SOUTH' 'PLAT2' /
'P-EAST' 'PLAT2' /
'G2' 'FIELD' /
'PLAT3' 'G2' /
'I-2' 'PLAT3' /
/
WELSPECS
'I-N-1' 'I-NORTH' 1 1 2000.15 'WATER' /
'I-N-2' 'I-NORTH' 5 1 2001.05 'WATER' /
'P-N-0' 'P-NORTH' 1 10 2000.15 'OIL' /
'P-N-1' 'P-NORTH' 10 15 2000.15 'OIL' /
'P-N-2' 'P-NORTH' 1 20 2000.15 'OIL' /
'P-N-3' 'P-NORTH' 19 20 2000.15 'OIL' /
'P-N-4' 'P-NORTH' 15 10 2000.15 'OIL' /
'P-N-5' 'P-NORTH' 10 10 2000.15 'OIL' /
'P-N-6' 'P-NORTH' 10 20 2000.15 'OIL' /
'P-N-7' 'P-NORTH' 7 15 2000.15 'OIL' /
'P-N-8' 'P-NORTH' 2 20 2000.15 'OIL' /
'P-N-9' 'P-NORTH' 20 1 2000.05 'OIL' /
/
COMPDAT
'I-N-1' 0 0 2 10 'OPEN' 1* 1* 1.0 /
'I-N-2' 0 0 10 15 'OPEN' 1* 1* 1.0 /
'P-N-0' 0 0 2 3 'OPEN' 1* 1* 1.0 /
'P-N-1' 0 0 2 4 'OPEN' 1* 1* 1.0 /
'P-N-2' 0 0 2 5 'OPEN' 1* 1* 1.0 /
'P-N-3' 0 0 2 6 'OPEN' 1* 1* 1.0 /
'P-N-4' 0 0 2 7 'OPEN' 1* 1* 1.0 /
'P-N-5' 0 0 2 8 'OPEN' 1* 1* 1.0 /
'P-N-6' 0 0 2 9 'OPEN' 1* 1* 1.0 /
'P-N-7' 0 0 2 10 'OPEN' 1* 1* 1.0 /
'P-N-8' 0 0 2 5 'OPEN' 1* 1* 1.0 /
'P-N-9' 0 0 1 15 'OPEN' 1* 1* 1.0 /
/
WCONPROD
-- Well O/S Mode ORAT WRAT GRAT LRAT RESV BHP
'P-N-*' 'OPEN' 'LRAT' 1* 1* 1* 5E3 1* 100 /
/
WCONINJE
-- Well Type O/S Mode RATE RESV BHP
'I-N-*' 'WATER' 'OPEN' 'RATE' 25E3 1* 500 /
/
TSTEP
100*30 /
END
)" };
return Opm::Parser{}.parseString(input);
}
Opm::Deck simCaseNodeGroupSizeFailure()
{
const auto input = std::string{ R"(RUNSPEC
TITLE
'Check Well Dimensions' /
DIMENS
20 20 15 /
OIL
WATER
METRIC
EQLDIMS
-- Defaulted
/
TABDIMS
-- Defaulted
/
WELLDIMS
-- NWMAX NCMAX NGMAX NWGMAX (too small)
2 20 16 4
/
-- ====================================================================
GRID
SPECGRID
20 20 15 1 F /
DXV
20*100.0 /
DYV
20*100.0 /
DZV
15*0.1 /
DEPTHZ
441*2000 /
PORO
6000*0.3 /
PERMX
6000*100.0 /
COPY
'PERMX' 'PERMY' /
'PERMX' 'PERMZ' /
/
MULTIPLY
'PERMZ' 0.1 /
/
-- ====================================================================
PROPS
SWOF
0 0 1 0
1 1 0 0 /
PVDO
1 1.0 0.5
800 0.99 0.51 /
PVTW
300 0.99 1.0e-6 0.25 0 /
DENSITY
850.0 1014.0 1.05 /
-- ====================================================================
SOLUTION
EQUIL
2000 300 2010 0.0 2000 10 /
-- ====================================================================
SUMMARY
ALL
-- ====================================================================
SCHEDULE
RPTRST
BASIC=5 FREQ=6 /
GRUPTREE
'G1' 'FIELD' /
'PLAT1' 'G1' /
'PLAT2' 'G1' /
'I-NORTH' 'PLAT1' /
'P-NORTH' 'PLAT1' /
'O-WEST' 'PLAT2' /
'I-SOUTH' 'PLAT2' /
'P-EAST' 'PLAT2' /
'G2' 'FIELD' /
'PLAT3' 'G2' /
'I-2' 'PLAT3' /
'I-21' 'PLAT3' /
'I-22' 'PLAT3' /
'I-23' 'PLAT3' /
'I-24' 'PLAT3' /
'I-25' 'PLAT3' /
/
WELSPECS
'I-N-1' 'I-NORTH' 1 1 2000.15 'WATER' /
'P-N-0' 'P-NORTH' 1 10 2000.15 'OIL' /
/
COMPDAT
'I-N-1' 0 0 2 10 'OPEN' 1* 1* 1.0 /
'P-N-0' 0 0 2 3 'OPEN' 1* 1* 1.0 /
/
WCONPROD
-- Well O/S Mode ORAT WRAT GRAT LRAT RESV BHP
'P-N-*' 'OPEN' 'LRAT' 1* 1* 1* 5E3 1* 100 /
/
WCONINJE
-- Well Type O/S Mode RATE RESV BHP
'I-N-*' 'WATER' 'OPEN' 'RATE' 25E3 1* 500 /
/
TSTEP
100*30 /
END
)" };
return Opm::Parser{}.parseString(input);
}
}
struct CaseObjects
{
explicit CaseObjects(const Opm::Deck& deck, const Opm::ParseContext& ctxt);
~CaseObjects();
CaseObjects(const CaseObjects& rhs) = default;
CaseObjects(CaseObjects&& rhs) = default;
CaseObjects& operator=(const CaseObjects& rhs) = delete;
CaseObjects& operator=(CaseObjects&& rhs) = delete;
Opm::ErrorGuard guard;
Opm::EclipseState es;
std::shared_ptr<Opm::Python> python;
Opm::Schedule sched;
};
CaseObjects::CaseObjects(const Opm::Deck& deck, const Opm::ParseContext& ctxt)
: guard{}
, es (deck)
, python( std::make_shared<Opm::Python>())
, sched(deck, es, ctxt, guard, python)
{
}
CaseObjects::~CaseObjects()
{
this->guard.clear();
}
class RedirectCERR
{
public:
explicit RedirectCERR(std::streambuf* buf);
~RedirectCERR();
private:
std::streambuf* orig_;
};
RedirectCERR::RedirectCERR(std::streambuf* buf)
: orig_{ std::cerr.rdbuf(buf) }
{
}
RedirectCERR::~RedirectCERR()
{
std::cerr.rdbuf(this->orig_);
}
// ====================================================================
BOOST_AUTO_TEST_SUITE(WellDimensions)
namespace {
void setWellDimsContext(const Opm::InputError::Action action,
Opm::ParseContext& ctxt)
{
ctxt.update(Opm::ParseContext::RUNSPEC_NUMWELLS_TOO_LARGE, action);
ctxt.update(Opm::ParseContext::RUNSPEC_CONNS_PER_WELL_TOO_LARGE, action);
ctxt.update(Opm::ParseContext::RUNSPEC_NUMGROUPS_TOO_LARGE, action);
ctxt.update(Opm::ParseContext::RUNSPEC_GROUPSIZE_TOO_LARGE, action);
}
}
BOOST_AUTO_TEST_CASE(MaxGroupSize)
{
Opm::ParseContext parseContext;
auto cse = CaseObjects{ simCaseWellDims(), parseContext };
// Verify at most ten wells in a single group.
BOOST_CHECK_EQUAL(Opm::maxGroupSize(cse.sched, 1), 10);
}
BOOST_AUTO_TEST_CASE(ManyChildGroups)
{
auto cse = CaseObjects {
simCaseNodeGroupSizeFailure(),
Opm::ParseContext{}
};
// 6 Child groups of 'PLAT3'
BOOST_CHECK_EQUAL(Opm::maxGroupSize(cse.sched, 1), 6);
}
BOOST_AUTO_TEST_CASE(WellDims)
{
Opm::ParseContext parseContext;
setWellDimsContext(Opm::InputError::THROW_EXCEPTION, parseContext);
auto cse = CaseObjects{ simCaseWellDims(), parseContext};
// There should be no failures in basic input layer
BOOST_CHECK(!cse.guard);
BOOST_CHECK_THROW( Opm::checkConsistentArrayDimensions(cse.es , cse.sched,
parseContext, cse.guard),
Opm::OpmInputError);
setWellDimsContext(Opm::InputError::DELAYED_EXIT1, parseContext);
Opm::checkConsistentArrayDimensions(cse.es , cse.sched,
parseContext, cse.guard);
// There *should* be errors from dimension checking
BOOST_CHECK(cse.guard);
{
std::stringstream estream;
RedirectCERR stream(estream.rdbuf());
cse.guard.dump();
const auto error_msg = estream.str();
for (const auto& s : {"RUNSPEC_NUMWELLS_TOO_LARGE", "item 1",
"RUNSPEC_CONNS_PER_WELL_TOO_LARGE", "item 2",
"RUNSPEC_NUMGROUPS_TOO_LARGE", "item 3",
"RUNSPEC_GROUPSIZE_TOO_LARGE", "item 4"})
BOOST_CHECK( error_msg.find(s) != std::string::npos );
}
}
BOOST_AUTO_TEST_CASE(WellDims_ManyChildGroups)
{
Opm::ParseContext parseContext;
setWellDimsContext(Opm::InputError::THROW_EXCEPTION, parseContext);
auto cse = CaseObjects{ simCaseNodeGroupSizeFailure(), parseContext};
// There should be no failures in basic input layer
BOOST_CHECK(!cse.guard);
// There *should* be errors from dimension checking
BOOST_CHECK_THROW( Opm::checkConsistentArrayDimensions(cse.es , cse.sched,
parseContext, cse.guard),
Opm::OpmInputError);
setWellDimsContext(Opm::InputError::DELAYED_EXIT1, parseContext);
Opm::checkConsistentArrayDimensions(cse.es , cse.sched,
parseContext, cse.guard);
// There *should* be errors from dimension checking
BOOST_CHECK(cse.guard);
// Verify that we get expected output from ErrorGuard::dump()
{
std::stringstream estream;
RedirectCERR stream(estream.rdbuf());
cse.guard.dump();
const auto error_msg = estream.str();
for (const auto& s : {"RUNSPEC_GROUPSIZE_TOO_LARGE", "item 4"})
BOOST_CHECK( error_msg.find(s) != std::string::npos );
}
}
BOOST_AUTO_TEST_SUITE_END()