Compare commits

..

36 Commits

Author SHA1 Message Date
Joakim Hove
2884d02a71 Merge pull request #501 from joakim-hove/komodo-fixup
Add quotes to ensure string evaluation
2018-09-20 13:18:13 +02:00
Joakim Hove
90459cac7e Add quotes to ensure string evaluation 2018-09-19 23:33:54 +02:00
Arne Morten Kvarving
4c6d4ee201 Merge pull request #498 from joakim-hove/komodo
Komodo: cmake config changes
2018-09-19 09:08:53 +02:00
Joakim Hove
906ff3f4b5 Add ability to CMAKE variable DIST_PREFIX before find_package(opm--) 2018-09-19 09:07:05 +02:00
Joakim Hove
238d3ddb58 Move CMAKE_MODULE_PATH manipulations before @OPM_PROJECT_EXTRA_CODE@ 2018-09-17 09:12:29 +02:00
Joakim Hove
42745e0bfc Merge pull request #493 from joakim-hove/default-ecl-compatible
Make ecl compatible restart files default
2018-09-15 11:09:44 +02:00
Bård Skaflestad
4e593da140 Merge pull request #497 from totto82/start2
Write restart when SAVE keyword is in the schedule file
2018-09-14 19:54:25 +02:00
Joakim Hove
662d673c44 Merge pull request #496 from joakim-hove/summary-start-at1
Summary start at1
2018-09-14 17:13:45 +02:00
Tor Harald Sandve
fdc4b0142e Write restart when SAVE keyword is in the schedule file 2018-09-14 13:36:22 +02:00
Arne Morten Kvarving
142994dde6 Merge pull request #491 from akva2/improve_lto
improve lto support
2018-09-14 12:30:16 +02:00
Arne Morten Kvarving
d58001f608 improve lto support
need to use the wrappers for ar and ranlib, or static
libraries are problematic.
2018-09-14 10:29:59 +02:00
Joakim Hove
7dccd13c50 Merge pull request #492 from totto82/minpvfil
fix unit of minpvfil
2018-09-13 19:08:45 +02:00
Joakim Hove
fb5e1229ef Merge pull request #495 from joakim-hove/remove-include
Remove include + use plain C api for test_area
2018-09-13 19:07:37 +02:00
Joakim Hove
d30fc975e3 Merge pull request #444 from lars-petter-hauge/support_ctfac
Support CTFAC
2018-09-13 18:21:24 +02:00
Joakim Hove
36af2aadb5 Will not write summary data for report step == 0 2018-09-13 18:08:57 +02:00
Joakim Hove
cf754c499d White space change 2018-09-13 18:07:37 +02:00
Joakim Hove
9d7ec59e9e Use plain C api for test area 2018-09-13 17:25:33 +02:00
Joakim Hove
eded8bacfc Remove unused include 2018-09-13 17:11:49 +02:00
Lars Petter Øren Hauge
87e3beb601 Support CTFAC in output 2018-09-13 10:23:47 +02:00
Joakim Hove
079d946723 Make ecl compatible restart files default 2018-09-12 18:02:47 +02:00
Joakim Hove
492a240f09 Merge pull request #488 from joakim-hove/opm-formatted-rst
Add opm_formatted property to the IOConfig
2018-09-12 12:57:26 +02:00
Tor Harald Sandve
e6c0ccb5bf fix unit of minpvfil 2018-09-12 11:44:33 +02:00
Joakim Hove
71d92c3dc8 Add opm_formatted property to the IOConfig 2018-09-12 11:34:57 +02:00
Bård Skaflestad
2ea1b7860f Merge pull request #490 from bska/fix-index-oob
Ensure Correctly Sized ACTNUM Vector
2018-09-12 10:09:24 +02:00
Bård Skaflestad
9fd5ce396b Ensure Correctly Sized ACTNUM Vector
Size must be equal to number of global (Cartesian) cells.
2018-09-12 09:46:53 +02:00
Arne Morten Kvarving
bff3c0bff1 Merge pull request #489 from joakim-hove/ignore-extra-kwII
Add option to ignore extra keywords in the regresstion testing
2018-09-11 16:09:53 +02:00
Joakim Hove
7cd29ea7d7 Add option to ignore extra keywords in the regresstion testing 2018-09-11 13:34:51 +02:00
Arne Morten Kvarving
a3c9943a5e Merge pull request #487 from joakim-hove/fix-shared-build
Fix build of tests when using shared library
2018-09-11 13:27:06 +02:00
Joakim Hove
552c390465 Merge pull request #464 from joakim-hove/peaceman-formula
Peaceman formula
2018-09-11 13:18:40 +02:00
Joakim Hove
9f9041dcae Fix build of tests when using shared library 2018-09-10 18:33:47 +02:00
Joakim Hove
c870172a30 Merge pull request #484 from joakim-hove/use-c-smspec
Using plain C api for smspec_nodes
2018-09-10 17:51:23 +02:00
Joakim Hove
4a5b71d8c1 Include Peaceman calculation in Connection class
- The calculation of well connection transmissibility CF and effective
   permeability is calculated.

 - The Connection objects are immutable; should never be updated.

 - The properties of the Connection class are just plain properties, have
   removed getter methods and the use of Value<double>.
2018-09-09 11:16:42 +02:00
Joakim Hove
ff939b44f2 Add testdecks SPE1 and SPE9 2018-09-09 11:16:42 +02:00
Joakim Hove
9e8110e67b Add Peaceman utility functions from opm-simulators 2018-09-09 11:16:42 +02:00
Joakim Hove
5aa0bfadf0 Use plain C api for smspec_nodes 2018-09-07 13:56:04 +02:00
Joakim Hove
e1948ccf99 Add operator[] to GridDims 2018-09-07 10:20:50 +02:00
46 changed files with 8382 additions and 446 deletions

View File

@@ -150,7 +150,7 @@ if(ENABLE_ECL_INPUT)
LIBRARIES ${_libs})
opm_add_test(test_EclFilesComparator CONDITION ENABLE_ECL_INPUT
LIBRARIES ${_libs})
if(HAVE_DYNAMIC_BOOST_TEST AND NOT BUILD_SHARED_LIBS)
if(HAVE_DYNAMIC_BOOST_TEST)
set_target_properties(test_compareSummary PROPERTIES
COMPILE_DEFINITIONS BOOST_TEST_DYN_LINK)
set_target_properties(test_EclFilesComparator PROPERTIES

View File

@@ -266,6 +266,8 @@ if(ENABLE_ECL_OUTPUT)
tests/table_deck.DATA
tests/summary_deck_non_constant_porosity.DATA
tests/SUMMARY_EFF_FAC.DATA
tests/SPE1CASE1.DATA
tests/SPE9_CP_PACKED.DATA
)
endif()

View File

