This commit is contained in:
Bård Skaflestad 2023-11-26 10:49:00 +00:00 committed by GitHub
commit 22a6bc3e16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 3925 additions and 2364 deletions

View File

@ -16,40 +16,42 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef COMPLETED_CELLS #ifndef COMPLETED_CELLS
#define COMPLETED_CELLS #define COMPLETED_CELLS
#include <optional>
#include <unordered_map>
#include <opm/input/eclipse/EclipseState/Grid/GridDims.hpp> #include <opm/input/eclipse/EclipseState/Grid/GridDims.hpp>
#include <array>
#include <cstddef>
#include <optional>
#include <unordered_map>
#include <utility>
namespace Opm { namespace Opm {
class CompletedCells { class CompletedCells
{
public: public:
struct Cell
struct Cell { {
std::size_t global_index; std::size_t global_index;
std::size_t i, j, k; std::size_t i, j, k;
struct Props{ struct Props
std::size_t active_index; {
double permx; std::size_t active_index{};
double permy; double permx{};
double permz; double permy{};
int satnum; double permz{};
int pvtnum; double poro{};
double ntg; int satnum{};
int pvtnum{};
double ntg{};
bool operator==(const Props& other) const{ bool operator==(const Props& other) const;
return this->active_index == other.active_index &&
this->permx == other.permx && static Props serializationTestObject();
this->permy == other.permy &&
this->permz == other.permz &&
this->satnum == other.satnum &&
this->pvtnum == other.pvtnum &&
this->ntg == other.ntg;
}
template<class Serializer> template<class Serializer>
void serializeOp(Serializer& serializer) void serializeOp(Serializer& serializer)
@ -57,46 +59,23 @@ public:
serializer(this->permx); serializer(this->permx);
serializer(this->permy); serializer(this->permy);
serializer(this->permz); serializer(this->permz);
serializer(this->poro);
serializer(this->satnum); serializer(this->satnum);
serializer(this->pvtnum); serializer(this->pvtnum);
serializer(this->ntg); 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> props; std::optional<Props> props;
std::size_t active_index() const; std::size_t active_index() const;
bool is_active() const; bool is_active() const;
double depth; double depth{};
std::array<double, 3> dimensions; std::array<double, 3> dimensions{};
bool operator==(const Cell& other) const { 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;
}
static Cell serializationTestObject() { static Cell serializationTestObject();
Cell cell(0,1,1,1);
cell.depth = 12345;
cell.dimensions = {1.0,2.0,3.0};
return cell;
}
template<class Serializer> template<class Serializer>
void serializeOp(Serializer& serializer) void serializeOp(Serializer& serializer)
@ -123,6 +102,7 @@ public:
CompletedCells() = default; CompletedCells() = default;
explicit CompletedCells(const GridDims& dims); explicit CompletedCells(const GridDims& dims);
CompletedCells(std::size_t nx, std::size_t ny, std::size_t nz); 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; const Cell& get(std::size_t i, std::size_t j, std::size_t k) const;
std::pair<bool, Cell&> try_get(std::size_t i, std::size_t j, std::size_t k); std::pair<bool, Cell&> try_get(std::size_t i, std::size_t j, std::size_t k);
@ -141,5 +121,5 @@ private:
std::unordered_map<std::size_t, Cell> cells; std::unordered_map<std::size_t, Cell> cells;
}; };
} }
#endif
#endif // COMPLETED_CELLS

View File

@ -21,17 +21,30 @@
#include <opm/input/eclipse/Schedule/CompletedCells.hpp> #include <opm/input/eclipse/Schedule/CompletedCells.hpp>
#include <cstddef>
#include <string>
namespace Opm { namespace Opm {
class EclipseGrid; class EclipseGrid;
class FieldPropsManager; class FieldPropsManager;
class ScheduleGrid { } // namespace Opm
namespace Opm {
class ScheduleGrid
{
public: 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); 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; const Opm::EclipseGrid* get_grid() const;
private: private:
@ -40,8 +53,6 @@ private:
CompletedCells& cells; CompletedCells& cells;
}; };
} // namespace Opm
#endif // SCHEDULE_GRID
}
#endif

View File

@ -17,91 +17,172 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef COMPLETION_HPP_ #ifndef COMPLETION_HPP_
#define COMPLETION_HPP_ #define COMPLETION_HPP_
#include <opm/input/eclipse/Schedule/Well/FilterCake.hpp>
#include <opm/input/eclipse/Schedule/Well/WINJMULT.hpp>
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <string_view>
#include <vector> #include <vector>
#include <optional> #include <optional>
#include <limits> #include <limits>
#include <opm/input/eclipse/Schedule/Well/FilterCake.hpp>
#include <opm/input/eclipse/Schedule/Well/WINJMULT.hpp>
namespace Opm { namespace Opm {
namespace RestartIO {
struct RstConnection;
}
class DeckKeyword; class DeckKeyword;
class DeckRecord; class DeckRecord;
class ScheduleGrid; class ScheduleGrid;
class FieldPropsManager; class FieldPropsManager;
} // namespace Opm
class Connection { namespace Opm { namespace RestartIO {
struct RstConnection;
}} // namespace Opm::RestartIO
namespace Opm {
class Connection
{
public: public:
enum class State { enum class State {
OPEN = 1, OPEN = 1,
SHUT = 2, 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 std::string State2String(State enumValue);
static State StateFromString( const std::string& stringValue ); static State StateFromString(std::string_view stringValue);
enum class Direction{ enum class Direction {
X = 1, X = 1,
Y = 2, Y = 2,
Z = 3 Z = 3,
}; };
static std::string Direction2String(const Direction enumValue); static std::string Direction2String(const Direction enumValue);
static Direction DirectionFromString(const std::string& stringValue); static Direction DirectionFromString(std::string_view stringValue);
enum class Order { enum class Order {
DEPTH, DEPTH,
INPUT, INPUT,
TRACK TRACK,
}; };
static const std::string Order2String( Order enumValue ); static std::string Order2String(Order enumValue);
static Order OrderFromString(const std::string& comporderStringValue); static Order OrderFromString(std::string_view comporderStringValue);
enum class CTFKind { enum class CTFKind {
DeckValue, DeckValue,
Defaulted, 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 <class Serializer>
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(); static Connection serializationTestObject();
@ -117,104 +198,105 @@ namespace RestartIO {
int satTableId() const; int satTableId() const;
int complnum() const; int complnum() const;
int segment() const; int segment() const;
double CF() const;
double wpimult() const; double wpimult() const;
double CF() const;
double Kh() const; double Kh() const;
double Ke() const;
double rw() const; double rw() const;
double r0() const; double r0() const;
double re() const; double re() const;
double connectionLength() const; double connectionLength() const;
double skinFactor() const; double skinFactor() const;
double dFactor() const; double dFactor() const;
double Ke() const;
CTFKind kind() const; CTFKind kind() const;
const InjMult& injmult() const; const InjMult& injmult() const;
bool activeInjMult() const; bool activeInjMult() const;
void setInjMult(const InjMult& inj_mult);
void setFilterCake(const FilterCake& filter_cake);
const FilterCake& getFilterCake() const; const FilterCake& getFilterCake() const;
bool filterCakeActive() const; bool filterCakeActive() const;
double getFilterCakeRadius() const; double getFilterCakeRadius() const;
double getFilterCakeArea() const; double getFilterCakeArea() const;
const CTFProperties& ctfProperties() const
{
return this->ctf_properties_;
}
std::size_t sort_value() const;
bool getDefaultSatTabId() const;
const std::optional<std::pair<double, double>>& 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 setState(State state);
void setComplnum(int compnum); void setComplnum(int compnum);
void setSkinFactor(double skin_factor); void setSkinFactor(double skin_factor);
void setDFactor(double d_factor); void setDFactor(double d_factor);
void setKe(double Ke); void setKe(double Ke);
void setCF(double CF); void setCF(double CF);
void setDefaultSatTabId(bool id);
void setStaticDFacCorrCoeff(const double c);
void scaleWellPi(double wellPi); void scaleWellPi(double wellPi);
bool prepareWellPIScaling(); bool prepareWellPIScaling();
bool applyWellPIScaling(const double scaleFactor); bool applyWellPIScaling(const double scaleFactor);
void updateSegmentRST(int segment_number_arg, void updateSegmentRST(int segment_number_arg,
double center_depth_arg); double center_depth_arg);
void updateSegment(int segment_number_arg, void updateSegment(int segment_number_arg,
double center_depth_arg, double center_depth_arg,
std::size_t compseg_insert_index, std::size_t compseg_insert_index,
const std::optional<std::pair<double,double>>& perf_range); const std::optional<std::pair<double,double>>& perf_range);
std::size_t sort_value() const;
const bool& getDefaultSatTabId() const;
void setDefaultSatTabId(bool id);
const std::optional<std::pair<double, double>>& 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<class Serializer> template<class Serializer>
void serializeOp(Serializer& serializer) void serializeOp(Serializer& serializer)
{ {
serializer(direction); serializer(this->direction);
serializer(center_depth); serializer(this->center_depth);
serializer(open_state); serializer(this->open_state);
serializer(sat_tableId); serializer(this->sat_tableId);
serializer(m_complnum); serializer(this->m_complnum);
serializer(m_CF); serializer(this->ctf_properties_);
serializer(m_Kh); serializer(this->ijk);
serializer(m_rw); serializer(this->m_ctfkind);
serializer(m_r0); serializer(this->m_global_index);
serializer(m_re); serializer(this->m_injmult);
serializer(m_connection_length); serializer(this->m_sort_value);
serializer(m_skin_factor); serializer(this->m_perf_range);
serializer(m_d_factor); serializer(this->m_defaultSatTabId);
serializer(m_Ke); serializer(this->segment_number);
serializer(ijk); serializer(this->m_wpimult);
serializer(m_global_index); serializer(this->m_subject_to_welpi);
serializer(m_ctfkind); serializer(this->m_filter_cake);
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);
} }
private: private:
Direction direction; // Note to maintainer: If you add new members to this list, then
double center_depth; // please also update the operator==(), serializeOp(), and
State open_state; // serializationTestObject() member functions.
int sat_tableId; Direction direction { Direction::Z };
int m_complnum; double center_depth { 0.0 };
double m_CF; State open_state { State::SHUT };
double m_Kh; int sat_tableId { -1 };
double m_rw; int m_complnum { -1 };
double m_r0; CTFProperties ctf_properties_{};
double m_re;
double m_connection_length; std::array<int,3> ijk{};
double m_skin_factor; CTFKind m_ctfkind { CTFKind::DeckValue };
double m_d_factor; std::optional<InjMult> m_injmult{};
double m_Ke; std::size_t m_global_index{};
std::array<int,3> ijk;
CTFKind m_ctfkind;
std::optional<InjMult> m_injmult;
std::size_t m_global_index;
/* /*
The sort_value member is a peculiar quantity. The connections are The sort_value member is a peculiar quantity. The connections are
assembled in the WellConnections class. During the lifetime of the 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 explicitly, so the truth is probably that the storage order
during simulation makes no difference? during simulation makes no difference?
*/ */
std::size_t m_sort_value{};
std::size_t m_sort_value; std::optional<std::pair<double,double>> m_perf_range{};
std::optional<std::pair<double,double>> m_perf_range; bool m_defaultSatTabId{true};
bool m_defaultSatTabId;
// related segment number // Associate segment number
// 0 means the completion is not related to segment //
int segment_number = 0; // 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. // 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 std::optional<FilterCake> m_filter_cake{};
double m_wpimult = 1.0;
std::optional<FilterCake> m_filter_cake;
static std::string CTFKindToString(const CTFKind); static std::string CTFKindToString(const CTFKind);
}; };
}
#endif /* COMPLETION_HPP_ */ } // namespace Opm
#endif // COMPLETION_HPP_

View File

@ -21,6 +21,7 @@
#define WDFAC_HPP_HEADER_INCLUDED #define WDFAC_HPP_HEADER_INCLUDED
namespace Opm { namespace Opm {
class Connection;
class DeckRecord; class DeckRecord;
class WellConnections; class WellConnections;
} // namespace Opm } // namespace Opm
@ -31,49 +32,221 @@ namespace Opm { namespace RestartIO {
namespace Opm { namespace Opm {
enum class WDFACTYPE {
NONE = 1,
DFACTOR = 2,
DAKEMODEL = 3,
CON_DFACTOR = 4
};
class WDFAC class WDFAC
{ {
public: 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 <class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(this->coeff_a);
serializer(this->exponent_b);
serializer(this->exponent_c);
}
};
/// Default constructor
WDFAC() = default;
/// Constructor
///
/// Creates an object from restart information
///
/// \param[in] rst_well Linearised well-level restart information,
/// including D-factor parameters.
explicit WDFAC(const RestartIO::RstWell& rst_well);
/// Serialisation test object
static WDFAC serializationTestObject(); 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 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); 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); 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; 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;
bool operator!=(const WDFAC& other) const;
template<class Serializer> /// 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 <class Serializer>
void serializeOp(Serializer& serializer) void serializeOp(Serializer& serializer)
{ {
serializer(m_a); serializer(this->m_type);
serializer(m_b); serializer(this->m_d);
serializer(m_c); serializer(this->m_total_cf);
serializer(m_d); serializer(this->m_corr);
serializer(m_total_cf);
serializer(m_type);
} }
private: private:
double m_a{0.0}; /// D-factor categories.
double m_b{0.0}; enum class WDFacType
double m_c{0.0}; {
/// 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}; double m_d{0.0};
/// Total CTF sum for this well.
double m_total_cf{-1.0}; 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 } // namespace Opm

View File

@ -527,7 +527,7 @@ public:
bool handleWELSEGS(const DeckKeyword& keyword); bool handleWELSEGS(const DeckKeyword& keyword);
bool handleCOMPSEGS(const DeckKeyword& keyword, const ScheduleGrid& grid, const ParseContext& parseContext, ErrorGuard& errors); bool handleCOMPSEGS(const DeckKeyword& keyword, const ScheduleGrid& grid, const ParseContext& parseContext, ErrorGuard& errors);
bool handleWELOPENConnections(const DeckRecord& record, Connection::State status); 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 handleCOMPLUMP(const DeckRecord& record);
bool handleWPIMULT(const DeckRecord& record); bool handleWPIMULT(const DeckRecord& record);
bool handleWINJCLN(const DeckRecord& record, const KeywordLocation& location); bool handleWINJCLN(const DeckRecord& record, const KeywordLocation& location);

View File

@ -23,9 +23,11 @@
#include <opm/input/eclipse/Schedule/Well/Connection.hpp> #include <opm/input/eclipse/Schedule/Well/Connection.hpp>
#include <external/resinsight/LibGeometry/cvfBoundingBoxTree.h> #include <external/resinsight/LibGeometry/cvfBoundingBoxTree.h>
#include <array>
#include <cstddef> #include <cstddef>
#include <optional> #include <optional>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include <stddef.h> #include <stddef.h>
@ -33,10 +35,11 @@
namespace Opm { namespace Opm {
class ActiveGridCells; class ActiveGridCells;
class DeckRecord; class DeckRecord;
class EclipseGrid;
class FieldPropsManager; class FieldPropsManager;
class KeywordLocation; class KeywordLocation;
class ScheduleGrid; class ScheduleGrid;
class EclipseGrid; class WDFAC;
} // namespace Opm } // namespace Opm
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, void addConnection(const int i, const int j, const int k,
const std::size_t global_index, const std::size_t global_index,
const double depth,
const Connection::State state, const Connection::State state,
const double CF, const double depth,
const double Kh, const Connection::CTFProperties& ctf_props,
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 int satTableId,
const Connection::Direction direction = Connection::Direction::Z, const Connection::Direction direction = Connection::Direction::Z,
const Connection::CTFKind ctf_kind = Connection::CTFKind::DeckValue, const Connection::CTFKind ctf_kind = Connection::CTFKind::DeckValue,
@ -89,16 +89,26 @@ namespace Opm {
void loadCOMPDAT(const DeckRecord& record, void loadCOMPDAT(const DeckRecord& record,
const ScheduleGrid& grid, const ScheduleGrid& grid,
const std::string& wname, const std::string& wname,
const WDFAC& wdfac,
const KeywordLocation& location); const KeywordLocation& location);
void loadCOMPTRAJ(const DeckRecord& record, const ScheduleGrid& grid, const std::string& wname, const KeywordLocation& location, external::cvf::ref<external::cvf::BoundingBoxTree>& cellSearchTree); void loadCOMPTRAJ(const DeckRecord& record,
const ScheduleGrid& grid,
const std::string& wname,
const KeywordLocation& location,
external::cvf::ref<external::cvf::BoundingBoxTree>& 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 getHeadI() const;
int getHeadJ() const; int getHeadJ() const;
const std::vector<double>& getMD() const; const std::vector<double>& getMD() const;
void add(Connection);
std::size_t size() const; std::size_t size() const;
bool empty() const; bool empty() const;
std::size_t num_open() const; std::size_t num_open() const;
@ -165,39 +175,32 @@ namespace Opm {
serializer(this->coord); serializer(this->coord);
serializer(this->md); serializer(this->md);
} }
private: private:
Connection::Order m_ordering { Connection::Order::TRACK }; Connection::Order m_ordering { Connection::Order::TRACK };
int headI{0}; int headI{0};
int headJ{0}; int headJ{0};
std::vector<Connection> m_connections{}; std::vector<Connection> m_connections{};
std::vector<std::vector<double>> coord{3, std::vector<double>(0, 0.0) };
std::array<std::vector<double>, 3> coord{};
std::vector<double> md{}; std::vector<double> md{};
void addConnection(const int i, const int j, const int k, void addConnection(const int i, const int j, const int k,
const std::size_t global_index, const std::size_t global_index,
const int complnum, const int complnum,
const double depth,
const Connection::State state, const Connection::State state,
const double CF, const double depth,
const double Kh, const Connection::CTFProperties& ctf_props,
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 int satTableId,
const Connection::Direction direction = Connection::Direction::Z, const Connection::Direction direction,
const Connection::CTFKind ctf_kind = Connection::CTFKind::DeckValue, const Connection::CTFKind ctf_kind,
const std::size_t seqIndex = 0, const std::size_t seqIndex,
const bool defaultSatTabId = true); const bool defaultSatTabId);
size_t findClosestConnection(int oi, int oj, double oz, size_t start_pos); size_t findClosestConnection(int oi, int oj, double oz, size_t start_pos);
void orderTRACK(); void orderTRACK();
void orderMSW(); void orderMSW();
void orderDEPTH(); void orderDEPTH();
}; };
std::optional<int> std::optional<int>

View File

@ -3,7 +3,8 @@
This file is part of the Open Porous Media project (OPM). This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
@ -19,19 +20,27 @@
#ifndef RST_CONNECTION #ifndef RST_CONNECTION
#define RST_CONNECTION #define RST_CONNECTION
#include <array>
#include <opm/input/eclipse/Schedule/Well/Connection.hpp> #include <opm/input/eclipse/Schedule/Well/Connection.hpp>
#include <array>
namespace Opm { namespace Opm {
class UnitSystem; class UnitSystem;
namespace RestartIO { } // namespace Opm
class Header; namespace Opm { namespace RestartIO {
struct RstConnection
{
RstConnection(const UnitSystem& unit_system,
std::size_t rst_index,
int nsconz,
const int* icon,
const float* scon,
const double *xcon);
struct RstConnection {
RstConnection(const ::Opm::UnitSystem& unit_system, std::size_t rst_index, int nsconz, const int* icon, const float* scon, const double *xcon);
static double inverse_peaceman(double cf, double kh, double rw, double skin); static double inverse_peaceman(double cf, double kh, double rw, double skin);
std::size_t rst_index; std::size_t rst_index;
@ -49,6 +58,9 @@ struct RstConnection {
float depth; float depth;
float diameter; float diameter;
float kh; float kh;
float denom;
float length;
float static_dfac_corr_coeff;
float segdist_end; float segdist_end;
float segdist_start; float segdist_start;
@ -60,11 +72,6 @@ struct RstConnection {
double r0; double r0;
}; };
}} // namespace Opm::RestartIO
} #endif // RST_CONNECTION
}
#endif

View File

@ -126,6 +126,9 @@ struct RstWell
float glift_min_rate; float glift_min_rate;
float glift_weight_factor; float glift_weight_factor;
float glift_inc_weight_factor; float glift_inc_weight_factor;
float dfac_corr_coeff_a{};
float dfac_corr_exponent_b{};
float dfac_corr_exponent_c{};
std::vector<float> tracer_concentration_injection; std::vector<float> tracer_concentration_injection;
double oil_rate; double oil_rate;

View File

@ -36,9 +36,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
namespace Opm { namespace Opm { namespace data {
namespace data {
class Rates { class Rates {
/* Methods are defined inline for performance, as the actual *work* done /* Methods are defined inline for performance, as the actual *work* done
@ -257,22 +255,25 @@ namespace Opm {
double effective_Kh; double effective_Kh;
double trans_factor; double trans_factor;
double d_factor; double d_factor;
double compact_mult{1.0}; // Rock compaction transmissibility multiplier (ROCKTAB)
ConnectionFiltrate filtrate; ConnectionFiltrate filtrate;
bool operator==(const Connection& conn2) const bool operator==(const Connection& conn2) const
{ {
return index == conn2.index && return (index == conn2.index)
rates == conn2.rates && && (rates == conn2.rates)
pressure == conn2.pressure && && (pressure == conn2.pressure)
reservoir_rate == conn2.reservoir_rate && && (reservoir_rate == conn2.reservoir_rate)
cell_pressure == conn2.cell_pressure && && (cell_pressure == conn2.cell_pressure)
cell_saturation_water == conn2.cell_saturation_water && && (cell_saturation_water == conn2.cell_saturation_water)
cell_saturation_gas == conn2.cell_saturation_gas && && (cell_saturation_gas == conn2.cell_saturation_gas)
effective_Kh == conn2.effective_Kh && && (effective_Kh == conn2.effective_Kh)
trans_factor == conn2.trans_factor && && (trans_factor == conn2.trans_factor)
d_factor == conn2.d_factor && && (d_factor == conn2.d_factor)
filtrate == conn2.filtrate; && (compact_mult == conn2.compact_mult)
&& (filtrate == conn2.filtrate)
;
} }
template <class MessageBufferType> template <class MessageBufferType>
@ -295,15 +296,18 @@ namespace Opm {
serializer(effective_Kh); serializer(effective_Kh);
serializer(trans_factor); serializer(trans_factor);
serializer(d_factor); serializer(d_factor);
serializer(compact_mult);
serializer(filtrate); serializer(filtrate);
} }
static Connection serializationTestObject() static Connection serializationTestObject()
{ {
return Connection{1, Rates::serializationTestObject(), return Connection {
2.0, 3.0, 4.0, 5.0, 1, Rates::serializationTestObject(),
6.0, 7.0, 8.0, 9.0, 2.0, 3.0, 4.0, 5.0,
ConnectionFiltrate::serializationTestObject() }; 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->effective_Kh);
buffer.write(this->trans_factor); buffer.write(this->trans_factor);
buffer.write(this->d_factor); buffer.write(this->d_factor);
buffer.write(this->compact_mult);
this->filtrate.write(buffer); this->filtrate.write(buffer);
} }
@ -1203,6 +1208,7 @@ namespace Opm {
json_data.add_item("Kh", this->effective_Kh); json_data.add_item("Kh", this->effective_Kh);
json_data.add_item("trans_factor", this->trans_factor); json_data.add_item("trans_factor", this->trans_factor);
json_data.add_item("d_factor", this->d_factor); json_data.add_item("d_factor", this->d_factor);
json_data.add_item("compact_mult", this->compact_mult);
} }
template <class MessageBufferType> template <class MessageBufferType>
@ -1349,6 +1355,7 @@ namespace Opm {
buffer.read(this->effective_Kh); buffer.read(this->effective_Kh);
buffer.read(this->trans_factor); buffer.read(this->trans_factor);
buffer.read(this->d_factor); buffer.read(this->d_factor);
buffer.read(this->compact_mult);
this->filtrate.read(buffer); this->filtrate.read(buffer);
} }

View File

@ -44,19 +44,29 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems
namespace SConn { namespace SConn {
enum index : std::vector<float>::size_type { enum index : std::vector<float>::size_type {
ConnTrans = 0, // Connection transmissibility factor EffConnTrans = 0, // Effective connection transmissibility factor (incl. ROCKTAB &c)
Depth = 1, // Connection centre depth Depth = 1, // Connection centre depth
Diameter = 2, // Connection diameter Diameter = 2, // Connection diameter
EffectiveKH = 3, // Effective Kh product of connection EffectiveKH = 3, // Effective Kh product of connection
SkinFactor = 4, // Skinfactor - item 'SKIN' from COMPDAT SkinFactor = 4, // Skinfactor - item 'SKIN' from COMPDAT
item12 = 11, // Connection transmissibility factor
CFDenom = 6, // Denominator in connection transmissibility
// factor expression
ConnTrans = 11, // Connection transmissibility factor
SegDistEnd = 20, // Distance to end of connection in segment SegDistEnd = 20, // Distance to end of connection in segment
SegDistStart = 21, // Distance to start of connection in segment SegDistStart = 21, // Distance to start of connection in segment
item30 = 29, // Unknown item30 = 29, // Unknown
item31 = 30, // Unknown item31 = 30, // Unknown
EffectiveLength = 31, // Effective length of connection's perforation interval.
StaticDFacCorrCoeff = 37, // Static component of Forchheimer
// D-factor correlation.
CFInDeck = 40, // = 0 for connection factor not defined, = 1 for connection factor defined CFInDeck = 40, // = 0 for connection factor not defined, = 1 for connection factor defined
}; };
} // SConn } // SConn

View File

@ -331,6 +331,9 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems
VfpBhpScalingFact = 83, // Tubing pressure loss scaling factor (WVFPDP(3)) VfpBhpScalingFact = 83, // Tubing pressure loss scaling factor (WVFPDP(3))
WGrupConGRScaling = 84, // Guide rate scaling factor (WGRUPCON(5)) WGrupConGRScaling = 84, // Guide rate scaling factor (WGRUPCON(5))
DFacCorrCoeffA = 104, // Coefficient 'A' of D-factor correlation (WDFACCOR(2))
DFacCorrExpB = 105, // Exponent 'B' of D-factor correlation (WDFACCOR(3))
DFacCorrExpC = 106, // Exponent 'C' of D-factor correlation (WDFACCOR(4))
LOincFac = 115, LOincFac = 115,

View File

@ -19,50 +19,105 @@
#include <opm/input/eclipse/Schedule/CompletedCells.hpp> #include <opm/input/eclipse/Schedule/CompletedCells.hpp>
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) 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_) Opm::CompletedCells::CompletedCells(const Opm::GridDims& dims_)
:dims(dims_) : dims(dims_)
{} {}
const Opm::CompletedCells::Cell&
const Opm::CompletedCells::Cell& Opm::CompletedCells::get(std::size_t i, std::size_t j, std::size_t k) const { 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); return this->cells.at(this->dims.getGlobalIndex(i, j, k));
} }
std::pair<bool, Opm::CompletedCells::Cell&>
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<bool, Opm::CompletedCells::Cell&> Opm::CompletedCells::try_get(std::size_t i, std::size_t j, std::size_t k) { const auto& [pos, inserted] = this->cells.try_emplace(g, g, i, j, k);
auto g = this->dims.getGlobalIndex(i,j,k);
auto iter = this->cells.find(g);
if (iter != this->cells.end())
return {true, iter->second};
this->cells.emplace(g, Cell{g,i,j,k}); return { !inserted, pos->second };
return {false, this->cells.at(g)};
} }
bool Opm::CompletedCells::operator==(const Opm::CompletedCells& other) const
bool Opm::CompletedCells::operator==(const Opm::CompletedCells& other) const { {
return this->dims == other.dims && return (this->dims == other.dims)
this->cells == other.cells; && (this->cells == other.cells)
;
} }
Opm::CompletedCells
Opm::CompletedCells Opm::CompletedCells::serializationTestObject() { Opm::CompletedCells::serializationTestObject()
{
Opm::CompletedCells cells(2,3,4); Opm::CompletedCells cells(2,3,4);
cells.cells.emplace(7, Opm::CompletedCells::Cell::serializationTestObject()); cells.cells.emplace(7, Opm::CompletedCells::Cell::serializationTestObject());
return cells; 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; 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(); return this->props.has_value();
} }

View File

@ -196,37 +196,51 @@ namespace {
this->snapshots.back().network.update( std::move( ext_network )); this->snapshots.back().network.update( std::move( ext_network ));
} }
void Schedule::handleCOMPDAT(HandlerContext& handlerContext) { void Schedule::handleCOMPDAT(HandlerContext& handlerContext) {
std::unordered_set<std::string> wells; std::unordered_set<std::string> wells;
for (const auto& record : handlerContext.keyword) { for (const auto& record : handlerContext.keyword) {
const std::string& wellNamePattern = record.getItem("WELL").getTrimmedString(0); const auto wellNamePattern = record.getItem("WELL").getTrimmedString(0);
auto wellnames = this->wellNames(wellNamePattern, handlerContext, const auto wellnames =
isWList(handlerContext.currentStep, this->wellNames(wellNamePattern, handlerContext,
wellNamePattern)); isWList(handlerContext.currentStep, wellNamePattern));
for (const auto& name : wellnames) { for (const auto& name : wellnames) {
auto well2 = this->snapshots.back().wells.get(name); auto well2 = this->snapshots.back().wells.get(name);
auto connections = std::shared_ptr<WellConnections>( new WellConnections( well2.getConnections()));
connections->loadCOMPDAT(record, handlerContext.grid, name, handlerContext.keyword.location());
if (well2.updateConnections(connections, handlerContext.grid)) { auto connections = std::make_shared<WellConnections>(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<WDFAC>(well2.getWDFAC()); auto wdfac = std::make_shared<WDFAC>(well2.getWDFAC());
wdfac->updateWDFACType(*connections); wdfac->updateWDFACType(well2.getConnections());
well2.updateWDFAC(std::move(wdfac)); well2.updateWDFAC(std::move(wdfac));
this->snapshots.back().wells.update( well2 ); this->snapshots.back().wells.update(well2);
wells.insert( name );
wells.insert(name);
} }
if (connections->empty() && well2.getConnections().empty()) { if (origWellConnSetIsEmpty && newWellConnSetIsEmpty) {
const auto& location = handlerContext.keyword.location(); const auto& location = handlerContext.keyword.location();
auto msg = fmt::format("Problem with COMPDAT/{}\n"
"In {} line {}\n" const auto msg = fmt::format(R"(Problem with COMPDAT/{}
"Well {} is not connected to grid - will remain SHUT", name, location.filename, location.lineno, name); In {} line {}
Well {} is not connected to grid - will remain SHUT)",
name, location.filename,
location.lineno, name);
OpmLog::warning(msg); 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); this->snapshots.back().events().addEvent(ScheduleEvents::COMPLETION_CHANGE);
// In the case the wells reference depth has been defaulted in the // 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 // reference depth exactly when the COMPDAT keyword has been completely
// processed. // processed.
for (const auto& wname : wells) { for (const auto& wname : wells) {
auto& well = this->snapshots.back().wells.get( wname ); auto well = this->snapshots.back().wells.get(wname);
well.updateRefDepth(); well.updateRefDepth();
this->snapshots.back().wells.update( std::move(well));
this->snapshots.back().wells.update(std::move(well));
} }
if (! wells.empty()) { if (! wells.empty()) {
@ -268,20 +283,30 @@ namespace {
this->snapshots.back().events().addEvent(ScheduleEvents::COMPLETION_CHANGE); this->snapshots.back().events().addEvent(ScheduleEvents::COMPLETION_CHANGE);
} }
void Schedule::handleCOMPTRAJ(HandlerContext& handlerContext) { void Schedule::handleCOMPTRAJ(HandlerContext& handlerContext)
{
// Keyword WELTRAJ must be read first // Keyword WELTRAJ must be read first
std::unordered_set<std::string> wells; std::unordered_set<std::string> wells;
external::cvf::ref<external::cvf::BoundingBoxTree> cellSearchTree = nullptr; external::cvf::ref<external::cvf::BoundingBoxTree> cellSearchTree = nullptr;
for (const auto& record : handlerContext.keyword) { for (const auto& record : handlerContext.keyword) {
const std::string& wellNamePattern = record.getItem("WELL").getTrimmedString(0); const auto wellNamePattern = record.getItem("WELL").getTrimmedString(0);
auto wellnames = this->wellNames(wellNamePattern, handlerContext ); const auto wellnames = this->wellNames(wellNamePattern, handlerContext);
for (const auto& name : wellnames) { for (const auto& name : wellnames) {
auto well2 = this->snapshots.back().wells.get(name); auto well2 = this->snapshots.back().wells.get(name);
auto connections = std::make_shared<WellConnections>(WellConnections(well2.getConnections())); 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); // cellsearchTree is calculated only once and is used to
// In the case that defaults are used in WELSPECS for headI/J the headI/J are calculated based on the well trajectory data // 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()); well2.updateHead(connections->getHeadI(), connections->getHeadJ());
if (well2.updateConnections(connections, handlerContext.grid)) { if (well2.updateConnections(connections, handlerContext.grid)) {
this->snapshots.back().wells.update( well2 ); 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); "Well {} is not connected to grid - will remain SHUT", name, location.filename, location.lineno, name);
OpmLog::warning(msg); 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); this->snapshots.back().events().addEvent(ScheduleEvents::COMPLETION_CHANGE);
// In the case the wells reference depth has been defaulted in the // In the case the wells reference depth has been defaulted in the
// WELSPECS keyword we need to force a calculation of the wells // WELSPECS keyword we need to force a calculation of the wells
// reference depth exactly when the WELCOML keyword has been completely // reference depth exactly when the WELCOML keyword has been
// processed. // completely processed.
for (const auto& wname : wells) { for (const auto& wname : wells) {
auto& well = this->snapshots.back().wells.get( wname ); auto& well = this->snapshots.back().wells.get(wname);
well.updateRefDepth(); well.updateRefDepth();
this->snapshots.back().wells.update( std::move(well));
this->snapshots.back().wells.update(std::move(well));
} }
if (! wells.empty()) { if (! wells.empty()) {
@ -377,23 +406,29 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
handlerContext.compsegs_handled(wname); 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 // Get CSKIN keyword info and current step
const auto& keyword = handlerContext.keyword; const auto& keyword = handlerContext.keyword;
const auto& currentStep = handlerContext.currentStep; const auto& currentStep = handlerContext.currentStep;
// Loop over records in CSKIN // Loop over records in CSKIN
for (const auto& record: keyword) { for (const auto& record : keyword) {
// Get well names // Get well names
const auto& wellNamePattern = record.getItem( "WELL" ).getTrimmedString(0); const auto wellNamePattern = record.getItem<Kw::WELL>().getTrimmedString(0);
const auto well_names = this->wellNames(wellNamePattern, handlerContext); const auto well_names = this->wellNames(wellNamePattern, handlerContext);
// Loop over well(s) in record // Loop over well(s) in record
for (const auto& wname : well_names) { 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); 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<WDFAC>(well.getWDFAC()); auto wdfac = std::make_shared<WDFAC>(well.getWDFAC());
wdfac->updateWDFAC( record ); wdfac->updateWDFAC( record );
wdfac->updateTotalCF(well.getConnections()); 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) { void Schedule::handleWDFACCOR(HandlerContext& handlerContext) {
for (const auto& record : handlerContext.keyword) { 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); const auto well_names = wellNames(wellNamePattern, handlerContext.currentStep);
if (well_names.empty()) if (well_names.empty())
this->invalidNamePattern(wellNamePattern, handlerContext); this->invalidNamePattern(wellNamePattern, handlerContext);
for (const auto& well_name : well_names) { for (const auto& well_name : well_names) {
auto well = this->snapshots.back().wells.get(well_name); auto well = this->snapshots.back().wells.get(well_name);
auto conns = std::make_shared<WellConnections>(well.getConnections());
auto wdfac = std::make_shared<WDFAC>(well.getWDFAC()); auto wdfac = std::make_shared<WDFAC>(well.getWDFAC());
wdfac->updateWDFACCOR( record ); wdfac->updateWDFACCOR(record);
if (well.updateWDFAC(std::move(wdfac)))
this->snapshots.back().wells.update( std::move(well) ); 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);
}
}
} }
} }
} }

View File

@ -17,16 +17,24 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <fmt/format.h>
#include <opm/input/eclipse/Schedule/CompletedCells.hpp>
#include <opm/input/eclipse/Schedule/ScheduleGrid.hpp> #include <opm/input/eclipse/Schedule/ScheduleGrid.hpp>
#include <opm/input/eclipse/Schedule/CompletedCells.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp> #include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp> #include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
Opm::ScheduleGrid::ScheduleGrid(const Opm::EclipseGrid& ecl_grid, const Opm::FieldPropsManager& fpm, Opm::CompletedCells& completed_cells) #include <cstddef>
: grid(&ecl_grid) #include <string>
, fp(&fpm)
, cells(completed_cells) #include <fmt/format.h>
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) Opm::ScheduleGrid::ScheduleGrid(Opm::CompletedCells& completed_cells)
@ -34,45 +42,58 @@ Opm::ScheduleGrid::ScheduleGrid(Opm::CompletedCells& completed_cells)
{} {}
namespace { namespace {
double try_get_value(const Opm::FieldPropsManager& fp, const std::string& kw, std::size_t active_index) { double try_get_value(const Opm::FieldPropsManager& fp,
if (fp.has_double(kw)) const std::string& kw,
return fp.try_get<double>(kw)->at(active_index); const std::size_t active_index)
else {
if (! fp.has_double(kw)) {
throw std::logic_error(fmt::format("FieldPropsManager is missing keyword '{}'", 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<double>(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 fp.try_get<double>(kw)->at(active_index);
return this->cells.get(i,j,k); }
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<double>(kw)->at(active_index)
: 1.0;
}
} }
const Opm::EclipseGrid* Opm::ScheduleGrid::get_grid() const { const Opm::CompletedCells::Cell&
return this->grid; 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;
} }

View File

@ -17,125 +17,164 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm> #include <opm/input/eclipse/Schedule/Well/Connection.hpp>
#include <cassert>
#include <exception>
#include <sstream>
#include <stdexcept>
#include <vector>
#include <opm/io/eclipse/rst/connection.hpp> #include <opm/io/eclipse/rst/connection.hpp>
#include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
#include <opm/input/eclipse/Schedule/ScheduleGrid.hpp>
#include <opm/input/eclipse/Schedule/Well/FilterCake.hpp>
#include <opm/input/eclipse/Deck/DeckItem.hpp> #include <opm/input/eclipse/Deck/DeckItem.hpp>
#include <opm/input/eclipse/Deck/DeckKeyword.hpp> #include <opm/input/eclipse/Deck/DeckKeyword.hpp>
#include <opm/input/eclipse/Deck/DeckRecord.hpp> #include <opm/input/eclipse/Deck/DeckRecord.hpp>
#include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
#include <opm/input/eclipse/Schedule/Well/Connection.hpp>
#include <opm/input/eclipse/Schedule/Well/FilterCake.hpp>
#include <opm/input/eclipse/Schedule/ScheduleGrid.hpp>
namespace Opm { #include <algorithm>
#include <array>
#include <cassert>
Connection::Connection(int i, int j , int k , #include <cstddef>
std::size_t global_index, #include <exception>
int compnum, #include <sstream>
double depth, #include <stdexcept>
State stateArg , #include <string>
double CF, #include <string_view>
double Kh, #include <utility>
double rw, #include <vector>
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 { 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 = rst_conn.length;
props.skin_factor = rst_conn.skin_factor;
props.d_factor = 0.0;
props.static_dfac_corr_coeff = rst_conn.static_dfac_corr_coeff;
props.peaceman_denom = rst_conn.denom;
return props;
}
} }
Connection::Connection(const RestartIO::RstConnection& rst_connection, const ScheduleGrid& grid, const FieldPropsManager& fp) : namespace Opm
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)
{
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];
}
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::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;
} }
Connection::Connection() bool Connection::CTFProperties::operator==(const CTFProperties& that) const
: 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) 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 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);
}
// TODO: recompute re from the grid
}
Connection Connection::serializationTestObject() Connection Connection::serializationTestObject()
{ {
Connection result; Connection result;
result.direction = Direction::Y; result.direction = Direction::Y;
result.center_depth = 1.0; result.center_depth = 1.0;
result.open_state = State::OPEN; result.open_state = State::OPEN;
result.sat_tableId = 2; result.sat_tableId = 2;
result.m_complnum = 3; result.m_complnum = 3;
result.m_CF = 4.0;
result.m_Kh = 5.0; result.ctf_properties_ = CTFProperties::serializationTestObject();
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.ijk = {9, 10, 11}; result.ijk = {9, 10, 11};
result.m_ctfkind = CTFKind::Defaulted; result.m_ctfkind = CTFKind::Defaulted;
result.m_global_index = 12; result.m_global_index = 12;
@ -144,144 +183,181 @@ Connection::Connection(const RestartIO::RstConnection& rst_connection, const Sch
result.m_sort_value = 14; result.m_sort_value = 14;
result.m_defaultSatTabId = true; result.m_defaultSatTabId = true;
result.segment_number = 16; result.segment_number = 16;
result.m_wpimult = 0.123;
result.m_subject_to_welpi = true; result.m_subject_to_welpi = true;
result.m_filter_cake = FilterCake::serializationTestObject(); result.m_filter_cake = FilterCake::serializationTestObject();
return result; return result;
} }
bool Connection::sameCoordinate(const int i, const int j, const int k) const { 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; return this->ijk == std::array { i, j, k };
} else {
return false;
}
} }
int Connection::getI() const { int Connection::getI() const
{
return ijk[0]; return ijk[0];
} }
int Connection::getJ() const { int Connection::getJ() const
{
return ijk[1]; return ijk[1];
} }
int Connection::getK() const { int Connection::getK() const
{
return ijk[2]; return ijk[2];
} }
std::size_t Connection::global_index() const { std::size_t Connection::global_index() const
{
return this->m_global_index; return this->m_global_index;
} }
bool Connection::attachedToSegment() const { bool Connection::attachedToSegment() const
return (segment_number > 0); {
return this->segment_number > 0;
} }
std::size_t Connection::sort_value() const { std::size_t Connection::sort_value() const
{
return m_sort_value; return m_sort_value;
} }
const bool& Connection::getDefaultSatTabId() const { bool Connection::getDefaultSatTabId() const
{
return m_defaultSatTabId; return m_defaultSatTabId;
} }
Connection::Direction Connection::dir() const { Connection::Direction Connection::dir() const
{
return this->direction; return this->direction;
} }
const std::optional<std::pair<double, double>>& Connection::perf_range() const { const std::optional<std::pair<double, double>>&
Connection::perf_range() const
{
return this->m_perf_range; return this->m_perf_range;
} }
void Connection::setDefaultSatTabId(bool id) { void Connection::setDefaultSatTabId(bool id)
{
m_defaultSatTabId = id; m_defaultSatTabId = id;
} }
double Connection::depth() const { double Connection::depth() const
{
return this->center_depth; return this->center_depth;
} }
Connection::State Connection::state() const { Connection::State Connection::state() const
{
return this->open_state; return this->open_state;
} }
int Connection::satTableId() const { int Connection::satTableId() const
{
return this->sat_tableId; return this->sat_tableId;
} }
int Connection::complnum() const { int Connection::complnum() const
{
return this->m_complnum; return this->m_complnum;
} }
void Connection::setComplnum(int complnum) { void Connection::setComplnum(int complnum)
{
this->m_complnum = complnum; this->m_complnum = complnum;
} }
void Connection::setSkinFactor(double skin_factor) { void Connection::setSkinFactor(double skin_factor)
this->m_skin_factor = 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) { void Connection::setDFactor(double d_factor)
this->m_d_factor = d_factor; {
this->ctf_properties_.d_factor = d_factor;
} }
void Connection::setKe(double Ke) { void Connection::setKe(double Ke)
this->m_Ke = Ke; {
this->ctf_properties_.Ke = Ke;
} }
void Connection::setCF(double CF) { void Connection::setCF(double CF)
this->m_CF = CF; {
this->ctf_properties_.CF = CF;
} }
double Connection::CF() const { double Connection::wpimult() const
return this->m_CF; {
}
double Connection::wpimult() const {
return this->m_wpimult; return this->m_wpimult;
} }
double Connection::Kh() const { double Connection::CF() const
return this->m_Kh; {
return this->ctf_properties_.CF;
} }
double Connection::rw() const { double Connection::Kh() const
return this->m_rw; {
return this->ctf_properties_.Kh;
} }
double Connection::r0() const { double Connection::rw() const
return this->m_r0; {
return this->ctf_properties_.rw;
} }
double Connection::re() const { double Connection::r0() const
return this->m_re; {
return this->ctf_properties_.r0;
} }
double Connection::connectionLength() const { double Connection::re() const
return this->m_connection_length; {
return this->ctf_properties_.re;
} }
double Connection::skinFactor() const { double Connection::connectionLength() const
return this->m_skin_factor; {
return this->ctf_properties_.connection_length;
} }
double Connection::dFactor() const { double Connection::skinFactor() const
return this->m_d_factor; {
return this->ctf_properties_.skin_factor;
} }
double Connection::Ke() const { double Connection::dFactor() const
return this->m_Ke; {
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; this->open_state = state;
} }
void Connection::updateSegment(int segment_number_arg, void Connection::updateSegment(const int segment_number_arg,
double center_depth_arg, const double center_depth_arg,
std::size_t compseg_insert_index, const std::size_t compseg_insert_index,
const std::optional<std::pair<double, double>>& perf_range) { const std::optional<std::pair<double, double>>& perf_range)
{
this->segment_number = segment_number_arg; this->segment_number = segment_number_arg;
this->center_depth = center_depth_arg; this->center_depth = center_depth_arg;
this->m_sort_value = compseg_insert_index; this->m_sort_value = compseg_insert_index;
@ -289,124 +365,142 @@ const std::optional<std::pair<double, double>>& Connection::perf_range() const {
} }
void Connection::updateSegmentRST(int segment_number_arg, void Connection::updateSegmentRST(int segment_number_arg,
double center_depth_arg) { double center_depth_arg)
{
this->segment_number = segment_number_arg; this->segment_number = segment_number_arg;
this->center_depth = center_depth_arg; this->center_depth = center_depth_arg;
} }
int Connection::segment() const { int Connection::segment() const
{
return this->segment_number; return this->segment_number;
} }
void Connection::scaleWellPi(double wellPi) { void Connection::scaleWellPi(double wellPi)
{
this->m_wpimult *= 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; const auto update = !this->m_subject_to_welpi;
this->m_subject_to_welpi = true; this->m_subject_to_welpi = true;
return update; return update;
} }
bool Connection::applyWellPIScaling(const double scaleFactor) { bool Connection::applyWellPIScaling(const double scaleFactor)
if (! this->m_subject_to_welpi) {
if (! this->m_subject_to_welpi) {
return false; return false;
}
this->scaleWellPi(scaleFactor); this->scaleWellPi(scaleFactor);
return true; 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; std::stringstream ss;
ss << "ijk: " << this->ijk[0] << "," << this->ijk[1] << "," << this->ijk[2] << std::endl;
ss << "COMPLNUM " << this->m_complnum << std::endl; ss << "ijk: " << this->ijk[0] << ',' << this->ijk[1] << ',' << this->ijk[2] << '\n'
ss << "CF " << this->m_CF << std::endl; << "COMPLNUM " << this->m_complnum << '\n'
ss << "RW " << this->m_rw << std::endl; << "CF " << this->CF() << '\n'
ss << "R0 " << this->m_r0 << std::endl; << "RW " << this->rw() << '\n'
ss << "Re " << this->m_re << std::endl; << "R0 " << this->r0() << '\n'
ss << "connection length " << this->m_connection_length << std::endl; << "Re " << this->re() << '\n'
ss << "skinf " << this->m_skin_factor << std::endl; << "connection length " << this->connectionLength() << '\n'
ss << "dfactor " << this->m_d_factor << std::endl; << "skinf " << this->skinFactor() << '\n'
ss << "Ke " << this->m_Ke << std::endl; << "dfactor " << this->dFactor() << '\n'
ss << "kh " << this->m_Kh << std::endl; << "Ke " << this->Ke() << '\n'
ss << "sat_tableId " << this->sat_tableId << std::endl; << "kh " << this->Kh() << '\n'
ss << "open_state " << Connection::State2String(this->open_state) << std::endl; << "sat_tableId " << this->sat_tableId << '\n'
ss << "direction " << Connection::Direction2String(this->direction) << std::endl; << "open_state " << Connection::State2String(this->open_state) << '\n'
ss << "CTF Source " << Connection::CTFKindToString(this->m_ctfkind) << '\n'; << "direction " << Connection::Direction2String(this->direction) << '\n'
ss << "segment_nr " << this->segment_number << std::endl; << "CTF Source " << Connection::CTFKindToString(this->m_ctfkind) << '\n'
ss << "center_depth " << this->center_depth << std::endl; << "segment_nr " << this->segment_number << '\n'
ss << "sort_value" << this->m_sort_value<< std::endl; << "center_depth " << this->center_depth << '\n'
<< "sort_value" << this->m_sort_value<< '\n';
if (this->m_injmult.has_value()) { 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()) { 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(); 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 { bool Connection::operator==(const Connection& that) const
return !( *this == rhs ); {
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)
;
} }
std::string Connection::State2String(State enumValue)
const std::string Connection::State2String( State enumValue ) { {
switch( enumValue ) { switch (enumValue) {
case State::OPEN: case State::OPEN:
return "OPEN"; return "OPEN";
case State::AUTO: case State::AUTO:
return "AUTO"; return "AUTO";
case State::SHUT: case State::SHUT:
return "SHUT"; return "SHUT";
default: default:
throw std::invalid_argument("Unhandled enum value"); throw std::invalid_argument {
"Unhandled Connection::State value " +
std::to_string(static_cast<int>(enumValue))
};
} }
} }
Connection::State
Connection::State Connection::StateFromString( const std::string& stringValue ) { Connection::StateFromString(std::string_view stringValue)
{
if (stringValue == "OPEN") if (stringValue == "OPEN")
return State::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) std::string Connection::Direction2String(const Direction enumValue)
@ -434,8 +528,7 @@ std::string Connection::Direction2String(const Direction enumValue)
return stringValue; return stringValue;
} }
Connection::Direction Connection::DirectionFromString(std::string_view s)
Connection::Direction Connection::DirectionFromString(const std::string& s )
{ {
Direction direction; Direction direction;
@ -443,47 +536,61 @@ Connection::Direction Connection::DirectionFromString(const std::string& s )
else if ((s == "Y") || (s == "y")) { direction = Direction::Y; } else if ((s == "Y") || (s == "y")) { direction = Direction::Y; }
else if ((s == "Z") || (s == "z")) { direction = Direction::Z; } else if ((s == "Z") || (s == "z")) { direction = Direction::Z; }
else { else {
std::string msg = "Unsupported completion direction " + s; throw std::invalid_argument {
throw std::invalid_argument(msg); "Unsupported completion direction "
+ std::string { s }
};
} }
return direction; return direction;
} }
const std::string Connection::Order2String( Order enumValue ) { std::string Connection::Order2String(Order enumValue)
switch( enumValue ) { {
switch (enumValue) {
case Order::DEPTH: case Order::DEPTH:
return "DEPTH"; return "DEPTH";
case Order::INPUT: case Order::INPUT:
return "INPUT"; return "INPUT";
case Order::TRACK: case Order::TRACK:
return "TRACK"; return "TRACK";
default: default:
throw std::invalid_argument("Unhandled enum value"); throw std::invalid_argument {
"Unhandled Connection::Order value " +
std::to_string(static_cast<int>(enumValue))
};
} }
} }
Connection::Order Connection::OrderFromString(std::string_view stringValue)
Connection::Order Connection::OrderFromString(const std::string& stringValue ) { {
if (stringValue == "DEPTH") if (stringValue == "DEPTH")
return Order::DEPTH; return Order::DEPTH;
else if (stringValue == "INPUT")
if (stringValue == "INPUT")
return Order::INPUT; return Order::INPUT;
else if (stringValue == "TRACK")
if (stringValue == "TRACK")
return Order::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) std::string Connection::CTFKindToString(const CTFKind ctf_kind)
{ {
switch (ctf_kind) { switch (ctf_kind) {
case CTFKind::DeckValue: case CTFKind::DeckValue:
return "DeckValue"; return "DeckValue";
case CTFKind::Defaulted: case CTFKind::Defaulted:
return "Defaulted"; return "Defaulted";
} }
throw std::invalid_argument { throw std::invalid_argument {
@ -492,58 +599,62 @@ std::string Connection::CTFKindToString(const CTFKind ctf_kind)
}; };
} }
Connection::CTFKind Connection::kind() const { Connection::CTFKind Connection::kind() const
{
return m_ctfkind; return m_ctfkind;
} }
const InjMult& Connection::injmult() const { const InjMult& Connection::injmult() const
{
assert(this->activeInjMult()); assert(this->activeInjMult());
return m_injmult.value(); return m_injmult.value();
} }
bool Connection::activeInjMult() const { bool Connection::activeInjMult() const
{
return this->m_injmult.has_value(); return this->m_injmult.has_value();
} }
void Connection::setInjMult(const InjMult& inj_mult) { void Connection::setInjMult(const InjMult& inj_mult)
{
m_injmult = inj_mult; m_injmult = inj_mult;
} }
void Connection::setFilterCake(const FilterCake& filter_cake)
void Connection::setFilterCake(const FilterCake& filter_cake) { {
this->m_filter_cake = filter_cake; this->m_filter_cake = filter_cake;
} }
bool Connection::filterCakeActive() const { bool Connection::filterCakeActive() const
{
return this->m_filter_cake.has_value(); return this->m_filter_cake.has_value();
} }
const FilterCake& Connection::getFilterCake() const { const FilterCake& Connection::getFilterCake() const
assert(this->filterCakeActive()); {
return this->m_filter_cake.value(); assert(this->filterCakeActive());
return this->m_filter_cake.value();
} }
double Connection::getFilterCakeRadius() const
double Connection::getFilterCakeRadius() const { {
if (this->getFilterCake().radius.has_value()) { if (const auto& radius = this->getFilterCake().radius; radius.has_value()) {
return this->getFilterCake().radius.value(); return *radius;
} else { }
return this->m_rw; else {
return this->rw();
} }
} }
double Connection::getFilterCakeArea() const
double Connection::getFilterCakeArea() const { {
if (this->getFilterCake().flow_area.has_value()) { if (const auto& flow_area = this->getFilterCake().flow_area; flow_area.has_value()) {
return this->getFilterCake().flow_area.value(); return *flow_area;
} else { }
const double radius = this->getFilterCakeRadius(); else {
const double length = this->m_connection_length; constexpr double two_pi = 2 * 3.14159265358979323846264;
constexpr double pi = 3.14159265; return two_pi * this->getFilterCakeRadius() * this->connectionLength();
return 2. * pi * radius * length;
} }
} }
} // end of namespace Opm } // end of namespace Opm

View File

@ -17,126 +17,180 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <opm/input/eclipse/Schedule/Well/WDFAC.hpp>
#include <opm/io/eclipse/rst/well.hpp> #include <opm/io/eclipse/rst/well.hpp>
#include <opm/output/eclipse/VectorItems/well.hpp> #include <opm/output/eclipse/VectorItems/well.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
#include <opm/input/eclipse/Deck/DeckRecord.hpp> #include <opm/input/eclipse/Schedule/Well/Connection.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Units/Dimension.hpp> #include <opm/input/eclipse/Units/Dimension.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp> #include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/input/eclipse/Units/Units.hpp> #include <opm/input/eclipse/Units/Units.hpp>
#include <opm/input/eclipse/Schedule/Well/WDFAC.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Deck/DeckRecord.hpp>
#include <string> #include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
#include <vector>
#include <cmath> #include <algorithm>
#include <iostream>
#include <cassert> #include <cassert>
#include <cmath>
#include <numeric> #include <numeric>
#include <string>
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 { 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(const RestartIO::RstWell& rst_well)
: m_type { WDFacType::NONE }
, m_d { 0.0 }
, m_corr { rst_well.dfac_corr_coeff_a ,
rst_well.dfac_corr_exponent_b ,
rst_well.dfac_corr_exponent_c }
{
if (m_corr.coeff_a > 0.0) {
this->m_type = WDFacType::DAKEMODEL;
}
}
WDFAC WDFAC::serializationTestObject() WDFAC WDFAC::serializationTestObject()
{ {
WDFAC result; WDFAC result;
result.m_a = 1.23;
result.m_b = 0.456; result.m_type = WDFacType::DAKEMODEL;
result.m_c = 0.457;
result.m_d = 0.458; result.m_d = 0.458;
result.m_total_cf = 1.0; result.m_total_cf = 1.0;
result.m_type = WDFACTYPE::NONE; result.m_corr = Correlation::serializationTestObject();
return result; return result;
} }
bool WDFAC::operator==(const WDFAC& other) const { void WDFAC::updateWDFAC(const DeckRecord& record)
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) {
m_d = record.getItem<ParserKeywords::WDFAC::DFACTOR>().getSIDouble(0); m_d = record.getItem<ParserKeywords::WDFAC::DFACTOR>().getSIDouble(0);
m_type = WDFACTYPE::DFACTOR;
m_type = WDFacType::DFACTOR;
} }
void WDFAC::updateWDFACCOR(const DeckRecord& record) { void WDFAC::updateWDFACCOR(const DeckRecord& record)
m_a = record.getItem<ParserKeywords::WDFACCOR::A>().getSIDouble(0); {
m_b = record.getItem<ParserKeywords::WDFACCOR::B>().getSIDouble(0); this->m_corr.coeff_a = record.getItem<ParserKeywords::WDFACCOR::A>().getSIDouble(0);
m_c = record.getItem<ParserKeywords::WDFACCOR::C>().getSIDouble(0); this->m_corr.exponent_b = record.getItem<ParserKeywords::WDFACCOR::B>().getSIDouble(0);
m_type = WDFACTYPE::DAKEMODEL; this->m_corr.exponent_c = record.getItem<ParserKeywords::WDFACCOR::C>().getSIDouble(0);
m_type = WDFacType::DAKEMODEL;
} }
void WDFAC::updateWDFACType(const WellConnections& connections) { void WDFAC::updateWDFACType(const WellConnections& connections)
{
const auto non_trivial_dfactor = const auto non_trivial_dfactor =
std::any_of(connections.begin(), connections.end(), std::any_of(connections.begin(), connections.end(),
[](const auto& conn) { return conn.dFactor() != 0.0; }); [](const auto& conn) { return conn.dFactor() != 0.0; });
// non-trivial dfactors detected use connection D factors
if (non_trivial_dfactor) { if (non_trivial_dfactor) {
m_type = WDFACTYPE::CON_DFACTOR; // Non-trivial D-factors detected. Use connection D-factors.
updateTotalCF(connections); m_type = WDFacType::CON_DFACTOR;
this->updateTotalCF(connections);
} }
} }
void WDFAC::updateTotalCF(const WellConnections& 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(); }); 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 { double WDFAC::getDFactor(const double rhoGS,
const double gas_visc,
switch (m_type) const Connection& conn) const
{ {
case WDFACTYPE::NONE: switch (this->m_type) {
case WDFacType::NONE:
return 0.0; 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::DFACTOR:
} return this->scaledWellLevelDFactor(this->m_d, conn);
case WDFACTYPE::DAKEMODEL:
{ case WDFacType::DAKEMODEL:
double Kh = connection.Kh(); return dakeModelDFactor(rhoGS, gas_visc, conn.ctfProperties());
double Ke = connection.Ke();
double h = Kh / Ke; case WDFacType::CON_DFACTOR:
double rw = connection.rw(); return this->connectionLevelDFactor(conn);
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;
} }
return 0.0; return 0.0;
} }
bool WDFAC::useDFactor() const { bool WDFAC::useDFactor() const
return m_type != WDFACTYPE::NONE; {
return m_type != WDFacType::NONE;
} }
bool WDFAC::operator!=(const WDFAC& other) const { bool WDFAC::operator==(const WDFAC& other) const
return !(*this == other); {
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();
} }
} }

View File

@ -40,13 +40,12 @@
#include <opm/input/eclipse/Schedule/Well/WVFPDP.hpp> #include <opm/input/eclipse/Schedule/Well/WVFPDP.hpp>
#include <opm/input/eclipse/Schedule/Well/WVFPEXP.hpp> #include <opm/input/eclipse/Schedule/Well/WVFPEXP.hpp>
#include <opm/input/eclipse/Units/Units.hpp> #include <opm/input/eclipse/Units/Units.hpp>
#include <opm/common/utility/OpmInputError.hpp> #include <opm/common/utility/OpmInputError.hpp>
#include <opm/common/utility/shmatch.hpp> #include <opm/common/utility/shmatch.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/C.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/S.hpp> #include <opm/input/eclipse/Parser/ParserKeywords/S.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp> #include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
@ -314,7 +313,7 @@ Well::Well(const RestartIO::RstWell& rst_well,
injection(std::make_shared<WellInjectionProperties>(unit_system_arg, wname)), injection(std::make_shared<WellInjectionProperties>(unit_system_arg, wname)),
wvfpdp(std::make_shared<WVFPDP>()), wvfpdp(std::make_shared<WVFPDP>()),
wvfpexp(explicitTHPOptions(rst_well)), wvfpexp(explicitTHPOptions(rst_well)),
wdfac(std::make_shared<WDFAC>()), wdfac(std::make_shared<WDFAC>(rst_well)),
status(status_from_int(rst_well.well_status)), status(status_from_int(rst_well.well_status)),
well_temperature(Metric::TemperatureOffset + ParserKeywords::STCOND::TEMPERATURE::defaultValue), well_temperature(Metric::TemperatureOffset + ParserKeywords::STCOND::TEMPERATURE::defaultValue),
well_inj_mult(std::nullopt) well_inj_mult(std::nullopt)
@ -1300,42 +1299,60 @@ bool Well::handleWELOPENConnections(const DeckRecord& record, Connection::State
return this->updateConnections(std::move(new_connections), false); return this->updateConnections(std::move(new_connections), false);
} }
bool Well::handleCSKINConnections(const DeckRecord& record) { bool Well::handleCSKIN(const DeckRecord& record,
// Lambda expression to check if record coordinates match connection coordinates const KeywordLocation& location)
auto match = [=]( const Connection &c) -> bool { {
if (!match_eq(c.getI(), record, "I" , -1)) return false; using Kw = ParserKeywords::CSKIN;
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;
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 // New connection set which will be updated with new connection level
auto new_connections = std::make_shared<WellConnections>(this->connections->ordering(), this->headI, this->headJ); // skin factors.
auto new_connections = std::make_shared<WellConnections>
(this->connections->ordering(), this->headI, this->headJ);
// Update skin factor const auto skin_factor = record.getItem<Kw::CONNECTION_SKIN_FACTOR>().getSIDouble(0);
double skin_factor = record.getItem("CONNECTION_SKIN_FACTOR").get<double>(0); for (const auto& connection : *this->connections) {
const double angle = 6.2831853071795864769252867665590057683943387987502116419498; if (! need_skin_adjustment(connection)) {
for (auto c : *this->connections) { // No CSKIN adjustment needed here. Include connection as-is
if (match(c)) { // into new connection set.
// Check for potential negative new CF new_connections->add(connection);
if ((std::log(c.r0() / std::min(c.rw(), c.r0())) + skin_factor) < 0.0) { continue;
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);
} }
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); return this->updateConnections(std::move(new_connections), false);
@ -1390,6 +1407,7 @@ bool Well::handleWPIMULT(const DeckRecord& record) {
new_connections->add(c); new_connections->add(c);
} }
return this->updateConnections(std::move(new_connections), false); return this->updateConnections(std::move(new_connections), false);
} }

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,8 @@
This file is part of the Open Porous Media project (OPM). This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
@ -16,99 +17,129 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cmath>
#include <stdexcept>
#include <opm/io/eclipse/rst/header.hpp>
#include <opm/io/eclipse/rst/connection.hpp> #include <opm/io/eclipse/rst/connection.hpp>
#include <opm/output/eclipse/VectorItems/connection.hpp> #include <opm/output/eclipse/VectorItems/connection.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp> #include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <array>
#include <cmath>
#include <cstddef>
#include <stdexcept>
#include <string>
namespace VI = ::Opm::RestartIO::Helpers::VectorItems; namespace VI = ::Opm::RestartIO::Helpers::VectorItems;
namespace Opm {
namespace RestartIO {
namespace { namespace {
template <typename T> template <typename T>
T from_int(int); T from_int(int);
template <> template<> Opm::Connection::State from_int(int int_state)
Connection::State from_int(int int_state) { {
if (int_state == 1) return (int_state == 1)
return Connection::State::OPEN; ? Opm::Connection::State::OPEN
: Opm::Connection::State::SHUT;
return Connection::State::SHUT;
} }
template <> template<> Opm::Connection::Direction from_int(int int_dir)
Connection::Direction from_int(int int_dir) { {
switch (int_dir) { switch (int_dir) {
case 1: case 1: return Opm::Connection::Direction::X;
return Connection::Direction::X; case 2: return Opm::Connection::Direction::Y;
case 2: case 3: return Opm::Connection::Direction::Z;
return Connection::Direction::Y;
case 3:
return Connection::Direction::Z;
default:
throw std::invalid_argument("Can not convert: " + std::to_string(int_dir) + " to string");
} }
throw std::invalid_argument {
"Unable to convert direction value: " +
std::to_string(int_dir) + " to Direction category"
};
} }
/* // Note: CTFKind originates in SCON and is indeed a float.
That the CTFKind variable comes from a float looks extremely suspicious; but Opm::Connection::CTFKind from_float(float float_kind)
it has been double checked ... {
*/ return (float_kind == 0.0f)
Connection::CTFKind from_float(float float_kind) { ? Opm::Connection::CTFKind::Defaulted
if (float_kind == 0) : Opm::Connection::CTFKind::DeckValue;
return Connection::CTFKind::Defaulted;
return Connection::CTFKind::DeckValue;
}
} }
double RstConnection::inverse_peaceman(double cf, double kh, double rw, double skin) { float as_float(const double x)
{
return static_cast<float>(x);
}
float staticDFactorCorrCoeff(const Opm::UnitSystem& usys,
const float coeff)
{
using M = ::Opm::UnitSystem::measure;
// Coefficient's units are [D] * [viscosity]
return as_float(usys.to_si(M::viscosity, usys.to_si(M::dfactor, coeff)));
}
double pressEquivRadius(const float denom,
const float skin,
const float rw)
{
// Recall: denom = log(r0 / rw) + skin
return rw * std::exp(denom - skin);
}
} // Anonymous namespace
double Opm::RestartIO::RstConnection::inverse_peaceman(double cf, double kh, double rw, double skin)
{
auto alpha = 3.14159265 * 2 * kh / cf - skin; auto alpha = 3.14159265 * 2 * kh / cf - skin;
return rw * std::exp(alpha); return rw * std::exp(alpha);
} }
using M = ::Opm::UnitSystem::measure; using M = ::Opm::UnitSystem::measure;
RstConnection::RstConnection(const ::Opm::UnitSystem& unit_system, std::size_t rst_index_, int nsconz, const int* icon, const float* scon, const double* xcon) : Opm::RestartIO::RstConnection::RstConnection(const UnitSystem& unit_system,
rst_index( rst_index_), const std::size_t rst_index_,
ijk( {icon[VI::IConn::CellI] - 1, icon[VI::IConn::CellJ] - 1, icon[VI::IConn::CellK] - 1}), const int nsconz,
state( from_int<Connection::State>(icon[VI::IConn::ConnStat])), const int* icon,
drain_sat_table( icon[VI::IConn::Drainage]), const float* scon,
imb_sat_table( icon[VI::IConn::Imbibition]), const double* xcon)
completion( icon[VI::IConn::ComplNum]), : rst_index { rst_index_ }
dir( from_int<Connection::Direction>(icon[VI::IConn::ConnDir])), // -----------------------------------------------------------------
segment( icon[VI::IConn::Segment]), // Integer values (ICON)
cf_kind( from_float(1.0)), , ijk { icon[VI::IConn::CellI] - 1 ,
skin_factor( scon[VI::SConn::SkinFactor]), icon[VI::IConn::CellJ] - 1 ,
cf( unit_system.to_si(M::transmissibility, scon[VI::SConn::ConnTrans])), icon[VI::IConn::CellK] - 1 }
depth( unit_system.to_si(M::length, scon[VI::SConn::Depth])), , state { from_int<Connection::State>(icon[VI::IConn::ConnStat]) }
diameter( unit_system.to_si(M::length, scon[VI::SConn::Diameter])), , drain_sat_table { icon[VI::IConn::Drainage] }
kh( unit_system.to_si(M::effective_Kh, scon[VI::SConn::EffectiveKH])), , imb_sat_table { icon[VI::IConn::Imbibition] }
segdist_end( unit_system.to_si(M::length, scon[VI::SConn::SegDistEnd])), , completion { icon[VI::IConn::ComplNum] }
segdist_start( unit_system.to_si(M::length, scon[VI::SConn::SegDistStart])), , dir { from_int<Connection::Direction>(icon[VI::IConn::ConnDir]) }
oil_rate( unit_system.to_si(M::liquid_surface_rate, xcon[VI::XConn::OilRate])), , segment { icon[VI::IConn::Segment] }
water_rate( unit_system.to_si(M::liquid_surface_rate, xcon[VI::XConn::WaterRate])), // -----------------------------------------------------------------
gas_rate( unit_system.to_si(M::gas_surface_rate, xcon[VI::XConn::GasRate])), // Float values (SCON)
pressure( unit_system.to_si(M::pressure, xcon[VI::XConn::Pressure])), , cf_kind { from_float(1.0f) }
resv_rate( unit_system.to_si(M::rate, xcon[VI::XConn::ResVRate])), , skin_factor { scon[VI::SConn::SkinFactor] }
r0( RstConnection::inverse_peaceman(this->cf, this->kh, this->diameter/2, this->skin_factor) ) , cf { as_float(unit_system.to_si(M::transmissibility, scon[VI::SConn::ConnTrans])) }
/* , depth { as_float(unit_system.to_si(M::length, scon[VI::SConn::Depth])) }
r0: The r0 quantity is currently not written or read from the restart , diameter { as_float(unit_system.to_si(M::length, scon[VI::SConn::Diameter])) }
file. If the r0 value is given explicitly in the deck it is possible , kh { as_float(unit_system.to_si(M::effective_Kh, scon[VI::SConn::EffectiveKH])) }
to give a value which is not consistent with the Peaceman formula - , denom { scon[VI::SConn::CFDenom] }
that value will be lost when loading back from a restart file. , length { as_float(unit_system.to_si(M::length, scon[VI::SConn::EffectiveLength])) }
*/ , static_dfac_corr_coeff { staticDFactorCorrCoeff(unit_system, scon[VI::SConn::StaticDFacCorrCoeff]) }
, segdist_end { as_float(unit_system.to_si(M::length, scon[VI::SConn::SegDistEnd])) }
, segdist_start { as_float(unit_system.to_si(M::length, scon[VI::SConn::SegDistStart])) }
// -----------------------------------------------------------------
// Double values (XCON)
, oil_rate { unit_system.to_si(M::liquid_surface_rate, xcon[VI::XConn::OilRate]) }
, water_rate { unit_system.to_si(M::liquid_surface_rate, xcon[VI::XConn::WaterRate]) }
, gas_rate { unit_system.to_si(M::gas_surface_rate, xcon[VI::XConn::GasRate]) }
, pressure { unit_system.to_si(M::pressure, xcon[VI::XConn::Pressure]) }
, resv_rate { unit_system.to_si(M::rate, xcon[VI::XConn::ResVRate]) }
// -----------------------------------------------------------------
// Derived quantities
, r0 { pressEquivRadius(this->denom, this->skin_factor, this->diameter / 2) }
{ {
if (static_cast<std::size_t>(nsconz) > VI::SConn::CFInDeck) if (static_cast<std::size_t>(nsconz) > VI::SConn::CFInDeck) {
this->cf_kind = from_float(scon[VI::SConn::CFInDeck]); this->cf_kind = from_float(scon[VI::SConn::CFInDeck]);
} }
}
} }

View File

@ -3,7 +3,8 @@
This file is part of the Open Porous Media project (OPM). This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.

View File

@ -17,17 +17,24 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <opm/common/utility/String.hpp> #include <opm/io/eclipse/rst/well.hpp>
#include <opm/io/eclipse/rst/header.hpp> #include <opm/io/eclipse/rst/header.hpp>
#include <opm/io/eclipse/rst/connection.hpp> #include <opm/io/eclipse/rst/connection.hpp>
#include <opm/io/eclipse/rst/well.hpp>
#include <opm/output/eclipse/VectorItems/connection.hpp> #include <opm/output/eclipse/VectorItems/connection.hpp>
#include <opm/output/eclipse/VectorItems/msw.hpp> #include <opm/output/eclipse/VectorItems/msw.hpp>
#include <opm/output/eclipse/VectorItems/well.hpp> #include <opm/output/eclipse/VectorItems/well.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp> #include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/common/utility/String.hpp>
#include <opm/input/eclipse/Parser/ParserItem.hpp>
#include <opm/input/eclipse/Parser/ParserKeyword.hpp>
#include <opm/input/eclipse/Parser/ParserRecord.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
@ -54,28 +61,34 @@ namespace {
{ {
return is_sentinel(raw_value) ? raw_value : convert(raw_value); return is_sentinel(raw_value) ? raw_value : convert(raw_value);
} }
}
float dfactor_correlation_coefficient_a(const Opm::UnitSystem& unit_system,
const float coeff_a)
{
const auto dimension = Opm::ParserKeywords::WDFACCOR{}
.getRecord(0).get(Opm::ParserKeywords::WDFACCOR::A::itemName)
.dimensions().front();
return static_cast<float>(unit_system.to_si(dimension, coeff_a));
}
constexpr int def_ecl_phase = 1;
constexpr int def_pvt_table = 0;
} // Anonymous namespace
namespace VI = ::Opm::RestartIO::Helpers::VectorItems; namespace VI = ::Opm::RestartIO::Helpers::VectorItems;
using M = ::Opm::UnitSystem::measure;
namespace Opm { Opm::RestartIO::RstWell::RstWell(const UnitSystem& unit_system,
namespace RestartIO { const RstHeader& header,
const std::string& group_arg,
constexpr int def_ecl_phase = 1; const std::string* zwel,
constexpr int def_pvt_table = 0; const int* iwel,
const float* swel,
using M = ::Opm::UnitSystem::measure; const double* xwel,
const int* icon,
RstWell::RstWell(const ::Opm::UnitSystem& unit_system, const float* scon,
const RstHeader& header, const double* xcon) :
const std::string& group_arg,
const std::string* zwel,
const int * iwel,
const float * swel,
const double * xwel,
const int * icon,
const float * scon,
const double * xcon) :
name(rtrim_copy(zwel[0])), name(rtrim_copy(zwel[0])),
group(group_arg), group(group_arg),
ij( {iwel[VI::IWell::IHead] - 1, iwel[VI::IWell::JHead] - 1}), ij( {iwel[VI::IWell::IHead] - 1, iwel[VI::IWell::JHead] - 1}),
@ -139,6 +152,9 @@ RstWell::RstWell(const ::Opm::UnitSystem& unit_system,
glift_min_rate( unit_system.to_si(M::gas_surface_rate, swel[VI::SWell::LOminRate])), glift_min_rate( unit_system.to_si(M::gas_surface_rate, swel[VI::SWell::LOminRate])),
glift_weight_factor( swel[VI::SWell::LOweightFac]), glift_weight_factor( swel[VI::SWell::LOweightFac]),
glift_inc_weight_factor( swel[VI::SWell::LOincFac]), glift_inc_weight_factor( swel[VI::SWell::LOincFac]),
dfac_corr_coeff_a(dfactor_correlation_coefficient_a(unit_system, swel[VI::SWell::DFacCorrCoeffA])),
dfac_corr_exponent_b( swel[VI::SWell::DFacCorrExpB]),
dfac_corr_exponent_c( swel[VI::SWell::DFacCorrExpC]),
// //
oil_rate( unit_system.to_si(M::liquid_surface_rate, xwel[VI::XWell::OilPrRate])), oil_rate( unit_system.to_si(M::liquid_surface_rate, xwel[VI::XWell::OilPrRate])),
water_rate( unit_system.to_si(M::liquid_surface_rate, xwel[VI::XWell::WatPrRate])), water_rate( unit_system.to_si(M::liquid_surface_rate, xwel[VI::XWell::WatPrRate])),
@ -167,66 +183,81 @@ RstWell::RstWell(const ::Opm::UnitSystem& unit_system,
gas_void_rate( unit_system.to_si(M::gas_surface_volume, xwel[VI::XWell::GasVoidPrRate])) gas_void_rate( unit_system.to_si(M::gas_surface_volume, xwel[VI::XWell::GasVoidPrRate]))
{ {
for (std::size_t tracer_index = 0; tracer_index < static_cast<std::size_t>(header.runspec.tracers().water_tracers()); tracer_index++) for (std::size_t tracer_index = 0;
this->tracer_concentration_injection.push_back( swel[VI::SWell::TracerOffset + tracer_index] ); tracer_index < static_cast<std::size_t>(header.runspec.tracers().water_tracers());
++tracer_index)
{
this->tracer_concentration_injection.push_back(swel[VI::SWell::TracerOffset + tracer_index]);
}
for (int ic = 0; ic < iwel[VI::IWell::NConn]; ++ic) {
const std::size_t icon_offset = ic * header.niconz;
const std::size_t scon_offset = ic * header.nsconz;
const std::size_t xcon_offset = ic * header.nxconz;
for (int ic = 0; ic < iwel[VI::IWell::NConn]; ic++) { this->connections.emplace_back(unit_system, ic, header.nsconz,
std::size_t icon_offset = ic * header.niconz; icon + icon_offset,
std::size_t scon_offset = ic * header.nsconz; scon + scon_offset,
std::size_t xcon_offset = ic * header.nxconz; xcon + xcon_offset);
this->connections.emplace_back( unit_system, ic, header.nsconz, icon + icon_offset, scon + scon_offset, xcon + xcon_offset);
} }
} }
RstWell::RstWell(const ::Opm::UnitSystem& unit_system, Opm::RestartIO::RstWell::RstWell(const UnitSystem& unit_system,
const RstHeader& header, const RstHeader& header,
const std::string& group_arg, const std::string& group_arg,
const std::string* zwel, const std::string* zwel,
const int * iwel, const int* iwel,
const float * swel, const float* swel,
const double * xwel, const double* xwel,
const int * icon, const int* icon,
const float * scon, const float* scon,
const double * xcon, const double* xcon,
const std::vector<int>& iseg, const std::vector<int>& iseg,
const std::vector<double>& rseg) : const std::vector<double>& rseg)
RstWell(unit_system, header, group_arg, zwel, iwel, swel, xwel, icon, scon, xcon) : RstWell { unit_system, header, group_arg,
zwel, iwel, swel, xwel,
icon, scon, xcon }
{ {
if (this->msw_index == 0) {
return;
}
if (this->msw_index) { std::unordered_map<int, std::size_t> segment_map;
std::unordered_map<int, std::size_t> segment_map; for (int is = 0; is < header.nsegmx; ++is) {
for (int is=0; is < header.nsegmx; is++) { const std::size_t iseg_offset = header.nisegz * (is + (this->msw_index - 1)*header.nsegmx);
std::size_t iseg_offset = header.nisegz * (is + (this->msw_index - 1) * header.nsegmx); const std::size_t rseg_offset = header.nrsegz * (is + (this->msw_index - 1)*header.nsegmx);
std::size_t rseg_offset = header.nrsegz * (is + (this->msw_index - 1) * header.nsegmx); const auto other_segment_number = iseg[iseg_offset + VI::ISeg::SegNo];
auto other_segment_number = iseg[iseg_offset + VI::ISeg::SegNo];
if (other_segment_number != 0) { if (other_segment_number != 0) {
auto segment_number = is + 1; auto segment_number = is + 1;
segment_map.insert({segment_number, this->segments.size()}); segment_map.insert({segment_number, this->segments.size()});
this->segments.emplace_back( unit_system, segment_number, iseg.data() + iseg_offset, rseg.data() + rseg_offset); this->segments.emplace_back(unit_system, segment_number,
} iseg.data() + iseg_offset,
rseg.data() + rseg_offset);
} }
}
for (auto& segment : this->segments) { for (auto& segment : this->segments) {
if (segment.outlet_segment != 0) { if (segment.outlet_segment != 0) {
auto& outlet_segment = this->segments[ segment_map[segment.outlet_segment] ]; auto& outlet_segment = this->segments[ segment_map[segment.outlet_segment] ];
outlet_segment.inflow_segments.push_back(segment.segment); outlet_segment.inflow_segments.push_back(segment.segment);
}
} }
} }
} }
const RstSegment&
RstWell::segment(int segment_number) const
{
const auto& iter = std::find_if(this->segments.begin(), this->segments.end(), [segment_number](const RstSegment& segment) { return segment.segment == segment_number; });
if (iter == this->segments.end()) const Opm::RestartIO::RstSegment&
Opm::RestartIO::RstWell::segment(int segment_number) const
{
auto iter = std::find_if(this->segments.begin(), this->segments.end(),
[segment_number](const RstSegment& segment)
{ return segment.segment == segment_number; });
if (iter == this->segments.end()) {
throw std::invalid_argument("No such segment"); throw std::invalid_argument("No such segment");
}
return *iter; return *iter;
} }
}} // namepace Opm::RestartIO

View File

@ -167,6 +167,16 @@ namespace {
}; };
} }
double staticDFacCorrCoeff(const Opm::Connection::CTFProperties& ctf_props,
const Opm::UnitSystem& units)
{
using M = Opm::UnitSystem::measure;
// Coefficient's units are [D] * [viscosity]
return units.from_si(M::viscosity, units.from_si(M::dfactor, ctf_props.static_dfac_corr_coeff));
}
template <class SConnArray> template <class SConnArray>
void staticContrib(const Opm::Connection& conn, void staticContrib(const Opm::Connection& conn,
const Opm::UnitSystem& units, const Opm::UnitSystem& units,
@ -180,7 +190,7 @@ namespace {
return static_cast<float>(units.from_si(u, x)); return static_cast<float>(units.from_si(u, x));
}; };
sConn[Ix::ConnTrans] = sConn[Ix::EffConnTrans] = sConn[Ix::ConnTrans] =
scprop(M::transmissibility, conn.CF()); scprop(M::transmissibility, conn.CF());
sConn[Ix::Depth] = scprop(M::length, conn.depth()); sConn[Ix::Depth] = scprop(M::length, conn.depth());
@ -191,8 +201,7 @@ namespace {
sConn[Ix::SkinFactor] = conn.skinFactor(); sConn[Ix::SkinFactor] = conn.skinFactor();
sConn[Ix::item12] = sConn[Ix::ConnTrans]; sConn[Ix::CFDenom] = conn.ctfProperties().peaceman_denom;
if (conn.attachedToSegment()) { if (conn.attachedToSegment()) {
const auto& [start, end] = *conn.perf_range(); const auto& [start, end] = *conn.perf_range();
@ -202,7 +211,14 @@ namespace {
sConn[Ix::item30] = -1.0e+20f; sConn[Ix::item30] = -1.0e+20f;
sConn[Ix::item31] = -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::StaticDFacCorrCoeff] =
staticDFacCorrCoeff(conn.ctfProperties(), units);
sConn[Ix::CFInDeck] = conn.ctfAssignedFromInput() ? 1.0f : 0.0f;
} }
template <class SConnArray> template <class SConnArray>
@ -218,8 +234,15 @@ namespace {
return static_cast<float>(units.from_si(u, x)); return static_cast<float>(units.from_si(u, x));
}; };
sConn[Ix::item12] = sConn[Ix::ConnTrans] = sConn[Ix::EffConnTrans] = sConn[Ix::ConnTrans] =
scprop(M::transmissibility, xconn.trans_factor); scprop(M::transmissibility, xconn.trans_factor);
// xconn.trans_factor == CTFAC == CF * rock compaction. Divide
// out the rock compaction contribution to infer the "real"
// connection transmissibility factor. No additional unit
// conversion needed since the rock compaction effect (keyword
// ROCKTAB) is imparted through a dimensionless multiplier.
sConn[Ix::ConnTrans] /= xconn.compact_mult;
} }
} // SConn } // SConn
@ -314,12 +337,12 @@ AggregateConnectionData(const std::vector<int>& inteHead)
void void
Opm::RestartIO::Helpers::AggregateConnectionData:: Opm::RestartIO::Helpers::AggregateConnectionData::
captureDeclaredConnData(const Schedule& sched, captureDeclaredConnData(const Schedule& sched,
const EclipseGrid& grid, const EclipseGrid& grid,
const UnitSystem& units, const UnitSystem& units,
const data::Wells& xw, const data::Wells& xw,
const SummaryState& summary_state, const SummaryState& summary_state,
const std::size_t sim_step) const std::size_t sim_step)
{ {
wellConnectionLoop(sched, sim_step, grid, xw, [&units, &summary_state, this] wellConnectionLoop(sched, sim_step, grid, xw, [&units, &summary_state, this]
(const std::string& wellName, (const std::string& wellName,

View File

@ -37,6 +37,7 @@
#include <opm/input/eclipse/Schedule/Schedule.hpp> #include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp> #include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/VFPProdTable.hpp> #include <opm/input/eclipse/Schedule/VFPProdTable.hpp>
#include <opm/input/eclipse/Schedule/Well/WDFAC.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp> #include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp> #include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Schedule/Well/WellEconProductionLimits.hpp> #include <opm/input/eclipse/Schedule/Well/WellEconProductionLimits.hpp>
@ -49,6 +50,11 @@
#include <opm/input/eclipse/Units/UnitSystem.hpp> #include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/input/eclipse/Units/Units.hpp> #include <opm/input/eclipse/Units/Units.hpp>
#include <opm/input/eclipse/Parser/ParserItem.hpp>
#include <opm/input/eclipse/Parser/ParserKeyword.hpp>
#include <opm/input/eclipse/Parser/ParserRecord.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
@ -1028,6 +1034,26 @@ namespace {
sWell[Ix::EfficiencyFactor2] = sWell[Ix::EfficiencyFactor1]; sWell[Ix::EfficiencyFactor2] = sWell[Ix::EfficiencyFactor1];
} }
template <class SWellArray>
void assignDFactorCorrelation(const Opm::Well& well,
const Opm::UnitSystem& units,
SWellArray& sWell)
{
using Ix = VI::SWell::index;
const auto& corr = well.getWDFAC().getDFactorCorrelationCoefficients();
// D-Factor correlation exponents don't need unit conversion.
sWell[Ix::DFacCorrExpB] = corr.exponent_b;
sWell[Ix::DFacCorrExpC] = corr.exponent_c;
const auto dimension = Opm::ParserKeywords::WDFACCOR{}
.getRecord(0).get(Opm::ParserKeywords::WDFACCOR::A::itemName)
.dimensions().front();
sWell[Ix::DFacCorrCoeffA] = units.from_si(dimension, corr.coeff_a);
}
template <class SWProp, class SWellArray> template <class SWProp, class SWellArray>
void assignBhpVfpAdjustment(const Opm::Well& well, void assignBhpVfpAdjustment(const Opm::Well& well,
SWProp&& swprop, SWProp&& swprop,
@ -1094,6 +1120,7 @@ namespace {
assignWGrupCon(well, sWell); assignWGrupCon(well, sWell);
assignEfficiencyFactors(well, sWell); assignEfficiencyFactors(well, sWell);
assignDFactorCorrelation(well, units, sWell);
assignEconomicLimits(well, swprop, sWell); assignEconomicLimits(well, swprop, sWell);
assignWellTest(well.name(), sched, wtest_state, sim_step, swprop, sWell); assignWellTest(well.name(), sched, wtest_state, sim_step, swprop, sWell);
assignTracerData(tracers, smry, well.name(), sWell); assignTracerData(tracers, smry, well.name(), sWell);

View File

@ -17,32 +17,39 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cstddef>
#include <stdexcept>
#include <ostream>
#define BOOST_TEST_MODULE CompletionTests #define BOOST_TEST_MODULE CompletionTests
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <opm/common/utility/ActiveGridCells.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Schedule/Well/Connection.hpp> #include <opm/input/eclipse/Schedule/Well/Connection.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp> #include <opm/common/utility/ActiveGridCells.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp> #include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp> #include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/Tables/TableManager.hpp>
#include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp> #include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
#include <opm/input/eclipse/Schedule/CompletedCells.hpp> #include <opm/input/eclipse/EclipseState/Tables/TableManager.hpp>
#include <opm/input/eclipse/Schedule/ScheduleGrid.hpp>
#include <opm/input/eclipse/Schedule/CompletedCells.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp> #include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/ScheduleGrid.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WDFAC.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/common/OpmLog/KeywordLocation.hpp> #include <opm/common/OpmLog/KeywordLocation.hpp>
#include <opm/input/eclipse/Units/Units.hpp> #include <opm/input/eclipse/Units/Units.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <cstddef>
#include <stdexcept>
#include <ostream>
namespace { namespace {
double cp_rm3_per_db() double cp_rm3_per_db()
@ -51,20 +58,32 @@ namespace {
/ (Opm::unit::day * Opm::unit::barsa); / (Opm::unit::day * Opm::unit::barsa);
} }
Opm::WellConnections loadCOMPDAT(const std::string& compdat_keyword) { Opm::WellConnections
Opm::EclipseGrid grid(10,10,10); loadCOMPDAT(const std::string& compdat_keyword)
Opm::TableManager tables; {
Opm::Parser parser; Opm::WellConnections connections {
const auto deck = parser.parseString(compdat_keyword); Opm::Connection::Order::TRACK, 10, 10
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", {});
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 { namespace Opm {
@ -94,12 +113,22 @@ BOOST_AUTO_TEST_CASE(CreateWellConnectionsOK) {
BOOST_AUTO_TEST_CASE(AddCompletionSizeCorrect) { BOOST_AUTO_TEST_CASE(AddCompletionSizeCorrect)
auto dir = Opm::Connection::Direction::Z; {
const auto dir = Opm::Connection::Direction::Z;
const auto kind = Opm::Connection::CTFKind::DeckValue; 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::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 ); completionSet.add( completion1 );
BOOST_CHECK_EQUAL( 1U , completionSet.size() ); BOOST_CHECK_EQUAL( 1U , completionSet.size() );
BOOST_CHECK_MESSAGE( !completionSet.empty(), "Non-empty completion set must not be empty" ); 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) { BOOST_AUTO_TEST_CASE(WellConnectionsGetOutOfRangeThrows)
auto dir = Opm::Connection::Direction::Z; {
const auto dir = Opm::Connection::Direction::Z;
const auto kind = Opm::Connection::CTFKind::DeckValue; 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); const auto depth = 0.0;
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);
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::WellConnections completionSet(Opm::Connection::Order::TRACK, 1,1);
completionSet.add( completion1 ); completionSet.add( completion1 );
BOOST_CHECK_EQUAL( 1U , completionSet.size() ); BOOST_CHECK_EQUAL( 1U , completionSet.size() );
@ -148,15 +187,23 @@ BOOST_AUTO_TEST_CASE(Compdat_Direction) {
} }
BOOST_AUTO_TEST_CASE(AddCompletionCopy) { BOOST_AUTO_TEST_CASE(AddCompletionCopy)
Opm::WellConnections completionSet(Opm::Connection::Order::TRACK, 10,10); {
auto dir = Opm::Connection::Direction::Z; const auto dir = Opm::Connection::Direction::Z;
const auto kind = Opm::Connection::CTFKind::DeckValue; 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); auto ctf_props = Opm::Connection::CTFProperties{};
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);
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( completion1 );
completionSet.add( completion2 ); completionSet.add( completion2 );
completionSet.add( completion3 ); completionSet.add( completion3 );
@ -171,31 +218,43 @@ BOOST_AUTO_TEST_CASE(AddCompletionCopy) {
} }
BOOST_AUTO_TEST_CASE(ActiveCompletions) { BOOST_AUTO_TEST_CASE(ActiveCompletions)
Opm::EclipseGrid grid(10,20,20); {
auto dir = Opm::Connection::Direction::Z; const auto dir = Opm::Connection::Direction::Z;
const auto kind = Opm::Connection::CTFKind::Defaulted; const auto kind = Opm::Connection::CTFKind::DeckValue;
Opm::WellConnections completions(Opm::Connection::Order::TRACK, 10,10); const auto depth = 0.0;
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);
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( completion1 );
completions.add( completion2 ); completions.add( completion2 );
completions.add( completion3 ); completions.add( completion3 );
std::vector<int> actnum(grid.getCartesianSize(), 1); std::vector<int> actnum(grid.getCartesianSize(), 1);
actnum[0] = 0; 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( active_completions.size() , 2U);
BOOST_CHECK_EQUAL( completion2, active_completions.get(0)); BOOST_CHECK_EQUAL( completion2, active_completions.get(0));
BOOST_CHECK_EQUAL( completion3, active_completions.get(1)); BOOST_CHECK_EQUAL( completion3, active_completions.get(1));
} }
BOOST_AUTO_TEST_CASE(loadCOMPDATTEST) { BOOST_AUTO_TEST_CASE(loadCOMPDATTEST)
Opm::UnitSystem units(Opm::UnitSystem::UnitType::UNIT_TYPE_METRIC); // Unit system used in deck FIRST_SIM.DATA. {
const Opm::UnitSystem units(Opm::UnitSystem::UnitType::UNIT_TYPE_METRIC); // Unit system used in deck FIRST_SIM.DATA.
{ {
const std::string deck = R"(GRID const std::string deck = R"(GRID
@ -206,13 +265,18 @@ COPY
'PERMX' 'PERMZ' / 'PERMX' 'PERMZ' /
'PERMX' 'PERMY' / 'PERMX' 'PERMY' /
/ /
PORO
1000*0.3 /
SCHEDULE SCHEDULE
COMPDAT COMPDAT
-- CF Diam Kh Skin Df -- CF Diam Kh Skin Df
'WELL' 1 1 1 1 'OPEN' 1* 1.168 0.311 107.872 1* 1* 'Z' 21.925 / '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]; const auto& conn0 = connections[0];
BOOST_CHECK_EQUAL(conn0.CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, 1.168)); 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)); BOOST_CHECK_EQUAL(conn0.Kh(), units.to_si(Opm::UnitSystem::measure::effective_Kh, 107.872));
@ -230,13 +294,17 @@ COPY
'PERMX' 'PERMY' / 'PERMX' 'PERMY' /
/ /
PORO
1000*0.3 /
SCHEDULE SCHEDULE
COMPDAT COMPDAT
-- CF Diam Kh Skin Df -- CF Diam Kh Skin Df
'WELL' 1 1 1 1 'OPEN' 1* 1.168 0.311 0 1* 1* 'Z' 21.925 / '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]; const auto& conn0 = connections[0];
BOOST_CHECK_EQUAL(conn0.CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, 1.168)); 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)); 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) // 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 connP.addConnection(9, 9, 1, // 10, 10, 2
199, 199,
2015.0, Opm::Connection::State::OPEN,
Opm::Connection::State::OPEN, 2015.0, ctf_props, 1);
50.0*cp_rm3_per_db(),
0.123,
0.234,
0.157,
0.0,
0.0,
0.0,
0.0,
0.0,
1);
BOOST_REQUIRE_EQUAL(connP.size(), std::size_t{3}); BOOST_REQUIRE_EQUAL(connP.size(), std::size_t{3});
@ -506,19 +570,9 @@ END
// Reset CF -- simulating COMPDAT record (active cell) // Reset CF -- simulating COMPDAT record (active cell)
connP.addConnection(8, 9, 1, // 10, 10, 2 connP.addConnection(8, 9, 1, // 10, 10, 2
198, 198,
2015.0, Opm::Connection::State::OPEN,
Opm::Connection::State::OPEN, 2015.0, ctf_props, 1);
50.0*cp_rm3_per_db(),
0.123,
0.234,
0.157,
0.0,
0.0,
0.0,
0.0,
0.0,
1);
BOOST_REQUIRE_EQUAL(connP.size(), std::size_t{4}); BOOST_REQUIRE_EQUAL(connP.size(), std::size_t{4});

View File

@ -55,20 +55,32 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
BOOST_AUTO_TEST_CASE(AICDWellTest) { BOOST_AUTO_TEST_CASE(AICDWellTest)
{
auto dir = Opm::Connection::Direction::Z; const auto dir_z = Opm::Connection::Direction::Z;
const auto dir_x = Opm::Connection::Direction::X;
const auto kind = Opm::Connection::CTFKind::DeckValue; 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) ); const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 };
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) ); const auto depth = 0.0;
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 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() ); BOOST_CHECK_EQUAL( 7U , connection_set.size() );
@ -212,23 +224,33 @@ WSEGAICD
const double center_depth_connection7 = connection7.depth(); const double center_depth_connection7 = connection7.depth();
BOOST_CHECK_EQUAL(segment_number_connection7, 8); BOOST_CHECK_EQUAL(segment_number_connection7, 8);
BOOST_CHECK_EQUAL(center_depth_connection7, 2534.5); BOOST_CHECK_EQUAL(center_depth_connection7, 2534.5);
} }
BOOST_AUTO_TEST_CASE(MultisegmentWellTest) { BOOST_AUTO_TEST_CASE(MultisegmentWellTest)
{
auto dir = Opm::Connection::Direction::Z; const auto dir_z = Opm::Connection::Direction::Z;
const auto dir_x = Opm::Connection::Direction::X;
const auto kind = Opm::Connection::CTFKind::DeckValue; const auto kind = Opm::Connection::CTFKind::DeckValue;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 };
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) ); const auto depth = 0.0;
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) ); const auto state = Opm::Connection::State::OPEN;
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) ); 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() ); BOOST_CHECK_EQUAL( 7U , connection_set.size() );
@ -383,19 +405,32 @@ WSEGSICD
} }
BOOST_AUTO_TEST_CASE(WrongDistanceCOMPSEGS) { BOOST_AUTO_TEST_CASE(WrongDistanceCOMPSEGS)
auto dir = Opm::Connection::Direction::Z; {
const auto dir_z = Opm::Connection::Direction::Z;
const auto dir_x = Opm::Connection::Direction::X;
const auto kind = Opm::Connection::CTFKind::DeckValue; 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) ); const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25., 2500.0 };
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) ); const auto depth = 0.0;
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 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() ); 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_CHECK_NO_THROW(Opm::Compsegs::processCOMPSEGS(compsegs, connection_set, segment_set, Opm::ScheduleGrid(grid, fp, cells), parseContext, errorGuard));
} }
BOOST_AUTO_TEST_CASE(NegativeDepthCOMPSEGS) { BOOST_AUTO_TEST_CASE(NegativeDepthCOMPSEGS)
auto dir = Opm::Connection::Direction::Z; {
const auto dir_z = Opm::Connection::Direction::Z;
const auto dir_x = Opm::Connection::Direction::X;
const auto kind = Opm::Connection::CTFKind::DeckValue; 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) ); const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 };
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) ); const auto depth = 0.0;
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 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() ); 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_CHECK_NO_THROW( Opm::Compsegs::processCOMPSEGS(compsegs, connection_set, segment_set, Opm::ScheduleGrid(grid, fp, cells), parseContext, errorGuard) );
} }
BOOST_AUTO_TEST_CASE(testwsegvalv) { BOOST_AUTO_TEST_CASE(testwsegvalv)
auto dir = Opm::Connection::Direction::Z; {
const auto dir_z = Opm::Connection::Direction::Z;
const auto dir_x = Opm::Connection::Direction::X;
const auto kind = Opm::Connection::CTFKind::DeckValue; const auto kind = Opm::Connection::CTFKind::DeckValue;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10); const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 };
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) ); const auto depth = 0.0;
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) ); const auto state = Opm::Connection::State::OPEN;
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) ); 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() ); BOOST_CHECK_EQUAL( 7U , connection_set.size() );

