From 5a81df350c55a6af96c1daba2ef64ea7cd863eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Wed, 19 May 2021 22:42:58 +0200 Subject: [PATCH 1/3] Identify Numeric Aquifer Restart Array Items In particular, identify most the items in the IAQN and RAQN arrays. While here, also make INTEHEAD Items for numeric aquifers public. --- opm/output/eclipse/VectorItems/aquifer.hpp | 32 +++++++++++++++++++++ opm/output/eclipse/VectorItems/intehead.hpp | 5 ++++ src/opm/output/eclipse/InteHEAD.cpp | 6 ++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/opm/output/eclipse/VectorItems/aquifer.hpp b/opm/output/eclipse/VectorItems/aquifer.hpp index c189968e8..06cd9f8a7 100644 --- a/opm/output/eclipse/VectorItems/aquifer.hpp +++ b/opm/output/eclipse/VectorItems/aquifer.hpp @@ -60,6 +60,38 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems } // Value } // IAnalyticAquiferConn + namespace INumericAquifer { + enum index : std::vector::size_type { + AquiferID = 0, // ID of numeric aquifer + Cell_I = 1, // I coordinate of aquifer cell + Cell_J = 2, // J coordinate of aquifer cell + Cell_K = 3, // K coordinate of aquifer cell + PVTTableID = 4, // PVT Table ID of numeric aquifer + SatFuncID = 5, // Saturation function ID of numeric aquifer + }; + } // INumericAquifer + + namespace RNumericAquifer { + enum index : std::vector::size_type { + Area = 0, // Aquifer inflow area, AQUNUM(5) + Length = 1, // Aquifer length, AQUNUM(6) + Porosity = 2, // Aquifer porosity, AQUNUM(7) + Permeability = 3, // Aquifer permeability, AQUNUM(8) + Depth = 4, // Aquifer depth, AQUNUM(9) + Pressure = 5, // Aquifer pressure, AQUNUM(10) + + Unknown_1 = 6, // Unknown item, = 1.0 + Unknown_2 = 7, // Unknown item, = 1.0 + Unknown_3 = 8, // Unknown item, = 1.0 + + PoreVolume = 9, // Total aquifer pore-volume (= Area * Length * Porosity) + + FlowRate = 10, // Aquifer inflow rate (ANQR:N) + ProdVolume = 11, // Total liquid volume produced from aquifer (AQNT:N) + DynPressure = 12, // Dynamic aquifer pressure (ANQP:N) + }; + } // RNumericAquifer + namespace SAnalyticAquifer { enum index : std::vector::size_type { Compressibility = 0, // Total aquifer compressibility (AQUCT(6), AQUFETP(5)) diff --git a/opm/output/eclipse/VectorItems/intehead.hpp b/opm/output/eclipse/VectorItems/intehead.hpp index 38c9271d6..ce306c576 100644 --- a/opm/output/eclipse/VectorItems/intehead.hpp +++ b/opm/output/eclipse/VectorItems/intehead.hpp @@ -141,6 +141,11 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems WSEGITR_IT2 = 208, // NR - maximum no of times that a new iteration cycle with a reduced Wp will be started + NIIAQN = 223, // Number of data elements in IAQN array pr AQUNUM record + NIRAQN = 224, // Number of data elements in RAQN array pr AQUNUM record + + NUM_AQUNUM_RECORDS = 226, // Number of AQUNUM records (lines of AQUNUM data) + MAX_ACT_COND = 245, // Maximum number of conditions pr action MAX_AN_AQUIFER_ID = 252, // Maximum aquifer ID of all analytic aquifers (<= AQUDIMS(5)) diff --git a/src/opm/output/eclipse/InteHEAD.cpp b/src/opm/output/eclipse/InteHEAD.cpp index c0eec8332..a81a92e28 100644 --- a/src/opm/output/eclipse/InteHEAD.cpp +++ b/src/opm/output/eclipse/InteHEAD.cpp @@ -271,10 +271,10 @@ enum index : std::vector::size_type { ih_220 = 220 , // 0 ih_221 = 221 , // 0 ih_222 = 222 , // 0 - NIIAQN = 223 , // 0 NIIAQN = number of lines of integer AQUNUM data. - NIRAQN = 224 , // 0 NIRAQN = number of lines of real AQUNUM data. + NIIAQN = VI::intehead::NIIAQN, // 0 NIIAQN = Number of integer data elements in IAQN array pr. numeric aquifer record in AQUNUM. + NIRAQN = VI::intehead::NIRAQN, // 0 NIRAQN = number of double precision data elements in RAQN array pr. numeric aquifer record in AQUNUM. ih_225 = 225 , // 0 - NUMAQN = 226 , // 0 NUMAQN = number of lines of AQUNUM data entered. + NUMAQN = VI::intehead::NUM_AQUNUM_RECORDS, // 0 NUMAQN = number of lines of AQUNUM data entered (#records). ih_227 = 227 , // 0 ih_228 = 228 , // 0 ih_229 = 229 , // 0 From 8c33979071084f70bbb78a844ad2982bf633dc80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Wed, 19 May 2021 23:03:45 +0200 Subject: [PATCH 2/3] Ensure Existence of ANQx Summary Vectors These are needed for restart purposes. --- src/opm/output/eclipse/Summary.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/opm/output/eclipse/Summary.cpp b/src/opm/output/eclipse/Summary.cpp index a7ace5d90..bb220e9ff 100644 --- a/src/opm/output/eclipse/Summary.cpp +++ b/src/opm/output/eclipse/Summary.cpp @@ -310,6 +310,29 @@ namespace { return entities; } + std::vector + requiredNumericAquiferVectors(const std::vector& aquiferIDs) + { + auto entities = std::vector {}; + + const auto vectors = std::vector { + { "ANQR" , Opm::EclIO::SummaryNode::Type::Rate }, + { "ANQP" , Opm::EclIO::SummaryNode::Type::Pressure }, + { "ANQT" , Opm::EclIO::SummaryNode::Type::Total }, + }; + + using Cat = Opm::EclIO::SummaryNode::Category; + + for (const auto& aquiferID : aquiferIDs) { + for (const auto& vector : vectors) { + entities.push_back({ vector.kw, Cat::Aquifer, + vector.type, "", aquiferID, {} }); + } + } + + return entities; + } + Opm::TimeStampUTC make_sim_time(const Opm::Schedule& sched, const Opm::SummaryState& st, double sim_step) { auto elapsed = st.get_elapsed() + sim_step; return Opm::TimeStampUTC( sched.getStartTime() ) + std::chrono::duration(elapsed); @@ -3609,6 +3632,13 @@ configureRequiredRestartParameters(const SummaryConfig& sumcfg, for (const auto& node : requiredAquiferVectors(aquiferIDs)) makeEvaluator(node); } + + if (aqConfig.hasNumericalAquifer()) { + const auto aquiferIDs = numericAquiferIDs(aqConfig); + + for (const auto& node : requiredNumericAquiferVectors(aquiferIDs)) + makeEvaluator(node); + } } Opm::out::Summary::SummaryImplementation::MiniStep& From 6c1c753764aff4e65f4917142310c9f9d02da53a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Wed, 19 May 2021 23:07:05 +0200 Subject: [PATCH 3/3] Add Numeric Aquifer Restart Output This commit generates the IAQN and RAQN restart vectors pertaining to numeric aquifers. The arrays are sized according to the number of records in the input AQUNUM keyword. RAQN is a mix of static and dynamic information, including the cumulative total inflow volume of water from the aquifer into the model. IAQN is exclusively static information. We add new members to 'AggregateAquiferData' and ensure that the numeric aquifer arrays remain empty when no numeric aquifers exist in the model. Add unit tests for these new arrays and update existing unit tests to account for new dimension items. --- opm/output/eclipse/AggregateAquiferData.hpp | 18 ++ opm/output/eclipse/InteHEAD.hpp | 9 + .../output/eclipse/AggregateAquiferData.cpp | 137 ++++++++++++++-- src/opm/output/eclipse/EclipseIO.cpp | 6 +- src/opm/output/eclipse/InteHEAD.cpp | 9 + src/opm/output/eclipse/RestartIO.cpp | 44 +++-- tests/test_AggregateAquiferData.cpp | 154 +++++++++++++++++- tests/test_InteHEAD.cpp | 39 ++++- 8 files changed, 381 insertions(+), 35 deletions(-) diff --git a/opm/output/eclipse/AggregateAquiferData.hpp b/opm/output/eclipse/AggregateAquiferData.hpp index 051cdbdc7..2bd1e7b7b 100644 --- a/opm/output/eclipse/AggregateAquiferData.hpp +++ b/opm/output/eclipse/AggregateAquiferData.hpp @@ -112,6 +112,18 @@ namespace Opm { namespace RestartIO { namespace Helpers { return this->doubleprecAnalyticAq_.data(); } + /// Retrieve Integer Aquifer Data Array for Numeric Aquifers. + const std::vector& getNumericAquiferIntegerData() const + { + return this->integerNumericAq_.data(); + } + + /// Retrieve Double Precision Aquifer Data Array for Numeric Aquifers. + const std::vector& getNumericAquiferDoublePrecData() const + { + return this->doubleprecNumericAq_.data(); + } + /// Retrieve Integer Aquifer Connection Data Array (analytic aquifers) /// /// \param[in] aquiferID Aquifer for which to retrieve integer @@ -160,6 +172,12 @@ namespace Opm { namespace RestartIO { namespace Helpers { /// Aggregate 'XAAQ' array (Double Precision) for all analytic aquifers. WindowedArray doubleprecAnalyticAq_; + /// Aggregate 'IAQN' array (integer) for all numeric aquifers. + WindowedArray integerNumericAq_; + + /// Aggregate 'RAQN' array (Double Precision) for all numeric aquifers. + WindowedArray doubleprecNumericAq_; + /// Aggregate ICAQ array (Integer) for all analytic aquifer /// connections. Separate array for each aquifer. std::vector> integerAnalyticAquiferConn_; diff --git a/opm/output/eclipse/InteHEAD.hpp b/opm/output/eclipse/InteHEAD.hpp index 4fe7f35c3..4a276c27b 100755 --- a/opm/output/eclipse/InteHEAD.hpp +++ b/opm/output/eclipse/InteHEAD.hpp @@ -154,6 +154,9 @@ namespace Opm { namespace RestartIO { // Maximum aquifer ID across all of the model's analytic aquifers. int maxAquiferID {0}; + // Number of numeric aquifer records (lines of AQUNUM data) + int numNumericAquiferRecords {0}; + // Number of data elements per aquifer in IAAQ array. int numIntAquiferElem {18}; @@ -163,6 +166,12 @@ namespace Opm { namespace RestartIO { // Number of data elements per aquifer in XAAQ array. int numDoubAquiferElem {10}; + // Number of data elements in IAQN array per numeric aquifer record. + int numNumericAquiferIntElem {10}; + + // Number of data elements in RAQN array per numeric aquifer record. + int numNumericAquiferDoubleElem {13}; + // Number of data elements per coonnection in ICAQ array. int numIntConnElem {7}; diff --git a/src/opm/output/eclipse/AggregateAquiferData.cpp b/src/opm/output/eclipse/AggregateAquiferData.cpp index c81e612c5..543e5e003 100644 --- a/src/opm/output/eclipse/AggregateAquiferData.cpp +++ b/src/opm/output/eclipse/AggregateAquiferData.cpp @@ -51,6 +51,15 @@ namespace VI = Opm::RestartIO::Helpers::VectorItems; namespace { + double getSummaryVariable(const Opm::SummaryState& summaryState, + const std::string& variable, + const int aquiferID) + { + const auto key = fmt::format("{}:{}", variable, aquiferID); + + return summaryState.get(key, 0.0); + } + template void CarterTracyAquiferLoop(const Opm::AquiferConfig& aqConfig, AquiferCallBack&& aquiferOp) @@ -69,6 +78,24 @@ namespace { } } + template + void numericAquiferLoop(const Opm::AquiferConfig& aqConfig, + AquiferCallBack&& aquiferOp) + { + if (! aqConfig.hasNumericalAquifer()) { + return; + } + + for (const auto& [aquiferID, aquifer] : aqConfig.numericalAquifers().aquifers()) { + const auto numCells = aquifer.numCells(); + + for (auto cellIndex = 0*numCells; cellIndex < numCells; ++cellIndex) { + const auto* aqCell = aquifer.getCellPrt(cellIndex); + aquiferOp(static_cast(aquiferID), cellIndex == 0*numCells, *aqCell); + } + } + } + template void analyticAquiferConnectionLoop(const Opm::AquiferConfig& aqConfig, ConnectionCallBack&& connectionOp) @@ -139,6 +166,37 @@ namespace { } // Fetckovich } // IntegerAnalyticAquifer + namespace IntegerNumericAquifer + { + Opm::RestartIO::Helpers::WindowedArray + allocate(const Opm::RestartIO::InteHEAD::AquiferDims& aqDims) + { + using WA = Opm::RestartIO::Helpers::WindowedArray; + + return WA { + WA::NumWindows{ static_cast(aqDims.numNumericAquiferRecords) }, + WA::WindowSize{ static_cast(aqDims.numNumericAquiferIntElem) } + }; + } + + template + void staticContrib(const Opm::NumericalAquiferCell& aqCell, + const int aquiferID, + IAqnArray& iaqn) + { + using Ix = VI::INumericAquifer::index; + + iaqn[Ix::AquiferID] = aquiferID; + + iaqn[Ix::Cell_I] = static_cast(aqCell.I) + 1; + iaqn[Ix::Cell_J] = static_cast(aqCell.J) + 1; + iaqn[Ix::Cell_K] = static_cast(aqCell.K) + 1; + + iaqn[Ix::PVTTableID] = aqCell.pvttable; + iaqn[Ix::SatFuncID] = aqCell.sattable; + } + } // IntegerNumericAquifer + namespace IntegerAnalyticAquiferConn { std::vector> @@ -311,15 +369,6 @@ namespace { return usys.from_si(M::length, usys.from_si(M::length, tot_influx)); } - double getSummaryVariable(const Opm::SummaryState& summaryState, - const std::string& variable, - const int aquiferID) - { - const auto key = fmt::format("{}:{}", variable, aquiferID); - - return summaryState.get(key, 0.0); - } - Opm::RestartIO::Helpers::WindowedArray allocate(const Opm::RestartIO::InteHEAD::AquiferDims& aqDims) { @@ -395,6 +444,50 @@ namespace { } // Fetkovich } // DoublePrecAnalyticAquifer + namespace DoublePrecNumericAquifer + { + Opm::RestartIO::Helpers::WindowedArray + allocate(const Opm::RestartIO::InteHEAD::AquiferDims& aqDims) + { + using WA = Opm::RestartIO::Helpers::WindowedArray; + + return WA { + WA::NumWindows{ static_cast(aqDims.numNumericAquiferRecords) }, + WA::WindowSize{ static_cast(aqDims.numNumericAquiferDoubleElem) } + }; + } + + template + void dynamicContrib(const Opm::NumericalAquiferCell& aqCell, + SummaryVariable&& summaryVariable, + const Opm::UnitSystem& usys, + RAqnArray& raqn) + { + using M = Opm::UnitSystem::measure; + using Ix = VI::RNumericAquifer::index; + + raqn[Ix::Area] = usys.from_si(M::length, usys.from_si(M::length, aqCell.area)); + raqn[Ix::Length] = usys.from_si(M::length, aqCell.length); + raqn[Ix::Porosity] = aqCell.porosity; + raqn[Ix::Permeability] = usys.from_si(M::permeability, aqCell.permeability); + raqn[Ix::Depth] = usys.from_si(M::length, aqCell.depth); + + if (aqCell.init_pressure.has_value()) { + raqn[Ix::Pressure] = usys.from_si(M::pressure, aqCell.init_pressure.value()); + } + + raqn[Ix::Unknown_1] = 1.0; // Unknown item. 1.0 in all cases so far. + raqn[Ix::Unknown_2] = 1.0; // Unknown item. 1.0 in all cases so far. + raqn[Ix::Unknown_3] = 1.0; // Unknown item. 1.0 in all cases so far. + + raqn[Ix::PoreVolume] = usys.from_si(M::volume, aqCell.poreVolume()); + + raqn[Ix::FlowRate] = summaryVariable("ANQR"); + raqn[Ix::ProdVolume] = summaryVariable("ANQT"); + raqn[Ix::DynPressure] = summaryVariable("ANQP"); + } + } // IntegerNumericAquifer + namespace DoublePrecAnalyticAquiferConn { std::vector> @@ -420,10 +513,16 @@ AggregateAquiferData(const InteHEAD::AquiferDims& aqDims, , integerAnalyticAq_ { IntegerAnalyticAquifer:: allocate(aqDims) } , singleprecAnalyticAq_ { SinglePrecAnalyticAquifer:: allocate(aqDims) } , doubleprecAnalyticAq_ { DoublePrecAnalyticAquifer:: allocate(aqDims) } + , integerNumericAq_ { IntegerNumericAquifer:: allocate(aqDims) } + , doubleprecNumericAq_ { DoublePrecNumericAquifer:: allocate(aqDims) } , integerAnalyticAquiferConn_ { IntegerAnalyticAquiferConn:: allocate(aqDims) } , singleprecAnalyticAquiferConn_{ SinglePrecAnalyticAquiferConn::allocate(aqDims) } , doubleprecAnalyticAquiferConn_{ DoublePrecAnalyticAquiferConn::allocate(aqDims) } { + if (! aqConfig.hasAnalyticalAquifer()) { + return; + } + const auto map = buildColumnarActiveIndexMappingTables(grid); // Aquifer connections do not change in SCHEDULE. Leverage that @@ -472,8 +571,7 @@ captureDynamicdAquiferData(const AquiferConfig& aqConfig, const auto tot_influx = this->totalInflux_[aquIndex]; auto sumVar = [&summaryState, &aquifer](const std::string& vector) { - return DoublePrecAnalyticAquifer:: - getSummaryVariable(summaryState, vector, aquifer.aquiferID); + return getSummaryVariable(summaryState, vector, aquifer.aquiferID); }; DoublePrecAnalyticAquifer::Fetkovich::dynamicContrib(sumVar, tot_influx, usys, xaaq); }); @@ -498,10 +596,23 @@ captureDynamicdAquiferData(const AquiferConfig& aqConfig, const auto tot_influx = this->totalInflux_[aquIndex]; auto sumVar = [&summaryState, &aquifer](const std::string& vector) { - return DoublePrecAnalyticAquifer:: - getSummaryVariable(summaryState, vector, aquifer.aquiferID); + return getSummaryVariable(summaryState, vector, aquifer.aquiferID); }; DoublePrecAnalyticAquifer::CarterTracy::dynamicContrib(sumVar, aquifer, pvtw, tot_influx, usys, xaaq); }); + + numericAquiferLoop(aqConfig, [this, &summaryState, &usys] + (const int aquiferID, const bool isNewID, const NumericalAquiferCell& aqCell) + { + auto iaqn = this->integerNumericAq_[aqCell.record_id]; + IntegerNumericAquifer::staticContrib(aqCell, aquiferID, iaqn); + + auto raqn = this->doubleprecNumericAq_[aqCell.record_id]; + auto sumVar = [&summaryState, aquiferID, isNewID](const std::string& vector) + { + return !isNewID ? 0.0 : getSummaryVariable(summaryState, vector, aquiferID); + }; + DoublePrecNumericAquifer::dynamicContrib(aqCell, sumVar, usys, raqn); + }); } diff --git a/src/opm/output/eclipse/EclipseIO.cpp b/src/opm/output/eclipse/EclipseIO.cpp index 9749a366c..288ee966a 100644 --- a/src/opm/output/eclipse/EclipseIO.cpp +++ b/src/opm/output/eclipse/EclipseIO.cpp @@ -131,10 +131,12 @@ EclipseIO::Impl::Impl( const EclipseState& eclipseState, , summary( eclipseState, summaryConfig, grid , schedule ) , output_enabled( eclipseState.getIOConfig().getOutputEnabled() ) { - if (this->es.aquifer().hasAnalyticalAquifer()) { + const auto& aqConfig = this->es.aquifer(); + + if (aqConfig.hasAnalyticalAquifer() || aqConfig.hasNumericalAquifer()) { this->aquiferData = RestartIO::Helpers::AggregateAquiferData { RestartIO::inferAquiferDimensions(this->es), - this->es.aquifer(), + aqConfig, this->grid }; } diff --git a/src/opm/output/eclipse/InteHEAD.cpp b/src/opm/output/eclipse/InteHEAD.cpp index a81a92e28..35448e4b6 100644 --- a/src/opm/output/eclipse/InteHEAD.cpp +++ b/src/opm/output/eclipse/InteHEAD.cpp @@ -615,6 +615,11 @@ aquiferDimensions(const AquiferDims& aqdims) this -> data_[NACAQZ] = aqdims.numDoubConnElem; this -> data_[NGCAUS] = aqdims.maxNumActiveAquiferConn; + + this -> data_[NIIAQN] = aqdims.numNumericAquiferIntElem; + this -> data_[NIRAQN] = aqdims.numNumericAquiferDoubleElem; + this -> data_[NUMAQN] = aqdims.numNumericAquiferRecords; + this -> data_[MAAQID] = aqdims.maxAquiferID; // Not characterised. Equal to NAQUIF in all cases seen this far. @@ -897,5 +902,9 @@ Opm::RestartIO::inferAquiferDimensions(const EclipseState& es) dim.maxAquiferID = getMaximumAnalyticAquiferID(cfg); } + if (cfg.hasNumericalAquifer()) { + dim.numNumericAquiferRecords = cfg.numericalAquifers().numRecords(); + } + return dim; } diff --git a/src/opm/output/eclipse/RestartIO.cpp b/src/opm/output/eclipse/RestartIO.cpp index 251c4d556..975889825 100644 --- a/src/opm/output/eclipse/RestartIO.cpp +++ b/src/opm/output/eclipse/RestartIO.cpp @@ -424,21 +424,14 @@ namespace { rstFile.write("XCON", connectionData.getXConn()); } - void updateAndWriteAquiferData(const AquiferConfig& aqConfig, - const SummaryState& summaryState, - const TableManager& tables, - const UnitSystem& usys, - Helpers::AggregateAquiferData& aquiferData, - EclIO::OutputStream::Restart& rstFile) + void writeAnalyticAquiferData(const Helpers::AggregateAquiferData& aquiferData, + EclIO::OutputStream::Restart& rstFile) { - aquiferData.captureDynamicdAquiferData(aqConfig, summaryState, - tables.getPvtwTable(), - tables.getDensityTable(), usys); - rstFile.write("IAAQ", aquiferData.getIntegerAquiferData()); rstFile.write("SAAQ", aquiferData.getSinglePrecAquiferData()); rstFile.write("XAAQ", aquiferData.getDoublePrecAquiferData()); + // Aquifer IDs in 1..maxID inclusive. const auto maxAquiferID = aquiferData.maximumActiveAnalyticAquiferID(); for (auto aquiferID = 1 + 0*maxAquiferID; aquiferID <= maxAquiferID; ++aquiferID) { const auto xCAQnum = std::vector{ aquiferID }; @@ -454,6 +447,33 @@ namespace { } } + void writeNumericAquiferData(const Helpers::AggregateAquiferData& aquiferData, + EclIO::OutputStream::Restart& rstFile) + { + rstFile.write("IAQN", aquiferData.getNumericAquiferIntegerData()); + rstFile.write("RAQN", aquiferData.getNumericAquiferDoublePrecData()); + } + + void updateAndWriteAquiferData(const AquiferConfig& aqConfig, + const SummaryState& summaryState, + const TableManager& tables, + const UnitSystem& usys, + Helpers::AggregateAquiferData& aquiferData, + EclIO::OutputStream::Restart& rstFile) + { + aquiferData.captureDynamicdAquiferData(aqConfig, summaryState, + tables.getPvtwTable(), + tables.getDensityTable(), usys); + + if (aqConfig.hasAnalyticalAquifer()) { + writeAnalyticAquiferData(aquiferData, rstFile); + } + + if (aqConfig.hasNumericalAquifer()) { + writeNumericAquiferData(aquiferData, rstFile); + } + } + void writeDynamicData(const int sim_step, const bool ecl_compatible_rst, const Phases& phases, @@ -497,7 +517,9 @@ namespace { wells, wellSol, action_state, sumState, inteHD, rstFile); } - if (es.aquifer().hasAnalyticalAquifer() && aquiferData.has_value()) { + if ((es.aquifer().hasAnalyticalAquifer() || es.aquifer().hasNumericalAquifer()) && + aquiferData.has_value()) + { updateAndWriteAquiferData(es.aquifer(), sumState, es.getTableManager(), units, aquiferData.value(), rstFile); } diff --git a/tests/test_AggregateAquiferData.cpp b/tests/test_AggregateAquiferData.cpp index 8ab5cf167..a762df668 100644 --- a/tests/test_AggregateAquiferData.cpp +++ b/tests/test_AggregateAquiferData.cpp @@ -361,6 +361,58 @@ namespace { }; } + 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 }; @@ -390,6 +442,15 @@ namespace { 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 @@ -398,7 +459,7 @@ DIMENS AQUDIMS -- MXNAQN MXNAQC NIFTBL NRIFTB NANAQU NNCAMAX - 1* 1* 5 100 5 1000 / + 4 4 5 100 5 1000 / GRID @@ -421,6 +482,19 @@ 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 @@ -470,6 +544,21 @@ AQUANCON 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; + } + template void check_is_close(const Coll1& coll1, const Coll2& coll2, const double tol) { @@ -497,6 +586,7 @@ BOOST_AUTO_TEST_CASE(AquiferDimensions) 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 @@ -507,6 +597,10 @@ BOOST_AUTO_TEST_CASE(AquiferDimensions) 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) @@ -813,4 +907,62 @@ BOOST_AUTO_TEST_CASE(Dynamic_Information_Analytic_Aquifers) } } +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, aqunum_sim_state(), + pvtw(), density(), + Opm::UnitSystem::newMETRIC()); + + // IAQN + { + const auto expect = std::vector { + 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 { + 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 { + 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, 0.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() diff --git a/tests/test_InteHEAD.cpp b/tests/test_InteHEAD.cpp index 8e156b7cd..fb6359d31 100644 --- a/tests/test_InteHEAD.cpp +++ b/tests/test_InteHEAD.cpp @@ -316,7 +316,20 @@ BOOST_AUTO_TEST_CASE(Analytic_Aquifer_Parameters) { // https://oeis.org/A001622 const auto aqudims = Opm::RestartIO::InteHEAD::AquiferDims { - 1, 61, 803, 3988, 74989, 484820, 45868, 3436, 563, 81, 1177203 + 1, // numAquifers + 61, // maxNumAquifers + 803, // maxNumAquiferConn + 3988, // maxNumActiveAquiferConn + 74989, // maxAquiferID + 484820, // numNumericAquiferRecords + 45868, // numIntAquiferElem + 3436, // numRealAquiferElem + 563, // numDoubAquiferElem + 81, // numNumericAquiferIntElem + 17, // numNumericAquiferDoubleElem + 720, // numIntConnElem + 30, // numRealConnElem + 9, // numDoubConnElem }; const auto ih = Opm::RestartIO::InteHEAD{} @@ -326,17 +339,21 @@ BOOST_AUTO_TEST_CASE(Analytic_Aquifer_Parameters) BOOST_CHECK_EQUAL(v[VI::intehead::NAQUIF], 1); BOOST_CHECK_EQUAL(v[VI::intehead::NCAMAX], 803); - BOOST_CHECK_EQUAL(v[VI::intehead::NIAAQZ], 484820); - BOOST_CHECK_EQUAL(v[VI::intehead::NSAAQZ], 45868); - BOOST_CHECK_EQUAL(v[VI::intehead::NXAAQZ], 3436); - BOOST_CHECK_EQUAL(v[VI::intehead::NICAQZ], 563); - BOOST_CHECK_EQUAL(v[VI::intehead::NSCAQZ], 81); - BOOST_CHECK_EQUAL(v[VI::intehead::NACAQZ], 1177203); + BOOST_CHECK_EQUAL(v[VI::intehead::NIAAQZ], 45868); + BOOST_CHECK_EQUAL(v[VI::intehead::NSAAQZ], 3436); + BOOST_CHECK_EQUAL(v[VI::intehead::NXAAQZ], 563); + BOOST_CHECK_EQUAL(v[VI::intehead::NICAQZ], 720); + BOOST_CHECK_EQUAL(v[VI::intehead::NSCAQZ], 30); + BOOST_CHECK_EQUAL(v[VI::intehead::NACAQZ], 9); BOOST_CHECK_EQUAL(v[VI::intehead::MAX_ACT_ANLYTIC_AQUCONN], 3988); BOOST_CHECK_EQUAL(v[VI::intehead::MAX_AN_AQUIFER_ID], 74989); BOOST_CHECK_EQUAL(v[VI::intehead::AQU_UNKNOWN_1], 1); BOOST_CHECK_EQUAL(v[VI::intehead::MAX_ANALYTIC_AQUIFERS], 61); + + BOOST_CHECK_EQUAL(v[VI::intehead::NIIAQN], 81); + BOOST_CHECK_EQUAL(v[VI::intehead::NIRAQN], 17); + BOOST_CHECK_EQUAL(v[VI::intehead::NUM_AQUNUM_RECORDS], 484820); } BOOST_AUTO_TEST_CASE(Time_and_report_step) @@ -564,6 +581,9 @@ BOOST_AUTO_TEST_CASE(TestHeader) { const auto nicaqz = 111111; const auto nscaqz = 1111111; const auto nacaqz = 11111111; + const auto niiaqn = 22222222; + const auto niraqn = 2222222; + const auto numaqn = 222222; const auto tstep = 78; const auto report_step = 12; const auto newtmx = 17; @@ -586,7 +606,10 @@ BOOST_AUTO_TEST_CASE(TestHeader) { const auto ngroup = 8; const auto aqudims = Opm::RestartIO::InteHEAD::AquiferDims { - 1, 61, ncamax, 3988, 74989, niaaqz, nsaaqz, nxaaqz, nicaqz, nscaqz, nacaqz + 1, 61, ncamax, 3988, 74989, + numaqn, niaaqz, nsaaqz, nxaaqz, + niiaqn, niraqn, + nicaqz, nscaqz, nacaqz }; auto unit_system = Opm::UnitSystem::newMETRIC();