This commit renames various data members of the *_data structures to aid the reader who may not be intimately familiar with the details of the analytic models. We switch the 'initial_pressure' into an optional<double> instead of a pair<bool,double> with 'std::nullopt' representing defaulted initial aquifer pressure values in the input file. We also add logic and private data members to compute/store aquifer time constants, mass density of water, water viscosity and, in the case of Carter-Tracy aquifers, also the influx constant 'beta'. This centralises the requisite logic which reduces the risk of subtle errors. In the case of defaulted initial pressures, we support deferred calculation of aquifer properties until the equilibrium pressure is known. Users are then expected to assign a value to the initial pressure members and call 'finishInitialisation()' to derive those properties. While here, also switch to using raw string literals instead of concatenated and escaped literals for the embedded deck strings in the ParserTests.
949 lines
22 KiB
C++
949 lines
22 KiB
C++
/*
|
|
Copyright 2017 TNO.
|
|
Copyright 2020 Equinor.
|
|
|
|
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 AquiferCTTest
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
#include <opm/parser/eclipse/EclipseState/Aquifer/Aquancon.hpp>
|
|
#include <opm/parser/eclipse/EclipseState/Aquifer/AquiferCT.hpp>
|
|
#include <opm/parser/eclipse/EclipseState/Aquifer/Aquifetp.hpp>
|
|
#include <opm/parser/eclipse/EclipseState/Aquifer/AquiferConfig.hpp>
|
|
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
|
#include <opm/parser/eclipse/EclipseState/Tables/TableManager.hpp>
|
|
|
|
#include <opm/parser/eclipse/Units/Units.hpp>
|
|
|
|
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
|
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
|
|
|
#include <cstddef>
|
|
#include <initializer_list>
|
|
#include <stdexcept>
|
|
|
|
using namespace Opm;
|
|
|
|
namespace {
|
|
|
|
EclipseGrid makeGrid()
|
|
{
|
|
EclipseGrid grid(3, 3, 3);
|
|
|
|
std::vector<int> actnum(27, 1);
|
|
for (const std::size_t layer : { 0, 1, 2 })
|
|
actnum[grid.getGlobalIndex(0, 0, layer)] = 0;
|
|
|
|
grid.resetACTNUM(actnum);
|
|
return grid;
|
|
}
|
|
|
|
Deck createAquiferCTDeck()
|
|
{
|
|
return Parser{}.parseString(R"(DIMENS
|
|
3 3 3 /
|
|
|
|
AQUDIMS
|
|
1* 1* 2 100 1 1000 /
|
|
|
|
GRID
|
|
ACTNUM
|
|
0 8*1 0 8*1 0 8*1 /
|
|
|
|
DXV
|
|
1 1 1 /
|
|
|
|
DYV
|
|
1 1 1 /
|
|
|
|
DZV
|
|
1 1 1 /
|
|
|
|
TOPS
|
|
9*100 /
|
|
|
|
PORO
|
|
27*0.15 /
|
|
|
|
PROPS
|
|
AQUTAB
|
|
0.01 0.112
|
|
0.05 0.229 /
|
|
|
|
SOLUTION
|
|
AQUCT
|
|
1 2000.0 1.5 100 .3 3.0e-5 330 10 360.0 1 2 /
|
|
/
|
|
)");
|
|
}
|
|
|
|
Deck createAquiferCTDeckDefaultP0()
|
|
{
|
|
return Parser{}.parseString(R"(DIMENS
|
|
3 3 3 /
|
|
|
|
AQUDIMS
|
|
1* 1* 2 100 1 1000 /
|
|
|
|
GRID
|
|
ACTNUM
|
|
0 8*1 0 8*1 0 8*1 /
|
|
|
|
DXV
|
|
1 1 1 /
|
|
|
|
DYV
|
|
1 1 1 /
|
|
|
|
DZV
|
|
1 1 1 /
|
|
|
|
TOPS
|
|
9*100 /
|
|
|
|
PORO
|
|
27*0.15 /
|
|
|
|
PROPS
|
|
AQUTAB
|
|
0.01 0.112
|
|
0.05 0.229 /
|
|
|
|
SOLUTION
|
|
AQUCT
|
|
1 2000.0 1* 100 .3 3.0e-5 330 10 360.0 1 2 /
|
|
/
|
|
)");
|
|
}
|
|
|
|
AquiferCT init_aquiferct(const Deck& deck)
|
|
{
|
|
EclipseState eclState( deck );
|
|
return AquiferCT(eclState.getTableManager(), deck);
|
|
}
|
|
|
|
} // Anonymous namespace
|
|
|
|
BOOST_AUTO_TEST_CASE(AquiferCTTest)
|
|
{
|
|
{
|
|
const auto aquiferct = init_aquiferct(createAquiferCTDeck());
|
|
BOOST_REQUIRE_EQUAL(aquiferct.size(), 1U);
|
|
|
|
for (const auto& it : aquiferct) {
|
|
BOOST_CHECK_EQUAL(it.aquiferID, 1);
|
|
BOOST_CHECK_CLOSE(it.porosity, 0.3, 1.0e-8);
|
|
BOOST_CHECK_EQUAL(it.inftableID, 2);
|
|
BOOST_CHECK_MESSAGE(it.initial_pressure.has_value(), "Initial pressure must be defined in CT aquifer");
|
|
BOOST_CHECK_CLOSE(it.initial_pressure.value(), 1.5e5, 1e-6);
|
|
}
|
|
}
|
|
|
|
{
|
|
const auto aquiferct = init_aquiferct(createAquiferCTDeckDefaultP0());
|
|
for (const auto& it : aquiferct) {
|
|
BOOST_CHECK_EQUAL(it.aquiferID, 1);
|
|
BOOST_CHECK_CLOSE(it.porosity, 0.3, 1.0e-8);
|
|
BOOST_CHECK_EQUAL(it.inftableID, 2);
|
|
BOOST_CHECK_MESSAGE(! it.initial_pressure.has_value(), "Initial pressure must NOT be defined in CT aquifer");
|
|
}
|
|
|
|
auto data = aquiferct.data();
|
|
AquiferCT aq2(data);
|
|
BOOST_CHECK( aq2 == aquiferct );
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
Deck createAQUANCONDeck_DEFAULT_INFLUX2()
|
|
{
|
|
return Parser{}.parseString(R"(DIMENS
|
|
3 3 3 /
|
|
|
|
GRID
|
|
ACTNUM
|
|
0 8*1 0 8*1 0 8*1 /
|
|
|
|
DXV
|
|
1 1 1 /
|
|
|
|
DYV
|
|
1 1 1 /
|
|
|
|
DZV
|
|
1 1 1 /
|
|
|
|
TOPS
|
|
9*100 /
|
|
|
|
PORO
|
|
27*0.15 /
|
|
|
|
SOLUTION
|
|
AQUANCON
|
|
1 2 2 1 1 1 1 J- 1.0 /
|
|
1 2 2 1 1 1 1 J- /
|
|
/
|
|
)");
|
|
}
|
|
|
|
Deck createAQUANCONDeck_DEFAULT_INFLUX1()
|
|
{
|
|
return Parser{}.parseString(R"(DIMENS
|
|
3 3 3 /
|
|
|
|
GRID
|
|
ACTNUM
|
|
0 8*1 0 8*1 0 8*1 /
|
|
|
|
DXV
|
|
1 1 1 /
|
|
|
|
DYV
|
|
1 1 1 /
|
|
|
|
DZV
|
|
1 1 1 /
|
|
|
|
TOPS
|
|
9*100 /
|
|
|
|
PORO
|
|
27*0.15 /
|
|
|
|
SOLUTION
|
|
AQUANCON
|
|
1 1 3 1 1 1 1 J- /
|
|
/
|
|
AQUANCON
|
|
2 1 1 2 2 1 1 J- /
|
|
/
|
|
)");
|
|
}
|
|
|
|
Deck createAQUANCONDeck_DEFAULT_ILLEGAL()
|
|
{
|
|
return Parser{}.parseString(R"(DIMENS
|
|
3 3 3 /
|
|
|
|
GRID
|
|
ACTNUM
|
|
0 8*1 0 8*1 0 8*1 /
|
|
|
|
DXV
|
|
1 1 1 /
|
|
|
|
DYV
|
|
1 1 1 /
|
|
|
|
DZV
|
|
1 1 1 /
|
|
|
|
TOPS
|
|
9*100 /
|
|
|
|
PORO
|
|
27*0.15 /
|
|
|
|
SOLUTION
|
|
AQUANCON
|
|
1 1 3 1 1 1 1 J- /
|
|
/
|
|
AQUANCON
|
|
2 1 2 1 2 1 1 J- /
|
|
/
|
|
)");
|
|
}
|
|
|
|
} // Anonymous namespace
|
|
|
|
BOOST_AUTO_TEST_CASE(AquanconTest_DEFAULT_INFLUX)
|
|
{
|
|
auto deck1 = createAQUANCONDeck_DEFAULT_INFLUX1();
|
|
const auto& grid = makeGrid();
|
|
Aquancon aqcon(grid, deck1);
|
|
|
|
const auto& cells_aq1 = aqcon[1];
|
|
/*
|
|
The cells I = 0..2 are connected to aquifer 1; cell I==0 is inactive and
|
|
not counted here ==> a total of 2 cells are connected to aquifer 1.
|
|
*/
|
|
BOOST_CHECK_EQUAL(cells_aq1.size(), 2U);
|
|
|
|
const auto& cells_aq2 = aqcon[2];
|
|
BOOST_CHECK_EQUAL(cells_aq2.size(), 1U);
|
|
BOOST_CHECK(aqcon.active());
|
|
|
|
auto deck2 = createAQUANCONDeck_DEFAULT_INFLUX2();
|
|
|
|
// The cell (2,1,1) is attached to both aquifer 1 and aquifer 2 - that is illegal.
|
|
auto deck3 = createAQUANCONDeck_DEFAULT_ILLEGAL();
|
|
BOOST_CHECK_THROW(Aquancon( grid, deck3), std::invalid_argument);
|
|
}
|
|
|
|
|
|
// allowing aquifer exists inside the reservoir
|
|
|
|
namespace {
|
|
|
|
Deck createAQUANCONDeck_ALLOW_INSIDE_AQUAN_OR_NOT()
|
|
{
|
|
return Parser{}.parseString(R"(DIMENS
|
|
3 3 3 /
|
|
|
|
GRID
|
|
ACTNUM
|
|
0 8*1 0 8*1 0 8*1 /
|
|
|
|
DXV
|
|
1 1 1 /
|
|
|
|
DYV
|
|
1 1 1 /
|
|
|
|
DZV
|
|
1 1 1 /
|
|
|
|
TOPS
|
|
9*100 /
|
|
|
|
PORO
|
|
27*0.15 /
|
|
|
|
SOLUTION
|
|
AQUFETP
|
|
1 20.0 1000.0 2000. 0.000001 200.0 /
|
|
2 20.0 1000.0 2000. 0.000001 200.0 /
|
|
/
|
|
AQUANCON
|
|
1 1 1 1 1 1 1 J- 2* YES /
|
|
1 2 2 1 1 1 1 J- 2* YES /
|
|
1 2 2 2 2 1 1 J- 2* YES /
|
|
2 1 1 1 1 3 3 J- 2* NO /
|
|
2 2 2 1 1 3 3 J- 2* NO /
|
|
2 2 2 2 2 3 3 J- 2* NO /
|
|
/
|
|
)");
|
|
}
|
|
|
|
} // Anonymous namespace
|
|
|
|
BOOST_AUTO_TEST_CASE(AquanconTest_ALLOW_AQUIFER_INSIDE_OR_NOT)
|
|
{
|
|
auto deck = createAQUANCONDeck_ALLOW_INSIDE_AQUAN_OR_NOT();
|
|
const EclipseState eclState( deck );
|
|
const Aquancon aqucon( eclState.getInputGrid(), deck);
|
|
|
|
const auto& data = aqucon.data();
|
|
const Aquancon aq2(data);
|
|
|
|
BOOST_CHECK(aqucon == aq2);
|
|
auto cells1 = aqucon[1];
|
|
auto cells2 = aqucon[2];
|
|
BOOST_CHECK_EQUAL(cells1.size() , 2U);
|
|
BOOST_CHECK_EQUAL(cells2.size() , 1U);
|
|
}
|
|
|
|
namespace {
|
|
|
|
Deck createAquifetpDeck()
|
|
{
|
|
return Parser{}.parseString(R"(RUNSPEC
|
|
DIMENS
|
|
3 3 3 /
|
|
|
|
AQUDIMS
|
|
1* 1* 2 100 1 1000 /
|
|
|
|
GRID
|
|
ACTNUM
|
|
0 8*1 0 8*1 0 8*1 /
|
|
|
|
DXV
|
|
1 1 1 /
|
|
|
|
DYV
|
|
1 1 1 /
|
|
|
|
DZV
|
|
1 1 1 /
|
|
|
|
TOPS
|
|
9*100 /
|
|
|
|
PORO
|
|
27*0.15 /
|
|
|
|
PROPS
|
|
AQUTAB
|
|
0.01 0.112
|
|
0.05 0.229 /
|
|
|
|
SOLUTION
|
|
AQUFETP
|
|
1 70000.0 4.0e3 2.0e9 1.0e-5 500 1 0 0 /
|
|
/
|
|
)");
|
|
}
|
|
|
|
Deck createNullAquifetpDeck()
|
|
{
|
|
return Parser{}.parseString(R"(RUNSPEC
|
|
DIMENS
|
|
3 3 3 /
|
|
|
|
AQUDIMS
|
|
1* 1* 2 100 1 1000 /
|
|
|
|
GRID
|
|
ACTNUM
|
|
0 8*1 0 8*1 0 8*1 /
|
|
|
|
DXV
|
|
1 1 1 /
|
|
|
|
DYV
|
|
1 1 1 /
|
|
|
|
DZV
|
|
1 1 1 /
|
|
|
|
TOPS
|
|
9*100 /
|
|
|
|
PORO
|
|
27*0.15 /
|
|
|
|
PROPS
|
|
AQUTAB
|
|
0.01 0.112
|
|
0.05 0.229 /
|
|
|
|
SOLUTION
|
|
)");
|
|
}
|
|
|
|
Deck createAquifetpDeck_defaultPressure()
|
|
{
|
|
return Parser{}.parseString(R"(DIMENS
|
|
3 3 3 /
|
|
|
|
AQUDIMS
|
|
1* 1* 2 100 1 1000 /
|
|
|
|
GRID
|
|
ACTNUM
|
|
0 8*1 0 8*1 0 8*1 /
|
|
|
|
DXV
|
|
1 1 1 /
|
|
|
|
DYV
|
|
1 1 1 /
|
|
|
|
DZV
|
|
1 1 1 /
|
|
|
|
TOPS
|
|
9*100 /
|
|
|
|
PORO
|
|
27*0.15 /
|
|
|
|
PROPS
|
|
AQUTAB
|
|
0.01 0.112
|
|
0.05 0.229 /
|
|
|
|
SOLUTION
|
|
AQUFETP
|
|
1 70000.0 1* 2.0e9 1.0e-5 500 1 0 0 /
|
|
/
|
|
)");
|
|
}
|
|
|
|
Aquifetp init_aquifetp(Deck& deck)
|
|
{
|
|
return { TableManager{ deck }, deck };
|
|
}
|
|
|
|
} // Anonymous namespace
|
|
|
|
BOOST_AUTO_TEST_CASE(AquifetpTest)
|
|
{
|
|
auto aqufetp_deck = createAquifetpDeck();
|
|
const auto& aquifetp = init_aquifetp(aqufetp_deck);
|
|
for (const auto& it : aquifetp) {
|
|
BOOST_CHECK_EQUAL(it.aquiferID, 1);
|
|
BOOST_CHECK_CLOSE(it.initial_watvolume, 2.0e9, 1.0e-8);
|
|
BOOST_CHECK_CLOSE(it.prod_index, 500/86400e5, 1.0e-8);
|
|
BOOST_CHECK_MESSAGE(it.initial_pressure.has_value(), "Fetkovich aquifer must have initial pressure value");
|
|
}
|
|
const auto& data = aquifetp.data();
|
|
Aquifetp aq2(data);
|
|
BOOST_CHECK_MESSAGE(aq2 == aquifetp, "Copy constructor must produce equal object");
|
|
|
|
auto aqufetp_deck_null = createNullAquifetpDeck();
|
|
const auto& aquifetp_null = init_aquifetp(aqufetp_deck_null);
|
|
BOOST_CHECK_EQUAL(aquifetp_null.size(), 0U);
|
|
|
|
auto aqufetp_deck_default = createAquifetpDeck_defaultPressure();
|
|
const auto& aquifetp_default = init_aquifetp(aqufetp_deck_default);
|
|
for (const auto& it : aquifetp_default) {
|
|
BOOST_CHECK_EQUAL(it.aquiferID, 1);
|
|
BOOST_CHECK_CLOSE(it.initial_watvolume, 2.0e9, 1.0e-8);
|
|
BOOST_CHECK_CLOSE(it.prod_index, 500/86400e5, 1.0e-8);
|
|
BOOST_CHECK_MESSAGE(!it.initial_pressure.has_value(), "Fetkovich aquifer mut NOT have initial pressure value when defaulted");
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TEST_CREATE)
|
|
{
|
|
Opm::Aqudims aqudims;
|
|
|
|
BOOST_CHECK_EQUAL( aqudims.getNumAqunum() , 1U );
|
|
BOOST_CHECK_EQUAL( aqudims.getNumConnectionNumericalAquifer() , 1U );
|
|
BOOST_CHECK_EQUAL( aqudims.getNumInfluenceTablesCT() , 1U );
|
|
BOOST_CHECK_EQUAL( aqudims.getNumRowsInfluenceTable() , 36U );
|
|
BOOST_CHECK_EQUAL( aqudims.getNumAnalyticAquifers() , 1U );
|
|
BOOST_CHECK_EQUAL( aqudims.getNumRowsAquancon() , 1U );
|
|
BOOST_CHECK_EQUAL( aqudims.getNumAquiferLists() , 0U );
|
|
BOOST_CHECK_EQUAL( aqudims.getNumAnalyticAquifersSingleList() , 0U );
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(Test_Aquifer_Config)
|
|
{
|
|
const std::string deck_string = R"(
|
|
DIMENS
|
|
3 3 3 /
|
|
GRID
|
|
DX
|
|
27*1 /
|
|
DY
|
|
27*1 /
|
|
DZ
|
|
27*1 /
|
|
TOPS
|
|
9*1 /
|
|
PORO
|
|
27*1 /
|
|
)";
|
|
Opm::Parser parser;
|
|
Opm::Deck deck = parser.parseString(deck_string);
|
|
Opm::EclipseState ecl_state(deck);
|
|
Opm::TableManager tables;
|
|
Opm::AquiferConfig conf(tables, ecl_state.getInputGrid(), deck, ecl_state.fieldProps());
|
|
BOOST_CHECK(!conf.active());
|
|
|
|
|
|
const auto& fetp = conf.fetp();
|
|
const auto& ct = conf.ct();
|
|
const auto& conn = conf.connections();
|
|
Opm::AquiferConfig conf2(fetp, ct, conn);
|
|
BOOST_CHECK( conf == conf2 );
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(Test_Aquifer_Config_Active)
|
|
{
|
|
const auto deck = Parser{}.parseString(R"(
|
|
START -- 0
|
|
10 MAY 2007 /
|
|
RUNSPEC
|
|
|
|
DIMENS
|
|
10 10 10 /
|
|
REGDIMS
|
|
3/
|
|
AQUDIMS
|
|
4 4 1* 1* 3 200 1* 1* /
|
|
GRID
|
|
DXV
|
|
10*400 /
|
|
DYV
|
|
10*400 /
|
|
DZV
|
|
10*400 /
|
|
TOPS
|
|
100*2202 /
|
|
PERMX
|
|
1000*0.25 /
|
|
COPY
|
|
PERMX PERMY /
|
|
PERMX PERMZ /
|
|
/
|
|
PORO
|
|
1000*0.15 /
|
|
AQUNUM
|
|
4 1 1 1 15000 5000 0.3 30 2700 / aq cell
|
|
5 2 1 1 150000 9000 0.3 30 2700 / aq cell
|
|
6 3 1 1 150000 9000 0.3 30 2700 / aq cell
|
|
7 4 1 1 150000 9000 0.3 30 2700 / aq cell
|
|
/
|
|
AQUCON
|
|
-- # I1 I2 J1 J2 K1 K2 Face
|
|
4 1 1 16 18 19 20 'I-' / connecting cells
|
|
5 2 2 16 18 19 20 'I-' / connecting cells
|
|
6 3 3 16 18 19 20 'I-' / connecting cells
|
|
7 4 4 16 18 19 20 'I-' / connecting cells
|
|
/
|
|
REGIONS
|
|
FIPNUM
|
|
200*1 300*2 500*3 /
|
|
FIPREG
|
|
200*10 300*20 500*30 /
|
|
SOLUTION
|
|
AQUCT
|
|
1 2040 1* 1000 .3 3.0e-5 1330 10 360.0 1 1* /
|
|
2 2040 1* 1000 .3 3.0e-5 1330 10 360.0 1 1* /
|
|
3 2040 1* 1000 .3 3.0e-5 1330 10 360.0 1 1* /
|
|
/
|
|
AQUANCON
|
|
1 1 10 10 2 10 10 'I-' 0.88 1 /
|
|
2 9 10 10 10 10 10 'I+' 0.88 1 /
|
|
3 9 9 8 10 9 8 'I+' 0.88 1 /
|
|
/
|
|
|
|
END
|
|
)");
|
|
|
|
const auto es = EclipseState { deck };
|
|
|
|
const auto& aquConfig = es.aquifer();
|
|
|
|
BOOST_CHECK_MESSAGE(aquConfig.hasAnalyticalAquifer(),
|
|
"Aquifer configuration object must have analytic aquifers");
|
|
|
|
BOOST_CHECK_MESSAGE(aquConfig.hasNumericalAquifer(),
|
|
"Aquifer configuration object must have numeric aquifers");
|
|
|
|
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(1), "Configuration object must have Aquifer ID 1");
|
|
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(2), "Configuration object must have Aquifer ID 2");
|
|
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(3), "Configuration object must have Aquifer ID 3");
|
|
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(4), "Configuration object must have Aquifer ID 4");
|
|
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(5), "Configuration object must have Aquifer ID 5");
|
|
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(6), "Configuration object must have Aquifer ID 6");
|
|
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(7), "Configuration object must have Aquifer ID 7");
|
|
BOOST_CHECK_MESSAGE(! aquConfig.hasAquifer(8), "Configuration object must NOT have Aquifer ID 8");
|
|
|
|
{
|
|
const auto expect = std::vector<int>{ 1, 2, 3 };
|
|
const auto analytic = analyticAquiferIDs(aquConfig);
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(analytic.begin(), analytic.end(),
|
|
expect .begin(), expect .end());
|
|
}
|
|
|
|
{
|
|
const auto expect = std::vector<int>{ 4, 5, 6, 7 };
|
|
const auto numeric = numericAquiferIDs(aquConfig);
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(numeric.begin(), numeric.end(),
|
|
expect .begin(), expect .end());
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
Deck createNumericalAquiferDeck()
|
|
{
|
|
const char *deckData = R"(
|
|
DIMENS
|
|
8 15 3 /
|
|
AQUDIMS
|
|
3 2 1* 1* 1* 50 1* 1* /
|
|
GRID
|
|
|
|
DX
|
|
360*10./
|
|
DY
|
|
360*10./
|
|
DZ
|
|
360*1./
|
|
TOPS
|
|
360*100./
|
|
|
|
PORO
|
|
0. 0.25 0. 357*0.25/
|
|
PERMX
|
|
360*1000./
|
|
PERMY
|
|
360*1000./
|
|
PERMZ
|
|
360*10./
|
|
-- setting the three cells for numerical aquifer to be inactive
|
|
ACTNUM
|
|
0 1 0 0 356*1 /
|
|
|
|
AQUNUM
|
|
--AQnr. I J K Area Length PHI K Depth Initial.Pr PVTNUM SATNUM
|
|
1 1 1 1 1000000.0 10000 0.25 400 2585.00 285.00 2 2 /
|
|
1 3 1 1 1500000.0 20000 0.24 600 2585.00 285.00 3 * /
|
|
1 4 1 1 2000000.0 30000 * 700 2585.00 285.00 * 3 /
|
|
/
|
|
AQUCON
|
|
-- Connect numerical aquifer to the reservoir
|
|
-- Id.nr I1 I2 J1 J2 K1 K2 Face Trans.mult. Trans.opt.
|
|
1 1 8 15 15 3 3 'J+' 1.00 1 /
|
|
/
|
|
)";
|
|
|
|
Parser parser;
|
|
return parser.parseString(deckData);
|
|
}
|
|
|
|
} // Anonymous namespace
|
|
|
|
BOOST_AUTO_TEST_CASE(NumericalAquiferTest)
|
|
{
|
|
const Opm::Deck numaquifer_deck = createNumericalAquiferDeck();
|
|
const Opm::EclipseState ecl_state(numaquifer_deck);
|
|
const Opm::EclipseGrid& grid = ecl_state.getInputGrid();
|
|
|
|
Opm::NumericalAquifers num_aqu{numaquifer_deck, grid, ecl_state.fieldProps()};
|
|
|
|
BOOST_CHECK_EQUAL(num_aqu.numRecords(), 3);
|
|
{
|
|
const auto mD = unit::convert::from(1.0, prefix::milli*unit::darcy);
|
|
|
|
const auto* c1 = num_aqu.getAquifer(1).getCellPrt(0);
|
|
const auto* c2 = num_aqu.getAquifer(1).getCellPrt(1);
|
|
const auto* c3 = num_aqu.getAquifer(1).getCellPrt(2);
|
|
|
|
BOOST_CHECK_EQUAL(c1->record_id, std::size_t{0});
|
|
BOOST_CHECK_EQUAL(c1->I, std::size_t{0});
|
|
BOOST_CHECK_EQUAL(c1->J, std::size_t{0});
|
|
BOOST_CHECK_EQUAL(c1->K, std::size_t{0});
|
|
|
|
BOOST_CHECK_CLOSE(c1->area, 1.0e6, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c1->length, 10.0e3, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c1->porosity, 0.25, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c1->permeability, 400*mD, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c1->depth, 2585.0, 1.0e-10);
|
|
BOOST_CHECK_MESSAGE(c1->init_pressure.has_value(), "Cell 1 must have an initial pressure");
|
|
BOOST_CHECK_CLOSE(c1->init_pressure.value(), 285.0*unit::barsa, 1.0e-10);
|
|
BOOST_CHECK_EQUAL(c1->pvttable, 2);
|
|
BOOST_CHECK_EQUAL(c1->sattable, 2);
|
|
BOOST_CHECK_EQUAL(c1->global_index, 0);
|
|
|
|
BOOST_CHECK_EQUAL(c2->record_id, std::size_t{1});
|
|
BOOST_CHECK_EQUAL(c2->I, std::size_t{2});
|
|
BOOST_CHECK_EQUAL(c2->J, std::size_t{0});
|
|
BOOST_CHECK_EQUAL(c2->K, std::size_t{0});
|
|
|
|
BOOST_CHECK_CLOSE(c2->area, 1.5e6, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c2->length, 20.0e3, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c2->porosity, 0.24, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c2->permeability, 600*mD, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c2->depth, 2585.0, 1.0e-10);
|
|
BOOST_CHECK_MESSAGE(c2->init_pressure.has_value(), "Cell 2 must have an initial pressure");
|
|
BOOST_CHECK_CLOSE(c2->init_pressure.value(), 285.0*unit::barsa, 1.0e-10);
|
|
BOOST_CHECK_EQUAL(c2->pvttable, 3);
|
|
BOOST_CHECK_EQUAL(c2->sattable, 1);
|
|
BOOST_CHECK_EQUAL(c2->global_index, 2);
|
|
|
|
BOOST_CHECK_EQUAL(c3->record_id, std::size_t{2});
|
|
BOOST_CHECK_EQUAL(c3->I, std::size_t{3});
|
|
BOOST_CHECK_EQUAL(c3->J, std::size_t{0});
|
|
BOOST_CHECK_EQUAL(c3->K, std::size_t{0});
|
|
|
|
BOOST_CHECK_CLOSE(c3->area, 2.0e6, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c3->length, 30.0e3, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c3->porosity, 0.25, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c3->permeability, 700*mD, 1.0e-10);
|
|
BOOST_CHECK_CLOSE(c3->depth, 2585.0, 1.0e-10);
|
|
BOOST_CHECK_MESSAGE(c3->init_pressure.has_value(), "Cell 3 must have an initial pressure");
|
|
BOOST_CHECK_CLOSE(c3->init_pressure.value(), 285.0*unit::barsa, 1.0e-10);
|
|
BOOST_CHECK_EQUAL(c3->pvttable, 1);
|
|
BOOST_CHECK_EQUAL(c3->sattable, 3);
|
|
BOOST_CHECK_EQUAL(c3->global_index, 3);
|
|
}
|
|
|
|
// using processed actnum for numerical aquifer connection generation
|
|
std::vector<int> new_actnum(360, 1);
|
|
new_actnum[0] = 0;
|
|
new_actnum[1] = 0;
|
|
new_actnum[3] = 0;
|
|
num_aqu.postProcessConnections(grid, new_actnum);
|
|
BOOST_CHECK(num_aqu.hasAquifer(1));
|
|
BOOST_CHECK(num_aqu.size() == 1);
|
|
const auto all_aquifer_cells = num_aqu.allAquiferCells();
|
|
BOOST_CHECK(all_aquifer_cells.count(0) > 0);
|
|
BOOST_CHECK(all_aquifer_cells.count(2) > 0);
|
|
BOOST_CHECK(all_aquifer_cells.count(3) > 0);
|
|
BOOST_CHECK(all_aquifer_cells.count(1) == 0);
|
|
|
|
const auto& aquifer = num_aqu.getAquifer(1);
|
|
BOOST_CHECK(aquifer.numCells() == 3);
|
|
BOOST_CHECK(aquifer.numConnections() == 8 );
|
|
|
|
BOOST_CHECK(grid.getNumActive() == 360);
|
|
// the three aquifer cells are active
|
|
BOOST_CHECK(grid.cellActive(0, 0, 0));
|
|
BOOST_CHECK(grid.cellActive(2, 0, 0));
|
|
BOOST_CHECK(grid.cellActive(3, 0, 0));
|
|
|
|
// checking the pore volume of the aquifer cells
|
|
const auto& porv_data = ecl_state.fieldProps().porv(true);
|
|
BOOST_CHECK_CLOSE(porv_data[0], 2500000000, 1.e-10);
|
|
BOOST_CHECK_CLOSE(porv_data[2], 7200000000, 1.e-10);
|
|
BOOST_CHECK_CLOSE(porv_data[3], 15000000000, 1.e-10);
|
|
|
|
const auto& pvtnum = ecl_state.fieldProps().get_int("PVTNUM");
|
|
BOOST_CHECK_EQUAL(pvtnum[0], 2);
|
|
BOOST_CHECK_EQUAL(pvtnum[1], 1); // none aquifer cell
|
|
BOOST_CHECK_EQUAL(pvtnum[2], 3);
|
|
BOOST_CHECK_EQUAL(pvtnum[3], 1);
|
|
|
|
const auto& satnum = ecl_state.fieldProps().get_int("SATNUM");
|
|
BOOST_CHECK_EQUAL(satnum[0], 2);
|
|
BOOST_CHECK_EQUAL(satnum[1], 1); // none aquifer cell
|
|
BOOST_CHECK_EQUAL(satnum[2], 1);
|
|
BOOST_CHECK_EQUAL(satnum[3], 3);
|
|
|
|
|
|
const auto& permx = ecl_state.fieldProps().get_double("PERMX");
|
|
BOOST_CHECK_SMALL(permx[0], 1.e-20);
|
|
BOOST_CHECK_SMALL(permx[2], 1.e-20);
|
|
BOOST_CHECK_SMALL(permx[3], 1.e-20);
|
|
const auto& permy = ecl_state.fieldProps().get_double("PERMY");
|
|
BOOST_CHECK_SMALL(permy[0], 1.e-20);
|
|
BOOST_CHECK_SMALL(permy[2], 1.e-20);
|
|
BOOST_CHECK_SMALL(permy[3], 1.e-20);
|
|
const auto& permz = ecl_state.fieldProps().get_double("PERMZ");
|
|
BOOST_CHECK_SMALL(permz[0], 1.e-20);
|
|
BOOST_CHECK_SMALL(permz[2], 1.e-20);
|
|
BOOST_CHECK_SMALL(permz[3], 1.e-20);
|
|
const auto& poro = ecl_state.fieldProps().get_double("PORO");
|
|
BOOST_CHECK_CLOSE(poro[0], 0.25, 1.e-10);
|
|
BOOST_CHECK_CLOSE(poro[2], 0.24, 1.e-10);
|
|
BOOST_CHECK_CLOSE(poro[3], 0.25, 1.e-10);
|
|
}
|
|
|
|
namespace {
|
|
|
|
std::pair<Opm::Aquancon, Opm::EclipseGrid> load_aquifer(const std::string& aqucon)
|
|
{
|
|
const std::string data1 = R"(
|
|
DIMENS
|
|
8 15 3 /
|
|
AQUDIMS
|
|
3 2 1* 1* 1* 50 1* 1* /
|
|
GRID
|
|
|
|
DX
|
|
360*10./
|
|
DY
|
|
360*10./
|
|
DZ
|
|
360*1./
|
|
TOPS
|
|
360*100./
|
|
|
|
PORO
|
|
0. 0.25 0. 357*0.25/
|
|
PERMX
|
|
360*1000./
|
|
PERMY
|
|
360*1000./
|
|
PERMZ
|
|
360*10./
|
|
-- setting the three cells for numerical aquifer to be inactive
|
|
ACTNUM
|
|
0 1 0 0 356*1 /
|
|
|
|
AQUNUM
|
|
--AQnr. I J K Area Length PHI K Depth Initial.Pr PVTNUM SATNUM
|
|
1 1 1 1 1000000.0 10000 0.25 400 2585.00 285.00 2 2 /
|
|
1 3 1 1 1500000.0 20000 0.24 600 2585.00 285.00 3 * /
|
|
1 4 1 1 2000000.0 30000 * 700 2585.00 285.00 * 3 /
|
|
/
|
|
)";
|
|
Opm::Parser parser;
|
|
auto deck = parser.parseString( data1 + aqucon );
|
|
auto grid = Opm::EclipseGrid( deck );
|
|
|
|
return { Opm::Aquancon(grid, deck), grid };
|
|
}
|
|
|
|
} // Anonymous namespace
|
|
|
|
BOOST_AUTO_TEST_CASE(AQUCONN_FUNKYNESS )
|
|
{
|
|
{
|
|
const std::string aq = R"(
|
|
AQUANCON
|
|
1 1 8 15 15 3 3 'J+' 1.00 2 /
|
|
/
|
|
)";
|
|
const auto& [aquconn, _] = load_aquifer(aq);
|
|
(void)_;
|
|
auto cell1 = aquconn[1][0];
|
|
BOOST_CHECK_EQUAL(cell1.influx_coeff, 2.0);
|
|
}
|
|
|
|
|
|
{
|
|
const std::string aq = R"(
|
|
AQUANCON
|
|
1 1 8 15 15 3 3 'J+' * 2 /
|
|
/
|
|
)";
|
|
const auto& [aquconn, grid] = load_aquifer(aq);
|
|
const auto& dims = grid.getCellDims(0,14,2);
|
|
auto cell1 = aquconn[1][0];
|
|
BOOST_CHECK_EQUAL(cell1.influx_coeff, 2.0 * dims[0]*dims[2]);
|
|
}
|
|
|
|
{
|
|
const std::string aq = R"(
|
|
AQUANCON
|
|
1 1 8 15 15 3 3 'I+' * 3 /
|
|
/
|
|
)";
|
|
const auto& [aquconn, grid] = load_aquifer(aq);
|
|
const auto& dims = grid.getCellDims(0,14,2);
|
|
auto cell1 = aquconn[1][0];
|
|
BOOST_CHECK_EQUAL(cell1.influx_coeff, 3.0 * dims[1]*dims[2]);
|
|
}
|
|
|
|
{
|
|
const std::string aq = R"(
|
|
AQUANCON
|
|
1 1 8 15 15 3 3 'I+' * 3 /
|
|
1 1 8 15 15 3 3 'I+' 100 4 /
|
|
/
|
|
)";
|
|
const auto& [aquconn, grid] = load_aquifer(aq);
|
|
const auto& dims = grid.getCellDims(0,14,2);
|
|
auto cell1 = aquconn[1][0];
|
|
BOOST_CHECK_EQUAL(cell1.influx_coeff, 4 * ( 100 + 3.0 * dims[1]*dims[2]));
|
|
}
|
|
|
|
{
|
|
const std::string aq = R"(
|
|
AQUANCON
|
|
1 1 8 15 15 3 3 'I+' 100 4 /
|
|
1 1 8 15 15 3 3 'I+' * 3 /
|
|
1 1 8 15 15 3 3 'I+' 77 2 /
|
|
/
|
|
)";
|
|
const auto& [aquconn, grid] = load_aquifer(aq);
|
|
auto cell1 = aquconn[1][0];
|
|
BOOST_CHECK_EQUAL(cell1.influx_coeff, 2*(77 + 3*(0 + 4*100)));
|
|
}
|
|
}
|