File diff suppressed because it is too large Load Diff

View File

@ -22,192 +22,219 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <opm/input/eclipse/Python/Python.hpp> #include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp> #include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp> #include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/Runspec.hpp> #include <opm/input/eclipse/EclipseState/Runspec.hpp>
#include <opm/input/eclipse/EclipseState/Tables/TableManager.hpp> #include <opm/input/eclipse/EclipseState/Tables/TableManager.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp> #include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp> #include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp> #include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Deck/DeckItem.hpp> #include <opm/input/eclipse/Deck/DeckItem.hpp>
#include <opm/input/eclipse/Deck/DeckKeyword.hpp> #include <opm/input/eclipse/Deck/DeckKeyword.hpp>
#include <opm/input/eclipse/Deck/DeckRecord.hpp> #include <opm/input/eclipse/Deck/DeckRecord.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp> #include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp> #include <opm/input/eclipse/Parser/ParseContext.hpp>
using namespace Opm; using namespace Opm;
static Deck createDeckWithOutSolvent() { namespace {
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";
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() { Deck createDeckWithGasInjector()
Opm::Parser parser; {
std::string input = return Parser{}.parseString(R"(
"GRID\n" GRID
"PERMX\n" PERMX
" 1000*0.25/\n" 1000*0.25/
"COPY\n" COPY
" PERMX PERMY /\n" PERMX PERMY /
" PERMX PERMZ /\n" PERMX PERMZ /
"/\n" /
"SCHEDULE\n" PORO
"WELSPECS\n" 1000*0.3 /
" '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";
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() { Deck createDeckWithDynamicWSOLVENT()
Opm::Parser parser; {
std::string input = return Parser{}.parseString(R"(
"START -- 0 \n" START -- 0
"1 JAN 2000 / \n" 1 JAN 2000 /
"GRID\n" GRID
"PERMX\n" PERMX
" 1000*0.25/\n" 1000*0.25/
"COPY\n" COPY
" PERMX PERMY /\n" PERMX PERMY /
" PERMX PERMZ /\n" PERMX PERMZ /
"/\n" /
"SCHEDULE\n" PORO
"WELSPECS\n" 1000*0.3 /
" '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";
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() { Deck createDeckWithOilInjector()
Opm::Parser parser; {
std::string input = return Parser{}.parseString(R"(
"GRID\n" GRID
"PERMX\n" PERMX
" 1000*0.25/\n" 1000*0.25/
"COPY\n" COPY
" PERMX PERMY /\n" PERMX PERMY /
" PERMX PERMZ /\n" PERMX PERMZ /
"/\n" /
"SCHEDULE\n" SCHEDULE
"WELSPECS\n" WELSPECS
" 'W_1' 'OP' 2 2 1* \'OIL\' 7* / \n" 'W_1' 'OP' 2 2 1* 'OIL' 7* /
"/\n" /
"COMPDAT\n" COMPDAT
" 'W_1' 2* 1 1 'OPEN' / \n" 'W_1' 2* 1 1 'OPEN' /
"/\n" /
"WCONINJE\n" WCONINJE
" 'W_1' 'OIL' 'OPEN' 'BHP' 1 2 3/\n/\n" 'W_1' 'OIL' 'OPEN' 'BHP' 1 2 3/
"WSOLVENT\n" /
" 'W_1' 1 / \n " WSOLVENT
"/\n"; 'W_1' 1 /
/
return parser.parseString(input); END
)");
} }
static Deck createDeckWithWaterInjector() { Deck createDeckWithWaterInjector()
Opm::Parser parser; {
std::string input = return Parser{}.parseString(R"(
"GRID\n" GRID
"PERMX\n" PERMX
" 1000*0.25/\n" 1000*0.25/
"COPY\n" COPY
" PERMX PERMY /\n" PERMX PERMY /
" PERMX PERMZ /\n" PERMX PERMZ /
"/\n" /
"SCHEDULE\n" SCHEDULE
"WELSPECS\n" WELSPECS
" 'W_1' 'OP' 2 2 1* \'OIL\' 7* / \n" 'W_1' 'OP' 2 2 1* \'OIL\' 7* /
"/\n" /
"COMPDAT\n" COMPDAT
" 'W_1' 2* 1 1 'OPEN' / \n" 'W_1' 2* 1 1 'OPEN' /
"/\n" /
"WCONINJE\n" WCONINJE
" 'W_1' 'WATER' 'OPEN' 'BHP' 1 2 3/\n/\n" 'W_1' 'WATER' 'OPEN' 'BHP' 1 2 3/
"WSOLVENT\n" /
" 'W_1' 1 / \n " WSOLVENT
"/\n"; 'W_1' 1 /
/
return parser.parseString(input); END
)");
} }
BOOST_AUTO_TEST_CASE(TestNoSolvent) {
auto deck = createDeckWithOutSolvent(); } // Anonymous namespace
auto python = std::make_shared<Python>();
EclipseGrid grid(10,10,10); BOOST_AUTO_TEST_CASE(TestNoSolvent)
TableManager table ( deck ); {
FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); const auto deck = createDeckWithOutSolvent();
Runspec runspec(deck); const EclipseGrid grid(10,10,10);
Schedule schedule(deck, grid , fp, runspec, python); 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<Python>());
BOOST_CHECK(!deck.hasKeyword("WSOLVENT")); BOOST_CHECK(!deck.hasKeyword("WSOLVENT"));
} }
BOOST_AUTO_TEST_CASE(TestGasInjector) { BOOST_AUTO_TEST_CASE(TestGasInjector) {
auto deck = createDeckWithGasInjector(); const auto deck = createDeckWithGasInjector();
auto python = std::make_shared<Python>(); const EclipseGrid grid(10,10,10);
EclipseGrid grid(10,10,10); const TableManager table (deck);
TableManager table ( deck ); const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table);
FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); const Runspec runspec(deck);
Runspec runspec(deck); const Schedule schedule(deck, grid, fp, runspec, std::make_shared<Python>());
Schedule schedule(deck, grid , fp, runspec, python);
BOOST_CHECK(deck.hasKeyword("WSOLVENT")); BOOST_CHECK(deck.hasKeyword("WSOLVENT"));
} }
BOOST_AUTO_TEST_CASE(TestDynamicWSOLVENT) { BOOST_AUTO_TEST_CASE(TestDynamicWSOLVENT)
auto deck = createDeckWithDynamicWSOLVENT(); {
auto python = std::make_shared<Python>(); const auto deck = createDeckWithDynamicWSOLVENT();
EclipseGrid grid(10,10,10); const EclipseGrid grid(10,10,10);
TableManager table ( deck ); const TableManager table (deck);
FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table);
Runspec runspec(deck); const Runspec runspec(deck);
Schedule schedule(deck, grid , fp, runspec, python); const Schedule schedule(deck, grid, fp, runspec, std::make_shared<Python>());
BOOST_CHECK(deck.hasKeyword("WSOLVENT")); BOOST_CHECK(deck.hasKeyword("WSOLVENT"));
const auto& keyword = deck["WSOLVENT"].back(); const auto& keyword = deck["WSOLVENT"].back();
BOOST_CHECK_EQUAL(keyword.size(),1U); BOOST_CHECK_EQUAL(keyword.size(),1U);
const auto& record = keyword.getRecord(0); const auto& record = keyword.getRecord(0);
const std::string& well_name = record.getItem("WELL").getTrimmedString(0); const std::string& well_name = record.getItem("WELL").getTrimmedString(0);
BOOST_CHECK_EQUAL(well_name, "W_1"); 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_CHECK_EQUAL(schedule.getWell("W_1", 3).getSolventFraction(),0);
} }
BOOST_AUTO_TEST_CASE(TestOilInjector) { BOOST_AUTO_TEST_CASE(TestOilInjector)
auto deck = createDeckWithOilInjector(); {
auto python = std::make_shared<Python>(); const auto deck = createDeckWithOilInjector();
EclipseGrid grid(10,10,10); const EclipseGrid grid(10,10,10);
TableManager table ( deck ); const TableManager table (deck);
FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table);
Runspec runspec(deck); const Runspec runspec(deck);
BOOST_CHECK_THROW (Schedule(deck , grid , fp, runspec, python), std::exception);
BOOST_CHECK_THROW (Schedule(deck, grid, fp, runspec, std::make_shared<Python>()), std::exception);
} }
BOOST_AUTO_TEST_CASE(TestWaterInjector) { BOOST_AUTO_TEST_CASE(TestWaterInjector)
auto deck = createDeckWithWaterInjector(); {
auto python = std::make_shared<Python>(); const auto deck = createDeckWithWaterInjector();
EclipseGrid grid(10,10,10); const EclipseGrid grid(10,10,10);
TableManager table ( deck ); const TableManager table ( deck );
FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table);
Runspec runspec(deck); const Runspec runspec(deck);
BOOST_CHECK_THROW (Schedule(deck, grid , fp, runspec, python), std::exception);
BOOST_CHECK_THROW (Schedule(deck, grid , fp, runspec, std::make_shared<Python>()), std::exception);
} }

