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/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/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 c0eec8332..35448e4b6 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 @@ -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/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& 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();