From 1ed4517d65106d5dc55c116b4860e646c5a6cd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20Gr=C3=B8n=C3=A5s=20Drange?= Date: Fri, 17 Feb 2017 15:07:09 +0100 Subject: [PATCH 1/4] implemented table lookup for eclipse_state --- python/sunbeam/properties.py | 27 +++++++++++++++++++++++++++ python/sunbeam/sunbeam.cpp | 14 +++++++++++++- tests/state.py | 15 +++++++++++++-- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/python/sunbeam/properties.py b/python/sunbeam/properties.py index bd858abb7..3cde4e908 100644 --- a/python/sunbeam/properties.py +++ b/python/sunbeam/properties.py @@ -22,6 +22,10 @@ class EclipseState(object): def cfg(self): return EclipseConfig(self._cfg()) + @property + def table(self): + return Tables(self._tables()) + def faults(self): """Returns a map from fault names to list of (i,j,k,D) where D ~ 'X+'""" fs = {} @@ -43,6 +47,29 @@ class Tables(object): def __repr__(self): return 'Tables()' + def _eval(self, x, table, col_name, tab_idx = 0): + return self._evaluate(table, tab_idx, col_name, x) + + def __getitem__(self, tab_name): + col_name = None + if isinstance(tab_name, tuple): + tab_name, col_name = tab_name + + tab_name = tab_name.upper() + if not tab_name in self: + raise ValueError('Table "%s" not in deck.' % tab_name) + + if col_name is None: + def t_eval(col_name, x, tab_idx = 0): + return self._eval(x, tab_name, col_name.upper(), tab_idx) + return t_eval + + col_name = col_name.upper() + def t_eval(x, tab_idx = 0): + return self._eval(x, tab_name, col_name, tab_idx) + return t_eval + + @delegate(lib.EclipseGrid) class EclipseGrid(object): diff --git a/python/sunbeam/sunbeam.cpp b/python/sunbeam/sunbeam.cpp index 0b3a7f9c7..3328a2ded 100644 --- a/python/sunbeam/sunbeam.cpp +++ b/python/sunbeam/sunbeam.cpp @@ -230,6 +230,17 @@ py::list get_groups( const Schedule& sch ) { } +namespace tables { +double evaluate( const TableManager& tab, + std::string tab_name, + int tab_idx, + std::string col_name, double x ) try { + return tab[tab_name].getTable(tab_idx).evaluate(col_name, x); +} catch( std::invalid_argument& e ) { + throw key_error( e.what() ); +} +} + EclipseState (*parse)( const std::string&, const ParseContext& ) = &Parser::parse; EclipseState (*parseData) (const std::string &data, const ParseContext& context) = &Parser::parseData; void (ParseContext::*ctx_update)(const std::string&, InputError::Action) = &ParseContext::update; @@ -262,7 +273,7 @@ py::class_< EclipseState >( "EclipseState", py::no_init ) .def( "_props", &EclipseState::get3DProperties, ref() ) .def( "_grid", &EclipseState::getInputGrid, ref() ) .def( "_cfg", &EclipseState::cfg, ref() ) - .def( "tables", &EclipseState::getTableManager, ref() ) + .def( "_tables", &EclipseState::getTableManager, ref() ) .def( "has_input_nnc", &EclipseState::hasInputNNC ) .def( "input_nnc", state::getNNC ) .def( "faultNames", state::faultNames ) @@ -286,6 +297,7 @@ py::class_< Eclipse3DProperties >( "Eclipse3DProperties", py::no_init ) py::class_< TableManager >( "Tables", py::no_init ) .def( "__contains__", &TableManager::hasTables ) + .def("_evaluate", tables::evaluate ) ; /* diff --git a/tests/state.py b/tests/state.py index d2f440f4b..e7bbedb44 100644 --- a/tests/state.py +++ b/tests/state.py @@ -99,15 +99,26 @@ SATNUM self.assertTrue(sim.hasVAPOIL()) def test_tables(self): - tables = self.spe3.tables() + tables = self.spe3.table self.assertTrue('SGOF' in tables) self.assertTrue('SWOF' in tables) self.assertFalse('SOF' in tables) - ct = self.cp_state.tables() + ct = self.cp_state.table self.assertFalse('SGOF' in ct) self.assertTrue('SWOF' in ct) + tab = 'SWOF' + col = 'KRW' + self.assertAlmostEqual(0.1345, self.spe3.table[tab](col, 0.5)) + self.assertAlmostEqual(0.39, self.spe3.table[tab](col, 0.72)) + + self.assertAlmostEqual(0.1345, self.spe3.table[tab, col](0.5)) + self.assertAlmostEqual(0.39, self.spe3.table[tab, col](0.72)) + + with self.assertRaises(KeyError): + self.spe3.table[tab, 'NO'](1) + def test_faults(self): self.assertEquals([], self.spe3.faultNames()) From e1f64e83e0db957d2d9d7b6bed0ad8dca4c9f026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20Gr=C3=B8n=C3=A5s=20Drange?= Date: Fri, 17 Feb 2017 15:07:22 +0100 Subject: [PATCH 2/4] table lookup in norne example --- examples/norne.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) mode change 100644 => 100755 examples/norne.py diff --git a/examples/norne.py b/examples/norne.py old mode 100644 new mode 100755 index 1366ab2f6..ab02c0dbb --- a/examples/norne.py +++ b/examples/norne.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + import sys from os.path import isdir, join import sunbeam @@ -24,6 +26,16 @@ def parse(fname): print('Parsing took %s sec' % (e - s).seconds) return es +def swof_krw(ecl): + assert('SWOF' in ecl.table) + krw = ecl.table['SWOF', 'KRW'] + krow = ecl.table['SWOF', 'KROW'] + pcow = ecl.table['SWOF', 'PCOW'] + + print('SWOF\tKRW\tKROW\tPCOW') + for i in range(21): + print('%.2f\t%.4f\t%.4f\t%.4f' % (i/20.0, krw(i/20.0), krow(i/20.0), pcow(i/20.0))) + def main(): es = parse(join(opmdatadir(), 'norne/NORNE_ATW2013.DATA')) sc = es.schedule @@ -41,6 +53,7 @@ def main(): print('pos: %s' % list(wp.pos())) print('fault: %s' % f0) print(' comprised of %d cells' % len(fl)) + swof_krw(es) if __name__ == '__main__': global OPMDATA_DIR From c66cf7e1593eae795d5376d1e282ec71f100719c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20Gr=C3=B8n=C3=A5s=20Drange?= Date: Fri, 17 Feb 2017 15:53:52 +0100 Subject: [PATCH 3/4] added matplotlib example --- examples/swofplt.py | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100755 examples/swofplt.py diff --git a/examples/swofplt.py b/examples/swofplt.py new file mode 100755 index 000000000..ef91a1d1e --- /dev/null +++ b/examples/swofplt.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +import sys +from os.path import isdir, join +import sunbeam +from datetime import datetime as dt + +import numpy as np +import matplotlib.pyplot as plt + + +def plotswof(ecl): + assert('SWOF' in ecl.table) + krw = ecl.table['SWOF', 'KRW'] + krow = ecl.table['SWOF', 'KROW'] + pcow = ecl.table['SWOF', 'PCOW'] + + swofl = [x/20.0 for x in range(21)] + krwl = [krw(x/20.0) for x in range(21)] + krowl = [krow(x/20.0) for x in range(21)] + pcowl = [pcow(x/20.0) for x in range(21)] + + plt.figure(1) + plt.plot(swofl, krwl, label = 'KRW') + plt.plot(swofl, krowl, label = 'KROW') + plt.legend() + plt.show() + plt.figure(2) + plt.plot(swofl, pcowl, label = 'Water-oil capillary pressure') + plt.legend() + plt.show() + + + +def opmdatadir(): + global OPMDATA_DIR + if isdir(OPMDATA_DIR): + return OPMDATA_DIR + if len(sys.argv) < 2: + return None + d = sys.argv[1] + if isdir(d) and isdir(join(d, 'norne')): + return d + return None + +def haveopmdata(): + return opmdatadir() is not None + +def parse(fname): + s = dt.now() + es = sunbeam.parse(fname, ('PARSE_RANDOM_SLASH', sunbeam.action.ignore)) + e = dt.now() + print('Parsing took %s sec' % (e - s).seconds) + return es + + +def main(): + es = parse(join(opmdatadir(), 'norne/NORNE_ATW2013.DATA')) + plotswof(es) + +if __name__ == '__main__': + global OPMDATA_DIR + OPMDATA_DIR = '../../opm-data' + if haveopmdata(): + print('Found norne, parsing ...') + main() + else: + print('Need to have path "%s" or give opm-data as argument' % OPMDATA_DIR) From a4ff0e11142f6ef8e16c4fd96d32d02a7badc8e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20Gr=C3=B8n=C3=A5s=20Drange?= Date: Fri, 17 Feb 2017 16:09:24 +0100 Subject: [PATCH 4/4] parser changed default behaviour, update test --- tests/props.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/props.py b/tests/props.py index cd51ee955..f54b566a3 100644 --- a/tests/props.py +++ b/tests/props.py @@ -21,7 +21,7 @@ class TestProps(unittest.TestCase): p = self.props self.assertTrue('PORO' in p) self.assertFalse('NONO' in p) - self.assertFalse('PORV' in p) + self.assertTrue('PORV' in p) # auto generated def test_getitem(self): p = self.props @@ -36,7 +36,7 @@ class TestProps(unittest.TestCase): def test_regions(self): p = self.props reg = p.getRegions('SATNUM') - self.assertEqual(0, len(reg)) + self.assertEqual(1, len(reg)) # auto generated def test_permx_values(self): def md2si(md):