View File

@ -22,141 +22,166 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <opm/common/utility/OpmInputError.hpp> #include <opm/common/utility/OpmInputError.hpp>
#include <opm/input/eclipse/Python/Python.hpp> #include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp> #include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp> #include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/Runspec.hpp> #include <opm/input/eclipse/EclipseState/Runspec.hpp>
#include <opm/input/eclipse/EclipseState/Tables/TableManager.hpp> #include <opm/input/eclipse/EclipseState/Tables/TableManager.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp> #include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp> #include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTracerProperties.hpp> #include <opm/input/eclipse/Schedule/Well/WellTracerProperties.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp> #include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Deck/DeckItem.hpp> #include <opm/input/eclipse/Deck/DeckItem.hpp>
#include <opm/input/eclipse/Deck/DeckKeyword.hpp> #include <opm/input/eclipse/Deck/DeckKeyword.hpp>
#include <opm/input/eclipse/Deck/DeckRecord.hpp> #include <opm/input/eclipse/Deck/DeckRecord.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp> #include <opm/input/eclipse/Parser/Parser.hpp>
using namespace Opm; using namespace Opm;
static Deck createDeckWithOutTracer() { namespace {
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";
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() { SCHEDULE
Opm::Parser parser; WELSPECS
std::string input = 'W_1' 'OP' 1 1 1* 'GAS' 7* /
"START -- 0 \n" /
"1 JAN 2000 / \n" COMPDAT
"GRID\n" 'W_1' 2* 1 1 'OPEN' /
"PERMX\n" /
" 1000*0.25/\n" WCONINJE
"COPY\n" 'W_1' 'GAS' 'OPEN' 'BHP' 1 2 3/
" PERMX PERMY /\n" /
" PERMX PERMZ /\n" DATES -- 1
"/\n" 1 MAY 2000 /
"SCHEDULE\n" /
"WELSPECS\n" WTRACER
" 'W_1' 'OP' 1 1 1* \'GAS\' 7* / \n" 'W_1' 'I1' 1 /
"/\n" 'W_1' 'I2' 1 /
"COMPDAT\n" /
" 'W_1' 2* 1 1 'OPEN' / \n" DATES -- 2, 3
"/\n" 1 JUL 2000 /
"WCONINJE\n" 1 AUG 2000 /
" 'W_1' 'GAS' 'OPEN' 'BHP' 1 2 3/\n/\n" /
"DATES -- 1\n" WTRACER
" 1 MAY 2000 / \n" 'W_1' 'I1' 0 /
"/\n" /
"WTRACER\n" DATES -- 4
" 'W_1' 'I1' 1 / \n " 1 SEP 2000 /
" '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";
return parser.parseString(input); END
)");
} }
static Deck createDeckWithTracerInProducer() { Deck createDeckWithTracerInProducer()
Opm::Parser parser; {
std::string input = return Parser{}.parseString(R"(
"START -- 0 \n" START -- 0
"1 JAN 2000 / \n" 1 JAN 2000 /
"GRID\n" GRID
"PERMX\n" PERMX
" 1000*0.25/\n" 1000*0.25/
"COPY\n" COPY
" PERMX PERMY /\n" PERMX PERMY /
" PERMX PERMZ /\n" PERMX PERMZ /
"/\n" /
"SCHEDULE\n" PORO
"WELSPECS\n" 1000*0.3 /
" '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";
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<Python>());
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<Python>();
Runspec runspec ( deck );
Schedule schedule(deck, grid , fp, runspec, python);
BOOST_CHECK(!deck.hasKeyword("WTRACER")); BOOST_CHECK(!deck.hasKeyword("WTRACER"));
} }
BOOST_AUTO_TEST_CASE(TestDynamicWTRACER) { BOOST_AUTO_TEST_CASE(TestDynamicWTRACER)
auto deck = createDeckWithDynamicWTRACER(); {
auto python = std::make_shared<Python>(); const auto deck = createDeckWithDynamicWTRACER();
EclipseGrid grid(10,10,10);
TableManager table ( deck ); const EclipseGrid grid(10,10,10);
FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); const TableManager table (deck);
Runspec runspec ( deck ); const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table);
Schedule schedule(deck, grid , fp, runspec, python); const Runspec runspec (deck);
const Schedule schedule(deck, grid, fp, runspec, std::make_shared<Python>());
BOOST_CHECK(deck.hasKeyword("WTRACER")); BOOST_CHECK(deck.hasKeyword("WTRACER"));
const auto& keyword = deck["WTRACER"].back(); const auto& keyword = deck["WTRACER"].back();
BOOST_CHECK_EQUAL(keyword.size(),1U); BOOST_CHECK_EQUAL(keyword.size(),1U);
const auto& record = keyword.getRecord(0); const auto& record = keyword.getRecord(0);
const std::string& well_name = record.getItem("WELL").getTrimmedString(0); const std::string& well_name = record.getItem("WELL").getTrimmedString(0);
BOOST_CHECK_EQUAL(well_name, "W_1"); BOOST_CHECK_EQUAL(well_name, "W_1");
@ -169,13 +194,13 @@ BOOST_AUTO_TEST_CASE(TestDynamicWTRACER) {
} }
BOOST_AUTO_TEST_CASE(TestTracerInProducerTHROW) { BOOST_AUTO_TEST_CASE(TestTracerInProducerTHROW)
auto deck = createDeckWithTracerInProducer(); {
auto python = std::make_shared<Python>(); const auto deck = createDeckWithTracerInProducer();
EclipseGrid grid(10,10,10); const EclipseGrid grid(10,10,10);
TableManager table ( deck ); const TableManager table (deck);
FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); const FieldPropsManager fp(deck, Phases{true, true, true}, grid, table);
Runspec runspec ( deck ); 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<Python>()), OpmInputError);
} }

