Merge pull request #2844 from hakonhagland/poro2

Implements access to the porosity from Python.
This commit is contained in:
Joakim Hove 2020-11-05 11:15:12 +01:00 committed by GitHub
commit e2909958c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 734 additions and 12 deletions

View File

@ -1440,6 +1440,13 @@ public:
Scalar referencePorosity(unsigned elementIdx, unsigned timeIdx) const Scalar referencePorosity(unsigned elementIdx, unsigned timeIdx) const
{ return referencePorosity_[timeIdx][elementIdx]; } { return referencePorosity_[timeIdx][elementIdx]; }
/*!
* \brief Sets the porosity of an element
*
*/
void setPorosity(Scalar poro, unsigned elementIdx, unsigned timeIdx = 0)
{ referencePorosity_[timeIdx][elementIdx] = poro; }
/*! /*!
* \brief Returns the depth of an degree of freedom [m] * \brief Returns the depth of an degree of freedom [m]

View File

@ -388,6 +388,11 @@ namespace Opm
OpmLog::note(ss.str()); OpmLog::note(ss.str());
} }
} }
EbosSimulator *getSimulatorPtr() {
return ebosSimulator_.get();
}
private: private:
// called by execute() or executeInitStep() // called by execute() or executeInitStep()
int execute_(int (FlowMainEbos::* runOrInitFunc)(), bool cleanup) int execute_(int (FlowMainEbos::* runOrInitFunc)(), bool cleanup)

View File

@ -0,0 +1,58 @@
/*
Copyright 2020 Equinor ASA.
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/>.
*/
#ifndef OPM_PY_MATERIAL_STATE_HEADER_INCLUDED
#define OPM_PY_MATERIAL_STATE_HEADER_INCLUDED
#include <opm/models/utils/propertysystem.hh>
#include <exception>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
namespace Opm::Pybind
{
template <class TypeTag>
class PyMaterialState {
using Simulator = GetPropType<TypeTag, Opm::Properties::Simulator>;
using Problem = GetPropType<TypeTag, Opm::Properties::Problem>;
using Model = GetPropType<TypeTag, Opm::Properties::Model>;
using ElementContext = GetPropType<TypeTag, Opm::Properties::ElementContext>;
using FluidSystem = GetPropType<TypeTag, Opm::Properties::FluidSystem>;
using Indices = GetPropType<TypeTag, Opm::Properties::Indices>;
using GridView = GetPropType<TypeTag, Opm::Properties::GridView>;
public:
PyMaterialState(Simulator *ebosSimulator)
: ebosSimulator_(ebosSimulator) { }
std::unique_ptr<double []> getCellVolumes( std::size_t *size);
std::unique_ptr<double []> getPorosity( std::size_t *size);
void setPorosity(const double *poro, std::size_t size);
private:
Simulator *ebosSimulator_;
};
}
#include "PyMaterialState_impl.hpp"
#endif // OPM_PY_MATERIAL_STATE_HEADER_INCLUDED

View File

@ -0,0 +1,69 @@
/*
Copyright 2020 Equinor ASA.
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/>.
*/
namespace Opm::Pybind {
template <class TypeTag>
std::unique_ptr<double []>
PyMaterialState<TypeTag>::
getCellVolumes( std::size_t *size)
{
Model &model = ebosSimulator_->model();
*size = model.numGridDof();
auto array = std::make_unique<double []>(*size);
for (unsigned dofIdx = 0; dofIdx < *size; ++dofIdx) {
array[dofIdx] = model.dofTotalVolume(dofIdx);
}
return array;
}
template <class TypeTag>
std::unique_ptr<double []>
PyMaterialState<TypeTag>::
getPorosity( std::size_t *size)
{
Problem &problem = ebosSimulator_->problem();
Model &model = ebosSimulator_->model();
*size = model.numGridDof();
auto array = std::make_unique<double []>(*size);
for (unsigned dofIdx = 0; dofIdx < *size; ++dofIdx) {
array[dofIdx] = problem.referencePorosity(dofIdx, /*timeIdx*/0);
}
return array;
}
template <class TypeTag>
void
PyMaterialState<TypeTag>::
setPorosity(const double *poro, std::size_t size)
{
Problem &problem = ebosSimulator_->problem();
Model &model = ebosSimulator_->model();
auto model_size = model.numGridDof();
if (model_size != size) {
std::ostringstream message;
message << "Cannot set porosity. Expected array of size: "
<< model_size << ", got array of size: " << size;
throw std::runtime_error(message.str());
}
for (unsigned dofIdx = 0; dofIdx < size; ++dofIdx) {
problem.setPorosity(poro[dofIdx], dofIdx);
}
}
} //namespace Opm::Pybind

