ERT/libecl routines used in the EclipseGrid class has been replace by new member functions. Grid properties are

calculated and stored as private data members in EclipseGrid.

The API for the class is unchanged except for some minor changes for exportACTNUM, exportZCORN and exportCOORD.

These changes have triggered some very few modifications in the opm-grid and opm-simulators repo.
This commit is contained in:
Torbjørn Skille 2019-08-24 14:59:52 +02:00
parent c3b0dab6e5
commit 34410cbe41
20 changed files with 2953 additions and 689 deletions

View File

@ -316,7 +316,7 @@ if(ENABLE_ECL_OUTPUT)
tests/test_Tables.cpp tests/test_Tables.cpp
tests/test_Wells.cpp tests/test_Wells.cpp
tests/test_WindowedArray.cpp tests/test_WindowedArray.cpp
tests/test_writenumwells.cpp tests/test_restartwellinfo.cpp
) )
endif() endif()

View File

@ -16,9 +16,10 @@
*/ */
#include <vector> #include <vector>
#include <array>
#include <math.h> #include <math.h>
double calculateCellVol(const std::vector<double>& X, const std::vector<double>& Y, const std::vector<double>& Z); double calculateCellVol(const std::array<double,8>& X, const std::array<double,8>& Y, const std::array<double,8>& Z);

View File

@ -25,6 +25,7 @@
#include <vector> #include <vector>
#include <ert/ecl/ecl_sum.h> #include <ert/ecl/ecl_sum.h>
#include <ert/util/ert_unique_ptr.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp> #include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp> #include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>

View File