View File

@ -14,6 +14,8 @@ COPY
PERMX PERMZ / PERMX PERMZ /
/ /
PORO
1000*0.3 /
DX DX
1000*1 / 1000*1 /

View File

@ -16,6 +16,9 @@ COPY
PERMX PERMZ / PERMX PERMZ /
/ /
PORO
48000*0.3 /
SCHEDULE SCHEDULE
WELSPECS WELSPECS

View File

@ -17,6 +17,9 @@ COPY
PERMX PERMZ / PERMX PERMZ /
/ /
PORO
27000*0.3 /
SCHEDULE SCHEDULE

View File

@ -15,6 +15,9 @@ COPY
PERMX PERMZ / PERMX PERMZ /
/ /
PORO
27000*0.3 /
SCHEDULE SCHEDULE

View File

@ -17,6 +17,8 @@ COPY
PERMX PERMZ / PERMX PERMZ /
/ /
PORO
800*0.3 /
SCHEDULE SCHEDULE

View File

@ -16,6 +16,8 @@ COPY
PERMX PERMZ / PERMX PERMZ /
/ /
PORO
27000*0.3 /
SCHEDULE SCHEDULE

View File

@ -13,6 +13,9 @@ COPY
PERMX PERMZ / PERMX PERMZ /
/ /
PORO
72000*0.3 /
SCHEDULE SCHEDULE
-- 0 -- 0