View File

@ -1,8 +1,5 @@
/* /*
Copyright 2013, 2014, 2015 SINTEF ICT, Applied Mathematics. Copyright 2020 Equinor ASA.
Copyright 2014 Dr. Blatt - HPC-Simulation-Software & Services
Copyright 2015 IRIS AS
Copyright 2014 STATOIL ASA.
This file is part of the Open Porous Media project (OPM). This file is part of the Open Porous Media project (OPM).
@ -19,21 +16,31 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef OPM_SIMULATORS_HEADER_INCLUDED #ifndef OPM_SIMULATORS_HEADER_INCLUDED
#define OPM_SIMULATORS_HEADER_INCLUDED #define OPM_SIMULATORS_HEADER_INCLUDED
#include <opm/simulators/flow/Main.hpp> #include <opm/simulators/flow/Main.hpp>
#include <opm/simulators/flow/FlowMainEbos.hpp> #include <opm/simulators/flow/FlowMainEbos.hpp>
#include <opm/models/utils/propertysystem.hh>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
namespace Opm::Pybind { namespace Opm::Pybind {
class BlackOilSimulator class BlackOilSimulator
{ {
private: private:
using FlowMainEbosType = Opm::FlowMainEbos<Opm::Properties::TTag::EclFlowProblem>; using TypeTag = Opm::Properties::TTag::EclFlowProblem;
using Simulator = Opm::GetPropType<TypeTag, Opm::Properties::Simulator>;
public: public:
BlackOilSimulator( const std::string &deckFilename); BlackOilSimulator( const std::string &deckFilename);
py::array_t<double> getPorosity();
int run(); int run();
void setPorosity(
py::array_t<double, py::array::c_style | py::array::forcecast> array);
int step(); int step();
int stepInit(); int stepInit();
int stepCleanup(); int stepCleanup();
@ -43,8 +50,10 @@ private:
bool hasRunInit_ = false; bool hasRunInit_ = false;
bool hasRunCleanup_ = false; bool hasRunCleanup_ = false;
std::unique_ptr<FlowMainEbosType> mainEbos_; std::unique_ptr<Opm::FlowMainEbos<TypeTag>> mainEbos_;
std::unique_ptr<Opm::Main> main_; std::unique_ptr<Opm::Main> main_;
Simulator *ebosSimulator_;
std::unique_ptr<PyMaterialState<TypeTag>> materialState_;
}; };
} // namespace Opm::Pybind } // namespace Opm::Pybind

View File

@ -1,3 +1,9 @@
# NOTE: we assume that add_subdirectory( pybind11 ) is called from the
# parent folder's CMakeLists.txt before this CMakeLists.txt is loaded.
# Therefore, pybind11's CMakeLists.txt has already run
# find_package(PYTHON) to define variables like
# ${PYTHON_EXECUTABLE}
#
pybind11_add_module(simulators simulators.cpp SYSTEM) pybind11_add_module(simulators simulators.cpp SYSTEM)
set_target_properties( simulators PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/python/opm2 ) set_target_properties( simulators PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/python/opm2 )
@ -13,3 +19,14 @@ set(PYTHON_PACKAGE_PATH "site-packages")
set(PYTHON_INSTALL_PREFIX "lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/${PYTHON_PACKAGE_PATH}" CACHE STRING "Subdirectory to install Python modules in") set(PYTHON_INSTALL_PREFIX "lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/${PYTHON_PACKAGE_PATH}" CACHE STRING "Subdirectory to install Python modules in")
install(TARGETS simulators DESTINATION ${DEST_PREFIX}${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTALL_PREFIX}/opm) install(TARGETS simulators DESTINATION ${DEST_PREFIX}${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTALL_PREFIX}/opm)
file( COPY ${PROJECT_SOURCE_DIR}/python/test
DESTINATION ${PROJECT_BINARY_DIR}/python)
file( COPY ${PROJECT_SOURCE_DIR}/python/test_data
DESTINATION ${PROJECT_BINARY_DIR}/python)
add_test(NAME python_tests
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/python
COMMAND ${CMAKE_COMMAND}
-E env PYTHONPATH=${PROJECT_BINARY_DIR}/python:$ENV{PYTHONPATH}
${PYTHON_EXECUTABLE} -m unittest )

View File

@ -1,3 +1,22 @@
/*
Copyright 2020 Equinor ASA.
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" #include "config.h"
#include <opm/parser/eclipse/Deck/Deck.hpp> #include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp> #include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
@ -6,7 +25,9 @@
#define FLOW_BLACKOIL_ONLY #define FLOW_BLACKOIL_ONLY
#include <opm/simulators/flow/Main.hpp> #include <opm/simulators/flow/Main.hpp>
#include <opm/simulators/flow/FlowMainEbos.hpp> #include <opm/simulators/flow/FlowMainEbos.hpp>
#include <opm/simulators/flow/python/PyMaterialState.hpp>
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/embed.h> #include <pybind11/embed.h>
// NOTE: EXIT_SUCCESS, EXIT_FAILURE is defined in cstdlib // NOTE: EXIT_SUCCESS, EXIT_FAILURE is defined in cstdlib
#include <cstdlib> #include <cstdlib>
@ -22,12 +43,27 @@ BlackOilSimulator::BlackOilSimulator( const std::string &deckFilename)
{ {
} }
py::array_t<double> BlackOilSimulator::getPorosity()
{
std::size_t len;
auto array = materialState_->getPorosity(&len);
return py::array(len, array.get());
}
int BlackOilSimulator::run() int BlackOilSimulator::run()
{ {
auto mainObject = Opm::Main( deckFilename_ ); auto mainObject = Opm::Main( deckFilename_ );
return mainObject.runDynamic(); return mainObject.runDynamic();
} }
void BlackOilSimulator::setPorosity( py::array_t<double,
py::array::c_style | py::array::forcecast> array)
{
std::size_t size_ = array.size();
const double *poro = array.data();
materialState_->setPorosity(poro, size_);
}
int BlackOilSimulator::step() int BlackOilSimulator::step()
{ {
if (!hasRunInit_) { if (!hasRunInit_) {
@ -63,6 +99,9 @@ int BlackOilSimulator::stepInit()
if (mainEbos_) { if (mainEbos_) {
int result = mainEbos_->executeInitStep(); int result = mainEbos_->executeInitStep();
hasRunInit_ = true; hasRunInit_ = true;
ebosSimulator_ = mainEbos_->getSimulatorPtr();
materialState_ = std::make_unique<PyMaterialState<TypeTag>>(
ebosSimulator_);
return result; return result;
} }
else { else {
@ -70,14 +109,18 @@ int BlackOilSimulator::stepInit()
} }
} }
} // namespace Opm::Python } // namespace Opm::Pybind
PYBIND11_MODULE(simulators, m) PYBIND11_MODULE(simulators, m)
{ {
py::class_<Opm::Pybind::BlackOilSimulator>(m, "BlackOilSimulator") using namespace Opm::Pybind;
py::class_<BlackOilSimulator>(m, "BlackOilSimulator")
.def(py::init< const std::string& >()) .def(py::init< const std::string& >())
.def("run", &Opm::Pybind::BlackOilSimulator::run) .def("get_porosity", &BlackOilSimulator::getPorosity,
.def("step", &Opm::Pybind::BlackOilSimulator::step) py::return_value_policy::copy)
.def("step_init", &Opm::Pybind::BlackOilSimulator::stepInit) .def("run", &BlackOilSimulator::run)
.def("step_cleanup", &Opm::Pybind::BlackOilSimulator::stepCleanup); .def("set_porosity", &BlackOilSimulator::setPorosity)
.def("step", &BlackOilSimulator::step)
.def("step_init", &BlackOilSimulator::stepInit)
.def("step_cleanup", &BlackOilSimulator::stepCleanup);
} }

0
python/test/__init__.py Normal file
View File

75
python/test/test_basic.py Executable file
View File

@ -0,0 +1,75 @@
import os
import unittest
from contextlib import contextmanager
from pathlib import Path
from opm2.simulators import BlackOilSimulator
@contextmanager
def pushd(path):
cwd = os.getcwd()
if not os.path.isdir(path):
os.makedirs(path)
os.chdir(path)
yield
os.chdir(cwd)
class TestBasic(unittest.TestCase):
@classmethod
def setUpClass(cls):
# NOTE: Loading the below extension module involves loading a
# a shared library like "simulators.cpython-37m-x86_64-linux-gnu.so"
# It turns out Python cannot unload this module, see:
#
# https://stackoverflow.com/a/8295590/2173773
# https://bugs.python.org/issue34309
#
# This is a problem when we want to create a new instance for each unit
# test. For example, when creating the first instance, static variables in
# in the shared object are initialized. However, when the
# corresponding Python object is later deleted (when the test finishes),
# the shared object is not unloaded and its static variables
# stays the same. So when a second Python instance is created,
# the same address is used for the static variables in the shared library
# i.e. the static variables are referring to the same memory location
# as for the first object (and they are not reinitialized).
#
# Unfortunatly, this leads to undefined behavior since the C++ code
# for flow simulation uses static variable to keep state information
# and since it was not built under the assumption that it would
# used as a shared library. It was assumed (?) that a flow simulation
# was executed from an executable file (not library file) and only
# executed once. To execute another simulation, it was assumed that
# the executable would be restarted from a controlling program like
# the Shell (which would reload and initialize the object into fresh memory).
#
# TODO: Fix the C++ code such that it allows multiple runs whith the same
# object file.
#
# NOTE: The result of the above is that we can only instantiate a
# single simulator object during the unit tests.
# This is not how the unittest module was supposed to be used. Usually one
# would write multiple test_xxx() methods that are independent and
# each method receives a new simulator object (also note that the order
# in which each test_xxx() method is called by unittest is not defined).
# However, as noted above this is not currently possible.
#
test_dir = Path(os.path.dirname(__file__))
cls.data_dir = test_dir.parent.joinpath("test_data/SPE1CASE1")
def test_all(self):
with pushd(self.data_dir):
sim = BlackOilSimulator("SPE1CASE1.DATA")
sim.step_init()
sim.step()
poro = sim.get_porosity()
self.assertEqual(len(poro), 300, 'length of porosity vector')
self.assertAlmostEqual(poro[0], 0.3, places=7, msg='value of porosity')
poro = poro *.95
sim.set_porosity(poro)
sim.step()
poro2 = sim.get_porosity()
self.assertAlmostEqual(poro2[0], 0.285, places=7, msg='value of porosity 2')

View File

@ -0,0 +1,439 @@
-- 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
-- NOTE: This deck is currently not supported by the OPM
-- simulator flow due to lack of support for DRSDT.
---------------------------------------------------------------------------
------------------------ 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' /
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
-- WELNAME GRPNAME III JJJ DEPTH PREFERRED_PHASE
'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
-- WELNAME III JJJ KUP KLOW OPEN/SHUT SATTAB TRANS DIAM
'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
-- WELLNAME OPEN/SHUT CTRLMODE OILRATE_UPLIM BHP_LOWLIM
'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
-- WELLNAME INJECTORTYP OPEN/SHUT CTRLMODE SURFTGTRATE 6 BHPUPLIMIT
'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