Adding python bindings for C++ class EclOutput

This commit is contained in:
Torbjørn Skille
2020-03-18 20:21:36 +01:00
parent 1a39c07981
commit c33372b8b4
4 changed files with 368 additions and 0 deletions

View File

@@ -8,6 +8,7 @@
#include <src/opm/io/eclipse/ESmry.cpp>
#include <src/opm/io/eclipse/EGrid.cpp>
#include <src/opm/io/eclipse/ERft.cpp>
#include <src/opm/io/eclipse/EclOutput.cpp>
#include <opm/common/utility/numeric/calculateCellVol.hpp>
@@ -22,6 +23,35 @@ namespace {
using npArray = std::tuple<py::array, Opm::EclIO::eclArrType>;
using EclEntry = std::tuple<std::string, Opm::EclIO::eclArrType, long int>;
class EclOutputBind {
public:
EclOutputBind(const std::string& filename,const bool formatted, const bool append)
{
if (append == true)
m_output = std::make_unique<Opm::EclIO::EclOutput>(filename, formatted, std::ios::app);
else
m_output = std::make_unique<Opm::EclIO::EclOutput>(filename, formatted, std::ios::out);
}
template<class T>
void writeArray(const std::string& name, const std::vector<T>& data){
m_output->write<T>(name, data);
m_output->flushStream();
}
void writeMessage(const std::string& name)
{
m_output->message(name);
m_output->flushStream();
}
private:
std::unique_ptr<Opm::EclIO::EclOutput> m_output;
};
npArray get_vector_index(Opm::EclIO::EclFile * file_ptr, std::size_t array_index)
{
auto array_type = std::get<1>(file_ptr->getList()[array_index]);
@@ -323,4 +353,19 @@ void python::common::export_IO(py::module& m) {
std::tuple<int,int,int>&) const) &Opm::EclIO::ERft::hasArray)
.def("__len__", &Opm::EclIO::ERft::numberOfReports);
py::class_<EclOutputBind>(m, "EclOutput")
.def(py::init<const std::string &, const bool, const bool>(), py::arg("filename"),
py::arg("formatted") = false, py::arg("append") = false)
.def("write_message", &EclOutputBind::writeMessage)
.def("__write_char_array", (void (EclOutputBind::*)(const std::string&,
const std::vector<std::string>&)) &EclOutputBind::writeArray)
.def("__write_logi_array", (void (EclOutputBind::*)(const std::string&,
const std::vector<bool>&)) &EclOutputBind::writeArray)
.def("__write_inte_array", (void (EclOutputBind::*)(const std::string&,
const std::vector<int>&)) &EclOutputBind::writeArray)
.def("__write_real_array", (void (EclOutputBind::*)(const std::string&,
const std::vector<float>&)) &EclOutputBind::writeArray)
.def("__write_doub_array", (void (EclOutputBind::*)(const std::string&,
const std::vector<double>&)) &EclOutputBind::writeArray);
}

View File

@@ -25,6 +25,7 @@ from .libopmcommon_python import ERst
from .libopmcommon_python import ESmry
from .libopmcommon_python import EGrid
from .libopmcommon_python import ERft
from .libopmcommon_python import EclOutput
from .libopmcommon_python import SummaryState
#from .schedule import Well, Connection, Schedule

View File

@@ -4,6 +4,7 @@ from opm._common import ERst
from opm._common import ESmry
from opm._common import EGrid
from opm._common import ERft
from opm._common import EclOutput
import sys
import datetime
@@ -177,6 +178,47 @@ def getitem_erft(self, arg):
return data
'''
EclOutput supports writing of numpy arrays. Data types
(CHAR, LOGI, REAL, DOUB and INTE) is derived from the numpy dtype property
EclOutput partly supports writing of python lists
(CHAR, LOGI, INTE)
'''
def ecloutput_write(self, name, array):
if isinstance(array, list):
if all(isinstance(element, str) for element in array):
array = np.array(array)
elif all(isinstance(element, bool) for element in array):
array = np.array(array)
elif all(isinstance(element, int) for element in array):
array = np.array(array, dtype = "int32")
elif sys.version_info.major == 2 and all(isinstance(element, unicode) for element in array):
array = np.array(array)
else:
raise ValueError("!!array {} is python list, type {}, not supported".format(name, type(array[0])))
if not isinstance(array, np.ndarray):
raise ValueError("EclOutput - write function works only for numpy arrays")
if array.dtype == "float32":
self.__write_real_array(name, array)
elif array.dtype == "int32":
self.__write_inte_array(name, array)
elif array.dtype == "int64":
print ("!Warning, writing numpy dtype=int64 to 32 bit integer format")
self.__write_inte_array(name, array)
elif array.dtype == "float64":
self.__write_doub_array(name, array)
elif array.dtype == "bool":
self.__write_logi_array(name, array)
elif array.dtype.kind in {'U', 'S'}:
self.__write_char_array(name, array)
else:
raise ValueError("unknown array type for array {}".format(name))
setattr(EclFile, "__getitem__", getitem_eclfile)
setattr(EclFile, "arrays", eclfile_get_list_of_arrays)
@@ -192,3 +234,5 @@ setattr(ERft, "__contains__", contains_erft)
setattr(ERft, "list_of_rfts", erft_list_of_rfts)
setattr(ERft, "arrays", erft_list_of_arrays)
setattr(ERft, "__getitem__",getitem_erft)
setattr(EclOutput, "write", ecloutput_write)

