python binding for EclFile

This commit is contained in:
Torbjørn Skille 2019-10-26 17:33:43 +02:00 committed by Joakim Hove
parent 0b8a42fd1f
commit 7cacc734e8
11 changed files with 41158 additions and 1 deletions

View File

@ -273,6 +273,20 @@ if (OPM_ENABLE_PYTHON)
add_custom_target(opmcommon_python ALL DEPENDS python/python/opm/${python_lib_target})
add_dependencies(opmcommon_python opmcommon)
add_custom_command(OUTPUT python/python/opm/python${PYTHON_VER}/libopmioecl_python.so
DEPENDS
python/cxx/eclipse_io.cpp
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/python/ ${CMAKE_BINARY_DIR}/python
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_BINARY_DIR}/python/setup.py
build
build_ext
--build-lib=${CMAKE_BINARY_DIR}/python/python/opm/python${PYTHON_VER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/python
COMMENT "Building more python bindings")
# The install target is based on manually copying the python file tree to the
# installation area with a small installation script 'install.py'. Would have
# preferred to use standard setup.py install, but the setup.py based solution

73
python/cxx/eclipse_io.cpp Normal file
View File

@ -0,0 +1,73 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
#include <src/opm/io/eclipse/EclFile.cpp>
#include <opm/io/eclipse/EclIOdata.hpp>
#include <src/opm/io/eclipse/EclUtil.cpp>
#include <src/opm/common/OpmLog/OpmLog.cpp>
#include <src/opm/common/OpmLog/LogBackend.cpp>
#include <src/opm/common/OpmLog/LogUtil.cpp>
#include <src/opm/common/OpmLog/StreamLog.cpp>
#include <src/opm/common/OpmLog/Logger.cpp>
namespace py = pybind11;
class EclFileTmp : public Opm::EclIO::EclFile {
public:
EclFileTmp(const std::string& filename): Opm::EclIO::EclFile(filename) {};
py::array_t<float> getFloatNumpy(int arrIndex) {
std::vector<float> tmp=get<float>(arrIndex);
return py::array(py::dtype("f"), {tmp.size()}, {}, &tmp[0]);
};
py::array_t<double> getDoubleNumpy(int arrIndex) {
std::vector<double> tmp=get<double>(arrIndex);
return py::array(py::dtype("d"), {tmp.size()}, {}, &tmp[0]);
};
py::array_t<int> getIntegerNumpy(int arrIndex) {
std::vector<int> tmp=get<int>(arrIndex);
return py::array(py::dtype("i"), {tmp.size()}, {}, &tmp[0]);
};
};
PYBIND11_MODULE(libopmioecl_python, m) {
py::enum_<Opm::EclIO::eclArrType>(m, "eclArrType", py::arithmetic())
.value("INTE", Opm::EclIO::INTE)
.value("REAL", Opm::EclIO::REAL)
.value("DOUB", Opm::EclIO::DOUB)
.value("CHAR", Opm::EclIO::CHAR)
.value("LOGI", Opm::EclIO::LOGI)
.value("MESS", Opm::EclIO::MESS)
.export_values();
py::class_<std::vector<std::tuple<std::string,Opm::EclIO::eclArrType,int>>>(m, "EclEntry")
.def(py::init<>())
.def("__len__", [](const std::vector<std::tuple<std::string,Opm::EclIO::eclArrType,int>> &v) { return v.size(); })
.def("__getitem__", [](std::vector<std::tuple<std::string,Opm::EclIO::eclArrType,int>> &v, int item) { return v[item];})
.def("__iter__", [](std::vector<std::tuple<std::string,Opm::EclIO::eclArrType,int>> &v) {
return py::make_iterator(v.begin(), v.end());
}, py::keep_alive<0, 1>());
py::class_<EclFileTmp>(m, "EclFileBind")
.def(py::init<const std::string &>())
.def("getList", &EclFileTmp::getList)
.def("hasKey", &EclFileTmp::hasKey)
.def("loadAllData", (void (EclFileTmp::*)(void)) &EclFileTmp::loadData)
.def("loadDataByIndex", (void (EclFileTmp::*)(int)) &EclFileTmp::loadData)
.def("getRealFromIndexNumpy", &EclFileTmp::getFloatNumpy)
.def("getDoubFromIndexNumpy", &EclFileTmp::getDoubleNumpy)
.def("getInteFromIndexNumpy", &EclFileTmp::getIntegerNumpy)
.def("getLogiFromIndex", (const std::vector<bool>& (EclFileTmp::*)(int)) &EclFileTmp::get<bool>)
.def("getCharFromIndex", (const std::vector<std::string>& (EclFileTmp::*)(int)) &EclFileTmp::get<std::string>);
}

