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/opm/input/eclipse/Schedule/Well/Connection.hpp b/opm/input/eclipse/Schedule/Well/Connection.hpp index 431a13cb3..293d4c11a 100644 --- a/opm/input/eclipse/Schedule/Well/Connection.hpp +++ b/opm/input/eclipse/Schedule/Well/Connection.hpp @@ -17,91 +17,172 @@ 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{}; + + /// 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{}; + + /// 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->static_dfac_corr_coeff); + 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 +198,105 @@ 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 setStaticDFacCorrCoeff(const double c); + 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 +348,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/WDFAC.hpp b/opm/input/eclipse/Schedule/Well/WDFAC.hpp index 3364d91f5..22c5cc3b6 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,210 @@ 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; + /// 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); - //void updateWDFAC(const RestartIO::RstWell& rst_well); - void updateWDFACCOR(const DeckRecord& record); - //void updateWDFACOR(const RestartIO::RstWell& rst_well); + /// 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 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 + { + 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{}; + + /// 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/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..73939323b 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,11 @@ namespace Opm { class ActiveGridCells; class DeckRecord; + class EclipseGrid; class FieldPropsManager; class KeywordLocation; class ScheduleGrid; - class EclipseGrid; + class WDFAC; } // namespace Opm namespace Opm { @@ -67,19 +70,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, @@ -89,16 +89,26 @@ 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, 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); + + void applyDFactorCorrelation(const ScheduleGrid& grid, + const WDFAC& wdfac); 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 +175,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/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/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/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/KeywordHandlers.cpp b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp index 3390b6bed..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()) { @@ -377,23 +406,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)); + } } } } @@ -1635,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/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/src/opm/input/eclipse/Schedule/Well/Connection.cpp b/src/opm/input/eclipse/Schedule/Well/Connection.cpp index b7f0958f3..be7712392 100644 --- a/src/opm/input/eclipse/Schedule/Well/Connection.cpp +++ b/src/opm/input/eclipse/Schedule/Well/Connection.cpp @@ -17,125 +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 -#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.static_dfac_corr_coeff = 10.0; + props.peaceman_denom = 11.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->static_dfac_corr_coeff == that.static_dfac_corr_coeff) + && (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 +178,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 +360,142 @@ 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 { + void Connection::setStaticDFacCorrCoeff(const double c) + { + this->ctf_properties_.static_dfac_corr_coeff = c; + } + + 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 +523,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 +531,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 +594,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/WDFAC.cpp b/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp index 51a3b7850..5c0295779 100644 --- a/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp +++ b/src/opm/input/eclipse/Schedule/Well/WDFAC.cpp @@ -17,126 +17,168 @@ 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 { + + 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() + { + 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 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 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 - if (m_total_cf < 0.0) { - throw std::invalid_argument { "Total connection factor is not set" }; - } - return d * m_total_cf / connection.CF(); - } - case WDFACTYPE::DAKEMODEL: - { - double Kh = connection.Kh(); - double Ke = connection.Ke(); - double h = Kh / Ke; - 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 specific_gravity = rho / 1.225; // divide by density of air at standard conditions. - return beta * specific_gravity * Ke / (h * mu * rw ); - } - default: - break; + case WDFacType::DFACTOR: + return this->scaledWellLevelDFactor(this->m_d, conn); + + case WDFacType::DAKEMODEL: + return dakeModelDFactor(rhoGS, gas_visc, conn.ctfProperties()); + + case WDFacType::CON_DFACTOR: + return this->connectionLevelDFactor(conn); } + 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) + ; + } + + 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/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..d6153a7e8 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,88 @@ 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); + } + + 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& 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 +314,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); } @@ -347,437 +355,452 @@ 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"); + 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); + + 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) + { + 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 { + 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(); } - 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 +812,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 +906,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 +950,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 +983,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 +992,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 +1021,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/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/parser/ConnectionTests.cpp b/tests/parser/ConnectionTests.cpp index 0f261a337..ae1566b7c 100644 --- a/tests/parser/ConnectionTests.cpp +++ b/tests/parser/ConnectionTests.cpp @@ -17,32 +17,39 @@ 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 +#include namespace { double cp_rm3_per_db() @@ -51,20 +58,32 @@ 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 wdfac = Opm::WDFAC{}; + 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", wdfac, loc); + } + + return connections; + } } namespace Opm { @@ -94,12 +113,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 +140,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 +187,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 +218,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 @@ -206,13 +265,18 @@ COPY 'PERMX' 'PERMZ' / 'PERMX' 'PERMY' / / + +PORO + 1000*0.3 / + SCHEDULE 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)); @@ -230,13 +294,17 @@ COPY 'PERMX' 'PERMY' / / +PORO + 1000*0.3 / + SCHEDULE 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 +528,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 +570,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..2217ea0fc 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,85 @@ 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; - // 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(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); + } + { + 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()); + // 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; - 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); + 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); + } + + { + 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_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(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); + } } 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/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 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_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) 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_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_Serialization.cpp b/tests/test_Serialization.cpp index c9d3710e6..eb0375bb6 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) @@ -315,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) @@ -335,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) { 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;