@ -26,9 +26,9 @@
#include <opm/parser/eclipse/EclipseState/Grid/MinpvMode.hpp> #include <opm/parser/eclipse/EclipseState/Grid/MinpvMode.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/PinchMode.hpp> #include <opm/parser/eclipse/EclipseState/Grid/PinchMode.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/GridDims.hpp> #include <opm/parser/eclipse/EclipseState/Grid/GridDims.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/NNC.hpp>
#include <ert/ecl/ecl_grid.h> #include <opm/io/eclipse/EclFile.hpp>
#include <ert/util/ert_unique_ptr.hpp>
#include <array> #include <array>
#include <memory> #include <memory>
@ -59,10 +59,10 @@ namespace Opm {
These constructors will make a copy of the src grid, with These constructors will make a copy of the src grid, with
zcorn and or actnum have been adjustments. zcorn and or actnum have been adjustments.
*/ */
EclipseGrid(const EclipseGrid& src, const double* zcorn , const std::vector<int>& actnum); EclipseGrid(const EclipseGrid& src) = default;
EclipseGrid(const EclipseGrid& src, const std::vector<double>& zcorn , const std::vector<int>& actnum);
EclipseGrid(const EclipseGrid& src, const std::vector<int>& actnum); EclipseGrid(const EclipseGrid& src, const std::vector<int>& actnum);
EclipseGrid(const EclipseGrid& src, const double* zcorn, const std::vector<int>& actnum);
EclipseGrid(size_t nx, size_t ny, size_t nz, EclipseGrid(size_t nx, size_t ny, size_t nz,
double dx = 1.0, double dy = 1.0, double dz = 1.0); double dx = 1.0, double dy = 1.0, double dz = 1.0);
@ -77,7 +77,6 @@ namespace Opm {
/// explicitly. If a null pointer is passed, every cell is active. /// explicitly. If a null pointer is passed, every cell is active.
EclipseGrid(const Deck& deck, const int * actnum = nullptr); EclipseGrid(const Deck& deck, const int * actnum = nullptr);
static bool hasGDFILE(const Deck& deck); static bool hasGDFILE(const Deck& deck);
static bool hasCylindricalKeywords(const Deck& deck); static bool hasCylindricalKeywords(const Deck& deck);
static bool hasCornerPointKeywords(const Deck&); static bool hasCornerPointKeywords(const Deck&);
@ -88,8 +87,7 @@ namespace Opm {
size_t activeIndex(size_t i, size_t j, size_t k) const; size_t activeIndex(size_t i, size_t j, size_t k) const;
size_t activeIndex(size_t globalIndex) const; size_t activeIndex(size_t globalIndex) const;
void save(const std::string& filename, UnitSystem::UnitType output_units) const; void save(const std::string& filename, bool formatted, const Opm::NNC& nnc, const Opm::UnitSystem& units) const;
void addNNC(const NNC& nnc);
/* /*
Observe that the there is a getGlobalIndex(i,j,k) Observe that the there is a getGlobalIndex(i,j,k)
implementation in the base class. This method - translating implementation in the base class. This method - translating
@ -105,6 +103,7 @@ namespace Opm {
applied in the 'THETA' direction; this will only apply if applied in the 'THETA' direction; this will only apply if
the theta keywords entered sum up to exactly 360 degrees! the theta keywords entered sum up to exactly 360 degrees!
*/ */
bool circle( ) const; bool circle( ) const;
bool isPinchActive( ) const; bool isPinchActive( ) const;
double getPinchThresholdThickness( ) const; double getPinchThresholdThickness( ) const;
@ -114,7 +113,6 @@ namespace Opm {
MinpvMode::ModeEnum getMinpvMode() const; MinpvMode::ModeEnum getMinpvMode() const;
const std::vector<double>& getMinpvVector( ) const; const std::vector<double>& getMinpvVector( ) const;
/* /*
Will return a vector of nactive elements. The method will Will return a vector of nactive elements. The method will
behave differently depending on the lenght of the behave differently depending on the lenght of the
@ -127,6 +125,7 @@ namespace Opm {
??? : Exception. ??? : Exception.
*/ */
template<typename T> template<typename T>
std::vector<T> compressedVector(const std::vector<T>& input_vector) const { std::vector<T> compressedVector(const std::vector<T>& input_vector) const {
if( input_vector.size() == this->getNumActive() ) { if( input_vector.size() == this->getNumActive() ) {
@ -151,13 +150,13 @@ namespace Opm {
/// Will return a vector a length num_active; where the value /// Will return a vector a length num_active; where the value
/// of each element is the corresponding global index. /// of each element is the corresponding global index.
const std::vector<int>& getActiveMap() const; const std::vector<int>& getActiveMap() const;
std::array<double, 3> getCellCenter(size_t i,size_t j, size_t k) const; const std::array<double, 3>& getCellCenter(size_t i,size_t j, size_t k) const;
std::array<double, 3> getCellCenter(size_t globalIndex) const; const std::array<double, 3>& getCellCenter(size_t globalIndex) const;
std::array<double, 3> getCornerPos(size_t i,size_t j, size_t k, size_t corner_index) const; std::array<double, 3> getCornerPos(size_t i,size_t j, size_t k, size_t corner_index) const;
double getCellVolume(size_t globalIndex) const; double getCellVolume(size_t globalIndex) const;
double getCellVolume(size_t i , size_t j , size_t k) const; double getCellVolume(size_t i , size_t j , size_t k) const;
double getCellThicknes(size_t globalIndex) const; double getCellThickness(size_t globalIndex) const;
double getCellThicknes(size_t i , size_t j , size_t k) const; double getCellThickness(size_t i , size_t j , size_t k) const;
std::array<double, 3> getCellDims(size_t i,size_t j, size_t k) const; std::array<double, 3> getCellDims(size_t i,size_t j, size_t k) const;
std::array<double, 3> getCellDims(size_t globalIndex) const; std::array<double, 3> getCellDims(size_t globalIndex) const;
bool cellActive( size_t globalIndex ) const; bool cellActive( size_t globalIndex ) const;
@ -166,19 +165,28 @@ namespace Opm {
double getCellDepth(size_t globalIndex) const; double getCellDepth(size_t globalIndex) const;
ZcornMapper zcornMapper() const; ZcornMapper zcornMapper() const;
const std::vector<double>& getCOORD() const;
const std::vector<double>& getZCORN() const;
const std::vector<int>& getACTNUM( ) const;
const std::vector<double>& getMAPAXES() const;
const std::string& getMAPUNITS() const { return m_mapunits; } ;
/* /*
The exportZCORN method will adjust the z coordinates to ensure that cells do not The fixupZCORN method is run as part of constructiong the grid. This will adjust the
overlap. The return value is the number of points which have been adjusted. z-coordinates to ensure that cells do not overlap. The return value is the number of
points which have been adjusted. The number of zcorn nodes that has ben fixed is
stored in private member zcorn_fixed.
*/ */
size_t exportZCORN( std::vector<double>& zcorn) const;
size_t fixupZCORN();
size_t getZcornFixed() { return zcorn_fixed; };
// resetACTNUM with no arguments will make all cells in the grid active.
void resetACTNUM();
void resetACTNUM( const std::vector<int>& actnum);
void exportMAPAXES( std::vector<double>& mapaxes) const;
void exportCOORD( std::vector<double>& coord) const;
void exportACTNUM( std::vector<int>& actnum) const;
void resetACTNUM( const int * actnum);
bool equal(const EclipseGrid& other) const; bool equal(const EclipseGrid& other) const;
const ecl_grid_type * c_ptr() const;
private: private:
std::vector<double> m_minpvVector; std::vector<double> m_minpvVector;
@ -186,25 +194,35 @@ namespace Opm {
Value<double> m_pinch; Value<double> m_pinch;
PinchMode::ModeEnum m_pinchoutMode; PinchMode::ModeEnum m_pinchoutMode;
PinchMode::ModeEnum m_multzMode; PinchMode::ModeEnum m_multzMode;
mutable std::vector<double> volume_cache;
mutable std::vector< int > activeMap; mutable std::vector< int > activeMap;
bool m_circle = false; bool m_circle = false;
/*
The internal class grid_ptr is a a std::unique_ptr with size_t zcorn_fixed = 0;
special copy semantics. The purpose of implementing this is bool m_useActnumFromGdfile = false;
that the EclipseGrid class can now use the default
implementation for the copy and move constructors. // Input grid data.
*/ std::vector<double> m_zcorn;
using ert_ptr = ERT::ert_unique_ptr<ecl_grid_type , ecl_grid_free>; std::vector<double> m_coord;
class grid_ptr : public ert_ptr { std::vector<double> m_mapaxes;
public: std::vector<int> m_actnum;
using ert_ptr::unique_ptr; std::string m_mapunits;
grid_ptr() = default;
grid_ptr(grid_ptr&&) = default; // Mapping to/from active cells.
grid_ptr(const grid_ptr& src) : int m_nactive;
ert_ptr( ecl_grid_alloc_copy( src.get() ) ) {} std::vector<int> m_active_to_global;
}; std::vector<int> m_global_to_active;
grid_ptr m_grid;
// Geometry data.
std::vector<double> m_volume;
std::vector<double> m_depth;
std::vector<double> m_dx;
std::vector<double> m_dy;
std::vector<double> m_dz;
std::vector<std::array<double, 3>> m_cellCenter;
void initGridFromEGridFile(Opm::EclIO::EclFile& egridfile, std::string fileName);
void initBinaryGrid(const Deck& deck); void initBinaryGrid(const Deck& deck);
void initCornerPointGrid(const std::array<int,3>& dims , void initCornerPointGrid(const std::array<int,3>& dims ,
@ -213,6 +231,8 @@ namespace Opm {
const int * actnum, const int * actnum,
const double * mapaxes); const double * mapaxes);
bool keywInputBeforeGdfile(const Deck& deck, const std::string keyword) const;
void initCylindricalGrid( const std::array<int, 3>&, const Deck&); void initCylindricalGrid( const std::array<int, 3>&, const Deck&);
void initCartesianGrid( const std::array<int, 3>&, const Deck&); void initCartesianGrid( const std::array<int, 3>&, const Deck&);
void initCornerPointGrid( const std::array<int, 3>&, const Deck&); void initCornerPointGrid( const std::array<int, 3>&, const Deck&);
@ -229,6 +249,20 @@ namespace Opm {
static std::vector<double> createDVector(const std::array<int, 3>& dims, size_t dim, const std::string& DKey, static std::vector<double> createDVector(const std::array<int, 3>& dims, size_t dim, const std::string& DKey,
const std::string& DVKey, const Deck&); const std::string& DVKey, const Deck&);
static void scatterDim(const std::array<int, 3>& dims , size_t dim , const std::vector<double>& DV , std::vector<double>& D); static void scatterDim(const std::array<int, 3>& dims , size_t dim , const std::vector<double>& DV , std::vector<double>& D);
std::vector<double> makeCoordDxDyDzTops(const std::array<int, 3>& dims, const std::vector<double>& dx, const std::vector<double>& dy, const std::vector<double>& dz, const std::vector<double>& tops) const;
std::vector<double> makeZcornDzTops(const std::array<int, 3>& dims, const std::vector<double>& dz, const std::vector<double>& tops) const ;
std::vector<double> makeZcornDzvDepthz(const std::array<int, 3>& dims, const std::vector<double>& dzv, const std::vector<double>& depthz) const;
std::vector<double> makeCoordDxvDyvDzvDepthz(const std::array<int, 3>& dims, const std::vector<double>& dxv, const std::vector<double>& dyv, const std::vector<double>& dzv, const std::vector<double>& depthz) const;
double sumIdir(int j, int k, int i1, const std::array<int, 3>& dims, const std::vector<double>& dx) const;
double sumJdir(int i, int k, int j1, const std::array<int, 3>& dims, const std::vector<double>& dy) const;
double sumKdir(int i, int j, const std::array<int, 3>& dims, const std::vector<double>& dz) const;
void calculateGeometryData();
void getCellCorners(const std::array<int, 3>& ijk, const std::array<int, 3>& dims, std::array<double,8>& X, std::array<double,8>& Y, std::array<double,8>& Z) const;
}; };
class CoordMapper { class CoordMapper {

View File

@ -21,7 +21,6 @@
#include <cmath> #include <cmath>
#include <opm/common/utility/numeric/calculateCellVol.hpp> #include <opm/common/utility/numeric/calculateCellVol.hpp>
/* /*
Cell volume calculation based on following publication: Cell volume calculation based on following publication:
@ -89,7 +88,7 @@ double perm123sign(int i1, int i2, int i3){
if ((i1 ==1 ) && (i2==2) && (i3 == 3)){ if ((i1 ==1 ) && (i2==2) && (i3 == 3)){
temp = 1.0; temp = 1.0;
} }
if ((i1 == 1) && (i2==3) && (i3 == 2)){ if ((i1 == 1) && (i2==3) && (i3 == 2)){
temp = -1.0; temp = -1.0;
} }
@ -113,7 +112,7 @@ double perm123sign(int i1, int i2, int i3){
return temp; return temp;
} }
double calculateCellVol(const std::vector<double>& X, const std::vector<double>& Y, const std::vector<double>& Z){ double calculateCellVol(const std::array<double,8>& X, const std::array<double,8>& Y, const std::array<double,8>& Z){
double volume = 0.0; double volume = 0.0;
const double* vect[3]; const double* vect[3];
@ -134,7 +133,8 @@ double calculateCellVol(const std::vector<double>& X, const std::vector<double>&
assert(false); assert(false);
vect[j] = 0; vect[j] = 0;
} }
} }
for (int pb = 0; pb < 2; ++pb){ for (int pb = 0; pb < 2; ++pb){
for (int pg = 0; pg < 2; ++pg){ for (int pg = 0; pg < 2; ++pg){

View File

@ -18,6 +18,7 @@
#include <opm/io/eclipse/EclFile.hpp> #include <opm/io/eclipse/EclFile.hpp>
#include <opm/io/eclipse/EclUtil.hpp> #include <opm/io/eclipse/EclUtil.hpp>
#include <opm/common/ErrorMacros.hpp>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@ -28,24 +29,18 @@
#include <iterator> #include <iterator>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <opm/common/ErrorMacros.hpp>
#include <iostream>
//#include <algorithm>
//#include <iostream>
//#include <list>
#include <numeric> #include <numeric>
//#include <random>
//#include <vector>
// anonymous namespace for EclFile // anonymous namespace for EclFile
namespace { namespace {
bool fileExists(const std::string& filename){
std::ifstream fileH(filename.c_str());
return fileH.good();
}
bool isFormatted(const std::string& filename) bool isFormatted(const std::string& filename)
{ {
@ -471,6 +466,11 @@ namespace Opm { namespace EclIO {
EclFile::EclFile(const std::string& filename) : inputFilename(filename) EclFile::EclFile(const std::string& filename) : inputFilename(filename)
{ {
if (!fileExists(filename)){
std::string message="Could not open EclFile: " + filename;
OPM_THROW(std::invalid_argument, message);
}
std::fstream fileH; std::fstream fileH;
formatted = isFormatted(filename); formatted = isFormatted(filename);

View File

@ -118,8 +118,7 @@ void EclipseIO::Impl::writeEGRIDFile( const NNC& nnc ) {
ECL_EGRID_FILE, ECL_EGRID_FILE,
ioConfig.getFMTOUT() )); ioConfig.getFMTOUT() ));
this->grid.addNNC( nnc ); this->grid.save( egridFile, ioConfig.getFMTOUT(), nnc, this->es.getDeckUnitSystem());
this->grid.save( egridFile, this->es.getDeckUnitSystem().getType());
} }
/* /*

View File

@ -61,7 +61,7 @@ namespace Opm {
m_simulationConfig( m_eclipseConfig.getInitConfig().restartRequested(), deck, m_eclipseProperties ), m_simulationConfig( m_eclipseConfig.getInitConfig().restartRequested(), deck, m_eclipseProperties ),
m_transMult( GridDims(deck), deck, m_eclipseProperties ) m_transMult( GridDims(deck), deck, m_eclipseProperties )
{ {
m_inputGrid.resetACTNUM(m_eclipseProperties.getIntGridProperty("ACTNUM").getData().data()); m_inputGrid.resetACTNUM(m_eclipseProperties.getIntGridProperty("ACTNUM").getData());
if( this->runspec().phases().size() < 3 ) if( this->runspec().phases().size() < 3 )
OpmLog::info("Only " + std::to_string( this->runspec().phases().size() ) OpmLog::info("Only " + std::to_string( this->runspec().phases().size() )

File diff suppressed because it is too large Load Diff

View File

@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(ActiveCompletions) {
std::vector<int> actnum(grid.getCartesianSize(), 1); std::vector<int> actnum(grid.getCartesianSize(), 1);
actnum[0] = 0; actnum[0] = 0;
grid.resetACTNUM( actnum.data() ); grid.resetACTNUM( actnum);
Opm::WellConnections active_completions(completions, grid); Opm::WellConnections active_completions(completions, grid);
BOOST_CHECK_EQUAL( active_completions.size() , 2); BOOST_CHECK_EQUAL( active_completions.size() , 2);

File diff suppressed because it is too large Load Diff

View File

@ -2960,7 +2960,6 @@ BOOST_AUTO_TEST_CASE(FilterCompletions2) {
BOOST_AUTO_TEST_CASE(VFPINJ_TEST) { BOOST_AUTO_TEST_CASE(VFPINJ_TEST) {
const char *deckData = "\ const char *deckData = "\
START\n \ START\n \

View File

@ -75,9 +75,8 @@ BOOST_AUTO_TEST_CASE(ExportFromCPGridAllActive) {
std::vector<int> actnum; std::vector<int> actnum;
actnum.push_back(100); actnum = grid.getACTNUM();
grid.exportACTNUM( actnum ); BOOST_CHECK_EQUAL( actnum.size() , 500U );
BOOST_CHECK_EQUAL( actnum.size() , 0U );
} }
@ -95,13 +94,13 @@ BOOST_AUTO_TEST_CASE(ExportFromCPGridACTNUM) {
std::vector<int> actnum; std::vector<int> actnum;
size_t volume = grid.getNX()*grid.getNY()*grid.getNZ(); size_t volume = grid.getNX()*grid.getNY()*grid.getNZ();
grid.exportCOORD( coord ); coord = grid.getCOORD();
BOOST_CHECK_EQUAL( coord.size() , (grid.getNX() + 1) * (grid.getNY() + 1) * 6); BOOST_CHECK_EQUAL( coord.size() , (grid.getNX() + 1) * (grid.getNY() + 1) * 6);
grid.exportZCORN( zcorn ); zcorn = grid.getZCORN();
BOOST_CHECK_EQUAL( zcorn.size() , volume * 8); BOOST_CHECK_EQUAL( zcorn.size() , volume * 8);
grid.exportACTNUM( actnum ); actnum = grid.getACTNUM();
BOOST_CHECK_EQUAL( actnum.size() , volume ); BOOST_CHECK_EQUAL( actnum.size() , volume );
{ {

View File

@ -723,7 +723,7 @@ BOOST_AUTO_TEST_CASE(InactiveCell) {
std::vector<int> actnum(500, 1); std::vector<int> actnum(500, 1);
// Here we deactive the cell holding connection number 2. // Here we deactive the cell holding connection number 2.
actnum[simCase.grid.getGlobalIndex(2,4,1)] = 0; actnum[simCase.grid.getGlobalIndex(2,4,1)] = 0;
simCase.grid.resetACTNUM(actnum.data()); simCase.grid.resetACTNUM(actnum);
auto conn1 = Opm::RestartIO::Helpers::AggregateConnectionData{ih.value}; auto conn1 = Opm::RestartIO::Helpers::AggregateConnectionData{ih.value};
conn1.captureDeclaredConnData(simCase.sched, conn1.captureDeclaredConnData(simCase.sched,

View File

@ -73,7 +73,7 @@ BOOST_AUTO_TEST_CASE(TestEclFile_BINARY) {
// check that exception is thrown when input file doesn't exist // check that exception is thrown when input file doesn't exist
BOOST_CHECK_THROW(EclFile file1("DUMMY.DAT") , std::runtime_error ); BOOST_CHECK_THROW(EclFile file1("DUMMY.DAT") , std::invalid_argument );
EclFile file1(testFile); EclFile file1(testFile);
@ -331,4 +331,4 @@ BOOST_AUTO_TEST_CASE(TestEcl_Write_CHAR) {
}; };
} }

View File

@ -120,19 +120,19 @@ void checkEgridFile( const EclipseGrid& eclGrid ) {
std::string keywordName(ecl_kw_get_header(eclKeyword)); std::string keywordName(ecl_kw_get_header(eclKeyword));
if (keywordName == "COORD") { if (keywordName == "COORD") {
std::vector< double > sourceData; std::vector< double > sourceData;
eclGrid.exportCOORD( sourceData ); sourceData = eclGrid.getCOORD();
auto resultData = getErtData< float >( eclKeyword ); auto resultData = getErtData< float >( eclKeyword );
compareErtData(sourceData, resultData, 1e-6); compareErtData(sourceData, resultData, 1e-6);
} }
else if (keywordName == "ZCORN") { else if (keywordName == "ZCORN") {
std::vector< double > sourceData; std::vector< double > sourceData;
eclGrid.exportZCORN(sourceData); sourceData = eclGrid.getZCORN();
auto resultData = getErtData< float >( eclKeyword ); auto resultData = getErtData< float >( eclKeyword );
compareErtData(sourceData, resultData, /*percentTolerance=*/1e-6); compareErtData(sourceData, resultData, /*percentTolerance=*/1e-6);
} }
else if (keywordName == "ACTNUM") { else if (keywordName == "ACTNUM") {
std::vector< int > sourceData( numCells ); std::vector< int > sourceData( numCells );
eclGrid.exportACTNUM(sourceData); sourceData = eclGrid.getACTNUM();
auto resultData = getErtData< int >( eclKeyword ); auto resultData = getErtData< int >( eclKeyword );
if( sourceData.empty() ) if( sourceData.empty() )

View File

@ -38,21 +38,21 @@ BOOST_AUTO_TEST_CASE (calc_cellvol)
/* This is four example cells with different geometries. */ /* This is four example cells with different geometries. */
std::vector<double> x1 {488100.140035, 488196.664549, 488085.584866, 488182.365605, 488099.065709, 488195.880889, 488084.559409, 488181.633495}; std::array<double,8> x1 {488100.140035, 488196.664549, 488085.584866, 488182.365605, 488099.065709, 488195.880889, 488084.559409, 488181.633495};
std::vector<double> y1 {6692539.945578, 6692550.834909, 6692638.574346, 6692650.086244, 6692538.810649, 6692550.080826, 6692637.628127, 6692649.429649}; std::array<double,8> y1 {6692539.945578, 6692550.834909, 6692638.574346, 6692650.086244, 6692538.810649, 6692550.080826, 6692637.628127, 6692649.429649};
std::vector<double> z1 {2841.856000, 2840.138000, 2842.042000, 2839.816000, 2846.142000, 2844.252000, 2846.244000, 2843.868000}; std::array<double,8> z1 {2841.856000, 2840.138000, 2842.042000, 2839.816000, 2846.142000, 2844.252000, 2846.244000, 2843.868000};
std::vector<double> x2 {489539.050892, 489638.562319, 489527.025803, 489627.914272, 489537.173839, 489638.562319, 489524.119410, 489627.914272}; std::array<double,8> x2 {489539.050892, 489638.562319, 489527.025803, 489627.914272, 489537.173839, 489638.562319, 489524.119410, 489627.914272};
std::vector<double> y2 {6695907.426615, 6695923.399538, 6696003.688945, 6696020.073168, 6695908.526577, 6695923.399538, 6696005.533276, 6696020.073168}; std::array<double,8> y2 {6695907.426615, 6695923.399538, 6696003.688945, 6696020.073168, 6695908.526577, 6695923.399538, 6696005.533276, 6696020.073168};
std::vector<double> z2 {2652.859000, 2651.765000, 2652.381000, 2652.608000, 2655.276000, 2651.765000, 2656.381000, 2652.608000}; std::array<double,8> z2 {2652.859000, 2651.765000, 2652.381000, 2652.608000, 2655.276000, 2651.765000, 2656.381000, 2652.608000};
std::vector<double> x3 {486819.009056, 486904.877179, 486812.975440, 486900.527416, 486818.450563, 486903.978383, 486812.975440, 486900.527416}; std::array<double,8> x3 {486819.009056, 486904.877179, 486812.975440, 486900.527416, 486818.450563, 486903.978383, 486812.975440, 486900.527416};
std::vector<double> y3 {6694966.253697, 6694998.360834, 6695061.104896, 6695086.717018, 6694967.135567, 6695000.197284, 6695061.104896, 6695086.717018}; std::array<double,8> y3 {6694966.253697, 6694998.360834, 6695061.104896, 6695086.717018, 6694967.135567, 6695000.197284, 6695061.104896, 6695086.717018};
std::vector<double> z3 {2795.193000, 2799.997000, 2792.240000, 2793.972000, 2795.671000, 2800.927000, 2792.240000, 2793.972000}; std::array<double,8> z3 {2795.193000, 2799.997000, 2792.240000, 2793.972000, 2795.671000, 2800.927000, 2792.240000, 2793.972000};
std::vector<double> x4 {489194.711544, 489259.232449, 489206.220219, 489294.099099, 489194.711544, 489254.725623, 489201.723535, 489289.423088}; std::array<double,8> x4 {489194.711544, 489259.232449, 489206.220219, 489294.099099, 489194.711544, 489254.725623, 489201.723535, 489289.423088};
std::vector<double> y4 {6694510.504054, 6694525.862296, 6694608.410134, 6694621.029382, 6694510.504054, 6694526.272988, 6694608.819829, 6694621.467114}; std::array<double,8> y4 {6694510.504054, 6694525.862296, 6694608.410134, 6694621.029382, 6694510.504054, 6694526.272988, 6694608.819829, 6694621.467114};
std::vector<double> z4 {2740.7340, 2754.7810, 2730.0150, 2726.1080, 2740.7340, 2758.3530, 2733.9490, 2730.1080}; std::array<double,8> z4 {2740.7340, 2754.7810, 2730.0150, 2726.1080, 2740.7340, 2758.3530, 2733.9490, 2730.1080};
BOOST_REQUIRE_CLOSE (calculateCellVol(x1,y1,z1), 40368.7852157, 1e-9); BOOST_REQUIRE_CLOSE (calculateCellVol(x1,y1,z1), 40368.7852157, 1e-9);
BOOST_REQUIRE_CLOSE (calculateCellVol(x2,y2,z2), 15766.9187847524, 1e-9); BOOST_REQUIRE_CLOSE (calculateCellVol(x2,y2,z2), 15766.9187847524, 1e-9);

View File

@ -0,0 +1,234 @@
/*
Copyright 2014 Statoil IT
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#define BOOST_TEST_MODULE EclipseWriter
#include <opm/common/utility/platform_dependent/disable_warnings.h>
#include <boost/test/unit_test.hpp>
#include <opm/common/utility/platform_dependent/reenable_warnings.h>
#include <opm/output/eclipse/EclipseIO.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
#include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellInjectionProperties.hpp>
#include <opm/io/eclipse/EclFile.hpp>
#include <stdio.h>
void verifyWellState(const std::string& rst_filename, const Opm::Schedule& schedule) {
/*
reference variables ref_wellList, ref_wellHead and ref_wellConn are
based on WELSPECS and COMPDAT in data deck -> testblackoilstate3.DATA.
*/
int step = std::stoi( rst_filename.substr(rst_filename.size()-1)) -1;
Opm::EclIO::EclFile rstFile(rst_filename);
std::vector<std::vector<std::string>> ref_wellList = {
{},
{"OP_1", "OP_2", "OP_3"},
{"OP_1", "OP_2", "OP_3", "OP_4", "OP_5", "OP_6"},
{"OP_1", "OP_2", "OP_3", "OP_4", "OP_5", "OP_6", "WI_1", "WI_2", "GI_1"}
};
std::vector<std::vector<std::tuple<int, int>>> ref_wellHead = {
{},
{{9,9}, {8,8}, {7,7}},
{{9,9}, {8,8}, {7,7}, {2,2}, {5,4}, {8,2}},
{{9,9}, {8,8}, {7,7}, {2,2}, {5,4}, {8,2}, {3,3}, {3,9}, {3,6}}
};
std::vector<std::vector<std::vector<std::tuple<int, int, int>>>> ref_wellConn = {
{{{}}}, // <- timestep 0
{ {{9,9,1},{9,9,2},{9,9,3},{9,9,4},{9,9,5},{9,9,6},{9,9,7},{9,9,8},{9,9,9},{9,9,10}}, // OP_1
{{8,8,1},{8,8,2},{8,8,3},{8,7,3},{8,7,4},{8,7,5},{8,7,6}}, // OP_2
{{7,7,1},{7,7,2},{7,7,3},{7,7,4},{7,7,5}} // OP_3
}, // <- timestep 1
{ {{9,9,1},{9,9,2},{9,9,3},{9,9,4},{9,9,5},{9,9,6},{9,9,7},{9,9,8},{9,9,9},{9,9,10}},
{{8,8,1},{8,8,2},{8,8,3},{8,7,3},{8,7,4},{8,7,5},{8,7,6}}, // OP_2
{{7,7,1},{7,7,2},{7,7,3},{7,7,4},{7,7,5}}, // OP_3
{{2,2,1},{2,2,2},{2,2,3},{2,2,4},{2,2,5},{2,2,6},{2,2,7},{2,2,8},{2,2,9},{2,2,10}}, // OP_4
{{5,4,1},{5,4,2},{5,4,3}}, // OP_5
{{8,2,1},{8,2,2},{8,2,3},{8,3,3},{8,4,3},{8,5,3},{8,5,4},{8,5,5},{8,5,6}} // OP_6
}, // <- timestep 2
{ {{9,9,1},{9,9,2},{9,9,3},{9,9,4},{9,9,5},{9,9,6},{9,9,7},{9,9,8},{9,9,9},{9,9,10}},
{{8,8,1},{8,8,2},{8,8,3},{8,7,3},{8,7,4},{8,7,5},{8,7,6}}, // OP_2
{{7,7,1},{7,7,2},{7,7,3},{7,7,4},{7,7,5}}, // OP_3
{{2,2,1},{2,2,2},{2,2,3},{2,2,4},{2,2,5},{2,2,6},{2,2,7},{2,2,8},{2,2,9},{2,2,10}}, // OP_4
{{5,4,1},{5,4,2},{5,4,3}}, // OP_5
{{8,2,1},{8,2,2},{8,2,3},{8,3,3},{8,4,3},{8,5,3},{8,5,4},{8,5,5},{8,5,6}}, // OP_6
{{3,3,1},{3,3,2},{3,3,3},{3,3,4},{3,3,5},{3,3,6},{3,3,7},{3,3,8},{3,3,9},{3,3,10}}, // WI_1
{{3,9,1},{3,9,2},{3,9,3},{3,9,4},{3,9,5},{3,9,6},{3,9,7}}, // WI_2
{{3,6,1},{3,6,2},{3,6,3}} // WI_3
} // <- timestep 3
};
std::vector<int> intehead = rstFile.get<int>("INTEHEAD");
std::vector<std::string> zwel;
std::vector<int> iwel;
std::vector<int> icon;
int ncwmax = intehead[17];
int niwelz = intehead[24];
int niconz = intehead[32];
if (rstFile.hasKey("ZWEL")) {
zwel = rstFile.get<std::string>("ZWEL");
}
if (rstFile.hasKey("IWEL")) {
iwel = rstFile.get<int>("IWEL");
}
if (rstFile.hasKey("ICON")) {
icon = rstFile.get<int>("ICON");
}
std::vector<std::string> wellList = schedule.wellNames(step);
//Verify number of active wells
BOOST_CHECK_EQUAL( wellList.size(), intehead[16]);
for (size_t i=0; i< wellList.size(); i++) {
// Verify wellname
BOOST_CHECK_EQUAL(zwel[i*3], ref_wellList[step][i]);
BOOST_CHECK_EQUAL(zwel[i*3], wellList[i]);
// Verify well I, J head
BOOST_CHECK_EQUAL(iwel[i*niwelz], std::get<0>(ref_wellHead[step][i]));
BOOST_CHECK_EQUAL(iwel[i*niwelz + 1], std::get<1>(ref_wellHead[step][i]));
Opm::Well2 sched_well2 = schedule.getWell2(wellList[i], step);
BOOST_CHECK_EQUAL(iwel[i*niwelz], sched_well2.getHeadI() +1 );
BOOST_CHECK_EQUAL(iwel[i*niwelz + 1], sched_well2.getHeadJ() +1 );
int sched_wtype = -99;
if (sched_well2.isProducer()) {
sched_wtype = 1;
} else {
switch( sched_well2.getInjectionProperties( ).injectorType ) {
case Opm::Well2::InjectorType::WATER:
sched_wtype = 3;
break;
case Opm::Well2::InjectorType::GAS:
sched_wtype = 4;
break;
case Opm::Well2::InjectorType::OIL:
sched_wtype = 2;
break;
default:
break;
}
}
// Verify well type
// 1 = producer, 2 = oil injection, 3 = water injector, 4 = gas injector
BOOST_CHECK_EQUAL(iwel[i*niwelz + 6], sched_wtype );
const auto& connections_set = sched_well2.getConnections();
// Verify number of connections
BOOST_CHECK_EQUAL(iwel[i*niwelz + 4], connections_set.size() );
BOOST_CHECK_EQUAL(ref_wellConn[step][i].size(), connections_set.size() );
for (size_t n=0; n< connections_set.size(); n++) {
const auto& completion = connections_set.get(n);
// Verify I, J and K indices for each connection
BOOST_CHECK_EQUAL(completion.getI()+1 , std::get<0>(ref_wellConn[step][i][n]));
BOOST_CHECK_EQUAL(completion.getJ()+1 , std::get<1>(ref_wellConn[step][i][n]));
BOOST_CHECK_EQUAL(completion.getK()+1 , std::get<2>(ref_wellConn[step][i][n]));
size_t ind = i*ncwmax*niconz+n*niconz;
BOOST_CHECK_EQUAL(completion.getI()+1 , icon[ind+1]);
BOOST_CHECK_EQUAL(completion.getJ()+1 , icon[ind+2]);
BOOST_CHECK_EQUAL(completion.getK()+1 , icon[ind+3]);
}
}
}
BOOST_AUTO_TEST_CASE(EclipseWriteRestartWellInfo) {
std::string eclipse_data_filename = "testblackoilstate3.DATA";
Opm::Parser parser;
Opm::Deck deck( parser.parseFile( eclipse_data_filename ));
Opm::EclipseState es(deck);
const Opm::EclipseGrid& grid = es.getInputGrid();
Opm::Schedule schedule( deck, es);
Opm::SummaryConfig summary_config( deck, schedule, es.getTableManager( ));
const auto num_cells = grid.getCartesianSize();
Opm::EclipseIO eclipseWriter( es, grid , schedule, summary_config);
int countTimeStep = schedule.getTimeMap().numTimesteps();
Opm::SummaryState st(std::chrono::system_clock::from_time_t(schedule.getStartTime()));
Opm::data::Solution solution;
solution.insert( "PRESSURE", Opm::UnitSystem::measure::pressure , std::vector< double >( num_cells, 1 ) , Opm::data::TargetType::RESTART_SOLUTION);
solution.insert( "SWAT" , Opm::UnitSystem::measure::identity , std::vector< double >( num_cells, 1 ) , Opm::data::TargetType::RESTART_SOLUTION);
solution.insert( "SGAS" , Opm::UnitSystem::measure::identity , std::vector< double >( num_cells, 1 ) , Opm::data::TargetType::RESTART_SOLUTION);
Opm::data::Wells wells;
for(int timestep = 0; timestep <= countTimeStep; ++timestep) {
eclipseWriter.writeTimeStep( st,
timestep,
false,
schedule.seconds(timestep),
Opm::RestartValue(solution, wells));
}
for (int i=1; i <=4; i++) {
verifyWellState("TESTBLACKOILSTATE3.X000" + std::to_string(i), schedule);
}
// cleaning up after test
for (int i=1; i <=4; i++) {
std::string fileName = "TESTBLACKOILSTATE3.X000" + std::to_string(i);
remove(fileName.c_str());
fileName = "testblackoilstate3.s000" + std::to_string(i);
remove(fileName.c_str());
}
remove("testblackoilstate3.smspec");
}

View File

@ -1,173 +0,0 @@
/*
Copyright 2014 Statoil IT
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#define BOOST_TEST_MODULE EclipseWriter
#include <opm/common/utility/platform_dependent/disable_warnings.h>
#include <boost/test/unit_test.hpp>
#include <opm/common/utility/platform_dependent/reenable_warnings.h>
#include <opm/output/eclipse/EclipseIO.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
#include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellInjectionProperties.hpp>
// ERT stuff
#include <ert/ecl/ecl_kw.h>
#include <ert/ecl/ecl_file.h>
#include <ert/ecl/ecl_kw_magic.h>
#include <ert/ecl_well/well_info.h>
#include <ert/ecl_well/well_state.h>
#include <ert/util/test_work_area.h>
using namespace Opm;
void verifyWellState(const std::string& rst_filename,
const EclipseGrid& ecl_grid,
const Schedule& schedule) {
well_info_type* well_info = well_info_alloc(ecl_grid.c_ptr());
well_info_load_rstfile(well_info, rst_filename.c_str(), false);
//Verify numwells
int numwells = well_info_get_num_wells(well_info);
BOOST_CHECK_EQUAL( numwells, schedule.numWells() );
auto wells = schedule.getWells2atEnd();
for (int i = 0; i < numwells; ++i) {
//Verify wellnames
const char * wellname = well_info_iget_well_name(well_info, i);
well_ts_type* well_ts = well_info_get_ts(well_info , wellname);
well_state_type* well_state = well_ts_iget_state(well_ts, 0);
{
const auto& sched_well = wells.at(i);
BOOST_CHECK_EQUAL( wellname, sched_well.name() );
// Verify well-head position data
const well_conn_type* well_head = well_state_get_wellhead(well_state, ECL_GRID_GLOBAL_GRID);
BOOST_CHECK_EQUAL(well_conn_get_i(well_head), sched_well.getHeadI());
BOOST_CHECK_EQUAL(well_conn_get_j(well_head), sched_well.getHeadJ());
}
for (int j = 0; j < well_ts_get_size(well_ts); ++j) {
const auto& well_at_end = schedule.getWell2atEnd(wellname);
if (!well_at_end.hasBeenDefined(j))
continue;
const auto& well = schedule.getWell2(wellname, j);
well_state = well_ts_iget_state(well_ts, j);
//Verify welltype
int well_type = ERT_UNDOCUMENTED_ZERO;
if( well.isProducer( ) ) {
well_type = ERT_PRODUCER;
}
else {
switch( well.getInjectionProperties( ).injectorType ) {
case Well2::InjectorType::WATER:
well_type = ERT_WATER_INJECTOR;
break;
case Well2::InjectorType::GAS:
well_type = ERT_GAS_INJECTOR;
break;
case Well2::InjectorType::OIL:
well_type = ERT_OIL_INJECTOR;
break;
default:
break;
}
}
int ert_well_type = well_state_get_type( well_state );
BOOST_CHECK_EQUAL( ert_well_type, well_type );
//Verify wellstatus
int ert_well_status = well_state_is_open(well_state) ? 1 : 0;
int wellstatus = well.getStatus( ) == Well2::Status::OPEN ? 1 : 0;
BOOST_CHECK_EQUAL(ert_well_status, wellstatus);
//Verify number of completion connections
const well_conn_collection_type * well_connections = well_state_get_global_connections( well_state );
size_t num_wellconnections = well_conn_collection_get_size(well_connections);
const auto& connections_set = well.getConnections();
BOOST_CHECK_EQUAL(num_wellconnections, connections_set.size());
//Verify coordinates for each completion connection
for (size_t k = 0; k < num_wellconnections; ++k) {
const well_conn_type * well_connection = well_conn_collection_iget_const(well_connections , k);
const auto& completion = connections_set.get(k);
BOOST_CHECK_EQUAL(well_conn_get_i(well_connection), completion.getI());
BOOST_CHECK_EQUAL(well_conn_get_j(well_connection), completion.getJ());
BOOST_CHECK_EQUAL(well_conn_get_k(well_connection), completion.getK());
}
}
}
well_info_free(well_info);
}
BOOST_AUTO_TEST_CASE(EclipseWriteRestartWellInfo) {
std::string eclipse_data_filename = "testblackoilstate3.DATA";
std::string eclipse_restart_filename = "TESTBLACKOILSTATE3.X0004";
test_work_area_type * test_area = test_work_area_alloc("TEST_EclipseWriteNumWells");
test_work_area_copy_file(test_area, eclipse_data_filename.c_str());
Parser parser;
Deck deck( parser.parseFile( eclipse_data_filename ));
EclipseState es(deck);
const EclipseGrid& grid = es.getInputGrid();
Schedule schedule( deck, es);
SummaryConfig summary_config( deck, schedule, es.getTableManager( ));
const auto num_cells = grid.getCartesianSize();
EclipseIO eclipseWriter( es, grid , schedule, summary_config);
int countTimeStep = schedule.getTimeMap().numTimesteps();
SummaryState st(std::chrono::system_clock::now());
data::Solution solution;
solution.insert( "PRESSURE",UnitSystem::measure::pressure , std::vector< double >( num_cells, 1 ) , data::TargetType::RESTART_SOLUTION);
solution.insert( "SWAT" ,UnitSystem::measure::identity , std::vector< double >( num_cells, 1 ) , data::TargetType::RESTART_SOLUTION);
solution.insert( "SGAS" ,UnitSystem::measure::identity , std::vector< double >( num_cells, 1 ) , data::TargetType::RESTART_SOLUTION);
data::Wells wells;
for(int timestep = 0; timestep <= countTimeStep; ++timestep){
eclipseWriter.writeTimeStep( st,
timestep,
false,
timestep,
RestartValue(solution, wells));
}
verifyWellState(eclipse_restart_filename, grid, schedule);
test_work_area_free(test_area);
}

View File

@ -79,6 +79,13 @@ COMPDAT
'OP_3' 7 7 5 5 'OPEN' 1* 4.728 0.311 420.067 1* 1* 'Y' 18.162 / 'OP_3' 7 7 5 5 'OPEN' 1* 4.728 0.311 420.067 1* 1* 'Y' 18.162 /
/ /
WCONPROD
'OP_1' OPEN ORAT 1000.0 4* 90.0 /
'OP_2' OPEN ORAT 1000.0 4* 90.0 /
'OP_3' SHUT ORAT 1000.0 4* 90.0 /
/
DATES DATES
1 JUN 1980/ 1 JUN 1980/
/ /
@ -100,24 +107,34 @@ COMPDAT
'OP_6' 8 5 5 6 'OPEN' 1* 4.728 0.311 420.067 1* 1* 'Y' 18.162 / 'OP_6' 8 5 5 6 'OPEN' 1* 4.728 0.311 420.067 1* 1* 'Y' 18.162 /
/ /
WCONPROD
'OP_4' OPEN ORAT 1000.0 4* 90.0 /
'OP_5' OPEN ORAT 1000.0 4* 90.0 /
'OP_6' OPEN ORAT 1000.0 4* 90.0 /
/
DATES DATES
1 NOV 1980/ 1 NOV 1980/
/ /
WELSPECS WELSPECS
'WI_1' 'WI' 3 3 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* / 'WI_1' 'WI' 3 3 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* /
'WI_2' 'WI' 3 9 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* / 'WI_2' 'WI' 3 9 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* /
'WI_3' 'WI' 3 6 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* / 'GI_1' 'WI' 3 6 1* 'GAS' 1* 1* 1* 1* 1* 1* 1* /
/ /
COMPDAT COMPDAT
'WI_1' 3 3 1 10 'OPEN' 1* 6.642 0.311 615.914 1* 1* 'X' 22.372 / 'WI_1' 3 3 1 10 'OPEN' 1* 6.642 0.311 615.914 1* 1* 'X' 22.372 /
'WI_2' 3 9 1 7 'OPEN' 1* 1.168 0.311 107.872 1* 1* 'Y' 21.925 / 'WI_2' 3 9 1 7 'OPEN' 1* 1.168 0.311 107.872 1* 1* 'Y' 21.925 /
'WI_3' 3 6 1 3 'OPEN' 1* 27.412 0.311 2445.337 1* 1* 'Y' 18.521 / 'GI_1' 3 6 1 3 'OPEN' 1* 27.412 0.311 2445.337 1* 1* 'Y' 18.521 /
/ /
WCONINJE
'WI_1' WATER OPEN RATE 1000.0 /
'WI_2' WATER OPEN RATE 1000.0 /
'GI_1' GAS OPEN RATE 1.0E6 /
/
DATES DATES
1 NOV 1982/ 1 NOV 1982/