View File

@ -518,34 +518,39 @@ BOOST_AUTO_TEST_CASE(WellTestWGRUPCONWellPropertiesSet) {
} }
BOOST_AUTO_TEST_CASE(TestDefaultedCOMPDATIJ) { BOOST_AUTO_TEST_CASE(TestDefaultedCOMPDATIJ)
Parser parser; {
const char * deckString = "\n\ const auto deck = Parser{}.parseString(R"(
START\n\ START
\n\
10 MAI 2007 /\n\ 10 MAI 2007 /
\n\
GRID\n\ GRID
PERMX\n\ PERMX
9000*0.25 /\n\ 9000*0.25 /
COPY \n\ COPY
PERMX PERMY /\n\ PERMX PERMY /
PERMX PERMZ /\n\ PERMX PERMZ /
/\n\ /
SCHEDULE\n\ PORO
WELSPECS \n\ 9000*0.3 /
'W1' 'OP' 11 21 3.33 'OIL' 7* / \n\
/\n\ SCHEDULE
COMPDAT \n\ WELSPECS
'W1' 2* 1 1 'OPEN' 1* 32.948 0.311 3047.839 2* 'X' 22.100 /\n\ 'W1' 'OP' 11 21 3.33 'OIL' 7* /
/\n"; /
auto deck = parser.parseString(deckString); COMPDAT
auto python = std::make_shared<Python>(); 'W1' 2* 1 1 'OPEN' 1* 32.948 0.311 3047.839 2* 'X' 22.100 /
EclipseGrid grid(30,30,10); /
TableManager table ( deck ); END
FieldPropsManager fp(deck, Phases{true, true, true}, grid, table); )");
Runspec runspec (deck);
Schedule sched(deck, grid , fp, runspec, python); 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<Python>());
const auto& connections = sched.getWell("W1", 0).getConnections(); const auto& connections = sched.getWell("W1", 0).getConnections();
BOOST_CHECK_EQUAL( 10 , connections.get(0).getI() ); BOOST_CHECK_EQUAL( 10 , connections.get(0).getI() );
BOOST_CHECK_EQUAL( 20 , connections.get(0).getJ() ); 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[0].wellgroup_events().hasEvent( "W_1", ScheduleEvents::COMPLETION_CHANGE));
BOOST_CHECK( sched[5].wellgroup_events().hasEvent( "W_1", ScheduleEvents::COMPLETION_CHANGE)); BOOST_CHECK( sched[5].wellgroup_events().hasEvent( "W_1", ScheduleEvents::COMPLETION_CHANGE));
} }