View File

@ -19,6 +19,7 @@ from .libopmcommon_python import Schedule
from .libopmcommon_python import OpmLog
from .libopmcommon_python import SummaryConfig
from .libopmioecl_python import EclFileBind, eclArrType
#from .schedule import Well, Connection, Schedule
#from .config import EclipseConfig

View File

@ -0,0 +1,71 @@
from opm._common import EclFileBind, eclArrType
class EclFile:
def __init__(self, fileName):
self.name = fileName
self.arrayNameList=[]
self.arrayTypeList=[]
self.eclfile = EclFileBind(fileName)
arrayList = self.eclfile.getList()
for name, type, size in arrayList:
self.arrayNameList.append(name)
if type==eclArrType.INTE:
self.arrayTypeList.append(eclArrType.INTE)
elif type==eclArrType.REAL:
self.arrayTypeList.append(eclArrType.REAL)
elif type==eclArrType.DOUB:
self.arrayTypeList.append(eclArrType.DOUB)
elif type==eclArrType.CHAR:
self.arrayTypeList.append(eclArrType.CHAR)
elif type==eclArrType.LOGI:
self.arrayTypeList.append(eclArrType.LOGI)
elif type==eclArrType.MESS:
self.arrayTypeList.append(eclArrType.MESS)
else:
message = "Unknown array type: %s for array: '%s' in file %s" % (repr(type),name, fileName)
raise NameError(message)
def getListOfArrays(self):
return self.arrayNameList
def getNumArrays(self):
return len(self.arrayNameList)
def hasArray(self, name):
return self.eclfile.hasKey(name)
def get(self, arg):
try:
ind = int(arg)
except:
if not arg in self.arrayNameList:
message = "Array '%s' not found in file %s" % (arg,self.name)
raise NameError(message)
ind = self.arrayNameList.index(arg)
self.eclfile.loadDataByIndex(ind)
if self.arrayTypeList[ind]==eclArrType.INTE:
array=self.eclfile.getInteFromIndexNumpy(ind)
elif self.arrayTypeList[ind]==eclArrType.REAL:
array=self.eclfile.getRealFromIndexNumpy(ind)
elif self.arrayTypeList[ind]==eclArrType.DOUB:
array=self.eclfile.getDoubFromIndexNumpy(ind)
elif self.arrayTypeList[ind]==eclArrType.LOGI:
array=self.eclfile.getLogiFromIndex(ind)
elif self.arrayTypeList[ind]==eclArrType.CHAR:
array=self.eclfile.getCharFromIndex(ind)
elif self.arrayTypeList[ind]==eclArrType.MESS:
array=[]
return array

View File

