From b42d52397b1024777e89fbb327e4f97c18c68190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Wed, 18 Dec 2024 14:23:31 +0100 Subject: [PATCH] Add Connection Fracturing Statistics Summary Vectors This commit adds support for collecting and reporting the following set of connection level fracturing statistics measures - CFRMAX -- Maximum value of connection level fracturing quantity - CFRMIN -- Minimum value of connection level fracturing quantity - CFRAVG -- Arithmetic average of connection level fracturing quantity - CFRSTD -- Unbiased standard deviation of connection level fracturing quantity The currently supported quantities () are - 'P' -- Fracture pressure - 'IR' -- Injection flow rate - 'WD' -- Fracture width These vectors adhere to the common connection level summary vector specification whence CFRPMAX 'I1' 16 16 6 / / CFRIRAVG 'I1' * / / CFRWDSTD * * / / will report the maximum pressure across all cells intersected by a fracture originating in connection (16,16,6) of well 'I1', one average value for each connection of well 'I1' with the average being taken over all cells intersected by the fraction originating at that connection and, finally, the standard deviation of the fracture width for each connection of each well with each standard deviation being calculated over all cells intersected by the fracture originating at that connection. --- .../keywords/900_OPM/C/CONNECTION_PROBE_OPM | 14 +- opm/output/data/Wells.hpp | 169 +++++++++++++++++- opm/output/eclipse/Summary.cpp | 72 ++++++++ 3 files changed, 252 insertions(+), 3 deletions(-) diff --git a/opm/input/eclipse/share/keywords/900_OPM/C/CONNECTION_PROBE_OPM b/opm/input/eclipse/share/keywords/900_OPM/C/CONNECTION_PROBE_OPM index fa8563d15c8..47558a61167 100644 --- a/opm/input/eclipse/share/keywords/900_OPM/C/CONNECTION_PROBE_OPM +++ b/opm/input/eclipse/share/keywords/900_OPM/C/CONNECTION_PROBE_OPM @@ -12,7 +12,19 @@ "CINJFVR", "CINJFVT", "CFCRAD", - "CFCAOF" + "CFCAOF", + "CFRPMAX", + "CFRPMIN", + "CFRPAVG", + "CFRPSTD", + "CFRIRMAX", + "CFRIRMIN", + "CFRIRAVG", + "CFRIRSTD", + "CFRWDMAX", + "CFRWDMIN", + "CFRWDAVG", + "CFRWDSTD" ], "items": [ { diff --git a/opm/output/data/Wells.hpp b/opm/output/data/Wells.hpp index ef3c0e446e7..1c152a17841 100644 --- a/opm/output/data/Wells.hpp +++ b/opm/output/data/Wells.hpp @@ -246,6 +246,163 @@ namespace Opm { namespace data { void read(MessageBufferType& buffer); }; + /// Connection Level Fracturing Statistics + struct ConnectionFracturing + { + /// Statistics collection for a single quantity + struct Statistics + { + /// Arithmetic average. + double avg{}; + + /// Maximum value. + double max{}; + + /// Minimum value. + double min{}; + + /// Unbiased sample standard deviation. + /// + /// Usable only if sample size is at least two. + double stdev{}; + + /// Create a serialization test object. + static Statistics serializationTestObject() + { + return { + 12.34, 56.78, 9.10, 11.12 + }; + } + + /// Convert between byte array and object representation. + /// + /// \tparam Serializer Byte array conversion protocol. + /// + /// \param[in,out] serializer Byte array conversion object. + template + void serializeOp(Serializer& serializer) + { + serializer(this->avg); + serializer(this->max); + serializer(this->min); + serializer(this->stdev); + } + + /// Equality predicate. + /// + /// \param[in] that Object against which \code *this \endcode + /// will be tested for equality. + /// + /// \return Whether or not \code *this \endcode is the same as + /// \p that. + bool operator==(const Statistics& that) const + { + return (this->avg == that.avg) + && (this->max == that.max) + && (this->min == that.min) + && (this->stdev == that.stdev) + ; + } + + /// MPI communication protocol--serialisation operation + template + void write(MessageBufferType& buffer) const + { + buffer.write(this->avg); + buffer.write(this->max); + buffer.write(this->min); + buffer.write(this->stdev); + } + + /// MPI communication protocol--deserialisation operation + template + void read(MessageBufferType& buffer) + { + buffer.read(this->avg); + buffer.read(this->max); + buffer.read(this->min); + buffer.read(this->stdev); + } + }; + + /// Sample size. + /// + /// Expected to be the same for each quantiy. + std::size_t numCells{}; + + /// Statistical measures for connection's fracture pressures. + Statistics press{}; + + /// Statistical measures for connection's fracture fracture flow rate. + Statistics rate{}; + + /// Statistical measures for connection's fracture fracture width. + Statistics width{}; + + /// Create a serialisation test object. + static ConnectionFracturing serializationTestObject() + { + auto fract = ConnectionFracturing{}; + + fract.numCells = 123; + fract.press = Statistics::serializationTestObject(); + fract.rate = Statistics::serializationTestObject(); + fract.width = Statistics::serializationTestObject(); + + return fract; + } + + /// Convert between byte array and object representation. + /// + /// \tparam Serializer Byte array conversion protocol. + /// + /// \param[in,out] serializer Byte array conversion object. + template + void serializeOp(Serializer& serializer) + { + serializer(this->numCells); + serializer(this->press); + serializer(this->rate); + serializer(this->width); + } + + /// Equality predicate. + /// + /// \param[in] that Object against which \code *this \endcode will + /// be tested for equality. + /// + /// \return Whether or not \code *this \endcode is the same as \p + /// that. + bool operator==(const ConnectionFracturing& that) const + { + return (this->numCells == that.numCells) + && (this->press == that.press) + && (this->rate == that.rate) + && (this->width == that.width) + ; + } + + /// MPI communication protocol--serialisation operation + template + void write(MessageBufferType& buffer) const + { + buffer.write(this->numCells); + buffer.write(this->press); + buffer.write(this->rate); + buffer.write(this->width); + } + + /// MPI communication protocol--deserialisation operation + template + void read(MessageBufferType& buffer) + { + buffer.read(this->numCells); + buffer.read(this->press); + buffer.read(this->rate); + buffer.read(this->width); + } + }; + struct Connection { using global_index = std::size_t; @@ -263,7 +420,10 @@ namespace Opm { namespace data { double d_factor{}; double compact_mult{1.0}; // Rock compaction transmissibility multiplier (ROCKTAB) - ConnectionFiltrate filtrate; + ConnectionFiltrate filtrate{}; + + /// Connection level fracturing statistics. + ConnectionFracturing fract{}; bool operator==(const Connection& conn2) const { @@ -279,6 +439,7 @@ namespace Opm { namespace data { && (d_factor == conn2.d_factor) && (compact_mult == conn2.compact_mult) && (filtrate == conn2.filtrate) + && (this->fract == conn2.fract) ; } @@ -304,6 +465,7 @@ namespace Opm { namespace data { serializer(d_factor); serializer(compact_mult); serializer(filtrate); + serializer(this->fract); } static Connection serializationTestObject() @@ -312,7 +474,8 @@ namespace Opm { namespace data { 1, Rates::serializationTestObject(), 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 0.987, - ConnectionFiltrate::serializationTestObject() + ConnectionFiltrate::serializationTestObject(), + ConnectionFracturing::serializationTestObject() }; } }; @@ -1268,6 +1431,7 @@ namespace Opm { namespace data { buffer.write(this->d_factor); buffer.write(this->compact_mult); this->filtrate.write(buffer); + this->fract.write(buffer); } void Connection::init_json(Json::JsonObject& json_data) const { @@ -1444,6 +1608,7 @@ namespace Opm { namespace data { buffer.read(this->d_factor); buffer.read(this->compact_mult); this->filtrate.read(buffer); + this->fract.read(buffer); } template diff --git a/opm/output/eclipse/Summary.cpp b/opm/output/eclipse/Summary.cpp index fa46e5fa41e..866bd370a1a 100644 --- a/opm/output/eclipse/Summary.cpp +++ b/opm/output/eclipse/Summary.cpp @@ -1108,6 +1108,45 @@ inline quantity cratel( const fn_args& args ) { return { sum, unit }; } +template +quantity connFracStatistics(const fn_args& args) +{ + const auto zero = quantity { 0.0, unit }; + + if (args.schedule_wells.empty()) { + return zero; + } + + const auto& name = args.schedule_wells.front()->name(); + auto xwPos = args.wells.find(name); + if ((xwPos == args.wells.end()) || + (xwPos->second.dynamicStatus == Opm::Well::Status::SHUT)) + { + return zero; + } + + const auto global_index = static_cast(args.num - 1); + + const auto& well_data = xwPos->second; + const auto connPos = + std::find_if(well_data.connections.begin(), + well_data.connections.end(), + [global_index](const Opm::data::Connection& c) + { + return c.index == global_index; + }); + + if ((connPos == well_data.connections.end()) || + (connPos->fract.numCells == 0)) + { + return zero; + } + + return { connPos->fract.*q.*stat, unit }; +} + template< bool injection > inline quantity flowing( const fn_args& args ) { const auto& wells = args.wells; @@ -2515,6 +2554,39 @@ static const auto funs = std::unordered_map { { "CFCPORO", filtrate_connection_quantities }, { "CFCRAD", filtrate_connection_quantities }, { "CFCAOF", filtrate_connection_quantities }, + + // Hydraulic fracturing (OPM extension) + // + // Fracture pressure + { "CFRPMAX", connFracStatistics<&Opm::data::ConnectionFracturing::press, + &Opm::data::ConnectionFracturing::Statistics::max, measure::pressure> }, + { "CFRPMIN", connFracStatistics<&Opm::data::ConnectionFracturing::press, + &Opm::data::ConnectionFracturing::Statistics::min, measure::pressure> }, + { "CFRPAVG", connFracStatistics<&Opm::data::ConnectionFracturing::press, + &Opm::data::ConnectionFracturing::Statistics::avg, measure::pressure> }, + { "CFRPSTD", connFracStatistics<&Opm::data::ConnectionFracturing::press, + &Opm::data::ConnectionFracturing::Statistics::stdev, measure::pressure> }, + + // Fracture injection rate + { "CFRIRMAX", connFracStatistics<&Opm::data::ConnectionFracturing::rate, + &Opm::data::ConnectionFracturing::Statistics::max, measure::rate> }, + { "CFRIRMIN", connFracStatistics<&Opm::data::ConnectionFracturing::rate, + &Opm::data::ConnectionFracturing::Statistics::min, measure::rate> }, + { "CFRIRAVG", connFracStatistics<&Opm::data::ConnectionFracturing::rate, + &Opm::data::ConnectionFracturing::Statistics::avg, measure::rate> }, + { "CFRIRSTD", connFracStatistics<&Opm::data::ConnectionFracturing::rate, + &Opm::data::ConnectionFracturing::Statistics::stdev, measure::rate> }, + + // Fracture width + { "CFRWDMAX", connFracStatistics<&Opm::data::ConnectionFracturing::width, + &Opm::data::ConnectionFracturing::Statistics::max, measure::length> }, + { "CFRWDMIN", connFracStatistics<&Opm::data::ConnectionFracturing::width, + &Opm::data::ConnectionFracturing::Statistics::min, measure::length> }, + { "CFRWDAVG", connFracStatistics<&Opm::data::ConnectionFracturing::width, + &Opm::data::ConnectionFracturing::Statistics::avg, measure::length> }, + { "CFRWDSTD", connFracStatistics<&Opm::data::ConnectionFracturing::width, + &Opm::data::ConnectionFracturing::Statistics::stdev, measure::length> }, + { "COIT", mul( crate< rt::oil, injector >, duration ) }, { "CWIT", mul( crate< rt::wat, injector >, duration ) }, { "CGIT", mul( crate< rt::gas, injector >, duration ) },