@@ -21,10 +21,28 @@ if (CXX_COMPAT_GCC)
# disabled due to widespread bugs in the linker plugin
option (WHOLE_PROG_OPTIM "Whole program optimization (lto)" OFF)
if (WHOLE_PROG_OPTIM)
check_cxx_accepts_flag ("-flto" HAVE_LINK_OPTS)
if (HAVE_LINK_OPTS)
list (APPEND _opt_flags "-flto")
endif (HAVE_LINK_OPTS)
check_cxx_accepts_flag ("-flto" HAVE_LINK_OPTS)
check_cxx_accepts_flag ("-fuse-linker-plugin" HAVE_LINK_PLUGIN)
if (HAVE_LINK_OPTS)
list (APPEND _opt_flags "-flto")
endif (HAVE_LINK_OPTS)
if (HAVE_LINK_PLUGIN)
list (APPEND _opt_flags "-fuse-linker-plugin")
endif (HAVE_LINK_PLUGIN)
if(HAVE_LINK_OPTS AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
string(REPLACE "." ";" VERSION_LIST "${CMAKE_C_COMPILER_VERSION}")
list(GET VERSION_LIST 0 VER_MAJOR)
find_program(LTO_AR_COMMAND NAMES ${CMAKE_C_COMPILER}-ar gcc-ar-${VER_MAJOR} gcc-ar)
find_program(LTO_RANLIB_COMMAND NAMES ${CMAKE_C_COMPILER}-ranlib gcc-ranlib-${VER_MAJOR} gcc-ranlib)
if(LTO_AR_COMMAND)
set(CMAKE_AR ${LTO_AR_COMMAND})
message(STATUS "Using LTO-enabled ar: ${CMAKE_AR}")
endif()
if(LTO_RANLIB_COMMAND)
set(CMAKE_RANLIB ${LTO_RANLIB_COMMAND})
message(STATUS "Using LTO-enabled ranlib: ${CMAKE_RANLIB}")
endif()
endif()
endif (WHOLE_PROG_OPTIM)
# native instruction set tuning

View File

@@ -29,11 +29,34 @@ if(NOT @opm-project_NAME@_FOUND)
set (@opm-project_NAME@_LIBRARY_DIRS "@opm-project_LIBRARY_DIRS@" "@CMAKE_LIBRARY_OUTPUT_DIRECTORY@")
set (@opm-project_NAME@_LINKER_FLAGS "@opm-project_LINKER_FLAGS@")
set (@opm-project_NAME@_CONFIG_VARS "@opm-project_CONFIG_VARS@")
set (HAVE_@opm-project_NAME_UC@ 1)
# libraries come from the build tree where this file was generated
set (@opm-project_NAME@_LIBRARY "@opm-project_LIBRARY@")
set (@opm-project_NAME@_LIBRARIES ${@opm-project_NAME@_LIBRARY} "@opm-project_LIBRARIES@")
# The purpose of this string replacement operation is to enable use of the
# generated opm-project-config.cmake file also in the situation where 'make
# install' has been invoked with the DESTDIR option:
#
# opm-common/build> cmake .. -DCMAKE_INSTALL_PREFIX=/real/prefix
# opm-common/budil> make install DESTDIR=/tmp/prefix
#
# downstream/build> cmake .. -DDEST_PREFIX=/tmp/prefix -DCMAKE_PREFIX_PATH=/tmp/prefix
# downstream/build> make install
#
# That way the downstream dependency can still use find_package( opm-common )
# even though the opm-common-config.cmake file is not internally consistent
# with it's own location in the filesystem.
if(DEST_PREFIX)
set(DEST_PREFIX "${DEST_PREFIX}${@opm-project_NAME@_PREFIX}")
string(REPLACE ${@opm-project_NAME@_PREFIX} ${DEST_PREFIX} @opm-project_NAME@_INCLUDE_DIRS "${@opm-project_NAME@_INCLUDE_DIRS}")
string(REPLACE ${@opm-project_NAME@_PREFIX} ${DEST_PREFIX} @opm-project_NAME@_LIBRARY_DIRS "${@opm-project_NAME@_LIBRARY_DIRS}")
string(REPLACE ${@opm-project_NAME@_PREFIX} ${DEST_PREFIX} @opm-project_NAME@_LIBRARY "${@opm-project_NAME@_LIBRARY}")
endif()
set (HAVE_@opm-project_NAME_UC@ 1)
mark_as_advanced (@opm-project_NAME@_LIBRARY)
# not all projects have targets; conditionally add this part
@@ -71,12 +94,17 @@ if(NOT @opm-project_NAME@_FOUND)
# this is the contents of config.h as far as our probes can tell:
# extra code from variable OPM_PROJECT_EXTRA_CODE
@OPM_PROJECT_EXTRA_CODE@
# end extra code
# This call is at the bottom as we need to include
# the OpmPackage Macros.
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" @PREREQ_LOCATION@)
include(@opm-project_NAME@-prereqs)
# The settings in this block do not mix well with the DEST_PREFIX
# setting.
if (NOT DEST_PREFIX)
# This is required to include OpmPackage
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" @PREREQ_LOCATION@)
# extra code from variable OPM_PROJECT_EXTRA_CODE
@OPM_PROJECT_EXTRA_CODE@
# end extra code
include(@opm-project_NAME@-prereqs)
endif()
endif()

View File

@@ -231,20 +231,23 @@ void ECLRegressionTest::gridCompare(const bool volumecheck) const {
void ECLRegressionTest::results() {
if (keywords1.size() != keywords2.size()) {
std::set<std::string> keys(keywords1.begin() , keywords1.end());
for (const auto& key2: keywords2)
keys.insert( key2 );
if (!this->acceptExtraKeywords) {
if (keywords1.size() != keywords2.size()) {
std::set<std::string> keys(keywords1.begin() , keywords1.end());
for (const auto& key2: keywords2)
keys.insert( key2 );
for (const auto& key : keys)
fprintf(stderr," %8s:%3d %8s:%3d \n",key.c_str() , ecl_file_get_num_named_kw( ecl_file1 , key.c_str()),
key.c_str() , ecl_file_get_num_named_kw( ecl_file2 , key.c_str()));
for (const auto& key : keys)
fprintf(stderr," %8s:%3d %8s:%3d \n",key.c_str() , ecl_file_get_num_named_kw( ecl_file1 , key.c_str()),
key.c_str() , ecl_file_get_num_named_kw( ecl_file2 , key.c_str()));
OPM_THROW(std::runtime_error, "\nKeywords in first file: " << keywords1.size()
<< "\nKeywords in second file: " << keywords2.size()
<< "\nThe number of keywords differ.");
OPM_THROW(std::runtime_error, "\nKeywords in first file: " << keywords1.size()
<< "\nKeywords in second file: " << keywords2.size()
<< "\nThe number of keywords differ.");
}
}
for (const auto& it : keywords1)
resultsForKeyword(it);

View File

@@ -40,6 +40,10 @@ class ECLRegressionTest: public ECLFilesComparator {
// Only compare last occurrence
bool onlyLastOccurrence = false;
// Accept extra keywords in the restart file of the 'new' simulation.
bool acceptExtraKeywords = false;
// Prints results stored in absDeviation and relDeviation.
void printResultsForKeyword(const std::string& keyword) const;
@@ -69,6 +73,11 @@ class ECLRegressionTest: public ECLFilesComparator {
//! \brief Option to only compare last occurrence
void setOnlyLastOccurrence(bool onlyLastOccurrenceArg) {this->onlyLastOccurrence = onlyLastOccurrenceArg;}
// Accept extra keywords: If this switch is set to true the comparison
// of restart files will ignore extra keywords which are only present
// in the new simulation.
void setAcceptExtraKeywords(bool acceptExtraKeywords) { this->acceptExtraKeywords = acceptExtraKeywords; }
//! \brief Compares grid properties of the two cases.
// gridCompare() checks if both the number of active and global cells in the two cases are the same. If they are, and volumecheck is true, all cells are looped over to calculate the cell volume deviation for the two cases. If the both the relative and absolute deviation exceeds the tolerances, an exception is thrown.
void gridCompare(const bool volumecheck) const;

View File

@@ -132,6 +132,7 @@ int main(int argc, char** argv) {
bool allowSpikes = false;
bool throwOnError = true;
bool throwTooGreatErrorRatio = true;
bool acceptExtraKeywords = false;
bool analysis = false;
bool volumecheck = true;
char* keyword = nullptr;
@@ -140,7 +141,7 @@ int main(int argc, char** argv) {
int c = 0;
int spikeLimit = -1;
while ((c = getopt(argc, argv, "hiIk:alnpPt:VRgs:m:vK")) != -1) {
while ((c = getopt(argc, argv, "hiIk:alnpPt:VRgs:m:vKx")) != -1) {
switch (c) {
case 'a':
analysis = true;
@@ -198,6 +199,9 @@ int main(int argc, char** argv) {
case 'V':
volumecheck = false;
break;
case 'x':
acceptExtraKeywords = true;
break;
case '?':
if (optopt == 'k' || optopt == 'm' || optopt == 's') {
std::cerr << "Option " << optopt << " requires a keyword as argument, see manual (-h) for more information." << std::endl;
@@ -344,6 +348,7 @@ int main(int argc, char** argv) {
ECLRegressionTest comparator(file_type, basename1, basename2, absTolerance, relTolerance);
comparator.throwOnErrors(throwOnError);
comparator.doAnalysis(analysis);
comparator.setAcceptExtraKeywords(acceptExtraKeywords);
if (printKeywords) {
comparator.printKeywords();
return 0;

View File

@@ -25,7 +25,6 @@
#include <unordered_map>
#include <ert/ecl/ecl_sum.h>
#include <ert/ecl/Smspec.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>

View File

@@ -42,6 +42,7 @@ namespace Opm {
size_t getNY() const;
size_t getNZ() const;
size_t operator[](int dim) const;
const std::array<int, 3> getNXYZ() const;

View File

@@ -105,6 +105,41 @@ namespace Opm {
------ Default ------
If no keywords for config of writing restart files have been handled; no restart files are written.
ECL compatible restart
======================
Unfortunately flow & eclipse are not compatible across restarts. The
RestartIO implementation can write restart files for flow -> flow restart
or alternatively for flow -> eclipse restart. This is regulated by the
boolean flag ecl_compatible_restart in the IOConfig class. The difference
between the two are as follows:
ecl_compatible_restart = false:
1. The 'extra' fields in the RestartValue structure are actually
written to disk.
2. You can optionally ask the RestartIO::save() function to save the
solution in double precision.
3. The RestartIO::save() function will save opm specific vector OPM_IWEL
and OPM_XWEL.
ecl_compatible_restart = true:
1. The 'extra' fields in the RestartValue are silently ignored.
2. If request double precision solution data that is silently ignored,
it will be float.
3. The OPM_IWEL and OPM_XWEL vectors will not be written.
Observe that the solution data in the RestartValue is passed
unconditionally to the solution section in the restart file, so if you
pass a field in the solution section which Eclipse does not recognize you
will end up with a restart file which Eclipse can not read, even if you
have set ecl_compatible_restart to true.
*/
@@ -117,7 +152,8 @@ namespace Opm {
explicit IOConfig( const Deck& );
explicit IOConfig( const std::string& input_path );
void setEclCompatibleRST(bool ecl_rst);
bool getEclCompatibleRST() const;
bool getWriteEGRIDFile() const;
bool getWriteINITFile() const;
bool getUNIFOUT() const;
@@ -165,6 +201,7 @@ namespace Opm {
std::string m_output_dir;
std::string m_base_name;
bool m_nosim;
bool ecl_compatible_rst = true;
IOConfig( const GRIDSection&,
const RUNSPECSection&,

View File

@@ -362,6 +362,7 @@ namespace Opm {
DynamicState< RestartSchedule > restart_schedule;
DynamicState< std::map< std::string, int > > restart_keywords;
std::vector< bool > save_keywords;
};
} //namespace Opm

View File

@@ -41,50 +41,57 @@ namespace Opm {
Connection(int i, int j , int k ,
int complnum,
double depth,
WellCompletion::StateEnum state ,
const Value<double>& connectionTransmissibilityFactor,
const Value<double>& diameter,
const Value<double>& skinFactor,
const Value<double>& Kh,
WellCompletion::StateEnum state,
double CF,
double Kh,
double rw,
const int satTableId,
const WellCompletion::DirectionEnum direction);
bool attachedToSegment() const;
bool sameCoordinate(const int i, const int j, const int k) const;
int getI() const;
int getJ() const;
int getK() const;
double getConnectionTransmissibilityFactor() const;
double getDiameter() const;
double getSkinFactor() const;
bool attachedToSegment() const;
const Value<double>& getConnectionTransmissibilityFactorAsValueObject() const;
const Value<double>& getEffectiveKhAsValueObject() const;
WellCompletion::StateEnum state() const;
WellCompletion::DirectionEnum dir() const;
double depth() const;
int satTableId() const;
int complnum() const;
int segment() const;
double CF() const;
double Kh() const;
double rw() const;
double wellPi() const;
void setState(WellCompletion::StateEnum state);
void setComplnum(int compnum);
void scaleWellPi(double wellPi);
void updateSegment(int segment_number, double center_depth);
bool operator==( const Connection& ) const;
bool operator!=( const Connection& ) const;
WellCompletion::DirectionEnum dir;
double center_depth;
WellCompletion::StateEnum state;
int sat_tableId;
int complnum;
private:
std::array<int,3> ijk;
Value<double> m_diameter;
Value<double> m_connectionTransmissibilityFactor;
Value<double> m_skinFactor;
Value<double> m_Kh;
WellCompletion::DirectionEnum direction;
double center_depth;
WellCompletion::StateEnum open_state;
int sat_tableId;
int m_complnum;
double m_CF;
double m_Kh;
double m_rw;
std::array<int,3> ijk;
public:
// related segment number
// -1 means the completion is not related to segment
int segment_number = -1;
double wellPi = 1.0;
double wPi = 1.0;
};
}
#endif /* COMPLETION_HPP_ */

View File

@@ -35,10 +35,9 @@ namespace Opm {
void addConnection(int i, int j , int k ,
double depth,
WellCompletion::StateEnum state ,
const Value<double>& connectionTransmissibilityFactor,
const Value<double>& diameter,
const Value<double>& skinFactor,
const Value<double>& Kh,
double CF,
double Kh,
double rw,
const int satTableId,
const WellCompletion::DirectionEnum direction = WellCompletion::DirectionEnum::Z);
void loadCOMPDAT(const DeckRecord& record, const EclipseGrid& grid, const Eclipse3DProperties& eclipseProperties);
@@ -47,6 +46,7 @@ namespace Opm {
void add( Connection );
size_t size() const;
const Connection& operator[](size_t index) const;
const Connection& get(size_t index) const;
const Connection& getFromIJK(const int i, const int j, const int k) const;
Connection& getFromIJK(const int i, const int j, const int k);
@@ -77,10 +77,9 @@ namespace Opm {
int complnum,
double depth,
WellCompletion::StateEnum state ,
const Value<double>& connectionTransmissibilityFactor,
const Value<double>& diameter,
const Value<double>& skinFactor,
const Value<double>& Kh,
double CF,
double Kh,
double rw,
const int satTableId,
const WellCompletion::DirectionEnum direction = WellCompletion::DirectionEnum::Z);

View File

@@ -24,10 +24,59 @@
#include <vector>
#include <set>
#include <ert/ecl/Smspec.hpp>
#include <ert/ecl/smspec_node.h>
namespace Opm {
/*
Very small utility class to get value semantics on the smspec_node
pointers. This should die as soon as the smspec_node class proper gets
value semantics.
*/
class SummaryNode {
public:
SummaryNode(smspec_node_type * c_ptr) :
ptr(c_ptr)
{}
SummaryNode(const SummaryNode& other) :
ptr( smspec_node_alloc_copy(other.get()))
{}
const smspec_node_type * get() const {
return this->ptr;
}
std::string wgname() const {
return smspec_node_get_wgname(this->ptr);
}
std::string keyword() const {
return smspec_node_get_keyword(this->ptr);
}
int num() const {
return smspec_node_get_num(this->ptr);
}
ecl_smspec_var_type type() const {
return smspec_node_get_var_type(this->ptr);
}
SummaryNode& operator=(const SummaryNode &other) {
this->ptr = smspec_node_alloc_copy(other.ptr);
return *this;
}
~SummaryNode() {
smspec_node_free(this->ptr);
}
private:
smspec_node_type * ptr;
};
class Deck;
class TableManager;
class EclipseState;
@@ -38,7 +87,9 @@ namespace Opm {
class SummaryConfig {
public:
typedef std::vector< ERT::smspec_node >::const_iterator const_iterator;
typedef SummaryNode keyword_type;
typedef std::vector< keyword_type > keyword_list;
typedef keyword_list::const_iterator const_iterator;
SummaryConfig( const Deck&, const Schedule&,
const TableManager&, const ParseContext&);
@@ -80,7 +131,7 @@ namespace Opm {
part, e.g. "WWCT", and not the qualification with
well/group name or a numerical value.
*/
std::vector< ERT::smspec_node > keywords;
keyword_list keywords;
std::set<std::string> short_keywords;
std::set<std::string> summary_keywords;
};

View File

@@ -354,7 +354,7 @@ void EclipseIO::Impl::writeINITFile( const data::Solution& simProps, std::map<st
const std::string& key = pair.first;
const std::vector<int>& int_vector = pair.second;
if (key.size() > ECL_STRING8_LENGTH)
throw std::invalid_argument("Keyword is too long.");
throw std::invalid_argument("Keyword is too long.");
writeKeyword( fortio , key , int_vector );
}
@@ -393,8 +393,8 @@ void EclipseIO::Impl::writeEGRIDFile( const NNC& nnc ) const {
/*
int_data: Writes key(string) and integers vector to INIT file as eclipse keywords
- Key: Max 8 chars.
- Wrong input: invalid_argument exception.
- Key: Max 8 chars.
- Wrong input: invalid_argument exception.
*/
void EclipseIO::writeInitial( data::Solution simProps, std::map<std::string, std::vector<int> > int_data, const NNC& nnc) {
if( !this->impl->output_enabled )
@@ -439,9 +439,10 @@ void EclipseIO::writeTimeStep(int report_step,
/*
Summary data is written unconditionally for every timestep.
Summary data is written unconditionally for every timestep except for the
very intial report_step==0 call, which is only garbage.
*/
{
if (report_step > 0) {
this->impl->summary.add_timestep( report_step,
secs_elapsed,
es,

View File

@@ -206,7 +206,7 @@ data::Wells restore_wells( const ecl_kw_type * opm_xwel,
for( const auto& sc : sched_well->getConnections( sim_step ) ) {
const auto i = sc.getI(), j = sc.getJ(), k = sc.getK();
if( !grid.cellActive( i, j, k ) || sc.state == WellCompletion::SHUT ) {
if( !grid.cellActive( i, j, k ) || sc.state() == WellCompletion::SHUT ) {
opm_xwel_data += data::Connection::restart_size + phases.size();
continue;
}
@@ -253,6 +253,10 @@ RestartValue load( const std::string& filename,
} else
file_view = ecl_file_get_global_view( file.get() );
if (!ecl_file_view_has_kw(file_view, "OPM_XWEL"))
throw std::runtime_error("Sorry - this file is missing the OPM_XWEL keyword - required for flow based restart\n");
const ecl_kw_type * intehead = ecl_file_view_iget_named_kw( file_view , "INTEHEAD", 0 );
const ecl_kw_type * opm_xwel = ecl_file_view_iget_named_kw( file_view , "OPM_XWEL", 0 );
const ecl_kw_type * opm_iwel = ecl_file_view_iget_named_kw( file_view, "OPM_IWEL", 0 );
@@ -310,10 +314,10 @@ std::vector<int> serialize_ICON( int sim_step,
data[ offset + ICON_I_INDEX ] = connection.getI() + 1;
data[ offset + ICON_J_INDEX ] = connection.getJ() + 1;
data[ offset + ICON_K_INDEX ] = connection.getK() + 1;
data[ offset + ICON_DIRECTION_INDEX ] = connection.dir;
data[ offset + ICON_DIRECTION_INDEX ] = connection.dir();
{
const auto open = WellCompletion::StateEnum::OPEN;
data[ offset + ICON_STATUS_INDEX ] = connection.state == open
data[ offset + ICON_STATUS_INDEX ] = connection.state() == open
? 1
: 0;
}
@@ -408,7 +412,7 @@ std::vector< double > serialize_OPM_XWEL( const data::Wells& wells,
const auto i = sc.getI(), j = sc.getJ(), k = sc.getK();
const auto rs_size = phases.size() + data::Connection::restart_size;
if( !grid.cellActive( i, j, k ) || sc.state == WellCompletion::SHUT ) {
if( !grid.cellActive( i, j, k ) || sc.state() == WellCompletion::SHUT ) {
xwel.insert( xwel.end(), rs_size, 0.0 );
continue;
}
@@ -554,16 +558,20 @@ void writeWell(ecl_rst_file_type* rst_file, int sim_step, const EclipseState& es
const auto& phases = es.runspec().phases();
const size_t ncwmax = schedule.getMaxNumConnectionsForWells(sim_step);
const auto opm_xwel = serialize_OPM_XWEL( wells, sim_step, sched_wells, phases, grid );
const auto opm_iwel = serialize_OPM_IWEL( wells, sched_wells );
const auto iwel_data = serialize_IWEL(sim_step, sched_wells, grid);
const auto icon_data = serialize_ICON(sim_step , ncwmax, sched_wells, grid);
const auto zwel_data = serialize_ZWEL( sched_wells );
write_kw( rst_file, ERT::EclKW< int >( IWEL_KW, iwel_data) );
write_kw( rst_file, ERT::EclKW< const char* >(ZWEL_KW, zwel_data ) );
write_kw( rst_file, ERT::EclKW< double >( OPM_XWEL, opm_xwel ) );
write_kw( rst_file, ERT::EclKW< int >( OPM_IWEL, opm_iwel ) );
if (!es.getIOConfig().getEclCompatibleRST()) {
const auto opm_xwel = serialize_OPM_XWEL( wells, sim_step, sched_wells, phases, grid );
const auto opm_iwel = serialize_OPM_IWEL( wells, sched_wells );
write_kw( rst_file, ERT::EclKW< double >( OPM_XWEL, opm_xwel ) );
write_kw( rst_file, ERT::EclKW< int >( OPM_IWEL, opm_iwel ) );
}
write_kw( rst_file, ERT::EclKW< int >( ICON_KW, icon_data ) );
}
@@ -605,6 +613,7 @@ void save(const std::string& filename,
{
checkSaveArguments(es, value, grid);
{
bool ecl_compatible_rst = es.getIOConfig().getEclCompatibleRST();
int sim_step = std::max(report_step - 1, 0);
int ert_phase_mask = es.runspec().eclPhaseMask( );
const auto& units = es.getUnits();
@@ -617,6 +626,9 @@ void save(const std::string& filename,
else
rst_file.reset( ecl_rst_file_open_write( filename.c_str() ) );
if (ecl_compatible_rst)
write_double = false;
// Convert solution fields and extra values from SI to user units.
value.solution.convertFromSI(units);
for (auto & extra_value : value.extra) {
@@ -628,8 +640,9 @@ void save(const std::string& filename,
writeHeader( rst_file.get(), sim_step, report_step, posix_time , sim_time, ert_phase_mask, units, schedule , grid );
writeWell( rst_file.get(), sim_step, es , grid, schedule, value.wells);
writeSolution( rst_file.get(), value, write_double );
writeExtraData( rst_file.get(), value.extra );
writeSolution( rst_file.get(), value, write_double);
if (!ecl_compatible_rst)
writeExtraData( rst_file.get(), value.extra );
}
}
}

View File

@@ -41,6 +41,7 @@
#include <opm/output/eclipse/Summary.hpp>
#include <opm/output/eclipse/RegionCache.hpp>
#include <ert/ecl/smspec_node.h>
#include <ert/ecl/ecl_smspec.h>
#include <ert/ecl/ecl_kw_magic.h>
@@ -362,6 +363,34 @@ inline quantity crate( const fn_args& args ) {
return { v, rate_unit< phase >() };
}
inline quantity trans_factors ( const fn_args& args ) {
const quantity zero = { 0, measure::transmissibility };
if( args.schedule_wells.empty() ) return zero;
// Like completion rate we need to look
// up a connection with offset 0.
const size_t global_index = args.num - 1;
const auto& well = args.schedule_wells.front();
const auto& name = well->name();
if( args.wells.count( name ) == 0 ) return zero;
const auto& grid = args.grid;
const auto& connections = well->getConnections(args.sim_step);
const auto& connection = std::find_if(
connections.begin(),
connections.end(),
[=]( const Opm::Connection& c ) {
return grid.getGlobalIndex(c.getI(), c.getJ(), c.getK()) == global_index;
} );
if( connection == connections.end() ) return zero;
const auto& v = connection->CF();
return { v, measure::transmissibility };
}
inline quantity bhp( const fn_args& args ) {
const quantity zero = { 0, measure::pressure };
if( args.schedule_wells.empty() ) return zero;
@@ -709,6 +738,7 @@ static const std::unordered_map< std::string, ofun > funs = {
{ "CGPT", mul( crate< rt::gas, producer >, duration ) },
{ "CNPT", mul( crate< rt::solvent, producer >, duration ) },
{ "CCIT", mul( crate< rt::wat, injector, polymer >, duration ) },
{ "CTFAC", trans_factors },
{ "FWPR", rate< rt::wat, producer > },
{ "FOPR", rate< rt::oil, producer > },
@@ -971,7 +1001,7 @@ Summary::Summary( const EclipseState& st,
std::set< std::string > unsupported_keywords;
for( const auto& node : sum ) {
const auto* keyword = node.keyword();
const auto* keyword = smspec_node_get_keyword(node.get());
const auto single_value_pair = single_values_units.find( keyword );
const auto funs_pair = funs.find( keyword );
@@ -985,15 +1015,15 @@ Summary::Summary( const EclipseState& st,
add_timestep.
*/
if (single_value_pair != single_values_units.end()) {
if ((node.type() != ECL_SMSPEC_FIELD_VAR) && (node.type() != ECL_SMSPEC_MISC_VAR)) {
auto node_type = smspec_node_get_var_type(node.get());
if ((node_type != ECL_SMSPEC_FIELD_VAR) && (node_type != ECL_SMSPEC_MISC_VAR)) {
continue;
}
auto* nodeptr = ecl_sum_add_var( this->ecl_sum.get(),
keyword,
node.wgname(),
node.num(),
smspec_node_get_wgname(node.get()),
smspec_node_get_num(node.get()),
st.getUnits().name( single_value_pair->second ),
0 );
@@ -1002,36 +1032,37 @@ Summary::Summary( const EclipseState& st,
auto* nodeptr = ecl_sum_add_var( this->ecl_sum.get(),
keyword,
node.wgname(),
node.num(),
smspec_node_get_wgname(node.get()),
smspec_node_get_num(node.get()),
st.getUnits().name( region_pair->second ),
0 );
this->handlers->region_nodes.emplace( std::make_pair(keyword, node.num()), nodeptr );
this->handlers->region_nodes.emplace( std::make_pair(keyword, smspec_node_get_num(node.get())), nodeptr );
} else if (block_pair != block_units.end()) {
if (node.type() != ECL_SMSPEC_BLOCK_VAR)
if (smspec_node_get_var_type(node.get()) != ECL_SMSPEC_BLOCK_VAR)
continue;
int global_index = node.num() - 1;
int global_index = smspec_node_get_num(node.get()) - 1;
if (!this->grid.cellActive(global_index))
continue;
auto* nodeptr = ecl_sum_add_var( this->ecl_sum.get(),
keyword,
node.wgname(),
node.num(),
smspec_node_get_wgname(node.get()),
smspec_node_get_num(node.get()),
st.getUnits().name( block_pair->second ),
0 );
this->handlers->block_nodes.emplace( std::make_pair(keyword, node.num()), nodeptr );
this->handlers->block_nodes.emplace( std::make_pair(keyword, smspec_node_get_num(node.get())), nodeptr );
} else if (funs_pair != funs.end()) {
auto node_type = smspec_node_get_var_type(node.get());
if ((node.type() == ECL_SMSPEC_COMPLETION_VAR) || (node.type() == ECL_SMSPEC_BLOCK_VAR)) {
int global_index = node.num() - 1;
if ((node_type == ECL_SMSPEC_COMPLETION_VAR) || (node_type == ECL_SMSPEC_BLOCK_VAR)) {
int global_index = smspec_node_get_num(node.get()) - 1;
if (!this->grid.cellActive(global_index))
continue;
}
@@ -1043,7 +1074,7 @@ Summary::Summary( const EclipseState& st,
const fn_args no_args { dummy_wells, // Wells from Schedule object
0, // Duration of time step
0, // Simulation step
node.num(), // NUMS value for the summary output.
smspec_node_get_num(node.get()), // NUMS value for the summary output.
{}, // Well results - data::Wells
{}, // Region <-> cell mappings.
this->grid,
@@ -1053,8 +1084,8 @@ Summary::Summary( const EclipseState& st,
auto* nodeptr = ecl_sum_add_var( this->ecl_sum.get(),
keyword,
node.wgname(),
node.num(),
smspec_node_get_wgname(node.get()),
smspec_node_get_num(node.get()),
st.getUnits().name( val.unit ),
0 );

View File

@@ -42,29 +42,8 @@ serialize_SCON(int lookup_step,
bool explicit_ctf_not_found = false;
for (const auto& connection : connections) {
const size_t offset = well_offset + connection_offset;
const auto& ctf = connection.getConnectionTransmissibilityFactorAsValueObject();
if (ctf.hasValue()) {
// CTF explicitly set in deck, overrides calculation
// from Peaceman model. We should also give the Kh
// factor, we output an explicitly invalid value
// instead. This is acceptable since it will not be
// used (the explicit CTF factor is used instead).
const double ctf_SI = ctf.getValue();
const double ctf_output = units.from_si(UnitSystem::measure::transmissibility, ctf_SI);
data[ offset + SCON_CF_INDEX ] = ctf_output;
data[ offset + SCON_KH_INDEX ] = UNIMPLEMENTED_VALUE;
} else {
// CTF not set in deck, Peaceman formula used to
// compute it. Here we should store the data for the
// connection required to recalculate the CTF (the Kh
// factor), as well as the actual CTF used by the
// simulator, but that requires access to more data
// from the simulator. As an interim measure we write
// invalid values and give a warning.
data[ offset + SCON_CF_INDEX ] = UNIMPLEMENTED_VALUE;
data[ offset + SCON_KH_INDEX ] = UNIMPLEMENTED_VALUE;
explicit_ctf_not_found = true;
}
data[ offset + SCON_CF_INDEX ] = units.from_si(Opm::UnitSystem::measure::transmissibility,connection.CF());
data[ offset + SCON_KH_INDEX ] = units.from_si(Opm::UnitSystem::measure::effective_Kh, connection.Kh());
connection_offset += nsconz;
}
if (explicit_ctf_not_found) {
@@ -73,7 +52,7 @@ serialize_SCON(int lookup_step,
}
well_offset += well_field_size;
}
return data;
return data;
}
// ----------------------------------------------------------------------------
@@ -83,7 +62,7 @@ serialize_ICON(int lookup_step,
int ncwmax,
int niconz,
const std::vector<const Opm::Well*>& sched_wells)
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
{
const size_t well_field_size = ncwmax * niconz;
std::vector<int> data(sched_wells.size() * well_field_size, 0);
@@ -94,17 +73,17 @@ serialize_ICON(int lookup_step,
for (const auto& connection : connections) {
const size_t offset = well_offset + connection_offset;
data[ offset + ICON_IC_INDEX ] = connection.complnum;
data[ offset + ICON_IC_INDEX ] = connection.complnum();
data[ offset + ICON_I_INDEX ] = connection.getI() + 1;
data[ offset + ICON_J_INDEX ] = connection.getJ() + 1;
data[ offset + ICON_K_INDEX ] = connection.getK() + 1;
data[ offset + ICON_DIRECTION_INDEX ] = connection.dir;
data[ offset + ICON_DIRECTION_INDEX ] = connection.dir();
data[ offset + ICON_STATUS_INDEX ] =
(connection.state == WellCompletion::StateEnum::OPEN) ?
(connection.state() == WellCompletion::StateEnum::OPEN) ?
1 : -1000;
data[ offset + ICON_SEGMENT_INDEX ] =
connection.attachedToSegment() ?
connection.segment_number : 0;
connection.segment() : 0;
connection_offset += niconz;
}

View File

@@ -62,6 +62,22 @@ namespace Opm {
return m_nz;
}
size_t GridDims::operator[](int dim) const {
switch (dim) {
case 0:
return this->m_nx;
break;
case 1:
return this->m_ny;
break;
case 2:
return this->m_nz;
break;
default:
throw std::invalid_argument("Invalid argument dim:" + std::to_string(dim));
}
}
const std::array<int, 3> GridDims::getNXYZ() const {
return std::array<int, 3> {{int( m_nx ), int( m_ny ), int( m_nz )}};
}
@@ -116,4 +132,5 @@ namespace Opm {
m_ny = dims[1];
m_nz = dims[2];
}
}

View File

@@ -122,6 +122,14 @@ namespace Opm {
}
bool IOConfig::getEclCompatibleRST() const {
return this->ecl_compatible_rst;
}
void IOConfig::setEclCompatibleRST(bool ecl_rst) {
this->ecl_compatible_rst = ecl_rst;
}
void IOConfig::overrideNOSIM(bool nosim) {

View File

@@ -397,6 +397,12 @@ void RestartConfig::handleScheduleSection(const SCHEDULESection& schedule) {
continue;
}
if (name == "SAVE") {
this->save_keywords[current_step] = true;
} else {
this->save_keywords[current_step]= false;
}
if( !( name == "RPTRST" || name == "RPTSCHED" ) ) continue;
if( this->m_timemap.size() <= current_step ) continue;
@@ -476,7 +482,8 @@ void RestartConfig::handleScheduleSection(const SCHEDULESection& schedule) {
m_timemap( std::move( timemap ) ),
m_first_restart_step( -1 ),
restart_schedule( m_timemap, { 0, 0, 1 } ),
restart_keywords( m_timemap, {} )
restart_keywords( m_timemap, {} ),
save_keywords( m_timemap.numTimesteps(), false )
{
handleSolutionSection( solution );
handleScheduleSection( schedule );
@@ -494,6 +501,12 @@ void RestartConfig::handleScheduleSection(const SCHEDULESection& schedule) {
if (0 == timestep)
return m_write_initial_RST_file;
if (save_keywords[timestep]) {
std::string logstring = "Fast restart using SAVE is not supported. Standard restart file is written instead";
Opm::OpmLog::warning("Unhandled output keyword", logstring);
return true;
}
{
RestartSchedule ts_restart_config = getNode( timestep );
return ts_restart_config.writeRestartFile( timestep , m_timemap );

View File

@@ -20,7 +20,6 @@
#include <algorithm>
#include <cassert>
#include <vector>
#include <iostream>
#include <opm/parser/eclipse/Deck/DeckItem.hpp>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
@@ -36,26 +35,25 @@
namespace Opm {
Connection::Connection(int i, int j , int k ,
int compnum,
double depth,
WellCompletion::StateEnum stateArg ,
const Value<double>& connectionTransmissibilityFactor,
const Value<double>& diameter,
const Value<double>& skinFactor,
const Value<double>& Kh,
double CF,
double Kh,
double rw,
const int satTableId,
const WellCompletion::DirectionEnum direction)
: dir(direction),
: direction(direction),
center_depth(depth),
state(stateArg),
open_state(stateArg),
sat_tableId(satTableId),
complnum( compnum ),
ijk({i,j,k}),
m_diameter(diameter),
m_connectionTransmissibilityFactor(connectionTransmissibilityFactor),
m_skinFactor(skinFactor),
m_Kh(Kh)
m_complnum( compnum ),
m_CF(CF),
m_Kh(Kh),
m_rw(rw),
ijk({i,j,k})
{}
bool Connection::sameCoordinate(const int i, const int j, const int k) const {
@@ -80,42 +78,77 @@ namespace Opm {
return ijk[2];
}
double Connection::getConnectionTransmissibilityFactor() const {
return m_connectionTransmissibilityFactor.getValue();
}
double Connection::getDiameter() const {
return m_diameter.getValue();
}
double Connection::getSkinFactor() const {
return m_skinFactor.getValue();
}
const Value<double>& Connection::getConnectionTransmissibilityFactorAsValueObject() const {
return m_connectionTransmissibilityFactor;
}
const Value<double>& Connection::getEffectiveKhAsValueObject() const {
return m_Kh;
}
bool Connection::attachedToSegment() const {
return (segment_number > 0);
}
WellCompletion::DirectionEnum Connection::dir() const {
return this->direction;
}
double Connection::depth() const {
return this->center_depth;
}
WellCompletion::StateEnum Connection::state() const {
return this->open_state;
}
int Connection::satTableId() const {
return this->sat_tableId;
}
int Connection::complnum() const {
return this->m_complnum;
}
void Connection::setComplnum(int complnum) {
this->m_complnum = complnum;
}
double Connection::CF() const {
return this->m_CF;
}
double Connection::Kh() const {
return this->m_Kh;
}
double Connection::rw() const {
return this->m_rw;
}
void Connection::setState(WellCompletion::StateEnum state) {
this->open_state = state;
}
void Connection::updateSegment(int segment_number, double center_depth) {
this->segment_number = segment_number;
this->center_depth = center_depth;
}
int Connection::segment() const {
return this->segment_number;
}
void Connection::scaleWellPi(double wellPi) {
this->wPi *= wellPi;
}
double Connection::wellPi() const {
return this->wPi;
}
bool Connection::operator==( const Connection& rhs ) const {
return this->ijk == rhs.ijk
&& this->complnum == rhs.complnum
&& this->m_diameter == rhs.m_diameter
&& this->m_connectionTransmissibilityFactor == rhs.m_connectionTransmissibilityFactor
&& this->wellPi == rhs.wellPi
&& this->m_skinFactor == rhs.m_skinFactor
&& this->m_complnum == rhs.m_complnum
&& this->m_CF == rhs.m_CF
&& this->m_rw == rhs.m_rw
&& this->wPi == rhs.wPi
&& this->m_Kh == rhs.m_Kh
&& this->sat_tableId == rhs.sat_tableId
&& this->state == rhs.state
&& this->dir == rhs.dir
&& this->open_state == rhs.open_state
&& this->direction == rhs.direction
&& this->segment_number == rhs.segment_number
&& this->center_depth == rhs.center_depth;
}

View File

@@ -239,8 +239,7 @@ namespace Opm {
const int k = compseg.m_k;
Connection& connection = connection_set.getFromIJK( i, j, k );
connection.segment_number = compseg.segment_number;
connection.center_depth = compseg.center_depth;
connection.updateSegment(compseg.segment_number, compseg.center_depth);
}
for (size_t ic = 0; ic < connection_set.size(); ++ic) {

View File

@@ -335,7 +335,7 @@ namespace Opm {
+ ". Can not infer reference depth" );
}
return completions.get( 0 ).center_depth;
return completions.get( 0 ).depth();
}
void Well::setRefDepth( size_t timestep, double depth ) {
@@ -356,11 +356,11 @@ namespace Opm {
const auto& connections = this->getConnections(time_step);
for (const auto& conn : connections) {
auto pair = completions.find( conn.complnum );
auto pair = completions.find( conn.complnum() );
if (pair == completions.end())
completions[conn.complnum] = {};
completions[conn.complnum()] = {};
pair = completions.find(conn.complnum);
pair = completions.find(conn.complnum());
pair->second.push_back(conn);
}
@@ -641,7 +641,7 @@ namespace Opm {
for (auto c : this->getConnections(time_step)) {
if (match(c))
c.complnum = complnum;
c.setComplnum( complnum );
new_connections->add(c);
}
@@ -654,8 +654,8 @@ namespace Opm {
if (!match_eq(c.getI(), record, "I" , -1)) return false;
if (!match_eq(c.getJ(), record, "J" , -1)) return false;
if (!match_eq(c.getK(), record, "K", -1)) return false;
if (!match_ge(c.complnum, record, "C1")) return false;
if (!match_le(c.complnum, record, "C2")) return false;
if (!match_ge(c.complnum(), record, "C1")) return false;
if (!match_le(c.complnum(), record, "C2")) return false;
return true;
};
@@ -664,7 +664,7 @@ namespace Opm {
for (auto c : this->getConnections(time_step)) {
if (match(c))
c.state = status;
c.setState( status );
new_connections->add(c);
}
@@ -692,8 +692,8 @@ namespace Opm {
void Well::handleWPIMULT(const DeckRecord& record, size_t time_step) {
auto match = [=]( const Connection &c) -> bool {
if (!match_ge(c.complnum, record, "FIRST")) return false;
if (!match_le(c.complnum, record, "LAST")) return false;
if (!match_ge(c.complnum(), record, "FIRST")) return false;
if (!match_le(c.complnum(), record, "LAST")) return false;
if (!match_eq(c.getI() , record, "I", -1)) return false;
if (!match_eq(c.getJ() , record, "J", -1)) return false;
if (!match_eq(c.getK() , record, "K", -1)) return false;
@@ -706,7 +706,7 @@ namespace Opm {
for (auto c : this->getConnections(time_step)) {
if (match(c))
c.wellPi *= wellPi;
c.scaleWellPi( wellPi );
new_connections->add(c);
}

View File

@@ -21,6 +21,7 @@
#include <cmath>
#include <limits>
#include <opm/parser/eclipse/Units/Units.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Eclipse3DProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
@@ -29,6 +30,90 @@
namespace Opm {
namespace {
// Compute direction permutation corresponding to completion's
// direction. First two elements of return value are directions
// perpendicular to completion while last element is direction
// along completion.
inline std::array< size_t, 3> directionIndices(const Opm::WellCompletion::DirectionEnum direction)
{
switch (direction) {
case Opm::WellCompletion::DirectionEnum::X:
return {{ 1,2,0 }};
case Opm::WellCompletion::DirectionEnum::Y:
return {{ 2,0,1}};
case Opm::WellCompletion::DirectionEnum::Z:
return {{ 0,1,2 }};
}
// All enum values should be handled above. Therefore
// we should never reach this one. Anyway for the sake
// of reduced warnings we throw an exception.
throw std::invalid_argument("unhandled enum value");
}
// Permute (diagonal) permeability components according to
// completion's direction.
inline std::array<double,3>
permComponents(const Opm::WellCompletion::DirectionEnum direction,
const std::array<double,3>& perm)
{
const auto p = directionIndices(direction);
return {{ perm[ p[0] ],
perm[ p[1] ],
perm[ p[2] ] }};
}
// Permute cell's geometric extent according to completion's
// direction. Honour net-to-gross ratio.
//
// Note: 'extent' is intentionally accepted by modifiable value
// rather than reference-to-const to support NTG manipulation.
inline std::array<double,3>
effectiveExtent(const Opm::WellCompletion::DirectionEnum direction,
const double ntg,
std::array<double,3> extent)
{
// Vertical extent affected by net-to-gross ratio.
extent[2] *= ntg;
const auto p = directionIndices(direction);
std::array<double,3>
D = {{ extent[ p[0] ] ,
extent[ p[1] ] ,
extent[ p[2] ] }};
return D;
}
// Compute Peaceman's effective radius of single completion.
inline double
effectiveRadius(const std::array<double,3>& K,
const std::array<double,3>& D)
{
const double K01 = K[0] / K[1];
const double K10 = K[1] / K[0];
const double D0_sq = D[0] * D[0];
const double D1_sq = D[1] * D[1];
const double num = std::sqrt((std::sqrt(K10) * D0_sq) +
(std::sqrt(K01) * D1_sq));
const double den = std::pow(K01, 0.25) + std::pow(K10, 0.25);
// Note: Analytic constant 0.28 derived for infintely sized
// formation with repeating well placement.
return 0.28 * (num / den);
}
} // anonymous namespace
WellConnections::WellConnections(int headIArg, int headJArg) :
headI(headIArg),
headJ(headJArg)
@@ -47,17 +132,16 @@ namespace Opm {
void WellConnections::addConnection(int i, int j , int k ,
int complnum,
double depth,
WellCompletion::StateEnum state ,
const Value<double>& connectionTransmissibilityFactor,
const Value<double>& diameter,
const Value<double>& skinFactor,
const Value<double>& Kh,
WellCompletion::StateEnum state,
double CF,
double Kh,
double rw,
const int satTableId,
const WellCompletion::DirectionEnum direction)
{
int conn_i = (i < 0) ? this->headI : i;
int conn_j = (j < 0) ? this->headJ : j;
Connection conn(conn_i, conn_j, k, complnum, depth, state, connectionTransmissibilityFactor, diameter, skinFactor, Kh, satTableId, direction);
Connection conn(conn_i, conn_j, k, complnum, depth, state, CF, Kh, rw, satTableId, direction);
this->add(conn);
}
@@ -66,10 +150,9 @@ namespace Opm {
void WellConnections::addConnection(int i, int j , int k ,
double depth,
WellCompletion::StateEnum state ,
const Value<double>& connectionTransmissibilityFactor,
const Value<double>& diameter,
const Value<double>& skinFactor,
const Value<double>& Kh,
double CF,
double Kh,
double rw,
const int satTableId,
const WellCompletion::DirectionEnum direction)
{
@@ -80,15 +163,19 @@ namespace Opm {
complnum,
depth,
state,
connectionTransmissibilityFactor,
diameter,
skinFactor,
CF,
Kh,
rw,
satTableId,
direction);
}
void WellConnections::loadCOMPDAT(const DeckRecord& record, const EclipseGrid& grid, const Eclipse3DProperties& eclipseProperties) {
const auto& permx = eclipseProperties.getDoubleGridProperty("PERMX").getData();
const auto& permy = eclipseProperties.getDoubleGridProperty("PERMY").getData();
const auto& permz = eclipseProperties.getDoubleGridProperty("PERMZ").getData();
const auto& ntg = eclipseProperties.getDoubleGridProperty("NTG").getData();
const auto& itemI = record.getItem( "I" );
const auto defaulted_I = itemI.defaultApplied( 0 ) || itemI.get< int >( 0 ) == 0;
const int I = !defaulted_I ? itemI.get< int >( 0 ) - 1 : this->headI;
@@ -100,42 +187,37 @@ namespace Opm {
int K1 = record.getItem("K1").get< int >(0) - 1;
int K2 = record.getItem("K2").get< int >(0) - 1;
WellCompletion::StateEnum state = WellCompletion::StateEnumFromString( record.getItem("STATE").getTrimmedString(0) );
Value<double> connectionTransmissibilityFactor("CompletionTransmissibilityFactor");
Value<double> diameter("Diameter");
Value<double> skinFactor("SkinFactor");
Value<double> Kh("Kh");
const auto& satnum = eclipseProperties.getIntGridProperty("SATNUM");
int satTableId = -1;
bool defaultSatTable = true;
const auto& CFItem = record.getItem("CONNECTION_TRANSMISSIBILITY_FACTOR");
const auto& diameterItem = record.getItem("DIAMETER");
const auto& KhItem = record.getItem("Kh");
const auto& satTableIdItem = record.getItem("SAT_TABLE");
const auto& r0Item = record.getItem("PR");
const WellCompletion::DirectionEnum direction = WellCompletion::DirectionEnumFromString(record.getItem("DIR").getTrimmedString(0));
double skin_factor = record.getItem("SKIN").getSIDouble(0);
double rw;
if (satTableIdItem.hasValue(0) && satTableIdItem.get < int > (0) > 0)
{
const auto& connectionTransmissibilityFactorItem = record.getItem("CONNECTION_TRANSMISSIBILITY_FACTOR");
const auto& diameterItem = record.getItem("DIAMETER");
const auto& skinFactorItem = record.getItem("SKIN");
const auto& KhItem = record.getItem("Kh");
const auto& satTableIdItem = record.getItem("SAT_TABLE");
if (connectionTransmissibilityFactorItem.hasValue(0) && connectionTransmissibilityFactorItem.getSIDouble(0) > 0)
connectionTransmissibilityFactor.setValue(connectionTransmissibilityFactorItem.getSIDouble(0));
if (diameterItem.hasValue(0))
diameter.setValue( diameterItem.getSIDouble(0));
if (skinFactorItem.hasValue(0))
skinFactor.setValue( skinFactorItem.get< double >(0));
if (KhItem.hasValue(0) && (KhItem.get< double >(0) > 0.0))
Kh.setValue( KhItem.getSIDouble(0));
if (satTableIdItem.hasValue(0) && satTableIdItem.get < int > (0) > 0)
{
satTableId = satTableIdItem.get< int >(0);
defaultSatTable = false;
}
satTableId = satTableIdItem.get< int >(0);
defaultSatTable = false;
}
const WellCompletion::DirectionEnum direction = WellCompletion::DirectionEnumFromString(record.getItem("DIR").getTrimmedString(0));
if (diameterItem.hasValue(0))
rw = 0.50 * diameterItem.getSIDouble(0);
else
// The Eclipse100 manual does not specify a default value for the wellbore
// diameter, but the Opm codebase has traditionally implemented a default
// value of one foot. The same default value is used by Eclipse300.
rw = 0.5*unit::feet;
for (int k = K1; k <= K2; k++) {
double CF = -1;
double Kh = -1;
if (defaultSatTable)
satTableId = satnum.iget(grid.getGlobalIndex(I,J,k));
@@ -143,6 +225,50 @@ namespace Opm {
return c.sameCoordinate( I,J,k );
};
if (KhItem.hasValue(0) && KhItem.getSIDouble(0) > 0.0)
Kh = KhItem.getSIDouble(0);
if (CFItem.hasValue(0) && CFItem.getSIDouble(0) > 0.0)
CF = CFItem.getSIDouble(0);
/* We start with the absolute happy path; both CF and Kh are explicitly given in the deck. */
if (CF > 0 && Kh > 0)
goto CF_done;
/* We must calculate CF and Kh from the items in the COMPDAT record and cell properties. */
{
// Angle of completion exposed to flow. We assume centre
// placement so there's complete exposure (= 2\pi).
const double angle = 6.2831853071795864769252867665590057683943387987502116419498;
size_t global_index = grid.getGlobalIndex(I,J,k);
std::array<double,3> cell_perm = {{ permx[global_index], permy[global_index], permz[global_index]}};
std::array<double,3> cell_size = grid.getCellDims(global_index);
double r0;
const auto& K = permComponents(direction, cell_perm);
const auto& D = effectiveExtent(direction, ntg[global_index], cell_size);
if (r0Item.hasValue(0))
r0 = r0Item.getSIDouble(0);
else
r0 = effectiveRadius(K,D);
if (CF < 0) {
if (Kh < 0)
Kh = std::sqrt(K[0] * K[1]) * D[2];
CF = angle * Kh / (std::log(r0 / std::min(rw, r0)) + skin_factor);
} else {
if (KhItem.defaultApplied(0) || KhItem.getSIDouble(0) < 0) {
Kh = CF * (std::log(r0 / std::min(r0, rw)) + skin_factor) / angle;
} else {
if (Kh < 0)
Kh = std::sqrt(K[0] * K[1]) * D[2];
}
}
}
CF_done:
auto prev = std::find_if( this->m_connections.begin(),
this->m_connections.end(),
same_ijk );
@@ -151,24 +277,22 @@ namespace Opm {
this->addConnection(I,J,k,
grid.getCellDepth( I,J,k ),
state,
connectionTransmissibilityFactor,
diameter,
skinFactor,
CF,
Kh,
rw,
satTableId,
direction );
} else {
// The complnum value carries over; the rest of the state is fully specified by
// the current COMPDAT keyword.
int complnum = prev->complnum;
int complnum = prev->complnum();
*prev = Connection(I,J,k,
complnum,
grid.getCellDepth(I,J,k),
state,
connectionTransmissibilityFactor,
diameter,
skinFactor,
CF,
Kh,
rw,
satTableId,
direction );
}
@@ -183,9 +307,14 @@ namespace Opm {
}
const Connection& WellConnections::get(size_t index) const {
return this->m_connections.at( index );
return (*this)[index];
}
const Connection& WellConnections::operator[](size_t index) const {
return this->m_connections.at(index);
}
const Connection& WellConnections::getFromIJK(const int i, const int j, const int k) const {
for (size_t ic = 0; ic < size(); ++ic) {
if (get(ic).sameCoordinate(i, j, k)) {
@@ -212,7 +341,7 @@ namespace Opm {
bool WellConnections::allConnectionsShut( ) const {
auto shut = []( const Connection& c ) {
return c.state == WellCompletion::StateEnum::SHUT;
return c.state() == WellCompletion::StateEnum::SHUT;
};
return std::all_of( this->m_connections.begin(),
@@ -243,7 +372,7 @@ namespace Opm {
for (size_t pos = 1; pos < m_connections.size() - 1; ++pos) {
const auto& prev = m_connections[pos - 1];
const double prevz = prev.center_depth;
const double prevz = prev.depth();
size_t next_index = findClosestConnection(prev.getI(), prev.getJ(), prevz, pos);
std::swap(m_connections[next_index], m_connections[pos]);
}
@@ -259,7 +388,7 @@ namespace Opm {
for (size_t pos = start_pos; pos < m_connections.size(); ++pos) {
const auto& connection = m_connections[ pos ];
const double depth = connection.center_depth;
const double depth = connection.depth();
const int ci = connection.getI();
const int cj = connection.getJ();
// Using square of distance to avoid non-integer arithmetics.

View File

@@ -36,7 +36,6 @@
#include <opm/parser/eclipse/EclipseState/Schedule/Well.hpp>
#include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <ert/ecl/Smspec.hpp>
#include <ert/ecl/ecl_smspec.h>
#include <iostream>
@@ -141,15 +140,15 @@ void handleMissingGroup( const ParseContext& parseContext , const std::string& k
parseContext.handleError( ParseContext::SUMMARY_UNKNOWN_GROUP , msg );
}
inline void keywordW( std::vector< ERT::smspec_node >& list,
inline void keywordW( SummaryConfig::keyword_list& list,
const ParseContext& parseContext,
const DeckKeyword& keyword,
const Schedule& schedule ) {
const auto type = ECL_SMSPEC_WELL_VAR;
const auto hasValue = []( const DeckKeyword& kw ) {
return kw.getDataRecord().getDataItem().hasValue( 0 );
};
const std::array<int,3> dummy_dims = {1,1,1};
if (keyword.size() && hasValue(keyword)) {
for( const std::string& pattern : keyword.getStringData()) {
@@ -159,20 +158,32 @@ inline void keywordW( std::vector< ERT::smspec_node >& list,
handleMissingWell( parseContext, keyword.name(), pattern );
for( const auto* well : wells )
list.emplace_back( type, well->name(), keyword.name() );
list.push_back( SummaryConfig::keyword_type( smspec_node_alloc( ECL_SMSPEC_WELL_VAR,
well->name().c_str(),
keyword.name().c_str(),
"",
":",
dummy_dims.data(),
0,0,0)));
}
} else
for (const auto* well : schedule.getWells())
list.emplace_back(type, well->name(), keyword.name());
}
list.push_back( SummaryConfig::keyword_type( smspec_node_alloc( ECL_SMSPEC_WELL_VAR,
well->name().c_str(),
keyword.name().c_str(),
"",
":",
dummy_dims.data(),
0,0,0)));
}
inline void keywordG( std::vector< ERT::smspec_node >& list,
inline void keywordG( SummaryConfig::keyword_list& list,
const ParseContext& parseContext,
const DeckKeyword& keyword,
const Schedule& schedule ) {
const auto type = ECL_SMSPEC_GROUP_VAR;
const std::array<int,3> dummy_dims = {1,1,1};
if( keyword.name() == "GMWSET" ) return;
if( keyword.size() == 0 ||
@@ -180,9 +191,14 @@ inline void keywordG( std::vector< ERT::smspec_node >& list,
for( const auto& group : schedule.getGroups() ) {
if( group->name() == "FIELD" ) continue;
list.emplace_back( type, group->name(), keyword.name() );
list.push_back( SummaryConfig::keyword_type( smspec_node_alloc( ECL_SMSPEC_GROUP_VAR,
group->name().c_str(),
keyword.name().c_str(),
"",
":",
dummy_dims.data(),
0, 0, 0)));
}
return;
}
@@ -190,17 +206,33 @@ inline void keywordG( std::vector< ERT::smspec_node >& list,
for( const std::string& group : item.getData< std::string >() ) {
if( schedule.hasGroup( group ) )
list.emplace_back( ECL_SMSPEC_GROUP_VAR, group, keyword.name() );
list.push_back( SummaryConfig::keyword_type( smspec_node_alloc( ECL_SMSPEC_GROUP_VAR,
group.c_str(),
keyword.name().c_str(),
"",
":",
dummy_dims.data(),
0, 0, 0)));
else
handleMissingGroup( parseContext, keyword.name(), group );
}
}
inline void keywordF( std::vector< ERT::smspec_node >& list,
const DeckKeyword& keyword ) {
if( keyword.name() == "FMWSET" ) return;
list.emplace_back( keyword.name() );
}
inline void keywordF( SummaryConfig::keyword_list& list,
const DeckKeyword& keyword ) {
const std::array<int,3> dummy_dims = {1,1,1};
if( keyword.name() == "FMWSET" ) return;
list.push_back( SummaryConfig::keyword_type( smspec_node_alloc( ECL_SMSPEC_FIELD_VAR,
NULL,
keyword.name().c_str(),
"",
":",
dummy_dims.data(),
0, 0, 0)));
}
inline std::array< int, 3 > getijk( const DeckRecord& record,
int offset = 0 ) {
@@ -212,19 +244,27 @@ inline std::array< int, 3 > getijk( const DeckRecord& record,
}
inline std::array< int, 3 > getijk( const Connection& completion ) {
return {{ completion.getI(), completion.getJ(), completion.getK() }};
return { { completion.getI(), completion.getJ(), completion.getK() }};
}
inline void keywordB( std::vector< ERT::smspec_node >& list,
const DeckKeyword& keyword,
const GridDims& dims) {
inline void keywordB( SummaryConfig::keyword_list& list,
const DeckKeyword& keyword,
const GridDims& dims) {
for( const auto& record : keyword ) {
auto ijk = getijk( record );
list.emplace_back( keyword.name(), dims.getNXYZ().data(), ijk.data() );
int global_index = 1 + dims.getGlobalIndex(ijk[0], ijk[1], ijk[2]);
list.push_back( SummaryConfig::keyword_type( smspec_node_alloc(ECL_SMSPEC_BLOCK_VAR,
NULL,
keyword.name().c_str(),
"",
":",
dims.getNXYZ().data(),
global_index,0,0)));
}
}
inline void keywordR( std::vector< ERT::smspec_node >& list,
inline void keywordR( SummaryConfig::keyword_list& list,
const DeckKeyword& keyword,
const TableManager& tables,
const GridDims& dims) {
@@ -249,26 +289,41 @@ inline void keywordR( std::vector< ERT::smspec_node >& list,
for( const int region : regions ) {
if (region >= 1 && region <= static_cast<int>(numfip))
list.emplace_back( keyword.name(), dims.getNXYZ().data(), region );
list.push_back( SummaryConfig::keyword_type( smspec_node_alloc( ECL_SMSPEC_REGION_VAR,
NULL,
keyword.name().c_str(),
"",
":",
dims.getNXYZ().data(),
region,
0, 0)));
else
throw std::invalid_argument("Illegal region value: " + std::to_string( region ));
}
}
inline void keywordMISC( std::vector< ERT::smspec_node >& list,
const DeckKeyword& keyword)
inline void keywordMISC( SummaryConfig::keyword_list& list,
const DeckKeyword& keyword)
{
const std::array<int,3> dummy_dims = {1,1,1};
if (meta_keywords.count( keyword.name() ) == 0)
list.emplace_back( keyword.name() );
list.push_back( SummaryConfig::keyword_type( smspec_node_alloc( ECL_SMSPEC_MISC_VAR,
NULL,
keyword.name().c_str(),
"",
":",
dummy_dims.data(),
0,
0, 0)));
}
inline void keywordC( std::vector< ERT::smspec_node >& list,
const ParseContext& parseContext,
const DeckKeyword& keyword,
const Schedule& schedule,
const GridDims& dims) {
inline void keywordC( SummaryConfig::keyword_list& list,
const ParseContext& parseContext,
const DeckKeyword& keyword,
const Schedule& schedule,
const GridDims& dims) {
const auto& keywordstring = keyword.name();
const auto last_timestep = schedule.getTimeMap().last();
@@ -297,25 +352,41 @@ inline void keywordC( std::vector< ERT::smspec_node >& list,
auto cijk = getijk( connection );
if( record.getItem( 1 ).defaultApplied( 0 ) ) {
list.emplace_back( keywordstring, name, dims.getNXYZ().data(), cijk.data() );
}
else {
int global_index = 1 + dims.getGlobalIndex(cijk[0], cijk[1], cijk[2]);
list.push_back( SummaryConfig::keyword_type( smspec_node_alloc( ECL_SMSPEC_COMPLETION_VAR,
name.c_str(),
keywordstring.c_str(),
"",
":",
dims.getNXYZ().data(),
global_index,
0, 0)));
} else {
/* block coordinates specified */
auto recijk = getijk( record, 1 );
if( std::equal( recijk.begin(), recijk.end(), cijk.begin() ) )
list.emplace_back( keywordstring, name, dims.getNXYZ().data(), cijk.data() );
if( std::equal( recijk.begin(), recijk.end(), cijk.begin() ) ) {
int global_index = 1 + dims.getGlobalIndex(recijk[0], recijk[1], recijk[2]);
list.push_back( SummaryConfig::keyword_type( smspec_node_alloc( ECL_SMSPEC_COMPLETION_VAR,
name.c_str(),
keywordstring.c_str(),
"",
":",
dims.getNXYZ().data(),
global_index,
0, 0)));
}
}
}
}
}
}
inline void handleKW( std::vector< ERT::smspec_node >& list,
const DeckKeyword& keyword,
const Schedule& schedule,
const TableManager& tables,
const ParseContext& parseContext,
const GridDims& dims) {
inline void handleKW( SummaryConfig::keyword_list& list,
const DeckKeyword& keyword,
const Schedule& schedule,
const TableManager& tables,
const ParseContext& parseContext,
const GridDims& dims) {
const auto var_type = ecl_smspec_identify_var_type( keyword.name().c_str() );
switch( var_type ) {
@@ -331,25 +402,23 @@ inline void handleKW( std::vector< ERT::smspec_node >& list,
}
}
inline void uniq( std::vector< ERT::smspec_node >& vec ) {
const auto lt = []( const ERT::smspec_node& lhs,
const ERT::smspec_node& rhs ) {
return ERT::smspec_node::cmp( lhs, rhs ) < 0;
inline void uniq( SummaryConfig::keyword_list& vec ) {
const auto lt = []( const SummaryConfig::keyword_type& lhs,
const SummaryConfig::keyword_type& rhs ) {
return smspec_node_cmp(lhs.get(), rhs.get()) < 0;
};
const auto eq = []( const ERT::smspec_node& lhs,
const ERT::smspec_node& rhs ) {
return ERT::smspec_node::cmp( lhs, rhs ) == 0;
const auto eq = []( const SummaryConfig::keyword_type& lhs,
const SummaryConfig::keyword_type& rhs ) {
return smspec_node_equal(lhs.get(), rhs.get());
};
std::sort( vec.begin(), vec.end(), lt );
auto logical_end = std::unique( vec.begin(), vec.end(), eq );
vec.erase( logical_end, vec.end() );
}
}
}
SummaryConfig::SummaryConfig( const Deck& deck,
const Schedule& schedule,
const TableManager& tables,
@@ -373,8 +442,8 @@ SummaryConfig::SummaryConfig( const Deck& deck,
uniq( this->keywords );
for (const auto& kw: this->keywords) {
this->short_keywords.insert( kw.keyword() );
this->summary_keywords.insert( kw.key1() );
this->short_keywords.insert( smspec_node_get_keyword( kw.get() ));
this->summary_keywords.insert( smspec_node_get_gen_key1( kw.get() ));
}
}

View File

@@ -1 +1 @@
{"name" : "MINPVFIL" , "sections" : ["GRID"], "size" : 1 , "items" : [{"name" : "VALUE" , "value_type" : "DOUBLE" , "default" : 0.000001, "dimension" : "LiquidSurfaceVolume"}]}
{"name" : "MINPVFIL" , "sections" : ["GRID"], "size" : 1 , "items" : [{"name" : "VALUE" , "value_type" : "DOUBLE" , "default" : 0.000001, "dimension" : "ReservoirVolume"}]}

435
tests/SPE1CASE1.DATA Normal file
View File

@@ -0,0 +1,435 @@
-- This reservoir simulation deck is made available under the Open Database
-- License: http://opendatacommons.org/licenses/odbl/1.0/. Any rights in
-- individual contents of the database are licensed under the Database Contents
-- License: http://opendatacommons.org/licenses/dbcl/1.0/
-- Copyright (C) 2015 Statoil
-- This simulation is based on the data given in
-- 'Comparison of Solutions to a Three-Dimensional
-- Black-Oil Reservoir Simulation Problem' by Aziz S. Odeh,
-- Journal of Petroleum Technology, January 1981
---------------------------------------------------------------------------
------------------------ SPE1 - CASE 1 ------------------------------------
---------------------------------------------------------------------------
RUNSPEC
-- -------------------------------------------------------------------------
TITLE
SPE1 - CASE 1
DIMENS
10 10 3 /
-- The number of equilibration regions is inferred from the EQLDIMS
-- keyword.
EQLDIMS
/
-- The number of PVTW tables is inferred from the TABDIMS keyword;
-- when no data is included in the keyword the default values are used.
TABDIMS
/
OIL
GAS
WATER
DISGAS
-- As seen from figure 4 in Odeh, GOR is increasing with time,
-- which means that dissolved gas is present
FIELD
START
1 'JAN' 2015 /
WELLDIMS
-- Item 1: maximum number of wells in the model
-- - there are two wells in the problem; injector and producer
-- Item 2: maximum number of grid blocks connected to any one well
-- - must be one as the wells are located at specific grid blocks
-- Item 3: maximum number of groups in the model
-- - we are dealing with only one 'group'
-- Item 4: maximum number of wells in any one group
-- - there must be two wells in a group as there are two wells in total
2 1 1 2 /
UNIFOUT
GRID
-- The INIT keyword is used to request an .INIT file. The .INIT file
-- is written before the simulation actually starts, and contains grid
-- properties and saturation tables as inferred from the input
-- deck. There are no other keywords which can be used to configure
-- exactly what is written to the .INIT file.
INIT
-- -------------------------------------------------------------------------
NOECHO
DX
-- There are in total 300 cells with length 1000ft in x-direction
300*1000 /
DY
-- There are in total 300 cells with length 1000ft in y-direction
300*1000 /
DZ
-- The layers are 20, 30 and 50 ft thick, in each layer there are 100 cells
100*20 100*30 100*50 /
TOPS
-- The depth of the top of each grid block
100*8325 /
PORO
-- Constant porosity of 0.3 throughout all 300 grid cells
300*0.3 /
PERMX
-- The layers have perm. 500mD, 50mD and 200mD, respectively.
100*500 100*50 100*200 /
PERMY
-- Equal to PERMX
100*500 100*50 100*200 /
PERMZ
-- Cannot find perm. in z-direction in Odeh's paper
-- For the time being, we will assume PERMZ equal to PERMX and PERMY:
100*500 100*50 100*200 /
ECHO
PROPS
-- -------------------------------------------------------------------------
PVTW
-- Item 1: pressure reference (psia)
-- Item 2: water FVF (rb per bbl or rb per stb)
-- Item 3: water compressibility (psi^{-1})
-- Item 4: water viscosity (cp)
-- Item 5: water 'viscosibility' (psi^{-1})
-- Using values from Norne:
-- In METRIC units:
-- 277.0 1.038 4.67E-5 0.318 0.0 /
-- In FIELD units:
4017.55 1.038 3.22E-6 0.318 0.0 /
ROCK
-- Item 1: reference pressure (psia)
-- Item 2: rock compressibility (psi^{-1})
-- Using values from table 1 in Odeh:
14.7 3E-6 /
SWOF
-- Column 1: water saturation
-- - this has been set to (almost) equally spaced values from 0.12 to 1
-- Column 2: water relative permeability
-- - generated from the Corey-type approx. formula
-- the coeffisient is set to 10e-5, S_{orw}=0 and S_{wi}=0.12
-- Column 3: oil relative permeability when only oil and water are present
-- - we will use the same values as in column 3 in SGOF.
-- This is not really correct, but since only the first
-- two values are of importance, this does not really matter
-- Column 4: water-oil capillary pressure (psi)
0.12 0 1 0
0.18 4.64876033057851E-008 1 0
0.24 0.000000186 0.997 0
0.3 4.18388429752066E-007 0.98 0
0.36 7.43801652892562E-007 0.7 0
0.42 1.16219008264463E-006 0.35 0
0.48 1.67355371900826E-006 0.2 0
0.54 2.27789256198347E-006 0.09 0
0.6 2.97520661157025E-006 0.021 0
0.66 3.7654958677686E-006 0.01 0
0.72 4.64876033057851E-006 0.001 0
0.78 0.000005625 0.0001 0
0.84 6.69421487603306E-006 0 0
0.91 8.05914256198347E-006 0 0
1 0.00001 0 0 /
SGOF
-- Column 1: gas saturation
-- Column 2: gas relative permeability
-- Column 3: oil relative permeability when oil, gas and connate water are present
-- Column 4: oil-gas capillary pressure (psi)
-- - stated to be zero in Odeh's paper
-- Values in column 1-3 are taken from table 3 in Odeh's paper:
0 0 1 0
0.001 0 1 0
0.02 0 0.997 0
0.05 0.005 0.980 0
0.12 0.025 0.700 0
0.2 0.075 0.350 0
0.25 0.125 0.200 0
0.3 0.190 0.090 0
0.4 0.410 0.021 0
0.45 0.60 0.010 0
0.5 0.72 0.001 0
0.6 0.87 0.0001 0
0.7 0.94 0.000 0
0.85 0.98 0.000 0
0.88 0.984 0.000 0 /
--1.00 1.0 0.000 0 /
-- Warning from Eclipse: first sat. value in SWOF + last sat. value in SGOF
-- must not be greater than 1, but Eclipse still runs
-- Flow needs the sum to be excactly 1 so I added a row with gas sat. = 0.88
-- The corresponding krg value was estimated by assuming linear rel. between
-- gas sat. and krw. between gas sat. 0.85 and 1.00 (the last two values given)
DENSITY
-- Density (lb per ft³) at surface cond. of
-- oil, water and gas, respectively (in that order)
-- Using values from Norne:
-- In METRIC units:
-- 859.5 1033.0 0.854 /
-- In FIELD units:
53.66 64.49 0.0533 /
PVDG
-- Column 1: gas phase pressure (psia)
-- Column 2: gas formation volume factor (rb per Mscf)
-- - in Odeh's paper the units are said to be given in rb per bbl,
-- but this is assumed to be a mistake: FVF-values in Odeh's paper
-- are given in rb per scf, not rb per bbl. This will be in
-- agreement with conventions
-- Column 3: gas viscosity (cP)
-- Using values from lower right table in Odeh's table 2:
14.700 166.666 0.008000
264.70 12.0930 0.009600
514.70 6.27400 0.011200
1014.7 3.19700 0.014000
2014.7 1.61400 0.018900
2514.7 1.29400 0.020800
3014.7 1.08000 0.022800
4014.7 0.81100 0.026800
5014.7 0.64900 0.030900
9014.7 0.38600 0.047000 /
PVTO
-- Column 1: dissolved gas-oil ratio (Mscf per stb)
-- Column 2: bubble point pressure (psia)
-- Column 3: oil FVF for saturated oil (rb per stb)
-- Column 4: oil viscosity for saturated oil (cP)
-- Use values from top left table in Odeh's table 2:
0.0010 14.7 1.0620 1.0400 /
0.0905 264.7 1.1500 0.9750 /
0.1800 514.7 1.2070 0.9100 /
0.3710 1014.7 1.2950 0.8300 /
0.6360 2014.7 1.4350 0.6950 /
0.7750 2514.7 1.5000 0.6410 /
0.9300 3014.7 1.5650 0.5940 /
1.2700 4014.7 1.6950 0.5100
9014.7 1.5790 0.7400 /
1.6180 5014.7 1.8270 0.4490
9014.7 1.7370 0.6310 /
-- It is required to enter data for undersaturated oil for the highest GOR
-- (i.e. the last row) in the PVTO table.
-- In order to fulfill this requirement, values for oil FVF and viscosity
-- at 9014.7psia and GOR=1.618 for undersaturated oil have been approximated:
-- It has been assumed that there is a linear relation between the GOR
-- and the FVF when keeping the pressure constant at 9014.7psia.
-- From Odeh we know that (at 9014.7psia) the FVF is 2.357 at GOR=2.984
-- for saturated oil and that the FVF is 1.579 at GOR=1.27 for undersaturated oil,
-- so it is possible to use the assumption described above.
-- An equivalent approximation for the viscosity has been used.
/
SOLUTION
-- -------------------------------------------------------------------------
EQUIL
-- Item 1: datum depth (ft)
-- Item 2: pressure at datum depth (psia)
-- - Odeh's table 1 says that initial reservoir pressure is
-- 4800 psi at 8400ft, which explains choice of item 1 and 2
-- Item 3: depth of water-oil contact (ft)
-- - chosen to be directly under the reservoir
-- Item 4: oil-water capillary pressure at the water oil contact (psi)
-- - given to be 0 in Odeh's paper
-- Item 5: depth of gas-oil contact (ft)
-- - chosen to be directly above the reservoir
-- Item 6: gas-oil capillary pressure at gas-oil contact (psi)
-- - given to be 0 in Odeh's paper
-- Item 7: RSVD-table
-- Item 8: RVVD-table
-- Item 9: Set to 0 as this is the only value supported by OPM
-- Item #: 1 2 3 4 5 6 7 8 9
8400 4800 8450 0 8300 0 1 0 0 /
RSVD
-- Dissolved GOR is initially constant with depth through the reservoir.
-- The reason is that the initial reservoir pressure given is higher
---than the bubble point presssure of 4014.7psia, meaning that there is no
-- free gas initially present.
8300 1.270
8450 1.270 /
SUMMARY
-- -------------------------------------------------------------------------
-- 1a) Oil rate vs time
FOPR
-- Field Oil Production Rate
-- 1b) GOR vs time
WGOR
-- Well Gas-Oil Ratio
'PROD'
/
-- Using FGOR instead of WGOR:PROD results in the same graph
FGOR
-- 2a) Pressures of the cell where the injector and producer are located
BPR
1 1 1 /
10 10 3 /
/
-- 2b) Gas saturation at grid points given in Odeh's paper
BGSAT
1 1 1 /
1 1 2 /
1 1 3 /
10 1 1 /
10 1 2 /
10 1 3 /
10 10 1 /
10 10 2 /
10 10 3 /
/
-- In order to compare Eclipse with Flow:
WBHP
'INJ'
'PROD'
/
WGIR
'INJ'
'PROD'
/
WGIT
'INJ'
'PROD'
/
WGPR
'INJ'
'PROD'
/
WGPT
'INJ'
'PROD'
/
WOIR
'INJ'
'PROD'
/
WOIT
'INJ'
'PROD'
/
WOPR
'INJ'
'PROD'
/
WOPT
'INJ'
'PROD'
/
WWIR
'INJ'
'PROD'
/
WWIT
'INJ'
'PROD'
/
WWPR
'INJ'
'PROD'
/
WWPT
'INJ'
'PROD'
/
SCHEDULE
-- -------------------------------------------------------------------------
RPTSCHED
'PRES' 'SGAS' 'RS' 'WELLS' 'WELSPECS' /
RPTRST
'BASIC=1' /
-- If no resolution (i.e. case 1), the two following lines must be added:
DRSDT
0 /
-- if DRSDT is set to 0, GOR cannot rise and free gas does not
-- dissolve in undersaturated oil -> constant bubble point pressure
WELSPECS
-- Item #: 1 2 3 4 5 6
'PROD' 'G1' 10 10 8400 'OIL' /
'INJ' 'G1' 1 1 8335 'GAS' /
/
-- Coordinates in item 3-4 are retrieved from Odeh's figure 1 and 2
-- Note that the depth at the midpoint of the well grid blocks
-- has been used as reference depth for bottom hole pressure in item 5
COMPDAT
-- Item #: 1 2 3 4 5 6 7 8 9
'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 /
'INJ' 1 1 1 1 'OPEN' 1* 1* 0.5 /
/
-- Coordinates in item 2-5 are retreived from Odeh's figure 1 and 2
-- Item 9 is the well bore internal diameter,
-- the radius is given to be 0.25ft in Odeh's paper
WCONPROD
-- Item #:1 2 3 4 5 9
'PROD' 'OPEN' 'ORAT' 20000 4* 1000 /
/
-- It is stated in Odeh's paper that the maximum oil prod. rate
-- is 20 000stb per day which explains the choice of value in item 4.
-- The items > 4 are defaulted with the exception of item 9,
-- the BHP lower limit, which is given to be 1000psia in Odeh's paper
WCONINJE
-- Item #:1 2 3 4 5 6 7
'INJ' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 /
/
-- Stated in Odeh that gas inj. rate (item 5) is 100MMscf per day
-- BHP upper limit (item 7) should not be exceeding the highest
-- pressure in the PVT table=9014.7psia (default is 100 000psia)
TSTEP
--Advance the simulater once a month for TEN years:
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31 /
--Advance the simulator once a year for TEN years:
--10*365 /
END

6758
tests/SPE9_CP_PACKED.DATA Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,8 @@
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Deck/DeckItem.hpp>
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
@@ -36,6 +37,9 @@
#include <opm/parser/eclipse/EclipseState/Schedule/WellConnections.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Eclipse3DProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
namespace Opm {
@@ -54,22 +58,6 @@ inline std::ostream& operator<<( std::ostream& stream, const WellConnections& cs
BOOST_AUTO_TEST_CASE(testGetFunctions) {
Opm::WellCompletion::DirectionEnum dir = Opm::WellCompletion::DirectionEnum::Z;
Opm::Connection completion(10,11,12, 1, 0.0, Opm::WellCompletion::OPEN,Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",17.29), 0, dir);
BOOST_CHECK_EQUAL( 10 , completion.getI() );
BOOST_CHECK_EQUAL( 11 , completion.getJ() );
BOOST_CHECK_EQUAL( 12 , completion.getK() );
BOOST_CHECK_EQUAL( Opm::WellCompletion::OPEN , completion.state);
BOOST_CHECK_EQUAL( 99.88 , completion.getConnectionTransmissibilityFactor());
BOOST_CHECK_EQUAL( 22.33 , completion.getDiameter());
BOOST_CHECK_EQUAL( 33.22 , completion.getSkinFactor());
BOOST_CHECK_CLOSE( 17.29 , completion.getEffectiveKhAsValueObject().getValue() , 1.0e-10);
BOOST_CHECK_EQUAL( 0 , completion.sat_tableId);
}
BOOST_AUTO_TEST_CASE(CreateWellConnectionsOK) {
Opm::WellConnections completionSet;
@@ -81,9 +69,9 @@ BOOST_AUTO_TEST_CASE(CreateWellConnectionsOK) {
BOOST_AUTO_TEST_CASE(AddCompletionSizeCorrect) {
Opm::WellCompletion::DirectionEnum dir = Opm::WellCompletion::DirectionEnum::Z;
Opm::WellConnections completionSet;
Opm::Connection completion1( 10,10,10, 1, 0.0,Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",2.718), 0, dir);
Opm::Connection completion2( 11,10,10, 1, 0.0,Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",3.141), 0, dir);
completionSet.add( completion1 );
Opm::Connection completion1( 10,10,10, 1, 0.0, Opm::WellCompletion::OPEN , 99.88, 355.113, 0.25, 0, dir);
Opm::Connection completion2( 10,10,11, 1, 0.0, Opm::WellCompletion::SHUT , 99.88, 355.113, 0.25, 0, dir);
completionSet.add( completion1 );
BOOST_CHECK_EQUAL( 1U , completionSet.size() );
completionSet.add( completion2 );
@@ -95,9 +83,9 @@ BOOST_AUTO_TEST_CASE(AddCompletionSizeCorrect) {
BOOST_AUTO_TEST_CASE(WellConnectionsGetOutOfRangeThrows) {
Opm::WellCompletion::DirectionEnum dir = Opm::WellCompletion::DirectionEnum::Z;
Opm::Connection completion1( 10,10,10, 1, 0.0, Opm::WellCompletion::OPEN , 99.88, 355.113, 0.25, 0, dir);
Opm::Connection completion2( 10,10,11, 1, 0.0, Opm::WellCompletion::SHUT , 99.88, 355.113, 0.25, 0, dir);
Opm::WellConnections completionSet;
Opm::Connection completion1( 10,10,10,1, 0.0,Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",17.29), 0, dir);
Opm::Connection completion2( 11,10,10,1, 0.0,Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",355.113), 0, dir);
completionSet.add( completion1 );
BOOST_CHECK_EQUAL( 1U , completionSet.size() );
@@ -115,9 +103,9 @@ BOOST_AUTO_TEST_CASE(AddCompletionCopy) {
Opm::WellConnections completionSet;
Opm::WellCompletion::DirectionEnum dir = Opm::WellCompletion::DirectionEnum::Z;
Opm::Connection completion1( 10,10,10, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",355.113), 0, dir);
Opm::Connection completion2( 10,10,11, 1, 0.0, Opm::WellCompletion::SHUT , Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",355.113), 0, dir);
Opm::Connection completion3( 10,10,12, 1, 0.0, Opm::WellCompletion::SHUT , Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",355.113), 0, dir);
Opm::Connection completion1( 10,10,10, 1, 0.0, Opm::WellCompletion::OPEN , 99.88, 355.113, 0.25, 0, dir);
Opm::Connection completion2( 10,10,11, 1, 0.0, Opm::WellCompletion::SHUT , 99.88, 355.113, 0.25, 0, dir);
Opm::Connection completion3( 10,10,12, 1, 0.0, Opm::WellCompletion::SHUT , 99.88, 355.113, 0.25, 0, dir);
completionSet.add( completion1 );
completionSet.add( completion2 );
@@ -134,18 +122,18 @@ BOOST_AUTO_TEST_CASE(AddCompletionCopy) {
BOOST_AUTO_TEST_CASE(ActiveCompletions) {
Opm::EclipseGrid grid(10,10,10);
Opm::EclipseGrid grid(10,20,20);
Opm::WellCompletion::DirectionEnum dir = Opm::WellCompletion::DirectionEnum::Z;
Opm::WellConnections completions;
Opm::Connection completion1( 0,0,0, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",355.113), 0, dir);
Opm::Connection completion2( 0,0,1, 1, 0.0, Opm::WellCompletion::SHUT , Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",355.113), 0, dir);
Opm::Connection completion3( 0,0,2, 1, 0.0, Opm::WellCompletion::SHUT , Opm::Value<double>("ConnectionTransmissibilityFactor",99.88), Opm::Value<double>("D",22.33), Opm::Value<double>("SKIN",33.22), Opm::Value<double>("Kh",355.113), 0, dir);
Opm::Connection completion1( 0,0,0, 1, 0.0, Opm::WellCompletion::OPEN , 99.88, 355.113, 0.25, 0, dir);
Opm::Connection completion2( 0,0,1, 1, 0.0, Opm::WellCompletion::SHUT , 99.88, 355.113, 0.25, 0, dir);
Opm::Connection completion3( 0,0,2, 1, 0.0, Opm::WellCompletion::SHUT , 99.88, 355.113, 0.25, 0, dir);
completions.add( completion1 );
completions.add( completion2 );
completions.add( completion3 );
std::vector<int> actnum(1000,1);
std::vector<int> actnum(grid.getCartesianSize(), 1);
actnum[0] = 0;
grid.resetACTNUM( actnum.data() );
@@ -154,3 +142,187 @@ BOOST_AUTO_TEST_CASE(ActiveCompletions) {
BOOST_CHECK_EQUAL( completion2, active_completions.get(0));
BOOST_CHECK_EQUAL( completion3, active_completions.get(1));
}
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::ParseContext());
Opm::Eclipse3DProperties props(deck, tables, grid );
const auto& keyword = deck.getKeyword("COMPDAT", 0);
Opm::WellConnections connections;
for (const auto& rec : keyword)
connections.loadCOMPDAT(rec, grid, props);
return connections;
}
BOOST_AUTO_TEST_CASE(loadCOMPDATTEST) {
Opm::UnitSystem units(Opm::UnitSystem::UnitType::UNIT_TYPE_METRIC); // Unit system used in deck FIRST_SIM.DATA.
{
const std::string deck = R"(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 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));
}
{
const std::string deck = R"(GRID
PERMX
1000*0.10 /
COPY
'PERMX' 'PERMZ' /
'PERMX' 'PERMY' /
/
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 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));
}
}
BOOST_AUTO_TEST_CASE(loadCOMPDATTESTSPE1) {
Opm::ParseContext parseContext;
Opm::Parser parser;
const auto deck = parser.parseFile("SPE1CASE1.DATA", parseContext);
Opm::EclipseState state(deck, parseContext);
Opm::Schedule sched(deck, state, parseContext);
const auto& units = deck.getActiveUnitSystem();
const auto& prod = sched.getWell("PROD");
const auto& connections = prod->getConnections(0);
const auto& conn0 = connections[0];
/* Expected values come from Eclipse simulation. */
BOOST_CHECK_CLOSE(conn0.CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, 10.609), 2e-2);
BOOST_CHECK_CLOSE(conn0.Kh(), units.to_si(Opm::UnitSystem::measure::effective_Kh, 10000), 1e-6);
}
struct exp_conn {
std::string well;
int ci;
double CF;
double Kh;
};
BOOST_AUTO_TEST_CASE(loadCOMPDATTESTSPE9) {
Opm::ParseContext parseContext;
Opm::Parser parser;
const auto deck = parser.parseFile("SPE9_CP_PACKED.DATA", parseContext);
Opm::EclipseState state(deck, parseContext);
Opm::Schedule sched(deck, state, parseContext);
const auto& units = deck.getActiveUnitSystem();
/*
The list of the expected values come from the PRT file in an ECLIPSE simulation.
*/
std::vector<exp_conn> expected = {
{"INJE1" ,1 , 0.166, 111.9},
{"INJE1" ,2 , 0.597, 402.6},
{"INJE1" ,3 , 1.866, 1259.2},
{"INJE1" ,4 , 12.442, 8394.2},
{"INJE1" ,5 , 6.974, 4705.3},
{"PRODU2" ,1 , 0.893, 602.8},
{"PRODU2" ,2 , 3.828, 2582.8},
{"PRODU2" ,3 , 0.563, 380.0},
{"PRODU3" ,1 , 1.322, 892.1},
{"PRODU3" ,2 , 3.416, 2304.4},
{"PRODU4" ,1 , 4.137, 2791.2},
{"PRODU4" ,2 , 66.455, 44834.7},
{"PRODU5" ,1 , 0.391, 264.0},
{"PRODU5" ,2 , 7.282, 4912.6},
{"PRODU5" ,3 , 1.374, 927.3},
{"PRODU6" ,1 , 1.463, 987.3},
{"PRODU6" ,2 , 1.891, 1275.8},
{"PRODU7" ,1 , 1.061, 716.1},
{"PRODU7" ,2 , 5.902, 3982.0},
{"PRODU7" ,3 , 0.593, 400.1},
{"PRODU8" ,1 , 0.993, 670.1},
{"PRODU8" ,2 , 17.759, 11981.5},
{"PRODU9" ,1 , 0.996, 671.9},
{"PRODU9" ,2 , 2.548, 1719.0},
{"PRODU10" ,1 , 11.641, 7853.9},
{"PRODU10" ,2 , 7.358, 4964.1},
{"PRODU10" ,3 , 0.390, 262.8},
{"PRODU11" ,2 , 3.536, 2385.6},
{"PRODU12" ,1 , 3.028, 2043.1},
{"PRODU12" ,2 , 0.301, 202.7},
{"PRODU12" ,3 , 0.279, 188.3},
{"PRODU13" ,2 , 5.837, 3938.1},
{"PRODU14" ,1 , 180.976, 122098.1},
{"PRODU14" ,2 , 25.134, 16957.0},
{"PRODU14" ,3 , 0.532, 358.7},
{"PRODU15" ,1 , 4.125, 2783.1},
{"PRODU15" ,2 , 6.431, 4338.7},
{"PRODU16" ,2 , 5.892, 3975.0},
{"PRODU17" ,1 , 80.655, 54414.9},
{"PRODU17" ,2 , 9.098, 6138.3},
{"PRODU18" ,1 , 1.267, 855.1},
{"PRODU18" ,2 , 18.556, 12518.9},
{"PRODU19" ,1 , 15.589, 10517.2},
{"PRODU19" ,3 , 1.273, 859.1},
{"PRODU20" ,1 , 3.410, 2300.5},
{"PRODU20" ,2 , 0.191, 128.8},
{"PRODU20" ,3 , 0.249, 168.1},
{"PRODU21" ,1 , 0.596, 402.0},
{"PRODU21" ,2 , 0.163, 109.9},
{"PRODU22" ,1 , 4.021, 2712.8},
{"PRODU22" ,2 , 0.663, 447.1},
{"PRODU23" ,1 , 1.542, 1040.2},
{"PRODU24" ,1 , 78.939, 53257.0},
{"PRODU24" ,3 , 17.517, 11817.8},
{"PRODU25" ,1 , 3.038, 2049.5},
{"PRODU25" ,2 , 0.926, 624.9},
{"PRODU25" ,3 , 0.891, 601.3},
{"PRODU26" ,1 , 0.770, 519.6},
{"PRODU26" ,3 , 0.176, 118.6}};
for (const auto& ec : expected) {
const auto& well = sched.getWell(ec.well);
const auto& connections = well->getConnections(0);
const auto& conn = connections[ec.ci - 1];
BOOST_CHECK_CLOSE( conn.CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, ec.CF), 2e-1);
BOOST_CHECK_CLOSE( conn.Kh(), units.to_si(Opm::UnitSystem::measure::effective_Kh, ec.Kh), 1e-1);
}
}

View File

@@ -127,6 +127,11 @@ BOOST_AUTO_TEST_CASE(CreateGridNoCells) {
BOOST_CHECK_EQUAL( 10 , grid.getNX());
BOOST_CHECK_EQUAL( 10 , grid.getNY());
BOOST_CHECK_EQUAL( 10 , grid.getNZ());
BOOST_CHECK_EQUAL(10, grid[0]);
BOOST_CHECK_EQUAL(10, grid[2]);
BOOST_CHECK_THROW( grid[10], std::invalid_argument);
BOOST_CHECK_EQUAL( 1000 , grid.getCartesianSize());
}

View File

@@ -43,15 +43,15 @@
BOOST_AUTO_TEST_CASE(MultisegmentWellTest) {
Opm::WellCompletion::DirectionEnum dir = Opm::WellCompletion::DirectionEnum::Z;
Opm::WellConnections connection_set;
connection_set.add(Opm::Connection( 19, 0, 0, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor", 200.), Opm::Value<double>("D", 0.5), Opm::Value<double>("SKIN", 0.), Opm::Value<double>("Kh", 17.29), 0, dir) );
connection_set.add(Opm::Connection( 19, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor", 200.), Opm::Value<double>("D", 0.5), Opm::Value<double>("SKIN", 0.), Opm::Value<double>("Kh", 17.29), 0, dir) );
connection_set.add(Opm::Connection( 19, 0, 2, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor", 200.), Opm::Value<double>("D", 0.4), Opm::Value<double>("SKIN", 0.), Opm::Value<double>("Kh", 17.29), 0, dir) );
Opm::WellConnections connection_set;
connection_set.add(Opm::Connection( 19, 0, 0, 1, 0.0, Opm::WellCompletion::OPEN , 200, 17.29, 0.25, 0, dir) );
connection_set.add(Opm::Connection( 19, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , 200, 17.29, 0.25, 0, dir) );
connection_set.add(Opm::Connection( 19, 0, 2, 1, 0.0, Opm::WellCompletion::OPEN , 200, 17.29, 0.25, 0, dir) );
connection_set.add(Opm::Connection( 18, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor", 200.), Opm::Value<double>("D", 0.4), Opm::Value<double>("SKIN", 0.), Opm::Value<double>("Kh", 17.29), 0, Opm::WellCompletion::DirectionEnum::X) );
connection_set.add(Opm::Connection( 17, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor", 200.), Opm::Value<double>("D", 0.4), Opm::Value<double>("SKIN", 0.), Opm::Value<double>("Kh", 17.29), 0, Opm::WellCompletion::DirectionEnum::X) );
connection_set.add(Opm::Connection( 16, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor", 200.), Opm::Value<double>("D", 0.4), Opm::Value<double>("SKIN", 0.), Opm::Value<double>("Kh", 17.29), 0, Opm::WellCompletion::DirectionEnum::X) );
connection_set.add(Opm::Connection( 15, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value<double>("ConnectionTransmissibilityFactor", 200.), Opm::Value<double>("D", 0.4), Opm::Value<double>("SKIN", 0.), Opm::Value<double>("Kh", 17.29), 0, Opm::WellCompletion::DirectionEnum::X) );
connection_set.add(Opm::Connection( 18, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , 200, 17.29, 0.25, 0, Opm::WellCompletion::DirectionEnum::X) );
connection_set.add(Opm::Connection( 17, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , 200, 17.29, 0.25, 0, Opm::WellCompletion::DirectionEnum::X) );
connection_set.add(Opm::Connection( 16, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , 200, 17.29, 0.25, 0, Opm::WellCompletion::DirectionEnum::X) );
connection_set.add(Opm::Connection( 15, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , 200, 17.29, 0.25, 0, Opm::WellCompletion::DirectionEnum::X) );
BOOST_CHECK_EQUAL( 7U , connection_set.size() );
@@ -93,32 +93,32 @@ Opm::WellConnections connection_set;
BOOST_CHECK_EQUAL(7U, new_connection_set->size());
const Opm::Connection& connection1 = new_connection_set->get(0);
const int segment_number_connection1 = connection1.segment_number;
const double center_depth_connection1 = connection1.center_depth;
const int segment_number_connection1 = connection1.segment();
const double center_depth_connection1 = connection1.depth();
BOOST_CHECK_EQUAL(segment_number_connection1, 1);
BOOST_CHECK_EQUAL(center_depth_connection1, 2512.5);
const Opm::Connection& connection3 = new_connection_set->get(2);
const int segment_number_connection3 = connection3.segment_number;
const double center_depth_connection3 = connection3.center_depth;
const int segment_number_connection3 = connection3.segment();
const double center_depth_connection3 = connection3.depth();
BOOST_CHECK_EQUAL(segment_number_connection3, 3);
BOOST_CHECK_EQUAL(center_depth_connection3, 2562.5);
const Opm::Connection& connection5 = new_connection_set->get(4);
const int segment_number_connection5 = connection5.segment_number;
const double center_depth_connection5 = connection5.center_depth;
const int segment_number_connection5 = connection5.segment();
const double center_depth_connection5 = connection5.depth();
BOOST_CHECK_EQUAL(segment_number_connection5, 6);
BOOST_CHECK_CLOSE(center_depth_connection5, 2538.83, 0.001);
const Opm::Connection& connection6 = new_connection_set->get(5);
const int segment_number_connection6 = connection6.segment_number;
const double center_depth_connection6 = connection6.center_depth;
const int segment_number_connection6 = connection6.segment();
const double center_depth_connection6 = connection6.depth();
BOOST_CHECK_EQUAL(segment_number_connection6, 6);
BOOST_CHECK_CLOSE(center_depth_connection6, 2537.83, 0.001);
const Opm::Connection& connection7 = new_connection_set->get(6);
const int segment_number_connection7 = connection7.segment_number;
const double center_depth_connection7 = connection7.center_depth;
const int segment_number_connection7 = connection7.segment();
const double center_depth_connection7 = connection7.depth();
BOOST_CHECK_EQUAL(segment_number_connection7, 7);
BOOST_CHECK_EQUAL(center_depth_connection7, 2534.5);
}

View File

@@ -897,5 +897,40 @@ BOOST_AUTO_TEST_CASE(RESTART_BASIC_LEQ_2) {
BOOST_CHECK( !ioConfig.getWriteRestartFile( ts ) );
}
BOOST_AUTO_TEST_CASE(RESTART_SAVE) {
const char* data = "RUNSPEC\n"
"DIMENS\n"
" 10 10 10 /\n"
"GRID\n"
"START\n"
" 21 MAY 1981 /\n"
"\n"
"SCHEDULE\n"
"DATES\n"
" 22 MAY 1981 /\n"
"/\n"
"DATES\n"
" 23 MAY 1981 /\n"
" 24 MAY 1981 /\n"
" 23 MAY 1982 /\n"
" 24 MAY 1982 /\n"
" 24 MAY 1983 /\n"
" 25 MAY 1984 /\n"
" 26 MAY 1984 /\n"
" 26 MAY 1985 /\n"
" 27 MAY 1985 /\n"
" 1 JAN 1986 /\n"
"/\n"
"SAVE \n"
"TSTEP \n"
" 1 /\n";
auto deck = Parser().parseString( data, ParseContext() );
RestartConfig ioConfig( deck );
for( size_t ts = 1; ts < 11; ++ts )
BOOST_CHECK( !ioConfig.getWriteRestartFile( ts ) );
BOOST_CHECK( ioConfig.getWriteRestartFile( 12 ) );
}

View File

@@ -654,25 +654,25 @@ BOOST_AUTO_TEST_CASE(CreateScheduleDeckWellsAndConnectionDataWithWELOPEN) {
constexpr auto open = WellCompletion::StateEnum::OPEN;
BOOST_CHECK_EQUAL( 7U, cs.size() );
BOOST_CHECK_EQUAL(open, cs.getFromIJK( 7, 6, 2 ).state);
BOOST_CHECK_EQUAL(open, cs.getFromIJK( 7, 6, 3 ).state);
BOOST_CHECK_EQUAL(open, cs.getFromIJK( 7, 6, 4 ).state);
BOOST_CHECK_EQUAL(open, cs.getFromIJK( 7, 7, 2 ).state);
BOOST_CHECK_EQUAL(open, cs.getFromIJK( 7, 6, 2 ).state());
BOOST_CHECK_EQUAL(open, cs.getFromIJK( 7, 6, 3 ).state());
BOOST_CHECK_EQUAL(open, cs.getFromIJK( 7, 6, 4 ).state());
BOOST_CHECK_EQUAL(open, cs.getFromIJK( 7, 7, 2 ).state());
const auto& cs2 = well->getConnections( 4 );
BOOST_CHECK_EQUAL(open, cs2.getFromIJK( 7, 6, 2 ).state);
BOOST_CHECK_EQUAL(open, cs2.getFromIJK( 7, 6, 3 ).state);
BOOST_CHECK_EQUAL(open, cs2.getFromIJK( 7, 6, 4 ).state);
BOOST_CHECK_EQUAL(open, cs2.getFromIJK( 7, 7, 2 ).state);
BOOST_CHECK_EQUAL(open, cs2.getFromIJK( 7, 6, 2 ).state());
BOOST_CHECK_EQUAL(open, cs2.getFromIJK( 7, 6, 3 ).state());
BOOST_CHECK_EQUAL(open, cs2.getFromIJK( 7, 6, 4 ).state());
BOOST_CHECK_EQUAL(open, cs2.getFromIJK( 7, 7, 2 ).state());
well = schedule.getWell("OP_3");
const auto& cs3 = well->getConnections( 3 );
BOOST_CHECK_EQUAL(shut, cs3.get( 0 ).state);
BOOST_CHECK_EQUAL(shut, cs3.get( 0 ).state());
const auto& cs4 = well->getConnections( 4 );
BOOST_CHECK_EQUAL(open, cs4.get( 0 ).state);
BOOST_CHECK_EQUAL(open, cs4.get( 0 ).state());
well = schedule.getWell("OP_1");
BOOST_CHECK_EQUAL(WellCommon::StatusEnum::SHUT, well->getStatus( 3 ));
@@ -1072,17 +1072,17 @@ BOOST_AUTO_TEST_CASE(createDeckWithWPIMULT) {
const auto& cs2 = well->getConnections( 2 );
for(size_t i = 0; i < cs2.size(); i++) {
BOOST_CHECK_EQUAL(cs2.get( i ).wellPi, 1.3);
BOOST_CHECK_EQUAL(cs2.get( i ).wellPi(), 1.3);
}
const auto& cs3 = well->getConnections( 3 );
for(size_t i = 0; i < cs3.size(); i++ ) {
BOOST_CHECK_EQUAL(cs3.get( i ).wellPi, (1.3*1.3));
BOOST_CHECK_EQUAL(cs3.get( i ).wellPi(), (1.3*1.3));
}
const auto& cs4 = well->getConnections( 4 );
for(size_t i = 0; i < cs4.size(); i++ ) {
BOOST_CHECK_EQUAL(cs4.get( i ).wellPi, 1.0);
BOOST_CHECK_EQUAL(cs4.get( i ).wellPi(), 1.0);
}
}
@@ -1673,16 +1673,16 @@ BOOST_AUTO_TEST_CASE( COMPDAT_sets_automatic_complnum ) {
Schedule schedule( deck, grid, eclipseProperties, Phases( true, true, true ) ,ctx);
const auto& cs1 = schedule.getWell( "W1" )->getConnections( 1 );
BOOST_CHECK_EQUAL( -1, cs1.get( 0 ).complnum );
BOOST_CHECK_EQUAL( -2, cs1.get( 1 ).complnum );
BOOST_CHECK_EQUAL( -3, cs1.get( 2 ).complnum );
BOOST_CHECK_EQUAL( -4, cs1.get( 3 ).complnum );
BOOST_CHECK_EQUAL( -1, cs1.get( 0 ).complnum() );
BOOST_CHECK_EQUAL( -2, cs1.get( 1 ).complnum() );
BOOST_CHECK_EQUAL( -3, cs1.get( 2 ).complnum() );
BOOST_CHECK_EQUAL( -4, cs1.get( 3 ).complnum() );
const auto& cs2 = schedule.getWell( "W1" )->getConnections( 2 );
BOOST_CHECK_EQUAL( -1, cs2.get( 0 ).complnum );
BOOST_CHECK_EQUAL( -2, cs2.get( 1 ).complnum );
BOOST_CHECK_EQUAL( -3, cs2.get( 2 ).complnum );
BOOST_CHECK_EQUAL( -4, cs2.get( 3 ).complnum );
BOOST_CHECK_EQUAL( -1, cs2.get( 0 ).complnum() );
BOOST_CHECK_EQUAL( -2, cs2.get( 1 ).complnum() );
BOOST_CHECK_EQUAL( -3, cs2.get( 2 ).complnum() );
BOOST_CHECK_EQUAL( -4, cs2.get( 3 ).complnum() );
}
BOOST_AUTO_TEST_CASE( COMPDAT_multiple_wells ) {
@@ -1717,20 +1717,20 @@ BOOST_AUTO_TEST_CASE( COMPDAT_multiple_wells ) {
Schedule schedule( deck, grid, eclipseProperties, Phases( true, true, true ) ,ctx);
const auto& w1cs = schedule.getWell( "W1" )->getConnections();
BOOST_CHECK_EQUAL( -1, w1cs.get( 0 ).complnum );
BOOST_CHECK_EQUAL( -2, w1cs.get( 1 ).complnum );
BOOST_CHECK_EQUAL( -3, w1cs.get( 2 ).complnum );
BOOST_CHECK_EQUAL( -4, w1cs.get( 3 ).complnum );
BOOST_CHECK_EQUAL( -5, w1cs.get( 4 ).complnum );
BOOST_CHECK_EQUAL( -1, w1cs.get( 0 ).complnum() );
BOOST_CHECK_EQUAL( -2, w1cs.get( 1 ).complnum() );
BOOST_CHECK_EQUAL( -3, w1cs.get( 2 ).complnum() );
BOOST_CHECK_EQUAL( -4, w1cs.get( 3 ).complnum() );
BOOST_CHECK_EQUAL( -5, w1cs.get( 4 ).complnum() );
const auto& w2cs = schedule.getWell( "W2" )->getConnections();
BOOST_CHECK_EQUAL( -1, w2cs.getFromIJK( 4, 4, 2 ).complnum );
BOOST_CHECK_EQUAL( -2, w2cs.getFromIJK( 4, 4, 0 ).complnum );
BOOST_CHECK_EQUAL( -3, w2cs.getFromIJK( 4, 4, 1 ).complnum );
BOOST_CHECK_EQUAL( -4, w2cs.getFromIJK( 4, 4, 3 ).complnum );
BOOST_CHECK_EQUAL( -5, w2cs.getFromIJK( 4, 4, 4 ).complnum );
BOOST_CHECK_EQUAL( -1, w2cs.getFromIJK( 4, 4, 2 ).complnum() );
BOOST_CHECK_EQUAL( -2, w2cs.getFromIJK( 4, 4, 0 ).complnum() );
BOOST_CHECK_EQUAL( -3, w2cs.getFromIJK( 4, 4, 1 ).complnum() );
BOOST_CHECK_EQUAL( -4, w2cs.getFromIJK( 4, 4, 3 ).complnum() );
BOOST_CHECK_EQUAL( -5, w2cs.getFromIJK( 4, 4, 4 ).complnum() );
BOOST_CHECK_THROW( w2cs.get( 5 ).complnum, std::out_of_range );
BOOST_CHECK_THROW( w2cs.get( 5 ).complnum(), std::out_of_range );
}
BOOST_AUTO_TEST_CASE( COMPDAT_multiple_records_same_completion ) {
@@ -1762,9 +1762,9 @@ BOOST_AUTO_TEST_CASE( COMPDAT_multiple_records_same_completion ) {
const auto& cs = schedule.getWell( "W1" )->getConnections();
BOOST_CHECK_EQUAL( 3U, cs.size() );
BOOST_CHECK_EQUAL( -1, cs.get( 0 ).complnum );
BOOST_CHECK_EQUAL( -2, cs.get( 1 ).complnum );
BOOST_CHECK_EQUAL( -3, cs.get( 2 ).complnum );
BOOST_CHECK_EQUAL( -1, cs.get( 0 ).complnum() );
BOOST_CHECK_EQUAL( -2, cs.get( 1 ).complnum() );
BOOST_CHECK_EQUAL( -3, cs.get( 2 ).complnum() );
}
BOOST_AUTO_TEST_CASE( complump_less_than_1 ) {
@@ -1843,20 +1843,20 @@ BOOST_AUTO_TEST_CASE( complump ) {
const auto& sc0 = well.getConnections( 0 );
/* complnum should be modified by COMPLNUM */
BOOST_CHECK_EQUAL( 1, sc0.getFromIJK( 2, 2, 0 ).complnum );
BOOST_CHECK_EQUAL( 1, sc0.getFromIJK( 2, 2, 1 ).complnum );
BOOST_CHECK_EQUAL( 1, sc0.getFromIJK( 2, 2, 2 ).complnum );
BOOST_CHECK( sc0.getFromIJK( 2, 2, 3 ).complnum < 0);
BOOST_CHECK_EQUAL( 1, sc0.getFromIJK( 2, 2, 0 ).complnum() );
BOOST_CHECK_EQUAL( 1, sc0.getFromIJK( 2, 2, 1 ).complnum() );
BOOST_CHECK_EQUAL( 1, sc0.getFromIJK( 2, 2, 2 ).complnum() );
BOOST_CHECK( sc0.getFromIJK( 2, 2, 3 ).complnum() < 0);
BOOST_CHECK_EQUAL( shut, sc0.getFromIJK( 2, 2, 0 ).state );
BOOST_CHECK_EQUAL( shut, sc0.getFromIJK( 2, 2, 1 ).state );
BOOST_CHECK_EQUAL( shut, sc0.getFromIJK( 2, 2, 2 ).state );
BOOST_CHECK_EQUAL( shut, sc0.getFromIJK( 2, 2, 0 ).state() );
BOOST_CHECK_EQUAL( shut, sc0.getFromIJK( 2, 2, 1 ).state() );
BOOST_CHECK_EQUAL( shut, sc0.getFromIJK( 2, 2, 2 ).state() );
const auto& sc1 = well.getConnections( 1 );
BOOST_CHECK_EQUAL( open, sc1.getFromIJK( 2, 2, 0 ).state );
BOOST_CHECK_EQUAL( open, sc1.getFromIJK( 2, 2, 1 ).state );
BOOST_CHECK_EQUAL( open, sc1.getFromIJK( 2, 2, 2 ).state );
BOOST_CHECK_EQUAL( shut, sc1.getFromIJK( 2, 2, 3 ).state );
BOOST_CHECK_EQUAL( open, sc1.getFromIJK( 2, 2, 0 ).state() );
BOOST_CHECK_EQUAL( open, sc1.getFromIJK( 2, 2, 1 ).state() );
BOOST_CHECK_EQUAL( open, sc1.getFromIJK( 2, 2, 2 ).state() );
BOOST_CHECK_EQUAL( shut, sc1.getFromIJK( 2, 2, 3 ).state() );
const auto completions = well.getCompletions(1);
BOOST_CHECK_EQUAL(completions.size(), 4);
@@ -1930,23 +1930,23 @@ BOOST_AUTO_TEST_CASE( COMPLUMP_specific_coordinates ) {
const auto& cs2 = well.getConnections( 2 );
BOOST_CHECK_EQUAL( 9U, cs1.size() );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 0, 0, 1 ).state );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 2, 2, 0 ).state );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 2, 2, 1 ).state );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 2, 2, 2 ).state );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 1, 1, 0 ).state );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 1, 1, 3 ).state );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 1, 1, 4 ).state );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 1, 1, 5 ).state );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 0, 0, 1 ).state() );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 2, 2, 0 ).state() );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 2, 2, 1 ).state() );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 2, 2, 2 ).state() );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 1, 1, 0 ).state() );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 1, 1, 3 ).state() );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 1, 1, 4 ).state() );
BOOST_CHECK_EQUAL( shut, cs1.getFromIJK( 1, 1, 5 ).state() );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 0, 0, 1 ).state );
BOOST_CHECK_EQUAL( shut, cs2.getFromIJK( 2, 2, 0 ).state );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 2, 2, 1 ).state );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 2, 2, 2 ).state );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 1, 1, 0 ).state );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 1, 1, 3 ).state );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 1, 1, 4 ).state );
BOOST_CHECK_EQUAL( shut, cs2.getFromIJK( 1, 1, 5 ).state );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 0, 0, 1 ).state() );
BOOST_CHECK_EQUAL( shut, cs2.getFromIJK( 2, 2, 0 ).state() );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 2, 2, 1 ).state() );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 2, 2, 2 ).state() );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 1, 1, 0 ).state() );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 1, 1, 3 ).state() );
BOOST_CHECK_EQUAL( open, cs2.getFromIJK( 1, 1, 4 ).state() );
BOOST_CHECK_EQUAL( shut, cs2.getFromIJK( 1, 1, 5 ).state() );
}
BOOST_AUTO_TEST_CASE(TestCompletionStateEnum2String) {

View File

@@ -98,9 +98,9 @@ static Deck createDeck( const std::string& summary ) {
static std::vector< std::string > sorted_names( const SummaryConfig& summary ) {
std::vector< std::string > ret;
for( const auto& x : summary ) {
auto wgname = x.wgname();
auto wgname = smspec_node_get_wgname(x.get());
if(wgname)
ret.push_back( x.wgname() );
ret.push_back( smspec_node_get_wgname(x.get()));
}
std::sort( ret.begin(), ret.end() );
@@ -110,7 +110,7 @@ static std::vector< std::string > sorted_names( const SummaryConfig& summary ) {
static std::vector< std::string > sorted_keywords( const SummaryConfig& summary ) {
std::vector< std::string > ret;
for( const auto& x : summary )
ret.push_back( x.keyword() );
ret.push_back( smspec_node_get_keyword(x.get()));
std::sort( ret.begin(), ret.end() );
return ret;
@@ -119,7 +119,7 @@ static std::vector< std::string > sorted_keywords( const SummaryConfig& summary
static std::vector< std::string > sorted_key_names( const SummaryConfig& summary ) {
std::vector< std::string > ret;
for( const auto& x : summary ) {
ret.push_back( x.key1() );
ret.push_back( smspec_node_get_gen_key1(x.get()));
}
std::sort( ret.begin(), ret.end() );

View File

@@ -340,15 +340,14 @@ BOOST_AUTO_TEST_CASE(NewWellZeroCompletions) {
// Helper function for CompletionOrder test.
inline Opm::Connection connection( int i, int j, int k, int complnum = 1 ) {
return Opm::Connection { i, j, k,
complnum,
k*1.0,
Opm::WellCompletion::AUTO,
Opm::Value<double>("ConnectionTransmissibilityFactor",99.88),
Opm::Value<double>("D",22.33),
Opm::Value<double>("SKIN",33.22),
Opm::Value<double>("Kh",17.29),
0,
Opm::WellCompletion::DirectionEnum::Z };
complnum,
k*1.0,
Opm::WellCompletion::AUTO,
99.88,
17.29,
0.25,
0,
Opm::WellCompletion::DirectionEnum::Z };
}

View File

@@ -345,6 +345,8 @@ BOOST_AUTO_TEST_CASE( RestartConfig2 ) {
else if (234 == report_step) rptConfig.push_back( std::make_tuple(report_step, true, boost::gregorian::date(2012,1,1)));
else if (240 == report_step) rptConfig.push_back( std::make_tuple(report_step, true, boost::gregorian::date(2012,7,1)));
else if (246 == report_step) rptConfig.push_back( std::make_tuple(report_step, true, boost::gregorian::date(2013,1,1)));
// output when SAVE is in the deck
else if (251 == report_step) rptConfig.push_back( std::make_tuple(report_step, true, boost::gregorian::date(2013,5,2)));
else rptConfig.push_back( std::make_tuple(report_step, false, boost::gregorian::date(2000,1,1)));
}

View File

@@ -577,26 +577,26 @@ BOOST_AUTO_TEST_CASE( MULTISEGMENT_ABS ) {
BOOST_CHECK_EQUAL(7U, connections.size());
const Connection& connection5 = connections.get(4);
const int seg_number_connection5 = connection5.segment_number;
const double connection5_depth = connection5.center_depth;
const int seg_number_connection5 = connection5.segment();
const double connection5_depth = connection5.depth();
BOOST_CHECK_EQUAL(seg_number_connection5, 6);
BOOST_CHECK_CLOSE(connection5_depth, 2538.83, 0.001);
const Connection& connection6 = connections.get(5);
const int seg_number_connection6 = connection6.segment_number;
const double connection6_depth = connection6.center_depth;
const int seg_number_connection6 = connection6.segment();
const double connection6_depth = connection6.depth();
BOOST_CHECK_EQUAL(seg_number_connection6, 6);
BOOST_CHECK_CLOSE(connection6_depth, 2537.83, 0.001);
const Connection& connection1 = connections.get(0);
const int seg_number_connection1 = connection1.segment_number;
const double connection1_depth = connection1.center_depth;
const int seg_number_connection1 = connection1.segment();
const double connection1_depth = connection1.depth();
BOOST_CHECK_EQUAL(seg_number_connection1, 1);
BOOST_CHECK_EQUAL(connection1_depth, 2512.5);
const Connection& connection3 = connections.get(2);
const int seg_number_connection3 = connection3.segment_number;
const double connection3_depth = connection3.center_depth;
const int seg_number_connection3 = connection3.segment();
const double connection3_depth = connection3.depth();
BOOST_CHECK_EQUAL(seg_number_connection3, 3);
BOOST_CHECK_EQUAL(connection3_depth, 2562.5);
}

View File

@@ -349,13 +349,11 @@ BOOST_AUTO_TEST_CASE(WellTestCOMPDAT) {
const auto& connections = well1->getConnections(3);
BOOST_CHECK_EQUAL(4U, connections.size());
BOOST_CHECK_EQUAL(WellCompletion::OPEN, connections.get(3).state);
BOOST_CHECK_EQUAL(2.2836805555555556e-12 , connections.get(3).getConnectionTransmissibilityFactor());
BOOST_CHECK_EQUAL(0.311/Metric::Length, connections.get(3).getDiameter());
BOOST_CHECK_EQUAL(3.3, connections.get(3).getSkinFactor());
BOOST_CHECK_EQUAL(WellCompletion::OPEN, connections.get(3).state());
BOOST_CHECK_EQUAL(2.2836805555555556e-12 , connections.get(3).CF());
BOOST_CHECK_EQUAL(4U, well1->getConnections( 7 ).size() );
BOOST_CHECK_EQUAL(WellCompletion::SHUT, well1->getConnections( 7 ).get( 3 ).state );
BOOST_CHECK_EQUAL(WellCompletion::SHUT, well1->getConnections( 7 ).get( 3 ).state() );
}
}

View File

@@ -39,6 +39,15 @@ ACTNUM
PORO
1000*0.2 /
PERMX
1000*50 /
PERMY
1000*50 /
PERMZ
1000*10 /
REGIONS
FIPNUM
@@ -578,6 +587,12 @@ CGPT
'W_3' /
/
CTFAC
'W_1' /
'W_2' /
'W_3' /
/
---- Connection production rates
----CGFR
----'E-4AH' /
@@ -598,12 +613,12 @@ WELSPECS
-- Completion data.
COMPDAT
-- 'Well' I J K1 K2
-- Passing 0 to I/J means they'll get the well head I/J
W_1 0 0 1 1 / -- Active index: 0
W_2 0 0 1 1 / -- Active index: 1
W_2 0 0 2 2 / -- Active index: 101
W_3 0 0 1 1 / -- Active index: 2
-- 'Well' I J K1 K2 STATUS SatTable TransFact Rw Kh S
W_1 0 0 1 1 2* 100 3* / -- Active index: 0
W_2 0 0 1 1 2* 1* 5 20 0.5 / -- Active index: 1
W_2 0 0 2 2 2* 1* 5 10 0.2 / -- Active index: 101
W_3 0 0 1 1 2* 1* 2* 0.7 / -- Active index: 2
/
WPOLYMER

View File

@@ -38,7 +38,7 @@
// ERT stuff
#include <ert/util/ert_unique_ptr.hpp>
#include <ert/util/TestArea.hpp>
#include <ert/util/test_work_area.h>
#include <ert/ecl/ecl_kw.h>
#include <ert/ecl/ecl_grid.h>
@@ -287,8 +287,6 @@ BOOST_AUTO_TEST_CASE(EclipseIOIntegration) {
"'PROD' 'G' 3 3 1000 'OIL' /\n"
"/\n";
ERT::TestArea ta("test_ecl_writer");
auto write_and_check = [&]( int first = 1, int last = 5 ) {
ParseContext parse_context;
auto deck = Parser().parseString( deckString, parse_context );
@@ -376,6 +374,8 @@ BOOST_AUTO_TEST_CASE(EclipseIOIntegration) {
* * https://github.com/OPM/opm-simulators/issues/753
* * https://github.com/OPM/opm-output/pull/61
*/
test_work_area_type * work_area = test_work_area_alloc("test_ecl_writer");
const auto file_size = write_and_check();
for( int i = 0; i < 3; ++i )
@@ -395,6 +395,7 @@ BOOST_AUTO_TEST_CASE(EclipseIOIntegration) {
* the file
*/
BOOST_CHECK_EQUAL( file_size, write_and_check( 3, 5 ) );
test_work_area_free(work_area);
}
BOOST_AUTO_TEST_CASE(OPM_XWEL) {

View File

@@ -407,6 +407,8 @@ struct Setup {
schedule( deck, grid, es.get3DProperties(), es.runspec().phases(), parseContext),
summary_config( deck, schedule, es.getTableManager( ), parseContext)
{
auto& io_config = es.getIOConfig();
io_config.setEclCompatibleRST(false);
}
};
@@ -433,6 +435,62 @@ BOOST_AUTO_TEST_CASE(EclipseReadWriteWellStateData) {
}
BOOST_AUTO_TEST_CASE(ECL_FORMATTED) {
Setup setup("FIRST_SIM.DATA");
test_work_area_type * test_area = test_work_area_alloc("test_Restart");
auto& io_config = setup.es.getIOConfig();
{
auto num_cells = setup.grid.getNumActive( );
auto cells = mkSolution( num_cells );
auto wells = mkWells();
{
RestartValue restart_value(cells, wells);
io_config.setEclCompatibleRST( false );
restart_value.addExtra("EXTRA", UnitSystem::measure::pressure, {10,1,2,3});
RestartIO::save("OPM_FILE.UNRST", 1 ,
100,
restart_value,
setup.es,
setup.grid,
setup.schedule,
true);
{
ecl_file_type * rst_file = ecl_file_open( "OPM_FILE.UNRST" , 0 );
ecl_kw_type * swat = ecl_file_iget_named_kw(rst_file, "SWAT", 0);
BOOST_CHECK_EQUAL( ECL_DOUBLE_TYPE, ecl_kw_get_type(swat));
BOOST_CHECK( ecl_file_has_kw(rst_file, "EXTRA"));
ecl_file_close(rst_file);
}
io_config.setEclCompatibleRST( true );
RestartIO::save("ECL_FILE.UNRST", 1 ,
100,
restart_value,
setup.es,
setup.grid,
setup.schedule,
true);
{
ecl_file_type * rst_file = ecl_file_open( "ECL_FILE.UNRST" , 0 );
ecl_kw_type * swat = ecl_file_iget_named_kw(rst_file, "SWAT", 0);
BOOST_CHECK_EQUAL( ECL_FLOAT_TYPE, ecl_kw_get_type(swat));
BOOST_CHECK( !ecl_file_has_kw(rst_file, "EXTRA"));
BOOST_CHECK( !ecl_file_has_kw(rst_file, "OPM_XWEL"));
BOOST_CHECK( !ecl_file_has_kw(rst_file, "OPM_IWEL"));
ecl_file_close(rst_file);
}
}
}
test_work_area_free(test_area);
}
void compare_equal( const RestartValue& fst,
const RestartValue& snd ,

View File

@@ -1036,6 +1036,11 @@ BOOST_AUTO_TEST_CASE(BLOCK_VARIABLES) {
BOOST_CHECK_CLOSE( 8.0 , units.to_si( UnitSystem::measure::identity , ecl_sum_get_general_var( resp, 1, "BSWAT:1,1,1")) , 1e-5);
BOOST_CHECK_CLOSE( 9.0 , units.to_si( UnitSystem::measure::identity , ecl_sum_get_general_var( resp, 1, "BSGAS:1,1,1")) , 1e-5);
BOOST_CHECK_CLOSE( 100 , ecl_sum_get_well_completion_var( resp, 1, "W_1", "CTFAC", 1) , 1e-5);
BOOST_CHECK_CLOSE( 2.1430730819702148 , ecl_sum_get_well_completion_var( resp, 1, "W_2", "CTFAC", 2) , 1e-5);
BOOST_CHECK_CLOSE( 2.6788413524627686 , ecl_sum_get_well_completion_var( resp, 1, "W_2", "CTFAC", 102) , 1e-5);
BOOST_CHECK_CLOSE( 2.7855057716369629 , ecl_sum_get_well_completion_var( resp, 1, "W_3", "CTFAC", 3) , 1e-5);
// Cell is not active
BOOST_CHECK( !ecl_sum_has_general_var( resp , "BPR:2,1,10"));
}

View File

@@ -37,7 +37,7 @@
#include <ert/ecl/ecl_sum.h>
#include <ert/ecl/ecl_file.h>
#include <ert/util/util.h>
#include <ert/util/TestArea.hpp>
#include <ert/util/test_work_area.h>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
@@ -53,15 +53,19 @@ using namespace Opm;
struct setup {
Deck deck;
EclipseState es;
ERT::TestArea ta;
test_work_area_type * ta;
setup( const std::string& path , const ParseContext& parseContext = ParseContext( )) :
deck( Parser().parseFile( path, parseContext ) ),
es( deck, ParseContext() ),
ta( ERT::TestArea("test_tables") )
ta( test_work_area_alloc( "test_tables"))
{
}
~setup() {
test_work_area_free(this->ta);
}
};
namespace {

View File

@@ -56,7 +56,7 @@ BOOST_AUTO_TEST_CASE( serialize_icon_test )
const size_t offset = w_offset + c_offset;
BOOST_CHECK_EQUAL(icondata[offset + ICON_IC_INDEX],
c.complnum);
c.complnum());
BOOST_CHECK_EQUAL(icondata[offset + ICON_I_INDEX],
c.getI() + 1);
BOOST_CHECK_EQUAL(icondata[offset + ICON_J_INDEX],
@@ -64,9 +64,9 @@ BOOST_AUTO_TEST_CASE( serialize_icon_test )
BOOST_CHECK_EQUAL(icondata[offset + ICON_K_INDEX],
c.getK() + 1);
BOOST_CHECK_EQUAL(icondata[offset + ICON_DIRECTION_INDEX],
c.dir);
c.dir());
if (c.state == Opm::WellCompletion::StateEnum::OPEN)
if (c.state() == Opm::WellCompletion::StateEnum::OPEN)
BOOST_CHECK_EQUAL(icondata[offset + ICON_STATUS_INDEX],
1);
else
@@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE( serialize_icon_test )
if (c.attachedToSegment())
BOOST_CHECK_EQUAL(icondata[offset + ICON_SEGMENT_INDEX],
c.segment_number);
c.segment());
else
BOOST_CHECK_EQUAL(icondata[offset + ICON_SEGMENT_INDEX],
0);

View File

@@ -56,15 +56,12 @@ BOOST_AUTO_TEST_CASE( serialize_scon_test )
for (const auto c : w->getConnections(tstep)) {
const size_t offset = w_offset + c_offset;
const auto ctf = c.getConnectionTransmissibilityFactorAsValueObject();
const double expected =
ctf.hasValue()
? units.from_si(Opm::UnitSystem::measure::transmissibility, ctf.getValue())
: Opm::RestartIO::Helpers::UNIMPLEMENTED_VALUE;
const double expected_cf = units.from_si(Opm::UnitSystem::measure::transmissibility, c.CF());
const double expected_kh = units.from_si(Opm::UnitSystem::measure::effective_Kh, c.Kh());
BOOST_CHECK_EQUAL(scondata[offset + SCON_CF_INDEX],
expected);
expected_cf);
BOOST_CHECK_EQUAL(scondata[offset + SCON_KH_INDEX],
Opm::RestartIO::Helpers::UNIMPLEMENTED_VALUE);
expected_kh);
c_offset += SCONZ;
}