@ -56,6 +56,18 @@ ext_modules = [
undef_macros=["NDEBUG"],
include_dirs=["pybind11/include"]
),
Extension(
'libopmioecl_python',
[
'cxx/eclipse_io.cpp',
],
libraries=['opmcommon', 'boost_filesystem', 'boost_regex'],
language='c++',
undef_macros=["NDEBUG"],
include_dirs=["pybind11/include"]
)
]
setup(
@ -68,10 +80,11 @@ setup(
'opm.io.ecl_state',
'opm.io.parser',
'opm.io.schedule',
'opm.io.ecl',
'opm.tools'
],
ext_modules=ext_modules,
package_data={'opm': ['libopmcommon_python{}'.format(suffix)]},
package_data={'opm': ['libopmcommon_python{}'.format(suffix), 'libopmioecl_python'.format(suffix)]},
include_package_data=True,
license='Open Source',
zip_safe=False,

Binary file not shown.

Binary file not shown.

Binary file not shown.

40860
python/tests/data/SPE9.FINIT Normal file

File diff suppressed because it is too large Load Diff

BIN
python/tests/data/SPE9.INIT Normal file

Binary file not shown.

125
python/tests/test_eclfile.py Executable file
View File

@ -0,0 +1,125 @@
import unittest
import sys
import numpy as np
from opm.io.ecl import EclFile
class TestEclFile(unittest.TestCase):
def test_getListOfArrays(self):
refList=["INTEHEAD","LOGIHEAD","DOUBHEAD","PORV","DEPTH","DX","DY","DZ","PORO",
"PERMX","PERMY", "PERMZ","NTG","TRANX","TRANY","TRANZ","TABDIMS","TAB",
"ACTNUM","EQLNUM","FIPNUM","PVTNUM","SATNUM","TRANNNC"]
self.assertRaises(ValueError, EclFile, "/file/that/does_not_exists")
file2uf = EclFile("tests/data/SPE9.INIT")
self.assertEqual(file2uf.getNumArrays(), 24)
arrList = file2uf.getListOfArrays()
self.assertEqual(arrList, refList)
file2f = EclFile("tests/data/SPE9.FINIT")
self.assertEqual(file2f.getNumArrays(), 24)
def test_get_function(self):
file1 = EclFile("tests/data/SPE9.INIT")
first = file1.get(0)
self.assertEqual(len(first), 95)
# get fourth array in file SPE9.INIT which is PORV
test1 = file1.get(3)
test2 = file1.get("PORV")
for val1, val2 in zip(test1, test2):
self.assertEqual(val1, val2)
def test_get_function_float(self):
file1 = EclFile("tests/data/SPE9.INIT")
dzList=[20.0, 15.0, 26.0, 15.0, 16.0, 14.0, 8.0, 8.0, 18.0, 12.0, 19.0, 18.0, 20.0, 50.0, 100.0]
poroList = [0.087, 0.097, 0.111, 0.16, 0.13, 0.17, 0.17, 0.08, 0.14, 0.13, 0.12, 0.105, 0.12, 0.116, 0.157]
ft3_to_bbl = 0.1781076
refporv = []
for poro, dz in zip(dzList, poroList):
for i in range(0,600):
refporv.append(300.0*300.0*dz*poro*ft3_to_bbl)
self.assertTrue(file1.hasArray("PORV"))
porv_np = file1.get("PORV")
self.assertEqual(len(porv_np), 9000)
self.assertTrue(isinstance(porv_np, np.ndarray))
self.assertEqual(porv_np.dtype, "float32")
porv_list = file1.get("PORV")
for val1, val2 in zip(porv_np, refporv):
self.assertLess(abs(1.0 - val1/val2), 1e-6)
def test_get_function_double(self):
refTabData=[0.147E+02, 0.2E+21, 0.4E+03, 0.2E+21, 0.8E+03, 0.2E+21, 0.12E+04, 0.2E+21, 0.16E+04, 0.2E+21, 0.2E+04, 0.2E+21, 0.24E+04, 0.2E+21, 0.28E+04, 0.2E+21, 0.32E+04, 0.2E+21, 0.36E+04, 0.2E+21, 0.4E+04, 0.5E+04, 0.1E+01, 0.2E+21, 0.98814229249012E+00, 0.2E+21, 0.97513408093613E+00]
file1 = EclFile("tests/data/SPE9.INIT")
tab = file1.get("TAB")
self.assertTrue(isinstance(tab, np.ndarray))
self.assertEqual(tab.dtype, "float64")
for i in range(0, len(refTabData)):
self.assertLess(abs(1.0 - refTabData[i]/tab[i]), 1e-12 )
def test_get_function_integer(self):
refTabdims = [ 885, 1, 1, 1, 1, 1, 1, 67, 11, 2, 1, 78, 1, 78, 78, 0, 0, 0, 83, 1, 686, 40, 1, 86, 40, 1,
286, 1, 80, 1 ]
file1 = EclFile("tests/data/SPE9.INIT")
tabdims = file1.get("TABDIMS")
self.assertTrue(isinstance(tabdims, np.ndarray))
self.assertEqual(tabdims.dtype, "int32")
for i in range(0, len(refTabdims)):
self.assertEqual(refTabdims[i], tabdims[i])
def test_get_function_logi(self):
file1 = EclFile("tests/data/9_EDITNNC.INIT")
self.assertTrue(file1.hasArray("LOGIHEAD"))
logih = file1.get("LOGIHEAD")
self.assertEqual(len(logih), 121)
self.assertEqual(logih[0], True)
self.assertEqual(logih[2], False)
self.assertEqual(logih[8], True)
def test_get_function_char(self):
file1 = EclFile("tests/data/9_EDITNNC.SMSPEC")
self.assertTrue(file1.hasArray("KEYWORDS"))
keyw = file1.get("KEYWORDS")
self.assertEqual(len(keyw), 312)
self.assertEqual(keyw[0], "TIME")
self.assertEqual(keyw[16], "FWCT")
if __name__ == "__main__":
unittest.main()