Merge pull request #3424 from bska/write-aquflux-rst-info

Add Restart File Output Support for Constant Flux Aquifers
This commit is contained in:
Bård Skaflestad 2023-03-16 12:04:14 +01:00 committed by GitHub
commit 553688b73e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 973 additions and 163 deletions

View File

@ -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<class Serializer>
template <class Serializer>
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<int, SingleAquiferFlux>;
explicit AquiferFlux(const std::vector<const DeckKeyword*>& keywords);
AquiferFlux() = default;
explicit AquiferFlux(const std::vector<const DeckKeyword*>& keywords);
// Primarily for unit testing purposes.
explicit AquiferFlux(const std::vector<SingleAquiferFlux>& aquifers);
void appendAqufluxSchedule(const std::unordered_set<int>& ids);
@ -86,15 +91,16 @@ namespace Opm {
void loadFromRestart(const RestartIO::RstAquifer& rst);
template<class Serializer>
void serializeOp(Serializer& serializer) {
template <class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(this->m_aquifers);
}
static AquiferFlux serializationTestObject();
private:
AquFluxs m_aquifers;
AquFluxs m_aquifers{};
};
} // end of namespace Opm

View File

@ -349,6 +349,11 @@ namespace Opm {
bool has_gpmaint() const;
bool hasAnalyticalAquifers() const
{
return ! this->aqufluxs.empty();
}
/*********************************************************************/
ptr_member<GConSale> gconsale;

View File

@ -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<WindowedArray<double>> 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

View File

@ -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

View File

@ -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<float>::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)

View File

@ -88,6 +88,13 @@ namespace Opm {
}
}
AquiferFlux::AquiferFlux(const std::vector<SingleAquiferFlux>& 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
} // end of namespace Opm

View File

