From f99ae4b7ccddea3c4e3e8a77a9885ecfe07442a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 28 Aug 2023 18:21:20 +0200 Subject: [PATCH 1/3] Make MULTREGT Processor Aware of Numerical Aquifers This commit adds a new data member MULTREGTScanner::aquifer_cells which holds a sorted sequence of Cartesian/global cell indices corresponding to the cells which comprise the model's numerical aquifers. These are needed to properly identify whether or not a connection--i.e., a cell pair--would constitute a "numerical aquifer connection" and be subject to 'NOAQUNNC' treatment. We assign the numerical aquifer cells as part of member function EclipseState::conveyNumericalAquiferEffects which runs at EclipseState construction time. We know all numerical aquifers at that point. --- .../eclipse/EclipseState/Grid/MULTREGTScanner.hpp | 4 ++++ opm/input/eclipse/EclipseState/Grid/TransMult.hpp | 1 + .../input/eclipse/EclipseState/EclipseState.cpp | 2 ++ .../eclipse/EclipseState/Grid/MULTREGTScanner.cpp | 15 +++++++++++++++ .../input/eclipse/EclipseState/Grid/TransMult.cpp | 4 ++++ 5 files changed, 26 insertions(+) diff --git a/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp b/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp index 361d99bcd..0a8a41a44 100644 --- a/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp +++ b/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp @@ -98,6 +98,8 @@ namespace Opm { bool operator==(const MULTREGTScanner& data) const; MULTREGTScanner& operator=(const MULTREGTScanner& data); + void applyNumericalAquifer(const std::vector& aquifer_cells); + double getRegionMultiplier(std::size_t globalCellIdx1, std::size_t globalCellIdx2, FaceDir::DirEnum faceDir) const; @@ -112,6 +114,7 @@ namespace Opm { serializer(m_searchMap); serializer(regions); + serializer(aquifer_cells); } private: @@ -126,6 +129,7 @@ namespace Opm { std::vector m_records{}; std::map m_searchMap{}; std::map> regions{}; + std::vector aquifer_cells{}; void addKeyword(const DeckKeyword& deckKeyword); void assertKeywordSupported(const DeckKeyword& deckKeyword); diff --git a/opm/input/eclipse/EclipseState/Grid/TransMult.hpp b/opm/input/eclipse/EclipseState/Grid/TransMult.hpp index fd4702886..6dad296be 100644 --- a/opm/input/eclipse/EclipseState/Grid/TransMult.hpp +++ b/opm/input/eclipse/EclipseState/Grid/TransMult.hpp @@ -59,6 +59,7 @@ namespace Opm { void applyMULT(const std::vector& srcMultProp, FaceDir::DirEnum faceDir); void applyMULTFLT(const FaultCollection& faults); void applyMULTFLT(const Fault& fault); + void applyNumericalAquifer(const std::vector& aquifer_cells); bool operator==(const TransMult& data) const; diff --git a/src/opm/input/eclipse/EclipseState/EclipseState.cpp b/src/opm/input/eclipse/EclipseState/EclipseState.cpp index c015548d7..ba277b04a 100644 --- a/src/opm/input/eclipse/EclipseState/EclipseState.cpp +++ b/src/opm/input/eclipse/EclipseState/EclipseState.cpp @@ -352,6 +352,8 @@ namespace Opm { // Add NNCs between aquifer cells and first aquifer cell and aquifer // connections. this->appendInputNNC(numerical_aquifer.aquiferCellNNCs()); + + this->m_transMult.applyNumericalAquifer(numerical_aquifer.allAquiferCellIds()); } void EclipseState::applyMULTXYZ() { diff --git a/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp b/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp index fb912572b..501dd7e8f 100644 --- a/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp +++ b/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp @@ -205,6 +205,7 @@ namespace Opm { std::forward_as_tuple(std::make_pair(1, 2)), std::forward_as_tuple(0)); result.regions = {{"test3", {11}}}; + result.aquifer_cells = { std::size_t{17}, std::size_t{29} }; return result; } @@ -215,6 +216,7 @@ namespace Opm { && (this->m_records == data.m_records) && (this->m_searchMap == data.m_searchMap) && (this->regions == data.regions) + && (this->aquifer_cells == data.aquifer_cells) ; } @@ -226,10 +228,23 @@ namespace Opm { this->m_records = data.m_records; this->m_searchMap = data.m_searchMap; this->regions = data.regions; + this->aquifer_cells = data.aquifer_cells; return *this; } + void MULTREGTScanner::applyNumericalAquifer(const std::vector& aquifer_cells_arg) + { + this->aquifer_cells.insert(this->aquifer_cells.end(), + aquifer_cells_arg.begin(), + aquifer_cells_arg.end()); + + std::sort(this->aquifer_cells.begin(), this->aquifer_cells.end()); + this->aquifer_cells.erase(std::unique(this->aquifer_cells.begin(), + this->aquifer_cells.end()), + this->aquifer_cells.end()); + } + // This function will check the region values in globalIndex1 and // globalIndex and see if they match the regionvalues specified in the // deck. The function checks both directions: diff --git a/src/opm/input/eclipse/EclipseState/Grid/TransMult.cpp b/src/opm/input/eclipse/EclipseState/Grid/TransMult.cpp index 7a961526f..7c9635095 100644 --- a/src/opm/input/eclipse/EclipseState/Grid/TransMult.cpp +++ b/src/opm/input/eclipse/EclipseState/Grid/TransMult.cpp @@ -156,6 +156,10 @@ namespace Opm { } } + void TransMult::applyNumericalAquifer(const std::vector& aquifer_cells) { + m_multregtScanner.applyNumericalAquifer(aquifer_cells); + } + bool TransMult::operator==(const TransMult& data) const { return this->m_nx == data.m_nx && this->m_ny == data.m_ny && From 51b1cf614defed3d34765e9b012207a2b12e15de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 28 Aug 2023 18:29:47 +0200 Subject: [PATCH 2/3] Make Region Multiplier Aware of Aquifer Connections This commit implements the 'NOAQUNNC' behaviour in member function MULTREGTScanner::getRegionMultiplier() We use the new 'aquifer_cells' data member to identify connections to and within numerical aquifers and ignore those if the record stipulates 'NOAQUNNC' behaviour. --- .../EclipseState/Grid/MULTREGTScanner.hpp | 3 ++ .../EclipseState/Grid/MULTREGTScanner.cpp | 33 ++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp b/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp index 0a8a41a44..e6af8bd4f 100644 --- a/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp +++ b/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp @@ -133,6 +133,9 @@ namespace Opm { void addKeyword(const DeckKeyword& deckKeyword); void assertKeywordSupported(const DeckKeyword& deckKeyword); + + bool isAquNNC(std::size_t globalCellIdx1, std::size_t globalCellIdx2) const; + bool isAquCell(std::size_t globalCellIdx) const; }; } // namespace Opm diff --git a/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp b/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp index 501dd7e8f..b4d1f2c43 100644 --- a/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp +++ b/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp @@ -279,15 +279,19 @@ namespace Opm { }; auto ignoreMultiplierRecord = - [is_adj = is_adjacent(this->gridDims, globalIndex1, globalIndex2)] + [is_adj = is_adjacent(this->gridDims, globalIndex1, globalIndex2), + is_aqu = this->isAquNNC(globalIndex1, globalIndex2)] (const MULTREGT::NNCBehaviourEnum nnc_behaviour) { // We ignore the record if either of the following conditions hold // // 1. Cells are adjacent, but record stipulates NNCs only // 2. Connection is an NNC, but record stipulates no NNCs - return ( is_adj && (nnc_behaviour == MULTREGT::NNCBehaviourEnum::NNC)) - || (!is_adj && (nnc_behaviour == MULTREGT::NNCBehaviourEnum::NONNC)); + // 3. Connection is associated to a numerical aquifer, but + // record stipulates that no such connections apply. + return ((is_adj && !is_aqu) && (nnc_behaviour == MULTREGT::NNCBehaviourEnum::NNC)) + || ((!is_adj || is_aqu) && (nnc_behaviour == MULTREGT::NNCBehaviourEnum::NONNC)) + || (is_aqu && (nnc_behaviour == MULTREGT::NNCBehaviourEnum::NOAQUNNC)); }; for (const auto& [regName, regMap] : this->m_searchMap) { @@ -335,15 +339,6 @@ namespace Opm { }; } } - - const auto nnc_behaviour = MULTREGT:: - NNCBehaviourFromString(deckRecord.getItem("NNC_MULT").get(0)); - - if (nnc_behaviour == MULTREGT::NNCBehaviourEnum::NOAQUNNC) { - throw std::invalid_argument { - "Sorry - currently we do not support 'NOAQUNNC' for MULTREGT." - }; - } } } @@ -390,4 +385,18 @@ namespace Opm { } } } + + bool MULTREGTScanner::isAquNNC(const std::size_t globalCellIdx1, + const std::size_t globalCellIdx2) const + { + return this->isAquCell(globalCellIdx1) + || this->isAquCell(globalCellIdx2); + } + + bool MULTREGTScanner::isAquCell(const std::size_t globalCellIdx) const + { + return std::binary_search(this->aquifer_cells.begin(), + this->aquifer_cells.end(), + globalCellIdx); + } } // namespace Opm From 07ddc1506864bbf31978c62556455e0b08d2cdc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 28 Aug 2023 18:35:57 +0200 Subject: [PATCH 3/3] Calculate Multiplier Values for Non-Neighbouring Connections This is intended as a possibly temporary measure for processing explicitly assigned NNCs (keywords NNC/EDITNNC/EDITNNCR) along with those NNCs arising from numerical aquifers, and for which there is no associate face direction. Add a set of unit tests to probe the implementation of all MULTREGT connection behaviours as exhibited by MULTREGTScanner member functions getRegionMultiplier() and getRegionMultiplierNNC(). --- .../EclipseState/Grid/MULTREGTScanner.hpp | 2 + .../eclipse/EclipseState/Grid/TransMult.hpp | 1 + .../EclipseState/Grid/MULTREGTScanner.cpp | 42 ++ .../eclipse/EclipseState/Grid/TransMult.cpp | 4 + tests/parser/MULTREGTScannerTests.cpp | 468 +++++++++++++++++- 5 files changed, 510 insertions(+), 7 deletions(-) diff --git a/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp b/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp index e6af8bd4f..9902198f8 100644 --- a/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp +++ b/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.hpp @@ -104,6 +104,8 @@ namespace Opm { std::size_t globalCellIdx2, FaceDir::DirEnum faceDir) const; + double getRegionMultiplierNNC(std::size_t globalCellIdx1, + std::size_t globalCellIdx2) const; template void serializeOp(Serializer& serializer) diff --git a/opm/input/eclipse/EclipseState/Grid/TransMult.hpp b/opm/input/eclipse/EclipseState/Grid/TransMult.hpp index 6dad296be..d60b139a7 100644 --- a/opm/input/eclipse/EclipseState/Grid/TransMult.hpp +++ b/opm/input/eclipse/EclipseState/Grid/TransMult.hpp @@ -56,6 +56,7 @@ namespace Opm { double getMultiplier(size_t globalIndex, FaceDir::DirEnum faceDir) const; double getMultiplier(size_t i , size_t j , size_t k, FaceDir::DirEnum faceDir) const; double getRegionMultiplier( size_t globalCellIndex1, size_t globalCellIndex2, FaceDir::DirEnum faceDir) const; + double getRegionMultiplierNNC(std::size_t globalCellIndex1, std::size_t globalCellIndex2) const; void applyMULT(const std::vector& srcMultProp, FaceDir::DirEnum faceDir); void applyMULTFLT(const FaultCollection& faults); void applyMULTFLT(const Fault& fault); diff --git a/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp b/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp index b4d1f2c43..78c5604e1 100644 --- a/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp +++ b/src/opm/input/eclipse/EclipseState/Grid/MULTREGTScanner.cpp @@ -324,6 +324,48 @@ namespace Opm { return 1.0; } + double MULTREGTScanner::getRegionMultiplierNNC(const std::size_t globalCellIdx1, + const std::size_t globalCellIdx2) const + { + if (this->m_searchMap.empty()) { + return 1.0; + } + + auto ignoreMultiplierRecord = + [is_aqu = this->isAquNNC(globalCellIdx1, globalCellIdx2)] + (const MULTREGT::NNCBehaviourEnum nnc_behaviour) + { + return (nnc_behaviour == MULTREGT::NNCBehaviourEnum::NONNC) + || (is_aqu && (nnc_behaviour == MULTREGT::NNCBehaviourEnum::NOAQUNNC)); + }; + + for (const auto& [regName, regMap] : this->m_searchMap) { + const auto& region_data = this->regions.at(regName); + + const auto regionId1 = region_data[globalCellIdx1]; + const auto regionId2 = region_data[globalCellIdx2]; + + auto regPairPos = regMap.find({ regionId1, regionId2 }); + if ((regionId1 != regionId2) && (regPairPos == regMap.end())) { + // 1 -> 2 not found. Try reverse direction. + regPairPos = regMap.find({ regionId2, regionId1 }); + } + + if (regPairPos == regMap.end()) { + // Neither 1->2 nor 2->1 found. Move on to next region set. + continue; + } + + const auto& record = this->m_records[regPairPos->second]; + + if (! ignoreMultiplierRecord(record.nnc_behaviour)) { + return record.trans_mult; + } + } + + return 1.0; + } + void MULTREGTScanner::assertKeywordSupported(const DeckKeyword& deckKeyword) { using Kw = ParserKeywords::MULTREGT; diff --git a/src/opm/input/eclipse/EclipseState/Grid/TransMult.cpp b/src/opm/input/eclipse/EclipseState/Grid/TransMult.cpp index 7c9635095..c0d06d687 100644 --- a/src/opm/input/eclipse/EclipseState/Grid/TransMult.cpp +++ b/src/opm/input/eclipse/EclipseState/Grid/TransMult.cpp @@ -114,6 +114,10 @@ namespace Opm { return m_multregtScanner.getRegionMultiplier(globalCellIndex1, globalCellIndex2, faceDir); } + double TransMult::getRegionMultiplierNNC(std::size_t globalCellIndex1, std::size_t globalCellIndex2) const { + return m_multregtScanner.getRegionMultiplierNNC(globalCellIndex1, globalCellIndex2); + } + bool TransMult::hasDirectionProperty(FaceDir::DirEnum faceDir) const { return m_trans.count(faceDir) == 1; } diff --git a/tests/parser/MULTREGTScannerTests.cpp b/tests/parser/MULTREGTScannerTests.cpp index c10e0ef91..dddd193db 100644 --- a/tests/parser/MULTREGTScannerTests.cpp +++ b/tests/parser/MULTREGTScannerTests.cpp @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -172,13 +173,6 @@ BOOST_AUTO_TEST_CASE(NotSupported) { Opm::EclipseGrid eg( deck ); Opm::FieldPropsManager fp(deck, Opm::Phases{true, true, true}, eg, tm); - - // Not support NOAQUNNC behaviour - std::vector keywords0; - const auto& multregtKeyword0 = deck["MULTREGT"][0]; - keywords0.push_back( &multregtKeyword0 ); - BOOST_CHECK_THROW( Opm::MULTREGTScanner scanner( grid, &fp, keywords0 ); , std::invalid_argument ); - // srcValue == targetValue - not supported std::vector keywords1; const Opm::DeckKeyword& multregtKeyword1 = deck["MULTREGT"][1]; @@ -296,3 +290,463 @@ BOOST_AUTO_TEST_CASE(MULTREGT_COPY_MULTNUM) { BOOST_CHECK_EQUAL(fdata[i], data[i]); } } + +namespace { + Opm::Deck aquNNCDeck_OneAquCell() + { + return Opm::Parser{}.parseString(R"(RUNSPEC +DIMENS + 1 6 2 / + +AQUDIMS +-- mxnaqn mxnaqc niftbl nriftb nanaqu ncamax mxnali mxaaql + 1 1 1* 1* 1* 1 1* 1* / + +GRID + +DXV + 100.0 / + +DYV + 6*100.0 / + +DZV + 2*10.0 / + +DEPTHZ + 14*2000.0 / + +PORO + 12*0.25 / + +PERMX + 12*100.0 / + +PERMZ + 12*10.0 / + +COPY + PERMX PERMY / +/ + +MULTNUM +-- J= 1 2 3 4 5 6 + 1 2 2 2 1 1 -- K=1 + 1 2 2 2 1 1 / -- K=2 + +ACTNUM +-- J= 1 2 3 4 5 6 + 1 1 1 1 0 0 -- K=1 + 1 1 1 1 0 0 / -- K=2 + +MULTREGT -- 0 + 1 2 0.1 1* 'NONNC' / +/ + +MULTREGT -- 1 + 1 2 0.2 1* 'ALL' / +/ + +MULTREGT -- 2 + 1 2 0.3 1* 'NOAQUNNC' / +/ + +MULTREGT -- 3 + 1 2 0.4 1* 'NNC' / +/ + +AQUNUM +--AQnr. I J K Area Length Poro Perm Depth Initial.Pr PVTNUM SATNUM + 1 1 5 2 100.0E+3 1000 0.25 400 2005.00 300.0 1 1 / MULTNUM=1 +/ + +AQUCON +-- Connect numerical aquifer to the reservoir +-- Id.nr I1 I2 J1 J2 K1 K2 Face Trans.mult. Trans.opt. + 1 1 1 4 4 2 2 'J+' 1.00 1* / +/ +)"); + } + + Opm::Deck aquNNCDeck_ThreeAquCells() + { + return Opm::Parser{}.parseString(R"(RUNSPEC +DIMENS + 1 10 2 / + +AQUDIMS +-- mxnaqn mxnaqc niftbl nriftb nanaqu ncamax mxnali mxaaql + 1 1 1* 1* 1* 1 1* 1* / + +GRID + +DXV + 100.0 / + +DYV + 10*100.0 / + +DZV + 2*10.0 / + +DEPTHZ + 22*2000.0 / + +PORO + 20*0.25 / + +PERMX + 20*100.0 / + +PERMZ + 20*10.0 / + +COPY + PERMX PERMY / +/ + +MULTNUM +-- J= 1 2 3 4 5 6 7 8 9 10 + 1 2 2 2 2 2 3 4 4 4 -- K=1 + 1 2 2 2 2 2 3 4 4 4 / -- K=2 + +ACTNUM +-- J= 1 2 3 4 5 6 7 8 9 10 + 1 1 1 1 0 0 0 0 0 0 -- K=1 + 1 1 1 1 0 0 0 0 0 0 / -- K=2 + +MULTREGT -- 0 + 1 2 0.5 1* 'ALL' / + 2 3 0.1 1* 'ALL' / + 3 4 0.01 1* 'ALL' / +/ + +MULTREGT -- 1 + 1 2 0.5 1* 'NONNC' / + 2 3 0.1 1* 'NONNC' / + 3 4 0.01 1* 'NONNC' / +/ + +MULTREGT -- 2 + 1 2 0.5 1* 'NOAQUNNC' / + 2 3 0.1 1* 'NOAQUNNC' / + 3 4 0.01 1* 'NOAQUNNC' / +/ + +MULTREGT -- 3 + 1 2 0.5 1* 'NNC' / + 2 3 0.1 1* 'NNC' / + 3 4 0.01 1* 'NNC' / +/ + +AQUNUM +--AQnr. I J K Area Length Poro Perm Depth Initial.Pr PVTNUM SATNUM + 1 1 6 2 100.0E+3 1000 0.25 400 2005.00 300.0 1 1 / MULTNUM=2 + 1 1 7 2 100.0E+3 1000 0.25 400 2005.00 300.0 1 1 / MULTNUM=3 + 1 1 8 2 100.0E+3 1000 0.25 400 2005.00 300.0 1 1 / MULTNUM=4 +/ + +AQUCON +-- Connect numerical aquifer to the reservoir +-- Id.nr I1 I2 J1 J2 K1 K2 Face Trans.mult. Trans.opt. + 1 1 1 4 4 2 2 'J+' 1.00 1* / +/ +)"); + } +} // Anonymous namespace + +BOOST_AUTO_TEST_CASE(AQUNNC_Handling_OneAquCell) +{ + const auto deck = aquNNCDeck_OneAquCell(); + const auto grid = Opm::EclipseGrid { deck }; + const auto fp = Opm::FieldPropsManager { + deck, Opm::Phases { true, true, true }, + grid, Opm::TableManager { deck } + }; + + auto makeScanner = [&deck, &grid, &fp, aquNum = Opm::NumericalAquifers { deck, grid, fp }] + (const std::size_t mrtID) + { + auto scanner = Opm::MULTREGTScanner { + grid, &fp, { &deck.get()[mrtID] } + }; + + scanner.applyNumericalAquifer(aquNum.allAquiferCellIds()); + + return scanner; + }; + + auto getMultRegular = [&grid] + (const Opm::MULTREGTScanner& scanner, + const std::array& c1, + const std::array& c2, + const Opm::FaceDir::DirEnum direction) + { + return scanner.getRegionMultiplier(grid.getGlobalIndex(c1[0], c1[1], c1[2]), + grid.getGlobalIndex(c2[0], c2[1], c2[2]), + direction); + }; + + auto getMultNNC = [&grid] + (const Opm::MULTREGTScanner& scanner, + const std::array& c1, + const std::array& c2) + { + return scanner.getRegionMultiplierNNC(grid.getGlobalIndex(c1[0], c1[1], c1[2]), + grid.getGlobalIndex(c2[0], c2[1], c2[2])); + }; + + // 1->2: 0.1, NONNC + { + const auto scanner = makeScanner(0); + + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.1, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.1, 1.0e-8); + + // Both cells in MULTNUM == 2 + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // Numerical aquifer + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 3, 1 }, { 0, 4, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // NNCs + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 0 }, { 0, 1, 1 }), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 1 }, { 0, 1, 0 }), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 3, 1 }, { 0, 4, 1 }), 1.0, 1.0e-8); // Numerical aquifer + } + + // 1->2: 0.2, ALL + { + const auto scanner = makeScanner(1); + + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.2, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.2, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.2, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.2, 1.0e-8); + + // Both cells in MULTNUM == 2 + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // Numerical aquifer + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 3, 1 }, { 0, 4, 1 }, Opm::FaceDir::YPlus), 0.2, 1.0e-8); + + // NNCs + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 0 }, { 0, 1, 1 }), 0.2, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 1 }, { 0, 1, 0 }), 0.2, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 3, 1 }, { 0, 4, 1 }), 0.2, 1.0e-8); // Numerical aquifer + } + + // 1->2: 0.3, NOAQUNNC + { + const auto scanner = makeScanner(2); + + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.3, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.3, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.3, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.3, 1.0e-8); + + // Both cells in MULTNUM == 2 + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // Numerical aquifer + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 3, 1 }, { 0, 4, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // NNCs + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 0 }, { 0, 1, 1 }), 0.3, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 1 }, { 0, 1, 0 }), 0.3, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 3, 1 }, { 0, 4, 1 }), 1.0, 1.0e-8); // Numerical aquifer + } + + // 1->2: 0.4, NNC + { + const auto scanner = makeScanner(3); + + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.4, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.4, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // Both cells in MULTNUM == 2 + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // Numerical aquifer + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 3, 1 }, { 0, 4, 1 }, Opm::FaceDir::YPlus), 0.4, 1.0e-8); + + // NNCs + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 0 }, { 0, 1, 1 }), 0.4, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 1 }, { 0, 1, 0 }), 0.4, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 3, 1 }, { 0, 4, 1 }), 0.4, 1.0e-8); // Numerical aquifer + } +} + +BOOST_AUTO_TEST_CASE(AQUNNC_Handling_ThreeAquCells) +{ + const auto deck = aquNNCDeck_ThreeAquCells(); + const auto grid = Opm::EclipseGrid { deck }; + const auto fp = Opm::FieldPropsManager { + deck, Opm::Phases { true, true, true }, + grid, Opm::TableManager { deck } + }; + + auto makeScanner = [&deck, &grid, &fp, aquNum = Opm::NumericalAquifers { deck, grid, fp }] + (const std::size_t mrtID) + { + auto scanner = Opm::MULTREGTScanner { + grid, &fp, { &deck.get()[mrtID] } + }; + + scanner.applyNumericalAquifer(aquNum.allAquiferCellIds()); + + return scanner; + }; + + auto getMultRegular = [&grid] + (const Opm::MULTREGTScanner& scanner, + const std::array& c1, + const std::array& c2, + const Opm::FaceDir::DirEnum direction) + { + return scanner.getRegionMultiplier(grid.getGlobalIndex(c1[0], c1[1], c1[2]), + grid.getGlobalIndex(c2[0], c2[1], c2[2]), + direction); + }; + + auto getMultNNC = [&grid] + (const Opm::MULTREGTScanner& scanner, + const std::array& c1, + const std::array& c2) + { + return scanner.getRegionMultiplierNNC(grid.getGlobalIndex(c1[0], c1[1], c1[2]), + grid.getGlobalIndex(c2[0], c2[1], c2[2])); + }; + + // 1->2: 0.5 , ALL + // 2->3: 0.1 , ALL + // 3->4: 0.01, ALL + { + const auto scanner = makeScanner(0); + + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); + + // Both cells in MULTNUM == 2 + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // Numerical aquifer + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 3, 1 }, { 0, 5, 1 }, Opm::FaceDir::YPlus), 1.0 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 5, 1 }, { 0, 6, 1 }, Opm::FaceDir::YPlus), 0.1 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 6, 1 }, { 0, 7, 1 }, Opm::FaceDir::YPlus), 0.01, 1.0e-8); + + // NNCs + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 0 }, { 0, 1, 1 }), 0.5 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 3, 1 }, { 0, 5, 1 }), 1.0 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 5, 1 }, { 0, 6, 1 }), 0.1 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 6, 1 }, { 0, 7, 1 }), 0.01, 1.0e-8); + } + + // 1->2: 0.5 , NONNC + // 2->3: 0.1 , NONNC + // 3->4: 0.01, NONNC + { + const auto scanner = makeScanner(1); + + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); + + // Both cells in MULTNUM == 2 + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // Numerical aquifer + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 3, 1 }, { 0, 5, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 5, 1 }, { 0, 6, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 6, 1 }, { 0, 7, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // NNCs + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 0 }, { 0, 1, 1 }), 1.0 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 3, 1 }, { 0, 5, 1 }), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 5, 1 }, { 0, 6, 1 }), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 6, 1 }, { 0, 7, 1 }), 1.0, 1.0e-8); + } + + // 1->2: 0.5 , NOAQUNNC + // 2->3: 0.1 , NOAQUNNC + // 3->4: 0.01, NOAQUNNC + { + const auto scanner = makeScanner(2); + + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); + + // Both cells in MULTNUM == 2 + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // Numerical aquifer + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 3, 1 }, { 0, 5, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 5, 1 }, { 0, 6, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 6, 1 }, { 0, 7, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // NNCs + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 0 }, { 0, 1, 1 }), 0.5 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 3, 1 }, { 0, 5, 1 }), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 5, 1 }, { 0, 6, 1 }), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 6, 1 }, { 0, 7, 1 }), 1.0, 1.0e-8); + } + + // 1->2: 0.5 , NNC + // 2->3: 0.1 , NNC + // 3->4: 0.01, NNC + { + const auto scanner = makeScanner(3); + + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 0 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 0 }, Opm::FaceDir::YPlus), 0.5, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 0, 1 }, { 0, 1, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // Both cells in MULTNUM == 2 + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 0 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 0 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); // NNC + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 1, 1 }, { 0, 2, 1 }, Opm::FaceDir::YPlus), 1.0, 1.0e-8); + + // Numerical aquifer + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 3, 1 }, { 0, 5, 1 }, Opm::FaceDir::YPlus), 1.0 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 5, 1 }, { 0, 6, 1 }, Opm::FaceDir::YPlus), 0.1 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultRegular(scanner, { 0, 6, 1 }, { 0, 7, 1 }, Opm::FaceDir::YPlus), 0.01, 1.0e-8); + + // NNCs + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 0, 0 }, { 0, 1, 1 }), 0.5 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 3, 1 }, { 0, 5, 1 }), 1.0 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 5, 1 }, { 0, 6, 1 }), 0.1 , 1.0e-8); + BOOST_CHECK_CLOSE(getMultNNC(scanner, { 0, 6, 1 }, { 0, 7, 1 }), 0.01, 1.0e-8); + } +}