From 73c58dba48c674a85d4448632b7dad221f54ad8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 10 Nov 2023 12:05:46 +0100 Subject: [PATCH 1/7] Extract CTF Input Data to Separate Structure This commit creates a new helper structure, Opm::Connection::CTFProperties that holds all of the individual items which go into calculating the final connection transmissibility factor (CTF). These include the 'Kh' product, the pressure equivalent radius ('r0'), the well-bore radius ('rw'), and the skin factor. We reimplement the Connection constructor in terms of this new helper structure and this changes the API of the constructor. As an additional helper for the implementation, and with a view towards simulation restart support, we also store the value of the Peaceman formula denominator in CTFProperties as double CTFProperties::peaceman_denom This, in turn, simplifies the implementation of CSKIN. It also fixes a latent problem in the current CSKIN implementation which would ignore any explicitly input CTF values in favour of the pressure equivalent radius. By updating the explicit denominator with a new skin factor instead we do not need to recompute the logarithmic term involving 'r0' as part of CSKIN handling. --- .../eclipse/Schedule/Well/Connection.hpp | 304 +++--- opm/input/eclipse/Schedule/Well/Well.hpp | 2 +- .../eclipse/Schedule/Well/WellConnections.hpp | 58 +- .../eclipse/Schedule/KeywordHandlers.cpp | 18 +- .../eclipse/Schedule/Well/Connection.cpp | 629 +++++++----- src/opm/input/eclipse/Schedule/Well/Well.cpp | 84 +- .../eclipse/Schedule/Well/WellConnections.cpp | 921 +++++++++--------- tests/parser/ConnectionTests.cpp | 207 ++-- tests/parser/MultisegmentWellTests.cpp | 176 ++-- tests/parser/ScheduleTests.cpp | 908 +++++++++-------- tests/parser/WellSolventTests.cpp | 359 +++---- tests/parser/WellTracerTests.cpp | 243 ++--- .../integration/ScheduleCreateFromDeck.cpp | 62 +- tests/test_PAvgCalculator.cpp | 54 +- tests/test_Serialization.cpp | 1 + 15 files changed, 2263 insertions(+), 1763 deletions(-) diff --git a/opm/input/eclipse/Schedule/Well/Connection.hpp b/opm/input/eclipse/Schedule/Well/Connection.hpp index 431a13cb3..4be15fbf2 100644 --- a/opm/input/eclipse/Schedule/Well/Connection.hpp +++ b/opm/input/eclipse/Schedule/Well/Connection.hpp @@ -17,91 +17,167 @@ along with OPM. If not, see . */ - #ifndef COMPLETION_HPP_ #define COMPLETION_HPP_ +#include +#include + #include #include #include #include #include +#include #include #include #include -#include -#include - namespace Opm { - -namespace RestartIO { - struct RstConnection; -} - class DeckKeyword; class DeckRecord; class ScheduleGrid; class FieldPropsManager; +} // namespace Opm - class Connection { +namespace Opm { namespace RestartIO { + struct RstConnection; +}} // namespace Opm::RestartIO + +namespace Opm { + + class Connection + { public: enum class State { OPEN = 1, SHUT = 2, - AUTO = 3 // Seems like the AUTO state can not be serialized to restart files. + AUTO = 3, // Seems like the AUTO state can not be serialized to restart files. }; - static const std::string State2String( State enumValue ); - static State StateFromString( const std::string& stringValue ); + static std::string State2String(State enumValue); + static State StateFromString(std::string_view stringValue); - enum class Direction{ + enum class Direction { X = 1, Y = 2, - Z = 3 + Z = 3, }; static std::string Direction2String(const Direction enumValue); - static Direction DirectionFromString(const std::string& stringValue); + static Direction DirectionFromString(std::string_view stringValue); enum class Order { - DEPTH, - INPUT, - TRACK + DEPTH, + INPUT, + TRACK, }; - static const std::string Order2String( Order enumValue ); - static Order OrderFromString(const std::string& comporderStringValue); + static std::string Order2String(Order enumValue); + static Order OrderFromString(std::string_view comporderStringValue); + enum class CTFKind { DeckValue, Defaulted, }; - Connection(); - Connection(int i, int j , int k , - std::size_t global_index, - int complnum, - double depth, - State state, - double CF, - double Kh, - double rw, - double r0, - double re, - double connection_length, - double skin_factor, - double d_factor, - double Ke, - const int satTableId, - const Direction direction, - const CTFKind ctf_kind, - const std::size_t sort_value, - const bool defaultSatTabId); - Connection(const RestartIO::RstConnection& rst_connection, const ScheduleGrid& grid, const FieldPropsManager& fp); + /// Quantities that go into calculating the connection + /// transmissibility factor. + struct CTFProperties + { + /// Static connection transmissibility factor calculated from + /// input quantities. + double CF{}; + + /// Static 'Kh' product + double Kh{}; + + /// Effective permeability. + double Ke{}; + + /// Connection's wellbore radius + double rw{}; + + /// Connection's pressure equivalent radius + double r0{}; + + /// Connection's area equivalent radius--mostly for use by the + /// polymer code. + double re{}; + + /// Length of connection's perfororation interval + double connection_length{}; + + /// Connection's skin factor. + double skin_factor{}; + + /// Connection's D factor-i.e., the flow-dependent skin factor + /// for gas. + double d_factor{}; + + /// Denominator in peaceman's formula-i.e., log(r0/rw) + skin. + double peaceman_denom{}; + + /// Serialisation test object. + static CTFProperties serializationTestObject(); + + /// Equality operator + /// + /// \param[in] that Property object to which \c *this will be compared. + bool operator==(const CTFProperties& that) const; + + /// Inequality operator + /// + /// \param[in] that Property object to which \c *this will be compared. + bool operator!=(const CTFProperties& that) const + { + return ! (*this == that); + } + + /// Serialisation operator + /// + /// \tparam Serializer Protocol for serialising and + /// deserialising objects between memory and character + /// buffers. + /// + /// \param[in,out] serializer Serialisation object. + template + void serializeOp(Serializer& serializer) + { + serializer(this->CF); + serializer(this->Kh); + serializer(this->Ke); + serializer(this->rw); + serializer(this->r0); + serializer(this->re); + serializer(this->connection_length); + serializer(this->skin_factor); + serializer(this->d_factor); + serializer(this->peaceman_denom); + } + }; + + + Connection() = default; + Connection(int i, int j, int k, + std::size_t global_index, + int complnum, + State state, + Direction direction, + CTFKind ctf_kind, + const int satTableId, + double depth, + const CTFProperties& ctf_properties, + const std::size_t sort_value, + const bool defaultSatTabId); + + Connection(const RestartIO::RstConnection& rst_connection, + const ScheduleGrid& grid, + const FieldPropsManager& fp); static Connection serializationTestObject(); @@ -117,104 +193,104 @@ namespace RestartIO { int satTableId() const; int complnum() const; int segment() const; - double CF() const; double wpimult() const; + double CF() const; double Kh() const; + double Ke() const; double rw() const; double r0() const; double re() const; double connectionLength() const; double skinFactor() const; double dFactor() const; - double Ke() const; CTFKind kind() const; const InjMult& injmult() const; bool activeInjMult() const; - void setInjMult(const InjMult& inj_mult); - void setFilterCake(const FilterCake& filter_cake); const FilterCake& getFilterCake() const; bool filterCakeActive() const; double getFilterCakeRadius() const; double getFilterCakeArea() const; + const CTFProperties& ctfProperties() const + { + return this->ctf_properties_; + } + + std::size_t sort_value() const; + bool getDefaultSatTabId() const; + const std::optional>& perf_range() const; + std::string str() const; + + bool ctfAssignedFromInput() const + { + return this->m_ctfkind == CTFKind::DeckValue; + } + + bool operator==(const Connection&) const; + bool operator!=(const Connection& that) const + { + return ! (*this == that); + } + + void setInjMult(const InjMult& inj_mult); + void setFilterCake(const FilterCake& filter_cake); void setState(State state); void setComplnum(int compnum); void setSkinFactor(double skin_factor); void setDFactor(double d_factor); void setKe(double Ke); void setCF(double CF); + void setDefaultSatTabId(bool id); + void scaleWellPi(double wellPi); bool prepareWellPIScaling(); bool applyWellPIScaling(const double scaleFactor); + void updateSegmentRST(int segment_number_arg, double center_depth_arg); void updateSegment(int segment_number_arg, double center_depth_arg, std::size_t compseg_insert_index, const std::optional>& perf_range); - std::size_t sort_value() const; - const bool& getDefaultSatTabId() const; - void setDefaultSatTabId(bool id); - const std::optional>& perf_range() const; - std::string str() const; - bool ctfAssignedFromInput() const - { - return this->m_ctfkind == CTFKind::DeckValue; - } - - bool operator==( const Connection& ) const; - bool operator!=( const Connection& ) const; template void serializeOp(Serializer& serializer) { - serializer(direction); - serializer(center_depth); - serializer(open_state); - serializer(sat_tableId); - serializer(m_complnum); - serializer(m_CF); - serializer(m_Kh); - serializer(m_rw); - serializer(m_r0); - serializer(m_re); - serializer(m_connection_length); - serializer(m_skin_factor); - serializer(m_d_factor); - serializer(m_Ke); - serializer(ijk); - serializer(m_global_index); - serializer(m_ctfkind); - serializer(m_injmult); - serializer(m_sort_value); - serializer(m_perf_range); - serializer(m_defaultSatTabId); - serializer(segment_number); - serializer(m_subject_to_welpi); - serializer(m_filter_cake); - serializer(m_wpimult); + serializer(this->direction); + serializer(this->center_depth); + serializer(this->open_state); + serializer(this->sat_tableId); + serializer(this->m_complnum); + serializer(this->ctf_properties_); + serializer(this->ijk); + serializer(this->m_ctfkind); + serializer(this->m_global_index); + serializer(this->m_injmult); + serializer(this->m_sort_value); + serializer(this->m_perf_range); + serializer(this->m_defaultSatTabId); + serializer(this->segment_number); + serializer(this->m_wpimult); + serializer(this->m_subject_to_welpi); + serializer(this->m_filter_cake); } private: - Direction direction; - double center_depth; - State open_state; - int sat_tableId; - int m_complnum; - double m_CF; - double m_Kh; - double m_rw; - double m_r0; - double m_re; - double m_connection_length; - double m_skin_factor; - double m_d_factor; - double m_Ke; + // Note to maintainer: If you add new members to this list, then + // please also update the operator==(), serializeOp(), and + // serializationTestObject() member functions. + Direction direction { Direction::Z }; + double center_depth { 0.0 }; + State open_state { State::SHUT }; + int sat_tableId { -1 }; + int m_complnum { -1 }; + CTFProperties ctf_properties_{}; + + std::array ijk{}; + CTFKind m_ctfkind { CTFKind::DeckValue }; + std::optional m_injmult{}; + std::size_t m_global_index{}; - std::array ijk; - CTFKind m_ctfkind; - std::optional m_injmult; - std::size_t m_global_index; /* The sort_value member is a peculiar quantity. The connections are assembled in the WellConnections class. During the lifetime of the @@ -266,26 +342,26 @@ namespace RestartIO { explicitly, so the truth is probably that the storage order during simulation makes no difference? */ + std::size_t m_sort_value{}; - std::size_t m_sort_value; - std::optional> m_perf_range; - bool m_defaultSatTabId; + std::optional> m_perf_range{}; + bool m_defaultSatTabId{true}; - // related segment number - // 0 means the completion is not related to segment - int segment_number = 0; + // Associate segment number + // + // 0 means the connection is not associated to a segment. + int segment_number { 0 }; + + double m_wpimult { 1.0 }; // Whether or not this Connection is subject to WELPI scaling. - bool m_subject_to_welpi = false; + bool m_subject_to_welpi { false }; - // For applying last known WPIMULT to when calculating connection transmissibilty factor in CSKIN - double m_wpimult = 1.0; - - std::optional m_filter_cake; + std::optional m_filter_cake{}; static std::string CTFKindToString(const CTFKind); }; -} -#endif /* COMPLETION_HPP_ */ +} // namespace Opm +#endif // COMPLETION_HPP_ diff --git a/opm/input/eclipse/Schedule/Well/Well.hpp b/opm/input/eclipse/Schedule/Well/Well.hpp index 4aca23351..3855582f9 100644 --- a/opm/input/eclipse/Schedule/Well/Well.hpp +++ b/opm/input/eclipse/Schedule/Well/Well.hpp @@ -527,7 +527,7 @@ public: bool handleWELSEGS(const DeckKeyword& keyword); bool handleCOMPSEGS(const DeckKeyword& keyword, const ScheduleGrid& grid, const ParseContext& parseContext, ErrorGuard& errors); bool handleWELOPENConnections(const DeckRecord& record, Connection::State status); - bool handleCSKINConnections(const DeckRecord& record); + bool handleCSKIN(const DeckRecord& record, const KeywordLocation& location); bool handleCOMPLUMP(const DeckRecord& record); bool handleWPIMULT(const DeckRecord& record); bool handleWINJCLN(const DeckRecord& record, const KeywordLocation& location); diff --git a/opm/input/eclipse/Schedule/Well/WellConnections.hpp b/opm/input/eclipse/Schedule/Well/WellConnections.hpp index 3ed188439..052a9054e 100644 --- a/opm/input/eclipse/Schedule/Well/WellConnections.hpp +++ b/opm/input/eclipse/Schedule/Well/WellConnections.hpp @@ -23,9 +23,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -33,10 +35,10 @@ namespace Opm { class ActiveGridCells; class DeckRecord; + class EclipseGrid; class FieldPropsManager; class KeywordLocation; class ScheduleGrid; - class EclipseGrid; } // namespace Opm namespace Opm { @@ -67,19 +69,16 @@ namespace Opm { } } + void add(const Connection& conn) + { + this->m_connections.push_back(conn); + } + void addConnection(const int i, const int j, const int k, const std::size_t global_index, - const double depth, const Connection::State state, - const double CF, - const double Kh, - const double rw, - const double r0, - const double re, - const double connection_length, - const double skin_factor, - const double d_factor, - const double Ke, + const double depth, + const Connection::CTFProperties& ctf_props, const int satTableId, const Connection::Direction direction = Connection::Direction::Z, const Connection::CTFKind ctf_kind = Connection::CTFKind::DeckValue, @@ -91,14 +90,20 @@ namespace Opm { const std::string& wname, const KeywordLocation& location); - void loadCOMPTRAJ(const DeckRecord& record, const ScheduleGrid& grid, const std::string& wname, const KeywordLocation& location, external::cvf::ref& cellSearchTree); + void loadCOMPTRAJ(const DeckRecord& record, + const ScheduleGrid& grid, + const std::string& wname, + const KeywordLocation& location, + external::cvf::ref& cellSearchTree); - void loadWELTRAJ(const DeckRecord& record, const ScheduleGrid& grid, const std::string& wname, const KeywordLocation& location); + void loadWELTRAJ(const DeckRecord& record, + const ScheduleGrid& grid, + const std::string& wname, + const KeywordLocation& location); int getHeadI() const; int getHeadJ() const; const std::vector& getMD() const; - void add(Connection); std::size_t size() const; bool empty() const; std::size_t num_open() const; @@ -165,39 +170,32 @@ namespace Opm { serializer(this->coord); serializer(this->md); } + private: Connection::Order m_ordering { Connection::Order::TRACK }; int headI{0}; int headJ{0}; std::vector m_connections{}; - std::vector> coord{3, std::vector(0, 0.0) }; + + std::array, 3> coord{}; std::vector md{}; void addConnection(const int i, const int j, const int k, const std::size_t global_index, const int complnum, - const double depth, const Connection::State state, - const double CF, - const double Kh, - const double rw, - const double r0, - const double re, - const double connection_length, - const double skin_factor, - const double d_factor, - const double Ke, + const double depth, + const Connection::CTFProperties& ctf_props, const int satTableId, - const Connection::Direction direction = Connection::Direction::Z, - const Connection::CTFKind ctf_kind = Connection::CTFKind::DeckValue, - const std::size_t seqIndex = 0, - const bool defaultSatTabId = true); + const Connection::Direction direction, + const Connection::CTFKind ctf_kind, + const std::size_t seqIndex, + const bool defaultSatTabId); size_t findClosestConnection(int oi, int oj, double oz, size_t start_pos); void orderTRACK(); void orderMSW(); void orderDEPTH(); - }; std::optional diff --git a/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp index 3390b6bed..e475ce6f6 100644 --- a/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp +++ b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp @@ -377,23 +377,29 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno) handlerContext.compsegs_handled(wname); } - void Schedule::handleCSKIN(HandlerContext& handlerContext) { + void Schedule::handleCSKIN(HandlerContext& handlerContext) + { + using Kw = ParserKeywords::CSKIN; + // Get CSKIN keyword info and current step const auto& keyword = handlerContext.keyword; const auto& currentStep = handlerContext.currentStep; // Loop over records in CSKIN - for (const auto& record: keyword) { + for (const auto& record : keyword) { // Get well names - const auto& wellNamePattern = record.getItem( "WELL" ).getTrimmedString(0); + const auto wellNamePattern = record.getItem().getTrimmedString(0); const auto well_names = this->wellNames(wellNamePattern, handlerContext); // Loop over well(s) in record for (const auto& wname : well_names) { - // Get well information, modify connection skin factor, and update well + // Get well information, modify connection skin factor, and + // update well. auto well = this->snapshots[currentStep].wells.get(wname); - well.handleCSKINConnections(record); - this->snapshots[currentStep].wells.update( std::move(well) ); + + if (well.handleCSKIN(record, handlerContext.keyword.location())) { + this->snapshots[currentStep].wells.update(std::move(well)); + } } } } diff --git a/src/opm/input/eclipse/Schedule/Well/Connection.cpp b/src/opm/input/eclipse/Schedule/Well/Connection.cpp index b7f0958f3..58673416b 100644 --- a/src/opm/input/eclipse/Schedule/Well/Connection.cpp +++ b/src/opm/input/eclipse/Schedule/Well/Connection.cpp @@ -17,125 +17,157 @@ along with OPM. If not, see . */ +#include + +#include + +#include + +#include +#include + +#include +#include +#include + #include #include #include #include #include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Opm { - - - Connection::Connection(int i, int j , int k , - std::size_t global_index, - int compnum, - double depth, - State stateArg , - double CF, - double Kh, - double rw, - double r0, - double re, - double connection_length, - double skin_factor, - double d_factor, - double Ke, - const int satTableId, - const Direction directionArg, - const CTFKind ctf_kind, - const std::size_t sort_value, - const bool defaultSatTabId) - : direction(directionArg), - center_depth(depth), - open_state(stateArg), - sat_tableId(satTableId), - m_complnum( compnum ), - m_CF(CF), - m_Kh(Kh), - m_rw(rw), - m_r0(r0), - m_re(re), - m_connection_length(connection_length), - m_skin_factor(skin_factor), - m_d_factor(d_factor), - m_Ke(Ke), - ijk({i,j,k}), - m_ctfkind(ctf_kind), - m_global_index(global_index), - m_sort_value(sort_value), - m_defaultSatTabId(defaultSatTabId) - { - } - namespace { -constexpr bool defaultSatTabId = true; + constexpr bool restartDefaultSatTabId = true; + + Opm::Connection::CTFProperties + collectCTFProps(const Opm::RestartIO::RstConnection& rst_conn) + { + auto props = Opm::Connection::CTFProperties{}; + + props.CF = rst_conn.cf; + props.Kh = rst_conn.kh; + props.Ke = 0.0; + props.rw = rst_conn.diameter / 2; + props.r0 = rst_conn.r0; + props.re = 0.0; + props.connection_length = 0.0; + props.skin_factor = rst_conn.skin_factor; + props.d_factor = 0.0; + + return props; + } } -Connection::Connection(const RestartIO::RstConnection& rst_connection, const ScheduleGrid& grid, const FieldPropsManager& fp) : - direction(rst_connection.dir), - center_depth(rst_connection.depth), - open_state(rst_connection.state), - sat_tableId(rst_connection.drain_sat_table), - m_complnum(rst_connection.completion), - m_CF(rst_connection.cf), - m_Kh(rst_connection.kh), - m_rw(rst_connection.diameter / 2), - m_r0(rst_connection.r0), - m_re(0.0), - m_connection_length(0.0), - m_skin_factor(rst_connection.skin_factor), - m_d_factor(0.0), - m_Ke(0.0), - ijk(rst_connection.ijk), - m_ctfkind(rst_connection.cf_kind), - m_global_index(grid.get_cell(this->ijk[0], this->ijk[1], this->ijk[2]).global_index), - m_sort_value(rst_connection.rst_index), - m_defaultSatTabId(defaultSatTabId), - segment_number(rst_connection.segment) +namespace Opm +{ + + Connection::CTFProperties + Connection::CTFProperties::serializationTestObject() + { + auto props = Opm::Connection::CTFProperties{}; + + props.CF = 1.0; + props.Kh = 2.0; + props.Ke = 3.0; + props.rw = 4.0; + props.r0 = 5.0; + props.re = 6.0; + props.connection_length = 7.0; + props.skin_factor = 8.0; + props.d_factor = 9.0; + props.peaceman_denom = 10.0; + + return props; + } + + bool Connection::CTFProperties::operator==(const CTFProperties& that) const + { + return (this->CF == that.CF) + && (this->Kh == that.Kh) + && (this->Ke == that.Ke) + && (this->rw == that.rw) + && (this->r0 == that.r0) + && (this->re == that.re) + && (this->connection_length == that.connection_length) + && (this->skin_factor == that.skin_factor) + && (this->d_factor == that.d_factor) + && (this->peaceman_denom == that.peaceman_denom) + ; + } + + // ========================================================================= + + Connection::Connection(const int i, const int j, const int k, + const std::size_t global_index, + const int complnum, + const State stateArg, + const Direction directionArg, + const CTFKind ctf_kind, + const int satTableId, + const double depth, + const CTFProperties& ctf_props, + const std::size_t sort_value, + const bool defaultSatTabId) + : direction (directionArg) + , center_depth (depth) + , open_state (stateArg) + , sat_tableId (satTableId) + , m_complnum (complnum) + , ctf_properties_ { ctf_props } + , ijk { i, j, k } + , m_ctfkind (ctf_kind) + , m_global_index (global_index) + , m_sort_value (sort_value) + , m_defaultSatTabId(defaultSatTabId) + {} + + Connection::Connection(const RestartIO::RstConnection& rst_connection, + const ScheduleGrid& grid, + const FieldPropsManager& fp) + : direction (rst_connection.dir) + , center_depth (rst_connection.depth) + , open_state (rst_connection.state) + , sat_tableId (rst_connection.drain_sat_table) + , m_complnum (rst_connection.completion) + , ctf_properties_ (collectCTFProps(rst_connection)) + , ijk (rst_connection.ijk) + , m_ctfkind (rst_connection.cf_kind) + , m_global_index (grid.get_cell(this->ijk[0], this->ijk[1], this->ijk[2]).global_index) + , m_sort_value (rst_connection.rst_index) + , m_defaultSatTabId(restartDefaultSatTabId) + , segment_number (rst_connection.segment) { if (this->m_defaultSatTabId) { - const auto& satnum = fp.get_int("SATNUM"); - auto active_index = grid.get_cell(this->ijk[0], this->ijk[1], this->ijk[2]).active_index(); - this->sat_tableId = satnum[active_index]; + const auto active_index = grid + .get_cell(this->ijk[0], this->ijk[1], this->ijk[2]) + .active_index(); + + this->sat_tableId = fp.get_int("SATNUM")[active_index]; + } + + if (this->segment_number > 0) { + this->m_perf_range = std::make_pair(rst_connection.segdist_start, + rst_connection.segdist_end); } - if (this->segment_number > 0) - this->m_perf_range = std::make_pair(rst_connection.segdist_start, rst_connection.segdist_end); //TODO recompute re and perf_length from the grid } - Connection::Connection() - : Connection(0, 0, 0, 0, 0, 0.0, State::SHUT, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0, Direction::X, CTFKind::DeckValue, 0, false) - {} - Connection Connection::serializationTestObject() { Connection result; + result.direction = Direction::Y; result.center_depth = 1.0; result.open_state = State::OPEN; result.sat_tableId = 2; result.m_complnum = 3; - result.m_CF = 4.0; - result.m_Kh = 5.0; - result.m_rw = 6.0; - result.m_r0 = 7.0; - result.m_re = 7.1; - result.m_connection_length = 7.2; - result.m_skin_factor = 8.0; - result.m_d_factor = 8.5; - result.m_Ke = 8.9; + + result.ctf_properties_ = CTFProperties::serializationTestObject(); + result.ijk = {9, 10, 11}; result.m_ctfkind = CTFKind::Defaulted; result.m_global_index = 12; @@ -144,144 +176,181 @@ Connection::Connection(const RestartIO::RstConnection& rst_connection, const Sch result.m_sort_value = 14; result.m_defaultSatTabId = true; result.segment_number = 16; + result.m_wpimult = 0.123; result.m_subject_to_welpi = true; result.m_filter_cake = FilterCake::serializationTestObject(); return result; } - bool Connection::sameCoordinate(const int i, const int j, const int k) const { - if ((ijk[0] == i) && (ijk[1] == j) && (ijk[2] == k)) { - return true; - } else { - return false; - } + bool Connection::sameCoordinate(const int i, const int j, const int k) const + { + return this->ijk == std::array { i, j, k }; } - int Connection::getI() const { + int Connection::getI() const + { return ijk[0]; } - int Connection::getJ() const { + int Connection::getJ() const + { return ijk[1]; } - int Connection::getK() const { + int Connection::getK() const + { return ijk[2]; } - std::size_t Connection::global_index() const { + std::size_t Connection::global_index() const + { return this->m_global_index; } - bool Connection::attachedToSegment() const { - return (segment_number > 0); + bool Connection::attachedToSegment() const + { + return this->segment_number > 0; } - std::size_t Connection::sort_value() const { + std::size_t Connection::sort_value() const + { return m_sort_value; } - const bool& Connection::getDefaultSatTabId() const { + bool Connection::getDefaultSatTabId() const + { return m_defaultSatTabId; } - Connection::Direction Connection::dir() const { + Connection::Direction Connection::dir() const + { return this->direction; } -const std::optional>& Connection::perf_range() const { + const std::optional>& + Connection::perf_range() const + { return this->m_perf_range; } - void Connection::setDefaultSatTabId(bool id) { + void Connection::setDefaultSatTabId(bool id) + { m_defaultSatTabId = id; } - double Connection::depth() const { + double Connection::depth() const + { return this->center_depth; } - Connection::State Connection::state() const { + Connection::State Connection::state() const + { return this->open_state; } - int Connection::satTableId() const { + int Connection::satTableId() const + { return this->sat_tableId; } - int Connection::complnum() const { + int Connection::complnum() const + { return this->m_complnum; } - void Connection::setComplnum(int complnum) { + void Connection::setComplnum(int complnum) + { this->m_complnum = complnum; } - void Connection::setSkinFactor(double skin_factor) { - this->m_skin_factor = skin_factor; + void Connection::setSkinFactor(double skin_factor) + { + auto& ctf_p = this->ctf_properties_; + + const auto peaceman_denom = ctf_p.peaceman_denom + - ctf_p.skin_factor + skin_factor; + + ctf_p.skin_factor = skin_factor; + ctf_p.CF *= ctf_p.peaceman_denom / peaceman_denom; + ctf_p.peaceman_denom = peaceman_denom; } - void Connection::setDFactor(double d_factor) { - this->m_d_factor = d_factor; + void Connection::setDFactor(double d_factor) + { + this->ctf_properties_.d_factor = d_factor; } - void Connection::setKe(double Ke) { - this->m_Ke = Ke; + void Connection::setKe(double Ke) + { + this->ctf_properties_.Ke = Ke; } - void Connection::setCF(double CF) { - this->m_CF = CF; + void Connection::setCF(double CF) + { + this->ctf_properties_.CF = CF; } - double Connection::CF() const { - return this->m_CF; - } - - double Connection::wpimult() const { + double Connection::wpimult() const + { return this->m_wpimult; } - double Connection::Kh() const { - return this->m_Kh; + double Connection::CF() const + { + return this->ctf_properties_.CF; } - double Connection::rw() const { - return this->m_rw; + double Connection::Kh() const + { + return this->ctf_properties_.Kh; } - double Connection::r0() const { - return this->m_r0; + double Connection::rw() const + { + return this->ctf_properties_.rw; } - double Connection::re() const { - return this->m_re; + double Connection::r0() const + { + return this->ctf_properties_.r0; } - double Connection::connectionLength() const { - return this->m_connection_length; + double Connection::re() const + { + return this->ctf_properties_.re; } - double Connection::skinFactor() const { - return this->m_skin_factor; + double Connection::connectionLength() const + { + return this->ctf_properties_.connection_length; } - double Connection::dFactor() const { - return this->m_d_factor; + double Connection::skinFactor() const + { + return this->ctf_properties_.skin_factor; } - double Connection::Ke() const { - return this->m_Ke; + double Connection::dFactor() const + { + return this->ctf_properties_.d_factor; } - void Connection::setState(State state) { + double Connection::Ke() const + { + return this->ctf_properties_.Ke; + } + + void Connection::setState(State state) + { this->open_state = state; } - void Connection::updateSegment(int segment_number_arg, - double center_depth_arg, - std::size_t compseg_insert_index, - const std::optional>& perf_range) { + void Connection::updateSegment(const int segment_number_arg, + const double center_depth_arg, + const std::size_t compseg_insert_index, + const std::optional>& perf_range) + { this->segment_number = segment_number_arg; this->center_depth = center_depth_arg; this->m_sort_value = compseg_insert_index; @@ -289,124 +358,137 @@ const std::optional>& Connection::perf_range() const { } void Connection::updateSegmentRST(int segment_number_arg, - double center_depth_arg) { + double center_depth_arg) + { this->segment_number = segment_number_arg; this->center_depth = center_depth_arg; } - int Connection::segment() const { + int Connection::segment() const + { return this->segment_number; } - void Connection::scaleWellPi(double wellPi) { + void Connection::scaleWellPi(double wellPi) + { this->m_wpimult *= wellPi; - this->m_CF *= wellPi; + this->ctf_properties_.CF *= wellPi; } - bool Connection::prepareWellPIScaling() { + bool Connection::prepareWellPIScaling() + { const auto update = !this->m_subject_to_welpi; this->m_subject_to_welpi = true; return update; } - - bool Connection::applyWellPIScaling(const double scaleFactor) { - if (! this->m_subject_to_welpi) + bool Connection::applyWellPIScaling(const double scaleFactor) + { + if (! this->m_subject_to_welpi) { return false; + } this->scaleWellPi(scaleFactor); return true; } - std::string Connection::str() const { + std::string Connection::str() const + { std::stringstream ss; - ss << "ijk: " << this->ijk[0] << "," << this->ijk[1] << "," << this->ijk[2] << std::endl; - ss << "COMPLNUM " << this->m_complnum << std::endl; - ss << "CF " << this->m_CF << std::endl; - ss << "RW " << this->m_rw << std::endl; - ss << "R0 " << this->m_r0 << std::endl; - ss << "Re " << this->m_re << std::endl; - ss << "connection length " << this->m_connection_length << std::endl; - ss << "skinf " << this->m_skin_factor << std::endl; - ss << "dfactor " << this->m_d_factor << std::endl; - ss << "Ke " << this->m_Ke << std::endl; - ss << "kh " << this->m_Kh << std::endl; - ss << "sat_tableId " << this->sat_tableId << std::endl; - ss << "open_state " << Connection::State2String(this->open_state) << std::endl; - ss << "direction " << Connection::Direction2String(this->direction) << std::endl; - ss << "CTF Source " << Connection::CTFKindToString(this->m_ctfkind) << '\n'; - ss << "segment_nr " << this->segment_number << std::endl; - ss << "center_depth " << this->center_depth << std::endl; - ss << "sort_value" << this->m_sort_value<< std::endl; + + ss << "ijk: " << this->ijk[0] << ',' << this->ijk[1] << ',' << this->ijk[2] << '\n' + << "COMPLNUM " << this->m_complnum << '\n' + << "CF " << this->CF() << '\n' + << "RW " << this->rw() << '\n' + << "R0 " << this->r0() << '\n' + << "Re " << this->re() << '\n' + << "connection length " << this->connectionLength() << '\n' + << "skinf " << this->skinFactor() << '\n' + << "dfactor " << this->dFactor() << '\n' + << "Ke " << this->Ke() << '\n' + << "kh " << this->Kh() << '\n' + << "sat_tableId " << this->sat_tableId << '\n' + << "open_state " << Connection::State2String(this->open_state) << '\n' + << "direction " << Connection::Direction2String(this->direction) << '\n' + << "CTF Source " << Connection::CTFKindToString(this->m_ctfkind) << '\n' + << "segment_nr " << this->segment_number << '\n' + << "center_depth " << this->center_depth << '\n' + << "sort_value" << this->m_sort_value<< '\n'; + if (this->m_injmult.has_value()) { - ss << "INJMULT " << InjMult::InjMultToString(this->m_injmult.value()) << std::endl; + ss << "INJMULT " << InjMult::InjMultToString(this->m_injmult.value()) << '\n'; } + if (this->m_filter_cake.has_value()) { - ss << "FilterCake " << FilterCake::filterCakeToString(this->m_filter_cake.value()) << std::endl; + ss << "FilterCake " << FilterCake::filterCakeToString(this->m_filter_cake.value()) << '\n'; } return ss.str(); -} - - bool Connection::operator==( const Connection& rhs ) const { - return this->ijk == rhs.ijk - && this->m_global_index == rhs.m_global_index - && this->m_complnum == rhs.m_complnum - && this->m_CF == rhs.m_CF - && this->m_rw == rhs.m_rw - && this->m_r0 == rhs.m_r0 - && this->m_re == rhs.m_re - && this->m_connection_length == rhs.m_connection_length - && this->m_skin_factor == rhs.m_skin_factor - && this->m_d_factor == rhs.m_d_factor - && this->m_Ke == rhs.m_Ke - && this->m_injmult == rhs.m_injmult - && this->m_Kh == rhs.m_Kh - && this->sat_tableId == rhs.sat_tableId - && this->open_state == rhs.open_state - && this->direction == rhs.direction - && this->segment_number == rhs.segment_number - && this->center_depth == rhs.center_depth - && this->m_sort_value == rhs.m_sort_value - && this->m_subject_to_welpi == rhs.m_subject_to_welpi - && this->m_filter_cake == rhs.m_filter_cake; } - bool Connection::operator!=( const Connection& rhs ) const { - return !( *this == rhs ); + bool Connection::operator==(const Connection& that) const + { + return (this->direction == that.direction) + && (this->open_state == that.open_state) + && (this->sat_tableId == that.sat_tableId) + && (this->m_complnum == that.m_complnum) + && (this->m_ctfkind == that.m_ctfkind) + && (this->m_global_index == that.m_global_index) + && (this->m_sort_value == that.m_sort_value) + && (this->m_defaultSatTabId == that.m_defaultSatTabId) + && (this->segment_number == that.segment_number) + && (this->m_subject_to_welpi == that.m_subject_to_welpi) + && (this->ijk == that.ijk) + && (this->m_injmult == that.m_injmult) + && (this->center_depth == that.center_depth) + && (this->m_perf_range == that.m_perf_range) + && (this->ctf_properties_ == that.ctf_properties_) + && (this->m_filter_cake == that.m_filter_cake) + ; } - -const std::string Connection::State2String( State enumValue ) { - switch( enumValue ) { +std::string Connection::State2String(State enumValue) +{ + switch (enumValue) { case State::OPEN: return "OPEN"; + case State::AUTO: return "AUTO"; + case State::SHUT: return "SHUT"; + default: - throw std::invalid_argument("Unhandled enum value"); + throw std::invalid_argument { + "Unhandled Connection::State value " + + std::to_string(static_cast(enumValue)) + }; } } - -Connection::State Connection::StateFromString( const std::string& stringValue ) { +Connection::State +Connection::StateFromString(std::string_view stringValue) +{ if (stringValue == "OPEN") return State::OPEN; - else if (stringValue == "SHUT") - return State::SHUT; - else if (stringValue == "STOP") - return State::SHUT; - else if (stringValue == "AUTO") - return State::AUTO; - else - throw std::invalid_argument("Unknown enum state string: " + stringValue ); -} + if (stringValue == "SHUT") + return State::SHUT; + + if (stringValue == "STOP") + return State::SHUT; + + if (stringValue == "AUTO") + return State::AUTO; + + throw std::invalid_argument { + "Unknown Connection::State string: " + std::string { stringValue } + }; +} std::string Connection::Direction2String(const Direction enumValue) @@ -434,8 +516,7 @@ std::string Connection::Direction2String(const Direction enumValue) return stringValue; } - -Connection::Direction Connection::DirectionFromString(const std::string& s ) +Connection::Direction Connection::DirectionFromString(std::string_view s) { Direction direction; @@ -443,47 +524,61 @@ Connection::Direction Connection::DirectionFromString(const std::string& s ) else if ((s == "Y") || (s == "y")) { direction = Direction::Y; } else if ((s == "Z") || (s == "z")) { direction = Direction::Z; } else { - std::string msg = "Unsupported completion direction " + s; - throw std::invalid_argument(msg); + throw std::invalid_argument { + "Unsupported completion direction " + + std::string { s } + }; } return direction; } -const std::string Connection::Order2String( Order enumValue ) { - switch( enumValue ) { +std::string Connection::Order2String(Order enumValue) +{ + switch (enumValue) { case Order::DEPTH: return "DEPTH"; + case Order::INPUT: return "INPUT"; + case Order::TRACK: return "TRACK"; + default: - throw std::invalid_argument("Unhandled enum value"); + throw std::invalid_argument { + "Unhandled Connection::Order value " + + std::to_string(static_cast(enumValue)) + }; } } - -Connection::Order Connection::OrderFromString(const std::string& stringValue ) { +Connection::Order Connection::OrderFromString(std::string_view stringValue) +{ if (stringValue == "DEPTH") return Order::DEPTH; - else if (stringValue == "INPUT") + + if (stringValue == "INPUT") return Order::INPUT; - else if (stringValue == "TRACK") + + if (stringValue == "TRACK") return Order::TRACK; - else - throw std::invalid_argument("Unknown enum state string: " + stringValue ); + + throw std::invalid_argument { + "Unknown Connection::Order string: " + std::string { stringValue } + }; } + std::string Connection::CTFKindToString(const CTFKind ctf_kind) { switch (ctf_kind) { - case CTFKind::DeckValue: - return "DeckValue"; + case CTFKind::DeckValue: + return "DeckValue"; - case CTFKind::Defaulted: - return "Defaulted"; + case CTFKind::Defaulted: + return "Defaulted"; } throw std::invalid_argument { @@ -492,58 +587,62 @@ std::string Connection::CTFKindToString(const CTFKind ctf_kind) }; } -Connection::CTFKind Connection::kind() const { +Connection::CTFKind Connection::kind() const +{ return m_ctfkind; } -const InjMult& Connection::injmult() const { +const InjMult& Connection::injmult() const +{ assert(this->activeInjMult()); return m_injmult.value(); } -bool Connection::activeInjMult() const { +bool Connection::activeInjMult() const +{ return this->m_injmult.has_value(); } -void Connection::setInjMult(const InjMult& inj_mult) { +void Connection::setInjMult(const InjMult& inj_mult) +{ m_injmult = inj_mult; } - -void Connection::setFilterCake(const FilterCake& filter_cake) { - this->m_filter_cake = filter_cake; +void Connection::setFilterCake(const FilterCake& filter_cake) +{ + this->m_filter_cake = filter_cake; } -bool Connection::filterCakeActive() const { +bool Connection::filterCakeActive() const +{ return this->m_filter_cake.has_value(); } -const FilterCake& Connection::getFilterCake() const { - assert(this->filterCakeActive()); - return this->m_filter_cake.value(); +const FilterCake& Connection::getFilterCake() const +{ + assert(this->filterCakeActive()); + return this->m_filter_cake.value(); } - -double Connection::getFilterCakeRadius() const { - if (this->getFilterCake().radius.has_value()) { - return this->getFilterCake().radius.value(); - } else { - return this->m_rw; +double Connection::getFilterCakeRadius() const +{ + if (const auto& radius = this->getFilterCake().radius; radius.has_value()) { + return *radius; + } + else { + return this->rw(); } } - -double Connection::getFilterCakeArea() const { - if (this->getFilterCake().flow_area.has_value()) { - return this->getFilterCake().flow_area.value(); - } else { - const double radius = this->getFilterCakeRadius(); - const double length = this->m_connection_length; - constexpr double pi = 3.14159265; - return 2. * pi * radius * length; +double Connection::getFilterCakeArea() const +{ + if (const auto& flow_area = this->getFilterCake().flow_area; flow_area.has_value()) { + return *flow_area; + } + else { + constexpr double two_pi = 2 * 3.14159265358979323846264; + return two_pi * this->getFilterCakeRadius() * this->connectionLength(); } } - - } // end of namespace Opm diff --git a/src/opm/input/eclipse/Schedule/Well/Well.cpp b/src/opm/input/eclipse/Schedule/Well/Well.cpp index 37d77dfaa..84ef0354d 100644 --- a/src/opm/input/eclipse/Schedule/Well/Well.cpp +++ b/src/opm/input/eclipse/Schedule/Well/Well.cpp @@ -40,13 +40,12 @@ #include #include - - #include #include #include +#include #include #include @@ -1300,42 +1299,60 @@ bool Well::handleWELOPENConnections(const DeckRecord& record, Connection::State return this->updateConnections(std::move(new_connections), false); } -bool Well::handleCSKINConnections(const DeckRecord& record) { - // Lambda expression to check if record coordinates match connection coordinates - auto match = [=]( const Connection &c) -> bool { - if (!match_eq(c.getI(), record, "I" , -1)) return false; - if (!match_eq(c.getJ(), record, "J" , -1)) return false; - if (!match_ge(c.getK(), record, "K_UPPER", -1)) return false; - if (!match_le(c.getK(), record, "K_LOWER", -1)) return false; +bool Well::handleCSKIN(const DeckRecord& record, + const KeywordLocation& location) +{ + using Kw = ParserKeywords::CSKIN; - return true; + auto need_skin_adjustment = [&record](const Connection &c) { + const auto value_shift = -1; + + return match_eq(c.getI(), record, Kw::I::itemName, value_shift) + && match_eq(c.getJ(), record, Kw::J::itemName, value_shift) + && match_ge(c.getK(), record, Kw::K_UPPER::itemName, value_shift) + && match_le(c.getK(), record, Kw::K_LOWER::itemName, value_shift); }; - // Generate a new connection which will be updated with new connection skin factor - auto new_connections = std::make_shared(this->connections->ordering(), this->headI, this->headJ); + // New connection set which will be updated with new connection level + // skin factors. + auto new_connections = std::make_shared + (this->connections->ordering(), this->headI, this->headJ); - // Update skin factor - double skin_factor = record.getItem("CONNECTION_SKIN_FACTOR").get(0); - const double angle = 6.2831853071795864769252867665590057683943387987502116419498; - for (auto c : *this->connections) { - if (match(c)) { - // Check for potential negative new CF - if ((std::log(c.r0() / std::min(c.rw(), c.r0())) + skin_factor) < 0.0) { - throw std::runtime_error("Negative connection transmissibility factor produced by CSKIN for well " - + name()); - } - - // Calculate new connection transmissibility factor - double CF = angle * c.Kh() / (std::log(c.r0() / std::min(c.rw(), c.r0())) + skin_factor); - - // Apply last known WPIMULT (defaulted to 1.0) - CF *= c.wpimult(); - - // Set skin factor and connection factor - c.setSkinFactor(skin_factor); - c.setCF(CF); + const auto skin_factor = record.getItem().getSIDouble(0); + for (const auto& connection : *this->connections) { + if (! need_skin_adjustment(connection)) { + // No CSKIN adjustment needed here. Include connection as-is + // into new connection set. + new_connections->add(connection); + continue; } - new_connections->add(c); + + // If we get here, we must make the connection's skin factor be + // 'skin_factor'. + // + // First guard against this adjustment making the CTF go negative, + // typically because the 'skin_factor' value is large and negative + // itself. + if (const auto& ctf_props = connection.ctfProperties(); + ctf_props.peaceman_denom + skin_factor - ctf_props.skin_factor < 0.0) + { + throw OpmInputError { + fmt::format("Negative connection transmissibility " + "factor generated by skin factor {} " + "in connection ({},{},{}) for well {}.", + skin_factor, + connection.getI() + 1, + connection.getJ() + 1, + connection.getK() + 1, + this->name()), + location + }; + } + + auto connection_copy = connection; + connection_copy.setSkinFactor(skin_factor); + + new_connections->add(connection_copy); } return this->updateConnections(std::move(new_connections), false); @@ -1390,6 +1407,7 @@ bool Well::handleWPIMULT(const DeckRecord& record) { new_connections->add(c); } + return this->updateConnections(std::move(new_connections), false); } diff --git a/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp b/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp index 582ac9cd6..38ffcb58b 100644 --- a/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp +++ b/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp @@ -19,22 +19,32 @@ #include +#include + #include #include #include - -#include +#include #include -#include #include -#include +#include + #include +#include +#include +#include #include #include +#include +#include +#include +#include +#include + #include #include #include @@ -46,28 +56,9 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include -#include -#include -#include -#include -#include -#include +#include namespace { @@ -150,50 +141,72 @@ namespace { return 0.28 * (num / den); } + double peacemanDenominator(const double r0, + const double rw, + const double skin_factor) + { + return std::log(r0 / std::min(rw, r0)) + skin_factor; + } + + double peacemanDenominator(const Opm::Connection::CTFProperties& ctf_props) + { + return peacemanDenominator(ctf_props.r0, ctf_props.rw, ctf_props.skin_factor); + } + // Calculate permeability thickness Kh for line segment in a cell for x,y,z directions std::array - permThickness(const external::cvf::Vec3d& connection_vector, + permThickness(const external::cvf::Vec3d& effective_connection, const std::array& cell_perm, const double ntg) { - std::array perm_thickness; - Opm::Connection::Direction direction[3] = {Opm::Connection::DirectionFromString("X"), - Opm::Connection::DirectionFromString("Y"), - Opm::Connection::DirectionFromString("Z")}; - external::cvf::Vec3d effective_connection = connection_vector; - effective_connection[2] *= ntg; - for (size_t i = 0; i < 3; ++i) - { - const auto& K = permComponents(direction[i], cell_perm); - perm_thickness[i] = std::sqrt(K[0] * K[1]) * effective_connection[i]; + auto perm_thickness = std::array { + effective_connection[0], + effective_connection[1], + effective_connection[2] * ntg, + }; + + const auto direction = std::array { + Opm::Connection::Direction::X, + Opm::Connection::Direction::Y, + Opm::Connection::Direction::Z, + }; + + for (size_t i = 0; i < 3; ++i) { + const auto K = permComponents(direction[i], cell_perm); + perm_thickness[i] *= std::sqrt(K[0] * K[1]); } + return perm_thickness; } // Calculate directional (x,y,z) peaceman connection factors CFx, CFy, CFz std::array - connectionFactor(const external::cvf::Vec3d& connection_vector, - const std::array& cell_perm, - std::array cell_size, + connectionFactor(const std::array& cell_perm, + const std::array& cell_size, const double ntg, - const std::array Kh, + const std::array& Kh, const double rw, const double skin_factor) { - std::array connection_factor; - Opm::Connection::Direction direction[3] = {Opm::Connection::DirectionFromString("X"), - Opm::Connection::DirectionFromString("Y"), - Opm::Connection::DirectionFromString("Z")}; - external::cvf::Vec3d effective_connection = connection_vector; - effective_connection[2] *= ntg; - for (size_t i = 0; i < 3; ++i) - { - const double angle = 6.2831853071795864769252867665590057683943387987502116419498; - const auto& K = permComponents(direction[i], cell_perm); - const auto& D = effectiveExtent(direction[i], ntg, cell_size); - const auto& r0 = effectiveRadius(K,D); - connection_factor[i] = angle * Kh[i] / (std::log(r0 / std::min(rw, r0)) + skin_factor); + auto connection_factor = std::array{}; + + const auto direction = std::array { + Opm::Connection::Direction::X, + Opm::Connection::Direction::Y, + Opm::Connection::Direction::Z, + }; + + const double angle = 6.2831853071795864769252867665590057683943387987502116419498; + + for (size_t i = 0; i < 3; ++i) { + const auto K = permComponents(direction[i], cell_perm); + const auto D = effectiveExtent(direction[i], ntg, cell_size); + const auto r0 = effectiveRadius(K, D); + + connection_factor[i] = angle * Kh[i] + / peacemanDenominator(r0, rw, skin_factor); } + return connection_factor; } @@ -285,61 +298,40 @@ namespace Opm { } void WellConnections::addConnection(const int i, const int j, const int k, - const std::size_t global_index, - const int complnum, - const double depth, - const Connection::State state, - const double CF, - const double Kh, - const double rw, - const double r0, - const double re, - const double connection_length, - const double skin_factor, - const double d_factor, - const double Ke, - const int satTableId, - const Connection::Direction direction, - const Connection::CTFKind ctf_kind, - const std::size_t seqIndex, - const bool defaultSatTabId) + const std::size_t global_index, + const int complnum, + const Connection::State state, + const double depth, + const Connection::CTFProperties& ctf_props, + const int satTableId, + const Connection::Direction direction, + const Connection::CTFKind ctf_kind, + const std::size_t seqIndex, + const bool defaultSatTabId) { const int conn_i = (i < 0) ? this->headI : i; const int conn_j = (j < 0) ? this->headJ : j; - Connection conn(conn_i, conn_j, k, - global_index, complnum, - depth, state, CF, Kh, rw, r0, re, connection_length, - skin_factor, d_factor, Ke, satTableId, direction, ctf_kind, - seqIndex, defaultSatTabId); - - this->add(conn); + this->m_connections.emplace_back(conn_i, conn_j, k, global_index, complnum, + state, direction, ctf_kind, satTableId, + depth, ctf_props, seqIndex, defaultSatTabId); } void WellConnections::addConnection(const int i, const int j, const int k, - const std::size_t global_index, - const double depth, - const Connection::State state, - const double CF, - const double Kh, - const double rw, - const double r0, - const double re, - const double connection_length, - const double skin_factor, - const double d_factor, - const double Ke, - const int satTableId, - const Connection::Direction direction, - const Connection::CTFKind ctf_kind, - const std::size_t seqIndex, - const bool defaultSatTabId) + const std::size_t global_index, + const Connection::State state, + const double depth, + const Connection::CTFProperties& ctf_props, + const int satTableId, + const Connection::Direction direction, + const Connection::CTFKind ctf_kind, + const std::size_t seqIndex, + const bool defaultSatTabId) { - const int complnum = (this->m_connections.size() + 1); + const auto complnum = static_cast(this->m_connections.size()) + 1; - this->addConnection(i, j, k, global_index, complnum, - depth, state, CF, Kh, rw, r0, re, - connection_length, skin_factor, d_factor, Ke, + this->addConnection(i, j, k, global_index, complnum, state, + depth, ctf_props, satTableId, direction, ctf_kind, seqIndex, defaultSatTabId); } @@ -349,435 +341,431 @@ namespace Opm { const std::string& wname, const KeywordLocation& location) { + const auto& itemI = record.getItem("I"); + const auto defaulted_I = itemI.defaultApplied(0) || (itemI.get(0) == 0); + const auto I = !defaulted_I ? itemI.get(0) - 1 : this->headI; - const auto& itemI = record.getItem( "I" ); - const auto defaulted_I = itemI.defaultApplied( 0 ) || itemI.get< int >( 0 ) == 0; - const int I = !defaulted_I ? itemI.get< int >( 0 ) - 1 : this->headI; + const auto& itemJ = record.getItem("J"); + const auto defaulted_J = itemJ.defaultApplied(0) || (itemJ.get(0) == 0); + const auto J = !defaulted_J ? itemJ.get(0) - 1 : this->headJ; - const auto& itemJ = record.getItem( "J" ); - const auto defaulted_J = itemJ.defaultApplied( 0 ) || itemJ.get< int >( 0 ) == 0; - const int J = !defaulted_J ? itemJ.get< int >( 0 ) - 1 : this->headJ; + const auto K1 = record.getItem("K1").get(0) - 1; + const auto K2 = record.getItem("K2").get(0) - 1; + const auto state = Connection::StateFromString(record.getItem("STATE").getTrimmedString(0)); - int K1 = record.getItem("K1").get< int >(0) - 1; - int K2 = record.getItem("K2").get< int >(0) - 1; - Connection::State state = Connection::StateFromString( record.getItem("STATE").getTrimmedString(0) ); - - int satTableId = -1; - bool defaultSatTable = true; const auto& r0Item = record.getItem("PR"); const auto& CFItem = record.getItem("CONNECTION_TRANSMISSIBILITY_FACTOR"); const auto& diameterItem = record.getItem("DIAMETER"); const auto& KhItem = record.getItem("Kh"); const auto& satTableIdItem = record.getItem("SAT_TABLE"); const auto direction = Connection::DirectionFromString(record.getItem("DIR").getTrimmedString(0)); - double skin_factor = record.getItem("SKIN").getSIDouble(0); - - double d_factor = record.getItem("D_FACTOR").getSIDouble(0); - double rw; + const auto skin_factor = record.getItem("SKIN").getSIDouble(0); + const auto d_factor = record.getItem("D_FACTOR").getSIDouble(0); - if (satTableIdItem.hasValue(0) && satTableIdItem.get < int > (0) > 0) - { - satTableId = satTableIdItem.get< int >(0); + int satTableId = -1; + bool defaultSatTable = true; + if (satTableIdItem.hasValue(0) && (satTableIdItem.get(0) > 0)) { + satTableId = satTableIdItem.get(0); defaultSatTable = false; } - if (diameterItem.hasValue(0)) - rw = 0.50 * diameterItem.getSIDouble(0); - else + double rw{}; + if (diameterItem.hasValue(0)) { + rw = diameterItem.getSIDouble(0) / 2; + } + else { // The Eclipse100 manual does not specify a default value for the wellbore // diameter, but the Opm codebase has traditionally implemented a default // value of one foot. The same default value is used by Eclipse300. rw = 0.5*unit::feet; + } - for (int k = K1; k <= K2; k++) { - const CompletedCells::Cell& cell = grid.get_cell(I, J, k); + // Angle of completion exposed to flow. We assume centre + // placement so there's complete exposure (= 2\pi). + const auto angle = 6.2831853071795864769252867665590057683943387987502116419498; + + for (int k = K1; k <= K2; ++k) { + const auto& cell = grid.get_cell(I, J, k); if (!cell.is_active()) { - auto msg = fmt::format("Problem with COMPDAT keyword\n" - "In {} line {}\n" - "The cell ({},{},{}) in well {} is not active and the connection will be ignored", location.filename, location.lineno, I,J,k, wname); + auto msg = fmt::format(R"(Problem with COMPDAT keyword +In {} line {} +The cell ({},{},{}) in well {} is not active and the connection will be ignored)", + location.filename, location.lineno, + I + 1, J + 1, k + 1, wname); + OpmLog::warning(msg); continue; } + const auto& props = cell.props; - double CF = -1; - double Kh = -1; - double r0 = -1; - auto ctf_kind = ::Opm::Connection::CTFKind::DeckValue; - if (defaultSatTable) + auto ctf_props = ::Opm::Connection::CTFProperties{}; + ctf_props.rw = rw; + ctf_props.skin_factor = skin_factor; + ctf_props.d_factor = d_factor; + + if (defaultSatTable) { satTableId = props->satnum; + } - auto same_ijk = [&]( const Connection& c ) { - return c.sameCoordinate( I,J,k ); - }; + ctf_props.r0 = -1.0; + if (r0Item.hasValue(0)) { + ctf_props.r0 = r0Item.getSIDouble(0); + } - if (r0Item.hasValue(0)) - r0 = r0Item.getSIDouble(0); + ctf_props.Kh = -1.0; + if (KhItem.hasValue(0) && (KhItem.getSIDouble(0) > 0.0)) { + ctf_props.Kh = KhItem.getSIDouble(0); + } - if (KhItem.hasValue(0) && KhItem.getSIDouble(0) > 0.0) - Kh = KhItem.getSIDouble(0); + ctf_props.CF = -1.0; + if (CFItem.hasValue(0) && (CFItem.getSIDouble(0) > 0.0)) { + ctf_props.CF = CFItem.getSIDouble(0); + } - if (CFItem.hasValue(0) && CFItem.getSIDouble(0) > 0.0) - CF = CFItem.getSIDouble(0); + const auto D = effectiveExtent(direction, props->ntg, cell.dimensions); + const auto K = permComponents(direction, { props->permx, props->permy, props->permz }); + ctf_props.Ke = std::sqrt(K[0] * K[1]); - // Angle of completion exposed to flow. We assume centre - // placement so there's complete exposure (= 2\pi). - const double angle = 6.2831853071795864769252867665590057683943387987502116419498; - std::array cell_size = cell.dimensions; - const auto& D = effectiveExtent(direction, props->ntg, cell_size); + const auto ctf_kind = (ctf_props.CF < 0.0) + ? ::Opm::Connection::CTFKind::Defaulted + : ::Opm::Connection::CTFKind::DeckValue; - std::array cell_perm = {{ props->permx, - props->permy, - props->permz}}; - const auto& K = permComponents(direction, cell_perm); - double Ke = std::sqrt(K[0] * K[1]); - - /* We start with the absolute happy path; both CF and Kh are explicitly given in the deck. */ - if (CF > 0 && Kh > 0) + // We start with the absolute happy path; both CF and Kh are + // explicitly given in the deck. + if ((ctf_props.CF > 0.0) && (ctf_props.Kh > 0.0)) { + ctf_props.peaceman_denom = angle * ctf_props.Kh / ctf_props.CF; goto CF_done; + } - /* We must calculate CF and Kh from the items in the COMPDAT record and cell properties. */ + // We must calculate CF and Kh from the items in the COMPDAT + // record and cell properties. + if (ctf_props.r0 < 0.0) { + ctf_props.r0 = effectiveRadius(K, D); + } + + if (const auto peaceman_denom = peacemanDenominator(ctf_props); + ctf_props.Kh > 0.0) { - if (r0 < 0) - r0 = effectiveRadius(K,D); - if (CF < 0) { - if (Kh < 0) - Kh = Ke * D[2]; - CF = angle * Kh / (std::log(r0 / std::min(rw, r0)) + skin_factor); - ctf_kind = ::Opm::Connection::CTFKind::Defaulted; - } else { - if (KhItem.defaultApplied(0) || KhItem.getSIDouble(0) < 0) { - Kh = CF * (std::log(r0 / std::min(r0, rw)) + skin_factor) / angle; - } else { - if (Kh < 0) { - Kh = Ke * D[2]; - - // Compute r0 to be consistent with other parameters - r0 = RestartIO::RstConnection::inverse_peaceman(CF, Kh, rw, skin_factor); - } - } + // CF < 0 + ctf_props.CF = angle * ctf_props.Kh / peaceman_denom; + ctf_props.peaceman_denom = peaceman_denom; + } + else if (ctf_props.CF > 0.0) { // Kh < 0 + if (KhItem.defaultApplied(0) || (KhItem.get(0) < 0.0)) { + // Kh explicitly defaulted. Derive compatible Kh value + // from specified CTF, r0, rw, and skin factor. + ctf_props.Kh = ctf_props.CF * peaceman_denom / angle; } + else { + // Kh = 0 entered in item 10 of COMPDAT. Compute Kh + // from permeability and length of perforation interval + // and request a compatible pressure equivalent radius + // (r0) be calculated. + ctf_props.Kh = ctf_props.Ke * D[2]; + + // Defer r0 calculation to CF_done: r0 < 0 case. + ctf_props.r0 = -1.0; + } + + ctf_props.peaceman_denom = angle * ctf_props.Kh / ctf_props.CF; + } + else { // (CF < 0) && (Kh < 0) + ctf_props.Kh = ctf_props.Ke * D[2]; + ctf_props.CF = angle * ctf_props.Kh / peaceman_denom; + ctf_props.peaceman_denom = peaceman_denom; } CF_done: - if (r0 < 0) - r0 = RestartIO::RstConnection::inverse_peaceman(CF, Kh, rw, skin_factor); + if (ctf_props.r0 < 0.0) { + ctf_props.r0 = RestartIO::RstConnection:: + inverse_peaceman(ctf_props.CF, ctf_props.Kh, + ctf_props.rw, + ctf_props.skin_factor); + } - // used by the PolymerMW module - double re = std::sqrt(D[0] * D[1] / angle * 2); // area equivalent radius of the grid block - double connection_length = D[2]; // the length of the well perforation + // Length of the well perforation interval. + ctf_props.connection_length = ctf_props.Kh / ctf_props.Ke; + + // Area equivalent radius of the grid block. Used by the + // PolymerMW module. + ctf_props.re = std::sqrt(D[0] * D[1] / angle * 2); + + auto prev = std::find_if(this->m_connections.begin(), + this->m_connections.end(), + [I, J, k](const Connection& c) + { + return c.sameCoordinate(I, J, k); + }); - auto prev = std::find_if( this->m_connections.begin(), - this->m_connections.end(), - same_ijk ); if (prev == this->m_connections.end()) { - std::size_t noConn = this->m_connections.size(); - this->addConnection(I,J,k, - cell.global_index, - cell.depth, - state, - CF, - Kh, - rw, - r0, - re, - connection_length, - skin_factor, - d_factor, - Ke, - satTableId, - direction, - ctf_kind, - noConn, - defaultSatTable); - } else { - std::size_t css_ind = prev->sort_value(); - int conSegNo = prev->segment(); - const auto& perf_range = prev->perf_range(); - double depth = cell.depth; - *prev = Connection(I,J,k, - cell.global_index, - prev->complnum(), - depth, - state, - CF, - Kh, - rw, - r0, - re, - connection_length, - skin_factor, - d_factor, - Ke, - satTableId, - direction, - ctf_kind, - prev->sort_value(), - defaultSatTable); + const std::size_t noConn = this->m_connections.size(); + this->addConnection(I, J, k, cell.global_index, state, + cell.depth, ctf_props, satTableId, + direction, ctf_kind, + noConn, defaultSatTable); + } + else { + const auto compl_num = prev->complnum(); + const auto css_ind = prev->sort_value(); + const auto conSegNo = prev->segment(); + const auto perf_range = prev->perf_range(); - prev->updateSegment(conSegNo, - depth, - css_ind, - perf_range); + *prev = Connection { + I, J, k, cell.global_index, compl_num, + state, direction, ctf_kind, satTableId, + cell.depth, ctf_props, + css_ind, defaultSatTable + }; + + prev->updateSegment(conSegNo, cell.depth, css_ind, perf_range); } } } - - void WellConnections::loadCOMPTRAJ(const DeckRecord& record, - const ScheduleGrid& grid, - const std::string& wname, - const KeywordLocation& location, - external::cvf::ref& cellSearchTree) { - // const std::string& completionNamePattern = record.getItem("BRANCH_NUMBER").getTrimmedString(0); + void WellConnections::loadCOMPTRAJ(const DeckRecord& record, + const ScheduleGrid& grid, + const std::string& wname, + const KeywordLocation& location, + external::cvf::ref& cellSearchTree) + { const auto& perf_top = record.getItem("PERF_TOP"); const auto& perf_bot = record.getItem("PERF_BOT"); const auto& CFItem = record.getItem("CONNECTION_TRANSMISSIBILITY_FACTOR"); const auto& diameterItem = record.getItem("DIAMETER"); const auto& KhItem = record.getItem("Kh"); - double skin_factor = record.getItem("SKIN").getSIDouble(0); - double d_factor = record.getItem("D_FACTOR").getSIDouble(0); + const auto skin_factor = record.getItem("SKIN").getSIDouble(0); + const auto d_factor = record.getItem("D_FACTOR").getSIDouble(0); const auto& satTableIdItem = record.getItem("SAT_TABLE"); - Connection::State state = Connection::StateFromString(record.getItem("STATE").getTrimmedString(0)); + const auto state = Connection::StateFromString(record.getItem("STATE").getTrimmedString(0)); int satTableId = -1; bool defaultSatTable = true; - if (satTableIdItem.hasValue(0) && satTableIdItem.get < int > (0) > 0) - { - satTableId = satTableIdItem.get< int >(0); + if (satTableIdItem.hasValue(0) && (satTableIdItem.get(0) > 0)) { + satTableId = satTableIdItem.get(0); defaultSatTable = false; } - double rw; - if (diameterItem.hasValue(0)) - rw = 0.50 * diameterItem.getSIDouble(0); - else + double rw{}; + if (diameterItem.hasValue(0)) { + rw = diameterItem.getSIDouble(0) / 2; + } + else { // The Eclipse100 manual does not specify a default value for the wellbore // diameter, but the Opm codebase has traditionally implemented a default // value of one foot. The same default value is used by Eclipse300. rw = 0.5*unit::feet; + } - // Get the grid - auto ecl_grid = grid.get_grid(); + // Get the grid + const auto& ecl_grid = grid.get_grid(); // Calulate the x,y,z coordinates of the begin and end of a perforation external::cvf::Vec3d p_top; external::cvf::Vec3d p_bot; for (size_t i = 0; i < 3 ; ++i) { - p_top[i] = Opm::linearInterpolation(this->md, this->coord[i], perf_top.getSIDouble(0)); - p_bot[i] = Opm::linearInterpolation(this->md, this->coord[i], perf_bot.getSIDouble(0)); + p_top[i] = linearInterpolation(this->md, this->coord[i], perf_top.getSIDouble(0)); + p_bot[i] = linearInterpolation(this->md, this->coord[i], perf_bot.getSIDouble(0)); } std::vector points{p_top, p_bot}; std::vector md_interval{perf_top.getSIDouble(0), perf_bot.getSIDouble(0)}; - - external::cvf::ref wellPathGeometry = new external::RigWellPath; + + external::cvf::ref wellPathGeometry { new external::RigWellPath }; wellPathGeometry->setWellPathPoints(points); wellPathGeometry->setMeasuredDepths(md_interval); - external::cvf::ref e = new external::RigEclipseWellLogExtractor(wellPathGeometry.p(), *ecl_grid, cellSearchTree); - - // Keep the AABB search tree of the grid to avoid redoing an expensive calulation + + external::cvf::ref e { + new external::RigEclipseWellLogExtractor { + wellPathGeometry.p(), *ecl_grid, cellSearchTree + } + }; + + // Keep the AABB search tree of the grid to avoid redoing an + // expensive calulation. cellSearchTree = e->getCellSearchTree(); - // This gives the intersected grid cells IJK, cell face entrance & exit cell face point and connection length + // This gives the intersected grid cells IJK, cell face entrance & + // exit cell face point and connection length. auto intersections = e->cellIntersectionInfosAlongWellPath(); - int I{0}; - int J{0}; - int k{0}; - for (size_t is = 0; is < intersections.size(); ++is){ - auto ijk = std::array{}; - ijk = ecl_grid->getIJK(intersections[is].globCellIndex); - I = ijk[0]; - J = ijk[1]; - k = ijk[2]; + for (size_t is = 0; is < intersections.size(); ++is) { + const auto ijk = ecl_grid->getIJK(intersections[is].globCellIndex); - // When using WELTRAJ & COMPTRAJ one may use default settings in WELSPECS for headI/J and let the - // headI/J be calculated by the trajectory data. - // If these defaults are used the headI/J are set to the first intersection. + // When using WELTRAJ & COMPTRAJ one may use default settings in + // WELSPECS for headI/J and let the headI/J be calculated by the + // trajectory data. + // + // If these defaults are used the headI/J are set to the first + // intersection. if (is == 0) { - if (this->headI == -1) - this->headI = I; - if (this->headJ == -1) - this->headJ = J; + if (this->headI < 0) { this->headI = ijk[0]; } + if (this->headJ < 0) { this->headJ = ijk[1]; } } - external::cvf::Vec3d connection_vector = intersections[is].intersectionLengthsInCellCS; + const auto& cell = grid.get_cell(ijk[0], ijk[1], ijk[2]); - const CompletedCells::Cell& cell = grid.get_cell(I, J, k); - - if (!cell.is_active()) { - auto msg = fmt::format("Problem with COMPTRAJ keyword\n" - "In {} line {}\n" - "The cell ({},{},{}) in well {} is not active and the connection will be ignored", location.filename, location.lineno, I,J,k, wname); + if (!cell.is_active()) { + const auto msg = fmt::format(R"(Problem with COMPTRAJ keyword +In {} line {} +The cell ({},{},{}) in well {} is not active and the connection will be ignored)", + location.filename, + location.lineno, + ijk[0] + 1, + ijk[1] + 1, + ijk[2] + 1, wname); OpmLog::warning(msg); + continue; } + const auto& props = cell.props; - double CF = -1; - double Kh = -1; - double r0 = -1; - auto ctf_kind = ::Opm::Connection::CTFKind::DeckValue; - if (defaultSatTable) - satTableId = props->satnum; + auto ctf_props = Connection::CTFProperties{}; + ctf_props.rw = rw; + ctf_props.skin_factor = skin_factor; + ctf_props.d_factor = d_factor; - auto same_ijk = [I, J, k]( const Connection& c ) { - return c.sameCoordinate( I,J,k ); + ctf_props.r0 = -1.0; + ctf_props.Kh = -1.0; + if (KhItem.hasValue(0) && (KhItem.getSIDouble(0) > 0.0)) { + ctf_props.Kh = KhItem.getSIDouble(0); + } + + ctf_props.CF = -1.0; + if (CFItem.hasValue(0) && (CFItem.getSIDouble(0) > 0.0)) { + ctf_props.CF = CFItem.getSIDouble(0); + } + + const auto cell_perm = std::array { + props->permx, props->permy, props->permz }; - if (KhItem.hasValue(0) && KhItem.getSIDouble(0) > 0.0) - Kh = KhItem.getSIDouble(0); - - if (CFItem.hasValue(0) && CFItem.getSIDouble(0) > 0.0) - CF = CFItem.getSIDouble(0); - - std::array cell_size = cell.dimensions; - - std::array cell_perm = {{ props->permx, - props->permy, - props->permz}}; - - if (CF < 0 && Kh < 0) { - /* We must calculate CF and Kh from the items in the COMPTRAJ record and cell properties. */ + auto ctf_kind = ::Opm::Connection::CTFKind::DeckValue; + if ((ctf_props.CF < 0.0) && (ctf_props.Kh < 0.0)) { + // We must calculate CF and Kh from the items in the + // COMPTRAJ record and cell properties. ctf_kind = ::Opm::Connection::CTFKind::Defaulted; + const auto& connection_vector = + intersections[is].intersectionLengthsInCellCS; - const auto& perm_thickness = permThickness(connection_vector, - cell_perm, - props->ntg); + const auto perm_thickness = + permThickness(connection_vector, cell_perm, props->ntg); - const auto& connection_factor = connectionFactor(connection_vector, - cell_perm, - cell_size, - props->ntg, - perm_thickness, - rw, - skin_factor); + const auto connection_factor = + connectionFactor(cell_perm, cell.dimensions, props->ntg, + perm_thickness, rw, skin_factor); - CF = std::sqrt(std::pow(connection_factor[0],2)+ std::pow(connection_factor[1],2)+std::pow(connection_factor[2],2)); - // std::cout<<"CF: " << CF << "; CFx: " << connection_factor[0] << " CFy: " << connection_factor[1] << " CFz: " << connection_factor[2] << "\n" < 0.0) && (ctf_props.Kh > 0.0))) { + auto msg = fmt::format(R"(Problem with COMPTRAJ keyword +In {} line {} +CF and Kh items for well {} must both be specified or both defaulted/negative)", + location.filename, location.lineno, wname); + + throw std::logic_error(msg); + } + + // Todo: check what needs to be done for polymerMW module, see + // loadCOMPDAT used by the PolymerMW module + + const auto direction = ::Opm::Connection::Direction::Z; + + ctf_props.re = -1; + + { + const auto K = permComponents(direction, cell_perm); + ctf_props.Ke = std::sqrt(K[0] * K[1]); + } + + auto prev = std::find_if(this->m_connections.begin(), + this->m_connections.end(), + [&ijk](const Connection& c) + { return c.sameCoordinate(ijk[0], ijk[1], ijk[2]); }); + + if (prev == this->m_connections.end()) { + const std::size_t noConn = this->m_connections.size(); + this->addConnection(ijk[0], ijk[1], ijk[2], + cell.global_index, state, + cell.depth, ctf_props, satTableId, + direction, ctf_kind, + noConn, defaultSatTable); } else { - if (! (CF > 0 && Kh > 0) ){ - auto msg = fmt::format("Problem with COMPTRAJ keyword\n" - "In {} line {}\n" - "CF and Kh items for well {} must both be specified or both defaulted/negative", - location.filename, location.lineno, wname); - throw std::logic_error(msg); - } - } + const auto compl_num = prev->complnum(); + const auto css_ind = prev->sort_value(); + const auto conSegNo = prev->segment(); + const auto perf_range = prev->perf_range(); - // Todo: check what needs to be done for polymerMW module, see loadCOMPDAT - // used by the PolymerMW module - // double re = std::sqrt(D[0] * D[1] / angle * 2); // area equivalent radius of the grid block - // double connection_length = D[2]; // the length of the well perforation - - const auto direction = Connection::DirectionFromString("Z"); - double re = -1; - double connection_length = connection_vector.length(); - const auto& K = permComponents(direction, cell_perm); - double Ke = std::sqrt(K[0] * K[1]); + *prev = Connection { + ijk[0], ijk[1], ijk[2], + cell.global_index, compl_num, + state, direction, ctf_kind, satTableId, + cell.depth, ctf_props, + css_ind, defaultSatTable + }; - auto prev = std::find_if( this->m_connections.begin(), - this->m_connections.end(), - same_ijk ); - if (prev == this->m_connections.end()) { - std::size_t noConn = this->m_connections.size(); - this->addConnection(I,J,k, - cell.global_index, - cell.depth, - state, - CF, - Kh, - rw, - r0, - re, - connection_length, - skin_factor, - d_factor, - Ke, - satTableId, - direction, - ctf_kind, - noConn, - defaultSatTable); - } else { - std::size_t css_ind = prev->sort_value(); - int conSegNo = prev->segment(); - const auto& perf_range = prev->perf_range(); - double depth = cell.depth; - *prev = Connection(I,J,k, - cell.global_index, - prev->complnum(), - depth, - state, - CF, - Kh, - rw, - r0, - re, - connection_length, - skin_factor, - d_factor, - Ke, - satTableId, - direction, - ctf_kind, - prev->sort_value(), - defaultSatTable); - - prev->updateSegment(conSegNo, - depth, - css_ind, - *perf_range); + prev->updateSegment(conSegNo, cell.depth, css_ind, *perf_range); } } } void WellConnections::loadWELTRAJ(const DeckRecord& record, - const ScheduleGrid& grid, - const std::string& wname, - const KeywordLocation& location) { - (void) grid; //surpress unused argument compile warning - (void) wname; - (void) location; + [[maybe_unused]] const ScheduleGrid& grid, + [[maybe_unused]] const std::string& wname, + [[maybe_unused]] const KeywordLocation& location) + { this->coord[0].push_back(record.getItem("X").getSIDouble(0)); - this->coord[1].push_back(record.getItem("Y").getSIDouble(0)), + this->coord[1].push_back(record.getItem("Y").getSIDouble(0)); this->coord[2].push_back(record.getItem("TVD").getSIDouble(0)); + this->md.push_back(record.getItem("MD").getSIDouble(0)); } - std::size_t WellConnections::size() const { + std::size_t WellConnections::size() const + { return m_connections.size(); } - std::size_t WellConnections::num_open() const { + std::size_t WellConnections::num_open() const + { return std::count_if(this->m_connections.begin(), this->m_connections.end(), [] (const Connection& c) { return c.state() == Connection::State::OPEN; }); } - bool WellConnections::empty() const { + bool WellConnections::empty() const + { return this->size() == size_t{0}; } - const Connection& WellConnections::get(size_t index) const { + const Connection& WellConnections::get(size_t index) const + { return (*this)[index]; } - const Connection& WellConnections::operator[](size_t index) const { + const Connection& WellConnections::operator[](size_t index) const + { return this->m_connections.at(index); } - const Connection& WellConnections::lowest() const { - if (this->m_connections.empty()) + const Connection& WellConnections::lowest() const + { + if (this->m_connections.empty()) { throw std::logic_error("Tried to get lowest connection from empty set"); + } const auto max_iter = std::max_element(this->m_connections.begin(), this->m_connections.end(), @@ -789,79 +777,92 @@ namespace Opm { return *max_iter; } - bool WellConnections::hasGlobalIndex(std::size_t global_index) const { - auto conn_iter = std::find_if(this->begin(), this->end(), - [global_index] (const Connection& conn) {return conn.global_index() == global_index;}); - return (conn_iter != this->end()); + bool WellConnections::hasGlobalIndex(std::size_t global_index) const + { + return std::any_of(this->begin(), this->end(), + [global_index](const Connection& conn) + { + return conn.global_index() == global_index; + }); } - const Connection& WellConnections::getFromIJK(const int i, const int j, const int k) const { + const Connection& + WellConnections::getFromIJK(const int i, const int j, const int k) const + { for (size_t ic = 0; ic < size(); ++ic) { if (get(ic).sameCoordinate(i, j, k)) { return get(ic); } } + throw std::runtime_error(" the connection is not found! \n "); } - const Connection& WellConnections::getFromGlobalIndex(std::size_t global_index) const { - auto conn_iter = std::find_if(this->begin(), this->end(), - [global_index] (const Connection& conn) {return conn.global_index() == global_index;}); + const Connection& WellConnections::getFromGlobalIndex(std::size_t global_index) const + { + auto conn_iter = + std::find_if(this->begin(), this->end(), + [global_index] (const Connection& conn) + { return conn.global_index() == global_index; }); - if (conn_iter == this->end()) + if (conn_iter == this->end()) { throw std::logic_error(fmt::format("No connection with global index {}", global_index)); + } + return *conn_iter; } - Connection& WellConnections::getFromIJK(const int i, const int j, const int k) { - for (size_t ic = 0; ic < size(); ++ic) { - if (get(ic).sameCoordinate(i, j, k)) { - return this->m_connections[ic]; - } - } - throw std::runtime_error(" the connection is not found! \n "); - } - - void WellConnections::add(Connection connection) + Connection& WellConnections::getFromIJK(const int i, const int j, const int k) { - this->m_connections.push_back(std::move(connection)); + for (size_t ic = 0; ic < size(); ++ic) { + if (get(ic).sameCoordinate(i, j, k)) { + return this->m_connections[ic]; + } + } + + throw std::runtime_error(" the connection is not found! \n "); } - bool WellConnections::allConnectionsShut( ) const { - if (this->empty()) + bool WellConnections::allConnectionsShut() const + { + if (this->empty()) { return false; + } - - auto shut = []( const Connection& c ) { - return c.state() == Connection::State::SHUT; - }; - - return std::all_of( this->m_connections.begin(), - this->m_connections.end(), - shut ); + return std::all_of(this->m_connections.begin(), + this->m_connections.end(), + [](const Connection& c) + { return c.state() == Connection::State::SHUT; }); } void WellConnections::order() { - if (m_connections.empty()) + if (m_connections.empty()) { return; + } - if (this->m_connections[0].attachedToSegment()) + if (this->m_connections[0].attachedToSegment()) { this->orderMSW(); - else if (this->m_ordering == Connection::Order::TRACK) + } + else if (this->m_ordering == Connection::Order::TRACK) { this->orderTRACK(); - else if (this->m_ordering == Connection::Order::DEPTH) + } + else if (this->m_ordering == Connection::Order::DEPTH) { this->orderDEPTH(); + } } - void WellConnections::orderMSW() { - std::sort(this->m_connections.begin(), this->m_connections.end(), [](const Opm::Connection& conn1, const Opm::Connection& conn2) + void WellConnections::orderMSW() + { + std::sort(this->m_connections.begin(), this->m_connections.end(), + [](const Opm::Connection& conn1, const Opm::Connection& conn2) { return conn1.sort_value() < conn2.sort_value(); }); } - void WellConnections::orderTRACK() { + void WellConnections::orderTRACK() + { // Find the first connection and swap it into the 0-position. const double surface_z = 0.0; size_t first_index = findClosestConnection(this->headI, this->headJ, surface_z, 0); @@ -870,10 +871,12 @@ namespace Opm { // Repeat for remaining connections. // // Note that since findClosestConnection() is O(n), this is an - // O(n^2) algorithm. However, it should be acceptable since - // the expected number of connections is fairly low (< 100). + // O(n^2) algorithm. However, it should be acceptable since the + // expected number of connections is fairly low (< 100). - if( this->m_connections.empty() ) return; + if (this->m_connections.empty()) { + return; + } for (size_t pos = 1; pos < m_connections.size() - 1; ++pos) { const auto& prev = m_connections[pos - 1]; @@ -912,27 +915,31 @@ namespace Opm { return closest; } - void WellConnections::orderDEPTH() { - std::sort(this->m_connections.begin(), this->m_connections.end(), [](const Opm::Connection& conn1, const Opm::Connection& conn2) + void WellConnections::orderDEPTH() + { + std::sort(this->m_connections.begin(), this->m_connections.end(), + [](const Opm::Connection& conn1, const Opm::Connection& conn2) { return conn1.depth() < conn2.depth(); }); - } - bool WellConnections::operator==( const WellConnections& rhs ) const { - return this->size() == rhs.size() && - this->m_ordering == rhs.m_ordering && - this->coord == rhs.coord && - this->md == rhs.md && - std::equal( this->begin(), this->end(), rhs.begin() ); + bool WellConnections::operator==(const WellConnections& rhs) const + { + return (this->size() == rhs.size()) + && (this->m_ordering == rhs.m_ordering) + && (this->coord == rhs.coord) + && (this->md == rhs.md) + && std::equal( this->begin(), this->end(), rhs.begin()); } - bool WellConnections::operator!=( const WellConnections& rhs ) const { - return !( *this == rhs ); + bool WellConnections::operator!=(const WellConnections& rhs) const + { + return ! (*this == rhs); } - void WellConnections::filter(const ActiveGridCells& grid) { + void WellConnections::filter(const ActiveGridCells& grid) + { auto isInactive = [&grid](const Connection& c) { return !grid.cellActive(c.getI(), c.getJ(), c.getK()); }; @@ -941,7 +948,8 @@ namespace Opm { m_connections.erase(new_end, m_connections.end()); } - double WellConnections::segment_perf_length(int segment) const { + double WellConnections::segment_perf_length(int segment) const + { double perf_length = 0; for (const auto& conn : this->m_connections) { if (conn.segment() == segment) { @@ -949,18 +957,22 @@ namespace Opm { perf_length += end - start; } } + return perf_length; } - int WellConnections::getHeadI() const { + int WellConnections::getHeadI() const + { return this->headI; } - int WellConnections::getHeadJ() const { + int WellConnections::getHeadJ() const + { return this->headJ; } - const std::vector& WellConnections::getMD() const { + const std::vector& WellConnections::getMD() const + { return this->md; } @@ -974,9 +986,10 @@ namespace Opm { return conn.global_index() == global_index; }); - if (connPos == connections.end()) + if (connPos == connections.end()) { // No connection exists with the requisite 'global_index' return {}; + } return { connPos->complnum() }; } diff --git a/tests/parser/ConnectionTests.cpp b/tests/parser/ConnectionTests.cpp index 0f261a337..0fe009226 100644 --- a/tests/parser/ConnectionTests.cpp +++ b/tests/parser/ConnectionTests.cpp @@ -17,32 +17,38 @@ along with OPM. If not, see . */ -#include -#include -#include - #define BOOST_TEST_MODULE CompletionTests #include -#include -#include -#include -#include - #include -#include -#include + +#include + +#include + #include #include -#include #include -#include -#include +#include +#include #include +#include +#include +#include + #include #include +#include + +#include + +#include + +#include +#include +#include namespace { double cp_rm3_per_db() @@ -51,20 +57,31 @@ namespace { / (Opm::unit::day * Opm::unit::barsa); } -Opm::WellConnections loadCOMPDAT(const std::string& compdat_keyword) { - Opm::EclipseGrid grid(10,10,10); - Opm::TableManager tables; - Opm::Parser parser; - const auto deck = parser.parseString(compdat_keyword); - Opm::FieldPropsManager field_props(deck, Opm::Phases{true, true, true}, grid, Opm::TableManager()); - const auto& keyword = deck["COMPDAT"][0]; - Opm::WellConnections connections(Opm::Connection::Order::TRACK, 10,10); - Opm::CompletedCells cells(grid); - for (const auto& rec : keyword) - connections.loadCOMPDAT(rec, Opm::ScheduleGrid(grid, field_props, cells), "WELL", {}); + Opm::WellConnections + loadCOMPDAT(const std::string& compdat_keyword) + { + Opm::WellConnections connections { + Opm::Connection::Order::TRACK, 10, 10 + }; - return connections; -} + const auto deck = Opm::Parser{}.parseString(compdat_keyword); + const auto loc = Opm::KeywordLocation{}; + + const Opm::EclipseGrid grid { 10, 10, 10 }; + const Opm::FieldPropsManager field_props { + deck, Opm::Phases{true, true, true}, grid, Opm::TableManager{} + }; + + // Must be mutable. + Opm::CompletedCells completed_cells(grid); + const auto sg = Opm::ScheduleGrid { grid, field_props, completed_cells }; + + for (const auto& rec : deck["COMPDAT"][0]) { + connections.loadCOMPDAT(rec, sg, "WELL", loc); + } + + return connections; + } } namespace Opm { @@ -94,12 +111,22 @@ BOOST_AUTO_TEST_CASE(CreateWellConnectionsOK) { -BOOST_AUTO_TEST_CASE(AddCompletionSizeCorrect) { - auto dir = Opm::Connection::Direction::Z; +BOOST_AUTO_TEST_CASE(AddCompletionSizeCorrect) +{ + const auto dir = Opm::Connection::Direction::Z; const auto kind = Opm::Connection::CTFKind::DeckValue; + const auto depth = 0.0; + + auto ctf_props = Opm::Connection::CTFProperties{}; + + ctf_props.CF = 99.88; + ctf_props.Kh = 355.113; + ctf_props.rw = 0.25; + + const auto completion1 = Opm::Connection { 10,10,10, 100, 1, Opm::Connection::State::OPEN, dir, kind, 0, depth, ctf_props, 0, true }; + const auto completion2 = Opm::Connection { 10,10,11, 102, 1, Opm::Connection::State::SHUT, dir, kind, 0, depth, ctf_props, 0, true }; + Opm::WellConnections completionSet(Opm::Connection::Order::TRACK, 1,1); - Opm::Connection completion1( 10,10,10, 100, 1, 0.0, Opm::Connection::State::OPEN , 99.88, 355.113, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true); - Opm::Connection completion2( 10,10,11, 102, 1, 0.0, Opm::Connection::State::SHUT , 99.88, 355.113, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true); completionSet.add( completion1 ); BOOST_CHECK_EQUAL( 1U , completionSet.size() ); BOOST_CHECK_MESSAGE( !completionSet.empty(), "Non-empty completion set must not be empty" ); @@ -111,11 +138,21 @@ BOOST_AUTO_TEST_CASE(AddCompletionSizeCorrect) { } -BOOST_AUTO_TEST_CASE(WellConnectionsGetOutOfRangeThrows) { - auto dir = Opm::Connection::Direction::Z; +BOOST_AUTO_TEST_CASE(WellConnectionsGetOutOfRangeThrows) +{ + const auto dir = Opm::Connection::Direction::Z; const auto kind = Opm::Connection::CTFKind::DeckValue; - Opm::Connection completion1( 10,10,10, 100, 1, 0.0, Opm::Connection::State::OPEN , 99.88, 355.113, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0,true); - Opm::Connection completion2( 10,10,11, 102, 1, 0.0, Opm::Connection::State::SHUT , 99.88, 355.113, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0,true); + const auto depth = 0.0; + + auto ctf_props = Opm::Connection::CTFProperties{}; + + ctf_props.CF = 99.88; + ctf_props.Kh = 355.113; + ctf_props.rw = 0.25; + + const auto completion1 = Opm::Connection { 10,10,10, 100, 1, Opm::Connection::State::OPEN, dir, kind, 0, depth, ctf_props, 0, true }; + const auto completion2 = Opm::Connection { 10,10,11, 102, 1, Opm::Connection::State::SHUT, dir, kind, 0, depth, ctf_props, 0, true }; + Opm::WellConnections completionSet(Opm::Connection::Order::TRACK, 1,1); completionSet.add( completion1 ); BOOST_CHECK_EQUAL( 1U , completionSet.size() ); @@ -148,15 +185,23 @@ BOOST_AUTO_TEST_CASE(Compdat_Direction) { } -BOOST_AUTO_TEST_CASE(AddCompletionCopy) { - Opm::WellConnections completionSet(Opm::Connection::Order::TRACK, 10,10); - auto dir = Opm::Connection::Direction::Z; +BOOST_AUTO_TEST_CASE(AddCompletionCopy) +{ + const auto dir = Opm::Connection::Direction::Z; const auto kind = Opm::Connection::CTFKind::DeckValue; + const auto depth = 0.0; - Opm::Connection completion1( 10,10,10, 100, 1, 0.0, Opm::Connection::State::OPEN , 99.88, 355.113, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true); - Opm::Connection completion2( 10,10,11, 101, 1, 0.0, Opm::Connection::State::SHUT , 99.88, 355.113, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true); - Opm::Connection completion3( 10,10,12, 102, 1, 0.0, Opm::Connection::State::SHUT , 99.88, 355.113, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true); + auto ctf_props = Opm::Connection::CTFProperties{}; + ctf_props.CF = 99.88; + ctf_props.Kh = 355.113; + ctf_props.rw = 0.25; + + const auto completion1 = Opm::Connection { 10,10,10, 100, 1, Opm::Connection::State::OPEN, dir, kind, 0, depth, ctf_props, 0, true }; + const auto completion2 = Opm::Connection { 10,10,11, 101, 1, Opm::Connection::State::SHUT, dir, kind, 0, depth, ctf_props, 0, true }; + const auto completion3 = Opm::Connection { 10,10,12, 102, 1, Opm::Connection::State::SHUT, dir, kind, 0, depth, ctf_props, 0, true }; + + Opm::WellConnections completionSet(Opm::Connection::Order::TRACK, 10,10); completionSet.add( completion1 ); completionSet.add( completion2 ); completionSet.add( completion3 ); @@ -171,31 +216,43 @@ BOOST_AUTO_TEST_CASE(AddCompletionCopy) { } -BOOST_AUTO_TEST_CASE(ActiveCompletions) { - Opm::EclipseGrid grid(10,20,20); - auto dir = Opm::Connection::Direction::Z; - const auto kind = Opm::Connection::CTFKind::Defaulted; - Opm::WellConnections completions(Opm::Connection::Order::TRACK, 10,10); - Opm::Connection completion1( 0,0,0, grid.getGlobalIndex(0,0,0), 1, 0.0, Opm::Connection::State::OPEN , 99.88, 355.113, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true); - Opm::Connection completion2( 0,0,1, grid.getGlobalIndex(0,0,1), 1, 0.0, Opm::Connection::State::SHUT , 99.88, 355.113, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true); - Opm::Connection completion3( 0,0,2, grid.getGlobalIndex(0,0,2), 1, 0.0, Opm::Connection::State::SHUT , 99.88, 355.113, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true); +BOOST_AUTO_TEST_CASE(ActiveCompletions) +{ + const auto dir = Opm::Connection::Direction::Z; + const auto kind = Opm::Connection::CTFKind::DeckValue; + const auto depth = 0.0; + auto ctf_props = Opm::Connection::CTFProperties{}; + + ctf_props.CF = 99.88; + ctf_props.Kh = 355.113; + ctf_props.rw = 0.25; + + Opm::EclipseGrid grid { 10, 20, 20 }; + + const auto completion1 = Opm::Connection { 0,0,0, grid.getGlobalIndex(0,0,0), 1, Opm::Connection::State::OPEN, dir, kind, 0, depth, ctf_props, 0, true }; + const auto completion2 = Opm::Connection { 0,0,1, grid.getGlobalIndex(0,0,1), 1, Opm::Connection::State::SHUT, dir, kind, 0, depth, ctf_props, 0, true }; + const auto completion3 = Opm::Connection { 0,0,2, grid.getGlobalIndex(0,0,2), 1, Opm::Connection::State::SHUT, dir, kind, 0, depth, ctf_props, 0, true }; + + Opm::WellConnections completions(Opm::Connection::Order::TRACK, 10,10); completions.add( completion1 ); completions.add( completion2 ); completions.add( completion3 ); std::vector actnum(grid.getCartesianSize(), 1); actnum[0] = 0; - grid.resetACTNUM( actnum); + grid.resetACTNUM(actnum); - Opm::WellConnections active_completions(completions, grid); + const Opm::WellConnections active_completions(completions, grid); BOOST_CHECK_EQUAL( active_completions.size() , 2U); BOOST_CHECK_EQUAL( completion2, active_completions.get(0)); BOOST_CHECK_EQUAL( completion3, active_completions.get(1)); } -BOOST_AUTO_TEST_CASE(loadCOMPDATTEST) { - Opm::UnitSystem units(Opm::UnitSystem::UnitType::UNIT_TYPE_METRIC); // Unit system used in deck FIRST_SIM.DATA. +BOOST_AUTO_TEST_CASE(loadCOMPDATTEST) +{ + const Opm::UnitSystem units(Opm::UnitSystem::UnitType::UNIT_TYPE_METRIC); // Unit system used in deck FIRST_SIM.DATA. + { const std::string deck = R"(GRID @@ -212,7 +269,8 @@ COMPDAT -- CF Diam Kh Skin Df 'WELL' 1 1 1 1 'OPEN' 1* 1.168 0.311 107.872 1* 1* 'Z' 21.925 / /)"; - Opm::WellConnections connections = loadCOMPDAT(deck); + + const Opm::WellConnections connections = loadCOMPDAT(deck); const auto& conn0 = connections[0]; BOOST_CHECK_EQUAL(conn0.CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, 1.168)); BOOST_CHECK_EQUAL(conn0.Kh(), units.to_si(Opm::UnitSystem::measure::effective_Kh, 107.872)); @@ -236,7 +294,8 @@ COMPDAT -- CF Diam Kh Skin Df 'WELL' 1 1 1 1 'OPEN' 1* 1.168 0.311 0 1* 1* 'Z' 21.925 / /)"; - Opm::WellConnections connections = loadCOMPDAT(deck); + + const Opm::WellConnections connections = loadCOMPDAT(deck); const auto& conn0 = connections[0]; BOOST_CHECK_EQUAL(conn0.CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, 1.168)); BOOST_CHECK_EQUAL(conn0.Kh(), units.to_si(Opm::UnitSystem::measure::effective_Kh, 0.10 * 1.0)); @@ -460,20 +519,16 @@ END } // Reset CF -- simulating COMPDAT record (inactive cell) + auto ctf_props = Opm::Connection::CTFProperties{}; + ctf_props.CF = 50.0*cp_rm3_per_db(); + ctf_props.Kh = 0.123; + ctf_props.rw = 0.234; + ctf_props.r0 = 0.157; + connP.addConnection(9, 9, 1, // 10, 10, 2 - 199, - 2015.0, - Opm::Connection::State::OPEN, - 50.0*cp_rm3_per_db(), - 0.123, - 0.234, - 0.157, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1); + 199, + Opm::Connection::State::OPEN, + 2015.0, ctf_props, 1); BOOST_REQUIRE_EQUAL(connP.size(), std::size_t{3}); @@ -506,19 +561,9 @@ END // Reset CF -- simulating COMPDAT record (active cell) connP.addConnection(8, 9, 1, // 10, 10, 2 - 198, - 2015.0, - Opm::Connection::State::OPEN, - 50.0*cp_rm3_per_db(), - 0.123, - 0.234, - 0.157, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1); + 198, + Opm::Connection::State::OPEN, + 2015.0, ctf_props, 1); BOOST_REQUIRE_EQUAL(connP.size(), std::size_t{4}); diff --git a/tests/parser/MultisegmentWellTests.cpp b/tests/parser/MultisegmentWellTests.cpp index 16d8050e9..a7443bfcb 100644 --- a/tests/parser/MultisegmentWellTests.cpp +++ b/tests/parser/MultisegmentWellTests.cpp @@ -55,20 +55,32 @@ #include #include -BOOST_AUTO_TEST_CASE(AICDWellTest) { - - auto dir = Opm::Connection::Direction::Z; +BOOST_AUTO_TEST_CASE(AICDWellTest) +{ + const auto dir_z = Opm::Connection::Direction::Z; + const auto dir_x = Opm::Connection::Direction::X; const auto kind = Opm::Connection::CTFKind::DeckValue; - Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); - Opm::EclipseGrid grid(20,20,20, 1., 1., 25.0, 2500.0); - connection_set.add(Opm::Connection( 19, 0, 0,grid.getGlobalIndex(19,0,0), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 19, 0, 1,grid.getGlobalIndex(19,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 19, 0, 2,grid.getGlobalIndex(19,0,2), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 18, 0, 1,grid.getGlobalIndex(18,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 17, 0, 1,grid.getGlobalIndex(17,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 16, 0, 1,grid.getGlobalIndex(16,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 15, 0, 1,grid.getGlobalIndex(15,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); + const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 }; + + const auto depth = 0.0; + const auto state = Opm::Connection::State::OPEN; + + auto ctf_props = Opm::Connection::CTFProperties{}; + + ctf_props.CF = 200.0; + ctf_props.Kh = 17.29; + ctf_props.rw = 0.25; + + Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); + connection_set.add({ 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + + connection_set.add({ 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); BOOST_CHECK_EQUAL( 7U , connection_set.size() ); @@ -212,23 +224,33 @@ WSEGAICD const double center_depth_connection7 = connection7.depth(); BOOST_CHECK_EQUAL(segment_number_connection7, 8); BOOST_CHECK_EQUAL(center_depth_connection7, 2534.5); - } -BOOST_AUTO_TEST_CASE(MultisegmentWellTest) { - - auto dir = Opm::Connection::Direction::Z; +BOOST_AUTO_TEST_CASE(MultisegmentWellTest) +{ + const auto dir_z = Opm::Connection::Direction::Z; + const auto dir_x = Opm::Connection::Direction::X; const auto kind = Opm::Connection::CTFKind::DeckValue; - Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); - Opm::EclipseGrid grid(20,20,20, 1., 1., 25.0, 2500.0); - connection_set.add(Opm::Connection( 19, 0, 0,grid.getGlobalIndex(19,0,0), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 19, 0, 1,grid.getGlobalIndex(19,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 19, 0, 2,grid.getGlobalIndex(19,0,2), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); + const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 }; - connection_set.add(Opm::Connection( 18, 0, 1,grid.getGlobalIndex(18,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 17, 0, 1,grid.getGlobalIndex(17,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 16, 0, 1,grid.getGlobalIndex(16,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 15, 0, 1,grid.getGlobalIndex(15,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); + const auto depth = 0.0; + const auto state = Opm::Connection::State::OPEN; + + auto ctf_props = Opm::Connection::CTFProperties{}; + + ctf_props.CF = 200.0; + ctf_props.Kh = 17.29; + ctf_props.rw = 0.25; + + Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); + connection_set.add({ 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + + connection_set.add({ 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); BOOST_CHECK_EQUAL( 7U , connection_set.size() ); @@ -383,19 +405,32 @@ WSEGSICD } -BOOST_AUTO_TEST_CASE(WrongDistanceCOMPSEGS) { - auto dir = Opm::Connection::Direction::Z; +BOOST_AUTO_TEST_CASE(WrongDistanceCOMPSEGS) +{ + const auto dir_z = Opm::Connection::Direction::Z; + const auto dir_x = Opm::Connection::Direction::X; const auto kind = Opm::Connection::CTFKind::DeckValue; - Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); - Opm::EclipseGrid grid(20,20,20, 1., 1., 25., 2500.); - connection_set.add(Opm::Connection( 19, 0, 0, grid.getGlobalIndex(19,0,0),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 19, 0, 1, grid.getGlobalIndex(19,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 19, 0, 2, grid.getGlobalIndex(19,0,2),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 18, 0, 1, grid.getGlobalIndex(18,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 17, 0, 1, grid.getGlobalIndex(17,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 16, 0, 1, grid.getGlobalIndex(16,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 15, 0, 1, grid.getGlobalIndex(15,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); + const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25., 2500.0 }; + + const auto depth = 0.0; + const auto state = Opm::Connection::State::OPEN; + + auto ctf_props = Opm::Connection::CTFProperties{}; + + ctf_props.CF = 200.0; + ctf_props.Kh = 17.29; + ctf_props.rw = 0.25; + + Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); + connection_set.add({ 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + + connection_set.add({ 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); BOOST_CHECK_EQUAL( 7U , connection_set.size() ); @@ -452,19 +487,32 @@ BOOST_AUTO_TEST_CASE(WrongDistanceCOMPSEGS) { BOOST_CHECK_NO_THROW(Opm::Compsegs::processCOMPSEGS(compsegs, connection_set, segment_set, Opm::ScheduleGrid(grid, fp, cells), parseContext, errorGuard)); } -BOOST_AUTO_TEST_CASE(NegativeDepthCOMPSEGS) { - auto dir = Opm::Connection::Direction::Z; +BOOST_AUTO_TEST_CASE(NegativeDepthCOMPSEGS) +{ + const auto dir_z = Opm::Connection::Direction::Z; + const auto dir_x = Opm::Connection::Direction::X; const auto kind = Opm::Connection::CTFKind::DeckValue; - Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); - Opm::EclipseGrid grid(20,20,20, 1., 1., 25., 2500.); - connection_set.add(Opm::Connection( 19, 0, 0, grid.getGlobalIndex(19,0,0),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 19, 0, 1, grid.getGlobalIndex(19,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 19, 0, 2, grid.getGlobalIndex(19,0,2),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 18, 0, 1, grid.getGlobalIndex(18,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 17, 0, 1, grid.getGlobalIndex(17,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 16, 0, 1, grid.getGlobalIndex(16,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 15, 0, 1, grid.getGlobalIndex(15,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); + const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 }; + + const auto depth = 0.0; + const auto state = Opm::Connection::State::OPEN; + + auto ctf_props = Opm::Connection::CTFProperties{}; + + ctf_props.CF = 200.0; + ctf_props.Kh = 17.29; + ctf_props.rw = 0.25; + + Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); + connection_set.add({ 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + + connection_set.add({ 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); BOOST_CHECK_EQUAL( 7U , connection_set.size() ); @@ -521,19 +569,31 @@ BOOST_AUTO_TEST_CASE(NegativeDepthCOMPSEGS) { BOOST_CHECK_NO_THROW( Opm::Compsegs::processCOMPSEGS(compsegs, connection_set, segment_set, Opm::ScheduleGrid(grid, fp, cells), parseContext, errorGuard) ); } -BOOST_AUTO_TEST_CASE(testwsegvalv) { - auto dir = Opm::Connection::Direction::Z; +BOOST_AUTO_TEST_CASE(testwsegvalv) +{ + const auto dir_z = Opm::Connection::Direction::Z; + const auto dir_x = Opm::Connection::Direction::X; const auto kind = Opm::Connection::CTFKind::DeckValue; - Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); - Opm::EclipseGrid grid(20,20,20, 1., 1., 25., 2500.); - connection_set.add(Opm::Connection( 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); - connection_set.add(Opm::Connection( 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) ); + const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 }; - connection_set.add(Opm::Connection( 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); - connection_set.add(Opm::Connection( 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) ); + const auto depth = 0.0; + const auto state = Opm::Connection::State::OPEN; + + auto ctf_props = Opm::Connection::CTFProperties{}; + + ctf_props.CF = 200.0; + ctf_props.Kh = 17.29; + ctf_props.rw = 0.25; + + Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); + connection_set.add({ 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true }); + + connection_set.add({ 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); + connection_set.add({ 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true }); BOOST_CHECK_EQUAL( 7U , connection_set.size() ); diff --git a/tests/parser/ScheduleTests.cpp b/tests/parser/ScheduleTests.cpp index 3d6469280..bff809685 100644 --- a/tests/parser/ScheduleTests.cpp +++ b/tests/parser/ScheduleTests.cpp @@ -15,16 +15,7 @@ You should have received a copy of the GNU General Public License along with OPM. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include +*/ #define BOOST_TEST_MODULE ScheduleTests @@ -37,6 +28,8 @@ #include #endif +#include + #include #include #include @@ -45,49 +38,62 @@ #include #include -#include #include -#include #include +#include #include +#include #include -#include -#include -#include -#include + +#include + +#include #include +#include +#include +#include #include +#include +#include #include -#include #include #include #include +#include +#include +#include #include +#include #include #include -#include -#include + +#include +#include +#include #include #include #include #include + #include #include -#include #include -#include -#include -#include - -#include -#include -#include -#include -#include +#include #include "tests/WorkArea.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + using namespace Opm; namespace { @@ -100,32 +106,33 @@ namespace { { return UnitSystem::newMETRIC().to_si(UnitSystem::measure::transmissibility, 1.0); } -} -static Schedule make_schedule(const std::string& deck_string) { - const auto& deck = Parser{}.parseString(deck_string); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck); - return Schedule(deck, grid , fp, runspec, python); -} + Schedule make_schedule(const std::string& deck_string) + { + const auto deck = Parser{}.parseString(deck_string); + const EclipseGrid grid(10, 10, 10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec (deck); -static std::string createDeck() { - std::string input = R"( + return { deck, grid, fp, runspec, std::make_shared() }; + } + + std::string createDeck() + { + return { R"( START 8 MAR 1998 / SCHEDULE -)"; - return input; -} +)" }; + } -static std::string createDeckWithWells() { - std::string input = R"( + std::string createDeckWithWells() + { + return { R"( START -- 0 10 MAI 2007 / SCHEDULE @@ -143,13 +150,12 @@ WELSPECS 'WX2' 'OP' 30 37 3.33 'OIL' 7* / 'W_3' 'OP' 20 51 3.92 'OIL' 7* / /; -)"; - return input; -} +)" }; + } - -static std::string createDeckWTEST() { - std::string input = R"( + std::string createDeckWTEST() + { + return { R"( START -- 0 10 MAI 2007 / GRID @@ -246,12 +252,12 @@ DATES -- 5 WCONINJH 'BAN' 'WATER' 1* 1.0 / / -)"; - return input; -} +)" }; + } -static std::string createDeckForTestingCrossFlow() { - std::string input = R"( + std::string createDeckForTestingCrossFlow() + { + return { R"( START -- 0 10 MAI 2007 / GRID @@ -318,12 +324,12 @@ DATES -- 4 WCONINJH 'BAN' 'WATER' 1* 1.0 / / -)"; - return input; -} +)" }; + } -static std::string createDeckWithWellsOrdered() { - std::string input = R"( + std::string createDeckWithWellsOrdered() + { + return { R"( START -- 0 10 MAI 2007 / WELLDIMS @@ -334,12 +340,12 @@ WELSPECS 'BW_2' 'BG' 3 3 3.33 'OIL' 7* / 'AW_3' 'AG' 2 5 3.92 'OIL' 7* / / -)"; - return input; -} +)" }; + } -static std::string createDeckWithWellsOrderedGRUPTREE() { - std::string input = R"( + std::string createDeckWithWellsOrderedGRUPTREE() + { + return { R"( START -- 0 10 MAI 2007 / SCHEDULE @@ -355,13 +361,12 @@ WELSPECS 'BW_2' 'CG2' 3 3 3.33 'OIL' 7* / 'AW_3' 'CG2' 2 5 3.92 'OIL' 7* / / -)"; +)" }; + } - return input; -} - -static std::string createDeckWithWellsAndCompletionData() { - std::string input = R"( + std::string createDeckWithWellsAndCompletionData() + { + return { R"( START -- 0 1 NOV 1979 / GRID @@ -398,17 +403,20 @@ DATES -- 2,3 COMPDAT // with defaulted I and J 'OP_1' 0 * 3 9 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 / / -)"; - return input; -} +)" }; + } + bool has_name(const std::vector& names, + const std::string& name) + { + return std::any_of(names.begin(), names.end(), + [&name](const std::string& search) + { + return search == name; + }); + } - -bool has_name(const std::vector& names, const std::string& name) { - auto find_iter = std::find(names.begin(), names.end(), name); - return (find_iter != names.end()); -} - +} // Anonymous namespace BOOST_AUTO_TEST_CASE(CreateScheduleDeckMissingReturnsDefaults) { Deck deck; @@ -448,14 +456,16 @@ BOOST_AUTO_TEST_CASE(CreateScheduleDeckWellsOrdered) { BOOST_CHECK_EQUAL(field_ptr->name(), "FIELD"); } +namespace { -static bool has_well( const std::vector& wells, const std::string& well_name) { +bool has_well( const std::vector& wells, const std::string& well_name) { for (const auto& well : wells ) if (well.name( ) == well_name) return true; return false; } +} // Anonymous namespace BOOST_AUTO_TEST_CASE(CreateScheduleDeckWellsOrderedGRUPTREE) { const auto& schedule = make_schedule( createDeckWithWellsOrderedGRUPTREE() ); @@ -527,7 +537,6 @@ BOOST_AUTO_TEST_CASE(GroupTree2TEST) { } - BOOST_AUTO_TEST_CASE(CreateScheduleDeckWithStart) { const auto& schedule = make_schedule( createDeck() ); BOOST_CHECK_EQUAL( schedule.getStartTime() , asTimeT(TimeStampUTC(1998, 3 , 8 ))); @@ -685,8 +694,11 @@ BOOST_AUTO_TEST_CASE(TestCrossFlowHandling) { BOOST_CHECK(Well::Status::OPEN == schedule.getWell("BAN", 5).getStatus()); } -static std::string createDeckWithWellsAndSkinFactorChanges() { - std::string input = R"( +namespace { + + std::string createDeckWithWellsAndSkinFactorChanges() + { + return { R"(RUNSPEC START -- 0 1 NOV 1979 / GRID @@ -708,40 +720,53 @@ WELSPECS 'OP_3' 'OP' 7 7 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* / / COMPDAT - 'OP_1' 9 9 1 1 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 / - 'OP_1' 9 9 2 2 'OPEN' 1* 46.825 0.311 4332.346 1* 1* 'X' 22.123 / - 'OP_2' 8 8 1 3 'OPEN' 1* 1.168 0.311 107.872 1* 1* 'Y' 21.925 / - 'OP_2' 8 7 3 3 'OPEN' 1* 15.071 0.311 1391.859 1* 1* 'Y' 21.920 / - 'OP_2' 8 7 3 6 'OPEN' 1* 6.242 0.311 576.458 1* 1* 'Y' 21.915 / - 'OP_3' 7 7 1 1 'OPEN' 1* 27.412 0.311 2445.337 1* 1* 'Y' 18.521 / - 'OP_3' 7 7 2 2 'OPEN' 1* 55.195 0.311 4923.842 1* 1* 'Y' 18.524 / +-- Well I J K1 K2 Status SATNUM CTF Diam Kh Skin D Dir PER (r0) + 'OP_1' 9 9 1 1 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 / + 'OP_1' 9 9 2 2 'OPEN' 1* 46.825 0.311 4332.346 1* 1* 'X' 22.123 / + 'OP_2' 8 8 1 3 'OPEN' 1* 1.168 0.311 107.872 1* 1* 'Y' 21.925 / + 'OP_2' 8 7 3 3 'OPEN' 1* 15.071 0.311 1391.859 1* 1* 'Y' 21.920 / + 'OP_2' 8 7 3 6 'OPEN' 1* 6.242 0.311 576.458 1* 1* 'Y' 21.915 / + 'OP_3' 7 7 1 1 'OPEN' 1* 27.412 0.311 2445.337 1* 1* 'Y' 18.521 / + 'OP_3' 7 7 2 2 'OPEN' 1* 55.195 0.311 4923.842 1* 1* 'Y' 18.524 / / DATES -- 2 10 JUL 2007 / / CSKIN -'OP_1' 9 9 1 1 1.5 / -'OP_2' 4* -1.0 / -'OP_3' 2* 1 2 10.0 / +'OP_1' 9 9 1 1 1.5 / +'OP_2' 4* -1.0 / +'OP_3' 2* 1 2 10.0 / 'OP_3' 7 7 1 1 -1.15 / / -)"; - return input; -} +)" }; + } + +} // Anonymous namespace + +BOOST_AUTO_TEST_CASE(CreateScheduleDeckWellsAndSkinFactorChanges) +{ + auto metricCF = [units = Opm::UnitSystem::newMETRIC()] + (const double ctf) + { + return units.from_si(Opm::UnitSystem::measure::transmissibility, ctf); + }; + + const auto schedule = make_schedule(createDeckWithWellsAndSkinFactorChanges()); -BOOST_AUTO_TEST_CASE(CreateScheduleDeckWellsAndSkinFactorChanges) { - Opm::UnitSystem units(Opm::UnitSystem::UnitType::UNIT_TYPE_METRIC); - const auto& schedule = make_schedule(createDeckWithWellsAndSkinFactorChanges()); - // OP_1 { const auto& cs = schedule.getWell("OP_1", 2).getConnections(); BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).skinFactor(), 1.5, 1e-10); - double CF = 25.290608354096133; - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5); + + // denom = 2*pi*Kh / CTF = 4.95609889 + // + // New CTF = CTF * denom / (denom + S) = 32.948 * 4.95609889 / (4.95609889 + 1.5) + const double expectCF = 25.292912792; + BOOST_CHECK_CLOSE(metricCF(cs.getFromIJK(8, 8, 0).CF()), expectCF, 1.0e-5); } + // OP_2 { const auto& well = schedule.getWell("OP_2", 2); @@ -749,24 +774,40 @@ BOOST_AUTO_TEST_CASE(CreateScheduleDeckWellsAndSkinFactorChanges) { for (size_t i = 0; i < cs.size(); i++) { BOOST_CHECK_CLOSE(cs.get(i).skinFactor(), -1.0, 1e-10); } - double CF = 7.822338909386947; - BOOST_CHECK_CLOSE(cs.getFromIJK(7, 6, 2).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5); + + // denom = 2*pi*Kh / CTF = 4.947899898 + // + // New CTF = CTF * denom / (denom + S) = 6.242 * 4.947899898 / (4.947899898 - 1.0) + const double expectCF = 7.82309378689; + BOOST_CHECK_CLOSE(metricCF(cs.getFromIJK(7, 6, 2).CF()), expectCF, 1.0e-5); } + // OP_3 { const auto& well = schedule.getWell("OP_3", 2); const auto& cs = well.getConnections(); - BOOST_CHECK_CLOSE(cs.getFromIJK(6, 6, 0).skinFactor(), -1.15, 1e-10); - BOOST_CHECK_CLOSE(cs.getFromIJK(6, 6, 1).skinFactor(), 10.0, 1e-10); - double CF1 = 36.09169888375442; - BOOST_CHECK_CLOSE(cs.getFromIJK(6, 6, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF1), 1e-5); - double CF2 = 17.848489977420336; - BOOST_CHECK_CLOSE(cs.getFromIJK(6, 6, 1).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF2), 1e-5); + BOOST_CHECK_CLOSE(cs.getFromIJK(6, 6, 0).skinFactor(), - 1.15, 1e-10); + BOOST_CHECK_CLOSE(cs.getFromIJK(6, 6, 1).skinFactor(), 10.0, 1e-10); + + // denom = 2*pi*Kh / CTF = 4.7794177751 + // + // New CTF = CTF * denom / (denom + S) = 27.412 * 4.7794177751 / (4.7794177751 - 1.15) + const double expectCF1 = 36.09763553531; + BOOST_CHECK_CLOSE(metricCF(cs.getFromIJK(6, 6, 0).CF()), expectCF1, 1.0e-5); + + // denom = 2*pi*Kh / CTF = 4.7794879307 + // + // New CTF = CTF * denom / (denom + S) = 55.195 * 4.7794879307 / (4.7794879307 + 10) + const double expectCF2 = 17.84932181501; + BOOST_CHECK_CLOSE(metricCF(cs.getFromIJK(6, 6, 1).CF()), expectCF2, 1.0e-5); } } -static std::string createDeckWithWPIMULTandWELPIandCSKIN() { - std::string input = R"( +namespace { + + std::string createDeckWithWPIMULTandWELPIandCSKIN() + { + return { R"( START -- 0 1 NOV 1979 / GRID @@ -848,22 +889,33 @@ CSKIN 'OP_1' 9 9 1 1 -1.0 / / -)"; - return input; -} +)" }; + } -BOOST_AUTO_TEST_CASE(CreateScheduleDeckWPIMULTandWELPIandCSKIN) { - // Setup - Opm::UnitSystem units(Opm::UnitSystem::UnitType::UNIT_TYPE_METRIC); +} // Anonymous namespace + +BOOST_AUTO_TEST_CASE(CreateScheduleDeckWPIMULTandWELPIandCSKIN) +{ + auto metricCF = [units = Opm::UnitSystem::newMETRIC()](const double ctf) + { + return units.from_si(Opm::UnitSystem::measure::transmissibility, ctf); + }; + + // Note: Schedule must be mutable for WELPI scaling. auto schedule = make_schedule(createDeckWithWPIMULTandWELPIandCSKIN()); // Report step 2 { const auto& cs = schedule.getWell("OP_1", 2).getConnections(); - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).skinFactor(), 1.5, 1e-10); - double CF = 25.290608354096133; - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5); - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).wpimult(), 1.0, 1e-5); + const auto& conn = cs.getFromIJK(8, 8, 0); + + BOOST_CHECK_CLOSE(conn.skinFactor(), 1.5, 1e-10); + + // denom = 2*pi*Kh / CTF = 4.95609889 + // + // New CTF = CTF * denom / (denom + S) = 32.948 * 4.95609889 / (4.95609889 + 1.5) + const double expectCF = 25.292912792376; + BOOST_CHECK_CLOSE(metricCF(conn.CF()), expectCF, 1.0e-5); } // Report step 3 @@ -871,15 +923,22 @@ BOOST_AUTO_TEST_CASE(CreateScheduleDeckWPIMULTandWELPIandCSKIN) { const auto& cs_prev = schedule.getWell("OP_1", 2).getConnections(); const auto& cs_curr = schedule.getWell("OP_1", 3).getConnections(); BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).CF() / cs_prev.getFromIJK(8, 8, 0).CF(), 1.3, 1e-5); - BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).wpimult(), 1.3, 1e-5); } // Report step 4 { const auto& cs = schedule.getWell("OP_1", 4).getConnections(); - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).skinFactor(), 0.5, 1e-10); - double CF = 38.90302007377862; // CF from CSKIN multiplied by 1.3 from WPIMULT - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5); + const auto& conn = cs.getFromIJK(8, 8, 0); + + BOOST_CHECK_CLOSE(conn.skinFactor(), 0.5, 1e-10); + + // CF from CSKIN multiplied by 1.3 from WPIMULT + // denom = 2*pi*Kh / CTF = 4.95609889 + // mult = 1.3 + // + // New CTF = mult * CTF * denom / (denom + S) = 1.3 * 32.948 * 4.95609889 / (4.95609889 + 0.5) + const double expectCF = 38.90721454349; + BOOST_CHECK_CLOSE(metricCF(conn.CF()), expectCF, 1e-5); } // Report step 5 @@ -887,49 +946,69 @@ BOOST_AUTO_TEST_CASE(CreateScheduleDeckWPIMULTandWELPIandCSKIN) { const auto& cs_prev = schedule.getWell("OP_1", 4).getConnections(); const auto& cs_curr = schedule.getWell("OP_1", 5).getConnections(); BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).CF() / cs_prev.getFromIJK(8, 8, 0).CF(), 1.3, 1e-5); - BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).wpimult(), 1.3 * 1.3, 1e-5); } // Report step 6 { - const auto& cs = schedule.getWell("OP_1", 6).getConnections(); - double init_pi = 100.0; - schedule.applyWellProdIndexScaling("OP_1", 6, units.to_si(Opm::UnitSystem::measure::liquid_productivity_index, init_pi)); - const auto& target_pi = schedule[6].target_wellpi.at("OP_1"); - BOOST_CHECK_CLOSE(target_pi, 50.0, 1e-5); - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).wpimult(), 1.3 * 1.3 * (target_pi / init_pi), 1e-5); + auto cvrtPI = [units = Opm::UnitSystem::newMETRIC()](const double pi) + { + return units.to_si(Opm::UnitSystem::measure::liquid_productivity_index, pi); + }; + + const auto init_pi = cvrtPI(100.0); + schedule.applyWellProdIndexScaling("OP_1", 6, init_pi); + + const auto target_pi = schedule[6].target_wellpi.at("OP_1"); + BOOST_CHECK_CLOSE(target_pi, 50.0, 1.0e-5); } // Report step 7 { - const auto& cs_prev = schedule.getWell("OP_1", 6).getConnections(); - const auto& cs_curr = schedule.getWell("OP_1", 7).getConnections(); - BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).skinFactor(), 5.0, 1e-10); - double CF = 13.858329011932668; // CF from CSKIN multiplied by 0.845 from WPIMULT and WELPI previous - BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5); - BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).wpimult(), cs_prev.getFromIJK(8, 8, 0).wpimult(), 1e-5); + const auto& cs = schedule.getWell("OP_1", 7).getConnections(); + const auto& conn = cs.getFromIJK(8, 8, 0); + + BOOST_CHECK_CLOSE(conn.skinFactor(), 5.0, 1e-10); + + // denom = 2*pi*Kh / CTF = 4.95609889 + // mult = 1.3 * 1.3 * (50 / 100) = 0.845 + // + // New CTF = mult * CTF * denom / (denom + S) = 0.845 * 32.948 * 4.95609889 / (4.95609889 + 5) + + const auto expectCF = 13.8591478493; + BOOST_CHECK_CLOSE(metricCF(conn.CF()), expectCF, 1.0e-5); } // Report step 8 { const auto& cs = schedule.getWell("OP_1", 8).getConnections(); - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, 32.948), 1e-5); - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).wpimult(), 1.0, 1e-5); + const auto& conn = cs.getFromIJK(8, 8, 0); + + const auto expectCF = 32.948; + BOOST_CHECK_CLOSE(metricCF(conn.CF()), expectCF, 1.0e-5); } // Report step 9 { const auto& cs = schedule.getWell("OP_1", 9).getConnections(); - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).skinFactor(), -1.0, 1e-10); - double CF = 41.27026972084714; // CF from CSKIN with WPIMULT and WELLPI multiplier reset to 1.0 - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5); - BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).wpimult(), 1.0, 1e-5); + const auto& conn = cs.getFromIJK(8, 8, 0); + + BOOST_CHECK_CLOSE(conn.skinFactor(), -1.0, 1e-10); + + // CF from CSKIN with WPIMULT and WELLPI multiplier reset to 1.0 + // + // denom = 2*pi*Kh / CTF = 4.95609889 + // + // New CTF = CTF * denom / (denom + S) = 32.948 * 4.95609889 / (4.95609889 - 1) + const auto expectCF = 41.276406579873; + BOOST_CHECK_CLOSE(metricCF(conn.CF()), expectCF, 1.0e-5); } } +namespace { -static std::string createDeckWithWellsAndConnectionDataWithWELOPEN() { - std::string input = R"( + std::string createDeckWithWellsAndConnectionDataWithWELOPEN() + { + return { R"( START -- 0 1 NOV 1979 / GRID @@ -986,9 +1065,10 @@ DATES -- 5 WELOPEN 'OP_1' SHUT 0 0 0 0 0 / / -)"; - return input; -} +)" }; + } + +} // Anonymous namespace BOOST_AUTO_TEST_CASE(CreateScheduleDeckWellsAndConnectionDataWithWELOPEN) { const auto& schedule = make_schedule(createDeckWithWellsAndConnectionDataWithWELOPEN()); @@ -2617,46 +2697,48 @@ BOOST_AUTO_TEST_CASE(WTEMPINJ_well_template) { } BOOST_AUTO_TEST_CASE( COMPDAT_sets_automatic_complnum ) { - std::string input = R"( - START -- 0 - 19 JUN 2007 / - GRID - PERMX - 1000*0.10/ - COPY - PERMX PERMY / - PERMX PERMZ / - / - SCHEDULE - DATES -- 1 - 10 OKT 2008 / - / - WELSPECS - 'W1' 'G1' 3 3 2873.94 'WATER' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / - / + const auto deck = Parser{}.parseString(R"( +START -- 0 +19 JUN 2007 / +GRID +PORO + 1000*0.3 / +PERMX + 1000*0.10/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +SCHEDULE +DATES -- 1 + 10 OKT 2008 / +/ +WELSPECS + 'W1' 'G1' 3 3 2873.94 'WATER' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / +/ - COMPDAT - 'W1' 0 0 1 1 'SHUT' 1* / -- regular completion (1) - 'W1' 0 0 2 2 'SHUT' 1* / -- regular completion (2) - 'W1' 0 0 3 4 'SHUT' 1* / -- two completions in one record (3, 4) - / +COMPDAT + 'W1' 0 0 1 1 'SHUT' 1* / -- regular completion (1) + 'W1' 0 0 2 2 'SHUT' 1* / -- regular completion (2) + 'W1' 0 0 3 4 'SHUT' 1* / -- two completions in one record (3, 4) +/ - DATES -- 2 - 11 OKT 2008 / - / +DATES -- 2 + 11 OKT 2008 / +/ - COMPDAT - 'W1' 0 0 1 1 'SHUT' 1* / -- respecify, essentially ignore (1) - / - )"; +COMPDAT + 'W1' 0 0 1 1 'SHUT' 1* / -- respecify, essentially ignore (1) +/ +END +)"); + + const EclipseGrid grid(10,10,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec (deck); + const Schedule schedule(deck, grid, fp, runspec, std::make_shared()); - auto deck = Parser().parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck); - Schedule schedule( deck, grid, fp, runspec, python); const auto& cs1 = schedule.getWell( "W1", 1 ).getConnections( ); BOOST_CHECK_EQUAL( 1, cs1.get( 0 ).complnum() ); BOOST_CHECK_EQUAL( 2, cs1.get( 1 ).complnum() ); @@ -2671,43 +2753,44 @@ BOOST_AUTO_TEST_CASE( COMPDAT_sets_automatic_complnum ) { } BOOST_AUTO_TEST_CASE( COMPDAT_multiple_wells ) { - std::string input = R"( - START -- 0 - 19 JUN 2007 / - GRID - PERMX - 1000*0.10/ - COPY - PERMX PERMY / - PERMX PERMZ / - / - SCHEDULE - DATES -- 1 - 10 OKT 2008 / - / - WELSPECS - 'W1' 'G1' 3 3 2873.94 'WATER' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / - 'W2' 'G2' 5 5 1 'OIL' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / - / + const auto deck = Parser{}.parseString(R"( +START -- 0 +19 JUN 2007 / +GRID +PERMX + 1000*0.10/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO + 1000*0.3 / - COMPDAT - 'W1' 0 0 1 1 'SHUT' 1* / -- regular completion (1) - 'W1' 0 0 2 2 'SHUT' 1* / -- regular completion (2) - 'W1' 0 0 3 4 'SHUT' 1* / -- two completions in one record (3, 4) - 'W2' 0 0 3 3 'SHUT' 1* / -- regular completion (1) - 'W2' 0 0 1 3 'SHUT' 1* / -- two completions (one exist already) (2, 3) - 'W*' 0 0 3 5 'SHUT' 1* / -- two completions, two wells (includes existing - -- and adding for both wells) - / - )"; +SCHEDULE +DATES -- 1 + 10 OKT 2008 / +/ +WELSPECS + 'W1' 'G1' 3 3 2873.94 'WATER' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / + 'W2' 'G2' 5 5 1 'OIL' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / +/ - auto deck = Parser().parseString( input); - auto python = std::make_shared(); - EclipseGrid grid( 10, 10, 10 ); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck); - Schedule schedule( deck, grid, fp, runspec, python); +COMPDAT + 'W1' 0 0 1 1 'SHUT' 1* / -- regular completion (1) + 'W1' 0 0 2 2 'SHUT' 1* / -- regular completion (2) + 'W1' 0 0 3 4 'SHUT' 1* / -- two completions in one record (3, 4) + 'W2' 0 0 3 3 'SHUT' 1* / -- regular completion (1) + 'W2' 0 0 1 3 'SHUT' 1* / -- two completions (one exist already) (2, 3) + 'W*' 0 0 3 5 'SHUT' 1* / -- two completions, two wells (includes existing + -- and adding for both wells) +/ +)"); + + const EclipseGrid grid(10, 10, 10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec (deck); + const Schedule schedule(deck, grid, fp, runspec, std::make_shared()); { const auto& w1cs = schedule.getWell( "W1", 1 ).getConnections(); @@ -2745,39 +2828,40 @@ BOOST_AUTO_TEST_CASE( COMPDAT_multiple_wells ) { } BOOST_AUTO_TEST_CASE( COMPDAT_multiple_records_same_completion ) { - std::string input = R"( - START -- 0 - 19 JUN 2007 / - GRID - PERMX - 1000*0.10/ - COPY - PERMX PERMY / - PERMX PERMZ / - / - SCHEDULE - DATES -- 1 - 10 OKT 2008 / - / - WELSPECS - 'W1' 'G1' 3 3 2873.94 'WATER' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / - 'W2' 'G2' 5 5 1 'OIL' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / - / + const auto deck = Parser{}.parseString(R"( +START -- 0 +19 JUN 2007 / +GRID +PERMX + 1000*0.10/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO + 1000*0.3 / +SCHEDULE +DATES -- 1 + 10 OKT 2008 / +/ +WELSPECS + 'W1' 'G1' 3 3 2873.94 'WATER' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / + 'W2' 'G2' 5 5 1 'OIL' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / +/ - COMPDAT - 'W1' 0 0 1 2 'SHUT' 1* / -- multiple completion (1, 2) - 'W1' 0 0 2 2 'SHUT' 1* / -- updated completion (2) - 'W1' 0 0 3 3 'SHUT' 1* / -- regular completion (3) - / - )"; +COMPDAT + 'W1' 0 0 1 2 'SHUT' 1* / -- multiple completion (1, 2) + 'W1' 0 0 2 2 'SHUT' 1* / -- updated completion (2) + 'W1' 0 0 3 3 'SHUT' 1* / -- regular completion (3) +/ +)"); + + const EclipseGrid grid(10,10,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec (deck); + const Schedule schedule(deck, grid, fp, runspec, std::make_shared()); - auto deck = Parser().parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck); - Schedule schedule( deck, grid, fp, runspec, python); const auto& cs = schedule.getWell( "W1", 1 ).getConnections(); BOOST_CHECK_EQUAL( 3U, cs.size() ); BOOST_CHECK_EQUAL( 1, cs.get( 0 ).complnum() ); @@ -2822,56 +2906,57 @@ BOOST_AUTO_TEST_CASE( complump_less_than_1 ) { } BOOST_AUTO_TEST_CASE( complump ) { - std::string input = R"( - START -- 0 - 19 JUN 2007 / - GRID - PERMX - 1000*0.10/ - COPY - PERMX PERMY / - PERMX PERMZ / - / - SCHEDULE + const auto deck = Parser{}.parseString(R"( +START -- 0 +19 JUN 2007 / +GRID +PERMX + 1000*0.10/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO + 1000*0.3 / - WELSPECS - 'W1' 'G1' 3 3 2873.94 'WATER' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / - 'W2' 'G2' 5 5 1 'OIL' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / - / +SCHEDULE - COMPDAT - 'W1' 0 0 1 2 'SHUT' 1* / Global Index = 23, 123, 223, 323, 423, 523 - 'W1' 0 0 2 3 'SHUT' 1* / - 'W1' 0 0 4 6 'SHUT' 1* / - 'W2' 0 0 3 4 'SHUT' 1* / - 'W2' 0 0 1 4 'SHUT' 1* / - / +WELSPECS + 'W1' 'G1' 3 3 2873.94 'WATER' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / + 'W2' 'G2' 5 5 1 'OIL' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / +/ - COMPLUMP - -- name I J K1 K2 C - -- where C is the completion number of this lump - 'W1' 0 0 1 3 1 / - / +COMPDAT + 'W1' 0 0 1 2 'SHUT' 1* / Global Index = 23, 123, 223, 323, 423, 523 + 'W1' 0 0 2 3 'SHUT' 1* / + 'W1' 0 0 4 6 'SHUT' 1* / + 'W2' 0 0 3 4 'SHUT' 1* / + 'W2' 0 0 1 4 'SHUT' 1* / +/ - DATES -- 1 - 10 OKT 2008 / - / +COMPLUMP + -- name I J K1 K2 C + -- where C is the completion number of this lump + 'W1' 0 0 1 3 1 / +/ - WELOPEN - 'W1' 'OPEN' 0 0 0 1 1 / - / - )"; +DATES -- 1 + 10 OKT 2008 / +/ + +WELOPEN + 'W1' 'OPEN' 0 0 0 1 1 / +/ +)"); constexpr auto open = Connection::State::OPEN; constexpr auto shut = Connection::State::SHUT; - auto deck = Parser().parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck); - Schedule schedule( deck, grid, fp, runspec, python); + const EclipseGrid grid(10,10,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec (deck); + const Schedule schedule(deck, grid, fp, runspec, std::make_shared()); const auto& sc0 = schedule.getWell("W1", 0).getConnections(); /* complnum should be modified by COMPLNUM */ @@ -2932,66 +3017,65 @@ BOOST_AUTO_TEST_CASE( complump ) { BOOST_CHECK_THROW( all_connections.getFromGlobalIndex(100000), std::exception ); } - - BOOST_AUTO_TEST_CASE( COMPLUMP_specific_coordinates ) { - std::string input = R"( - START -- 0 - 19 JUN 2007 / - GRID - PERMX - 1000*0.10/ - COPY - PERMX PERMY / - PERMX PERMZ / - / - SCHEDULE + const auto deck = Parser{}.parseString(R"( +START -- 0 +19 JUN 2007 / +GRID +PERMX + 1000*0.10/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO + 1000*0.3 / - WELSPECS - 'W1' 'G1' 3 3 2873.94 'WATER' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / - / +SCHEDULE - COMPDAT -- completion number - 'W1' 1 1 1 1 'SHUT' 1* / -- 1 - 'W1' 1 1 2 2 'SHUT' 1* / -- 2 - 'W1' 0 0 1 2 'SHUT' 1* / -- 3, 4 - 'W1' 0 0 2 3 'SHUT' 1* / -- 5 - 'W1' 2 2 1 1 'SHUT' 1* / -- 6 - 'W1' 2 2 4 6 'SHUT' 1* / -- 7, 8, 9 - / +WELSPECS + 'W1' 'G1' 3 3 2873.94 'WATER' 0.00 'STD' 'SHUT' 'NO' 0 'SEG' / +/ - DATES -- 1 - 10 OKT 2008 / - / +COMPDAT -- completion number + 'W1' 1 1 1 1 'SHUT' 1* / -- 1 + 'W1' 1 1 2 2 'SHUT' 1* / -- 2 + 'W1' 0 0 1 2 'SHUT' 1* / -- 3, 4 + 'W1' 0 0 2 3 'SHUT' 1* / -- 5 + 'W1' 2 2 1 1 'SHUT' 1* / -- 6 + 'W1' 2 2 4 6 'SHUT' 1* / -- 7, 8, 9 +/ + +DATES -- 1 + 10 OKT 2008 / +/ - DATES -- 2 - 15 OKT 2008 / - / +DATES -- 2 + 15 OKT 2008 / +/ - COMPLUMP - -- name I J K1 K2 C - -- where C is the completion number of this lump - 'W1' 0 0 2 3 2 / -- all with k = [2 <= k <= 3] -> {2, 4, 5} - 'W1' 2 2 1 5 7 / -- fix'd i,j, k = [1 <= k <= 5] -> {6, 7, 8} - / +COMPLUMP + -- name I J K1 K2 C + -- where C is the completion number of this lump + 'W1' 0 0 2 3 2 / -- all with k = [2 <= k <= 3] -> {2, 4, 5} + 'W1' 2 2 1 5 7 / -- fix'd i,j, k = [1 <= k <= 5] -> {6, 7, 8} +/ - WELOPEN - 'W1' OPEN 0 0 0 2 2 / -- open the new 2 {2, 4, 5} - 'W1' OPEN 0 0 0 5 7 / -- open 5..7 {5, 6, 7, 8} - / - )"; +WELOPEN + 'W1' OPEN 0 0 0 2 2 / -- open the new 2 {2, 4, 5} + 'W1' OPEN 0 0 0 5 7 / -- open 5..7 {5, 6, 7, 8} +/ +)"); constexpr auto open = Connection::State::OPEN; constexpr auto shut = Connection::State::SHUT; - auto deck = Parser().parseString( input); - auto python = std::make_shared(); - EclipseGrid grid( 10, 10, 10 ); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck); - Schedule schedule( deck, grid, fp, runspec, python); + const EclipseGrid grid(10, 10, 10); + const TableManager table (deck ); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec (deck); + const Schedule schedule(deck, grid, fp, runspec, std::make_shared()); const auto& cs1 = schedule.getWell("W1", 1).getConnections(); const auto& cs2 = schedule.getWell("W1", 2).getConnections(); @@ -3843,9 +3927,16 @@ BOOST_AUTO_TEST_CASE(WTEST_CONFIG) { } -static bool has(const std::vector& l, const std::string& s) { - auto f = std::find(l.begin(), l.end(), s); - return (f != l.end()); +namespace { + + bool has(const std::vector& l, const std::string& s) + { + return std::any_of(l.begin(), l.end(), + [&s](const std::string& search) + { + return search == s; + }); + } } @@ -3883,20 +3974,10 @@ BOOST_AUTO_TEST_CASE(WELL_STATIC) { const auto& connections = ws.getConnections(); BOOST_CHECK_EQUAL(connections.size(), 0U); auto c2 = std::make_shared(Connection::Order::TRACK, 1,1); - c2->addConnection(1,1,1, - grid1.getGlobalIndex(1,1,1), - 100, + c2->addConnection(1, 1, 1, + grid1.getGlobalIndex(1, 1, 1), Connection::State::OPEN, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 100); + 100.0, Connection::CTFProperties{}, 10); BOOST_CHECK( ws.updateConnections(c2, false) ); BOOST_CHECK( !ws.updateConnections(c2, false) ); @@ -4869,12 +4950,16 @@ END } } +namespace { + void cmp_vector(const std::vector&v1, const std::vector& v2) { BOOST_CHECK_EQUAL(v1.size(), v2.size()); for (std::size_t i = 0; i < v1.size(); i++) BOOST_CHECK_CLOSE(v1[i], v2[i], 1e-4); } +} // Anonymous namespace + BOOST_AUTO_TEST_CASE(VFPPROD_SCALING) { const auto deck = Parser{}.parseFile("VFP_CASE.DATA"); const auto es = EclipseState{ deck }; @@ -5128,6 +5213,8 @@ END BOOST_CHECK_EQUAL( netbalan1.thp_max_iter(), 5 ); } +namespace { + bool compare_dates(const time_point& t, int year, int month, int day) { return t == TimeService::from_time_t( asTimeT( TimeStampUTC(year, month, day))); } @@ -5141,6 +5228,7 @@ std::string dates_msg(const time_point& t, std::array& ymd) { return fmt::format("Different dates: {}-{}-{} != {}-{}-{}", ts.year(), ts.month(), ts.day(), ymd[0], ymd[1], ymd[2]); } +} // Anonymous namespace BOOST_AUTO_TEST_CASE(ScheduleStateDatesTest) { const auto& sched = make_schedule(createDeckWTEST()); @@ -5692,7 +5780,7 @@ END BOOST_AUTO_TEST_CASE(Test_wdfac) { - std::string input = R"( + const auto deck = Parser{}.parseString(R"( DIMENS 10 10 10 / @@ -5718,7 +5806,7 @@ PERMY 1000*10 / PERMZ 1000*10 / - + SCHEDULE DATES -- 1 @@ -5730,10 +5818,10 @@ WELSPECS / COMPDAT - 'W1' 3 3 1 1 'OPEN' 1* 1 0.216 200 1* 1* 'X' / - 'W1' 3 3 2 2 'OPEN' 1* 2 0.216 200 1* 1* 'X' / - 'W1' 3 3 3 3 'OPEN' 1* 3 0.216 200 1* 1* 'X' / - 'W2' 3 3 3 3 'OPEN' 1* 1 0.216 200 1* 11 'X' / + 'W1' 3 3 1 1 'OPEN' 1* 1 0.216 200 1* 1* 'X' / + 'W1' 3 3 2 2 'OPEN' 1* 2 0.216 200 1* 1* 'X' / + 'W1' 3 3 3 3 'OPEN' 1* 3 0.216 200 1* 1* 'X' / + 'W2' 3 3 3 3 'OPEN' 1* 1 0.216 200 1* 11 'X' / / WDFAC @@ -5746,10 +5834,10 @@ DATES -- 2 / COMPDAT - 'W1' 3 3 1 1 'OPEN' 1* 1* 0.216 200 1* 1* 'X' / - 'W1' 3 3 2 2 'OPEN' 1* 1* 0.216 200 1* 1* 'X' / - 'W1' 3 3 3 3 'OPEN' 1* 1* 0.216 200 1* 1* 'X' / - 'W2' 3 3 3 3 'OPEN' 1* 1 0.216 200 1* 11 'X' / + 'W1' 3 3 1 1 'OPEN' 1* 1* 0.216 200 1* 1* 'X' / + 'W1' 3 3 2 2 'OPEN' 1* 1* 0.216 200 1* 1* 'X' / + 'W1' 3 3 3 3 'OPEN' 1* 1* 0.216 200 1* 1* 'X' / + 'W2' 3 3 3 3 'OPEN' 1* 1 0.216 200 1* 11 'X' / / WDFACCOR @@ -5762,57 +5850,65 @@ DATES -- 3 / COMPDAT - 'W1' 3 3 1 1 'OPEN' 1* 1 0.216 200 1* 1* 'X' / - 'W1' 3 3 2 2 'OPEN' 1* 2 0.216 200 1* 0 'X' / - 'W1' 3 3 3 3 'OPEN' 1* 3 0.216 200 1* 11 'X' / - 'W2' 3 3 3 3 'OPEN' 1* 1 0.216 200 1* 11 'X' / + 'W1' 3 3 1 1 'OPEN' 1* 1 0.216 200 1* 1* 'X' / + 'W1' 3 3 2 2 'OPEN' 1* 2 0.216 200 1* 0 'X' / + 'W1' 3 3 3 3 'OPEN' 1* 3 0.216 200 1* 11 'X' / + 'W2' 3 3 3 3 'OPEN' 1* 1 0.216 200 1* 11 'X' / / - END +)"); -)"; - Deck deck = Parser{}.parseString(input); const auto es = EclipseState { deck }; const auto sched = Schedule { deck, es, std::make_shared() }; - const auto& well11 = sched.getWell("W1", 1); - const auto& well21 = sched.getWell("W2", 1); - const auto& wdfac11 = well11.getWDFAC(); - const auto& wdfac21 = well21.getWDFAC(); + const auto dFacUnit = 1*unit::day/unit::cubic(unit::meter); - double rho = 1.0; - double mu = 0.01*Opm::prefix::centi * Opm::unit::Poise; - double phi = 0.3; + const double rho = 1.0; + const double mu = 0.01*prefix::centi*unit::Poise; + const double phi = 0.3; - // WDFAC overwrites D factor in COMDAT - BOOST_CHECK(wdfac11.useDFactor()); + { + const auto& well11 = sched.getWell("W1", 1); + const auto& well21 = sched.getWell("W2", 1); + const auto& wdfac11 = well11.getWDFAC(); + const auto& wdfac21 = well21.getWDFAC(); - // well d factor scaled by connection CF. - BOOST_CHECK_CLOSE(wdfac11.getDFactor(well11.getConnections()[0], mu, rho, phi), 6*1*Opm::unit::day, 1e-12); - BOOST_CHECK_CLOSE(wdfac21.getDFactor(well21.getConnections()[0], mu, rho, phi), 2*Opm::unit::day, 1e-12); - - const auto& well12 = sched.getWell("W1", 2); - const auto& well22 = sched.getWell("W2", 2); - const auto& wdfac12 = well12.getWDFAC(); - const auto& wdfac22 = well22.getWDFAC(); + // WDFAC overwrites D factor in COMDAT + BOOST_CHECK_MESSAGE(wdfac11.useDFactor(), + R"(Well "W1" must use D-Factors at step 1)"); - BOOST_CHECK_CLOSE(wdfac12.getDFactor(well12.getConnections()[0], mu, rho, phi), 5.19e-1, 3); - BOOST_CHECK_CLOSE(wdfac22.getDFactor(well22.getConnections()[0], mu, rho, phi), 2*Opm::unit::day, 1e-12); + // Well-level D-factor scaled by connection transmissibility factor. + BOOST_CHECK_CLOSE(wdfac11.getDFactor(well11.getConnections()[0], mu, rho, phi), 6*1.0*dFacUnit, 1e-12); + BOOST_CHECK_CLOSE(wdfac21.getDFactor(well21.getConnections()[0], mu, rho, phi), 2.0*dFacUnit, 1e-12); + } + { + const auto& well12 = sched.getWell("W1", 2); + const auto& well22 = sched.getWell("W2", 2); + const auto& wdfac12 = well12.getWDFAC(); + const auto& wdfac22 = well22.getWDFAC(); - const auto& well13 = sched.getWell("W1", 3); - const auto& well23 = sched.getWell("W2", 3); - const auto& wdfac13 = well13.getWDFAC(); - const auto& wdfac23 = well23.getWDFAC(); - BOOST_CHECK(wdfac13.useDFactor()); + BOOST_CHECK_CLOSE(wdfac12.getDFactor(well12.getConnections()[0], mu, rho, phi), 5.19e-1, 3); + BOOST_CHECK_CLOSE(wdfac22.getDFactor(well22.getConnections()[0], mu, rho, phi), 2.0*dFacUnit, 1e-12); + } + { + const auto& well13 = sched.getWell("W1", 3); + const auto& well23 = sched.getWell("W2", 3); + const auto& wdfac13 = well13.getWDFAC(); + const auto& wdfac23 = well23.getWDFAC(); - BOOST_CHECK_CLOSE(well13.getConnections()[0].dFactor(), 0*Opm::unit::day, 1e-12); - BOOST_CHECK_CLOSE(well13.getConnections()[1].dFactor(), 0*Opm::unit::day, 1e-12); - BOOST_CHECK_CLOSE(well13.getConnections()[2].dFactor(), 11*Opm::unit::day, 1e-12); - BOOST_CHECK_CLOSE(wdfac13.getDFactor(well13.getConnections()[2], mu, rho, phi), 6/3*11*Opm::unit::day, 1e-12); - BOOST_CHECK_CLOSE(wdfac23.getDFactor(well23.getConnections()[0], mu, rho, phi), 2*Opm::unit::day, 1e-12); + BOOST_CHECK_MESSAGE(wdfac13.useDFactor(), + R"(Well "W1" must use D-Factors at step 3)"); + + BOOST_CHECK_CLOSE(well13.getConnections()[0].dFactor(), 0.0*dFacUnit, 1e-12); + BOOST_CHECK_CLOSE(well13.getConnections()[1].dFactor(), 0.0*dFacUnit, 1e-12); + BOOST_CHECK_CLOSE(well13.getConnections()[2].dFactor(), 11.0*dFacUnit, 1e-12); + + BOOST_CHECK_CLOSE(wdfac13.getDFactor(well13.getConnections()[2], mu, rho, phi), 6.0/3.0*11.0*dFacUnit, 1e-12); + BOOST_CHECK_CLOSE(wdfac23.getDFactor(well23.getConnections()[0], mu, rho, phi), 2.0*dFacUnit, 1e-12); + } } BOOST_AUTO_TEST_CASE(createDeckWithBC) { diff --git a/tests/parser/WellSolventTests.cpp b/tests/parser/WellSolventTests.cpp index 390d404fa..dbe7a83fd 100644 --- a/tests/parser/WellSolventTests.cpp +++ b/tests/parser/WellSolventTests.cpp @@ -22,192 +22,219 @@ #include #include + #include #include #include #include + #include #include + #include #include #include #include + #include #include using namespace Opm; -static Deck createDeckWithOutSolvent() { - Opm::Parser parser; - std::string input = - "GRID\n" - "PERMX\n" - " 1000*0.25/\n" - "COPY\n" - " PERMX PERMY /\n" - " PERMX PERMZ /\n" - "/\n" - "SCHEDULE\n" - "WELSPECS\n" - " 'W_1' 'OP' 2 2 1* \'OIL\' 7* / \n" - "/\n" - "COMPDAT\n" - " 'W_1' 2* 1 1 'OPEN' / \n" - "/\n" - "WCONINJE\n" - " 'W_1' 'WATER' 'OPEN' 'BHP' 1 2 3/\n/\n"; +namespace { - return parser.parseString(input); +Deck createDeckWithOutSolvent() +{ + return Parser{}.parseString(R"( +GRID +PERMX + 1000*0.25/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO + 1000*0.3 / + +SCHEDULE +WELSPECS + 'W_1' 'OP' 2 2 1* 'OIL' 7* / +/ +COMPDAT + 'W_1' 2* 1 1 'OPEN' / +/ +WCONINJE + 'W_1' 'WATER' 'OPEN' 'BHP' 1 2 3/ +/ +END +)"); } -static Deck createDeckWithGasInjector() { - Opm::Parser parser; - std::string input = - "GRID\n" - "PERMX\n" - " 1000*0.25/\n" - "COPY\n" - " PERMX PERMY /\n" - " PERMX PERMZ /\n" - "/\n" - "SCHEDULE\n" - "WELSPECS\n" - " 'W_1' 'OP' 1 1 1* \'GAS\' 7* / \n" - "/\n" - "COMPDAT\n" - " 'W_1' 2* 1 1 'OPEN' / \n" - "/\n" - "WCONINJE\n" - " 'W_1' 'GAS' 'OPEN' 'BHP' 1 2 3/\n/\n" - "WSOLVENT\n" - " 'W_1' 1 / \n " - "/\n"; +Deck createDeckWithGasInjector() +{ + return Parser{}.parseString(R"( +GRID +PERMX + 1000*0.25/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO + 1000*0.3 / - return parser.parseString(input); +SCHEDULE +WELSPECS + 'W_1' 'OP' 1 1 1* 'GAS' 7* / +/ +COMPDAT + 'W_1' 2* 1 1 'OPEN' / +/ +WCONINJE + 'W_1' 'GAS' 'OPEN' 'BHP' 1 2 3/ +/ +WSOLVENT + 'W_1' 1 / +/ +END +)"); } -static Deck createDeckWithDynamicWSOLVENT() { - Opm::Parser parser; - std::string input = - "START -- 0 \n" - "1 JAN 2000 / \n" - "GRID\n" - "PERMX\n" - " 1000*0.25/\n" - "COPY\n" - " PERMX PERMY /\n" - " PERMX PERMZ /\n" - "/\n" - "SCHEDULE\n" - "WELSPECS\n" - " 'W_1' 'OP' 1 1 1* \'GAS\' 7* / \n" - "/\n" - "COMPDAT\n" - " 'W_1' 2* 1 1 'OPEN' / \n" - "/\n" - "WCONINJE\n" - " 'W_1' 'GAS' 'OPEN' 'BHP' 1 2 3/\n/\n" - "DATES -- 2\n" - " 1 MAY 2000 / \n" - "/\n" - "WSOLVENT\n" - " 'W_1' 1 / \n " - "/\n" - "DATES -- 3,4\n" - " 1 JUL 2000 / \n" - " 1 AUG 2000 / \n" - "/\n" - "WSOLVENT\n" - " 'W_1' 0 / \n " - "/\n"; +Deck createDeckWithDynamicWSOLVENT() +{ + return Parser{}.parseString(R"( +START -- 0 +1 JAN 2000 / +GRID +PERMX + 1000*0.25/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO + 1000*0.3 / - return parser.parseString(input); +SCHEDULE +WELSPECS + 'W_1' 'OP' 1 1 1* 'GAS' 7* / +/ +COMPDAT + 'W_1' 2* 1 1 'OPEN' / +/ +WCONINJE + 'W_1' 'GAS' 'OPEN' 'BHP' 1 2 3/ +/ +DATES -- 2 + 1 MAY 2000 / +/ +WSOLVENT + 'W_1' 1 / +/ +DATES -- 3,4 + 1 JUL 2000 / + 1 AUG 2000 / +/ +WSOLVENT + 'W_1' 0 / +/ +END +)"); } -static Deck createDeckWithOilInjector() { - Opm::Parser parser; - std::string input = - "GRID\n" - "PERMX\n" - " 1000*0.25/\n" - "COPY\n" - " PERMX PERMY /\n" - " PERMX PERMZ /\n" - "/\n" - "SCHEDULE\n" - "WELSPECS\n" - " 'W_1' 'OP' 2 2 1* \'OIL\' 7* / \n" - "/\n" - "COMPDAT\n" - " 'W_1' 2* 1 1 'OPEN' / \n" - "/\n" - "WCONINJE\n" - " 'W_1' 'OIL' 'OPEN' 'BHP' 1 2 3/\n/\n" - "WSOLVENT\n" - " 'W_1' 1 / \n " - "/\n"; - - return parser.parseString(input); +Deck createDeckWithOilInjector() +{ + return Parser{}.parseString(R"( +GRID +PERMX + 1000*0.25/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +SCHEDULE +WELSPECS + 'W_1' 'OP' 2 2 1* 'OIL' 7* / +/ +COMPDAT + 'W_1' 2* 1 1 'OPEN' / +/ +WCONINJE + 'W_1' 'OIL' 'OPEN' 'BHP' 1 2 3/ +/ +WSOLVENT + 'W_1' 1 / +/ +END +)"); } -static Deck createDeckWithWaterInjector() { - Opm::Parser parser; - std::string input = - "GRID\n" - "PERMX\n" - " 1000*0.25/\n" - "COPY\n" - " PERMX PERMY /\n" - " PERMX PERMZ /\n" - "/\n" - "SCHEDULE\n" - "WELSPECS\n" - " 'W_1' 'OP' 2 2 1* \'OIL\' 7* / \n" - "/\n" - "COMPDAT\n" - " 'W_1' 2* 1 1 'OPEN' / \n" - "/\n" - "WCONINJE\n" - " 'W_1' 'WATER' 'OPEN' 'BHP' 1 2 3/\n/\n" - "WSOLVENT\n" - " 'W_1' 1 / \n " - "/\n"; - - return parser.parseString(input); +Deck createDeckWithWaterInjector() +{ + return Parser{}.parseString(R"( +GRID +PERMX + 1000*0.25/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +SCHEDULE +WELSPECS + 'W_1' 'OP' 2 2 1* \'OIL\' 7* / +/ +COMPDAT + 'W_1' 2* 1 1 'OPEN' / +/ +WCONINJE + 'W_1' 'WATER' 'OPEN' 'BHP' 1 2 3/ +/ +WSOLVENT + 'W_1' 1 / +/ +END +)"); } -BOOST_AUTO_TEST_CASE(TestNoSolvent) { - auto deck = createDeckWithOutSolvent(); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); - Runspec runspec(deck); - Schedule schedule(deck, grid , fp, runspec, python); + +} // Anonymous namespace + +BOOST_AUTO_TEST_CASE(TestNoSolvent) +{ + const auto deck = createDeckWithOutSolvent(); + const EclipseGrid grid(10,10,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec(deck); + const Schedule schedule(deck, grid, fp, runspec, std::make_shared()); + BOOST_CHECK(!deck.hasKeyword("WSOLVENT")); } BOOST_AUTO_TEST_CASE(TestGasInjector) { - auto deck = createDeckWithGasInjector(); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); - Runspec runspec(deck); - Schedule schedule(deck, grid , fp, runspec, python); + const auto deck = createDeckWithGasInjector(); + const EclipseGrid grid(10,10,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec(deck); + const Schedule schedule(deck, grid, fp, runspec, std::make_shared()); + BOOST_CHECK(deck.hasKeyword("WSOLVENT")); } -BOOST_AUTO_TEST_CASE(TestDynamicWSOLVENT) { - auto deck = createDeckWithDynamicWSOLVENT(); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); - Runspec runspec(deck); - Schedule schedule(deck, grid , fp, runspec, python); +BOOST_AUTO_TEST_CASE(TestDynamicWSOLVENT) +{ + const auto deck = createDeckWithDynamicWSOLVENT(); + const EclipseGrid grid(10,10,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec(deck); + const Schedule schedule(deck, grid, fp, runspec, std::make_shared()); + BOOST_CHECK(deck.hasKeyword("WSOLVENT")); + const auto& keyword = deck["WSOLVENT"].back(); BOOST_CHECK_EQUAL(keyword.size(),1U); + const auto& record = keyword.getRecord(0); const std::string& well_name = record.getItem("WELL").getTrimmedString(0); BOOST_CHECK_EQUAL(well_name, "W_1"); @@ -217,22 +244,24 @@ BOOST_AUTO_TEST_CASE(TestDynamicWSOLVENT) { BOOST_CHECK_EQUAL(schedule.getWell("W_1", 3).getSolventFraction(),0); } -BOOST_AUTO_TEST_CASE(TestOilInjector) { - auto deck = createDeckWithOilInjector(); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); - Runspec runspec(deck); - BOOST_CHECK_THROW (Schedule(deck , grid , fp, runspec, python), std::exception); +BOOST_AUTO_TEST_CASE(TestOilInjector) +{ + const auto deck = createDeckWithOilInjector(); + const EclipseGrid grid(10,10,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec(deck); + + BOOST_CHECK_THROW (Schedule(deck, grid, fp, runspec, std::make_shared()), std::exception); } -BOOST_AUTO_TEST_CASE(TestWaterInjector) { - auto deck = createDeckWithWaterInjector(); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); - Runspec runspec(deck); - BOOST_CHECK_THROW (Schedule(deck, grid , fp, runspec, python), std::exception); +BOOST_AUTO_TEST_CASE(TestWaterInjector) +{ + const auto deck = createDeckWithWaterInjector(); + const EclipseGrid grid(10,10,10); + const TableManager table ( deck ); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec(deck); + + BOOST_CHECK_THROW (Schedule(deck, grid , fp, runspec, std::make_shared()), std::exception); } diff --git a/tests/parser/WellTracerTests.cpp b/tests/parser/WellTracerTests.cpp index 4696bbf29..120e09df7 100644 --- a/tests/parser/WellTracerTests.cpp +++ b/tests/parser/WellTracerTests.cpp @@ -22,141 +22,166 @@ #include #include + #include + #include #include #include #include + #include #include #include + #include #include #include #include + #include using namespace Opm; -static Deck createDeckWithOutTracer() { - Opm::Parser parser; - std::string input = - "GRID\n" - "PERMX\n" - " 1000*0.25/\n" - "COPY\n" - " PERMX PERMY /\n" - " PERMX PERMZ /\n" - "/\n" - "SCHEDULE\n" - "WELSPECS\n" - " 'W_1' 'OP' 2 2 1* \'OIL\' 7* / \n" - "/\n" - "COMPDAT\n" - " 'W_1' 2* 1 1 'OPEN' / \n" - "/\n" - "WCONINJE\n" - " 'W_1' 'WATER' 'OPEN' 'BHP' 1 2 3/\n/\n"; +namespace { - return parser.parseString(input); +Deck createDeckWithOutTracer() +{ + return Parser{}.parseString(R"( +GRID +PERMX + 1000*0.25/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO +1000*0.3 / + +SCHEDULE +WELSPECS + 'W_1' 'OP' 2 2 1* 'OIL' 7* / +/ +COMPDAT + 'W_1' 2* 1 1 'OPEN' / +/ +WCONINJE + 'W_1' 'WATER' 'OPEN' 'BHP' 1 2 3/ +/ +END +)"); } +Deck createDeckWithDynamicWTRACER() +{ + return Parser{}.parseString(R"( +START -- 0 +1 JAN 2000 / +GRID +PERMX + 1000*0.25/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO +1000*0.3 / -static Deck createDeckWithDynamicWTRACER() { - Opm::Parser parser; - std::string input = - "START -- 0 \n" - "1 JAN 2000 / \n" - "GRID\n" - "PERMX\n" - " 1000*0.25/\n" - "COPY\n" - " PERMX PERMY /\n" - " PERMX PERMZ /\n" - "/\n" - "SCHEDULE\n" - "WELSPECS\n" - " 'W_1' 'OP' 1 1 1* \'GAS\' 7* / \n" - "/\n" - "COMPDAT\n" - " 'W_1' 2* 1 1 'OPEN' / \n" - "/\n" - "WCONINJE\n" - " 'W_1' 'GAS' 'OPEN' 'BHP' 1 2 3/\n/\n" - "DATES -- 1\n" - " 1 MAY 2000 / \n" - "/\n" - "WTRACER\n" - " 'W_1' 'I1' 1 / \n " - " 'W_1' 'I2' 1 / \n " - "/\n" - "DATES -- 2, 3\n" - " 1 JUL 2000 / \n" - " 1 AUG 2000 / \n" - "/\n" - "WTRACER\n" - " 'W_1' 'I1' 0 / \n " - "/\n" - "DATES -- 4\n" - " 1 SEP 2000 / \n" - "/\n"; +SCHEDULE +WELSPECS + 'W_1' 'OP' 1 1 1* 'GAS' 7* / +/ +COMPDAT + 'W_1' 2* 1 1 'OPEN' / +/ +WCONINJE + 'W_1' 'GAS' 'OPEN' 'BHP' 1 2 3/ +/ +DATES -- 1 + 1 MAY 2000 / +/ +WTRACER + 'W_1' 'I1' 1 / + 'W_1' 'I2' 1 / +/ +DATES -- 2, 3 + 1 JUL 2000 / + 1 AUG 2000 / +/ +WTRACER + 'W_1' 'I1' 0 / +/ +DATES -- 4 + 1 SEP 2000 / +/ - return parser.parseString(input); +END +)"); } -static Deck createDeckWithTracerInProducer() { - Opm::Parser parser; - std::string input = - "START -- 0 \n" - "1 JAN 2000 / \n" - "GRID\n" - "PERMX\n" - " 1000*0.25/\n" - "COPY\n" - " PERMX PERMY /\n" - " PERMX PERMZ /\n" - "/\n" - "SCHEDULE\n" - "WELSPECS\n" - " 'W_1' 'OP' 1 1 1* \'GAS\' 7* / \n" - "/\n" - "COMPDAT\n" - " 'W_1' 2* 1 1 'OPEN' / \n" - "/\n" - "WCONPROD\n" - "'W_1' 'OPEN' 'ORAT' 20000 4* 1000 /\n" - "WTRACER\n" - " 'W_1' 'I1' 1 / \n " - " 'W_1' 'I2' 1 / \n " - "/\n"; +Deck createDeckWithTracerInProducer() +{ + return Parser{}.parseString(R"( +START -- 0 +1 JAN 2000 / +GRID +PERMX + 1000*0.25/ +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO + 1000*0.3 / - return parser.parseString(input); +SCHEDULE +WELSPECS + 'W_1' 'OP' 1 1 1* 'GAS' 7* / +/ +COMPDAT + 'W_1' 2* 1 1 'OPEN' / +/ +WCONPROD + 'W_1' 'OPEN' 'ORAT' 20000 4* 1000 / +WTRACER + 'W_1' 'I1' 1 / + 'W_1' 'I2' 1 / +/ +END +)"); } +} // Anonymous namespace + +BOOST_AUTO_TEST_CASE(TestNoTracer) +{ + const auto deck = createDeckWithOutTracer(); + + const EclipseGrid grid(10,10,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec (deck); + const Schedule schedule(deck, grid, fp, runspec, std::make_shared()); -BOOST_AUTO_TEST_CASE(TestNoTracer) { - auto deck = createDeckWithOutTracer(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); - auto python = std::make_shared(); - Runspec runspec ( deck ); - Schedule schedule(deck, grid , fp, runspec, python); BOOST_CHECK(!deck.hasKeyword("WTRACER")); } -BOOST_AUTO_TEST_CASE(TestDynamicWTRACER) { - auto deck = createDeckWithDynamicWTRACER(); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec ( deck ); - Schedule schedule(deck, grid , fp, runspec, python); +BOOST_AUTO_TEST_CASE(TestDynamicWTRACER) +{ + const auto deck = createDeckWithDynamicWTRACER(); + + const EclipseGrid grid(10,10,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec (deck); + const Schedule schedule(deck, grid, fp, runspec, std::make_shared()); + BOOST_CHECK(deck.hasKeyword("WTRACER")); + const auto& keyword = deck["WTRACER"].back(); BOOST_CHECK_EQUAL(keyword.size(),1U); + const auto& record = keyword.getRecord(0); const std::string& well_name = record.getItem("WELL").getTrimmedString(0); BOOST_CHECK_EQUAL(well_name, "W_1"); @@ -169,13 +194,13 @@ BOOST_AUTO_TEST_CASE(TestDynamicWTRACER) { } -BOOST_AUTO_TEST_CASE(TestTracerInProducerTHROW) { - auto deck = createDeckWithTracerInProducer(); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec ( deck ); +BOOST_AUTO_TEST_CASE(TestTracerInProducerTHROW) +{ + const auto deck = createDeckWithTracerInProducer(); + const EclipseGrid grid(10,10,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec (deck); - BOOST_CHECK_THROW(Schedule(deck, grid, fp, runspec, python), OpmInputError); + BOOST_CHECK_THROW(Schedule(deck, grid, fp, runspec, std::make_shared()), OpmInputError); } diff --git a/tests/parser/integration/ScheduleCreateFromDeck.cpp b/tests/parser/integration/ScheduleCreateFromDeck.cpp index 7e4bbd1bb..b96b6d071 100644 --- a/tests/parser/integration/ScheduleCreateFromDeck.cpp +++ b/tests/parser/integration/ScheduleCreateFromDeck.cpp @@ -518,34 +518,39 @@ BOOST_AUTO_TEST_CASE(WellTestWGRUPCONWellPropertiesSet) { } -BOOST_AUTO_TEST_CASE(TestDefaultedCOMPDATIJ) { - Parser parser; - const char * deckString = "\n\ -START\n\ -\n\ -10 MAI 2007 /\n\ -\n\ -GRID\n\ -PERMX\n\ - 9000*0.25 /\n\ -COPY \n\ - PERMX PERMY /\n\ - PERMX PERMZ /\n\ -/\n\ -SCHEDULE\n\ -WELSPECS \n\ - 'W1' 'OP' 11 21 3.33 'OIL' 7* / \n\ -/\n\ -COMPDAT \n\ - 'W1' 2* 1 1 'OPEN' 1* 32.948 0.311 3047.839 2* 'X' 22.100 /\n\ -/\n"; - auto deck = parser.parseString(deckString); - auto python = std::make_shared(); - EclipseGrid grid(30,30,10); - TableManager table ( deck ); - FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck); - Schedule sched(deck, grid , fp, runspec, python); +BOOST_AUTO_TEST_CASE(TestDefaultedCOMPDATIJ) +{ + const auto deck = Parser{}.parseString(R"( +START + +10 MAI 2007 / + +GRID +PERMX + 9000*0.25 / +COPY + PERMX PERMY / + PERMX PERMZ / +/ +PORO + 9000*0.3 / + +SCHEDULE +WELSPECS + 'W1' 'OP' 11 21 3.33 'OIL' 7* / +/ +COMPDAT + 'W1' 2* 1 1 'OPEN' 1* 32.948 0.311 3047.839 2* 'X' 22.100 / +/ +END +)"); + + const EclipseGrid grid(30,30,10); + const TableManager table (deck); + const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); + const Runspec runspec (deck); + const Schedule sched(deck, grid, fp, runspec, std::make_shared()); + const auto& connections = sched.getWell("W1", 0).getConnections(); BOOST_CHECK_EQUAL( 10 , connections.get(0).getI() ); BOOST_CHECK_EQUAL( 20 , connections.get(0).getJ() ); @@ -907,4 +912,3 @@ BOOST_AUTO_TEST_CASE(TestWellEvents) { BOOST_CHECK( sched[0].wellgroup_events().hasEvent( "W_1", ScheduleEvents::COMPLETION_CHANGE)); BOOST_CHECK( sched[5].wellgroup_events().hasEvent( "W_1", ScheduleEvents::COMPLETION_CHANGE)); } - diff --git a/tests/test_PAvgCalculator.cpp b/tests/test_PAvgCalculator.cpp index 9369ce6ec..22977b91d 100644 --- a/tests/test_PAvgCalculator.cpp +++ b/tests/test_PAvgCalculator.cpp @@ -57,12 +57,22 @@ namespace { const auto j = (dims[1] - 1) - 1; for (auto k = top; k < static_cast(dims.size()); ++k) { + const auto depth = 2000 + (2*k + 1) / static_cast(2); + + auto ctf_props = Opm::Connection::CTFProperties{}; + ctf_props.CF = k / 100.0; + ctf_props.Kh = 1.0; + ctf_props.Ke = 1.0; + ctf_props.rw = 1.0; + ctf_props.r0 = 0.5; + ctf_props.re = 0.5; + ctf_props.connection_length = 1.0; + conns.emplace_back(i, j, k, globIndex({i, j, k}, dims), k, - 2000 + (2*k + 1) / static_cast(2), Opm::Connection::State::OPEN, - k / 100.0, 1.0, 1.0, 0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::Z, - Opm::Connection::CTFKind::DeckValue, k, false); + Opm::Connection::CTFKind::DeckValue, + 0, depth, ctf_props, k, false); } return { Opm::Connection::Order::INPUT, i, j, conns }; @@ -87,18 +97,28 @@ namespace { }; for (auto k = topConn; k < kMax; ++k) { + const auto depth = 2000 + (2*k + 1) / static_cast(2); + + auto ctf_props = Opm::Connection::CTFProperties{}; + + // 0.03, 0.0, 0.01, 0.02, 0.03, ... + ctf_props.CF = ((k + 3 - topConn) % 4) / 100.0; + + ctf_props.Kh = 1.0; + ctf_props.Ke = 1.0; + ctf_props.rw = 1.0; + ctf_props.r0 = 0.5; + ctf_props.re = 0.5; + ctf_props.connection_length = 1.0; + conns.emplace_back(i, j, k, globIndex({i, j, k}, dims), k - topConn, - 2000 + (2*k + 1) / static_cast(2), // Open, Shut, Open, Open, Shut, ... state[(k - topConn) % state.size()], - // 0.03, 0.0, 0.01, 0.02, 0.03, ... - ((k + 3 - topConn) % 4) / 100.0, - - 1.0, 1.0, 0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::Z, - Opm::Connection::CTFKind::DeckValue, k - topConn, false); + Opm::Connection::CTFKind::DeckValue, + 0, depth, ctf_props, k - topConn, false); } return { Opm::Connection::Order::INPUT, i, j, conns }; @@ -115,12 +135,22 @@ namespace { const auto iMax = std::min(dims[0] - 1, left + numConns); for (auto i = left; i < iMax; ++i) { + const auto depth = 2000 + (2*k + 1) / static_cast(2); + + auto ctf_props = Opm::Connection::CTFProperties{}; + ctf_props.CF = i / 100.0; + ctf_props.Kh = 1.0; + ctf_props.Ke = 1.0; + ctf_props.rw = 1.0; + ctf_props.r0 = 0.5; + ctf_props.re = 0.5; + ctf_props.connection_length = 1.0; + conns.emplace_back(i, j, k, globIndex({i, j, k}, dims), i - left, - 2000 + (2*k + 1) / static_cast(2), Opm::Connection::State::OPEN, - i / 100.0, 1.0, 1.0, 0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, - Opm::Connection::CTFKind::DeckValue, i - left, false); + Opm::Connection::CTFKind::DeckValue, + 0, depth, ctf_props, i - left, false); } return { Opm::Connection::Order::INPUT, left, j, conns }; diff --git a/tests/test_Serialization.cpp b/tests/test_Serialization.cpp index c9d3710e6..9a6946813 100644 --- a/tests/test_Serialization.cpp +++ b/tests/test_Serialization.cpp @@ -193,6 +193,7 @@ TEST_FOR_TYPE_NAMED(Action::State, ActionState) TEST_FOR_TYPE(BCConfig) TEST_FOR_TYPE(BrineDensityTable) TEST_FOR_TYPE(ColumnSchema) +TEST_FOR_TYPE_NAMED(Connection::CTFProperties, CTFProperties) TEST_FOR_TYPE(Connection) TEST_FOR_TYPE_NAMED_OBJ(data::AquiferData, AquiferData_CarterTracy, serializationTestObjectC) TEST_FOR_TYPE_NAMED_OBJ(data::AquiferData, AquiferData_Fetkovich, serializationTestObjectF) From f790e81e9a3815c4be14e35085087b992aca3931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 10 Nov 2023 12:52:49 +0100 Subject: [PATCH 2/7] Make Connecting Cell's Background Porosity Available in ScheduleGrid The porosity is needed for WDFACCOR-related connection output to the restart file. --- opm/input/eclipse/Schedule/CompletedCells.hpp | 82 +++++--------- opm/input/eclipse/Schedule/ScheduleGrid.hpp | 25 ++-- .../input/eclipse/Schedule/CompletedCells.cpp | 99 ++++++++++++---- .../input/eclipse/Schedule/ScheduleGrid.cpp | 107 +++++++++++------- tests/parser/ConnectionTests.cpp | 7 ++ .../SCHEDULE/SCHEDULE_COMPDAT1 | 2 + .../SCHEDULE/SCHEDULE_EVENTS | 3 + .../integration_tests/SCHEDULE/SCHEDULE_FOAM | 3 + .../SCHEDULE/SCHEDULE_POLYMER | 3 + .../SCHEDULE/SCHEDULE_SHUT_WELL | 2 + .../integration_tests/SCHEDULE/SCHEDULE_WECON | 2 + .../SCHEDULE/SCHEDULE_WELLS2 | 3 + 12 files changed, 215 insertions(+), 123 deletions(-) diff --git a/opm/input/eclipse/Schedule/CompletedCells.hpp b/opm/input/eclipse/Schedule/CompletedCells.hpp index 38d822d99..c1a6089a3 100644 --- a/opm/input/eclipse/Schedule/CompletedCells.hpp +++ b/opm/input/eclipse/Schedule/CompletedCells.hpp @@ -16,40 +16,42 @@ You should have received a copy of the GNU General Public License along with OPM. If not, see . */ + #ifndef COMPLETED_CELLS #define COMPLETED_CELLS -#include -#include #include +#include +#include +#include +#include +#include + namespace Opm { -class CompletedCells { +class CompletedCells +{ public: - - struct Cell { + struct Cell + { std::size_t global_index; std::size_t i, j, k; - struct Props{ - std::size_t active_index; - double permx; - double permy; - double permz; - int satnum; - int pvtnum; - double ntg; + struct Props + { + std::size_t active_index{}; + double permx{}; + double permy{}; + double permz{}; + double poro{}; + int satnum{}; + int pvtnum{}; + double ntg{}; - bool operator==(const Props& other) const{ - return this->active_index == other.active_index && - this->permx == other.permx && - this->permy == other.permy && - this->permz == other.permz && - this->satnum == other.satnum && - this->pvtnum == other.pvtnum && - this->ntg == other.ntg; - } + bool operator==(const Props& other) const; + + static Props serializationTestObject(); template void serializeOp(Serializer& serializer) @@ -57,46 +59,23 @@ public: serializer(this->permx); serializer(this->permy); serializer(this->permz); + serializer(this->poro); serializer(this->satnum); serializer(this->pvtnum); serializer(this->ntg); } - - static Props serializationTestObject(){ - Props props; - props.permx = 10.0; - props.permy = 78.0; - props.permz = 45.4; - props.satnum = 3; - props.pvtnum = 5; - props.ntg = 45.1; - return props; - } }; std::optional props; std::size_t active_index() const; bool is_active() const; - double depth; - std::array dimensions; + double depth{}; + std::array dimensions{}; - bool operator==(const Cell& other) const { - return this->global_index == other.global_index && - this->i == other.i && - this->j == other.j && - this->k == other.k && - this->depth == other.depth && - this->dimensions == other.dimensions && - this->props == other.props; - } + bool operator==(const Cell& other) const; - static Cell serializationTestObject() { - Cell cell(0,1,1,1); - cell.depth = 12345; - cell.dimensions = {1.0,2.0,3.0}; - return cell; - } + static Cell serializationTestObject(); template void serializeOp(Serializer& serializer) @@ -123,6 +102,7 @@ public: CompletedCells() = default; explicit CompletedCells(const GridDims& dims); CompletedCells(std::size_t nx, std::size_t ny, std::size_t nz); + const Cell& get(std::size_t i, std::size_t j, std::size_t k) const; std::pair try_get(std::size_t i, std::size_t j, std::size_t k); @@ -141,5 +121,5 @@ private: std::unordered_map cells; }; } -#endif +#endif // COMPLETED_CELLS diff --git a/opm/input/eclipse/Schedule/ScheduleGrid.hpp b/opm/input/eclipse/Schedule/ScheduleGrid.hpp index 210f76f59..764bdce21 100644 --- a/opm/input/eclipse/Schedule/ScheduleGrid.hpp +++ b/opm/input/eclipse/Schedule/ScheduleGrid.hpp @@ -21,17 +21,30 @@ #include +#include +#include + namespace Opm { class EclipseGrid; class FieldPropsManager; -class ScheduleGrid { +} // namespace Opm + +namespace Opm { + +class ScheduleGrid +{ public: - ScheduleGrid(const EclipseGrid& ecl_grid, const FieldPropsManager& fpm, CompletedCells& completed_cells); + ScheduleGrid(const EclipseGrid& ecl_grid, + const FieldPropsManager& fpm, + CompletedCells& completed_cells); + explicit ScheduleGrid(CompletedCells& completed_cells); - const CompletedCells::Cell& get_cell(std::size_t i, std::size_t j, std::size_t k) const; + const CompletedCells::Cell& + get_cell(std::size_t i, std::size_t j, std::size_t k) const; + const Opm::EclipseGrid* get_grid() const; private: @@ -40,8 +53,6 @@ private: CompletedCells& cells; }; +} // namespace Opm - -} -#endif - +#endif // SCHEDULE_GRID diff --git a/src/opm/input/eclipse/Schedule/CompletedCells.cpp b/src/opm/input/eclipse/Schedule/CompletedCells.cpp index ee5a0f3e4..b157dbe40 100644 --- a/src/opm/input/eclipse/Schedule/CompletedCells.cpp +++ b/src/opm/input/eclipse/Schedule/CompletedCells.cpp @@ -19,50 +19,105 @@ #include +bool Opm::CompletedCells::Cell::Props::operator==(const Props& other) const +{ + return (this->active_index == other.active_index) + && (this->permx == other.permx) + && (this->permy == other.permy) + && (this->permz == other.permz) + && (this->poro == other.poro) + && (this->satnum == other.satnum) + && (this->pvtnum == other.pvtnum) + && (this->ntg == other.ntg) + ; +} + +Opm::CompletedCells::Cell::Props +Opm::CompletedCells::Cell::Props::serializationTestObject() +{ + Props props; + + props.permx = 10.0; + props.permy = 78.0; + props.permz = 45.4; + props.poro = 0.321; + props.satnum = 3; + props.pvtnum = 5; + props.ntg = 45.1; + + return props; +} + +bool Opm::CompletedCells::Cell::operator==(const Cell& other) const +{ + return (this->global_index == other.global_index) + && (this->i == other.i) + && (this->j == other.j) + && (this->k == other.k) + && (this->depth == other.depth) + && (this->dimensions == other.dimensions) + && (this->props == other.props) + ; +} + +Opm::CompletedCells::Cell +Opm::CompletedCells::Cell::serializationTestObject() +{ + Cell cell { 0, 1, 1, 1 }; + + cell.props = Props::serializationTestObject(); + cell.depth = 12345; + cell.dimensions = {1.0,2.0,3.0}; + + return cell; +} Opm::CompletedCells::CompletedCells(std::size_t nx, std::size_t ny, std::size_t nz) - : dims(nx,ny,nz) + : dims(nx, ny, nz) {} - Opm::CompletedCells::CompletedCells(const Opm::GridDims& dims_) - :dims(dims_) + : dims(dims_) {} - -const Opm::CompletedCells::Cell& Opm::CompletedCells::get(std::size_t i, std::size_t j, std::size_t k) const { - auto g = this->dims.getGlobalIndex(i,j,k); - return this->cells.at(g); +const Opm::CompletedCells::Cell& +Opm::CompletedCells::get(std::size_t i, std::size_t j, std::size_t k) const +{ + return this->cells.at(this->dims.getGlobalIndex(i, j, k)); } +std::pair +Opm::CompletedCells::try_get(std::size_t i, std::size_t j, std::size_t k) +{ + const auto g = this->dims.getGlobalIndex(i, j, k); -std::pair Opm::CompletedCells::try_get(std::size_t i, std::size_t j, std::size_t k) { - auto g = this->dims.getGlobalIndex(i,j,k); - auto iter = this->cells.find(g); - if (iter != this->cells.end()) - return {true, iter->second}; + const auto& [pos, inserted] = this->cells.try_emplace(g, g, i, j, k); - this->cells.emplace(g, Cell{g,i,j,k}); - return {false, this->cells.at(g)}; + return { !inserted, pos->second }; } - -bool Opm::CompletedCells::operator==(const Opm::CompletedCells& other) const { - return this->dims == other.dims && - this->cells == other.cells; +bool Opm::CompletedCells::operator==(const Opm::CompletedCells& other) const +{ + return (this->dims == other.dims) + && (this->cells == other.cells) + ; } - -Opm::CompletedCells Opm::CompletedCells::serializationTestObject() { +Opm::CompletedCells +Opm::CompletedCells::serializationTestObject() +{ Opm::CompletedCells cells(2,3,4); cells.cells.emplace(7, Opm::CompletedCells::Cell::serializationTestObject()); + return cells; } -std::size_t Opm::CompletedCells::Cell::active_index() const{ +std::size_t Opm::CompletedCells::Cell::active_index() const +{ return this->props.value().active_index; } -bool Opm::CompletedCells::Cell::is_active() const{ +bool Opm::CompletedCells::Cell::is_active() const +{ return this->props.has_value(); } diff --git a/src/opm/input/eclipse/Schedule/ScheduleGrid.cpp b/src/opm/input/eclipse/Schedule/ScheduleGrid.cpp index e40b7892f..8bee88787 100644 --- a/src/opm/input/eclipse/Schedule/ScheduleGrid.cpp +++ b/src/opm/input/eclipse/Schedule/ScheduleGrid.cpp @@ -17,16 +17,24 @@ along with OPM. If not, see . */ -#include -#include #include + +#include + #include #include -Opm::ScheduleGrid::ScheduleGrid(const Opm::EclipseGrid& ecl_grid, const Opm::FieldPropsManager& fpm, Opm::CompletedCells& completed_cells) - : grid(&ecl_grid) - , fp(&fpm) - , cells(completed_cells) +#include +#include + +#include + +Opm::ScheduleGrid::ScheduleGrid(const Opm::EclipseGrid& ecl_grid, + const Opm::FieldPropsManager& fpm, + Opm::CompletedCells& completed_cells) + : grid { &ecl_grid } + , fp { &fpm } + , cells { completed_cells } {} Opm::ScheduleGrid::ScheduleGrid(Opm::CompletedCells& completed_cells) @@ -34,45 +42,58 @@ Opm::ScheduleGrid::ScheduleGrid(Opm::CompletedCells& completed_cells) {} namespace { - double try_get_value(const Opm::FieldPropsManager& fp, const std::string& kw, std::size_t active_index) { - if (fp.has_double(kw)) - return fp.try_get(kw)->at(active_index); - else + double try_get_value(const Opm::FieldPropsManager& fp, + const std::string& kw, + const std::size_t active_index) + { + if (! fp.has_double(kw)) { throw std::logic_error(fmt::format("FieldPropsManager is missing keyword '{}'", kw)); - } - - double try_get_ntg_value(const Opm::FieldPropsManager& fp, const std::string& kw, std::size_t active_index){ - if (fp.has_double(kw)) - return fp.try_get(kw)->at(active_index); - else - return 1.0; - } -} - -const Opm::CompletedCells::Cell& Opm::ScheduleGrid::get_cell(std::size_t i, std::size_t j, std::size_t k) const { - if (this->grid) { - auto [valid, cell] = this->cells.try_get(i,j,k); - if (!valid) { - cell.depth = this->grid->getCellDepth(i,j,k); - cell.dimensions = this->grid->getCellDimensions(i,j,k); - if (this->grid->cellActive(i,j,k)){ - CompletedCells::Cell::Props props; - - props.active_index = this->grid->getActiveIndex(i,j,k); - props.permx = try_get_value(*this->fp, "PERMX", props.active_index); - props.permy = try_get_value(*this->fp, "PERMY", props.active_index); - props.permz = try_get_value(*this->fp, "PERMZ", props.active_index); - props.satnum = this->fp->get_int("SATNUM").at(props.active_index); - props.pvtnum = this->fp->get_int("PVTNUM").at(props.active_index); - props.ntg = try_get_ntg_value(*this->fp, "NTG", props.active_index); - cell.props = props; - } } - return cell; - } else - return this->cells.get(i,j,k); + + return fp.try_get(kw)->at(active_index); + } + + double try_get_ntg_value(const Opm::FieldPropsManager& fp, + const std::string& kw, + const std::size_t active_index) + { + return fp.has_double(kw) + ? fp.try_get(kw)->at(active_index) + : 1.0; + } } -const Opm::EclipseGrid* Opm::ScheduleGrid::get_grid() const { - return this->grid; +const Opm::CompletedCells::Cell& +Opm::ScheduleGrid::get_cell(std::size_t i, std::size_t j, std::size_t k) const +{ + if (this->grid == nullptr) { + return this->cells.get(i, j, k); + } + + auto [valid, cellRef] = this->cells.try_get(i, j, k); + + if (!valid) { + cellRef.depth = this->grid->getCellDepth(i, j, k); + cellRef.dimensions = this->grid->getCellDimensions(i, j, k); + + if (this->grid->cellActive(i, j, k)) { + auto& props = cellRef.props.emplace(CompletedCells::Cell::Props{}); + + props.active_index = this->grid->getActiveIndex(i, j, k); + props.permx = try_get_value(*this->fp, "PERMX", props.active_index); + props.permy = try_get_value(*this->fp, "PERMY", props.active_index); + props.permz = try_get_value(*this->fp, "PERMZ", props.active_index); + props.poro = try_get_value(*this->fp, "PORO", props.active_index); + props.satnum = this->fp->get_int("SATNUM").at(props.active_index); + props.pvtnum = this->fp->get_int("PVTNUM").at(props.active_index); + props.ntg = try_get_ntg_value(*this->fp, "NTG", props.active_index); + } + } + + return cellRef; +} + +const Opm::EclipseGrid* Opm::ScheduleGrid::get_grid() const +{ + return this->grid; } diff --git a/tests/parser/ConnectionTests.cpp b/tests/parser/ConnectionTests.cpp index 0fe009226..7e32848e6 100644 --- a/tests/parser/ConnectionTests.cpp +++ b/tests/parser/ConnectionTests.cpp @@ -263,6 +263,10 @@ COPY 'PERMX' 'PERMZ' / 'PERMX' 'PERMY' / / + +PORO + 1000*0.3 / + SCHEDULE COMPDAT @@ -288,6 +292,9 @@ COPY 'PERMX' 'PERMY' / / +PORO + 1000*0.3 / + SCHEDULE COMPDAT diff --git a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_COMPDAT1 b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_COMPDAT1 index 72c364c33..b8a6331bf 100644 --- a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_COMPDAT1 +++ b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_COMPDAT1 @@ -14,6 +14,8 @@ COPY PERMX PERMZ / / +PORO + 1000*0.3 / DX 1000*1 / diff --git a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_EVENTS b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_EVENTS index 04aad5c3c..4c18fcdaf 100644 --- a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_EVENTS +++ b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_EVENTS @@ -16,6 +16,9 @@ COPY PERMX PERMZ / / +PORO + 48000*0.3 / + SCHEDULE WELSPECS diff --git a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_FOAM b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_FOAM index 65447eaab..58eb04007 100644 --- a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_FOAM +++ b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_FOAM @@ -17,6 +17,9 @@ COPY PERMX PERMZ / / +PORO + 27000*0.3 / + SCHEDULE diff --git a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_POLYMER b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_POLYMER index 8f311be25..0531f2312 100644 --- a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_POLYMER +++ b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_POLYMER @@ -15,6 +15,9 @@ COPY PERMX PERMZ / / +PORO + 27000*0.3 / + SCHEDULE diff --git a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_SHUT_WELL b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_SHUT_WELL index 855b67674..dead6e100 100644 --- a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_SHUT_WELL +++ b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_SHUT_WELL @@ -17,6 +17,8 @@ COPY PERMX PERMZ / / +PORO + 800*0.3 / SCHEDULE diff --git a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WECON b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WECON index 45d14f690..7335614b8 100644 --- a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WECON +++ b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WECON @@ -16,6 +16,8 @@ COPY PERMX PERMZ / / +PORO + 27000*0.3 / SCHEDULE diff --git a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELLS2 b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELLS2 index a6378f6d2..14cd6e962 100644 --- a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELLS2 +++ b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELLS2 @@ -13,6 +13,9 @@ COPY PERMX PERMZ / / +PORO + 72000*0.3 / + SCHEDULE -- 0 From 736eb9f7dbab016a44d6c60c81763e91d7ae28e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 10 Nov 2023 15:26:44 +0100 Subject: [PATCH 3/7] Pull Dake Model Correlation Parameters Out to Helper Structure Some of these parameters must be output to the restart file so make it easier to refer to them as a group. --- opm/input/eclipse/Schedule/Well/WDFAC.hpp | 175 +++++++++++++++--- src/opm/input/eclipse/Schedule/Well/WDFAC.cpp | 165 ++++++++++------- tests/test_Serialization.cpp | 5 + 3 files changed, 256 insertions(+), 89 deletions(-) diff --git a/opm/input/eclipse/Schedule/Well/WDFAC.hpp b/opm/input/eclipse/Schedule/Well/WDFAC.hpp index 3364d91f5..1a6455f77 100644 --- a/opm/input/eclipse/Schedule/Well/WDFAC.hpp +++ b/opm/input/eclipse/Schedule/Well/WDFAC.hpp @@ -21,6 +21,7 @@ #define WDFAC_HPP_HEADER_INCLUDED namespace Opm { + class Connection; class DeckRecord; class WellConnections; } // namespace Opm @@ -31,49 +32,177 @@ namespace Opm { namespace RestartIO { namespace Opm { - enum class WDFACTYPE { - NONE = 1, - DFACTOR = 2, - DAKEMODEL = 3, - CON_DFACTOR = 4 - }; - class WDFAC { public: + /// Parameters for Dake's D-factor correlation model. + /// + /// In particular, holds the coefficient 'A' and the exponents 'B' + /// and 'C' of the correlation relation + /// + /// D = A * (Ke/K0)**B * porosity**C * Ke / (h * rw) * (sg_g/mu_g) + /// + /// in which + /// + /// * Ke is the connection's effective permeability (sqrt(Kx*Ky) + /// in the case of a vertical connection) + /// + /// * K0 is a reference/background permeability scale (1mD) + /// + /// * h is the effective length of the connection's perforation + /// interval (dz*ntg in the case of a vertical connection) + /// + /// * rw is the connection's wellbore radius + /// + /// * sg_g is the specific gravity of surface condition gas + /// relative to surface condition air + /// + /// * mu_g is the reservoir condition viscosity of the free gas phase. + struct Correlation + { + /// Multiplicative coefficient 'A'. + double coeff_a{0.0}; + + /// Power coefficient 'B' for the effective permeability. + double exponent_b{0.0}; + + /// Power coefficient 'C' for the porosity term. + double exponent_c{0.0}; + + /// Serialisation test object. + static Correlation serializationTestObject(); + + /// Equality operator + /// + /// \param[in] other Object to which \c *this will be compared. + bool operator==(const Correlation& other) const; + + /// Inequality operator + /// + /// \param[in] other Object to which \c *this will be compared. + bool operator!=(const Correlation& other) const + { + return ! (*this == other); + } + + /// Serialisation operator + /// + /// \tparam Serializer Protocol for serialising and + /// deserialising objects between memory and character + /// buffers. + /// + /// \param[in,out] serializer Serialisation object. + template + void serializeOp(Serializer& serializer) + { + serializer(this->coeff_a); + serializer(this->exponent_b); + serializer(this->exponent_c); + } + }; + + /// Serialisation test object static WDFAC serializationTestObject(); double getDFactor(const Connection& connection, double mu, double rho, double phi) const; - void updateWDFAC(const DeckRecord& record); - //void updateWDFAC(const RestartIO::RstWell& rst_well); - void updateWDFACCOR(const DeckRecord& record); - //void updateWDFACOR(const RestartIO::RstWell& rst_well); + /// Configure D-factor calculation from well-level D-factor + /// description (keyword 'WDFAC') + /// + /// \param[in] record Well-level D-factor description. Single + /// record from WDFAC keyword. + void updateWDFAC(const DeckRecord& record); + + /// Configure D-factor calculation from Dake correlation model + /// (keyword WDFACCOR). + /// + /// \param[in] record Dake correlation model description. Single + /// record from WDFACCOR keyword. + void updateWDFACCOR(const DeckRecord& record); + + /// Check if any input-level connctions have a non-trivial D-factor + /// and update this well's D-factor category accordingly. + /// + /// \param[in] connections Connection set as defined by keyword + /// COMPDAT. This function will detect if any of the connections + /// created from COMPDAT define a non-trivial D-factor at the + /// connection level (item 12 of COMPDAT) and update the D-factor + /// category if so. void updateWDFACType(const WellConnections& connections); + + /// Capture sum of all CTFs for the purpose of translating + /// well-level D-factors to connection-level D-factors. + /// + /// \param[in] connections Connection set as defined by keyword + /// COMPDAT. void updateTotalCF(const WellConnections& connections); + + /// Retrieve current D-factor correlation model coefficients. + const Correlation& getDFactorCorrelationCoefficients() const + { + return this->m_corr; + } + + /// Whether or not a flow-dependent skin factor ('D') has been + /// configured for the current well. bool useDFactor() const; + /// Equality operator + /// + /// \param[in] other Object to which \c *this will be compared. bool operator==(const WDFAC& other) const; - bool operator!=(const WDFAC& other) const; - template + /// Inequality operator + /// + /// \param[in] other Object to which \c *this will be compared. + bool operator!=(const WDFAC& other) const + { + return ! (*this == other); + } + + /// Serialisation operator + /// + /// \tparam Serializer Protocol for serialising and deserialising + /// objects between memory and character buffers. + /// + /// \param[in,out] serializer Serialisation object. + template void serializeOp(Serializer& serializer) { - serializer(m_a); - serializer(m_b); - serializer(m_c); - serializer(m_d); - serializer(m_total_cf); - serializer(m_type); + serializer(this->m_type); + serializer(this->m_d); + serializer(this->m_total_cf); + serializer(this->m_corr); } private: - double m_a{0.0}; - double m_b{0.0}; - double m_c{0.0}; + /// D-factor categories. + enum class WDFacType + { + /// No flow-dependent skin factor is configured for this well. + NONE = 1, + + /// Well-level D-factor. + DFACTOR = 2, + + /// Use Dake's D-factor correlation model. + DAKEMODEL = 3, + + /// Connection-level D-factor. + CON_DFACTOR = 4, + }; + + /// D-factor category for this well. + WDFacType m_type { WDFacType::NONE }; + + /// Well-level D-factor for this well. double m_d{0.0}; + + /// Total CTF sum for this well. double m_total_cf{-1.0}; - WDFACTYPE m_type = WDFACTYPE::NONE; + + /// Coefficients for Dake's correlation model. + Correlation m_corr{}; }; } // namespace Opm diff --git a/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp b/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp index 51a3b7850..00b899208 100644 --- a/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp +++ b/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp @@ -17,126 +17,159 @@ along with OPM. If not, see . */ +#include #include #include -#include -#include + +#include +#include + #include #include #include -#include -#include +#include -#include -#include -#include -#include +#include + +#include #include +#include #include +#include namespace Opm { + WDFAC::Correlation WDFAC::Correlation::serializationTestObject() + { + return { 1.23, 0.456, 0.457 }; + } + + bool WDFAC::Correlation::operator==(const Correlation& other) const + { + return (this->coeff_a == other.coeff_a) + && (this->exponent_b == other.exponent_b) + && (this->exponent_c == other.exponent_c) + ; + } + + // ------------------------------------------------------------------------- + WDFAC WDFAC::serializationTestObject() { WDFAC result; - result.m_a = 1.23; - result.m_b = 0.456; - result.m_c = 0.457; + + result.m_type = WDFacType::DAKEMODEL; result.m_d = 0.458; result.m_total_cf = 1.0; - result.m_type = WDFACTYPE::NONE; + result.m_corr = Correlation::serializationTestObject(); return result; } - bool WDFAC::operator==(const WDFAC& other) const { - return (m_a == other.m_a) - && (m_b == other.m_b) - && (m_c == other.m_c) - && (m_d == other.m_d) - && (m_total_cf == other.m_total_cf) - && (m_type == other.m_type); - } - - void WDFAC::updateWDFAC(const DeckRecord& record) { + void WDFAC::updateWDFAC(const DeckRecord& record) + { m_d = record.getItem().getSIDouble(0); - m_type = WDFACTYPE::DFACTOR; + + m_type = WDFacType::DFACTOR; } - void WDFAC::updateWDFACCOR(const DeckRecord& record) { - m_a = record.getItem().getSIDouble(0); - m_b = record.getItem().getSIDouble(0); - m_c = record.getItem().getSIDouble(0); - m_type = WDFACTYPE::DAKEMODEL; + void WDFAC::updateWDFACCOR(const DeckRecord& record) + { + this->m_corr.coeff_a = record.getItem().getSIDouble(0); + this->m_corr.exponent_b = record.getItem().getSIDouble(0); + this->m_corr.exponent_c = record.getItem().getSIDouble(0); + + m_type = WDFacType::DAKEMODEL; } - void WDFAC::updateWDFACType(const WellConnections& connections) { - + void WDFAC::updateWDFACType(const WellConnections& connections) + { const auto non_trivial_dfactor = std::any_of(connections.begin(), connections.end(), [](const auto& conn) { return conn.dFactor() != 0.0; }); - // non-trivial dfactors detected use connection D factors if (non_trivial_dfactor) { - m_type = WDFACTYPE::CON_DFACTOR; - updateTotalCF(connections); + // Non-trivial D-factors detected. Use connection D-factors. + m_type = WDFacType::CON_DFACTOR; + this->updateTotalCF(connections); } } - void WDFAC::updateTotalCF(const WellConnections& connections) { - m_total_cf = std::accumulate(connections.begin(), connections.end(), 0.0, - [](const double tot_cf, const auto& conn) { return tot_cf + conn.CF(); }); + void WDFAC::updateTotalCF(const WellConnections& connections) + { + this->m_total_cf = std::accumulate(connections.begin(), connections.end(), 0.0, + [](const double tot_cf, const auto& conn) { return tot_cf + conn.CF(); }); } - double WDFAC::getDFactor(const Connection& connection, double mu, double rho, double phi) const { - - switch (m_type) - { - case WDFACTYPE::NONE: + double WDFAC::getDFactor(const Connection& connection, double mu, double rho, double phi) const + { + switch (this->m_type) { + case WDFacType::NONE: return 0.0; - case WDFACTYPE::DFACTOR: { - if (m_total_cf < 0.0) { - throw std::invalid_argument { "Total connection factor is not set" }; - } - return m_d * m_total_cf / connection.CF(); - } - case WDFACTYPE::CON_DFACTOR: { - double d = connection.dFactor(); - // If a negative d factor is set in COMPDAT individual connection d factors should be used directly. - if (d < 0) - return -d; - // If a positive d factor is set in COMPDAT the connection d factors is treated like a well d factor. - // and thus scaled with the connection index + + case WDFacType::DFACTOR: + { if (m_total_cf < 0.0) { throw std::invalid_argument { "Total connection factor is not set" }; } - return d * m_total_cf / connection.CF(); + return this->m_d * this->m_total_cf / connection.CF(); } - case WDFACTYPE::DAKEMODEL: - { - double Kh = connection.Kh(); - double Ke = connection.Ke(); - double h = Kh / Ke; - double rw = connection.rw(); + + case WDFacType::DAKEMODEL: + { + const double Kh = connection.Kh(); + const double Ke = connection.Ke(); + const double h = Kh / Ke; + const double rw = connection.rw(); + const auto k_md = unit::convert::to(Ke, prefix::milli*unit::darcy); - double beta = m_a * (std::pow(k_md, m_b) * std::pow(phi, m_c)); + double beta = m_corr.coeff_a * (std::pow(k_md, m_corr.exponent_b) * std::pow(phi, m_corr.exponent_c)); double specific_gravity = rho / 1.225; // divide by density of air at standard conditions. return beta * specific_gravity * Ke / (h * mu * rw ); } + break; + + case WDFacType::CON_DFACTOR: + { + const double d = connection.dFactor(); + + // If a negative D-factor is set in COMPDAT, then the individual + // connection D-factor should be used directly. + if (d < 0.0) { + return -d; + } + + if (this->m_total_cf < 0.0) { + throw std::invalid_argument { "Total connection factor is not set" }; + } + + // If a positive D-factor is set in COMPDAT, then the connection + // D-factor is treated as a well-level D-factor and thus scaled + // with the connection transmissibility factor. + return d * m_total_cf / connection.CF(); + } + default: break; } + return 0.0; } - bool WDFAC::useDFactor() const { - return m_type != WDFACTYPE::NONE; + bool WDFAC::useDFactor() const + { + return m_type != WDFacType::NONE; } - bool WDFAC::operator!=(const WDFAC& other) const { - return !(*this == other); + bool WDFAC::operator==(const WDFAC& other) const + { + return (this->m_type == other.m_type) + && (this->m_d == other.m_d) + && (this->m_total_cf == other.m_total_cf) + && (this->m_corr == other.m_corr) + ; } } diff --git a/tests/test_Serialization.cpp b/tests/test_Serialization.cpp index 9a6946813..eb0375bb6 100644 --- a/tests/test_Serialization.cpp +++ b/tests/test_Serialization.cpp @@ -316,6 +316,8 @@ TEST_FOR_TYPE(VFPInjTable) TEST_FOR_TYPE(VFPProdTable) TEST_FOR_TYPE(ViscrefTable) TEST_FOR_TYPE(WatdentTable) +TEST_FOR_TYPE_NAMED(WDFAC::Correlation, Correlation) +TEST_FOR_TYPE(WDFAC) TEST_FOR_TYPE(Well) TEST_FOR_TYPE(Welldims) TEST_FOR_TYPE(WellBrineProperties) @@ -336,11 +338,14 @@ TEST_FOR_TYPE(WListManager) TEST_FOR_TYPE(WriteRestartFileEvents) +namespace { + bool init_unit_test_func() { return true; } +} // Anonymous namespace int main(int argc, char** argv) { From e0fe776e2eca6c65134ea36820075728d5b2c99b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 10 Nov 2023 15:47:46 +0100 Subject: [PATCH 4/7] Move Static Dake Model Calculation to Connection Class Certain parts of the Dake model D-factor correlation relation depend only on static parameters at the cell level. Calculate the static product once, and store it as a member of Connection::CTFProperties. In particular, the new member double CTFProperties::static_dfac_corr_coeff stores the product A * (K/K0)**B * poro**C * Ke/(h*rw) with 'A', 'B', and 'C', being the Dake model parameters. We compute new values for the 'static_dfac_corr_coeff' as part of processing the WDFACCOR keyword. This static product will also be output to the restart file in follow-up commits, and will be used to simplify evaluating the full dynamic Dake model correlation. --- .../eclipse/Schedule/Well/Connection.hpp | 6 + .../eclipse/Schedule/Well/WellConnections.hpp | 5 + .../eclipse/Schedule/KeywordHandlers.cpp | 131 +++++++++++++----- .../eclipse/Schedule/Well/Connection.cpp | 9 +- .../eclipse/Schedule/Well/WellConnections.cpp | 35 +++++ tests/parser/ConnectionTests.cpp | 4 +- 6 files changed, 152 insertions(+), 38 deletions(-) diff --git a/opm/input/eclipse/Schedule/Well/Connection.hpp b/opm/input/eclipse/Schedule/Well/Connection.hpp index 4be15fbf2..293d4c11a 100644 --- a/opm/input/eclipse/Schedule/Well/Connection.hpp +++ b/opm/input/eclipse/Schedule/Well/Connection.hpp @@ -119,6 +119,10 @@ namespace Opm { /// for gas. double d_factor{}; + /// Product of certain static elements of D-factor correlation + /// law (WDFACCOR keyword). + double static_dfac_corr_coeff{}; + /// Denominator in peaceman's formula-i.e., log(r0/rw) + skin. double peaceman_denom{}; @@ -157,6 +161,7 @@ namespace Opm { serializer(this->connection_length); serializer(this->skin_factor); serializer(this->d_factor); + serializer(this->static_dfac_corr_coeff); serializer(this->peaceman_denom); } }; @@ -241,6 +246,7 @@ namespace Opm { void setKe(double Ke); void setCF(double CF); void setDefaultSatTabId(bool id); + void setStaticDFacCorrCoeff(const double c); void scaleWellPi(double wellPi); bool prepareWellPIScaling(); diff --git a/opm/input/eclipse/Schedule/Well/WellConnections.hpp b/opm/input/eclipse/Schedule/Well/WellConnections.hpp index 052a9054e..73939323b 100644 --- a/opm/input/eclipse/Schedule/Well/WellConnections.hpp +++ b/opm/input/eclipse/Schedule/Well/WellConnections.hpp @@ -39,6 +39,7 @@ namespace Opm { class FieldPropsManager; class KeywordLocation; class ScheduleGrid; + class WDFAC; } // namespace Opm namespace Opm { @@ -88,6 +89,7 @@ namespace Opm { void loadCOMPDAT(const DeckRecord& record, const ScheduleGrid& grid, const std::string& wname, + const WDFAC& wdfac, const KeywordLocation& location); void loadCOMPTRAJ(const DeckRecord& record, @@ -101,6 +103,9 @@ namespace Opm { const std::string& wname, const KeywordLocation& location); + void applyDFactorCorrelation(const ScheduleGrid& grid, + const WDFAC& wdfac); + int getHeadI() const; int getHeadJ() const; const std::vector& getMD() const; diff --git a/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp index e475ce6f6..98d83d76c 100644 --- a/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp +++ b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp @@ -196,37 +196,51 @@ namespace { this->snapshots.back().network.update( std::move( ext_network )); } - void Schedule::handleCOMPDAT(HandlerContext& handlerContext) { + void Schedule::handleCOMPDAT(HandlerContext& handlerContext) { std::unordered_set wells; for (const auto& record : handlerContext.keyword) { - const std::string& wellNamePattern = record.getItem("WELL").getTrimmedString(0); - auto wellnames = this->wellNames(wellNamePattern, handlerContext, - isWList(handlerContext.currentStep, - wellNamePattern)); + const auto wellNamePattern = record.getItem("WELL").getTrimmedString(0); + const auto wellnames = + this->wellNames(wellNamePattern, handlerContext, + isWList(handlerContext.currentStep, wellNamePattern)); for (const auto& name : wellnames) { auto well2 = this->snapshots.back().wells.get(name); - auto connections = std::shared_ptr( new WellConnections( well2.getConnections())); - connections->loadCOMPDAT(record, handlerContext.grid, name, handlerContext.keyword.location()); - if (well2.updateConnections(connections, handlerContext.grid)) { + auto connections = std::make_shared(well2.getConnections()); + const auto origWellConnSetIsEmpty = connections->empty(); + + connections->loadCOMPDAT(record, handlerContext.grid, name, + well2.getWDFAC(), handlerContext.keyword.location()); + const auto newWellConnSetIsEmpty = connections->empty(); + + if (well2.updateConnections(std::move(connections), handlerContext.grid)) { auto wdfac = std::make_shared(well2.getWDFAC()); - wdfac->updateWDFACType(*connections); + wdfac->updateWDFACType(well2.getConnections()); + well2.updateWDFAC(std::move(wdfac)); - this->snapshots.back().wells.update( well2 ); - wells.insert( name ); + this->snapshots.back().wells.update(well2); + + wells.insert(name); } - if (connections->empty() && well2.getConnections().empty()) { + if (origWellConnSetIsEmpty && newWellConnSetIsEmpty) { const auto& location = handlerContext.keyword.location(); - auto msg = fmt::format("Problem with COMPDAT/{}\n" - "In {} line {}\n" - "Well {} is not connected to grid - will remain SHUT", name, location.filename, location.lineno, name); + + const auto msg = fmt::format(R"(Problem with COMPDAT/{} +In {} line {} +Well {} is not connected to grid - will remain SHUT)", + name, location.filename, + location.lineno, name); + OpmLog::warning(msg); } - this->snapshots.back().wellgroup_events().addEvent( name, ScheduleEvents::COMPLETION_CHANGE); + + this->snapshots.back().wellgroup_events() + .addEvent(name, ScheduleEvents::COMPLETION_CHANGE); } } + this->snapshots.back().events().addEvent(ScheduleEvents::COMPLETION_CHANGE); // In the case the wells reference depth has been defaulted in the @@ -234,9 +248,10 @@ namespace { // reference depth exactly when the COMPDAT keyword has been completely // processed. for (const auto& wname : wells) { - auto& well = this->snapshots.back().wells.get( wname ); + auto well = this->snapshots.back().wells.get(wname); well.updateRefDepth(); - this->snapshots.back().wells.update( std::move(well)); + + this->snapshots.back().wells.update(std::move(well)); } if (! wells.empty()) { @@ -268,20 +283,30 @@ namespace { this->snapshots.back().events().addEvent(ScheduleEvents::COMPLETION_CHANGE); } - void Schedule::handleCOMPTRAJ(HandlerContext& handlerContext) { + void Schedule::handleCOMPTRAJ(HandlerContext& handlerContext) + { // Keyword WELTRAJ must be read first std::unordered_set wells; external::cvf::ref cellSearchTree = nullptr; + for (const auto& record : handlerContext.keyword) { - const std::string& wellNamePattern = record.getItem("WELL").getTrimmedString(0); - auto wellnames = this->wellNames(wellNamePattern, handlerContext ); + const auto wellNamePattern = record.getItem("WELL").getTrimmedString(0); + const auto wellnames = this->wellNames(wellNamePattern, handlerContext); for (const auto& name : wellnames) { auto well2 = this->snapshots.back().wells.get(name); - auto connections = std::make_shared(WellConnections(well2.getConnections())); - // cellsearchTree is calculated only once and is used to calculated cell intersections of the perforations specified in COMPTRAJ - connections->loadCOMPTRAJ(record, handlerContext.grid, name, handlerContext.keyword.location(), cellSearchTree); - // In the case that defaults are used in WELSPECS for headI/J the headI/J are calculated based on the well trajectory data + auto connections = std::make_shared(well2.getConnections()); + + // cellsearchTree is calculated only once and is used to + // calculated cell intersections of the perforations + // specified in COMPTRAJ + connections->loadCOMPTRAJ(record, handlerContext.grid, name, + handlerContext.keyword.location(), + cellSearchTree); + + // In the case that defaults are used in WELSPECS for + // headI/J the headI/J are calculated based on the well + // trajectory data well2.updateHead(connections->getHeadI(), connections->getHeadJ()); if (well2.updateConnections(connections, handlerContext.grid)) { this->snapshots.back().wells.update( well2 ); @@ -295,19 +320,23 @@ namespace { "Well {} is not connected to grid - will remain SHUT", name, location.filename, location.lineno, name); OpmLog::warning(msg); } - this->snapshots.back().wellgroup_events().addEvent( name, ScheduleEvents::COMPLETION_CHANGE); + + this->snapshots.back().wellgroup_events() + .addEvent(name, ScheduleEvents::COMPLETION_CHANGE); } } + this->snapshots.back().events().addEvent(ScheduleEvents::COMPLETION_CHANGE); // In the case the wells reference depth has been defaulted in the // WELSPECS keyword we need to force a calculation of the wells - // reference depth exactly when the WELCOML keyword has been completely - // processed. + // reference depth exactly when the WELCOML keyword has been + // completely processed. for (const auto& wname : wells) { - auto& well = this->snapshots.back().wells.get( wname ); + auto& well = this->snapshots.back().wells.get(wname); well.updateRefDepth(); - this->snapshots.back().wells.update( std::move(well)); + + this->snapshots.back().wells.update(std::move(well)); } if (! wells.empty()) { @@ -1641,25 +1670,55 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno) auto wdfac = std::make_shared(well.getWDFAC()); wdfac->updateWDFAC( record ); wdfac->updateTotalCF(well.getConnections()); - if (well.updateWDFAC(std::move(wdfac))) - this->snapshots.back().wells.update( std::move(well) ); + + if (well.updateWDFAC(std::move(wdfac))) { + this->snapshots.back().wells.update(std::move(well)); + + handlerContext.affected_well(well_name); + handlerContext.record_well_structure_change(); + + this->snapshots.back().events() + .addEvent(ScheduleEvents::COMPLETION_CHANGE); + + this->snapshots.back().wellgroup_events() + .addEvent(well_name, ScheduleEvents::COMPLETION_CHANGE); + } } } } void Schedule::handleWDFACCOR(HandlerContext& handlerContext) { for (const auto& record : handlerContext.keyword) { - const std::string& wellNamePattern = record.getItem("WELLNAME").getTrimmedString(0); + const std::string wellNamePattern = record.getItem("WELLNAME").getTrimmedString(0); const auto well_names = wellNames(wellNamePattern, handlerContext.currentStep); if (well_names.empty()) this->invalidNamePattern(wellNamePattern, handlerContext); for (const auto& well_name : well_names) { auto well = this->snapshots.back().wells.get(well_name); + auto conns = std::make_shared(well.getConnections()); + auto wdfac = std::make_shared(well.getWDFAC()); - wdfac->updateWDFACCOR( record ); - if (well.updateWDFAC(std::move(wdfac))) - this->snapshots.back().wells.update( std::move(well) ); + wdfac->updateWDFACCOR(record); + + conns->applyDFactorCorrelation(handlerContext.grid, *wdfac); + const auto updateConns = + well.updateConnections(std::move(conns), handlerContext.grid); + + if (well.updateWDFAC(std::move(wdfac)) || updateConns) { + this->snapshots.back().wells.update(std::move(well)); + + handlerContext.affected_well(well_name); + handlerContext.record_well_structure_change(); + + if (updateConns) { + this->snapshots.back().events() + .addEvent(ScheduleEvents::COMPLETION_CHANGE); + + this->snapshots.back().wellgroup_events() + .addEvent(well_name, ScheduleEvents::COMPLETION_CHANGE); + } + } } } } diff --git a/src/opm/input/eclipse/Schedule/Well/Connection.cpp b/src/opm/input/eclipse/Schedule/Well/Connection.cpp index 58673416b..be7712392 100644 --- a/src/opm/input/eclipse/Schedule/Well/Connection.cpp +++ b/src/opm/input/eclipse/Schedule/Well/Connection.cpp @@ -78,7 +78,8 @@ namespace Opm props.connection_length = 7.0; props.skin_factor = 8.0; props.d_factor = 9.0; - props.peaceman_denom = 10.0; + props.static_dfac_corr_coeff = 10.0; + props.peaceman_denom = 11.0; return props; } @@ -94,6 +95,7 @@ namespace Opm && (this->connection_length == that.connection_length) && (this->skin_factor == that.skin_factor) && (this->d_factor == that.d_factor) + && (this->static_dfac_corr_coeff == that.static_dfac_corr_coeff) && (this->peaceman_denom == that.peaceman_denom) ; } @@ -394,6 +396,11 @@ namespace Opm return true; } + void Connection::setStaticDFacCorrCoeff(const double c) + { + this->ctf_properties_.static_dfac_corr_coeff = c; + } + std::string Connection::str() const { std::stringstream ss; diff --git a/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp b/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp index 38ffcb58b..d6153a7e8 100644 --- a/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp +++ b/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp @@ -153,6 +153,22 @@ namespace { return peacemanDenominator(ctf_props.r0, ctf_props.rw, ctf_props.skin_factor); } + double staticForchheimerCoeffcient(const Opm::Connection::CTFProperties& ctf_props, + const double porosity, + const Opm::WDFAC& wdfac) + { + // Reference/background permeability against which to scale cell + // permeability for model evaluation. + constexpr auto Kref = 1.0*Opm::prefix::milli*Opm::unit::darcy; + + const auto& corr_coeff = wdfac.getDFactorCorrelationCoefficients(); + + return corr_coeff.coeff_a + * std::pow(ctf_props.Ke / Kref, corr_coeff.exponent_b) + * std::pow(porosity , corr_coeff.exponent_c) + * ctf_props.Ke / (ctf_props.connection_length * ctf_props.rw); + } + // Calculate permeability thickness Kh for line segment in a cell for x,y,z directions std::array permThickness(const external::cvf::Vec3d& effective_connection, @@ -339,6 +355,7 @@ namespace Opm { void WellConnections::loadCOMPDAT(const DeckRecord& record, const ScheduleGrid& grid, const std::string& wname, + const WDFAC& wdfac, const KeywordLocation& location) { const auto& itemI = record.getItem("I"); @@ -492,6 +509,9 @@ The cell ({},{},{}) in well {} is not active and the connection will be ignored) // PolymerMW module. ctf_props.re = std::sqrt(D[0] * D[1] / angle * 2); + ctf_props.static_dfac_corr_coeff = + staticForchheimerCoeffcient(ctf_props, props->poro, wdfac); + auto prev = std::find_if(this->m_connections.begin(), this->m_connections.end(), [I, J, k](const Connection& c) @@ -734,6 +754,21 @@ CF and Kh items for well {} must both be specified or both defaulted/negative)", this->md.push_back(record.getItem("MD").getSIDouble(0)); } + void WellConnections::applyDFactorCorrelation(const ScheduleGrid& grid, + const WDFAC& wdfac) + { + for (auto& conn : this->m_connections) { + const auto& complCell = grid.get_cell(conn.getI(), conn.getJ(), conn.getK()); + if (! complCell.is_active()) { + continue; + } + + conn.setStaticDFacCorrCoeff + (staticForchheimerCoeffcient(conn.ctfProperties(), + complCell.props->poro, wdfac)); + } + } + std::size_t WellConnections::size() const { return m_connections.size(); diff --git a/tests/parser/ConnectionTests.cpp b/tests/parser/ConnectionTests.cpp index 7e32848e6..ae1566b7c 100644 --- a/tests/parser/ConnectionTests.cpp +++ b/tests/parser/ConnectionTests.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,7 @@ namespace { }; const auto deck = Opm::Parser{}.parseString(compdat_keyword); + const auto wdfac = Opm::WDFAC{}; const auto loc = Opm::KeywordLocation{}; const Opm::EclipseGrid grid { 10, 10, 10 }; @@ -77,7 +79,7 @@ namespace { const auto sg = Opm::ScheduleGrid { grid, field_props, completed_cells }; for (const auto& rec : deck["COMPDAT"][0]) { - connections.loadCOMPDAT(rec, sg, "WELL", loc); + connections.loadCOMPDAT(rec, sg, "WELL", wdfac, loc); } return connections; From 3dc49856ee44a4b89fa883f3fe299add0f732a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 10 Nov 2023 16:19:30 +0100 Subject: [PATCH 5/7] Use Static D-Factor Correlation Coefficient This commit removes the porosity parameter from the getDFactor() member function. We then reimplement the Dake model correlation in terms of the Connection's 'static_dfac_corr_coeff' since this bypasses the exponentiation and, in the future, will simplify restarting a simulation run using the WDFACCOR keyword. While here, also split out the implementation of the connection level D-factor to a helper function and make another helper function for scaling a well-level D-factor to a connection-level value. --- opm/input/eclipse/Schedule/Well/WDFAC.hpp | 37 +++++++- src/opm/input/eclipse/Schedule/Well/WDFAC.cpp | 91 ++++++++++--------- tests/parser/ScheduleTests.cpp | 34 +++++-- 3 files changed, 112 insertions(+), 50 deletions(-) diff --git a/opm/input/eclipse/Schedule/Well/WDFAC.hpp b/opm/input/eclipse/Schedule/Well/WDFAC.hpp index 1a6455f77..22c5cc3b6 100644 --- a/opm/input/eclipse/Schedule/Well/WDFAC.hpp +++ b/opm/input/eclipse/Schedule/Well/WDFAC.hpp @@ -104,8 +104,6 @@ namespace Opm { /// Serialisation test object static WDFAC serializationTestObject(); - double getDFactor(const Connection& connection, double mu, double rho, double phi) const; - /// Configure D-factor calculation from well-level D-factor /// description (keyword 'WDFAC') /// @@ -137,6 +135,20 @@ namespace Opm { /// COMPDAT. void updateTotalCF(const WellConnections& connections); + /// Retrieve currently configured D-factor for single connection + /// + /// \param[in] rhoGS Surface condition mass density of gas + /// + /// \param[in] gas_visc Reservoir condition gas viscosity + /// + /// \param[in] conn Reservoir connection for which to retrieve the + /// D-factor. + /// + /// \return D-factor for connection \p conn. + double getDFactor(const double rhoGS, + const double gas_visc, + const Connection& conn) const; + /// Retrieve current D-factor correlation model coefficients. const Correlation& getDFactorCorrelationCoefficients() const { @@ -203,6 +215,27 @@ namespace Opm { /// Coefficients for Dake's correlation model. Correlation m_corr{}; + + /// Retrieve connection-level D-Factor from COMPDAT entries + /// + /// Possibly translated from well-level values. + /// + /// \param[in] conn Reservoir connection for which to retrieve the + /// D-factor. + /// + /// \return Connection-level D-factor. + double connectionLevelDFactor(const Connection& conn) const; + + /// Translate well-level D-factor to connection level D-factor + /// + /// \param[in] dfac Well-level D-factor. + /// + /// \param[in] conn Reservoir connection for which to retrieve the + /// D-factor. + /// + /// \return Connection-level D-factor, translated from well level. + double scaledWellLevelDFactor(const double dfac, + const Connection& conn) const; }; } // namespace Opm diff --git a/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp b/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp index 00b899208..5c0295779 100644 --- a/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp +++ b/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp @@ -39,6 +39,22 @@ #include #include +namespace { + + double dakeModelDFactor(const double rhoGS, + const double gas_visc, + const Opm::Connection::CTFProperties& ctf_props) + { + using namespace Opm::unit; + + // Specific gravity of gas relative to air at standard conditions. + constexpr auto rho_air = 1.22*kilogram / cubic(meter); + const auto specific_gravity = rhoGS / rho_air; + + return ctf_props.static_dfac_corr_coeff * specific_gravity / gas_visc; + } +} + namespace Opm { WDFAC::Correlation WDFAC::Correlation::serializationTestObject() @@ -103,57 +119,22 @@ namespace Opm { [](const double tot_cf, const auto& conn) { return tot_cf + conn.CF(); }); } - double WDFAC::getDFactor(const Connection& connection, double mu, double rho, double phi) const + double WDFAC::getDFactor(const double rhoGS, + const double gas_visc, + const Connection& conn) const { switch (this->m_type) { case WDFacType::NONE: return 0.0; case WDFacType::DFACTOR: - { - if (m_total_cf < 0.0) { - throw std::invalid_argument { "Total connection factor is not set" }; - } - - return this->m_d * this->m_total_cf / connection.CF(); - } + return this->scaledWellLevelDFactor(this->m_d, conn); case WDFacType::DAKEMODEL: - { - const double Kh = connection.Kh(); - const double Ke = connection.Ke(); - const double h = Kh / Ke; - const double rw = connection.rw(); - - const auto k_md = unit::convert::to(Ke, prefix::milli*unit::darcy); - double beta = m_corr.coeff_a * (std::pow(k_md, m_corr.exponent_b) * std::pow(phi, m_corr.exponent_c)); - double specific_gravity = rho / 1.225; // divide by density of air at standard conditions. - return beta * specific_gravity * Ke / (h * mu * rw ); - } - break; + return dakeModelDFactor(rhoGS, gas_visc, conn.ctfProperties()); case WDFacType::CON_DFACTOR: - { - const double d = connection.dFactor(); - - // If a negative D-factor is set in COMPDAT, then the individual - // connection D-factor should be used directly. - if (d < 0.0) { - return -d; - } - - if (this->m_total_cf < 0.0) { - throw std::invalid_argument { "Total connection factor is not set" }; - } - - // If a positive D-factor is set in COMPDAT, then the connection - // D-factor is treated as a well-level D-factor and thus scaled - // with the connection transmissibility factor. - return d * m_total_cf / connection.CF(); - } - - default: - break; + return this->connectionLevelDFactor(conn); } return 0.0; @@ -172,4 +153,32 @@ namespace Opm { && (this->m_corr == other.m_corr) ; } + + double WDFAC::connectionLevelDFactor(const Connection& conn) const + { + const double d = conn.dFactor(); + + // Negative D-factor values in COMPDAT should be used directly as + // connection-level D-factors. + if (d < 0.0) { + return -d; + } + + // Positive D-factor values in COMPDAT are treated as well-level + // values and scaled with the CTF for translation to connection + // level. + return this->scaledWellLevelDFactor(d, conn); + } + + double WDFAC::scaledWellLevelDFactor(const double dfac, + const Connection& conn) const + { + if (this->m_total_cf < 0.0) { + throw std::invalid_argument { + "Total well-level connection factor is not set" + }; + } + + return dfac * this->m_total_cf / conn.CF(); + } } diff --git a/tests/parser/ScheduleTests.cpp b/tests/parser/ScheduleTests.cpp index bff809685..2217ea0fc 100644 --- a/tests/parser/ScheduleTests.cpp +++ b/tests/parser/ScheduleTests.cpp @@ -5866,7 +5866,6 @@ END const double rho = 1.0; const double mu = 0.01*prefix::centi*unit::Poise; - const double phi = 0.3; { const auto& well11 = sched.getWell("W1", 1); @@ -5879,8 +5878,8 @@ END R"(Well "W1" must use D-Factors at step 1)"); // Well-level D-factor scaled by connection transmissibility factor. - BOOST_CHECK_CLOSE(wdfac11.getDFactor(well11.getConnections()[0], mu, rho, phi), 6*1.0*dFacUnit, 1e-12); - BOOST_CHECK_CLOSE(wdfac21.getDFactor(well21.getConnections()[0], mu, rho, phi), 2.0*dFacUnit, 1e-12); + BOOST_CHECK_CLOSE(wdfac11.getDFactor(rho, mu, well11.getConnections()[0]), 6*1.0*dFacUnit, 1e-12); + BOOST_CHECK_CLOSE(wdfac21.getDFactor(rho, mu, well21.getConnections()[0]), 2.0*dFacUnit, 1e-12); } { @@ -5889,8 +5888,29 @@ END const auto& wdfac12 = well12.getWDFAC(); const auto& wdfac22 = well22.getWDFAC(); - BOOST_CHECK_CLOSE(wdfac12.getDFactor(well12.getConnections()[0], mu, rho, phi), 5.19e-1, 3); - BOOST_CHECK_CLOSE(wdfac22.getDFactor(well22.getConnections()[0], mu, rho, phi), 2.0*dFacUnit, 1e-12); + // Intentional copy. + auto conn0 = well12.getConnections()[0]; + { + const auto& corr = wdfac12.getDFactorCorrelationCoefficients(); + + const auto k = 10.0*prefix::milli*unit::darcy; + const auto k0 = 1.0*prefix::milli*unit::darcy; + const auto h = 20.0*unit::meter; + const auto rw = 0.108*unit::meter; + const auto poro = 0.3; + + const auto static_dfac_corr_coeff = corr.coeff_a + * std::pow(k / k0, corr.exponent_b) + * std::pow(poro , corr.exponent_c) + * k / (h * rw); + + BOOST_CHECK_CLOSE(static_dfac_corr_coeff, 6.238808556951547e-06, 1.0e-8); + + conn0.setStaticDFacCorrCoeff(static_dfac_corr_coeff); + } + + BOOST_CHECK_CLOSE(wdfac12.getDFactor(rho, mu, conn0), 5.19e-1, 3); + BOOST_CHECK_CLOSE(wdfac22.getDFactor(rho, mu, conn0), 2.0*dFacUnit, 1e-12); } { @@ -5906,8 +5926,8 @@ END BOOST_CHECK_CLOSE(well13.getConnections()[1].dFactor(), 0.0*dFacUnit, 1e-12); BOOST_CHECK_CLOSE(well13.getConnections()[2].dFactor(), 11.0*dFacUnit, 1e-12); - BOOST_CHECK_CLOSE(wdfac13.getDFactor(well13.getConnections()[2], mu, rho, phi), 6.0/3.0*11.0*dFacUnit, 1e-12); - BOOST_CHECK_CLOSE(wdfac23.getDFactor(well23.getConnections()[0], mu, rho, phi), 2.0*dFacUnit, 1e-12); + BOOST_CHECK_CLOSE(wdfac13.getDFactor(rho, mu, well13.getConnections()[2]), 6.0/3.0*11.0*dFacUnit, 1e-12); + BOOST_CHECK_CLOSE(wdfac23.getDFactor(rho, mu, well23.getConnections()[0]), 2.0*dFacUnit, 1e-12); } } From 722559e1f5a37a6a7320bb09e071e7601431a8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 10 Nov 2023 17:59:34 +0100 Subject: [PATCH 6/7] Collect Dynamic Connection Level Transmissibilty Multiplier This commit adds a new data member to the data::Connection structure, double data::Connection::compact_mult This provides a slot in which the simulator can communicate transmissibility multiplier values resulting from rock compaction. The goal is to be able to distinguish background transmissibility factors from the effective transmissibility factors which include rock compaction effects. --- opm/output/data/Wells.hpp | 43 +++++++++++++++++++++--------------- tests/test_RFT.cpp | 8 +++---- tests/test_Restart.cpp | 6 ++--- tests/test_Summary.cpp | 10 ++++----- tests/test_Summary_Group.cpp | 2 +- tests/test_Wells.cpp | 6 ++--- 6 files changed, 41 insertions(+), 34 deletions(-) diff --git a/opm/output/data/Wells.hpp b/opm/output/data/Wells.hpp index 917535c28..e8688ba0f 100644 --- a/opm/output/data/Wells.hpp +++ b/opm/output/data/Wells.hpp @@ -36,9 +36,7 @@ #include #include -namespace Opm { - - namespace data { +namespace Opm { namespace data { class Rates { /* Methods are defined inline for performance, as the actual *work* done @@ -257,22 +255,25 @@ namespace Opm { double effective_Kh; double trans_factor; double d_factor; + double compact_mult{1.0}; // Rock compaction transmissibility multiplier (ROCKTAB) ConnectionFiltrate filtrate; bool operator==(const Connection& conn2) const { - return index == conn2.index && - rates == conn2.rates && - pressure == conn2.pressure && - reservoir_rate == conn2.reservoir_rate && - cell_pressure == conn2.cell_pressure && - cell_saturation_water == conn2.cell_saturation_water && - cell_saturation_gas == conn2.cell_saturation_gas && - effective_Kh == conn2.effective_Kh && - trans_factor == conn2.trans_factor && - d_factor == conn2.d_factor && - filtrate == conn2.filtrate; + return (index == conn2.index) + && (rates == conn2.rates) + && (pressure == conn2.pressure) + && (reservoir_rate == conn2.reservoir_rate) + && (cell_pressure == conn2.cell_pressure) + && (cell_saturation_water == conn2.cell_saturation_water) + && (cell_saturation_gas == conn2.cell_saturation_gas) + && (effective_Kh == conn2.effective_Kh) + && (trans_factor == conn2.trans_factor) + && (d_factor == conn2.d_factor) + && (compact_mult == conn2.compact_mult) + && (filtrate == conn2.filtrate) + ; } template @@ -295,15 +296,18 @@ namespace Opm { serializer(effective_Kh); serializer(trans_factor); serializer(d_factor); + serializer(compact_mult); serializer(filtrate); } static Connection serializationTestObject() { - return Connection{1, Rates::serializationTestObject(), - 2.0, 3.0, 4.0, 5.0, - 6.0, 7.0, 8.0, 9.0, - ConnectionFiltrate::serializationTestObject() }; + return Connection { + 1, Rates::serializationTestObject(), + 2.0, 3.0, 4.0, 5.0, + 6.0, 7.0, 8.0, 9.0, 0.987, + ConnectionFiltrate::serializationTestObject() + }; } }; @@ -1187,6 +1191,7 @@ namespace Opm { buffer.write(this->effective_Kh); buffer.write(this->trans_factor); buffer.write(this->d_factor); + buffer.write(this->compact_mult); this->filtrate.write(buffer); } @@ -1203,6 +1208,7 @@ namespace Opm { json_data.add_item("Kh", this->effective_Kh); json_data.add_item("trans_factor", this->trans_factor); json_data.add_item("d_factor", this->d_factor); + json_data.add_item("compact_mult", this->compact_mult); } template @@ -1349,6 +1355,7 @@ namespace Opm { buffer.read(this->effective_Kh); buffer.read(this->trans_factor); buffer.read(this->d_factor); + buffer.read(this->compact_mult); this->filtrate.read(buffer); } diff --git a/tests/test_RFT.cpp b/tests/test_RFT.cpp index 4991bf09e..c72e4390c 100644 --- a/tests/test_RFT.cpp +++ b/tests/test_RFT.cpp @@ -732,12 +732,12 @@ BOOST_AUTO_TEST_CASE(test_RFT) std::vector well1_comps(9); Opm::data::ConnectionFiltrate con_filtrate {0.1, 1, 3, 0.4, 1.e-9, 0.2, 0.05, 10.}; // values are not used in this test for (size_t i = 0; i < 9; ++i) { - Opm::data::Connection well_comp { grid.getGlobalIndex(8,8,i) ,r1, 0.0 , 0.0, (double)i, 0.1*i,0.2*i, 1.2e3, 4.321, 0.0, con_filtrate}; + Opm::data::Connection well_comp { grid.getGlobalIndex(8,8,i), r1, 0.0 , 0.0, (double)i, 0.1*i,0.2*i, 1.2e3, 4.321, 0.0, 1.23, con_filtrate}; well1_comps[i] = std::move(well_comp); } std::vector well2_comps(6); for (size_t i = 0; i < 6; ++i) { - Opm::data::Connection well_comp { grid.getGlobalIndex(3,3,i+3) ,r2, 0.0 , 0.0, (double)i, i*0.1,i*0.2, 0.15, 0.54321, 0.0, con_filtrate}; + Opm::data::Connection well_comp { grid.getGlobalIndex(3,3,i+3), r2, 0.0 , 0.0, (double)i, i*0.1,i*0.2, 0.15, 0.54321, 0.0, 0.98, con_filtrate}; well2_comps[i] = std::move(well_comp); } @@ -871,12 +871,12 @@ BOOST_AUTO_TEST_CASE(test_RFT2) std::vector well1_comps(9); Opm::data::ConnectionFiltrate con_filtrate {0.1, 1, 3, 0.4, 1.e-9, 0.2, 0.05, 10.}; // values are not used in this test for (size_t i = 0; i < 9; ++i) { - Opm::data::Connection well_comp { grid.getGlobalIndex(8,8,i) ,r1, 0.0 , 0.0, (double)i, 0.1*i,0.2*i, 3.14e5, 0.1234, 0.0, con_filtrate}; + Opm::data::Connection well_comp { grid.getGlobalIndex(8,8,i), r1, 0.0 , 0.0, (double)i, 0.1*i,0.2*i, 3.14e5, 0.1234, 0.0, 1.23, con_filtrate}; well1_comps[i] = std::move(well_comp); } std::vector well2_comps(6); for (size_t i = 0; i < 6; ++i) { - Opm::data::Connection well_comp { grid.getGlobalIndex(3,3,i+3) ,r2, 0.0 , 0.0, (double)i, i*0.1,i*0.2, 355.113, 0.9876, 0.0, con_filtrate}; + Opm::data::Connection well_comp { grid.getGlobalIndex(3,3,i+3), r2, 0.0 , 0.0, (double)i, i*0.1,i*0.2, 355.113, 0.9876, 0.0, 0.98, con_filtrate}; well2_comps[i] = std::move(well_comp); } diff --git a/tests/test_Restart.cpp b/tests/test_Restart.cpp index 400608f68..5668de3cd 100644 --- a/tests/test_Restart.cpp +++ b/tests/test_Restart.cpp @@ -187,15 +187,15 @@ data::Wells mkWells() { * input deck. All other entries in the well structures are arbitrary. */ Opm::data::ConnectionFiltrate con_filtrate {0.1, 1, 3, 0.4, 1.e-9, 0.2, 0.05, 10.}; // values are not used in this test - w1.connections.push_back( { 88, rc1, 30.45, 123.4, 543.21, 0.62, 0.15, 1.0e3, 1.234, 0.0, con_filtrate } ); - w1.connections.push_back( { 288, rc2, 33.19, 123.4, 432.1, 0.26, 0.45, 2.56, 2.345, 0.0, con_filtrate } ); + w1.connections.push_back( { 88, rc1, 30.45, 123.4, 543.21, 0.62, 0.15, 1.0e3, 1.234, 0.0, 1.23, con_filtrate } ); + w1.connections.push_back( { 288, rc2, 33.19, 123.4, 432.1, 0.26, 0.45, 2.56, 2.345, 0.0, 0.98, con_filtrate } ); w2.rates = r2; w2.thp = 2.0; w2.bhp = 2.34; w2.temperature = 4.56; w2.control = 2; - w2.connections.push_back( { 188, rc3, 36.22, 123.4, 256.1, 0.55, 0.0125, 314.15, 3.456, 0.0, con_filtrate } ); + w2.connections.push_back( { 188, rc3, 36.22, 123.4, 256.1, 0.55, 0.0125, 314.15, 3.456, 0.0, 2.46, con_filtrate } ); { data::Wells wellRates; diff --git a/tests/test_Summary.cpp b/tests/test_Summary.cpp index fd18a0349..d11711010 100644 --- a/tests/test_Summary.cpp +++ b/tests/test_Summary.cpp @@ -319,11 +319,11 @@ data::Wells result_wells(const bool w3_injector = true) data::ConnectionFiltrate zero_filtrate {}; // only injecting connections are counted for filtration related data::ConnectionFiltrate con_filtrate = {0.1*sm3_pr_day(), 1*sm3(), 3, 0.01*unit::meter, 1.e-3*unit::darcy, 0.2, 0.05*unit::meter, 10.*unit::square(unit::meter)}; data::ConnectionFiltrate w3_con_filtrate = w3_injector ? con_filtrate : zero_filtrate; - data::Connection well1_comp1 { 0 , crates1, 1.9 *unit::barsa, -123.4 *rm3_pr_day(), 314.15, 0.35 , 0.25, 2.718e2, 111.222*cp_rm3_per_db(), 0.0, zero_filtrate}; - data::Connection well2_comp1 { 1 , crates2, 1.10*unit::barsa, - 23.4 *rm3_pr_day(), 212.1 , 0.78 , 0.0 , 12.34 , 222.333*cp_rm3_per_db(), 0.0, zero_filtrate}; - data::Connection well2_comp2 { 101, crates3, 1.11*unit::barsa, -234.5 *rm3_pr_day(), 150.6 , 0.001, 0.89, 100.0 , 333.444*cp_rm3_per_db(), 0.0, con_filtrate /* output should be zero since it is a producer */}; - data::Connection well3_comp1 { 2 , crates3, 1.11*unit::barsa, 432.1 *rm3_pr_day(), 456.78, 0.0 , 0.15, 432.1 , 444.555*cp_rm3_per_db(), 0.0, w3_con_filtrate}; - data::Connection well6_comp1 { 77 , crates6, 6.11*unit::barsa, 321.09*rm3_pr_day(), 656.78, 0.0 , 0.65, 632.1 , 555.666*cp_rm3_per_db(), 0.0, zero_filtrate}; + data::Connection well1_comp1 { 0 , crates1, 1.9 *unit::barsa, -123.4 *rm3_pr_day(), 314.15, 0.35 , 0.25, 2.718e2, 111.222*cp_rm3_per_db(), 0.0, 1.0, zero_filtrate}; + data::Connection well2_comp1 { 1 , crates2, 1.10*unit::barsa, - 23.4 *rm3_pr_day(), 212.1 , 0.78 , 0.0 , 12.34 , 222.333*cp_rm3_per_db(), 0.0, 1.0, zero_filtrate}; + data::Connection well2_comp2 { 101, crates3, 1.11*unit::barsa, -234.5 *rm3_pr_day(), 150.6 , 0.001, 0.89, 100.0 , 333.444*cp_rm3_per_db(), 0.0, 1.0, con_filtrate /* output should be zero since it is a producer */}; + data::Connection well3_comp1 { 2 , crates3, 1.11*unit::barsa, 432.1 *rm3_pr_day(), 456.78, 0.0 , 0.15, 432.1 , 444.555*cp_rm3_per_db(), 0.0, 1.0, w3_con_filtrate}; + data::Connection well6_comp1 { 77 , crates6, 6.11*unit::barsa, 321.09*rm3_pr_day(), 656.78, 0.0 , 0.65, 632.1 , 555.666*cp_rm3_per_db(), 0.0, 1.0, zero_filtrate}; /* The completions diff --git a/tests/test_Summary_Group.cpp b/tests/test_Summary_Group.cpp index 9e66a0de9..e9c7c9488 100644 --- a/tests/test_Summary_Group.cpp +++ b/tests/test_Summary_Group.cpp @@ -172,7 +172,7 @@ static data::Wells result_wells() { input deck. */ data::ConnectionFiltrate con_filtrate {0.1, 1, 3, 0.4, 1.e-9, 0.2, 0.05, 10.}; // values are not tested in this test - data::Connection well1_comp1 { 0 , crates1, 1.9 , 123.4, 314.15, 0.35, 0.25, 2.718e2, 0.12345, 0.0, con_filtrate }; + data::Connection well1_comp1 { 0, crates1, 1.9, 123.4, 314.15, 0.35, 0.25, 2.718e2, 0.12345, 0.0, 1.23, con_filtrate }; /* The completions diff --git a/tests/test_Wells.cpp b/tests/test_Wells.cpp index a4852e164..85b206842 100644 --- a/tests/test_Wells.cpp +++ b/tests/test_Wells.cpp @@ -116,8 +116,8 @@ BOOST_AUTO_TEST_CASE(get_connections) { * the completion keys (active indices) and well names correspond to the * input deck. All other entries in the well structures are arbitrary. */ - w1.connections.push_back( { 88, rc1, 30.45, 123.45, 543.21, 0.123, 0.5, 17.29, 0.1729,0.0,{}} ); - w1.connections.push_back( { 288, rc2, 33.19, 67.89, 98.76, 0.5, 0.125, 355.113, 0.355113,0.0, {}} ); + w1.connections.push_back( { 88, rc1, 30.45, 123.45, 543.21, 0.123, 0.5, 17.29, 0.1729, 0.0, 1.23, {}} ); + w1.connections.push_back( { 288, rc2, 33.19, 67.89, 98.76, 0.5, 0.125, 355.113, 0.355113, 0.0, 0.98, {}} ); { Json::JsonObject json_data; @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(get_connections) { w2.temperature = 4.56; w2.control = 2; w2.filtrate = {0.3, 3, 0.4}; // values are not tested in this test - w2.connections.push_back( { 188, rc3, 36.22, 19.28, 28.91, 0.125, 0.125, 3.141, 0.31415, 0.0, {}} ); + w2.connections.push_back( { 188, rc3, 36.22, 19.28, 28.91, 0.125, 0.125, 3.141, 0.31415, 0.0, 1.21, {}} ); data::Wells wellRates; wellRates["OP_1"] = w1; From 1129e95a16f1cf93ec147c36e3d19c99e0384f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 13 Nov 2023 09:12:05 +0100 Subject: [PATCH 7/7] Store Peaceman Denominator and Connection Length in Restart File This commit writes the value of the denominator expression in Peaceman's formula to SCON[6] and the connection's interval length to SCON[31] in the restart file. Using this information at restart time is the subject of follow-up work. --- opm/output/eclipse/VectorItems/connection.hpp | 7 +++++++ src/opm/output/eclipse/AggregateConnectionData.cpp | 9 +++++++-- tests/test_AggregateConnectionData.cpp | 6 ++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/opm/output/eclipse/VectorItems/connection.hpp b/opm/output/eclipse/VectorItems/connection.hpp index 5c95c1b27..6741f6df8 100644 --- a/opm/output/eclipse/VectorItems/connection.hpp +++ b/opm/output/eclipse/VectorItems/connection.hpp @@ -50,6 +50,10 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems EffectiveKH = 3, // Effective Kh product of connection SkinFactor = 4, // Skinfactor - item 'SKIN' from COMPDAT + + CFDenom = 6, // Denominator in connection transmissibility + // factor expression + item12 = 11, // Connection transmissibility factor SegDistEnd = 20, // Distance to end of connection in segment @@ -57,6 +61,9 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems item30 = 29, // Unknown item31 = 30, // Unknown + + EffectiveLength = 31, // Effective length of connection's perforation interval. + CFInDeck = 40, // = 0 for connection factor not defined, = 1 for connection factor defined }; } // SConn diff --git a/src/opm/output/eclipse/AggregateConnectionData.cpp b/src/opm/output/eclipse/AggregateConnectionData.cpp index a2b2227cd..808978469 100644 --- a/src/opm/output/eclipse/AggregateConnectionData.cpp +++ b/src/opm/output/eclipse/AggregateConnectionData.cpp @@ -191,8 +191,9 @@ namespace { sConn[Ix::SkinFactor] = conn.skinFactor(); - sConn[Ix::item12] = sConn[Ix::ConnTrans]; + sConn[Ix::CFDenom] = conn.ctfProperties().peaceman_denom; + sConn[Ix::item12] = sConn[Ix::ConnTrans]; if (conn.attachedToSegment()) { const auto& [start, end] = *conn.perf_range(); @@ -202,7 +203,11 @@ namespace { sConn[Ix::item30] = -1.0e+20f; sConn[Ix::item31] = -1.0e+20f; - sConn[Ix::CFInDeck] = (conn.ctfAssignedFromInput()) ? 1 : 0; + + sConn[Ix::EffectiveLength] = + scprop(M::length, conn.connectionLength()); + + sConn[Ix::CFInDeck] = conn.ctfAssignedFromInput() ? 1.0f : 0.0f; } template diff --git a/tests/test_AggregateConnectionData.cpp b/tests/test_AggregateConnectionData.cpp index f8376969a..82056efb8 100644 --- a/tests/test_AggregateConnectionData.cpp +++ b/tests/test_AggregateConnectionData.cpp @@ -687,11 +687,13 @@ BOOST_AUTO_TEST_CASE(Declared_Connection_Data) BOOST_CHECK_CLOSE(sconn[i0 + Ix::Depth], 7050., 1.0e-5); // PROD - conn 1 : Centre depth BOOST_CHECK_CLOSE(sconn[i0 + Ix::Diameter], 0.20, 1.0e-5); // PROD - conn 1 : diameter BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffectiveKH], 1581.13879, 1.0e-5); // PROD - conn 1 : effective kh-product + BOOST_CHECK_CLOSE(sconn[i0 + Ix::CFDenom], 4.37696314, 1.0e-5); BOOST_CHECK_CLOSE(sconn[i0 + Ix::item12], 2.55826545, 1.0e-5); // PROD - conn 1 : Transmissibility factor BOOST_CHECK_CLOSE( sconn[i0 + Ix::SegDistEnd], 130., 1.0e-5); // PROD - conn 1 : Distance to end of connection in segment BOOST_CHECK_CLOSE( sconn[i0 + Ix::SegDistStart], 30., 1.0e-5); // PROD - conn 1 : Distance to start of connection in segment + BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffectiveLength], 100.0, 1.0e-5); // Well no 2 - WINJ well connNo = 3; @@ -700,11 +702,13 @@ BOOST_AUTO_TEST_CASE(Declared_Connection_Data) BOOST_CHECK_CLOSE(sconn[i0 + Ix::Depth], 7250., 1.0e-5); // WINJ - conn 3 : Centre depth BOOST_CHECK_CLOSE(sconn[i0 + Ix::Diameter], 0.20, 1.0e-5); // WINJ - conn 3 : diameter BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffectiveKH], 1581.13879, 1.0e-5); // WINJ - conn 3 : effective kh-product + BOOST_CHECK_CLOSE(sconn[i0 + Ix::CFDenom], 4.37696314, 1.0e-5); BOOST_CHECK_CLOSE(sconn[i0 + Ix::item12], 2.55826545, 1.0e-5); // WINJ - conn 3 : Transmissibility factor BOOST_CHECK_CLOSE( sconn[i0 + Ix::SegDistEnd], 0., 1.0e-5); // WINJ - conn 3 : Distance to end of connection in segment BOOST_CHECK_CLOSE( sconn[i0 + Ix::SegDistStart], 0., 1.0e-5); // WINJ - conn 3 : Distance to start of connection in segment + BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffectiveLength], 100.0, 1.0e-5); connNo = 4; i0 = ih.ncwmax * ih.nsconz + (connNo - 1) * ih.nsconz; @@ -712,11 +716,13 @@ BOOST_AUTO_TEST_CASE(Declared_Connection_Data) BOOST_CHECK_CLOSE(sconn[i0 + Ix::Depth], 7250., 1.0e-5); // WINJ - conn 4 : Centre depth BOOST_CHECK_CLOSE(sconn[i0 + Ix::Diameter], 0.20, 1.0e-5); // WINJ - conn 4 : diameter BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffectiveKH], 1581.13879, 1.0e-5); // WINJ - conn 4 : effective kh-product + BOOST_CHECK_CLOSE(sconn[i0 + Ix::CFDenom], 4.37696314, 1.0e-5); BOOST_CHECK_CLOSE(sconn[i0 + Ix::item12], 2.55826545, 1.0e-5); // WINJ - conn 4 : Transmissibility factor BOOST_CHECK_CLOSE( sconn[i0 + Ix::SegDistEnd], 0., 1.0e-5); // WINJ - conn 4 : Distance to end of connection in segment BOOST_CHECK_CLOSE( sconn[i0 + Ix::SegDistStart], 0., 1.0e-5); // WINJ - conn 4 : Distance to start of connection in segment + BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffectiveLength], 100.0, 1.0e-5); } // XCONN (PROD) + (WINJ)