@ -26,15 +26,16 @@
#include <opm/output/eclipse/VectorItems/aquifer.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/Aquancon.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferConfig.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferCT.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferConfig.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/Aquifetp.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/EclipseState/Tables/FlatTable.hpp>
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
@ -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 <typename AquiferCallBack>
void CarterTracyAquiferLoop(const Opm::AquiferConfig& aqConfig,
AquiferCallBack&& aquiferOp)
@ -69,6 +75,27 @@ namespace {
}
}
template <typename AquiferCallBack>
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 <typename AquiferCallBack>
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 <typename IAaqArray>
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 <typename IAaqArray>
@ -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 <typename IAaqArray>
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 <typename IAaqArray>
@ -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<int>;
return std::vector<WA>(aqDims.maxAquiferID, WA {
return std::vector<WA>(maxNumberOfAquifers(aqDims), WA {
WA::NumWindows{ static_cast<WA::Idx>(aqDims.maxNumActiveAquiferConn) },
WA::WindowSize{ static_cast<WA::Idx>(aqDims.numIntConnElem) }
});
@ -307,6 +354,23 @@ namespace {
}
} // CarterTracy
namespace ConstantFlux {
template <typename SAaqArray>
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 <typename SAaqArray>
void staticContrib(const Opm::Aquifetp::AQUFETP_data& aquifer,
@ -351,7 +415,7 @@ namespace {
{
using WA = Opm::RestartIO::Helpers::WindowedArray<float>;
return std::vector<WA>(aqDims.maxAquiferID, WA {
return std::vector<WA>(maxNumberOfAquifers(aqDims), WA {
WA::NumWindows{ static_cast<WA::Idx>(aqDims.maxNumActiveAquiferConn) },
WA::WindowSize{ static_cast<WA::Idx>(aqDims.numRealConnElem) }
});
@ -442,6 +506,18 @@ namespace {
}
} // CarterTracy
namespace ConstantFlux {
template <typename SummaryVariable, typename XAaqArray>
void dynamicContrib(SummaryVariable&& summaryVariable,
const double tot_influx,
const Opm::UnitSystem& usys,
XAaqArray& xaaq)
{
Common::dynamicContrib(std::forward<SummaryVariable>(summaryVariable),
tot_influx, usys, xaaq);
}
} // ConstantFlux
namespace Fetkovich {
template <typename SummaryVariable, typename XAaqArray>
void dynamicContrib(SummaryVariable&& summaryVariable,
@ -515,7 +591,7 @@ namespace {
{
using WA = Opm::RestartIO::Helpers::WindowedArray<double>;
return std::vector<WA>(aqDims.maxAquiferID, WA {
return std::vector<WA>(maxNumberOfAquifers(aqDims), WA {
WA::NumWindows{ static_cast<WA::Idx>(aqDims.maxNumActiveAquiferConn) },
WA::WindowSize{ static_cast<WA::Idx>(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<WindowedArray<int>::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<data::AquiferType::Fetkovich>())
{
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<WindowedArray<int>::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<WindowedArray<int>::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<data::AquiferType::Fetkovich>())
{
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);

View File

@ -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))

View File

@ -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);
}
}

View File

@ -22,19 +22,24 @@
#include <opm/output/eclipse/VectorItems/intehead.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferConfig.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/Aquancon.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferConfig.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/Runspec.hpp>
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/common/utility/TimeService.hpp>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <ctime>
#include <initializer_list>
#include <numeric>
#include <ratio>
#include <utility>
@ -881,11 +886,66 @@ Opm::RestartIO::getSimulationTimePoint(const std::time_t start,
}
namespace {
template <typename T, class A>
int numUnique(std::vector<T, A> 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<int>{};
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<int>(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 <typename AquiferCollection>
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 <typename AquFluxIter>
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;
}

View File

@ -34,7 +34,6 @@
#include <opm/output/eclipse/AggregateUDQData.hpp>
#include <opm/output/eclipse/AggregateActionxData.hpp>
#include <opm/output/eclipse/RestartValue.hpp>
#include <opm/input/eclipse/EclipseState/TracerConfig.hpp>
#include <opm/output/eclipse/WriteRestartHelpers.hpp>
@ -43,16 +42,19 @@
#include <opm/io/eclipse/OutputStream.hpp>
#include <opm/io/eclipse/PaddedOutputString.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/Tables/Eqldims.hpp>
#include <opm/input/eclipse/EclipseState/TracerConfig.hpp>
#include <opm/input/eclipse/Schedule/Action/Actions.hpp>
#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/Tuning.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/EclipseState/Tables/Eqldims.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
@ -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);
}
}

View File

@ -31,15 +31,17 @@
#include <opm/input/eclipse/EclipseState/Aquifer/Aquancon.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferCT.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/Aquifetp.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferConfig.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/Aquifetp.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/Grid/FaceDir.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/EclipseState/Tables/FlatTable.hpp>
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
@ -50,6 +52,7 @@
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>
@ -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 <class Coll1, class Coll2>
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<int> {
// 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<float> {
// 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<int> {
// 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<float> {
// 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<int> {
// Connection 0
20, 5, 7, 993, 1, 0, 0,
// Connection 1
20, 5, 8, 994, 3, 0, 0,
// Connection 2
20, 5, 9, 995, 5, 0, 0,
// 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<float> {
// Connection 0
1.0f/6.0f, 1.0f,
// Connection 1
1.0f/3.0f, 2.0f,
// Connection 2
1.0f/2.0f, 3.0f,
// 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<int> {
// Connection 0
20, 1, 7, 955, 4, 0, 0,
// Connection 1 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
// Connection 2 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
// 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<float> {
// 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<int> {
// 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<float> {
// 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<int> {
// Connection 0
20, 1, 5, 953, 6, 0, 0,
// Connection 1 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
// Connection 2 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
// 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<float> {
// 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<int> {
// 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<float> {
// 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<int> {
// Connection 0
20, 4, 6, 982, 2, 0, 0,
// Connection 1 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
// Connection 2 (nonexistent)
0, 0, 0, 0, 0, 0, 0,
// 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<float> {
// 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<int> {
// 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<float> {
// 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<double> {
// 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");
}
}