278
python/tests/test_ecloutput.py Executable file
View File

@@ -0,0 +1,278 @@
import unittest
import sys
import numpy as np
import io
import os
from opm.io.ecl import EclOutput, EclFile, ERst, eclArrType
try:
from tests.utils import test_path
except ImportError:
from utils import test_path
class TestEclOutput(unittest.TestCase):
def test_write_formatted_float32(self):
npArr1=np.array([1.1, 2.2e+28, 3.3, 4.4], dtype='float32')
testFile = test_path("data/RESULT.FINIT")
# default is binary format (unformatted), new file
out1 = EclOutput(testFile, formatted=True, append=False)
out1.write("ARR1", npArr1)
file1 = EclFile(testFile)
testArray = file1[0]
self.assertEqual(len(testArray), len(npArr1))
for v1,v2 in zip (testArray, npArr1):
self.assertEqual(v1, v2)
if os.path.isfile(testFile):
os.remove(testFile)
def test_write_binary_float32(self):
npArr1=np.array([1.1, 2.2e+28, 3.3, 4.4], dtype='float32')
testFile = test_path("data/RESULT.INIT")
# default is binary format (unformatted) and append = false
out1 = EclOutput(testFile)
out1.write("ARR1", npArr1)
file1 = EclFile(testFile)
testArray = file1[0]
self.assertEqual(len(testArray), len(npArr1))
for v1,v2 in zip (testArray, npArr1):
self.assertEqual(v1, v2)
if os.path.isfile(testFile):
os.remove(testFile)
def test_write_binary_append(self):
npArr1 = np.array([1.1, 2.2e+28, 3.3, 4.4], dtype='float32')
npArr2 = np.array([11.11, 12.12, 13.13, 14.14], dtype='float64')
npArr3 = np.array([1,2,3,4], dtype='int32')
npArr4 = np.array(["PROD1","","INJ1"], dtype='str')
npArr5 = np.array([True, True, False, True, False, False], dtype='bool')
#npArr6=np.array([11,12,13,14], dtype='int64')
testFile = test_path("data/RESULT.INIT")
# default is binary format (unformatted) and append = false
# will create a new file1
out1 = EclOutput(testFile)
out1.write("ARR0", npArr1)
# append = False (default value), will create a new file,
# hence, array ARR0 from out1 will be errased
out2 = EclOutput(testFile)
out2.write("ARR1", npArr1)
# append =True, will add ARR2 to existing file
out3 = EclOutput(testFile, append=True)
out3.write("ARR2", npArr2)
out3.write("ARR3", npArr3)
out3.write("ARR4", npArr4)
out3.write("ARR5", npArr5)
file1 = EclFile(testFile)
self.assertEqual(len(file1), 5)
# check array ARR1
testArray = file1[0]
self.assertEqual(len(testArray), len(npArr1))
for v1,v2 in zip (testArray, npArr1):
self.assertEqual(v1, v2)
# check array ARR2
testArray = file1[1]
self.assertEqual(len(testArray), len(npArr2))
for v1,v2 in zip (testArray, npArr2):
self.assertEqual(v1, v2)
# check array ARR3
testArray = file1[2]
self.assertEqual(len(testArray), len(npArr3))
for v1,v2 in zip (testArray, npArr3):
self.assertEqual(v1, v2)
# check array ARR4
testArray = file1[3]
self.assertEqual(len(testArray), len(npArr4))
for v1,v2 in zip (testArray, npArr4):
self.assertEqual(v1, v2)
# check array ARR5
testArray = file1[4]
self.assertEqual(len(testArray), len(npArr5))
for v1,v2 in zip (testArray, npArr5):
self.assertEqual(v1, v2)
if os.path.isfile(testFile):
os.remove(testFile)
def test_write_formatted_append(self):
npArr1 = np.array([1.1, 2.2e+28, 3.3, 4.4], dtype='float32')
npArr2 = np.array([11.11, 12.12, 13.13, 14.14], dtype='float64')
npArr3 = np.array([1,2,3,4], dtype='int32')
npArr4 = np.array(["PROD1","","INJ1"], dtype='str')
npArr5 = np.array([True, True, False, True, False, False], dtype='bool')
#npArr6=np.array([11,12,13,14], dtype='int64')
testFile = test_path("data/RESULT.FINIT")
# default is binary format (unformatted) and append = false
# will create a new file1
out1 = EclOutput(testFile, formatted = True)
out1.write("ARR0", npArr1)
# append = False (default value), will create a new file,
# hence, array ARR0 from out1 will be errased
out2 = EclOutput(testFile, formatted = True)
out2.write("ARR1", npArr1)
# append =True, will add ARR2 to existing file
out3 = EclOutput(testFile, append=True, formatted = True)
out3.write("ARR2", npArr2)
out3.write("ARR3", npArr3)
out3.write("ARR4", npArr4)
out3.write("ARR5", npArr5)
file1 = EclFile(testFile)
self.assertEqual(len(file1), 5)
# check array ARR1
testArray = file1[0]
self.assertEqual(len(testArray), len(npArr1))
for v1,v2 in zip (testArray, npArr1):
self.assertEqual(v1, v2)
# check array ARR2
testArray = file1[1]
self.assertEqual(len(testArray), len(npArr2))
for v1,v2 in zip (testArray, npArr2):
self.assertEqual(v1, v2)
# check array ARR3
testArray = file1[2]
self.assertEqual(len(testArray), len(npArr3))
for v1,v2 in zip (testArray, npArr3):
self.assertEqual(v1, v2)
# check array ARR4
testArray = file1[3]
self.assertEqual(len(testArray), len(npArr4))
for v1,v2 in zip (testArray, npArr4):
self.assertEqual(v1, v2)
# check array ARR5
testArray = file1[4]
self.assertEqual(len(testArray), len(npArr5))
for v1,v2 in zip (testArray, npArr5):
self.assertEqual(v1, v2)
if os.path.isfile(testFile):
os.remove(testFile)
def test_rewrite_rstfile_(self):
rstep = 74
rst1 = ERst(test_path("data/SPE9.UNRST"))
arrayList74=rst1.arrays(rstep)
outFile = test_path("data/TMP.UNRST")
out1 = EclOutput(outFile)
for n, (name, arrType, arrSize) in enumerate(arrayList74):
if arrType == eclArrType.MESS:
out1.write_message(name)
else:
array = rst1[name, rstep]
out1.write(name, array)
rst2 = ERst(outFile)
tmpArrayList74=rst2.arrays(rstep)
self.assertEqual(len(tmpArrayList74), len(arrayList74))
for n in range(0, len(tmpArrayList74)):
name1, arrType1, arrSize1 = tmpArrayList74[n]
name2, arrType2, arrSize2 = arrayList74[n]
self.assertEqual(name1, name2)
self.assertEqual(arrType1, arrType2)
if arrType1 != eclArrType.MESS:
arr1 = rst1[name1, rstep]
arr2 = rst2[name2, rstep]
self.assertEqual(len(arr1), len(arr2))
for v1,v2 in zip(arr1, arr2):
self.assertEqual(v1, v2)
if os.path.isfile(outFile):
os.remove(outFile)
def test_write_lists(self):
intList = [1,2,3,4,5,6]
boolList = [True, True, False, True]
strList = ["A-1H", "A-2H", "A-3H"]
testFile = "data/TMP.DAT"
outFile = test_path(testFile)
out1 = EclOutput(outFile)
out1.write("ARR1", intList)
out1.write("ARR2", boolList)
out1.write("ARR3", strList)
file1 = EclFile(outFile)
arr1 = file1["ARR1"]
arr2 = file1["ARR2"]
arr3 = file1["ARR3"]
self.assertEqual(len(arr1), len(intList))
self.assertEqual(len(arr2), len(boolList))
self.assertEqual(len(arr3), len(strList))
for v1, v2 in zip(arr1, intList):
self.assertEqual(v1, v2)
for v1, v2 in zip(arr2, boolList):
self.assertEqual(v1, v2)
for v1, v2 in zip(arr3, strList):
self.assertEqual(v1, v2)
if os.path.isfile(testFile):
os.remove(testFile)
if __name__ == "__main__":
unittest.main()