Files
opm-common/tests/test_AggregateAquiferData.cpp
2023-02-17 17:32:24 +01:00

1030 lines
33 KiB
C++

/*
Copyright 2021 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 Aggregate_Aquifer_Data
#include <boost/test/unit_test.hpp>
#include <opm/output/eclipse/AggregateAquiferData.hpp>
#include <opm/output/data/Aquifer.hpp>
#include <opm/output/eclipse/InteHEAD.hpp>
#include <opm/output/eclipse/VectorItems/aquifer.hpp>
#include <opm/output/eclipse/WriteRestartHelpers.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/Aquancon.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferCT.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/Aquifetp.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferConfig.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/Grid/FaceDir.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/EclipseState/Tables/FlatTable.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/common/utility/TimeService.hpp>
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <unordered_map>
#include <utility>
#include <vector>
namespace {
class AquiferConnections
{
public:
using Connections = std::vector<Opm::Aquancon::AquancCell>;
using AllConnections = std::unordered_map<int, Connections>;
void addConnection(const int aquiferID,
const std::size_t cartesianCell,
const double influxCoefficient,
const double effectiveFaceArea,
const Opm::FaceDir::DirEnum direction)
{
this->all_connections_[aquiferID]
.emplace_back(aquiferID, cartesianCell, influxCoefficient,
effectiveFaceArea, direction);
}
const AllConnections& getAllConnections() const
{
return this->all_connections_;
}
private:
AllConnections all_connections_;
};
double prodIndexUnit()
{
using M = Opm::UnitSystem::measure;
return Opm::UnitSystem::newMETRIC().to_si(M::liquid_productivity_index, 1.0);
}
double temperatureUnit()
{
using M = Opm::UnitSystem::measure;
return Opm::UnitSystem::newMETRIC().to_si(M::temperature, 1.0);
}
double pressureUnit()
{
using M = Opm::UnitSystem::measure;
return Opm::UnitSystem::newMETRIC().to_si(M::pressure, 1.0);
}
double compressibilityUnit()
{
using M = Opm::UnitSystem::measure;
return Opm::UnitSystem::newMETRIC().from_si(M::pressure, 1.0);
}
double volumeUnit()
{
using M = Opm::UnitSystem::measure;
return Opm::UnitSystem::newMETRIC().to_si(M::liquid_surface_volume, 1.0);
}
double depthUnit()
{
using M = Opm::UnitSystem::measure;
return Opm::UnitSystem::newMETRIC().to_si(M::length, 1.0);
}
double permeabilityUnit()
{
using M = Opm::UnitSystem::measure;
return Opm::UnitSystem::newMETRIC().to_si(M::permeability, 1.0);
}
Opm::TableManager waterProperties()
{
const auto deck = Opm::Parser{}.parseString(R"(RUNSPEC
TABDIMS
1 2 50 60 2 60 /
AQUDIMS
1* 1* 3 100 3 1000 /
PROPS
PVTW
279.0 1.038 5.0E-05 0.318 0.0001 /
279.0 1.038 5.0E-05 0.118 0.0001 /
DENSITY
856.50000 1053.0 0.85204 /
856.50000 1033.0 0.85204 /
AQUTAB
0.01 0.112
1000.00 666.982 /
0.01 0.112
1000.00 250.578 /
)");
return Opm::TableManager { deck };
}
std::pair<std::vector<double>, std::vector<double>> CTInfluenceFunction_1()
{
return {
std::vector<double> { // tD
0.010, 0.050, 0.100, 0.150, 0.200, 0.250, 0.300, 0.400,
0.500, 0.600, 0.700, 0.800, 0.900, 1.000, 1.500, 2.000,
2.500, 3.000, 4.000, 5.000, 6.000, 7.000, 8.000, 9.000,
10.00, 15.00, 20.00, 25.00, 30.00, 40.00, 50.00, 60.00,
70.00, 80.00, 90.00, 100.0, 150.0, 200.0, 250.0, 300.0,
400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000,
}
,
std::vector<double> { // pD
0.112, 0.229, 0.315, 0.376, 0.424, 0.469, 0.503, 0.564,
0.616, 0.659, 0.702, 0.735, 0.772, 0.802, 0.927, 1.020,
1.101, 1.169, 1.275, 1.362, 1.436, 1.500, 1.556, 1.604,
1.651, 1.829, 1.960, 2.067, 2.147, 2.282, 2.388, 2.476,
2.550, 2.615, 2.672, 2.723, 2.921, 3.064, 3.173, 3.263,
3.406, 3.516, 3.608, 3.684, 3.750, 3.809, 3.86,
}
};
}
std::pair<std::vector<double>, std::vector<double>> CTInfluenceFunction_3()
{
return {
std::vector<double> { // tD
0.01, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.40, 0.50, 0.52,
0.54, 0.56, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95,
1.00, 1.20, 1.40, 1.60, 2.00, 3.00, 4.00, 5.00, 10.00,
20.00, 30.00, 50.00, 100.00, 200.00, 300.00, 500.00,
1000.00,
},
std::vector<double> { // pD
0.112, 0.229, 0.315, 0.376, 0.424, 0.469, 0.503, 0.564, 0.616,
0.627, 0.636, 0.645, 0.662, 0.683, 0.703, 0.721, 0.740, 0.758,
0.776, 0.791, 0.806, 0.865, 0.920, 0.973, 1.076, 1.328, 1.578,
1.828, 3.078, 5.578, 8.078, 13.078, 25.578, 50.578, 75.578,
125.578, 250.578,
}
};
}
void connectCarterTracy(AquiferConnections& aquancon)
{
using FDir = Opm::FaceDir::DirEnum;
{
const auto aquiferID = 1;
const auto cartCell1 = std::size_t{699}; // one-based IJK = (20,5,7)
const auto cartCell2 = std::size_t{799}; // one-based IJK = (20,5,8)
const auto cartCell3 = std::size_t{899}; // one-based IJK = (20,5,9)
const auto effFaceArea = 4.0 * 0.2; // DY * DZ
const auto influxCoeff = effFaceArea;
aquancon.addConnection(aquiferID, cartCell1, 1.0*influxCoeff, 6.0*effFaceArea, FDir::XMinus);
aquancon.addConnection(aquiferID, cartCell2, 2.0*influxCoeff, 12.0*effFaceArea, FDir::YMinus);
aquancon.addConnection(aquiferID, cartCell3, 3.0*influxCoeff, 18.0*effFaceArea, FDir::ZMinus);
}
{
const auto aquiferID = 6;
const auto cartCell = std::size_t{579}; // one-based IJK = (20,4,6)
const auto effFaceArea = 4.0 * 0.2; // DY * DZ
const auto influxCoeff = 1.0; // [m^2]
aquancon.addConnection(aquiferID, cartCell, influxCoeff, effFaceArea, FDir::XPlus);
}
}
Opm::AquiferCT createCarterTracy()
{
const auto porosity = 0.3;
const auto compr = 3.0e-5*compressibilityUnit();
const auto datumDepth = 2000.0*depthUnit();
const auto initialPressure = 269.0*pressureUnit();
const auto initialTemperature = 50*temperatureUnit();
const auto angle = 360.0; // degrees
const auto thickness = 10.0*depthUnit();
auto properties = std::vector<Opm::AquiferCT::AQUCT_data>{};
{
const auto aquiferID = 1;
const auto influenceFunction = 1;
const auto pvtTable = 2;
const auto ro = 800.0*depthUnit();
const auto ka = 1000.0*permeabilityUnit();
const auto& [tD, pD] = CTInfluenceFunction_1();
auto& ct = properties
.emplace_back(aquiferID, influenceFunction, pvtTable,
porosity, datumDepth, compr, ro, ka,
thickness, angle / 360.0, initialPressure, initialTemperature);
ct.dimensionless_time = tD;
ct.dimensionless_pressure = pD;
ct.finishInitialisation(waterProperties());
}
{
const auto aquiferID = 6;
const auto influenceFunction = 3;
const auto pvtTable = 1;
const auto ro = 900.0*depthUnit();
const auto ka = 5000.0*permeabilityUnit();
const auto& [tD, pD] = CTInfluenceFunction_3();
auto& ct = properties
.emplace_back(aquiferID, influenceFunction, pvtTable,
porosity, datumDepth, compr, ro, ka,
thickness, angle / 360.0, initialPressure, initialTemperature);
ct.dimensionless_time = tD;
ct.dimensionless_pressure = pD;
ct.finishInitialisation(waterProperties());
}
return Opm::AquiferCT(properties);
}
void connectFetkovic(AquiferConnections& aquancon)
{
using FDir = Opm::FaceDir::DirEnum;
{
const auto aquiferID = 2;
const auto cartCell = std::size_t{619}; // one-based IJK = (20,1,7)
const auto effFaceArea = 4.0 * 0.2; // DY * DZ
const auto influxCoeff = effFaceArea;
aquancon.addConnection(aquiferID, cartCell, influxCoeff, effFaceArea, FDir::YPlus);
}
{
const auto aquiferID = 4;
const auto cartCell = std::size_t{419}; // one-based IJK = (20,1,5)
const auto effFaceArea = 4.0 * 0.2; // DY * DZ
const auto influxCoeff = 1.0; // [m^2]
aquancon.addConnection(aquiferID, cartCell, influxCoeff, effFaceArea, FDir::ZPlus);
}
}
Opm::Aquifetp createFetkovich()
{
const auto compr = 1.5312e-4*compressibilityUnit();
const auto datumDepth = 2000.0*depthUnit();
const auto initialPressure = 250.0*pressureUnit();
const auto initialTemperature = 20*temperatureUnit();
auto properties = std::vector<Opm::Aquifetp::AQUFETP_data>{};
{
const auto aquiferID = 2;
const auto pvtTable = 2;
const auto prodIndex = 495.0*prodIndexUnit();
const auto initialVolume = 5.0e+10*volumeUnit();
auto& fetp = properties
.emplace_back(aquiferID, pvtTable, prodIndex, compr,
initialVolume, datumDepth, initialPressure, initialTemperature);
fetp.finishInitialisation(waterProperties());
}
{
const auto aquiferID = 4;
const auto pvtTable = 1;
const auto prodIndex = 910.0*prodIndexUnit();
const auto initialVolume = 2.0e+10*volumeUnit();
auto& fetp = properties
.emplace_back(aquiferID, pvtTable, prodIndex, compr,
initialVolume, datumDepth, initialPressure, initialTemperature);
fetp.finishInitialisation(waterProperties());
}
return Opm::Aquifetp(properties);
}
Opm::AquiferFlux createAquiferFluxs() {
// TODO: just for compilation for now, will complete it
Opm::AquiferFlux aquifers;
return aquifers;
}
Opm::AquiferConfig createAquiferConfig()
{
auto aquancon = AquiferConnections{};
connectCarterTracy(aquancon);
connectFetkovic(aquancon);
return {
createFetkovich(), createCarterTracy(), createAquiferFluxs(), Opm::Aquancon(aquancon.getAllConnections())
};
}
Opm::AquiferConfig createNumericAquiferConfig()
{
const auto deck = Opm::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
--ID I J K Area Len Phi K Depth P0 PVT SAT
1 1 1 1 15000 5000 0.3 30 2700 285 /
1 2 1 1 160000 6000 0.4 400 2705 295 1* 3 /
1 3 1 1 150000 7000 0.5 5000 2710 1* 2 /
2 4 1 1 140000 9000 0.3 300 2715 250 2 3 / aq cell
/
AQUCON
-- # I1 I2 J1 J2 K1 K2 Face
1 1 1 16 18 19 20 'I-' /
1 2 2 16 18 19 20 'I-' /
1 3 3 16 18 19 20 'I-' /
2 4 4 16 18 19 20 'I-' /
/
END
)");
return Opm::EclipseState { deck }.aquifer();
}
Opm::EclipseGrid createGrid()
{
auto grid = Opm::EclipseGrid { 20, 5, 10, 5.0, 4.0, 0.2 };
auto actnum = std::vector<int>(grid.getCartesianSize(), 1);
actnum[grid.getGlobalIndex( 1, 1, 1)] = 0;
actnum[grid.getGlobalIndex( 2, 1, 1)] = 0;
actnum[grid.getGlobalIndex(19, 1, 6)] = 0;
actnum[grid.getGlobalIndex(19, 2, 6)] = 0;
grid.resetACTNUM(actnum);
return grid;
}
Opm::RestartIO::InteHEAD::AquiferDims syntheticAquiferDimensions()
{
auto aqDims = Opm::RestartIO::InteHEAD::AquiferDims{};
aqDims.numAquifers = 4; // 1, 2, 4, 6
aqDims.maxNumAquifers = 10; // >= 6
aqDims.maxNumAquiferConn = 5; // >= 3
aqDims.maxNumActiveAquiferConn = 3; // ID = 1
aqDims.maxAquiferID = 6;
return aqDims;
}
Opm::RestartIO::InteHEAD::AquiferDims syntheticNumericAquiferDimensions()
{
auto aqDims = Opm::RestartIO::InteHEAD::AquiferDims{};
aqDims.numNumericAquiferRecords = 4;
return aqDims;
}
Opm::RestartIO::InteHEAD::AquiferDims parseAquiferDimensions()
{
const auto deck = Opm::Parser{}.parseString(R"(RUNSPEC
DIMENS
20 5 10 /
AQUDIMS
-- MXNAQN MXNAQC NIFTBL NRIFTB NANAQU NNCAMAX
4 4 5 100 5 1000 /
GRID
DXV
20*100.0 /
DYV
5*50.0 /
DZV
10*10.0 /
DEPTHZ
126*2000.0 /
PORO
1000*0.25 /
EQUALS
'PORO' 0.0 2 3 2 2 2 2 /
'PORO' 0.0 20 20 2 3 7 7 /
/
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
/
SOLUTION
AQUFETP
-- Aqu depth Pr vol Comp PI PVTW
2 2000.0 250.0 5.0E+10 1.5312E-4 495 2 /
4 2000.0 250.0 2.0E+10 1.5312E-4 910 1 /
/
AQUANCON
-- Aq# I1 I2 J1 J2 K1 K2 FACE
2 20 20 1 5 7 9 'I+' 1* 1.0 NO /
4 20 20 1 5 5 6 'I+' 1.0 1.0 NO /
/
)");
const auto es = Opm::EclipseState{ deck };
return Opm::RestartIO::inferAquiferDimensions(es);
}
Opm::SummaryState sim_state()
{
auto state = Opm::SummaryState{Opm::TimeService::now()};
state.update("AAQP:1", 123.456);
state.update("AAQR:1", 234.567);
state.update("AAQT:1", 3456.789);
state.update("AAQPD:1", 4.567);
state.update("AAQTD:1", 50.607);
state.update("AAQP:2", 121.212);
state.update("AAQR:2", 222.333);
state.update("AAQT:2", 333.444);
state.update("AAQP:4", 555.444);
state.update("AAQR:4", 333.222);
state.update("AAQT:4", 222.111);
state.update("AAQP:6", 456.123);
state.update("AAQR:6", 34.567);
state.update("AAQT:6", 4444.5555);
state.update("AAQPD:6", 50.706);
state.update("AAQTD:6", 100.321);
return state;
}
Opm::SummaryState aqunum_sim_state()
{
auto state = Opm::SummaryState{Opm::TimeService::now()};
state.update("ANQP:1", 123.456);
state.update("ANQR:1", 234.567);
state.update("ANQT:1", 3456.789);
state.update("ANQP:2", 121.212);
state.update("ANQR:2", 222.333);
state.update("ANQT:2", 333.444);
return state;
}
Opm::data::Aquifers aquiferValues(const Opm::AquiferConfig& aquConfig)
{
auto aquiferValues = Opm::data::Aquifers{};
for (const auto& aquct : aquConfig.ct()) {
auto& aquifer = aquiferValues[aquct.aquiferID];
aquifer.aquiferID = aquct.aquiferID;
aquifer.initPressure = aquct.initial_pressure.value();
aquifer.datumDepth = aquct.datum_depth;
auto* aquCT = aquifer.typeData.create<Opm::data::AquiferType::CarterTracy>();
aquCT->timeConstant = aquct.timeConstant();
aquCT->influxConstant = aquct.influxConstant();
aquCT->waterDensity = aquct.waterDensity();
aquCT->waterViscosity = aquct.waterViscosity();
}
for (const auto& aqufetp : aquConfig.fetp()) {
auto& aquifer = aquiferValues[aqufetp.aquiferID];
aquifer.aquiferID = aqufetp.aquiferID;
aquifer.initPressure = aqufetp.initial_pressure.value();
aquifer.datumDepth = aqufetp.datum_depth;
auto* aquFet = aquifer.typeData.create<Opm::data::AquiferType::Fetkovich>();
aquFet->initVolume = aqufetp.initial_watvolume;
aquFet->prodIndex = aqufetp.prod_index;
aquFet->timeConstant = aqufetp.timeConstant();
}
return aquiferValues;
}
Opm::data::Aquifers numericAquiferValues(const Opm::AquiferConfig& aquConfig)
{
const auto equilibratedAquiferCellPressure = 300.0*pressureUnit();
auto aquiferValues = Opm::data::Aquifers{};
for (const auto& [aquiferID, aquNum] : aquConfig.numericalAquifers().aquifers()) {
const auto numCells = aquNum.numCells();
auto& aquifer = aquiferValues[aquiferID];
aquifer.aquiferID = static_cast<int>(aquiferID);
auto* aquNumData = aquifer.typeData.create<Opm::data::AquiferType::Numerical>();
aquNumData->initPressure.reserve(numCells);
for (auto cellIndex = 0*numCells; cellIndex < numCells; ++cellIndex) {
const auto* aqCell = aquNum.getCellPrt(cellIndex);
const auto p0 = aqCell->init_pressure.has_value()
? aqCell->init_pressure.value()
: equilibratedAquiferCellPressure;
aquNumData->initPressure.push_back(p0);
}
}
return aquiferValues;
}
template <class Coll1, class Coll2>
void check_is_close(const Coll1& coll1, const Coll2& coll2, const double tol)
{
BOOST_REQUIRE_EQUAL(std::size(coll1), std::size(coll2));
if (coll1.empty()) { return; }
auto c1 = std::begin(coll1);
auto e1 = std::end (coll1);
auto c2 = std::begin(coll2);
for (; c1 != e1; ++c1, ++c2) {
BOOST_CHECK_CLOSE(*c1, *c2, tol);
}
}
} // namespace
BOOST_AUTO_TEST_SUITE(Aggregate_Aquifer_Data)
BOOST_AUTO_TEST_CASE(AquiferDimensions)
{
const auto aqDims = parseAquiferDimensions();
BOOST_CHECK_EQUAL(aqDims.numAquifers, 2); // 2 unique aquifer IDs (2 and 4)
BOOST_CHECK_EQUAL(aqDims.maxNumAquifers, 5); // AQUDIMS(5)
BOOST_CHECK_EQUAL(aqDims.maxNumAquiferConn, 1000); // AQUDIMS(6)
BOOST_CHECK_EQUAL(aqDims.maxNumActiveAquiferConn, 13); // In aquifer ID 2
BOOST_CHECK_EQUAL(aqDims.maxAquiferID, 4); // Maximum aquifer ID
BOOST_CHECK_EQUAL(aqDims.numNumericAquiferRecords, 4); // Number of lines of AQUNUM data
// # Data items per analytic aquifer
BOOST_CHECK_EQUAL(aqDims.numIntAquiferElem, 18); // # Integer
BOOST_CHECK_EQUAL(aqDims.numRealAquiferElem, 24); // # Single precision
BOOST_CHECK_EQUAL(aqDims.numDoubAquiferElem, 10); // # Double precision
// # Data items per analytic aquifer *connection*
BOOST_CHECK_EQUAL(aqDims.numIntConnElem, 7); // # Integer
BOOST_CHECK_EQUAL(aqDims.numRealConnElem, 2); // # Single precision
BOOST_CHECK_EQUAL(aqDims.numDoubConnElem, 4); // # Double precision
// # Data items per numeric aquifer
BOOST_CHECK_EQUAL(aqDims.numNumericAquiferIntElem, 10); // # Integer
BOOST_CHECK_EQUAL(aqDims.numNumericAquiferDoubleElem, 13); // # Double precision
}
BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers)
{
const auto aqDims = syntheticAquiferDimensions();
const auto aqConfig = createAquiferConfig();
const auto aquiferData = Opm::RestartIO::Helpers::
AggregateAquiferData{ aqDims, aqConfig, createGrid() };
BOOST_CHECK_EQUAL(aquiferData.maximumActiveAnalyticAquiferID(), 6);
// ICAQ:1
{
const auto expect = std::vector<int> {
// Connection 0
20, 5, 7, 993, 1, 0, 0,
// Connection 1
20, 5, 8, 994, 3, 0, 0,
// Connection 2
20, 5, 9, 995, 5, 0, 0,
};
const auto& icaq = aquiferData.getIntegerAquiferConnectionData(1);
BOOST_CHECK_EQUAL_COLLECTIONS(icaq.begin(), icaq.end(), expect.begin(), expect.end());
}
// SCAQ:1
{
const auto expect = std::vector<float> {
// Connection 0
1.0f/6.0f, 1.0f,
// Connection 1
1.0f/3.0f, 2.0f,
// Connection 2
1.0f/2.0f, 3.0f,
};
const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(1);
check_is_close(scaq, expect, 1.0e-7);
}
// ICAQ:2
{
const auto expect = std::vector<int> {
// Connection 0
20, 1, 7, 955, 4, 0, 0,
// Connection 1 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
// Connection 2 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
};
const auto& icaq = aquiferData.getIntegerAquiferConnectionData(2);
BOOST_CHECK_EQUAL_COLLECTIONS(icaq.begin(), icaq.end(), expect.begin(), expect.end());
}
// SCAQ:2
{
const auto expect = std::vector<float> {
// Connection 0
1.0f, 1.0f,
// Connection 1 (nonexistent)
0.0f, 0.0f,
// Connection 2 (nonexistent)
0.0f, 0.0f,
};
const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(2);
check_is_close(scaq, expect, 1.0e-7);
}
// ICAQ:3 (not activated/connected)
{
const auto expect = std::vector<int> {
// Connection 0
0, 0, 0, 0, 0, 0, 0,
// Connection 1
0, 0, 0, 0, 0, 0, 0,
// Connection 2
0, 0, 0, 0, 0, 0, 0,
};
const auto& icaq = aquiferData.getIntegerAquiferConnectionData(3);
BOOST_CHECK_EQUAL_COLLECTIONS(icaq.begin(), icaq.end(), expect.begin(), expect.end());
}
// SCAQ:3 (not activated/connected)
{
const auto expect = std::vector<float> {
// Connection 0
0.0f, 0.0f,
// Connection 1
0.0f, 0.0f,
// Connection 2
0.0f, 0.0f,
};
const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(3);
check_is_close(scaq, expect, 1.0e-7);
}
// ICAQ:4
{
const auto expect = std::vector<int> {
// Connection 0
20, 1, 5, 953, 6, 0, 0,
// Connection 1 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
// Connection 2 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
};
const auto& icaq = aquiferData.getIntegerAquiferConnectionData(4);
BOOST_CHECK_EQUAL_COLLECTIONS(icaq.begin(), icaq.end(), expect.begin(), expect.end());
}
// SCAQ:4
{
const auto expect = std::vector<float> {
// Connection 0
1.0f, 0.8f,
// Connection 1 (nonexistent)
0.0f, 0.0f,
// Connection 2 (nonexistent)
0.0f, 0.0f,
};
const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(4);
check_is_close(scaq, expect, 1.0e-7);
}
// ICAQ:5 (not activated/connected)
{
const auto expect = std::vector<int> {
// Connection 0
0, 0, 0, 0, 0, 0, 0,
// Connection 1
0, 0, 0, 0, 0, 0, 0,
// Connection 2
0, 0, 0, 0, 0, 0, 0,
};
const auto& icaq = aquiferData.getIntegerAquiferConnectionData(5);
BOOST_CHECK_EQUAL_COLLECTIONS(icaq.begin(), icaq.end(), expect.begin(), expect.end());
}
// SCAQ:5 (not activated/connected)
{
const auto expect = std::vector<float> {
// Connection 0
0.0f, 0.0f,
// Connection 1
0.0f, 0.0f,
// Connection 2
0.0f, 0.0f,
};
const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(5);
check_is_close(scaq, expect, 1.0e-7);
}
// ICAQ:6
{
const auto expect = std::vector<int> {
// Connection 0
20, 4, 6, 982, 2, 0, 0,
// Connection 1 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
// Connection 2 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
};
const auto& icaq = aquiferData.getIntegerAquiferConnectionData(6);
BOOST_CHECK_EQUAL_COLLECTIONS(icaq.begin(), icaq.end(), expect.begin(), expect.end());
}
// SCAQ:6
{
const auto expect = std::vector<float> {
// Connection 0
1.0f, 0.8f,
// Connection 1 (nonexistent)
0.0f, 0.0f,
// Connection 2 (nonexistent)
0.0f, 0.0f,
};
const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(6);
check_is_close(scaq, expect, 1.0e-7);
}
}
BOOST_AUTO_TEST_CASE(Dynamic_Information_Analytic_Aquifers)
{
const auto aqDims = syntheticAquiferDimensions();
const auto aqConfig = createAquiferConfig();
auto aquiferData = Opm::RestartIO::Helpers::
AggregateAquiferData{ aqDims, aqConfig, createGrid() };
aquiferData.captureDynamicdAquiferData(aqConfig, aquiferValues(aqConfig), sim_state(),
Opm::UnitSystem::newMETRIC());
// IAAQ
{
const auto expect = std::vector<int> {
// Aquifer 1
3, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
// Aquifer 2
1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
// Aquifer 3
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// Aquifer 4
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
// Aquifer 5
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// Aquifer 6
1, 1, 0, 0, 0, 0, 0, 0, 0, 3, 1, 1, 0, 0, 0, 0, 0, 0,
};
const auto& iaaq = aquiferData.getIntegerAquiferData();
BOOST_CHECK_EQUAL_COLLECTIONS(iaaq.begin(), iaaq.end(), expect.begin(), expect.end());
}
// SAAQ
{
const auto expect = std::vector<float> {
// Aquifer 1
3.0e-5f, 800.0f, 1000.0f, 0.3f, 269.0f, 2000.0f, 10.0f, 1.0f, // 0.. 7
994.6857f, 0.117882f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 8..15
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 16..23
// Aquifer 2
1.5312e-4f, 5.0e10f, 495.0f, 1.546666666666667e+04f, 250.0f, 2000.0f, 0.0f, 0.0f, // 0.. 7 (24..31)
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 8..15 (32..39)
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 16..23 (40..47)
// Aquifer 3
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 0.. 7 (48..55)
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 8..15 (56..63)
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 16..23 (64..71)
// Aquifer 4
1.5312e-4f, 2.0e10f, 910.0f, 3.365274725274725e+03f, 250.0f, 2000.0f, 0.0f, 0.0f, // 0.. 7 (72..79)
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 8..15 (80..87)
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 16..23 (88..95)
// Aquifer 5
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 0.. 7 ( 96..103)
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 8..15 (104..111)
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 16..23 (112..119)
// Aquifer 6
3.0e-5f, 900.0f, 5000.0f, 0.3f, 269.0f, 2000.0f, 10.0f, 1.0f, // 0.. 7 (120..127)
1013.943893f, 0.317682f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 8..15 (128..135)
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 16..23 (136..143)
};
const auto& saaq = aquiferData.getSinglePrecAquiferData();
check_is_close(saaq, expect, 1.0e-7);
}
// XAAQ
{
const auto expect = std::vector<double> {
// Aquifer 1
234.567, 123.456, 3456.789, 4.8, 12.558192939329325, 361.9008, 0.0, 0.0, 50.607, 4.567,
// Aquifer 2
222.333, 121.212, 333.444, 0.8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
// Aquifer 3
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
// Aquifer 4
333.222, 555.444, 222.111, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
// Aquifer 5
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
// Aquifer 6
34.567, 456.123, 4444.5555, 1.0, 18.409712143375852, 458.0307, 0.0, 0.0, 100.321, 50.706,
};
const auto& xaaq = aquiferData.getDoublePrecAquiferData();
check_is_close(xaaq, expect, 1.0e-7);
}
}
BOOST_AUTO_TEST_CASE(Dynamic_Information_Numeric_Aquifers)
{
const auto aqDims = syntheticNumericAquiferDimensions();
const auto aqConfig = createNumericAquiferConfig();
BOOST_REQUIRE_MESSAGE(aqConfig.hasNumericalAquifer(), "Aquifer configuration object must have numeric aquifers");
auto aquiferData = Opm::RestartIO::Helpers::
AggregateAquiferData{ aqDims, aqConfig, createGrid() };
{
BOOST_CHECK_EQUAL(aquiferData.maximumActiveAnalyticAquiferID(), 0);
BOOST_CHECK_MESSAGE(aquiferData.getIntegerAquiferData().empty(), "IAAQ must be empty");
BOOST_CHECK_MESSAGE(aquiferData.getSinglePrecAquiferData().empty(), "SAAQ must be empty");
BOOST_CHECK_MESSAGE(aquiferData.getDoublePrecAquiferData().empty(), "XAAQ must be empty");
BOOST_CHECK_EQUAL(aquiferData.getNumericAquiferIntegerData().size(), 4u * 10);
BOOST_CHECK_EQUAL(aquiferData.getNumericAquiferDoublePrecData().size(), 4u * 13);
}
aquiferData.captureDynamicdAquiferData(aqConfig,
numericAquiferValues(aqConfig),
aqunum_sim_state(),
Opm::UnitSystem::newMETRIC());
// IAQN
{
const auto expect = std::vector<int> {
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 0.. 9 (record 0)
1, 2, 1, 1, 1, 3, 0, 0, 0, 0, // 10..19 (record 1)
1, 3, 1, 1, 2, 1, 0, 0, 0, 0, // 20..29 (record 2)
2, 4, 1, 1, 2, 3, 0, 0, 0, 0, // 30..39 (record 3)
};
const auto& iaqn = aquiferData.getNumericAquiferIntegerData();
BOOST_CHECK_EQUAL_COLLECTIONS(iaqn.begin(), iaqn.end(), expect.begin(), expect.end());
}
// RAQN
{
const auto pv = std::vector<double> {
15.0e3 * 5.0e3 * 0.3,
160.0e3 * 6.0e3 * 0.4,
150.0e3 * 7.0e3 * 0.5,
140.0e3 * 9.0e3 * 0.3,
};
const auto expect = std::vector<double> {
15.0e3, 5.0e3, 0.3, 30.0, 2700.0, 285.0, 1.0, 1.0, 1.0, pv[0], 234.567, 3456.789, 123.456, // 0..12 (record 0)
160.0e3, 6.0e3, 0.4, 400.0, 2705.0, 295.0, 1.0, 1.0, 1.0, pv[1], 0.0 , 0.0 , 0.0 , // 13..25 (record 1)
150.0e3, 7.0e3, 0.5, 5000.0, 2710.0, 300.0, 1.0, 1.0, 1.0, pv[2], 0.0 , 0.0 , 0.0 , // 26..38 (record 2)
140.0e3, 9.0e3, 0.3, 300.0, 2715.0, 250.0, 1.0, 1.0, 1.0, pv[3], 222.333, 333.444, 121.212, // 39..51 (record 3)
};
const auto& raqn = aquiferData.getNumericAquiferDoublePrecData();
check_is_close(raqn, expect, 1.0e-10);
}
}
BOOST_AUTO_TEST_SUITE_END()