View File

@ -25,6 +25,7 @@
#include <opm/output/eclipse/AggregateMSWData.hpp> #include <opm/output/eclipse/AggregateMSWData.hpp>
#include <opm/output/eclipse/AggregateWellData.hpp> #include <opm/output/eclipse/AggregateWellData.hpp>
#include <opm/output/eclipse/VectorItems/connection.hpp> #include <opm/output/eclipse/VectorItems/connection.hpp>
#include <opm/output/eclipse/VectorItems/intehead.hpp> #include <opm/output/eclipse/VectorItems/intehead.hpp>
#include <opm/output/eclipse/VectorItems/well.hpp> #include <opm/output/eclipse/VectorItems/well.hpp>
@ -34,70 +35,100 @@
#include <opm/output/data/Wells.hpp> #include <opm/output/data/Wells.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp> #include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp> #include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp> #include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp> #include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp> #include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include <opm/common/utility/TimeService.hpp> #include <opm/common/utility/TimeService.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <cmath>
#include <cstddef> #include <cstddef>
#include <exception> #include <exception>
#include <memory>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <tuple>
#include <utility> #include <utility>
#include <vector> #include <vector>
struct MockIH namespace {
{ struct MockIH
MockIH(const int numWells, {
MockIH(const int numWells,
const int nsegWell = 1, // E100
const int ncwMax = 20,
const int iConnPerConn = 25, // NICONZ
const int sConnPerConn = 41, // NSCONZ
const int xConnPerConn = 58); // NXCONZ
const int nsegWell = 1, // E100 std::vector<int> value;
const int ncwMax = 20,
const int iConnPerConn = 25, // NICONZ
const int sConnPerConn = 41, // NSCONZ
const int xConnPerConn = 58); // NXCONZ
using Sz = std::vector<int>::size_type;
std::vector<int> value; Sz nwells;
Sz nsegwl;
Sz nsegmx;
Sz nswlmx;
Sz ncwmax;
Sz niconz;
Sz nsconz;
Sz nxconz;
};
using Sz = std::vector<int>::size_type; MockIH::MockIH(const int numWells,
const int nsegWell,
const int ncwMax,
const int iConnPerConn,
const int sConnPerConn,
const int xConnPerConn)
: value(411, 0)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::intehead;
Sz nwells; this->nwells = this->value[Ix::NWELLS] = numWells;
Sz nsegwl;
Sz nsegmx;
Sz nswlmx;
Sz ncwmax;
Sz niconz;
Sz nsconz;
Sz nxconz;
};
MockIH::MockIH(const int numWells, this->nsegwl = this->value[Ix::NSEGWL] = nsegWell;
const int nsegWell, this->ncwmax = this->value[Ix::NCWMAX] = ncwMax;
const int /* ncwMax */, this->nswlmx = this->value[Ix::NSWLMX] = nsegWell;
const int iConnPerConn, this->nsegmx = this->value[Ix::NSEGMX] = 32;
const int sConnPerConn, this->niconz = this->value[Ix::NICONZ] = iConnPerConn;
const int xConnPerConn) this->nsconz = this->value[Ix::NSCONZ] = sConnPerConn;
: value(411, 0) this->nxconz = this->value[Ix::NXCONZ] = xConnPerConn;
{ }
using Ix = ::Opm::RestartIO::Helpers::VectorItems::intehead;
this->nwells = this->value[Ix::NWELLS] = numWells; struct SimulationCase
{
explicit SimulationCase(const Opm::Deck& deck)
: es (deck)
, grid (deck)
, sched(deck, es, std::make_shared<Opm::Python>())
{}
this->nsegwl = this->value[Ix::NSEGWL] = nsegWell; // Order requirement: 'es' must be declared/initialised before 'sched'.
this->ncwmax = this->value[Ix::NCWMAX] = 20; Opm::EclipseState es;
this->nswlmx = this->value[Ix::NSWLMX] = 1; Opm::EclipseGrid grid;
this->nsegmx = this->value[Ix::NSEGMX] = 32; Opm::Schedule sched;
this->niconz = this->value[Ix::NICONZ] = iConnPerConn; };
this->nsconz = this->value[Ix::NSCONZ] = sConnPerConn;
this->nxconz = this->value[Ix::NXCONZ] = xConnPerConn; } // Anonymous namespace
}
// =====================================================================
BOOST_AUTO_TEST_SUITE(Aggregate_ConnData)
namespace { namespace {
Opm::Deck first_sim() Opm::Deck first_sim()
{ {
// Mostly copy of tests/FIRST_SIM.DATA // Mostly copy of tests/FIRST_SIM.DATA
@ -474,7 +505,8 @@ END
return Opm::Parser{}.parseString(input); return Opm::Parser{}.parseString(input);
} }
std::pair<Opm::data::Wells, Opm::SummaryState> wr(const Opm::Schedule& sched) std::pair<Opm::data::Wells, Opm::SummaryState>
wr(const Opm::Schedule& sched)
{ {
using o = ::Opm::data::Rates::opt; using o = ::Opm::data::Rates::opt;
@ -502,6 +534,7 @@ END
c.pressure = 215.0; c.pressure = 215.0;
c.index = connections[i].global_index(); c.index = connections[i].global_index();
c.trans_factor = connections[i].CF(); c.trans_factor = connections[i].CF();
c.compact_mult = 0.875;
const auto& global_index = connections[i].global_index(); const auto& global_index = connections[i].global_index();
sum_state.update_conn_var("PROD", "CWPR", global_index + 1, qw * (i + 1)); sum_state.update_conn_var("PROD", "CWPR", global_index + 1, qw * (i + 1));
@ -543,6 +576,7 @@ END
c.pressure = 218.0; c.pressure = 218.0;
c.index = connections[i].global_index(); c.index = connections[i].global_index();
c.trans_factor = connections[i].CF(); c.trans_factor = connections[i].CF();
c.compact_mult = 0.987;
const auto& global_index = connections[i].global_index(); const auto& global_index = connections[i].global_index();
sum_state.update_conn_var("WINJ", "CWIR", global_index+ 1, qw*(i + 1)); sum_state.update_conn_var("WINJ", "CWIR", global_index+ 1, qw*(i + 1));
@ -562,24 +596,8 @@ END
return { std::move(xw), std::move(sum_state) }; return { std::move(xw), std::move(sum_state) };
} }
} // namespace
struct SimulationCase { } // Anonymous namespace
explicit SimulationCase(const Opm::Deck& deck)
: es (deck)
, grid (deck)
, sched(deck, es, std::make_shared<Opm::Python>())
{}
// Order requirement: 'es' must be declared/initialised before 'sched'.
Opm::EclipseState es;
Opm::EclipseGrid grid;
Opm::Schedule sched;
};
// =====================================================================
BOOST_AUTO_TEST_SUITE(Aggregate_ConnData)
// test dimensions of Connection data // test dimensions of Connection data
BOOST_AUTO_TEST_CASE (Constructor) BOOST_AUTO_TEST_CASE (Constructor)
@ -683,40 +701,46 @@ BOOST_AUTO_TEST_CASE(Declared_Connection_Data)
const auto& sconn = amconn.getSConn(); const auto& sconn = amconn.getSConn();
int connNo = 1; int connNo = 1;
auto i0 = (connNo - 1) * ih.nsconz; auto i0 = (connNo - 1) * ih.nsconz;
BOOST_CHECK_CLOSE(sconn[i0 + Ix::ConnTrans], 2.55826545, 1.0e-5); // PROD - conn 1 : Transmissibility factor BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffConnTrans], 2.55826545, 1.0e-5); // PROD - conn 1 : Effective transmissibility factor
BOOST_CHECK_CLOSE(sconn[i0 + Ix::Depth], 7050., 1.0e-5); // PROD - conn 1 : Centre depth 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::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::EffectiveKH], 1581.13879, 1.0e-5); // PROD - conn 1 : effective kh-product
BOOST_CHECK_CLOSE(sconn[i0 + Ix::item12], 2.55826545, 1.0e-5); // PROD - conn 1 : Transmissibility factor BOOST_CHECK_CLOSE(sconn[i0 + Ix::CFDenom], 4.37696314, 1.0e-5);
BOOST_CHECK_CLOSE(sconn[i0 + Ix::ConnTrans], 2.55826545 / 0.875, 1.0e-5); // PROD - conn 1 : Transmissibility factor
BOOST_CHECK_CLOSE( BOOST_CHECK_CLOSE(
sconn[i0 + Ix::SegDistEnd], 130., 1.0e-5); // PROD - conn 1 : Distance to end of connection in segment sconn[i0 + Ix::SegDistEnd], 130., 1.0e-5); // PROD - conn 1 : Distance to end of connection in segment
BOOST_CHECK_CLOSE( BOOST_CHECK_CLOSE(
sconn[i0 + Ix::SegDistStart], 30., 1.0e-5); // PROD - conn 1 : Distance to start of connection in segment 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 // Well no 2 - WINJ well
connNo = 3; connNo = 3;
i0 = ih.ncwmax * ih.nsconz + (connNo - 1) * ih.nsconz; i0 = ih.ncwmax * ih.nsconz + (connNo - 1) * ih.nsconz;
BOOST_CHECK_CLOSE(sconn[i0 + Ix::ConnTrans], 2.55826545, 1.0e-5); // WINJ - conn 3 : Transmissibility factor BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffConnTrans], 2.55826545, 1.0e-5); // WINJ - conn 3 : Effective transmissibility factor
BOOST_CHECK_CLOSE(sconn[i0 + Ix::Depth], 7250., 1.0e-5); // WINJ - conn 3 : Centre depth 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::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::EffectiveKH], 1581.13879, 1.0e-5); // WINJ - conn 3 : effective kh-product
BOOST_CHECK_CLOSE(sconn[i0 + Ix::item12], 2.55826545, 1.0e-5); // WINJ - conn 3 : Transmissibility factor BOOST_CHECK_CLOSE(sconn[i0 + Ix::CFDenom], 4.37696314, 1.0e-5);
BOOST_CHECK_CLOSE(sconn[i0 + Ix::ConnTrans], 2.55826545 / 0.987, 1.0e-5); // WINJ - conn 3 : Transmissibility factor
BOOST_CHECK_CLOSE( BOOST_CHECK_CLOSE(
sconn[i0 + Ix::SegDistEnd], 0., 1.0e-5); // WINJ - conn 3 : Distance to end of connection in segment sconn[i0 + Ix::SegDistEnd], 0., 1.0e-5); // WINJ - conn 3 : Distance to end of connection in segment
BOOST_CHECK_CLOSE( BOOST_CHECK_CLOSE(
sconn[i0 + Ix::SegDistStart], 0., 1.0e-5); // WINJ - conn 3 : Distance to start of connection in segment 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; connNo = 4;
i0 = ih.ncwmax * ih.nsconz + (connNo - 1) * ih.nsconz; i0 = ih.ncwmax * ih.nsconz + (connNo - 1) * ih.nsconz;
BOOST_CHECK_CLOSE(sconn[i0 + Ix::ConnTrans], 2.55826545, 1.0e-5); // WINJ - conn 4 : Transmissibility factor BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffConnTrans], 2.55826545, 1.0e-5); // WINJ - conn 4 : Effective transmissibility factor
BOOST_CHECK_CLOSE(sconn[i0 + Ix::Depth], 7250., 1.0e-5); // WINJ - conn 4 : Centre depth 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::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::EffectiveKH], 1581.13879, 1.0e-5); // WINJ - conn 4 : effective kh-product
BOOST_CHECK_CLOSE(sconn[i0 + Ix::item12], 2.55826545, 1.0e-5); // WINJ - conn 4 : Transmissibility factor BOOST_CHECK_CLOSE(sconn[i0 + Ix::CFDenom], 4.37696314, 1.0e-5);
BOOST_CHECK_CLOSE(sconn[i0 + Ix::ConnTrans], 2.55826545 / 0.987, 1.0e-5); // WINJ - conn 4 : Transmissibility factor
BOOST_CHECK_CLOSE( BOOST_CHECK_CLOSE(
sconn[i0 + Ix::SegDistEnd], 0., 1.0e-5); // WINJ - conn 4 : Distance to end of connection in segment sconn[i0 + Ix::SegDistEnd], 0., 1.0e-5); // WINJ - conn 4 : Distance to end of connection in segment
BOOST_CHECK_CLOSE( BOOST_CHECK_CLOSE(
sconn[i0 + Ix::SegDistStart], 0., 1.0e-5); // WINJ - conn 4 : Distance to start of connection in segment 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) // XCONN (PROD) + (WINJ)
@ -808,10 +832,12 @@ BOOST_AUTO_TEST_CASE(Declared_Connection_Data)
} }
} }
BOOST_AUTO_TEST_CASE(InactiveCell) { BOOST_AUTO_TEST_CASE(InactiveCell)
{
auto simCase = SimulationCase{first_sim()}; auto simCase = SimulationCase{first_sim()};
const auto rptStep = std::size_t{1}; const auto rptStep = std::size_t{1};
const auto ih = MockIH {static_cast<int>(simCase.sched.getWells(rptStep).size())}; const auto ih = MockIH {static_cast<int>(simCase.sched.getWells(rptStep).size())};
const auto& [wrc, sum_state] = wr(simCase.sched); const auto& [wrc, sum_state] = wr(simCase.sched);
auto conn0 = Opm::RestartIO::Helpers::AggregateConnectionData{ih.value}; auto conn0 = Opm::RestartIO::Helpers::AggregateConnectionData{ih.value};
conn0.captureDeclaredConnData(simCase.sched, conn0.captureDeclaredConnData(simCase.sched,
@ -819,11 +845,10 @@ BOOST_AUTO_TEST_CASE(InactiveCell) {
simCase.es.getUnits(), simCase.es.getUnits(),
wrc, wrc,
sum_state, sum_state,
rptStep rptStep);
);
std::vector<int> actnum(500, 1);
// Here we deactive the cell holding connection number 2. // Here we deactive the cell holding connection number 2.
std::vector<int> actnum(500, 1);
actnum[simCase.grid.getGlobalIndex(2,4,1)] = 0; actnum[simCase.grid.getGlobalIndex(2,4,1)] = 0;
simCase.grid.resetACTNUM(actnum); simCase.grid.resetACTNUM(actnum);
@ -833,62 +858,330 @@ BOOST_AUTO_TEST_CASE(InactiveCell) {
simCase.es.getUnits(), simCase.es.getUnits(),
wrc, wrc,
sum_state, sum_state,
rptStep rptStep);
);
const std::size_t num_test_connections = 4; const std::size_t num_test_connections = 4;
{ using IC = ::Opm::RestartIO::Helpers::VectorItems::IConn::index;
using IC = ::Opm::RestartIO::Helpers::VectorItems::IConn::index;
const auto iconn0 = conn0.getIConn();
const auto iconn1 = conn1.getIConn();
for (std::size_t conn_index = 0; conn_index < num_test_connections; conn_index++) {
std::size_t offset1 = conn_index * ih.niconz;
std::size_t offset0 = offset1;
if (conn_index >= 2) const auto iconn0 = conn0.getIConn();
offset0 += ih.niconz; const auto iconn1 = conn1.getIConn();
for (std::size_t elm_index = 0; elm_index < ih.niconz; elm_index++) { for (std::size_t conn_index = 0; conn_index < num_test_connections; ++conn_index) {
if (elm_index == IC::SeqIndex && conn_index >= 2) { const std::size_t offset1 = conn_index * ih.niconz;
// Comparing the connection ID - which should be different; const std::size_t offset0 = offset1 + (conn_index >= 2)*ih.niconz;
BOOST_CHECK_EQUAL(iconn1[offset1 + elm_index] + 1 , iconn0[offset0 + elm_index]);
} else for (std::size_t elm_index = 0; elm_index < ih.niconz; ++elm_index) {
BOOST_CHECK_EQUAL(iconn1[offset1 + elm_index], iconn0[offset0 + elm_index]); if (elm_index == IC::SeqIndex && conn_index >= 2) {
// Comparing the connection ID - which should be different;
BOOST_CHECK_EQUAL(iconn1[offset1 + elm_index] + 1,
iconn0[offset0 + elm_index]);
}
else {
BOOST_CHECK_EQUAL(iconn1[offset1 + elm_index],
iconn0[offset0 + elm_index]);
} }
} }
} }
{ {
const auto sconn0 = conn0.getSConn(); const auto sconn0 = conn0.getSConn();
const auto sconn1 = conn1.getSConn(); const auto sconn1 = conn1.getSConn();
for (std::size_t conn_index = 0; conn_index < num_test_connections; conn_index++) {
std::size_t offset1 = conn_index * ih.nsconz;
std::size_t offset0 = offset1;
if (conn_index >= 2) for (std::size_t conn_index = 0; conn_index < num_test_connections; ++conn_index) {
offset0 += ih.nsconz; const std::size_t offset1 = conn_index * ih.nsconz;
const std::size_t offset0 = offset1 + (conn_index >= 2)*ih.nsconz;
for (std::size_t elm_index = 0; elm_index < ih.nsconz; elm_index++) for (std::size_t elm_index = 0; elm_index < ih.nsconz; ++elm_index) {
BOOST_CHECK_EQUAL(sconn1[offset1 + elm_index], sconn0[offset0 + elm_index]); BOOST_CHECK_EQUAL(sconn1[offset1 + elm_index],
sconn0[offset0 + elm_index]);
}
} }
} }
{ {
const auto xconn0 = conn0.getXConn(); const auto xconn0 = conn0.getXConn();
const auto xconn1 = conn1.getXConn(); const auto xconn1 = conn1.getXConn();
for (std::size_t conn_index = 0; conn_index < num_test_connections; conn_index++) {
for (std::size_t conn_index = 0; conn_index < num_test_connections; ++conn_index) {
std::size_t offset1 = conn_index * ih.nxconz; std::size_t offset1 = conn_index * ih.nxconz;
std::size_t offset0 = offset1; std::size_t offset0 = offset1;
if (conn_index >= 2) if (conn_index >= 2) {
offset0 += ih.nxconz; offset0 += ih.nxconz;
}
for (std::size_t elm_index = 0; elm_index < ih.nxconz; elm_index++) for (std::size_t elm_index = 0; elm_index < ih.nxconz; ++elm_index) {
BOOST_CHECK_EQUAL(xconn1[offset1 + elm_index], xconn0[offset0 + elm_index]); BOOST_CHECK_EQUAL(xconn1[offset1 + elm_index],
xconn0[offset0 + elm_index]);
}
} }
} }
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() // Aggregate_ConnData
// ---------------------------------------------------------------------------
BOOST_AUTO_TEST_SUITE(DFactor_Correlation)
namespace {
SimulationCase wdfaccorCase()
{
return SimulationCase { Opm::Parser{}.parseString(R"(
DIMENS
10 10 10 /
START -- 0
19 JUN 2007 /
GRID
DXV
10*100.0 /
DYV
10*100.0 /
DZV
10*10.0 /
DEPTHZ
121*2000.0 /
PORO
1000*0.1 /
PERMX
1000*1 /
PERMY
1000*0.1 /
PERMZ
1000*0.01 /
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' 3 3 1 1 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
'W2' 3 3 2 2 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
/
WDFACCOR
'W2' 1.984e-7 -1.1045 0.0 /
/
WCONINJE
'W1' 'WATER' 'OPEN' 'RATE' 4000.0 1* 850.0 /
/
WCONPROD
'W2' 'OPEN' 'ORAT' 5000.0 4* 20.0 /
/
DATES -- 2
12 NOV 2008 /
/
END
)") };
}
std::pair<Opm::data::Wells, Opm::SummaryState>
dynamicState(const Opm::Schedule& sched)
{
auto dyn_state = std::pair<Opm::data::Wells, Opm::SummaryState> {
std::piecewise_construct,
std::forward_as_tuple(),
std::forward_as_tuple(Opm::TimeService::now())
};
using o = ::Opm::data::Rates::opt;
auto& [xw, sum_state] = dyn_state;
const double qv = 12.34*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day;
{
const auto wname = std::string { "W2" };
xw[wname].rates.set(o::wat, 1.0).set(o::oil, 2.0).set(o::gas, 3.0);
xw[wname].bhp = 213.0*Opm::unit::barsa;
{
const double qw = 4.0*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day;
const double qo = 5.0*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day;
const double qg = 50.0*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day;
const auto& well = sched.getWell(wname, 1);
const auto& connections = well.getConnections();
for (auto i = 0*connections.size(); i < connections.size(); ++i) {
auto& c = xw[wname].connections.emplace_back();
c.rates
.set(o::wat, qw * (float(i) + 1.0)*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day)
.set(o::oil, qo * (float(i) + 1.0)*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day)
.set(o::gas, qg * (float(i) + 1.0)*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day);
c.pressure = 215.0*Opm::unit::barsa;
c.index = connections[i].global_index();
c.trans_factor = connections[i].CF();
c.cell_pressure = 200.0*Opm::unit::barsa;
c.cell_saturation_water = 0.55;
c.cell_saturation_gas =.038;
c.effective_Kh = 100*Opm::prefix::milli*Opm::unit::darcy*Opm::unit::meter;
c.d_factor = 3.0e-6*Opm::unit::day/Opm::unit::cubic(Opm::unit::meter);
c.compact_mult = 0.875;
const auto& global_index = connections[i].global_index();
sum_state.update_conn_var(wname, "CWPR", global_index + 1, qw * (i + 1));
sum_state.update_conn_var(wname, "COPR", global_index + 1, qo * (i + 1));
sum_state.update_conn_var(wname, "CGPR", global_index + 1, qg * (i + 1));
sum_state.update_conn_var(wname, "CVPR", global_index + 1, qv * (i + 1));
sum_state.update_conn_var(wname, "COPT", global_index + 1, qo * (i + 1) * 2.0);
sum_state.update_conn_var(wname, "CWPT", global_index + 1, qw * (i + 1) * 2.0);
sum_state.update_conn_var(wname, "CGPT", global_index + 1, qg * (i + 1) * 2.0);
sum_state.update_conn_var(wname, "CVPT", global_index + 1, qv * (i + 1) * 2.0);
sum_state.update_conn_var(wname, "CGOR", global_index + 1, qg / qo);
sum_state.update_conn_var(wname, "CPR", global_index + 1, 215.0);
}
}
}
{
const auto wname = std::string { "W1" };
const auto& well = sched.getWell(wname, 1);
const auto& connections = well.getConnections();
xw[wname].bhp = 234.0*Opm::unit::barsa;
xw[wname].rates
.set(o::wat, 5.0*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day)
.set(o::oil, 0.0*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day)
.set(o::gas, 0.0*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day);
const double qw = 7.0;
for (auto i = 0*connections.size(); i < connections.size(); ++i) {
xw[wname].connections.emplace_back();
auto& c = xw[wname].connections.back();
c.rates.set(o::wat, qw * (float(i) + 1.0)).set(o::oil, 0.0).set(o::gas, 0.0);
c.pressure = 218.0*Opm::unit::barsa;
c.index = connections[i].global_index();
c.trans_factor = connections[i].CF();
c.cell_pressure = 230.0*Opm::unit::barsa;
c.cell_saturation_water = 0.15;
c.cell_saturation_gas = 0.50;
c.effective_Kh = 100*Opm::prefix::milli*Opm::unit::darcy*Opm::unit::meter;
c.d_factor = 3.0e-6*Opm::unit::day/Opm::unit::cubic(Opm::unit::meter);
c.compact_mult = 0.987;
const auto& global_index = connections[i].global_index();
sum_state.update_conn_var(wname, "CWIR", global_index+ 1, qw*(i + 1));
sum_state.update_conn_var(wname, "COIR", global_index+ 1, 0.0);
sum_state.update_conn_var(wname, "CGIR", global_index+ 1, 0.0);
sum_state.update_conn_var(wname, "CVIR", global_index+ 1, qv*(i + 1));
sum_state.update_conn_var(wname, "COIT", global_index + 1, 543.21 * (i + 1));
sum_state.update_conn_var(wname, "CWIT", global_index + 1, qw * (i + 1) * 2.0);
sum_state.update_conn_var(wname, "CGIT", global_index + 1, 9876.54 * (i + 1));
sum_state.update_conn_var(wname, "CVIT", global_index + 1, qv * (i + 1) * 2.0);
sum_state.update_conn_var(wname, "CPR", global_index + 1, 218.0);
}
}
return dyn_state;
}
} // Anonymous namespace
BOOST_AUTO_TEST_CASE(Basic)
{
const auto cse = wdfaccorCase();
const auto ih = MockIH { 2, 0, 1 };
const auto& [wrc, sum_state] = dynamicState(cse.sched);
auto amconn = Opm::RestartIO::Helpers::AggregateConnectionData {ih.value};
{
const auto rptStep = std::size_t {0};
amconn.captureDeclaredConnData(cse.sched,
cse.grid,
cse.es.getUnits(),
wrc, sum_state, rptStep);
// W1 (injector)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::SConn::index;
const auto& sconn = amconn.getSConn();
const auto wellNo = 1;
const auto connNo = 1;
const auto wellStart = (wellNo - 1) * ih.ncwmax * ih.nsconz;
const auto start = wellStart + (connNo - 1) * ih.nsconz;
BOOST_CHECK_CLOSE(sconn[start + Ix::StaticDFacCorrCoeff], 0.0, 1.0e-7);
}
// W2 (producer)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::SConn::index;
const auto& sconn = amconn.getSConn();
const auto wellNo = 2;
const auto connNo = 1;
const auto wellStart = (wellNo - 1) * ih.ncwmax * ih.nsconz;
const auto start = wellStart + (connNo - 1)*ih.nsconz;
BOOST_CHECK_CLOSE(sconn[start + Ix::StaticDFacCorrCoeff], 0.0, 1.0e-7);
}
}
{
const auto rptStep = std::size_t {1};
amconn.captureDeclaredConnData(cse.sched,
cse.grid,
cse.es.getUnits(),
wrc, sum_state, rptStep);
// W1 (injector)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::SConn::index;
const auto& sconn = amconn.getSConn();
const auto wellNo = 1;
const auto connNo = 1;
const auto wellStart = (wellNo - 1) * ih.ncwmax * ih.nsconz;
const auto start = wellStart + (connNo - 1) * ih.nsconz;
BOOST_CHECK_CLOSE(sconn[start + Ix::StaticDFacCorrCoeff], 0.0, 1.0e-7);
}
// W2 (producer)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::SConn::index;
const auto& sconn = amconn.getSConn();
const auto wellNo = 2;
const auto connNo = 1;
const auto wellStart = (wellNo - 1) * ih.ncwmax * ih.nsconz;
const auto start = wellStart + (connNo - 1)*ih.nsconz;
BOOST_CHECK_CLOSE(sconn[start + Ix::StaticDFacCorrCoeff], 1.89919365e-11, 1.0e-7);
}
}
}
BOOST_AUTO_TEST_SUITE_END() // DFactor_Correlation

View File

@ -51,6 +51,8 @@
#include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp> #include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestState.hpp> #include <opm/input/eclipse/Schedule/Well/WellTestState.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp> #include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp> #include <opm/input/eclipse/Deck/Deck.hpp>
@ -64,42 +66,65 @@
#include "tests/WorkArea.hpp" #include "tests/WorkArea.hpp"
struct MockIH namespace {
{
MockIH(const int numWells,
const int iwelPerWell = 155, // E100
const int swelPerWell = 122, // E100
const int xwelPerWell = 130, // E100
const int zwelPerWell = 3); // E100
std::vector<int> value; struct MockIH
{
MockIH(const int numWells,
const int iwelPerWell = 155, // E100
const int swelPerWell = 122, // E100
const int xwelPerWell = 130, // E100
const int zwelPerWell = 3); // E100
using Sz = std::vector<int>::size_type; std::vector<int> value;
Sz nwells; using Sz = std::vector<int>::size_type;
Sz niwelz;
Sz nswelz;
Sz nxwelz;
Sz nzwelz;
};
MockIH::MockIH(const int numWells, Sz nwells;
const int iwelPerWell, Sz niwelz;
const int swelPerWell, Sz nswelz;
const int xwelPerWell, Sz nxwelz;
const int zwelPerWell) Sz nzwelz;
: value(411, 0) };
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::intehead;
this->nwells = this->value[Ix::NWELLS] = numWells; MockIH::MockIH(const int numWells,
this->niwelz = this->value[Ix::NIWELZ] = iwelPerWell; const int iwelPerWell,
this->nswelz = this->value[Ix::NSWELZ] = swelPerWell; const int swelPerWell,
this->nxwelz = this->value[Ix::NXWELZ] = xwelPerWell; const int xwelPerWell,
this->nzwelz = this->value[Ix::NZWELZ] = zwelPerWell; const int zwelPerWell)
} : value(411, 0)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::intehead;
this->nwells = this->value[Ix::NWELLS] = numWells;
this->niwelz = this->value[Ix::NIWELZ] = iwelPerWell;
this->nswelz = this->value[Ix::NSWELZ] = swelPerWell;
this->nxwelz = this->value[Ix::NXWELZ] = xwelPerWell;
this->nzwelz = this->value[Ix::NZWELZ] = zwelPerWell;
}
struct SimulationCase
{
explicit SimulationCase(const Opm::Deck& deck)
: es { deck }
, grid { deck }
, sched{ deck, es, std::make_shared<Opm::Python>() }
{}
// Order requirement: 'es' must be declared/initialised before 'sched'.
Opm::EclipseState es;
Opm::EclipseGrid grid;
Opm::Schedule sched;
};
} // Anonymous namespace
// =====================================================================
BOOST_AUTO_TEST_SUITE(Aggregate_WD)
namespace { namespace {
Opm::Deck first_sim() Opm::Deck first_sim()
{ {
// Mostly copy of tests/FIRST_SIM.DATA // Mostly copy of tests/FIRST_SIM.DATA
@ -817,25 +842,8 @@ TSTEP -- 3
return xw; return xw;
} }
} // namespace
struct SimulationCase } // Anonymous namespace
{
explicit SimulationCase(const Opm::Deck& deck)
: es { deck }
, grid { deck }
, sched{ deck, es, std::make_shared<Opm::Python>() }
{}
// Order requirement: 'es' must be declared/initialised before 'sched'.
Opm::EclipseState es;
Opm::EclipseGrid grid;
Opm::Schedule sched;
};
// =====================================================================
BOOST_AUTO_TEST_SUITE(Aggregate_WD)
BOOST_AUTO_TEST_CASE (Constructor) BOOST_AUTO_TEST_CASE (Constructor)
{ {
@ -1822,3 +1830,157 @@ BOOST_AUTO_TEST_CASE(WELL_POD)
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
// ===========================================================================
BOOST_AUTO_TEST_SUITE(Extra_Effects)
namespace {
SimulationCase wdfaccorCase()
{
return SimulationCase { Opm::Parser{}.parseString(R"~(
DIMENS
10 10 10 /
START -- 0
19 JUN 2007 /
GRID
DXV
10*100.0 /
DYV
10*100.0 /
DZV
10*10.0 /
DEPTHZ
121*2000.0 /
PORO
1000*0.1 /
PERMX
1000*1 /
PERMY
1000*0.1 /
PERMZ
1000*0.01 /
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' 3 3 1 1 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
'W2' 3 3 2 2 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
/
WDFACCOR
'W2' 1.984e-7 -1.1045 0.0 /
/
WCONINJE
'W1' 'WATER' 'OPEN' 'RATE' 4000.0 1* 850.0 /
/
WCONPROD
'W2' 'OPEN' 'ORAT' 5000.0 4* 20.0 /
/
DATES -- 2
12 NOV 2008 /
/
END
)~") };
}
std::pair<Opm::data::Wells, Opm::SummaryState>
dynamicStateWDFacCorr(const Opm::Schedule& sched)
{
auto dyn_state = std::pair<Opm::data::Wells, Opm::SummaryState> {
std::piecewise_construct,
std::forward_as_tuple(),
std::forward_as_tuple(Opm::TimeService::now())
};
using o = ::Opm::data::Rates::opt;
auto& [xw, sum_state] = dyn_state;
{
const auto wname = std::string { "W2" };
xw[wname].rates.set(o::wat, 1.0).set(o::oil, 2.0).set(o::gas, 3.0);
xw[wname].bhp = 213.0*Opm::unit::barsa;
}
{
const auto wname = std::string { "W1" };
xw[wname].bhp = 234.0*Opm::unit::barsa;
xw[wname].rates
.set(o::wat, 5.0*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day)
.set(o::oil, 0.0*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day)
.set(o::gas, 0.0*Opm::unit::cubic(Opm::unit::meter)/Opm::unit::day);
}
return dyn_state;
}
} // Anonymous namespace
BOOST_AUTO_TEST_CASE(WdFac_Correlation)
{
const auto cse = wdfaccorCase();
const auto& [xw, smry] = dynamicStateWDFacCorr(cse.sched);
const auto ih = MockIH{ 2 };
const auto action_state = Opm::Action::State{};
const auto wtest_state = Opm::WellTestState{};
const auto rptStep = std::size_t{1};
auto awd = Opm::RestartIO::Helpers::AggregateWellData{ ih.value };
awd.captureDeclaredWellData(cse.sched,
cse.es.tracer(),
rptStep,
action_state,
wtest_state,
smry,
ih.value);
// W1 (injector)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::SWell::index;
const auto i1 = 0*ih.nswelz;
const auto& swell = awd.getSWell();
BOOST_CHECK_CLOSE(swell[i1 + Ix::DFacCorrCoeffA], 0.0f, 1.0e-7f);
BOOST_CHECK_CLOSE(swell[i1 + Ix::DFacCorrExpB] , 0.0f, 1.0e-7f);
BOOST_CHECK_CLOSE(swell[i1 + Ix::DFacCorrExpC] , 0.0f, 1.0e-7f);
}
// W2 (producer)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::SWell::index;
const auto i1 = 1*ih.nswelz;
const auto& swell = awd.getSWell();
BOOST_CHECK_CLOSE(swell[i1 + Ix::DFacCorrCoeffA], 1.984e-7f, 1.0e-7f);
BOOST_CHECK_CLOSE(swell[i1 + Ix::DFacCorrExpB] , -1.1045f , 1.0e-7f);
BOOST_CHECK_CLOSE(swell[i1 + Ix::DFacCorrExpC] , 0.0f , 1.0e-7f);
}
}
BOOST_AUTO_TEST_SUITE_END() // Extra_Effects

View File

@ -57,12 +57,22 @@ namespace {
const auto j = (dims[1] - 1) - 1; const auto j = (dims[1] - 1) - 1;
for (auto k = top; k < static_cast<int>(dims.size()); ++k) { for (auto k = top; k < static_cast<int>(dims.size()); ++k) {
const auto depth = 2000 + (2*k + 1) / static_cast<double>(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, conns.emplace_back(i, j, k, globIndex({i, j, k}, dims), k,
2000 + (2*k + 1) / static_cast<double>(2),
Opm::Connection::State::OPEN, 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::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 }; return { Opm::Connection::Order::INPUT, i, j, conns };
@ -87,18 +97,28 @@ namespace {
}; };
for (auto k = topConn; k < kMax; ++k) { for (auto k = topConn; k < kMax; ++k) {
const auto depth = 2000 + (2*k + 1) / static_cast<double>(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, conns.emplace_back(i, j, k, globIndex({i, j, k}, dims), k - topConn,
2000 + (2*k + 1) / static_cast<double>(2),
// Open, Shut, Open, Open, Shut, ... // Open, Shut, Open, Open, Shut, ...
state[(k - topConn) % state.size()], 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::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 }; return { Opm::Connection::Order::INPUT, i, j, conns };
@ -115,12 +135,22 @@ namespace {
const auto iMax = std::min(dims[0] - 1, left + numConns); const auto iMax = std::min(dims[0] - 1, left + numConns);
for (auto i = left; i < iMax; ++i) { for (auto i = left; i < iMax; ++i) {
const auto depth = 2000 + (2*k + 1) / static_cast<double>(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, conns.emplace_back(i, j, k, globIndex({i, j, k}, dims), i - left,
2000 + (2*k + 1) / static_cast<double>(2),
Opm::Connection::State::OPEN, 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::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 }; return { Opm::Connection::Order::INPUT, left, j, conns };

View File

@ -732,12 +732,12 @@ BOOST_AUTO_TEST_CASE(test_RFT)
std::vector<Opm::data::Connection> well1_comps(9); std::vector<Opm::data::Connection> 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 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) { 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); well1_comps[i] = std::move(well_comp);
} }
std::vector<Opm::data::Connection> well2_comps(6); std::vector<Opm::data::Connection> well2_comps(6);
for (size_t i = 0; i < 6; ++i) { 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); well2_comps[i] = std::move(well_comp);
} }
@ -871,12 +871,12 @@ BOOST_AUTO_TEST_CASE(test_RFT2)
std::vector<Opm::data::Connection> well1_comps(9); std::vector<Opm::data::Connection> 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 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) { 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); well1_comps[i] = std::move(well_comp);
} }
std::vector<Opm::data::Connection> well2_comps(6); std::vector<Opm::data::Connection> well2_comps(6);
for (size_t i = 0; i < 6; ++i) { 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); well2_comps[i] = std::move(well_comp);
} }

View File

@ -187,15 +187,15 @@ data::Wells mkWells() {
* input deck. All other entries in the well structures are arbitrary. * 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 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( { 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, 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.rates = r2;
w2.thp = 2.0; w2.thp = 2.0;
w2.bhp = 2.34; w2.bhp = 2.34;
w2.temperature = 4.56; w2.temperature = 4.56;
w2.control = 2; 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; data::Wells wellRates;

View File

@ -193,6 +193,7 @@ TEST_FOR_TYPE_NAMED(Action::State, ActionState)
TEST_FOR_TYPE(BCConfig) TEST_FOR_TYPE(BCConfig)
TEST_FOR_TYPE(BrineDensityTable) TEST_FOR_TYPE(BrineDensityTable)
TEST_FOR_TYPE(ColumnSchema) TEST_FOR_TYPE(ColumnSchema)
TEST_FOR_TYPE_NAMED(Connection::CTFProperties, CTFProperties)
TEST_FOR_TYPE(Connection) TEST_FOR_TYPE(Connection)
TEST_FOR_TYPE_NAMED_OBJ(data::AquiferData, AquiferData_CarterTracy, serializationTestObjectC) TEST_FOR_TYPE_NAMED_OBJ(data::AquiferData, AquiferData_CarterTracy, serializationTestObjectC)
TEST_FOR_TYPE_NAMED_OBJ(data::AquiferData, AquiferData_Fetkovich, serializationTestObjectF) 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(VFPProdTable)
TEST_FOR_TYPE(ViscrefTable) TEST_FOR_TYPE(ViscrefTable)
TEST_FOR_TYPE(WatdentTable) TEST_FOR_TYPE(WatdentTable)
TEST_FOR_TYPE_NAMED(WDFAC::Correlation, Correlation)
TEST_FOR_TYPE(WDFAC)
TEST_FOR_TYPE(Well) TEST_FOR_TYPE(Well)
TEST_FOR_TYPE(Welldims) TEST_FOR_TYPE(Welldims)
TEST_FOR_TYPE(WellBrineProperties) TEST_FOR_TYPE(WellBrineProperties)
@ -335,11 +338,14 @@ TEST_FOR_TYPE(WListManager)
TEST_FOR_TYPE(WriteRestartFileEvents) TEST_FOR_TYPE(WriteRestartFileEvents)
namespace {
bool init_unit_test_func() bool init_unit_test_func()
{ {
return true; return true;
} }
} // Anonymous namespace
int main(int argc, char** argv) int main(int argc, char** argv)
{ {

View File

@ -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 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 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::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 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, 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, con_filtrate /* output should be zero since it is a producer */}; 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, w3_con_filtrate}; 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, zero_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 The completions

View File

@ -172,7 +172,7 @@ static data::Wells result_wells() {
input deck. 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::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 The completions

View File

@ -116,8 +116,8 @@ BOOST_AUTO_TEST_CASE(get_connections) {
* the completion keys (active indices) and well names correspond to the * the completion keys (active indices) and well names correspond to the
* input deck. All other entries in the well structures are arbitrary. * 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( { 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, {}} ); 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; Json::JsonObject json_data;
@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(get_connections) {
w2.temperature = 4.56; w2.temperature = 4.56;
w2.control = 2; w2.control = 2;
w2.filtrate = {0.3, 3, 0.4}; // values are not tested in this test 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; data::Wells wellRates;
wellRates["OP_1"] = w1; wellRates["OP_1"] = w1;