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

View File

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

View File

@ -21,17 +21,30 @@
#include <opm/input/eclipse/Schedule/CompletedCells.hpp>
#include <cstddef>
#include <string>
namespace Opm {
class EclipseGrid;
class FieldPropsManager;
class ScheduleGrid {
} // namespace Opm
namespace Opm {
class ScheduleGrid
{
public:
ScheduleGrid(const EclipseGrid& ecl_grid, const FieldPropsManager& fpm, CompletedCells& completed_cells);
ScheduleGrid(const EclipseGrid& ecl_grid,
const FieldPropsManager& fpm,
CompletedCells& completed_cells);
explicit ScheduleGrid(CompletedCells& completed_cells);
const CompletedCells::Cell& get_cell(std::size_t i, std::size_t j, std::size_t k) const;
const CompletedCells::Cell&
get_cell(std::size_t i, std::size_t j, std::size_t k) const;
const Opm::EclipseGrid* get_grid() const;
private:
@ -40,8 +53,6 @@ private:
CompletedCells& cells;
};
} // namespace Opm
}
#endif
#endif // SCHEDULE_GRID

View File

@ -17,91 +17,172 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMPLETION_HPP_
#define COMPLETION_HPP_
#include <opm/input/eclipse/Schedule/Well/FilterCake.hpp>
#include <opm/input/eclipse/Schedule/Well/WINJMULT.hpp>
#include <array>
#include <cstddef>
#include <map>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <optional>
#include <limits>
#include <opm/input/eclipse/Schedule/Well/FilterCake.hpp>
#include <opm/input/eclipse/Schedule/Well/WINJMULT.hpp>
namespace Opm {
namespace RestartIO {
struct RstConnection;
}
class DeckKeyword;
class DeckRecord;
class ScheduleGrid;
class FieldPropsManager;
} // namespace Opm
class Connection {
namespace Opm { namespace RestartIO {
struct RstConnection;
}} // namespace Opm::RestartIO
namespace Opm {
class Connection
{
public:
enum class State {
OPEN = 1,
SHUT = 2,
AUTO = 3 // Seems like the AUTO state can not be serialized to restart files.
AUTO = 3, // Seems like the AUTO state can not be serialized to restart files.
};
static const std::string State2String( State enumValue );
static State StateFromString( const std::string& stringValue );
static std::string State2String(State enumValue);
static State StateFromString(std::string_view stringValue);
enum class Direction{
enum class Direction {
X = 1,
Y = 2,
Z = 3
Z = 3,
};
static std::string Direction2String(const Direction enumValue);
static Direction DirectionFromString(const std::string& stringValue);
static Direction DirectionFromString(std::string_view stringValue);
enum class Order {
DEPTH,
INPUT,
TRACK
DEPTH,
INPUT,
TRACK,
};
static const std::string Order2String( Order enumValue );
static Order OrderFromString(const std::string& comporderStringValue);
static std::string Order2String(Order enumValue);
static Order OrderFromString(std::string_view comporderStringValue);
enum class CTFKind {
DeckValue,
Defaulted,
};
Connection();
Connection(int i, int j , int k ,
std::size_t global_index,
int complnum,
double depth,
State state,
double CF,
double Kh,
double rw,
double r0,
double re,
double connection_length,
double skin_factor,
double d_factor,
double Ke,
const int satTableId,
const Direction direction,
const CTFKind ctf_kind,
const std::size_t sort_value,
const bool defaultSatTabId);
Connection(const RestartIO::RstConnection& rst_connection, const ScheduleGrid& grid, const FieldPropsManager& fp);
/// Quantities that go into calculating the connection
/// transmissibility factor.
struct CTFProperties
{
/// Static connection transmissibility factor calculated from
/// input quantities.
double CF{};
/// Static 'Kh' product
double Kh{};
/// Effective permeability.
double Ke{};
/// Connection's wellbore radius
double rw{};
/// Connection's pressure equivalent radius
double r0{};
/// Connection's area equivalent radius--mostly for use by the
/// polymer code.
double re{};
/// Length of connection's perfororation interval
double connection_length{};
/// Connection's skin factor.
double skin_factor{};
/// Connection's D factor-i.e., the flow-dependent skin factor
/// for gas.
double d_factor{};
/// Product of certain static elements of D-factor correlation
/// law (WDFACCOR keyword).
double static_dfac_corr_coeff{};
/// Denominator in peaceman's formula-i.e., log(r0/rw) + skin.
double peaceman_denom{};
/// Serialisation test object.
static CTFProperties serializationTestObject();
/// Equality operator
///
/// \param[in] that Property object to which \c *this will be compared.
bool operator==(const CTFProperties& that) const;
/// Inequality operator
///
/// \param[in] that Property object to which \c *this will be compared.
bool operator!=(const CTFProperties& that) const
{
return ! (*this == that);
}
/// Serialisation operator
///
/// \tparam Serializer Protocol for serialising and
/// deserialising objects between memory and character
/// buffers.
///
/// \param[in,out] serializer Serialisation object.
template <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();
@ -117,104 +198,105 @@ namespace RestartIO {
int satTableId() const;
int complnum() const;
int segment() const;
double CF() const;
double wpimult() const;
double CF() const;
double Kh() const;
double Ke() const;
double rw() const;
double r0() const;
double re() const;
double connectionLength() const;
double skinFactor() const;
double dFactor() const;
double Ke() const;
CTFKind kind() const;
const InjMult& injmult() const;
bool activeInjMult() const;
void setInjMult(const InjMult& inj_mult);
void setFilterCake(const FilterCake& filter_cake);
const FilterCake& getFilterCake() const;
bool filterCakeActive() const;
double getFilterCakeRadius() const;
double getFilterCakeArea() const;
const CTFProperties& ctfProperties() const
{
return this->ctf_properties_;
}
std::size_t sort_value() const;
bool getDefaultSatTabId() const;
const std::optional<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 setComplnum(int compnum);
void setSkinFactor(double skin_factor);
void setDFactor(double d_factor);
void setKe(double Ke);
void setCF(double CF);
void setDefaultSatTabId(bool id);
void setStaticDFacCorrCoeff(const double c);
void scaleWellPi(double wellPi);
bool prepareWellPIScaling();
bool applyWellPIScaling(const double scaleFactor);
void updateSegmentRST(int segment_number_arg,
double center_depth_arg);
void updateSegment(int segment_number_arg,
double center_depth_arg,
std::size_t compseg_insert_index,
const std::optional<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>
void serializeOp(Serializer& serializer)
{
serializer(direction);
serializer(center_depth);
serializer(open_state);
serializer(sat_tableId);
serializer(m_complnum);
serializer(m_CF);
serializer(m_Kh);
serializer(m_rw);
serializer(m_r0);
serializer(m_re);
serializer(m_connection_length);
serializer(m_skin_factor);
serializer(m_d_factor);
serializer(m_Ke);
serializer(ijk);
serializer(m_global_index);
serializer(m_ctfkind);
serializer(m_injmult);
serializer(m_sort_value);
serializer(m_perf_range);
serializer(m_defaultSatTabId);
serializer(segment_number);
serializer(m_subject_to_welpi);
serializer(m_filter_cake);
serializer(m_wpimult);
serializer(this->direction);
serializer(this->center_depth);
serializer(this->open_state);
serializer(this->sat_tableId);
serializer(this->m_complnum);
serializer(this->ctf_properties_);
serializer(this->ijk);
serializer(this->m_ctfkind);
serializer(this->m_global_index);
serializer(this->m_injmult);
serializer(this->m_sort_value);
serializer(this->m_perf_range);
serializer(this->m_defaultSatTabId);
serializer(this->segment_number);
serializer(this->m_wpimult);
serializer(this->m_subject_to_welpi);
serializer(this->m_filter_cake);
}
private:
Direction direction;
double center_depth;
State open_state;
int sat_tableId;
int m_complnum;
double m_CF;
double m_Kh;
double m_rw;
double m_r0;
double m_re;
double m_connection_length;
double m_skin_factor;
double m_d_factor;
double m_Ke;
// Note to maintainer: If you add new members to this list, then
// please also update the operator==(), serializeOp(), and
// serializationTestObject() member functions.
Direction direction { Direction::Z };
double center_depth { 0.0 };
State open_state { State::SHUT };
int sat_tableId { -1 };
int m_complnum { -1 };
CTFProperties ctf_properties_{};
std::array<int,3> ijk{};
CTFKind m_ctfkind { CTFKind::DeckValue };
std::optional<InjMult> m_injmult{};
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
assembled in the WellConnections class. During the lifetime of the
@ -266,26 +348,26 @@ namespace RestartIO {
explicitly, so the truth is probably that the storage order
during simulation makes no difference?
*/
std::size_t m_sort_value{};
std::size_t m_sort_value;
std::optional<std::pair<double,double>> m_perf_range;
bool m_defaultSatTabId;
std::optional<std::pair<double,double>> m_perf_range{};
bool m_defaultSatTabId{true};
// related segment number
// 0 means the completion is not related to segment
int segment_number = 0;
// Associate segment number
//
// 0 means the connection is not associated to a segment.
int segment_number { 0 };
double m_wpimult { 1.0 };
// Whether or not this Connection is subject to WELPI scaling.
bool m_subject_to_welpi = false;
bool m_subject_to_welpi { false };
// For applying last known WPIMULT to when calculating connection transmissibilty factor in CSKIN
double m_wpimult = 1.0;
std::optional<FilterCake> m_filter_cake;
std::optional<FilterCake> m_filter_cake{};
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
namespace Opm {
class Connection;
class DeckRecord;
class WellConnections;
} // namespace Opm
@ -31,49 +32,210 @@ namespace Opm { namespace RestartIO {
namespace Opm {
enum class WDFACTYPE {
NONE = 1,
DFACTOR = 2,
DAKEMODEL = 3,
CON_DFACTOR = 4
};
class WDFAC
{
public:
/// Parameters for Dake's D-factor correlation model.
///
/// In particular, holds the coefficient 'A' and the exponents 'B'
/// and 'C' of the correlation relation
///
/// D = A * (Ke/K0)**B * porosity**C * Ke / (h * rw) * (sg_g/mu_g)
///
/// in which
///
/// * Ke is the connection's effective permeability (sqrt(Kx*Ky)
/// in the case of a vertical connection)
///
/// * K0 is a reference/background permeability scale (1mD)
///
/// * h is the effective length of the connection's perforation
/// interval (dz*ntg in the case of a vertical connection)
///
/// * rw is the connection's wellbore radius
///
/// * sg_g is the specific gravity of surface condition gas
/// relative to surface condition air
///
/// * mu_g is the reservoir condition viscosity of the free gas phase.
struct Correlation
{
/// Multiplicative coefficient 'A'.
double coeff_a{0.0};
/// Power coefficient 'B' for the effective permeability.
double exponent_b{0.0};
/// Power coefficient 'C' for the porosity term.
double exponent_c{0.0};
/// Serialisation test object.
static Correlation serializationTestObject();
/// Equality operator
///
/// \param[in] other Object to which \c *this will be compared.
bool operator==(const Correlation& other) const;
/// Inequality operator
///
/// \param[in] other Object to which \c *this will be compared.
bool operator!=(const Correlation& other) const
{
return ! (*this == other);
}
/// Serialisation operator
///
/// \tparam Serializer Protocol for serialising and
/// deserialising objects between memory and character
/// buffers.
///
/// \param[in,out] serializer Serialisation object.
template <class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(this->coeff_a);
serializer(this->exponent_b);
serializer(this->exponent_c);
}
};
/// Serialisation test object
static WDFAC serializationTestObject();
double getDFactor(const Connection& connection, double mu, double rho, double phi) const;
/// Configure D-factor calculation from well-level D-factor
/// description (keyword 'WDFAC')
///
/// \param[in] record Well-level D-factor description. Single
/// record from WDFAC keyword.
void updateWDFAC(const DeckRecord& record);
//void updateWDFAC(const RestartIO::RstWell& rst_well);
void updateWDFACCOR(const DeckRecord& record);
//void updateWDFACOR(const RestartIO::RstWell& rst_well);
/// Configure D-factor calculation from Dake correlation model
/// (keyword WDFACCOR).
///
/// \param[in] record Dake correlation model description. Single
/// record from WDFACCOR keyword.
void updateWDFACCOR(const DeckRecord& record);
/// Check if any input-level connctions have a non-trivial D-factor
/// and update this well's D-factor category accordingly.
///
/// \param[in] connections Connection set as defined by keyword
/// COMPDAT. This function will detect if any of the connections
/// created from COMPDAT define a non-trivial D-factor at the
/// connection level (item 12 of COMPDAT) and update the D-factor
/// category if so.
void updateWDFACType(const WellConnections& connections);
/// Capture sum of all CTFs for the purpose of translating
/// well-level D-factors to connection-level D-factors.
///
/// \param[in] connections Connection set as defined by keyword
/// COMPDAT.
void updateTotalCF(const WellConnections& connections);
/// Retrieve currently configured D-factor for single connection
///
/// \param[in] rhoGS Surface condition mass density of gas
///
/// \param[in] gas_visc Reservoir condition gas viscosity
///
/// \param[in] conn Reservoir connection for which to retrieve the
/// D-factor.
///
/// \return D-factor for connection \p conn.
double getDFactor(const double rhoGS,
const double gas_visc,
const Connection& conn) const;
/// Retrieve current D-factor correlation model coefficients.
const Correlation& getDFactorCorrelationCoefficients() const
{
return this->m_corr;
}
/// Whether or not a flow-dependent skin factor ('D') has been
/// configured for the current well.
bool useDFactor() const;
/// Equality operator
///
/// \param[in] other Object to which \c *this will be compared.
bool operator==(const WDFAC& other) const;
bool operator!=(const WDFAC& other) const;
template<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)
{
serializer(m_a);
serializer(m_b);
serializer(m_c);
serializer(m_d);
serializer(m_total_cf);
serializer(m_type);
serializer(this->m_type);
serializer(this->m_d);
serializer(this->m_total_cf);
serializer(this->m_corr);
}
private:
double m_a{0.0};
double m_b{0.0};
double m_c{0.0};
/// D-factor categories.
enum class WDFacType
{
/// No flow-dependent skin factor is configured for this well.
NONE = 1,
/// Well-level D-factor.
DFACTOR = 2,
/// Use Dake's D-factor correlation model.
DAKEMODEL = 3,
/// Connection-level D-factor.
CON_DFACTOR = 4,
};
/// D-factor category for this well.
WDFacType m_type { WDFacType::NONE };
/// Well-level D-factor for this well.
double m_d{0.0};
/// Total CTF sum for this well.
double m_total_cf{-1.0};
WDFACTYPE m_type = WDFACTYPE::NONE;
/// Coefficients for Dake's correlation model.
Correlation m_corr{};
/// Retrieve connection-level D-Factor from COMPDAT entries
///
/// Possibly translated from well-level values.
///
/// \param[in] conn Reservoir connection for which to retrieve the
/// D-factor.
///
/// \return Connection-level D-factor.
double connectionLevelDFactor(const Connection& conn) const;
/// Translate well-level D-factor to connection level D-factor
///
/// \param[in] dfac Well-level D-factor.
///
/// \param[in] conn Reservoir connection for which to retrieve the
/// D-factor.
///
/// \return Connection-level D-factor, translated from well level.
double scaledWellLevelDFactor(const double dfac,
const Connection& conn) const;
};
} // namespace Opm

View File

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

View File

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

View File

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

View File

@ -44,19 +44,29 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems
namespace SConn {
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
Diameter = 2, // Connection diameter
EffectiveKH = 3, // Effective Kh product of connection
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
SegDistStart = 21, // Distance to start of connection in segment
item30 = 29, // 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
};
} // SConn

View File

@ -331,6 +331,9 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems
VfpBhpScalingFact = 83, // Tubing pressure loss scaling factor (WVFPDP(3))
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,

View File

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

View File

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

View File

@ -17,16 +17,24 @@
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/CompletedCells.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.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)
: grid(&ecl_grid)
, fp(&fpm)
, cells(completed_cells)
#include <cstddef>
#include <string>
#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)
@ -34,45 +42,58 @@ Opm::ScheduleGrid::ScheduleGrid(Opm::CompletedCells& completed_cells)
{}
namespace {
double try_get_value(const Opm::FieldPropsManager& fp, const std::string& kw, std::size_t active_index) {
if (fp.has_double(kw))
return fp.try_get<double>(kw)->at(active_index);
else
double try_get_value(const Opm::FieldPropsManager& fp,
const std::string& kw,
const std::size_t active_index)
{
if (! fp.has_double(kw)) {
throw std::logic_error(fmt::format("FieldPropsManager is missing keyword '{}'", kw));
}
double try_get_ntg_value(const Opm::FieldPropsManager& fp, const std::string& kw, std::size_t active_index){
if (fp.has_double(kw))
return fp.try_get<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 this->cells.get(i,j,k);
return fp.try_get<double>(kw)->at(active_index);
}
double try_get_ntg_value(const Opm::FieldPropsManager& fp,
const std::string& kw,
const std::size_t active_index)
{
return fp.has_double(kw)
? fp.try_get<double>(kw)->at(active_index)
: 1.0;
}
}
const Opm::EclipseGrid* Opm::ScheduleGrid::get_grid() const {
return this->grid;
const Opm::CompletedCells::Cell&
Opm::ScheduleGrid::get_cell(std::size_t i, std::size_t j, std::size_t k) const
{
if (this->grid == nullptr) {
return this->cells.get(i, j, k);
}
auto [valid, cellRef] = this->cells.try_get(i, j, k);
if (!valid) {
cellRef.depth = this->grid->getCellDepth(i, j, k);
cellRef.dimensions = this->grid->getCellDimensions(i, j, k);
if (this->grid->cellActive(i, j, k)) {
auto& props = cellRef.props.emplace(CompletedCells::Cell::Props{});
props.active_index = this->grid->getActiveIndex(i, j, k);
props.permx = try_get_value(*this->fp, "PERMX", props.active_index);
props.permy = try_get_value(*this->fp, "PERMY", props.active_index);
props.permz = try_get_value(*this->fp, "PERMZ", props.active_index);
props.poro = try_get_value(*this->fp, "PORO", props.active_index);
props.satnum = this->fp->get_int("SATNUM").at(props.active_index);
props.pvtnum = this->fp->get_int("PVTNUM").at(props.active_index);
props.ntg = try_get_ntg_value(*this->fp, "NTG", props.active_index);
}
}
return cellRef;
}
const Opm::EclipseGrid* Opm::ScheduleGrid::get_grid() const
{
return this->grid;
}

View File

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

View File

@ -17,126 +17,168 @@
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/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/UnitSystem.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 <vector>
#include <cmath>
#include <iostream>
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
#include <algorithm>
#include <cassert>
#include <cmath>
#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 {
WDFAC::Correlation WDFAC::Correlation::serializationTestObject()
{
return { 1.23, 0.456, 0.457 };
}
bool WDFAC::Correlation::operator==(const Correlation& other) const
{
return (this->coeff_a == other.coeff_a)
&& (this->exponent_b == other.exponent_b)
&& (this->exponent_c == other.exponent_c)
;
}
// -------------------------------------------------------------------------
WDFAC WDFAC::serializationTestObject()
{
WDFAC result;
result.m_a = 1.23;
result.m_b = 0.456;
result.m_c = 0.457;
result.m_type = WDFacType::DAKEMODEL;
result.m_d = 0.458;
result.m_total_cf = 1.0;
result.m_type = WDFACTYPE::NONE;
result.m_corr = Correlation::serializationTestObject();
return result;
}
bool WDFAC::operator==(const WDFAC& other) const {
return (m_a == other.m_a)
&& (m_b == other.m_b)
&& (m_c == other.m_c)
&& (m_d == other.m_d)
&& (m_total_cf == other.m_total_cf)
&& (m_type == other.m_type);
}
void WDFAC::updateWDFAC(const DeckRecord& record) {
void WDFAC::updateWDFAC(const DeckRecord& record)
{
m_d = record.getItem<ParserKeywords::WDFAC::DFACTOR>().getSIDouble(0);
m_type = WDFACTYPE::DFACTOR;
m_type = WDFacType::DFACTOR;
}
void WDFAC::updateWDFACCOR(const DeckRecord& record) {
m_a = record.getItem<ParserKeywords::WDFACCOR::A>().getSIDouble(0);
m_b = record.getItem<ParserKeywords::WDFACCOR::B>().getSIDouble(0);
m_c = record.getItem<ParserKeywords::WDFACCOR::C>().getSIDouble(0);
m_type = WDFACTYPE::DAKEMODEL;
void WDFAC::updateWDFACCOR(const DeckRecord& record)
{
this->m_corr.coeff_a = record.getItem<ParserKeywords::WDFACCOR::A>().getSIDouble(0);
this->m_corr.exponent_b = record.getItem<ParserKeywords::WDFACCOR::B>().getSIDouble(0);
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 =
std::any_of(connections.begin(), connections.end(),
[](const auto& conn) { return conn.dFactor() != 0.0; });
// non-trivial dfactors detected use connection D factors
if (non_trivial_dfactor) {
m_type = WDFACTYPE::CON_DFACTOR;
updateTotalCF(connections);
// Non-trivial D-factors detected. Use connection D-factors.
m_type = WDFacType::CON_DFACTOR;
this->updateTotalCF(connections);
}
}
void WDFAC::updateTotalCF(const WellConnections& connections) {
m_total_cf = std::accumulate(connections.begin(), connections.end(), 0.0,
[](const double tot_cf, const auto& conn) { return tot_cf + conn.CF(); });
void WDFAC::updateTotalCF(const WellConnections& connections)
{
this->m_total_cf = std::accumulate(connections.begin(), connections.end(), 0.0,
[](const double tot_cf, const auto& conn) { return tot_cf + conn.CF(); });
}
double WDFAC::getDFactor(const Connection& connection, double mu, double rho, double phi) const {
switch (m_type)
{
case WDFACTYPE::NONE:
double WDFAC::getDFactor(const double rhoGS,
const double gas_visc,
const Connection& conn) const
{
switch (this->m_type) {
case WDFacType::NONE:
return 0.0;
case WDFACTYPE::DFACTOR: {
if (m_total_cf < 0.0) {
throw std::invalid_argument { "Total connection factor is not set" };
}
return m_d * m_total_cf / connection.CF();
}
case WDFACTYPE::CON_DFACTOR: {
double d = connection.dFactor();
// If a negative d factor is set in COMPDAT individual connection d factors should be used directly.
if (d < 0)
return -d;
// If a positive d factor is set in COMPDAT the connection d factors is treated like a well d factor.
// and thus scaled with the connection index
if (m_total_cf < 0.0) {
throw std::invalid_argument { "Total connection factor is not set" };
}
return d * m_total_cf / connection.CF();
}
case WDFACTYPE::DAKEMODEL:
{
double Kh = connection.Kh();
double Ke = connection.Ke();
double h = Kh / Ke;
double rw = connection.rw();
const auto k_md = unit::convert::to(Ke, prefix::milli*unit::darcy);
double beta = m_a * (std::pow(k_md, m_b) * std::pow(phi, m_c));
double specific_gravity = rho / 1.225; // divide by density of air at standard conditions.
return beta * specific_gravity * Ke / (h * mu * rw );
}
default:
break;
case WDFacType::DFACTOR:
return this->scaledWellLevelDFactor(this->m_d, conn);
case WDFacType::DAKEMODEL:
return dakeModelDFactor(rhoGS, gas_visc, conn.ctfProperties());
case WDFacType::CON_DFACTOR:
return this->connectionLevelDFactor(conn);
}
return 0.0;
}
bool WDFAC::useDFactor() const {
return m_type != WDFACTYPE::NONE;
bool WDFAC::useDFactor() const
{
return m_type != WDFacType::NONE;
}
bool WDFAC::operator!=(const WDFAC& other) const {
return !(*this == other);
bool WDFAC::operator==(const WDFAC& other) const
{
return (this->m_type == other.m_type)
&& (this->m_d == other.m_d)
&& (this->m_total_cf == other.m_total_cf)
&& (this->m_corr == other.m_corr)
;
}
double WDFAC::connectionLevelDFactor(const Connection& conn) const
{
const double d = conn.dFactor();
// Negative D-factor values in COMPDAT should be used directly as
// connection-level D-factors.
if (d < 0.0) {
return -d;
}
// Positive D-factor values in COMPDAT are treated as well-level
// values and scaled with the CTF for translation to connection
// level.
return this->scaledWellLevelDFactor(d, conn);
}
double WDFAC::scaledWellLevelDFactor(const double dfac,
const Connection& conn) const
{
if (this->m_total_cf < 0.0) {
throw std::invalid_argument {
"Total well-level connection factor is not set"
};
}
return dfac * this->m_total_cf / conn.CF();
}
}

View File

@ -40,13 +40,12 @@
#include <opm/input/eclipse/Schedule/Well/WVFPDP.hpp>
#include <opm/input/eclipse/Schedule/Well/WVFPEXP.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include <opm/common/utility/OpmInputError.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/W.hpp>
@ -1300,42 +1299,60 @@ bool Well::handleWELOPENConnections(const DeckRecord& record, Connection::State
return this->updateConnections(std::move(new_connections), false);
}
bool Well::handleCSKINConnections(const DeckRecord& record) {
// Lambda expression to check if record coordinates match connection coordinates
auto match = [=]( const Connection &c) -> bool {
if (!match_eq(c.getI(), record, "I" , -1)) return false;
if (!match_eq(c.getJ(), record, "J" , -1)) return false;
if (!match_ge(c.getK(), record, "K_UPPER", -1)) return false;
if (!match_le(c.getK(), record, "K_LOWER", -1)) return false;
bool Well::handleCSKIN(const DeckRecord& record,
const KeywordLocation& location)
{
using Kw = ParserKeywords::CSKIN;
return true;
auto need_skin_adjustment = [&record](const Connection &c) {
const auto value_shift = -1;
return match_eq(c.getI(), record, Kw::I::itemName, value_shift)
&& match_eq(c.getJ(), record, Kw::J::itemName, value_shift)
&& match_ge(c.getK(), record, Kw::K_UPPER::itemName, value_shift)
&& match_le(c.getK(), record, Kw::K_LOWER::itemName, value_shift);
};
// Generate a new connection which will be updated with new connection skin factor
auto new_connections = std::make_shared<WellConnections>(this->connections->ordering(), this->headI, this->headJ);
// New connection set which will be updated with new connection level
// skin factors.
auto new_connections = std::make_shared<WellConnections>
(this->connections->ordering(), this->headI, this->headJ);
// Update skin factor
double skin_factor = record.getItem("CONNECTION_SKIN_FACTOR").get<double>(0);
const double angle = 6.2831853071795864769252867665590057683943387987502116419498;
for (auto c : *this->connections) {
if (match(c)) {
// Check for potential negative new CF
if ((std::log(c.r0() / std::min(c.rw(), c.r0())) + skin_factor) < 0.0) {
throw std::runtime_error("Negative connection transmissibility factor produced by CSKIN for well "
+ name());
}
// Calculate new connection transmissibility factor
double CF = angle * c.Kh() / (std::log(c.r0() / std::min(c.rw(), c.r0())) + skin_factor);
// Apply last known WPIMULT (defaulted to 1.0)
CF *= c.wpimult();
// Set skin factor and connection factor
c.setSkinFactor(skin_factor);
c.setCF(CF);
const auto skin_factor = record.getItem<Kw::CONNECTION_SKIN_FACTOR>().getSIDouble(0);
for (const auto& connection : *this->connections) {
if (! need_skin_adjustment(connection)) {
// No CSKIN adjustment needed here. Include connection as-is
// into new connection set.
new_connections->add(connection);
continue;
}
new_connections->add(c);
// If we get here, we must make the connection's skin factor be
// 'skin_factor'.
//
// First guard against this adjustment making the CTF go negative,
// typically because the 'skin_factor' value is large and negative
// itself.
if (const auto& ctf_props = connection.ctfProperties();
ctf_props.peaceman_denom + skin_factor - ctf_props.skin_factor < 0.0)
{
throw OpmInputError {
fmt::format("Negative connection transmissibility "
"factor generated by skin factor {} "
"in connection ({},{},{}) for well {}.",
skin_factor,
connection.getI() + 1,
connection.getJ() + 1,
connection.getK() + 1,
this->name()),
location
};
}
auto connection_copy = connection;
connection_copy.setSkinFactor(skin_factor);
new_connections->add(connection_copy);
}
return this->updateConnections(std::move(new_connections), false);
@ -1390,6 +1407,7 @@ bool Well::handleWPIMULT(const DeckRecord& record) {
new_connections->add(c);
}
return this->updateConnections(std::move(new_connections), false);
}

File diff suppressed because it is too large Load Diff

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>
void staticContrib(const Opm::Connection& conn,
const Opm::UnitSystem& units,
@ -180,7 +190,7 @@ namespace {
return static_cast<float>(units.from_si(u, x));
};
sConn[Ix::ConnTrans] =
sConn[Ix::EffConnTrans] = sConn[Ix::ConnTrans] =
scprop(M::transmissibility, conn.CF());
sConn[Ix::Depth] = scprop(M::length, conn.depth());
@ -191,8 +201,7 @@ namespace {
sConn[Ix::SkinFactor] = conn.skinFactor();
sConn[Ix::item12] = sConn[Ix::ConnTrans];
sConn[Ix::CFDenom] = conn.ctfProperties().peaceman_denom;
if (conn.attachedToSegment()) {
const auto& [start, end] = *conn.perf_range();
@ -202,7 +211,14 @@ namespace {
sConn[Ix::item30] = -1.0e+20f;
sConn[Ix::item31] = -1.0e+20f;
sConn[Ix::CFInDeck] = (conn.ctfAssignedFromInput()) ? 1 : 0;
sConn[Ix::EffectiveLength] =
scprop(M::length, conn.connectionLength());
sConn[Ix::StaticDFacCorrCoeff] =
staticDFacCorrCoeff(conn.ctfProperties(), units);
sConn[Ix::CFInDeck] = conn.ctfAssignedFromInput() ? 1.0f : 0.0f;
}
template <class SConnArray>
@ -218,8 +234,15 @@ namespace {
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);
// 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
@ -314,12 +337,12 @@ AggregateConnectionData(const std::vector<int>& inteHead)
void
Opm::RestartIO::Helpers::AggregateConnectionData::
captureDeclaredConnData(const Schedule& sched,
const EclipseGrid& grid,
const UnitSystem& units,
const data::Wells& xw,
const SummaryState& summary_state,
const std::size_t sim_step)
captureDeclaredConnData(const Schedule& sched,
const EclipseGrid& grid,
const UnitSystem& units,
const data::Wells& xw,
const SummaryState& summary_state,
const std::size_t sim_step)
{
wellConnectionLoop(sched, sim_step, grid, xw, [&units, &summary_state, this]
(const std::string& wellName,

View File

@ -37,6 +37,7 @@
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.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/WellConnections.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/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 <cassert>
#include <cmath>
@ -1028,6 +1034,26 @@ namespace {
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>
void assignBhpVfpAdjustment(const Opm::Well& well,
SWProp&& swprop,
@ -1094,6 +1120,7 @@ namespace {
assignWGrupCon(well, sWell);
assignEfficiencyFactors(well, sWell);
assignDFactorCorrelation(well, units, sWell);
assignEconomicLimits(well, swprop, sWell);
assignWellTest(well.name(), sched, wtest_state, sim_step, swprop, sWell);
assignTracerData(tracers, smry, well.name(), sWell);

View File

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

View File

@ -55,20 +55,32 @@
#include <stdexcept>
#include <string>
BOOST_AUTO_TEST_CASE(AICDWellTest) {
auto dir = Opm::Connection::Direction::Z;
BOOST_AUTO_TEST_CASE(AICDWellTest)
{
const auto dir_z = Opm::Connection::Direction::Z;
const auto dir_x = Opm::Connection::Direction::X;
const auto kind = Opm::Connection::CTFKind::DeckValue;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10);
Opm::EclipseGrid grid(20,20,20, 1., 1., 25.0, 2500.0);
connection_set.add(Opm::Connection( 19, 0, 0,grid.getGlobalIndex(19,0,0), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 19, 0, 1,grid.getGlobalIndex(19,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 19, 0, 2,grid.getGlobalIndex(19,0,2), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 18, 0, 1,grid.getGlobalIndex(18,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 17, 0, 1,grid.getGlobalIndex(17,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 16, 0, 1,grid.getGlobalIndex(16,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 15, 0, 1,grid.getGlobalIndex(15,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 };
const auto depth = 0.0;
const auto state = Opm::Connection::State::OPEN;
auto ctf_props = Opm::Connection::CTFProperties{};
ctf_props.CF = 200.0;
ctf_props.Kh = 17.29;
ctf_props.rw = 0.25;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10);
connection_set.add({ 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
BOOST_CHECK_EQUAL( 7U , connection_set.size() );
@ -212,23 +224,33 @@ WSEGAICD
const double center_depth_connection7 = connection7.depth();
BOOST_CHECK_EQUAL(segment_number_connection7, 8);
BOOST_CHECK_EQUAL(center_depth_connection7, 2534.5);
}
BOOST_AUTO_TEST_CASE(MultisegmentWellTest) {
auto dir = Opm::Connection::Direction::Z;
BOOST_AUTO_TEST_CASE(MultisegmentWellTest)
{
const auto dir_z = Opm::Connection::Direction::Z;
const auto dir_x = Opm::Connection::Direction::X;
const auto kind = Opm::Connection::CTFKind::DeckValue;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10);
Opm::EclipseGrid grid(20,20,20, 1., 1., 25.0, 2500.0);
connection_set.add(Opm::Connection( 19, 0, 0,grid.getGlobalIndex(19,0,0), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 19, 0, 1,grid.getGlobalIndex(19,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 19, 0, 2,grid.getGlobalIndex(19,0,2), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 };
connection_set.add(Opm::Connection( 18, 0, 1,grid.getGlobalIndex(18,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 17, 0, 1,grid.getGlobalIndex(17,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 16, 0, 1,grid.getGlobalIndex(16,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 15, 0, 1,grid.getGlobalIndex(15,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
const auto depth = 0.0;
const auto state = Opm::Connection::State::OPEN;
auto ctf_props = Opm::Connection::CTFProperties{};
ctf_props.CF = 200.0;
ctf_props.Kh = 17.29;
ctf_props.rw = 0.25;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10);
connection_set.add({ 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
BOOST_CHECK_EQUAL( 7U , connection_set.size() );
@ -383,19 +405,32 @@ WSEGSICD
}
BOOST_AUTO_TEST_CASE(WrongDistanceCOMPSEGS) {
auto dir = Opm::Connection::Direction::Z;
BOOST_AUTO_TEST_CASE(WrongDistanceCOMPSEGS)
{
const auto dir_z = Opm::Connection::Direction::Z;
const auto dir_x = Opm::Connection::Direction::X;
const auto kind = Opm::Connection::CTFKind::DeckValue;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10);
Opm::EclipseGrid grid(20,20,20, 1., 1., 25., 2500.);
connection_set.add(Opm::Connection( 19, 0, 0, grid.getGlobalIndex(19,0,0),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 19, 0, 1, grid.getGlobalIndex(19,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 19, 0, 2, grid.getGlobalIndex(19,0,2),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 18, 0, 1, grid.getGlobalIndex(18,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 17, 0, 1, grid.getGlobalIndex(17,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 16, 0, 1, grid.getGlobalIndex(16,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 15, 0, 1, grid.getGlobalIndex(15,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25., 2500.0 };
const auto depth = 0.0;
const auto state = Opm::Connection::State::OPEN;
auto ctf_props = Opm::Connection::CTFProperties{};
ctf_props.CF = 200.0;
ctf_props.Kh = 17.29;
ctf_props.rw = 0.25;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10);
connection_set.add({ 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
BOOST_CHECK_EQUAL( 7U , connection_set.size() );
@ -452,19 +487,32 @@ BOOST_AUTO_TEST_CASE(WrongDistanceCOMPSEGS) {
BOOST_CHECK_NO_THROW(Opm::Compsegs::processCOMPSEGS(compsegs, connection_set, segment_set, Opm::ScheduleGrid(grid, fp, cells), parseContext, errorGuard));
}
BOOST_AUTO_TEST_CASE(NegativeDepthCOMPSEGS) {
auto dir = Opm::Connection::Direction::Z;
BOOST_AUTO_TEST_CASE(NegativeDepthCOMPSEGS)
{
const auto dir_z = Opm::Connection::Direction::Z;
const auto dir_x = Opm::Connection::Direction::X;
const auto kind = Opm::Connection::CTFKind::DeckValue;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10);
Opm::EclipseGrid grid(20,20,20, 1., 1., 25., 2500.);
connection_set.add(Opm::Connection( 19, 0, 0, grid.getGlobalIndex(19,0,0),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 19, 0, 1, grid.getGlobalIndex(19,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 19, 0, 2, grid.getGlobalIndex(19,0,2),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 18, 0, 1, grid.getGlobalIndex(18,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 17, 0, 1, grid.getGlobalIndex(17,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 16, 0, 1, grid.getGlobalIndex(16,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 15, 0, 1, grid.getGlobalIndex(15,0,1),1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 };
const auto depth = 0.0;
const auto state = Opm::Connection::State::OPEN;
auto ctf_props = Opm::Connection::CTFProperties{};
ctf_props.CF = 200.0;
ctf_props.Kh = 17.29;
ctf_props.rw = 0.25;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10);
connection_set.add({ 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
BOOST_CHECK_EQUAL( 7U , connection_set.size() );
@ -521,19 +569,31 @@ BOOST_AUTO_TEST_CASE(NegativeDepthCOMPSEGS) {
BOOST_CHECK_NO_THROW( Opm::Compsegs::processCOMPSEGS(compsegs, connection_set, segment_set, Opm::ScheduleGrid(grid, fp, cells), parseContext, errorGuard) );
}
BOOST_AUTO_TEST_CASE(testwsegvalv) {
auto dir = Opm::Connection::Direction::Z;
BOOST_AUTO_TEST_CASE(testwsegvalv)
{
const auto dir_z = Opm::Connection::Direction::Z;
const auto dir_x = Opm::Connection::Direction::X;
const auto kind = Opm::Connection::CTFKind::DeckValue;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10);
Opm::EclipseGrid grid(20,20,20, 1., 1., 25., 2500.);
connection_set.add(Opm::Connection( 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
connection_set.add(Opm::Connection( 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, dir, kind, 0, true) );
const Opm::EclipseGrid grid { 20,20,20, 1.0, 1.0, 25.0, 2500.0 };
connection_set.add(Opm::Connection( 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
connection_set.add(Opm::Connection( 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, 0.0, Opm::Connection::State::OPEN , 200, 17.29, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, Opm::Connection::Direction::X, kind, 0, true) );
const auto depth = 0.0;
const auto state = Opm::Connection::State::OPEN;
auto ctf_props = Opm::Connection::CTFProperties{};
ctf_props.CF = 200.0;
ctf_props.Kh = 17.29;
ctf_props.rw = 0.25;
Opm::WellConnections connection_set(Opm::Connection::Order::TRACK, 10,10);
connection_set.add({ 19, 0, 0, grid.getGlobalIndex(19,0,0), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 19, 0, 1, grid.getGlobalIndex(19,0,1), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 19, 0, 2, grid.getGlobalIndex(19,0,2), 1, state, dir_z, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 18, 0, 1, grid.getGlobalIndex(18,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 17, 0, 1, grid.getGlobalIndex(17,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 16, 0, 1, grid.getGlobalIndex(16,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
connection_set.add({ 15, 0, 1, grid.getGlobalIndex(15,0,1), 1, state, dir_x, kind, 0, depth, ctf_props, 0, true });
BOOST_CHECK_EQUAL( 7U , connection_set.size() );

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,6 +25,7 @@
#include <opm/output/eclipse/AggregateMSWData.hpp>
#include <opm/output/eclipse/AggregateWellData.hpp>
#include <opm/output/eclipse/VectorItems/connection.hpp>
#include <opm/output/eclipse/VectorItems/intehead.hpp>
#include <opm/output/eclipse/VectorItems/well.hpp>
@ -34,70 +35,100 @@
#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/Python/Python.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.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/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <cmath>
#include <cstddef>
#include <exception>
#include <memory>
#include <stdexcept>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
struct MockIH
{
MockIH(const int numWells,
namespace {
struct MockIH
{
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
const int ncwMax = 20,
const int iConnPerConn = 25, // NICONZ
const int sConnPerConn = 41, // NSCONZ
const int xConnPerConn = 58); // NXCONZ
std::vector<int> value;
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;
Sz nsegwl;
Sz nsegmx;
Sz nswlmx;
Sz ncwmax;
Sz niconz;
Sz nsconz;
Sz nxconz;
};
this->nwells = this->value[Ix::NWELLS] = numWells;
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;
this->nsegwl = this->value[Ix::NSEGWL] = nsegWell;
this->ncwmax = this->value[Ix::NCWMAX] = ncwMax;
this->nswlmx = this->value[Ix::NSWLMX] = nsegWell;
this->nsegmx = this->value[Ix::NSEGMX] = 32;
this->niconz = this->value[Ix::NICONZ] = iConnPerConn;
this->nsconz = this->value[Ix::NSCONZ] = sConnPerConn;
this->nxconz = this->value[Ix::NXCONZ] = xConnPerConn;
}
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;
this->ncwmax = this->value[Ix::NCWMAX] = 20;
this->nswlmx = this->value[Ix::NSWLMX] = 1;
this->nsegmx = this->value[Ix::NSEGMX] = 32;
this->niconz = this->value[Ix::NICONZ] = iConnPerConn;
this->nsconz = this->value[Ix::NSCONZ] = sConnPerConn;
this->nxconz = this->value[Ix::NXCONZ] = xConnPerConn;
}
// 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_ConnData)
namespace {
Opm::Deck first_sim()
{
// Mostly copy of tests/FIRST_SIM.DATA
@ -474,7 +505,8 @@ END
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;
@ -502,6 +534,7 @@ END
c.pressure = 215.0;
c.index = connections[i].global_index();
c.trans_factor = connections[i].CF();
c.compact_mult = 0.875;
const auto& global_index = connections[i].global_index();
sum_state.update_conn_var("PROD", "CWPR", global_index + 1, qw * (i + 1));
@ -543,6 +576,7 @@ END
c.pressure = 218.0;
c.index = connections[i].global_index();
c.trans_factor = connections[i].CF();
c.compact_mult = 0.987;
const auto& global_index = connections[i].global_index();
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) };
}
} // namespace
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;
};
// =====================================================================
BOOST_AUTO_TEST_SUITE(Aggregate_ConnData)
} // Anonymous namespace
// test dimensions of Connection data
BOOST_AUTO_TEST_CASE (Constructor)
@ -683,40 +701,46 @@ BOOST_AUTO_TEST_CASE(Declared_Connection_Data)
const auto& sconn = amconn.getSConn();
int connNo = 1;
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::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::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(
sconn[i0 + Ix::SegDistEnd], 130., 1.0e-5); // PROD - conn 1 : Distance to end of connection in segment
BOOST_CHECK_CLOSE(
sconn[i0 + Ix::SegDistStart], 30., 1.0e-5); // PROD - conn 1 : Distance to start of connection in segment
BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffectiveLength], 100.0, 1.0e-5);
// Well no 2 - WINJ well
connNo = 3;
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::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::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(
sconn[i0 + Ix::SegDistEnd], 0., 1.0e-5); // WINJ - conn 3 : Distance to end of connection in segment
BOOST_CHECK_CLOSE(
sconn[i0 + Ix::SegDistStart], 0., 1.0e-5); // WINJ - conn 3 : Distance to start of connection in segment
BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffectiveLength], 100.0, 1.0e-5);
connNo = 4;
i0 = ih.ncwmax * ih.nsconz + (connNo - 1) * ih.nsconz;
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::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::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(
sconn[i0 + Ix::SegDistEnd], 0., 1.0e-5); // WINJ - conn 4 : Distance to end of connection in segment
BOOST_CHECK_CLOSE(
sconn[i0 + Ix::SegDistStart], 0., 1.0e-5); // WINJ - conn 4 : Distance to start of connection in segment
BOOST_CHECK_CLOSE(sconn[i0 + Ix::EffectiveLength], 100.0, 1.0e-5);
}
// XCONN (PROD) + (WINJ)
@ -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()};
const auto rptStep = std::size_t{1};
const auto ih = MockIH {static_cast<int>(simCase.sched.getWells(rptStep).size())};
const auto& [wrc, sum_state] = wr(simCase.sched);
auto conn0 = Opm::RestartIO::Helpers::AggregateConnectionData{ih.value};
conn0.captureDeclaredConnData(simCase.sched,
@ -819,11 +845,10 @@ BOOST_AUTO_TEST_CASE(InactiveCell) {
simCase.es.getUnits(),
wrc,
sum_state,
rptStep
);
rptStep);
std::vector<int> actnum(500, 1);
// Here we deactive the cell holding connection number 2.
std::vector<int> actnum(500, 1);
actnum[simCase.grid.getGlobalIndex(2,4,1)] = 0;
simCase.grid.resetACTNUM(actnum);
@ -833,62 +858,330 @@ BOOST_AUTO_TEST_CASE(InactiveCell) {
simCase.es.getUnits(),
wrc,
sum_state,
rptStep
);
rptStep);
const std::size_t num_test_connections = 4;
{
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;
using IC = ::Opm::RestartIO::Helpers::VectorItems::IConn::index;
if (conn_index >= 2)
offset0 += ih.niconz;
const auto iconn0 = conn0.getIConn();
const auto iconn1 = conn1.getIConn();
for (std::size_t elm_index = 0; elm_index < ih.niconz; 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]);
for (std::size_t conn_index = 0; conn_index < num_test_connections; ++conn_index) {
const std::size_t offset1 = conn_index * ih.niconz;
const std::size_t offset0 = offset1 + (conn_index >= 2)*ih.niconz;
for (std::size_t elm_index = 0; elm_index < ih.niconz; ++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 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)
offset0 += ih.nsconz;
for (std::size_t conn_index = 0; conn_index < num_test_connections; ++conn_index) {
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++)
BOOST_CHECK_EQUAL(sconn1[offset1 + elm_index], sconn0[offset0 + 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]);
}
}
}
{
const auto xconn0 = conn0.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 offset0 = offset1;
if (conn_index >= 2)
if (conn_index >= 2) {
offset0 += ih.nxconz;
}
for (std::size_t elm_index = 0; elm_index < ih.nxconz; elm_index++)
BOOST_CHECK_EQUAL(xconn1[offset1 + elm_index], xconn0[offset0 + 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_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/WellTestState.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
@ -64,42 +66,65 @@
#include "tests/WorkArea.hpp"
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
namespace {
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;
Sz niwelz;
Sz nswelz;
Sz nxwelz;
Sz nzwelz;
};
using Sz = std::vector<int>::size_type;
MockIH::MockIH(const int numWells,
const int iwelPerWell,
const int swelPerWell,
const int xwelPerWell,
const int zwelPerWell)
: value(411, 0)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::intehead;
Sz nwells;
Sz niwelz;
Sz nswelz;
Sz nxwelz;
Sz nzwelz;
};
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;
}
MockIH::MockIH(const int numWells,
const int iwelPerWell,
const int swelPerWell,
const int xwelPerWell,
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 {
Opm::Deck first_sim()
{
// Mostly copy of tests/FIRST_SIM.DATA
@ -817,25 +842,8 @@ TSTEP -- 3
return xw;
}
} // namespace
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;
};
// =====================================================================
BOOST_AUTO_TEST_SUITE(Aggregate_WD)
} // Anonymous namespace
BOOST_AUTO_TEST_CASE (Constructor)
{
@ -1822,3 +1830,157 @@ BOOST_AUTO_TEST_CASE(WELL_POD)
}
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;
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,
2000 + (2*k + 1) / static_cast<double>(2),
Opm::Connection::State::OPEN,
k / 100.0, 1.0, 1.0, 0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 0,
Opm::Connection::Direction::Z,
Opm::Connection::CTFKind::DeckValue, k, false);
Opm::Connection::CTFKind::DeckValue,
0, depth, ctf_props, k, false);
}
return { Opm::Connection::Order::INPUT, i, j, conns };
@ -87,18 +97,28 @@ namespace {
};
for (auto k = topConn; k < kMax; ++k) {
const auto depth = 2000 + (2*k + 1) / static_cast<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,
2000 + (2*k + 1) / static_cast<double>(2),
// Open, Shut, Open, Open, Shut, ...
state[(k - topConn) % state.size()],
// 0.03, 0.0, 0.01, 0.02, 0.03, ...
((k + 3 - topConn) % 4) / 100.0,
1.0, 1.0, 0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 0,
Opm::Connection::Direction::Z,
Opm::Connection::CTFKind::DeckValue, k - topConn, false);
Opm::Connection::CTFKind::DeckValue,
0, depth, ctf_props, k - topConn, false);
}
return { Opm::Connection::Order::INPUT, i, j, conns };
@ -115,12 +135,22 @@ namespace {
const auto iMax = std::min(dims[0] - 1, left + numConns);
for (auto i = left; i < iMax; ++i) {
const auto depth = 2000 + (2*k + 1) / static_cast<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,
2000 + (2*k + 1) / static_cast<double>(2),
Opm::Connection::State::OPEN,
i / 100.0, 1.0, 1.0, 0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 0,
Opm::Connection::Direction::X,
Opm::Connection::CTFKind::DeckValue, i - left, false);
Opm::Connection::CTFKind::DeckValue,
0, depth, ctf_props, i - left, false);
}
return { Opm::Connection::Order::INPUT, left, j, conns };

View File

@ -732,12 +732,12 @@ BOOST_AUTO_TEST_CASE(test_RFT)
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
for (size_t i = 0; i < 9; ++i) {
Opm::data::Connection well_comp { grid.getGlobalIndex(8,8,i) ,r1, 0.0 , 0.0, (double)i, 0.1*i,0.2*i, 1.2e3, 4.321, 0.0, con_filtrate};
Opm::data::Connection well_comp { grid.getGlobalIndex(8,8,i), r1, 0.0 , 0.0, (double)i, 0.1*i,0.2*i, 1.2e3, 4.321, 0.0, 1.23, con_filtrate};
well1_comps[i] = std::move(well_comp);
}
std::vector<Opm::data::Connection> well2_comps(6);
for (size_t i = 0; i < 6; ++i) {
Opm::data::Connection well_comp { grid.getGlobalIndex(3,3,i+3) ,r2, 0.0 , 0.0, (double)i, i*0.1,i*0.2, 0.15, 0.54321, 0.0, con_filtrate};
Opm::data::Connection well_comp { grid.getGlobalIndex(3,3,i+3), r2, 0.0 , 0.0, (double)i, i*0.1,i*0.2, 0.15, 0.54321, 0.0, 0.98, con_filtrate};
well2_comps[i] = std::move(well_comp);
}
@ -871,12 +871,12 @@ BOOST_AUTO_TEST_CASE(test_RFT2)
std::vector<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
for (size_t i = 0; i < 9; ++i) {
Opm::data::Connection well_comp { grid.getGlobalIndex(8,8,i) ,r1, 0.0 , 0.0, (double)i, 0.1*i,0.2*i, 3.14e5, 0.1234, 0.0, con_filtrate};
Opm::data::Connection well_comp { grid.getGlobalIndex(8,8,i), r1, 0.0 , 0.0, (double)i, 0.1*i,0.2*i, 3.14e5, 0.1234, 0.0, 1.23, con_filtrate};
well1_comps[i] = std::move(well_comp);
}
std::vector<Opm::data::Connection> well2_comps(6);
for (size_t i = 0; i < 6; ++i) {
Opm::data::Connection well_comp { grid.getGlobalIndex(3,3,i+3) ,r2, 0.0 , 0.0, (double)i, i*0.1,i*0.2, 355.113, 0.9876, 0.0, con_filtrate};
Opm::data::Connection well_comp { grid.getGlobalIndex(3,3,i+3), r2, 0.0 , 0.0, (double)i, i*0.1,i*0.2, 355.113, 0.9876, 0.0, 0.98, con_filtrate};
well2_comps[i] = std::move(well_comp);
}

View File

@ -187,15 +187,15 @@ data::Wells mkWells() {
* input deck. All other entries in the well structures are arbitrary.
*/
Opm::data::ConnectionFiltrate con_filtrate {0.1, 1, 3, 0.4, 1.e-9, 0.2, 0.05, 10.}; // values are not used in this test
w1.connections.push_back( { 88, rc1, 30.45, 123.4, 543.21, 0.62, 0.15, 1.0e3, 1.234, 0.0, con_filtrate } );
w1.connections.push_back( { 288, rc2, 33.19, 123.4, 432.1, 0.26, 0.45, 2.56, 2.345, 0.0, con_filtrate } );
w1.connections.push_back( { 88, rc1, 30.45, 123.4, 543.21, 0.62, 0.15, 1.0e3, 1.234, 0.0, 1.23, con_filtrate } );
w1.connections.push_back( { 288, rc2, 33.19, 123.4, 432.1, 0.26, 0.45, 2.56, 2.345, 0.0, 0.98, con_filtrate } );
w2.rates = r2;
w2.thp = 2.0;
w2.bhp = 2.34;
w2.temperature = 4.56;
w2.control = 2;
w2.connections.push_back( { 188, rc3, 36.22, 123.4, 256.1, 0.55, 0.0125, 314.15, 3.456, 0.0, con_filtrate } );
w2.connections.push_back( { 188, rc3, 36.22, 123.4, 256.1, 0.55, 0.0125, 314.15, 3.456, 0.0, 2.46, con_filtrate } );
{
data::Wells wellRates;

View File

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

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 con_filtrate = {0.1*sm3_pr_day(), 1*sm3(), 3, 0.01*unit::meter, 1.e-3*unit::darcy, 0.2, 0.05*unit::meter, 10.*unit::square(unit::meter)};
data::ConnectionFiltrate w3_con_filtrate = w3_injector ? con_filtrate : zero_filtrate;
data::Connection well1_comp1 { 0 , crates1, 1.9 *unit::barsa, -123.4 *rm3_pr_day(), 314.15, 0.35 , 0.25, 2.718e2, 111.222*cp_rm3_per_db(), 0.0, zero_filtrate};
data::Connection well2_comp1 { 1 , crates2, 1.10*unit::barsa, - 23.4 *rm3_pr_day(), 212.1 , 0.78 , 0.0 , 12.34 , 222.333*cp_rm3_per_db(), 0.0, zero_filtrate};
data::Connection well2_comp2 { 101, crates3, 1.11*unit::barsa, -234.5 *rm3_pr_day(), 150.6 , 0.001, 0.89, 100.0 , 333.444*cp_rm3_per_db(), 0.0, con_filtrate /* output should be zero since it is a producer */};
data::Connection well3_comp1 { 2 , crates3, 1.11*unit::barsa, 432.1 *rm3_pr_day(), 456.78, 0.0 , 0.15, 432.1 , 444.555*cp_rm3_per_db(), 0.0, w3_con_filtrate};
data::Connection well6_comp1 { 77 , crates6, 6.11*unit::barsa, 321.09*rm3_pr_day(), 656.78, 0.0 , 0.65, 632.1 , 555.666*cp_rm3_per_db(), 0.0, zero_filtrate};
data::Connection well1_comp1 { 0 , crates1, 1.9 *unit::barsa, -123.4 *rm3_pr_day(), 314.15, 0.35 , 0.25, 2.718e2, 111.222*cp_rm3_per_db(), 0.0, 1.0, zero_filtrate};
data::Connection well2_comp1 { 1 , crates2, 1.10*unit::barsa, - 23.4 *rm3_pr_day(), 212.1 , 0.78 , 0.0 , 12.34 , 222.333*cp_rm3_per_db(), 0.0, 1.0, zero_filtrate};
data::Connection well2_comp2 { 101, crates3, 1.11*unit::barsa, -234.5 *rm3_pr_day(), 150.6 , 0.001, 0.89, 100.0 , 333.444*cp_rm3_per_db(), 0.0, 1.0, con_filtrate /* output should be zero since it is a producer */};
data::Connection well3_comp1 { 2 , crates3, 1.11*unit::barsa, 432.1 *rm3_pr_day(), 456.78, 0.0 , 0.15, 432.1 , 444.555*cp_rm3_per_db(), 0.0, 1.0, w3_con_filtrate};
data::Connection well6_comp1 { 77 , crates6, 6.11*unit::barsa, 321.09*rm3_pr_day(), 656.78, 0.0 , 0.65, 632.1 , 555.666*cp_rm3_per_db(), 0.0, 1.0, zero_filtrate};
/*
The completions

View File

@ -172,7 +172,7 @@ static data::Wells result_wells() {
input deck.
*/
data::ConnectionFiltrate con_filtrate {0.1, 1, 3, 0.4, 1.e-9, 0.2, 0.05, 10.}; // values are not tested in this test
data::Connection well1_comp1 { 0 , crates1, 1.9 , 123.4, 314.15, 0.35, 0.25, 2.718e2, 0.12345, 0.0, con_filtrate };
data::Connection well1_comp1 { 0, crates1, 1.9, 123.4, 314.15, 0.35, 0.25, 2.718e2, 0.12345, 0.0, 1.23, con_filtrate };
/*
The completions

View File

@ -116,8 +116,8 @@ BOOST_AUTO_TEST_CASE(get_connections) {
* the completion keys (active indices) and well names correspond to the
* input deck. All other entries in the well structures are arbitrary.
*/
w1.connections.push_back( { 88, rc1, 30.45, 123.45, 543.21, 0.123, 0.5, 17.29, 0.1729,0.0,{}} );
w1.connections.push_back( { 288, rc2, 33.19, 67.89, 98.76, 0.5, 0.125, 355.113, 0.355113,0.0, {}} );
w1.connections.push_back( { 88, rc1, 30.45, 123.45, 543.21, 0.123, 0.5, 17.29, 0.1729, 0.0, 1.23, {}} );
w1.connections.push_back( { 288, rc2, 33.19, 67.89, 98.76, 0.5, 0.125, 355.113, 0.355113, 0.0, 0.98, {}} );
{
Json::JsonObject json_data;
@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(get_connections) {
w2.temperature = 4.56;
w2.control = 2;
w2.filtrate = {0.3, 3, 0.4}; // values are not tested in this test
w2.connections.push_back( { 188, rc3, 36.22, 19.28, 28.91, 0.125, 0.125, 3.141, 0.31415, 0.0, {}} );
w2.connections.push_back( { 188, rc3, 36.22, 19.28, 28.91, 0.125, 0.125, 3.141, 0.31415, 0.0, 1.21, {}} );
data::Wells wellRates;
wellRates["OP_1"] = w1;