diff --git a/opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp b/opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp index d7e9cc462..2eabc4ddd 100644 --- a/opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp +++ b/opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp @@ -35,12 +35,13 @@ namespace Opm::RestartIO { } // Opm::RestartIO namespace Opm { - struct SingleAquiferFlux { + struct SingleAquiferFlux + { + SingleAquiferFlux() = default; explicit SingleAquiferFlux(const DeckRecord& record); // using id to create an inactive dummy aquifer explicit SingleAquiferFlux(int id); - SingleAquiferFlux() = default; SingleAquiferFlux(int id, double flux, double sal, bool active_, double temp, double pres); int id {0}; @@ -52,7 +53,7 @@ namespace Opm { bool operator==(const SingleAquiferFlux& other) const; - template + template void serializeOp(Serializer& serializer) { serializer(this->id); @@ -66,12 +67,16 @@ namespace Opm { static SingleAquiferFlux serializationTestObject(); }; - class AquiferFlux { + class AquiferFlux + { public: using AquFluxs = std::unordered_map; - explicit AquiferFlux(const std::vector& keywords); AquiferFlux() = default; + explicit AquiferFlux(const std::vector& keywords); + + // Primarily for unit testing purposes. + explicit AquiferFlux(const std::vector& aquifers); void appendAqufluxSchedule(const std::unordered_set& ids); @@ -86,15 +91,16 @@ namespace Opm { void loadFromRestart(const RestartIO::RstAquifer& rst); - template - void serializeOp(Serializer& serializer) { + template + void serializeOp(Serializer& serializer) + { serializer(this->m_aquifers); } static AquiferFlux serializationTestObject(); private: - AquFluxs m_aquifers; + AquFluxs m_aquifers{}; }; } // end of namespace Opm diff --git a/opm/input/eclipse/Schedule/ScheduleState.hpp b/opm/input/eclipse/Schedule/ScheduleState.hpp index 6b23d3d96..e70925559 100644 --- a/opm/input/eclipse/Schedule/ScheduleState.hpp +++ b/opm/input/eclipse/Schedule/ScheduleState.hpp @@ -349,6 +349,11 @@ namespace Opm { bool has_gpmaint() const; + bool hasAnalyticalAquifers() const + { + return ! this->aqufluxs.empty(); + } + /*********************************************************************/ ptr_member gconsale; diff --git a/opm/output/eclipse/AggregateAquiferData.hpp b/opm/output/eclipse/AggregateAquiferData.hpp index dc6129c39..f695dacb8 100644 --- a/opm/output/eclipse/AggregateAquiferData.hpp +++ b/opm/output/eclipse/AggregateAquiferData.hpp @@ -30,6 +30,7 @@ namespace Opm { class AquiferConfig; class EclipseGrid; + class ScheduleState; class SummaryState; class UnitSystem; } // Opm @@ -46,8 +47,9 @@ namespace Opm { namespace RestartIO { namespace Helpers { /// aquifer (or connection) in the various output arrays. /// /// \param[in] aqConfig Aquifer configuration object. Keeps track - /// of aquifer types (Carter-Tracy vs. Fetkovich) and provides - /// read-only access to the individual aquifer objects. + /// of aquifer types (Carter-Tracy, Fetkovich, constant flux &c) + /// and provides read-only access to the individual aquifer + /// objects. /// /// \param[in] grid Simulation grid. Needed to map active to /// Cartesian cell indices and to extract (I,J,K) index tuples of @@ -59,9 +61,18 @@ namespace Opm { namespace RestartIO { namespace Helpers { /// Linearise dynamic information pertinent to analytic aquifers /// into internal arrays. /// + /// \param[in] aqDims Aquifer dimensions including number of active + /// aquifers, maximum aquifer IDs, and number of data items per + /// aquifer (or connection) in the various output arrays. + /// /// \param[in] aqConfig Aquifer configuration object. Keeps track - /// of aquifer types (Carter-Tracy vs. Fetkovich) and provides - /// read-only access to the individual aquifer objects. + /// of aquifer types (Carter-Tracy, Fetkovich, constant flux &c) + /// and provides read-only access to the individual aquifer + /// objects. + /// + /// \param[in] sched Schedule state at particular report step. + /// Keeps track of dynamically defined analytic aquifers, e.g., + /// the constant flux aquifers entered in the SCHEDULE section. /// /// \param[in] aquData Dynamic aquifer data, including time /// constants, water mass densities, water viscosities, and @@ -74,10 +85,12 @@ namespace Opm { namespace RestartIO { namespace Helpers { /// /// \param[in] usys Unit system. Needed to convert quantities from /// internal to output units. - void captureDynamicdAquiferData(const AquiferConfig& aqConfig, - const data::Aquifers& aquData, - const SummaryState& summaryState, - const UnitSystem& usys); + void captureDynamicAquiferData(const InteHEAD::AquiferDims& aqDims, + const AquiferConfig& aqConfig, + const ScheduleState& sched, + const data::Aquifers& aquData, + const SummaryState& summaryState, + const UnitSystem& usys); /// Retrieve the maximum active aquifer ID over all analytic /// aquifers. @@ -184,6 +197,28 @@ namespace Opm { namespace RestartIO { namespace Helpers { /// Aggregate ACAQ array (Double Precision) for all analytic aquifer /// connections. Separate array for each aquifer. std::vector> doubleprecAnalyticAquiferConn_; + + void allocateDynamicBackingStorage(const InteHEAD::AquiferDims& aqDims); + + void handleCarterTracy(const AquiferConfig& aqConfig, + const data::Aquifers& aquData, + const SummaryState& summaryState, + const UnitSystem& usys); + + void handleConstantFlux(const AquiferConfig& aqConfig, + const ScheduleState& sched, + const SummaryState& summaryState, + const UnitSystem& usys); + + void handleFetkovich(const AquiferConfig& aqConfig, + const data::Aquifers& aquData, + const SummaryState& summaryState, + const UnitSystem& usys); + + void handleNumeric(const AquiferConfig& aqConfig, + const data::Aquifers& aquData, + const SummaryState& summaryState, + const UnitSystem& usys); }; }}} // Opm::RestartIO::Helpers diff --git a/opm/output/eclipse/InteHEAD.hpp b/opm/output/eclipse/InteHEAD.hpp index 60009ed63..197bc9276 100644 --- a/opm/output/eclipse/InteHEAD.hpp +++ b/opm/output/eclipse/InteHEAD.hpp @@ -30,8 +30,10 @@ namespace Opm { class EclipseGrid; class EclipseState; -class UnitSystem; class Phases; +class ScheduleState; +class UnitSystem; + } namespace Opm { namespace RestartIO { @@ -164,7 +166,7 @@ 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) + // Number of numeric aquifer records (lines of AQUNUM data, AQUDIMS(1)) int numNumericAquiferRecords {0}; // Number of data elements per aquifer in IAAQ array. @@ -250,6 +252,10 @@ namespace Opm { namespace RestartIO { InteHEAD::AquiferDims inferAquiferDimensions(const EclipseState& es); + + InteHEAD::AquiferDims + inferAquiferDimensions(const EclipseState& es, + const ScheduleState& sched); }} // Opm::RestartIO #endif // OPM_INTEHEAD_HEADER_INCLUDED diff --git a/opm/output/eclipse/VectorItems/aquifer.hpp b/opm/output/eclipse/VectorItems/aquifer.hpp index 06cd9f8a7..659b47e47 100644 --- a/opm/output/eclipse/VectorItems/aquifer.hpp +++ b/opm/output/eclipse/VectorItems/aquifer.hpp @@ -31,7 +31,7 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems WatPropTable = 1, // PVT number (ACUCT(10) or AQUFETP(7)) CTInfluenceFunction = 9, // AQUCT(11) - TypeRelated1 = 10, // =1 for CT, =0 for FETP + TypeRelated1 = 10, // =0 for FETP, =1 for CT, =2 for FLUX Unknown_1 = 11, // Unknown item. =1 in all cases seen thus far. }; @@ -40,6 +40,7 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems enum ModelType : int { Fetkovich = 0, CarterTracy = 1, + ConstantFlux = 2, }; } // Value } // IAnalyticAquifer @@ -96,6 +97,8 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems enum index : std::vector::size_type { Compressibility = 0, // Total aquifer compressibility (AQUCT(6), AQUFETP(5)) + ConstFluxValue = 0, // Constant flux aquifer's flux value (AQUFLUX(2)) + FetInitVol = 1, // Initial aquifer volume (AQUFETP(4)) FetProdIndex = 2, // Aquifer productivity index (AQUFETP(6)) FetTimeConstant = 3, // Fetkovich Aquifer time constant (Compressibility * InitVol / ProdIndex) diff --git a/src/opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.cpp b/src/opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.cpp index f5ba7075d..461be9362 100644 --- a/src/opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.cpp +++ b/src/opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.cpp @@ -88,6 +88,13 @@ namespace Opm { } } + AquiferFlux::AquiferFlux(const std::vector& aquifers) + { + for (const auto& aquifer : aquifers) { + this->m_aquifers.emplace(aquifer.id, aquifer); + } + } + bool AquiferFlux::operator==(const AquiferFlux& other) const { return this->m_aquifers == other.m_aquifers; } @@ -118,4 +125,4 @@ namespace Opm { result.m_aquifers.insert({single_aquifer.id, single_aquifer}); return result; } -} // end of namespace Opm \ No newline at end of file +} // end of namespace Opm diff --git a/src/opm/output/eclipse/AggregateAquiferData.cpp b/src/opm/output/eclipse/AggregateAquiferData.cpp index 3c9d796a6..5caa6c95b 100644 --- a/src/opm/output/eclipse/AggregateAquiferData.cpp +++ b/src/opm/output/eclipse/AggregateAquiferData.cpp @@ -26,15 +26,16 @@ #include #include -#include #include +#include +#include #include #include - -#include - #include +#include +#include + #include #include @@ -60,6 +61,11 @@ namespace { return summaryState.get(key, 0.0); } + std::size_t maxNumberOfAquifers(const Opm::RestartIO::InteHEAD::AquiferDims& aqDims) + { + return std::max(aqDims.maxNumAquifers, aqDims.numNumericAquiferRecords); + } + template void CarterTracyAquiferLoop(const Opm::AquiferConfig& aqConfig, AquiferCallBack&& aquiferOp) @@ -69,6 +75,27 @@ namespace { } } + template + void ConstantFluxAquiferLoop(const Opm::AquiferConfig& aqConfig, + const Opm::ScheduleState& sched, + AquiferCallBack&& aquiferOp) + { + // Note: Loop 'aqConfig' before 'sched'. That way, dynamic settings + // in 'sched' will override the static settings in 'aqConfig'. + + for (const auto& aquifer : aqConfig.aquflux()) { + if (aquifer.second.active) { + aquiferOp(aquifer.second); + } + } + + for (const auto& aquifer : sched.aqufluxs) { + if (aquifer.second.active) { + aquiferOp(aquifer.second); + } + } + } + template void FetkovichAquiferLoop(const Opm::AquiferConfig& aqConfig, AquiferCallBack&& aquiferOp) @@ -101,10 +128,6 @@ namespace { ConnectionCallBack&& connectionOp) { for (const auto& [aquiferID, connections] : aqConfig.connections().data()) { - if ( !aqConfig.hasAnalyticalAquifer(int(aquiferID)) ) { - continue; - } - const auto tot_influx = std::accumulate(connections.begin(), connections.end(), 0.0, [](const double t, const Opm::Aquancon::AquancCell& connection) -> double @@ -133,6 +156,19 @@ namespace { }; } + namespace Common + { + template + void staticContrib(const int numActiveConn, + IAaqArray& iaaq) + { + using Ix = VI::IAnalyticAquifer::index; + + iaaq[Ix::NumAquiferConn] = numActiveConn; + iaaq[Ix::Unknown_1] = 1; // Not characterised; =1 in all cases seen thus far. + } + } // Common + namespace CarterTracy { template @@ -142,16 +178,28 @@ namespace { { using Ix = VI::IAnalyticAquifer::index; - iaaq[Ix::NumAquiferConn] = numActiveConn; + Common::staticContrib(numActiveConn, iaaq); + iaaq[Ix::WatPropTable] = aquifer.pvttableID; // One-based (=AQUCT(10)) iaaq[Ix::CTInfluenceFunction] = aquifer.inftableID; iaaq[Ix::TypeRelated1] = VI::IAnalyticAquifer::Value::ModelType::CarterTracy; - - iaaq[Ix::Unknown_1] = 1; // Not characterised; =1 in all cases seen thus far. } } // CarterTracy + namespace ConstantFlux + { + template + void staticContrib(const int numActiveConn, IAaqArray& iaaq) + { + namespace IAAQ = VI::IAnalyticAquifer; + + Common::staticContrib(numActiveConn, iaaq); + + iaaq[IAAQ::index::TypeRelated1] = IAAQ::Value::ModelType::ConstantFlux; + } + } // ConstantFlux + namespace Fetkovich { template @@ -161,11 +209,10 @@ namespace { { using Ix = VI::IAnalyticAquifer::index; - iaaq[Ix::NumAquiferConn] = numActiveConn; - iaaq[Ix::WatPropTable] = aquifer.pvttableID; // One-based (=AQUFETP(7)) + Common::staticContrib(numActiveConn, iaaq); + iaaq[Ix::WatPropTable] = aquifer.pvttableID; // One-based (=AQUFETP(7)) iaaq[Ix::TypeRelated1] = VI::IAnalyticAquifer::Value::ModelType::Fetkovich; - iaaq[Ix::Unknown_1] = 1; // Not characterised; =1 in all cases seen thus far. } } // Fetkovich } // IntegerAnalyticAquifer @@ -208,7 +255,7 @@ namespace { { using WA = Opm::RestartIO::Helpers::WindowedArray; - return std::vector(aqDims.maxAquiferID, WA { + return std::vector(maxNumberOfAquifers(aqDims), WA { WA::NumWindows{ static_cast(aqDims.maxNumActiveAquiferConn) }, WA::WindowSize{ static_cast(aqDims.numIntConnElem) } }); @@ -307,6 +354,23 @@ namespace { } } // CarterTracy + namespace ConstantFlux { + template + void staticContrib(const Opm::SingleAquiferFlux& aquifer, + const Opm::UnitSystem& usys, + SAaqArray& saaq) + { + using M = Opm::UnitSystem::measure; + using Ix = VI::SAnalyticAquifer::index; + + auto q = usys.from_si(M::liquid_surface_rate, aquifer.flux); + + // Unit hack: *to_si()* here since we don't have an area unit. + saaq[Ix::ConstFluxValue] = + usys.to_si(M::length, usys.to_si(M::length, q)); + } + } // ConstantFlux + namespace Fetkovich { template void staticContrib(const Opm::Aquifetp::AQUFETP_data& aquifer, @@ -351,7 +415,7 @@ namespace { { using WA = Opm::RestartIO::Helpers::WindowedArray; - return std::vector(aqDims.maxAquiferID, WA { + return std::vector(maxNumberOfAquifers(aqDims), WA { WA::NumWindows{ static_cast(aqDims.maxNumActiveAquiferConn) }, WA::WindowSize{ static_cast(aqDims.numRealConnElem) } }); @@ -442,6 +506,18 @@ namespace { } } // CarterTracy + namespace ConstantFlux { + template + void dynamicContrib(SummaryVariable&& summaryVariable, + const double tot_influx, + const Opm::UnitSystem& usys, + XAaqArray& xaaq) + { + Common::dynamicContrib(std::forward(summaryVariable), + tot_influx, usys, xaaq); + } + } // ConstantFlux + namespace Fetkovich { template void dynamicContrib(SummaryVariable&& summaryVariable, @@ -515,7 +591,7 @@ namespace { { using WA = Opm::RestartIO::Helpers::WindowedArray; - return std::vector(aqDims.maxAquiferID, WA { + return std::vector(maxNumberOfAquifers(aqDims), WA { WA::NumWindows{ static_cast(aqDims.maxNumActiveAquiferConn) }, WA::WindowSize{ static_cast(aqDims.numDoubConnElem) } }); @@ -528,8 +604,8 @@ AggregateAquiferData(const InteHEAD::AquiferDims& aqDims, const AquiferConfig& aqConfig, const EclipseGrid& grid) : maxActiveAnalyticAquiferID_ { aqDims.maxAquiferID } - , numActiveConn_ ( aqDims.maxAquiferID, 0 ) - , totalInflux_ ( aqDims.maxAquiferID, 0.0 ) + , numActiveConn_ ( maxNumberOfAquifers(aqDims), 0 ) + , totalInflux_ ( maxNumberOfAquifers(aqDims), 0.0 ) , integerAnalyticAq_ { IntegerAnalyticAquifer:: allocate(aqDims) } , singleprecAnalyticAq_ { SinglePrecAnalyticAquifer:: allocate(aqDims) } , doubleprecAnalyticAq_ { DoublePrecAnalyticAquifer:: allocate(aqDims) } @@ -539,7 +615,7 @@ AggregateAquiferData(const InteHEAD::AquiferDims& aqDims, , singleprecAnalyticAquiferConn_{ SinglePrecAnalyticAquiferConn::allocate(aqDims) } , doubleprecAnalyticAquiferConn_{ DoublePrecAnalyticAquiferConn::allocate(aqDims) } { - if (! aqConfig.hasAnalyticalAquifer()) { + if (! aqConfig.connections().active()) { return; } @@ -569,39 +645,43 @@ AggregateAquiferData(const InteHEAD::AquiferDims& aqDims, void Opm::RestartIO::Helpers::AggregateAquiferData:: -captureDynamicdAquiferData(const AquiferConfig& aqConfig, - const data::Aquifers& aquData, - const SummaryState& summaryState, - const UnitSystem& usys) +captureDynamicAquiferData(const InteHEAD::AquiferDims& aqDims, + const AquiferConfig& aqConfig, + const ScheduleState& sched, + const data::Aquifers& aquData, + const SummaryState& summaryState, + const UnitSystem& usys) { - FetkovichAquiferLoop(aqConfig, [this, &summaryState, &aquData, &usys] - (const Aquifetp::AQUFETP_data& aquifer) - { - const auto aquIndex = static_cast::Idx>(aquifer.aquiferID - 1); + if (! (aqConfig.active() || sched.hasAnalyticalAquifers())) { + return; + } - auto iaaq = this->integerAnalyticAq_[aquIndex]; - const auto nActiveConn = this->numActiveConn_[aquIndex]; - IntegerAnalyticAquifer::Fetkovich::staticContrib(aquifer, nActiveConn, iaaq); + this->allocateDynamicBackingStorage(aqDims); - auto xaqPos = aquData.find(aquifer.aquiferID); - if ((xaqPos != aquData.end()) && - xaqPos->second.typeData.is()) - { - const auto& aquFetPData = xaqPos->second; + this->maxActiveAnalyticAquiferID_ = aqDims.maxAquiferID; - auto saaq = this->singleprecAnalyticAq_[aquIndex]; - SinglePrecAnalyticAquifer::Fetkovich::staticContrib(aquifer, aquFetPData, usys, saaq); - } + this->handleFetkovich(aqConfig, aquData, summaryState, usys); + this->handleCarterTracy(aqConfig, aquData, summaryState, usys); + this->handleConstantFlux(aqConfig, sched, summaryState, usys); + this->handleNumeric(aqConfig, aquData, summaryState, usys); +} - auto xaaq = this->doubleprecAnalyticAq_[aquIndex]; - const auto tot_influx = this->totalInflux_[aquIndex]; - auto sumVar = [&summaryState, &aquifer](const std::string& vector) - { - return getSummaryVariable(summaryState, vector, aquifer.aquiferID); - }; - DoublePrecAnalyticAquifer::Fetkovich::dynamicContrib(sumVar, tot_influx, usys, xaaq); - }); +void +Opm::RestartIO::Helpers::AggregateAquiferData:: +allocateDynamicBackingStorage(const InteHEAD::AquiferDims& aqDims) +{ + this->integerAnalyticAq_ = IntegerAnalyticAquifer:: allocate(aqDims); + this->singleprecAnalyticAq_ = SinglePrecAnalyticAquifer::allocate(aqDims); + this->doubleprecAnalyticAq_ = DoublePrecAnalyticAquifer::allocate(aqDims); +} +void +Opm::RestartIO::Helpers::AggregateAquiferData:: +handleCarterTracy(const AquiferConfig& aqConfig, + const data::Aquifers& aquData, + const SummaryState& summaryState, + const UnitSystem& usys) +{ CarterTracyAquiferLoop(aqConfig, [this, &summaryState, &aquData, &usys] (const AquiferCT::AQUCT_data& aquifer) { @@ -628,15 +708,91 @@ captureDynamicdAquiferData(const AquiferConfig& aqConfig, return getSummaryVariable(summaryState, vector, aquifer.aquiferID); }; - DoublePrecAnalyticAquifer::CarterTracy::dynamicContrib(sumVar, aquCTData, - tot_influx, usys, xaaq); + DoublePrecAnalyticAquifer::CarterTracy:: + dynamicContrib(sumVar, aquCTData, tot_influx, usys, xaaq); } }); +} - // TODO: we should make the AQUFLUX aquifer here +void +Opm::RestartIO::Helpers::AggregateAquiferData:: +handleConstantFlux(const AquiferConfig& aqConfig, + const ScheduleState& sched, + const SummaryState& summaryState, + const UnitSystem& usys) +{ + ConstantFluxAquiferLoop(aqConfig, sched, [this, &usys, &summaryState] + (const SingleAquiferFlux& aquifer) + { + const auto aquIndex = static_cast::Idx>(aquifer.id - 1); + auto iaaq = this->integerAnalyticAq_[aquIndex]; + const auto nActiveConn = this->numActiveConn_[aquIndex]; + IntegerAnalyticAquifer::ConstantFlux::staticContrib(nActiveConn, iaaq); + + auto saaq = this->singleprecAnalyticAq_[aquIndex]; + SinglePrecAnalyticAquifer::ConstantFlux::staticContrib(aquifer, usys, saaq); + + auto xaaq = this->doubleprecAnalyticAq_[aquIndex]; + const auto tot_influx = this->totalInflux_[aquIndex]; + auto sumVar = [&summaryState, &aquifer](const std::string& vector) + { + return getSummaryVariable(summaryState, vector, aquifer.id); + }; + DoublePrecAnalyticAquifer::ConstantFlux:: + dynamicContrib(sumVar, tot_influx, usys, xaaq); + }); +} + +void +Opm::RestartIO::Helpers::AggregateAquiferData:: +handleFetkovich(const AquiferConfig& aqConfig, + const data::Aquifers& aquData, + const SummaryState& summaryState, + const UnitSystem& usys) +{ + FetkovichAquiferLoop(aqConfig, [this, &summaryState, &aquData, &usys] + (const Aquifetp::AQUFETP_data& aquifer) + { + const auto aquIndex = static_cast::Idx>(aquifer.aquiferID - 1); + + auto iaaq = this->integerAnalyticAq_[aquIndex]; + const auto nActiveConn = this->numActiveConn_[aquIndex]; + IntegerAnalyticAquifer::Fetkovich::staticContrib(aquifer, nActiveConn, iaaq); + + auto xaqPos = aquData.find(aquifer.aquiferID); + if ((xaqPos != aquData.end()) && + xaqPos->second.typeData.is()) + { + const auto& aquFetPData = xaqPos->second; + + auto saaq = this->singleprecAnalyticAq_[aquIndex]; + SinglePrecAnalyticAquifer::Fetkovich:: + staticContrib(aquifer, aquFetPData, usys, saaq); + } + + auto xaaq = this->doubleprecAnalyticAq_[aquIndex]; + const auto tot_influx = this->totalInflux_[aquIndex]; + auto sumVar = [&summaryState, &aquifer](const std::string& vector) + { + return getSummaryVariable(summaryState, vector, aquifer.aquiferID); + }; + DoublePrecAnalyticAquifer::Fetkovich:: + dynamicContrib(sumVar, tot_influx, usys, xaaq); + }); +} + +void +Opm::RestartIO::Helpers::AggregateAquiferData:: +handleNumeric(const AquiferConfig& aqConfig, + const data::Aquifers& aquData, + const SummaryState& summaryState, + const UnitSystem& usys) +{ numericAquiferLoop(aqConfig, [this, &summaryState, &aquData, &usys] - (const int aquiferID, const std::size_t cellIndex, const NumericalAquiferCell& aqCell) + (const int aquiferID, + const std::size_t cellIndex, + const NumericalAquiferCell& aqCell) { auto iaqn = this->integerNumericAq_[aqCell.record_id]; IntegerNumericAquifer::staticContrib(aqCell, aquiferID, iaqn); diff --git a/src/opm/output/eclipse/CreateInteHead.cpp b/src/opm/output/eclipse/CreateInteHead.cpp index 06a979ab6..219587612 100644 --- a/src/opm/output/eclipse/CreateInteHead.cpp +++ b/src/opm/output/eclipse/CreateInteHead.cpp @@ -601,7 +601,7 @@ createInteHead(const EclipseState& es, .params_NWELZ (155 + num_water_tracer, 122 + 2*num_water_tracer, 130 + nxwelz_tracer_shift, 3) // n{isxz}welz: number of data elements per well in {ISXZ}WELL .params_NCON (25, 41, 58 + 5*num_water_tracer) // n{isx}conz: number of data elements per completion in ICON .params_GRPZ (getNGRPZ(nwgmax, ngmax, num_water_tracer, rspec)) - .aquiferDimensions (inferAquiferDimensions(es)) + .aquiferDimensions (inferAquiferDimensions(es, sched[lookup_step])) .stepParam (num_solver_steps, report_step) .tuningParam (getTuningPars(sched[lookup_step].tuning())) .liftOptParam (getLiftOptPar(sched, report_step, lookup_step)) diff --git a/src/opm/output/eclipse/EclipseIO.cpp b/src/opm/output/eclipse/EclipseIO.cpp index f82f496b5..5dec689bf 100644 --- a/src/opm/output/eclipse/EclipseIO.cpp +++ b/src/opm/output/eclipse/EclipseIO.cpp @@ -149,14 +149,12 @@ EclipseIO::Impl::Impl( const EclipseState& eclipseState, , summary( eclipseState, summaryConfig, grid , schedule, base_name, writeEsmry ) , output_enabled( eclipseState.getIOConfig().getOutputEnabled() ) { - const auto& aqConfig = this->es.aquifer(); - - if (aqConfig.hasAnalyticalAquifer() || aqConfig.hasNumericalAquifer()) { - this->aquiferData = RestartIO::Helpers::AggregateAquiferData { - RestartIO::inferAquiferDimensions(this->es), - aqConfig, - this->grid - }; + if (const auto& aqConfig = this->es.aquifer(); + aqConfig.connections().active() || aqConfig.hasNumericalAquifer()) + { + this->aquiferData + .emplace(RestartIO::inferAquiferDimensions(this->es), + aqConfig, this->grid); } } diff --git a/src/opm/output/eclipse/InteHEAD.cpp b/src/opm/output/eclipse/InteHEAD.cpp index f12ccabc6..28ffe3c31 100644 --- a/src/opm/output/eclipse/InteHEAD.cpp +++ b/src/opm/output/eclipse/InteHEAD.cpp @@ -22,19 +22,24 @@ #include -#include #include -#include +#include +#include #include -#include +#include #include +#include + +#include + #include #include #include #include #include +#include #include #include #include @@ -881,11 +886,66 @@ Opm::RestartIO::getSimulationTimePoint(const std::time_t start, } namespace { + template + int numUnique(std::vector elems) + { + if (elems.empty()) { return 0; } + if (elems.size() == 1) { return 1; } + + std::sort(elems.begin(), elems.end()); + auto end = std::unique(elems.begin(), elems.end()); + + return std::distance(elems.begin(), end); + } + + int numberOfCarterTracyAquifers(const Opm::AquiferConfig& cfg) + { + return cfg.ct().size(); + } + + int numberOfFetkovichAquifers(const Opm::AquiferConfig& cfg) + { + return cfg.fetp().size(); + } + + int numberOfConstantFluxAquifers(const Opm::AquiferConfig& cfg) + { + return cfg.aquflux().size(); + } + + int numberOfConstantFluxAquifers(const Opm::AquiferConfig& cfg, + const Opm::ScheduleState& sched) + { + auto aquiferIDs = std::vector{}; + aquiferIDs.reserve(numberOfConstantFluxAquifers(cfg) + + sched.aqufluxs.size()); + + for (const auto& [id, aq] : cfg.aquflux()) { + if (aq.active) { + aquiferIDs.push_back(id); + } + } + + for (const auto& aq : sched.aqufluxs) { + aquiferIDs.push_back(aq.first); + } + + return numUnique(std::move(aquiferIDs)); + } + int getNumberOfAnalyticAquifers(const Opm::AquiferConfig& cfg) { - const auto numAnalyticAquifers = cfg.ct().size() + cfg.fetp().size() + cfg.aquflux().size(); + return numberOfCarterTracyAquifers(cfg) + + numberOfFetkovichAquifers(cfg) + + numberOfConstantFluxAquifers(cfg); + } - return static_cast(numAnalyticAquifers); + int getNumberOfAnalyticAquifers(const Opm::AquiferConfig& cfg, + const Opm::ScheduleState& sched) + { + return numberOfCarterTracyAquifers(cfg) + + numberOfFetkovichAquifers(cfg) + + numberOfConstantFluxAquifers(cfg, sched); } int getMaximumNumberOfAnalyticAquifers(const Opm::Runspec& runspec) @@ -910,14 +970,47 @@ namespace { return maxNumActiveConn; } + template + int maxAquID(const AquiferCollection& aquiferCollection) + { + return std::accumulate(aquiferCollection.begin(), aquiferCollection.end(), 0, + [](const int maxID, const auto& aquiferData) + { + return std::max(maxID, aquiferData.aquiferID); + }); + } + + template + int maxAquID(AquFluxIter begin, AquFluxIter end) + { + auto maxIDPos = + std::max_element(begin, end, + [](const auto& aq1, const auto& aq2) + { + return aq1.first < aq2.first; + }); + + return (maxIDPos == end) ? 0 : maxIDPos->first; + } + int getMaximumAnalyticAquiferID(const Opm::AquiferConfig& cfg) { - const auto& aquifer_ids = analyticAquiferIDs(cfg); - if (!aquifer_ids.empty()) { - return *max_element(std::begin(aquifer_ids), std::end(aquifer_ids)); - } else { - return 0; - } + return std::max({ + maxAquID(cfg.ct()), + maxAquID(cfg.fetp()), + maxAquID(cfg.aquflux().begin(), cfg.aquflux().end()) + }); + } + + int getMaximumAnalyticAquiferID(const int maxAquiferID, + const Opm::ScheduleState& sched) + { + return std::max(maxAquiferID, maxAquID(sched.aqufluxs.begin(), sched.aqufluxs.end())); + } + + bool hasAnalyticalAquifer(const Opm::ScheduleState& sched) + { + return ! sched.aqufluxs.empty(); } } @@ -946,3 +1039,19 @@ Opm::RestartIO::inferAquiferDimensions(const EclipseState& es) return dim; } + +Opm::RestartIO::InteHEAD::AquiferDims +Opm::RestartIO::inferAquiferDimensions(const EclipseState& es, + const ScheduleState& sched) +{ + auto dim = inferAquiferDimensions(es); + + if (const auto& cfg = es.aquifer(); + cfg.hasAnalyticalAquifer() || hasAnalyticalAquifer(sched)) + { + dim.numAquifers = getNumberOfAnalyticAquifers(cfg, sched); + dim.maxAquiferID = getMaximumAnalyticAquiferID(dim.maxAquiferID, sched); + } + + return dim; +} diff --git a/src/opm/output/eclipse/RestartIO.cpp b/src/opm/output/eclipse/RestartIO.cpp index bcc5828a4..b86d100db 100644 --- a/src/opm/output/eclipse/RestartIO.cpp +++ b/src/opm/output/eclipse/RestartIO.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include @@ -43,16 +42,19 @@ #include #include -#include -#include #include +#include +#include +#include + #include #include #include +#include +#include #include #include #include -#include #include @@ -463,16 +465,24 @@ namespace { rstFile.write("RAQN", aquiferData.getNumericAquiferDoublePrecData()); } - void updateAndWriteAquiferData(const AquiferConfig& aqConfig, + void updateAndWriteAquiferData(const EclipseState& es, + const ScheduleState& sched, const data::Aquifers& aquData, const SummaryState& summaryState, const UnitSystem& usys, Helpers::AggregateAquiferData& aquiferData, EclIO::OutputStream::Restart& rstFile) { - aquiferData.captureDynamicdAquiferData(aqConfig, aquData, summaryState, usys); + const auto& aqConfig = es.aquifer(); - if (aqConfig.hasAnalyticalAquifer()) { + aquiferData.captureDynamicAquiferData(inferAquiferDimensions(es, sched), + aqConfig, + sched, + aquData, + summaryState, + usys); + + if (aqConfig.hasAnalyticalAquifer() || sched.hasAnalyticalAquifers()) { writeAnalyticAquiferData(aquiferData, rstFile); } @@ -506,9 +516,9 @@ namespace { } // Write well and MSW data only when applicable (i.e., when present) - const auto& wells = schedule.wellNames(sim_step); - - if (! wells.empty()) { + if (const auto& wells = schedule.wellNames(sim_step); + ! wells.empty()) + { const auto haveMSW = std::any_of(std::begin(wells), std::end(wells), [&schedule, sim_step](const std::string& well) @@ -525,11 +535,16 @@ namespace { wells, wellSol, action_state, wtest_state, sumState, inteHD, rstFile); } - if ((es.aquifer().hasAnalyticalAquifer() || es.aquifer().hasNumericalAquifer()) && - aquiferData.has_value()) + if (const auto& aqCfg = es.aquifer(); + aqCfg.active() && aquiferData.has_value()) { - updateAndWriteAquiferData(es.aquifer(), aquDynData, sumState, - schedule.getUnits(), aquiferData.value(), rstFile); + updateAndWriteAquiferData(es, + schedule[sim_step], + aquDynData, + sumState, + schedule.getUnits(), + aquiferData.value(), + rstFile); } } diff --git a/tests/test_AggregateAquiferData.cpp b/tests/test_AggregateAquiferData.cpp index b3e4e9c33..b99842fe0 100644 --- a/tests/test_AggregateAquiferData.cpp +++ b/tests/test_AggregateAquiferData.cpp @@ -31,15 +31,17 @@ #include #include -#include -#include #include +#include +#include #include #include #include -#include #include +#include +#include + #include #include @@ -50,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -100,6 +103,15 @@ namespace { return Opm::UnitSystem::newMETRIC().to_si(M::pressure, 1.0); } + double fluxUnit() + { + using M = Opm::UnitSystem::measure; + const auto usys = Opm::UnitSystem::newMETRIC(); + + auto q = usys.to_si(M::liquid_surface_rate, 1.0); + return usys.from_si(M::length, usys.from_si(M::length, q)); + } + double compressibilityUnit() { using M = Opm::UnitSystem::measure; @@ -205,7 +217,7 @@ AQUTAB 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 effFaceArea = 4.0 * 0.2; // DY * DZ const auto influxCoeff = effFaceArea; aquancon.addConnection(aquiferID, cartCell1, 1.0*influxCoeff, 6.0*effFaceArea, FDir::XMinus); @@ -289,7 +301,8 @@ AQUTAB const auto effFaceArea = 4.0 * 0.2; // DY * DZ const auto influxCoeff = effFaceArea; - aquancon.addConnection(aquiferID, cartCell, influxCoeff, effFaceArea, FDir::YPlus); + aquancon.addConnection(aquiferID, cartCell, influxCoeff, + effFaceArea, FDir::YPlus); } { @@ -298,7 +311,8 @@ AQUTAB const auto effFaceArea = 4.0 * 0.2; // DY * DZ const auto influxCoeff = 1.0; // [m^2] - aquancon.addConnection(aquiferID, cartCell, influxCoeff, effFaceArea, FDir::ZPlus); + aquancon.addConnection(aquiferID, cartCell, influxCoeff, + effFaceArea, FDir::ZPlus); } } @@ -319,7 +333,8 @@ AQUTAB auto& fetp = properties .emplace_back(aquiferID, pvtTable, prodIndex, compr, - initialVolume, datumDepth, initialPressure, initialTemperature); + initialVolume, datumDepth, + initialPressure, initialTemperature); fetp.finishInitialisation(waterProperties()); } @@ -332,7 +347,8 @@ AQUTAB auto& fetp = properties .emplace_back(aquiferID, pvtTable, prodIndex, compr, - initialVolume, datumDepth, initialPressure, initialTemperature); + initialVolume, datumDepth, + initialPressure, initialTemperature); fetp.finishInitialisation(waterProperties()); } @@ -340,10 +356,47 @@ AQUTAB return Opm::Aquifetp(properties); } - Opm::AquiferFlux createAquiferFluxs() { - // TODO: just for compilation for now, will complete it - Opm::AquiferFlux aquifers; - return aquifers; + void connectConstantFluxAquifers(AquiferConnections& aquancon) + { + using FDir = Opm::FaceDir::DirEnum; + + { + const auto aquiferID = 3; + const auto cartCell1 = std::size_t{680}; // one-based IJK = (1,5, 7) + const auto cartCell2 = std::size_t{780}; // one-based IJK = (1,5, 8) + const auto cartCell3 = std::size_t{880}; // one-based IJK = (1,5, 9) + const auto cartCell4 = std::size_t{980}; // one-based IJK = (1,5,10) + const auto effFaceArea = 5.0 * 0.2; // DX * DZ + const auto influxCoeff = effFaceArea; + + aquancon.addConnection(aquiferID, cartCell1, 1.0*influxCoeff, 6.0*effFaceArea, FDir::YPlus); + aquancon.addConnection(aquiferID, cartCell2, 2.0*influxCoeff, 12.0*effFaceArea, FDir::YPlus); + aquancon.addConnection(aquiferID, cartCell3, 3.0*influxCoeff, 18.0*effFaceArea, FDir::YPlus); + aquancon.addConnection(aquiferID, cartCell4, 4.0*influxCoeff, 24.0*effFaceArea, FDir::YPlus); + } + + { + const auto aquiferID = 5; + const auto cartCell = std::size_t{490}; // one-based IJK = (11,5,5) + const auto effFaceArea = 5.0 * 0.2; // DX * DZ + const auto influxCoeff = 1.0; // [m^2] + + aquancon.addConnection(aquiferID, cartCell, influxCoeff, effFaceArea, FDir::XPlus); + } + } + + Opm::AquiferFlux createConstantFluxAquifers() + { + const auto uQ = fluxUnit(); + const auto uP = pressureUnit(); + const auto salt = 0.0; + const auto temp = 300.0*temperatureUnit(); + const auto active = true; + + return Opm::AquiferFlux { std::vector { + Opm::SingleAquiferFlux { 3, 12.34*uQ, salt, active, temp, 234.5*uP }, + Opm::SingleAquiferFlux { 5, 43.21*uQ, salt, active, temp, 345.6*uP }, + }}; } Opm::AquiferConfig createAquiferConfig() @@ -351,9 +404,28 @@ AQUTAB auto aquancon = AquiferConnections{}; connectCarterTracy(aquancon); connectFetkovic(aquancon); + connectConstantFluxAquifers(aquancon); return { - createFetkovich(), createCarterTracy(), createAquiferFluxs(), Opm::Aquancon(aquancon.getAllConnections()) + createFetkovich(), + createCarterTracy(), + createConstantFluxAquifers(), + Opm::Aquancon { aquancon.getAllConnections() } + }; + } + + Opm::AquiferConfig createEmptyAquiferConfig() + { + auto aquancon = AquiferConnections{}; + connectCarterTracy(aquancon); + connectFetkovic(aquancon); + connectConstantFluxAquifers(aquancon); + + return { + Opm::Aquifetp{}, + Opm::AquiferCT{}, + Opm::AquiferFlux{}, + Opm::Aquancon { aquancon.getAllConnections() } }; } @@ -429,15 +501,41 @@ END { auto aqDims = Opm::RestartIO::InteHEAD::AquiferDims{}; - aqDims.numAquifers = 4; // 1, 2, 4, 6 + aqDims.numAquifers = 6; // 1, 2, 3, 4, 5, 6 aqDims.maxNumAquifers = 10; // >= 6 - aqDims.maxNumAquiferConn = 5; // >= 3 - aqDims.maxNumActiveAquiferConn = 3; // ID = 1 + aqDims.maxNumAquiferConn = 5; // >= 4 + aqDims.maxNumActiveAquiferConn = 4; // ID = 3 aqDims.maxAquiferID = 6; return aqDims; } + Opm::RestartIO::InteHEAD::AquiferDims syntheticDynamicAquiferDimensionsSOLUTION() + { + auto aqDims = Opm::RestartIO::InteHEAD::AquiferDims{}; + + aqDims.numAquifers = 0; + aqDims.maxNumAquifers = 10; // >= 5 + aqDims.maxNumAquiferConn = 5; // >= 4 + aqDims.maxNumActiveAquiferConn = 4; // ID = 3 + aqDims.maxAquiferID = 0; + + return aqDims; + } + + Opm::RestartIO::InteHEAD::AquiferDims syntheticDynamicAquiferDimensionsSCHEDULE() + { + auto aqDims = Opm::RestartIO::InteHEAD::AquiferDims{}; + + aqDims.numAquifers = 2; // 3, 5 + aqDims.maxNumAquifers = 10; // >= 5 + aqDims.maxNumAquiferConn = 5; // >= 4 + aqDims.maxNumActiveAquiferConn = 4; // ID = 3 + aqDims.maxAquiferID = 5; + + return aqDims; + } + Opm::RestartIO::InteHEAD::AquiferDims syntheticNumericAquiferDimensions() { auto aqDims = Opm::RestartIO::InteHEAD::AquiferDims{}; @@ -527,10 +625,18 @@ AQUANCON state.update("AAQR:2", 222.333); state.update("AAQT:2", 333.444); + state.update("AAQP:3", 212.121); + state.update("AAQR:3", 333.222); + state.update("AAQT:3", 444.333); + state.update("AAQP:4", 555.444); state.update("AAQR:4", 333.222); state.update("AAQT:4", 222.111); + state.update("AAQP:5", 444.555); + state.update("AAQR:5", 323.232); + state.update("AAQT:5", 232.131); + state.update("AAQP:6", 456.123); state.update("AAQR:6", 34.567); state.update("AAQT:6", 4444.5555); @@ -620,17 +726,38 @@ AQUANCON return aquiferValues; } + Opm::ScheduleState emptyScheduleState() + { + return {}; + } + + Opm::ScheduleState dynamicAquFluxState() + { + auto sched = emptyScheduleState(); + const auto cfg = createAquiferConfig(); + + sched.aqufluxs.insert(cfg.aquflux().begin(), + cfg.aquflux().end()); + + return sched; + } + template - void check_is_close(const Coll1& coll1, const Coll2& coll2, const double tol) + void check_is_close(const Coll1& coll1, + const Coll2& coll2, + const double tol, + std::string_view context) { BOOST_REQUIRE_EQUAL(std::size(coll1), std::size(coll2)); if (coll1.empty()) { return; } + auto i = 0; auto c1 = std::begin(coll1); auto e1 = std::end (coll1); auto c2 = std::begin(coll2); - for (; c1 != e1; ++c1, ++c2) { + for (; c1 != e1; ++c1, ++c2, ++i) { + BOOST_TEST_MESSAGE(context << '[' << i << ']'); BOOST_CHECK_CLOSE(*c1, *c2, tol); } } @@ -683,6 +810,8 @@ BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers) 20, 5, 8, 994, 3, 0, 0, // Connection 2 20, 5, 9, 995, 5, 0, 0, + // Connection 3 (nonexistent) + 0, 0, 0, 0, 0, 0, 0, }; const auto& icaq = aquiferData.getIntegerAquiferConnectionData(1); @@ -699,11 +828,13 @@ BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers) 1.0f/3.0f, 2.0f, // Connection 2 1.0f/2.0f, 3.0f, + // Connection 3 (nonexistent) + 0.0f, 0.0f, }; const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(1); - check_is_close(scaq, expect, 1.0e-7); + check_is_close(scaq, expect, 1.0e-7, "SCAQ:1"); } // ICAQ:2 @@ -715,6 +846,8 @@ BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers) 0, 0, 0, 0, 0, 0, 0, // Connection 2 (nonexistent) 0, 0, 0, 0, 0, 0, 0, + // Connection 3 (nonexistent) + 0, 0, 0, 0, 0, 0, 0, }; const auto& icaq = aquiferData.getIntegerAquiferConnectionData(2); @@ -731,22 +864,26 @@ BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers) 0.0f, 0.0f, // Connection 2 (nonexistent) 0.0f, 0.0f, + // Connection 3 (nonexistent) + 0.0f, 0.0f, }; const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(2); - check_is_close(scaq, expect, 1.0e-7); + check_is_close(scaq, expect, 1.0e-7, "SCAQ:2"); } - // ICAQ:3 (not activated/connected) + // ICAQ:3 { const auto expect = std::vector { // Connection 0 - 0, 0, 0, 0, 0, 0, 0, + 1, 5, 7, 47, 4, 0, 0, // Connection 1 - 0, 0, 0, 0, 0, 0, 0, + 1, 5, 8, 48, 4, 0, 0, // Connection 2 - 0, 0, 0, 0, 0, 0, 0, + 1, 5, 9, 49, 4, 0, 0, + // Connection 3 + 1, 5, 10, 50, 4, 0, 0, }; const auto& icaq = aquiferData.getIntegerAquiferConnectionData(3); @@ -754,20 +891,22 @@ BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers) BOOST_CHECK_EQUAL_COLLECTIONS(icaq.begin(), icaq.end(), expect.begin(), expect.end()); } - // SCAQ:3 (not activated/connected) + // SCAQ:3 { const auto expect = std::vector { // Connection 0 - 0.0f, 0.0f, + 1.0f/10.0f, 3.0f/5.0f, // Connection 1 - 0.0f, 0.0f, + 1.0f/ 5.0f, 6.0f/5.0f, // Connection 2 - 0.0f, 0.0f, + 3.0f/10.0f, 9.0f/5.0f, + // Connection 3 + 2.0f/ 5.0f, 12.0f/5.0f, }; const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(3); - check_is_close(scaq, expect, 1.0e-7); + check_is_close(scaq, expect, 1.0e-7, "SCAQ:3"); } // ICAQ:4 @@ -779,6 +918,8 @@ BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers) 0, 0, 0, 0, 0, 0, 0, // Connection 2 (nonexistent) 0, 0, 0, 0, 0, 0, 0, + // Connection 3 (nonexistent) + 0, 0, 0, 0, 0, 0, 0, }; const auto& icaq = aquiferData.getIntegerAquiferConnectionData(4); @@ -795,21 +936,25 @@ BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers) 0.0f, 0.0f, // Connection 2 (nonexistent) 0.0f, 0.0f, + // Connection 3 (nonexistent) + 0.0f, 0.0f, }; const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(4); - check_is_close(scaq, expect, 1.0e-7); + check_is_close(scaq, expect, 1.0e-7, "SCAQ:4"); } - // ICAQ:5 (not activated/connected) + // ICAQ:5 { const auto expect = std::vector { // Connection 0 + 11, 5, 5, 543, 2, 0, 0, + // Connection 1 (nonexistent) 0, 0, 0, 0, 0, 0, 0, - // Connection 1 + // Connection 2 (nonexistent) 0, 0, 0, 0, 0, 0, 0, - // Connection 2 + // Connection 3 (nonexistent) 0, 0, 0, 0, 0, 0, 0, }; @@ -818,20 +963,22 @@ BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers) BOOST_CHECK_EQUAL_COLLECTIONS(icaq.begin(), icaq.end(), expect.begin(), expect.end()); } - // SCAQ:5 (not activated/connected) + // SCAQ:5 { const auto expect = std::vector { // Connection 0 + 1.0f, 1.0f, + // Connection 1 (nonexistent) 0.0f, 0.0f, - // Connection 1 + // Connection 2 (nonexistent) 0.0f, 0.0f, - // Connection 2 + // Connection 3 (nonexistent) 0.0f, 0.0f, }; const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(5); - check_is_close(scaq, expect, 1.0e-7); + check_is_close(scaq, expect, 1.0e-7, "SCAQ:5"); } // ICAQ:6 @@ -843,6 +990,8 @@ BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers) 0, 0, 0, 0, 0, 0, 0, // Connection 2 (nonexistent) 0, 0, 0, 0, 0, 0, 0, + // Connection 3 (nonexistent) + 0, 0, 0, 0, 0, 0, 0, }; const auto& icaq = aquiferData.getIntegerAquiferConnectionData(6); @@ -859,11 +1008,13 @@ BOOST_AUTO_TEST_CASE(Static_Information_Analytic_Aquifers) 0.0f, 0.0f, // Connection 2 (nonexistent) 0.0f, 0.0f, + // Connection 3 (nonexistent) + 0.0f, 0.0f, }; const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(6); - check_is_close(scaq, expect, 1.0e-7); + check_is_close(scaq, expect, 1.0e-7, "SCAQ:6"); } } @@ -875,8 +1026,11 @@ BOOST_AUTO_TEST_CASE(Dynamic_Information_Analytic_Aquifers) auto aquiferData = Opm::RestartIO::Helpers:: AggregateAquiferData{ aqDims, aqConfig, createGrid() }; - aquiferData.captureDynamicdAquiferData(aqConfig, aquiferValues(aqConfig), sim_state(), - Opm::UnitSystem::newMETRIC()); + aquiferData.captureDynamicAquiferData(aqDims, aqConfig, + emptyScheduleState(), + aquiferValues(aqConfig), + sim_state(), + Opm::UnitSystem::newMETRIC()); // IAAQ { @@ -886,11 +1040,11 @@ BOOST_AUTO_TEST_CASE(Dynamic_Information_Analytic_Aquifers) // 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, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 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, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 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, }; @@ -914,9 +1068,9 @@ BOOST_AUTO_TEST_CASE(Dynamic_Information_Analytic_Aquifers) 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) + 12.34f, 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) @@ -924,9 +1078,9 @@ BOOST_AUTO_TEST_CASE(Dynamic_Information_Analytic_Aquifers) 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) + 43.21f, 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) @@ -936,7 +1090,7 @@ BOOST_AUTO_TEST_CASE(Dynamic_Information_Analytic_Aquifers) const auto& saaq = aquiferData.getSinglePrecAquiferData(); - check_is_close(saaq, expect, 1.0e-7); + check_is_close(saaq, expect, 1.0e-7, "SAAQ"); } // XAAQ @@ -949,13 +1103,13 @@ BOOST_AUTO_TEST_CASE(Dynamic_Information_Analytic_Aquifers) 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, + 333.222, 212.121, 444.333, 10.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, + 323.232, 444.555, 232.131, 1.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, @@ -963,7 +1117,7 @@ BOOST_AUTO_TEST_CASE(Dynamic_Information_Analytic_Aquifers) const auto& xaaq = aquiferData.getDoublePrecAquiferData(); - check_is_close(xaaq, expect, 1.0e-7); + check_is_close(xaaq, expect, 1.0e-7, "XAAQ"); } } @@ -972,7 +1126,8 @@ 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"); + BOOST_REQUIRE_MESSAGE(aqConfig.hasNumericalAquifer(), + "Aquifer configuration object must have numeric aquifers"); auto aquiferData = Opm::RestartIO::Helpers:: AggregateAquiferData{ aqDims, aqConfig, createGrid() }; @@ -987,10 +1142,11 @@ BOOST_AUTO_TEST_CASE(Dynamic_Information_Numeric_Aquifers) BOOST_CHECK_EQUAL(aquiferData.getNumericAquiferDoublePrecData().size(), 4u * 13); } - aquiferData.captureDynamicdAquiferData(aqConfig, - numericAquiferValues(aqConfig), - aqunum_sim_state(), - Opm::UnitSystem::newMETRIC()); + aquiferData.captureDynamicAquiferData(aqDims, aqConfig, + emptyScheduleState(), + numericAquiferValues(aqConfig), + aqunum_sim_state(), + Opm::UnitSystem::newMETRIC()); // IAQN { @@ -1022,7 +1178,321 @@ BOOST_AUTO_TEST_CASE(Dynamic_Information_Numeric_Aquifers) }; const auto& raqn = aquiferData.getNumericAquiferDoublePrecData(); - check_is_close(raqn, expect, 1.0e-10); + check_is_close(raqn, expect, 1.0e-10, "RAQN"); + } +} + +BOOST_AUTO_TEST_CASE(Constant_Flux_Aquifer_SCHEDULE) +{ + const auto aqConfig = createEmptyAquiferConfig(); + + BOOST_CHECK_MESSAGE(! aqConfig.active(), "Empty aquifer configuration object must not be active()"); + + auto aquiferData = Opm::RestartIO::Helpers::AggregateAquiferData { + syntheticDynamicAquiferDimensionsSOLUTION(), aqConfig, createGrid() + }; + + BOOST_CHECK_EQUAL(aquiferData.maximumActiveAnalyticAquiferID(), 0); + + aquiferData.captureDynamicAquiferData(syntheticDynamicAquiferDimensionsSCHEDULE(), + aqConfig, dynamicAquFluxState(), + Opm::data::Aquifers{}, + sim_state(), Opm::UnitSystem::newMETRIC()); + + BOOST_CHECK_EQUAL(aquiferData.maximumActiveAnalyticAquiferID(), 5); + + // ICAQ:1 + { + const auto expect = std::vector { + // 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, + // Connection 3 (nonexistent) + 0, 0, 0, 0, 0, 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 { + // Connection 0 + 1.0f/6.0f, 1.0f, + // Connection 1 + 1.0f/3.0f, 2.0f, + // Connection 2 + 1.0f/2.0f, 3.0f, + // Connection 3 (nonexistent) + 0.0f, 0.0f, + }; + + const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(1); + + check_is_close(scaq, expect, 1.0e-7, "SCAQ:1"); + } + + // ICAQ:2 + { + const auto expect = std::vector { + // 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, + // Connection 3 (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 { + // Connection 0 + 1.0f, 1.0f, + // Connection 1 (nonexistent) + 0.0f, 0.0f, + // Connection 2 (nonexistent) + 0.0f, 0.0f, + // Connection 3 (nonexistent) + 0.0f, 0.0f, + }; + + const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(2); + + check_is_close(scaq, expect, 1.0e-7, "SCAQ:2"); + } + + // ICAQ:3 + { + const auto expect = std::vector { + // Connection 0 + 1, 5, 7, 47, 4, 0, 0, + // Connection 1 + 1, 5, 8, 48, 4, 0, 0, + // Connection 2 + 1, 5, 9, 49, 4, 0, 0, + // Connection 3 + 1, 5, 10, 50, 4, 0, 0, + }; + + const auto& icaq = aquiferData.getIntegerAquiferConnectionData(3); + + BOOST_CHECK_EQUAL_COLLECTIONS(icaq.begin(), icaq.end(), expect.begin(), expect.end()); + } + + // SCAQ:3 + { + const auto expect = std::vector { + // Connection 0 + 1.0f/10.0f, 3.0f/5.0f, + // Connection 1 + 1.0f/ 5.0f, 6.0f/5.0f, + // Connection 2 + 3.0f/10.0f, 9.0f/5.0f, + // Connection 3 + 2.0f/ 5.0f, 12.0f/5.0f, + }; + + const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(3); + + check_is_close(scaq, expect, 1.0e-7, "SCAQ:3"); + } + + // ICAQ:4 + { + const auto expect = std::vector { + // 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, + // Connection 3 (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 { + // Connection 0 + 1.0f, 0.8f, + // Connection 1 (nonexistent) + 0.0f, 0.0f, + // Connection 2 (nonexistent) + 0.0f, 0.0f, + // Connection 3 (nonexistent) + 0.0f, 0.0f, + }; + + const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(4); + + check_is_close(scaq, expect, 1.0e-7, "SCAQ:4"); + } + + // ICAQ:5 + { + const auto expect = std::vector { + // Connection 0 + 11, 5, 5, 543, 2, 0, 0, + // Connection 1 (nonexistent) + 0, 0, 0, 0, 0, 0, 0, + // Connection 2 (nonexistent) + 0, 0, 0, 0, 0, 0, 0, + // Connection 3 (nonexistent) + 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 + { + const auto expect = std::vector { + // Connection 0 + 1.0f, 1.0f, + // Connection 1 (nonexistent) + 0.0f, 0.0f, + // Connection 2 (nonexistent) + 0.0f, 0.0f, + // Connection 3 (nonexistent) + 0.0f, 0.0f, + }; + + const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(5); + + check_is_close(scaq, expect, 1.0e-7, "SCAQ:5"); + } + + // ICAQ:6 + { + const auto expect = std::vector { + // 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, + // Connection 3 (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 { + // Connection 0 + 1.0f, 0.8f, + // Connection 1 (nonexistent) + 0.0f, 0.0f, + // Connection 2 (nonexistent) + 0.0f, 0.0f, + // Connection 3 (nonexistent) + 0.0f, 0.0f, + }; + + const auto& scaq = aquiferData.getSinglePrecAquiferConnectionData(6); + + check_is_close(scaq, expect, 1.0e-7, "SCAQ:6"); + } + + // IAAQ + { + const auto expect = std::vector { + // Aquifer 1 (nonexistent) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Aquifer 2 (nonexistent) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Aquifer 3 + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, + // Aquifer 4 (nonexistent) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Aquifer 5 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 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 { + // Aquifer 1 + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 0.. 7 + 0.0f, 0.0f, 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 + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 0.. 7 (24..38) + 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 + 12.34f, 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 + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.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 + 43.21f, 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) + }; + + const auto& saaq = aquiferData.getSinglePrecAquiferData(); + + check_is_close(saaq, expect, 1.0e-7, "SAAQ"); + } + + // XAAQ + { + const auto expect = std::vector { + // Aquifer 1 + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + + // Aquifer 2 + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + + // Aquifer 3 + 333.222, 212.121, 444.333, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + + // Aquifer 4 + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + + // Aquifer 5 + 323.232, 444.555, 232.131, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + }; + + const auto& xaaq = aquiferData.getDoublePrecAquiferData(); + + check_is_close(xaaq, expect, 1.0e-7, "XAAQ"); } }