Add support for dynamic parser extensions
Sunbeam can now be used to parse eclipse files to deck representations. Added support for dynamic parser extensions which can be used to handle unsupported eclipse keywords when parsing to deck representations. Fixed missing function declarations
This commit is contained in:
@@ -2,6 +2,7 @@ set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
sunbeam.py
|
||||
config.py
|
||||
parser.py
|
||||
properties.py
|
||||
schedule.py)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from .schedule import Well
|
||||
from .libsunbeam import action
|
||||
from .properties import parse
|
||||
from .config import EclipseConfig
|
||||
from .parser import parse_deck, parse
|
||||
|
||||
__version__ = '0.0.1'
|
||||
__license__ = 'GNU General Public License version 3'
|
||||
|
||||
58
python/sunbeam/parser.py
Normal file
58
python/sunbeam/parser.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from os.path import isfile
|
||||
import json
|
||||
import libsunbeam as lib
|
||||
from .properties import EclipseState
|
||||
|
||||
|
||||
def _parse_context(actions):
|
||||
ctx = lib.ParseContext()
|
||||
|
||||
if actions is None:
|
||||
return ctx
|
||||
|
||||
# this might be a single tuple, in which case we unpack it and repack it
|
||||
# into a list. If it's not a tuple we assume it's an iterable and just
|
||||
# carry on
|
||||
try:
|
||||
key, action = actions
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
actions = [(key, action)]
|
||||
|
||||
for key, action in actions:
|
||||
ctx.update(key, action)
|
||||
|
||||
return ctx
|
||||
|
||||
|
||||
def parse(deck, actions = None):
|
||||
"""deck may be a deck string, or a file path"""
|
||||
if isfile(deck):
|
||||
return EclipseState(lib.parse(deck, _parse_context(actions)))
|
||||
return EclipseState(lib.parse_data(deck, _parse_context(actions)))
|
||||
|
||||
|
||||
def parse_deck(deck, **kwargs):
|
||||
args = [deck]
|
||||
|
||||
if 'keywords' in kwargs:
|
||||
keywords = kwargs['keywords']
|
||||
# this might be a single keyword dictionary, in which case we pack it
|
||||
# into a list. If it's not a dict we assume it's an iterable and just
|
||||
# carry on
|
||||
if isinstance(keywords, dict):
|
||||
keywords = [keywords]
|
||||
json_keywords = map(json.dumps, keywords)
|
||||
args.append(json_keywords);
|
||||
else:
|
||||
args.append([])
|
||||
|
||||
args.append(isfile(deck)) # If the deck is a file, the deck is read from
|
||||
# that file. Otherwise it is assumed to be a
|
||||
# string representation of the the deck.
|
||||
|
||||
if 'actions' in kwargs:
|
||||
args.append(_parse_context(kwargs['actions']))
|
||||
|
||||
return lib.parse_deck(*args)
|
||||
@@ -101,30 +101,3 @@ class EclipseGrid(object):
|
||||
if na != g:
|
||||
cnt += ', active = %s' % na
|
||||
return 'EclipseGrid(%s)' % cnt
|
||||
|
||||
def _parse_context(actions):
|
||||
ctx = lib.ParseContext()
|
||||
|
||||
if actions is None:
|
||||
return ctx
|
||||
|
||||
# this might be a single tuple, in which case we unpack it and repack it
|
||||
# into a list. If it's not a tuple we assume it's an iterable and just
|
||||
# carry on
|
||||
try:
|
||||
key, action = actions
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
actions = [(key, action)]
|
||||
|
||||
for key, action in actions:
|
||||
ctx.update(key, action)
|
||||
|
||||
return ctx
|
||||
|
||||
def parse(deck, actions = None):
|
||||
"""deck may be a deck string, or a file path"""
|
||||
if isfile(deck):
|
||||
return EclipseState(lib.parse(deck, _parse_context(actions)))
|
||||
return EclipseState(lib.parseData(deck, _parse_context(actions)))
|
||||
|
||||
@@ -5,15 +5,17 @@ include_directories(SYSTEM ${PYTHON_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIR}
|
||||
${opm-parser_INCLUDE_DIRS})
|
||||
|
||||
add_library( sunbeam SHARED eclipse_state.cpp
|
||||
add_library( sunbeam SHARED sunbeam.cpp
|
||||
completion.cpp
|
||||
deck.cpp
|
||||
deck_keyword.cpp
|
||||
eclipse_3d_properties.cpp
|
||||
eclipse_config.cpp
|
||||
eclipse_grid.cpp
|
||||
eclipse_state.cpp
|
||||
group.cpp
|
||||
parser.cpp
|
||||
schedule.cpp
|
||||
sunbeam.cpp
|
||||
table_manager.cpp
|
||||
well.cpp )
|
||||
set_target_properties( sunbeam PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/python/sunbeam )
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
namespace py = boost::python;
|
||||
|
||||
|
||||
/*
|
||||
* boost.python lacks converters for a lot of types we use, or we need to
|
||||
* fine-tune the behaviour a little bit. They're often for one specific
|
||||
|
||||
58
sunbeam/deck.cpp
Normal file
58
sunbeam/deck.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
|
||||
|
||||
#include "deck.hpp"
|
||||
|
||||
|
||||
namespace py = boost::python;
|
||||
using namespace Opm;
|
||||
|
||||
using ref = py::return_internal_reference<>;
|
||||
using copy = py::return_value_policy< py::copy_const_reference >;
|
||||
|
||||
|
||||
namespace deck {
|
||||
|
||||
/*
|
||||
* Deck methods inherited from DeckView
|
||||
*/
|
||||
size_t size( const Deck& deck ) {
|
||||
return deck.size();
|
||||
}
|
||||
size_t count( const Deck& deck, const std::string& kw ) {
|
||||
return deck.count(kw);
|
||||
}
|
||||
bool hasKeyword( const Deck& deck, const std::string& kw ) {
|
||||
return deck.hasKeyword(kw);
|
||||
}
|
||||
|
||||
const DeckKeyword& getKeyword0( const Deck& deck, py::tuple i ) {
|
||||
const std::string kw = py::extract<const std::string>(py::str(i[0]));
|
||||
const size_t index = py::extract<size_t>(i[1]);
|
||||
return deck.getKeyword(kw, index);
|
||||
}
|
||||
const DeckKeyword& getKeyword1( const Deck& deck, const std::string& kw ) {
|
||||
return deck.getKeyword(kw);
|
||||
}
|
||||
const DeckKeyword& getKeyword2( const Deck& deck, size_t index ) {
|
||||
return deck.getKeyword(index);
|
||||
}
|
||||
|
||||
const DeckView::const_iterator begin( const Deck& deck ) { return deck.begin(); }
|
||||
const DeckView::const_iterator end( const Deck& deck ) { return deck.end(); }
|
||||
|
||||
|
||||
void export_Deck() {
|
||||
|
||||
py::class_< Deck >( "Deck", py::no_init )
|
||||
.def( "__len__", &size )
|
||||
.def( "__contains__", &hasKeyword )
|
||||
.def( "__iter__", py::range< ref >( &begin, &end ) )
|
||||
.def( "__getitem__", &getKeyword0, ref() )
|
||||
.def( "__getitem__", &getKeyword1, ref() )
|
||||
.def( "__getitem__", &getKeyword2, ref() )
|
||||
.def( "count", &count )
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
25
sunbeam/deck.hpp
Normal file
25
sunbeam/deck.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <boost/python.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
|
||||
|
||||
namespace Opm {
|
||||
class Deck;
|
||||
class DeckKeyword;
|
||||
}
|
||||
|
||||
namespace deck {
|
||||
using namespace Opm;
|
||||
|
||||
size_t size( const Deck& deck );
|
||||
size_t count( const Deck& deck, const std::string& kw );
|
||||
bool hasKeyword( const Deck& deck, const std::string& kw );
|
||||
const DeckKeyword& getKeyword0( const Deck& deck, boost::python::tuple i );
|
||||
const DeckKeyword& getKeyword1( const Deck& deck, const std::string& kw );
|
||||
const DeckKeyword& getKeyword2( const Deck& deck, size_t index );
|
||||
const DeckView::const_iterator begin( const Deck& deck );
|
||||
const DeckView::const_iterator end( const Deck& deck );
|
||||
|
||||
void export_Deck();
|
||||
|
||||
}
|
||||
25
sunbeam/deck_keyword.cpp
Normal file
25
sunbeam/deck_keyword.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <boost/python.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
|
||||
|
||||
#include "deck_keyword.hpp"
|
||||
|
||||
namespace py = boost::python;
|
||||
using namespace Opm;
|
||||
|
||||
// using ref = py::return_internal_reference<>;
|
||||
using copy = py::return_value_policy< py::copy_const_reference >;
|
||||
|
||||
|
||||
namespace deck_keyword {
|
||||
|
||||
void export_DeckKeyword() {
|
||||
|
||||
py::class_< DeckKeyword >( "DeckKeyword", py::no_init )
|
||||
.def( "__repr__", &DeckKeyword::name, copy() )
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
7
sunbeam/deck_keyword.hpp
Normal file
7
sunbeam/deck_keyword.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
namespace deck_keyword {
|
||||
|
||||
void export_DeckKeyword();
|
||||
|
||||
}
|
||||
@@ -38,7 +38,6 @@ namespace eclipse_3d_properties {
|
||||
|
||||
void export_Eclipse3DProperties() {
|
||||
|
||||
|
||||
py::class_< Eclipse3DProperties >( "Eclipse3DProperties", py::no_init )
|
||||
.def( "getRegions", ®ions )
|
||||
.def( "__contains__", &contains )
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace eclipse_3d_properties {
|
||||
|
||||
py::list getitem( const Eclipse3DProperties& p, const std::string& kw);
|
||||
bool contains( const Eclipse3DProperties& p, const std::string& kw);
|
||||
py::list regions( const Eclipse3DProperties& p, const std::string& kw);
|
||||
|
||||
void export_Eclipse3DProperties();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include <boost/python.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseConfig.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/InitConfig/InitConfig.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/SimulationConfig/SimulationConfig.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/InitConfig/InitConfig.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp>
|
||||
|
||||
#include "eclipse_config.hpp"
|
||||
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
#ifndef SUNBEAM_ECLIPSE_CONFIG_HPP
|
||||
#define SUNBEAM_ECLIPSE_CONFIG_HPP
|
||||
|
||||
#include <boost/python.hpp>
|
||||
|
||||
|
||||
namespace Opm {
|
||||
}
|
||||
|
||||
namespace eclipse_config {
|
||||
namespace py = boost::python;
|
||||
using namespace Opm;
|
||||
|
||||
void export_EclipseConfig();
|
||||
|
||||
|
||||
@@ -13,6 +13,13 @@ namespace eclipse_grid {
|
||||
using namespace Opm;
|
||||
|
||||
py::tuple getXYZ( const EclipseGrid& grid );
|
||||
int getNumActive( const EclipseGrid& grid );
|
||||
int getCartesianSize( const EclipseGrid& grid );
|
||||
int getGlobalIndex( const EclipseGrid& grid, int i, int j, int k );
|
||||
py::tuple getIJK( const EclipseGrid& grid, int g );
|
||||
double cellVolume1G( const EclipseGrid& grid, size_t glob_idx);
|
||||
double cellVolume3( const EclipseGrid& grid, size_t i_idx, size_t j_idx, size_t k_idx);
|
||||
|
||||
void export_EclipseGrid();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/FaultCollection.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
|
||||
#include "eclipse_state.hpp"
|
||||
|
||||
@@ -88,16 +87,8 @@ namespace eclipse_state {
|
||||
return l;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
void export_EclipseState() {
|
||||
|
||||
py::def( "parse", parse );
|
||||
py::def( "parseData", parseData );
|
||||
|
||||
py::class_< EclipseState >( "EclipseState", py::no_init )
|
||||
.add_property( "title", &EclipseState::getTitle )
|
||||
.def( "_schedule", &EclipseState::getSchedule, ref() )
|
||||
@@ -112,21 +103,5 @@ namespace eclipse_state {
|
||||
.def( "jfunc", &jfunc )
|
||||
;
|
||||
|
||||
|
||||
/*
|
||||
* Temporarily
|
||||
*/
|
||||
py::class_< ParseContext >( "ParseContext" )
|
||||
.def( "update", ctx_update )
|
||||
;
|
||||
|
||||
py::enum_< InputError::Action >( "action" )
|
||||
.value( "throw", InputError::Action::THROW_EXCEPTION )
|
||||
.value( "warn", InputError::Action::WARN )
|
||||
.value( "ignore", InputError::Action::IGNORE )
|
||||
;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#define ECLIPSE_STATE_HPP
|
||||
|
||||
#include <boost/python.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/FaceDir.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/FaceDir.hpp>
|
||||
|
||||
namespace Opm {
|
||||
class EclipseState;
|
||||
|
||||
53
sunbeam/parser.cpp
Normal file
53
sunbeam/parser.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <opm/json/JsonObject.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
|
||||
#include "parser.hpp"
|
||||
|
||||
namespace py = boost::python;
|
||||
using namespace Opm;
|
||||
|
||||
|
||||
namespace parser {
|
||||
|
||||
Deck parseDeck( const std::string& deckStr,
|
||||
const boost::python::list& keywords,
|
||||
bool isFile,
|
||||
const ParseContext& pc ) {
|
||||
Parser p;
|
||||
size_t len = py::extract<size_t>(keywords.attr("__len__")());
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
const std::string kw = py::extract<const std::string>(py::str(keywords[i]));
|
||||
const Json::JsonObject jkw(kw);
|
||||
p.addParserKeyword(jkw);
|
||||
}
|
||||
if (isFile)
|
||||
return p.parseFile(deckStr, pc);
|
||||
return p.parseString(deckStr, pc);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
void export_Parser() {
|
||||
|
||||
py::def( "parse", parse );
|
||||
py::def( "parse_data", parseData );
|
||||
py::def( "parse_deck", &parseDeck );
|
||||
|
||||
py::class_< ParseContext >( "ParseContext" )
|
||||
.def( "update", ctx_update )
|
||||
;
|
||||
|
||||
py::enum_< InputError::Action >( "action" )
|
||||
.value( "throw", InputError::Action::THROW_EXCEPTION )
|
||||
.value( "warn", InputError::Action::WARN )
|
||||
.value( "ignore", InputError::Action::IGNORE )
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
19
sunbeam/parser.hpp
Normal file
19
sunbeam/parser.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <boost/python.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
|
||||
|
||||
namespace Opm {
|
||||
class Deck;
|
||||
class ParseContext;
|
||||
}
|
||||
|
||||
namespace parser {
|
||||
using namespace Opm;
|
||||
|
||||
Deck parseDeck( const std::string& deckStr,
|
||||
const boost::python::list& keywords,
|
||||
bool isFile,
|
||||
const ParseContext& = ParseContext() );
|
||||
|
||||
void export_Parser();
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
|
||||
#include "converters.hpp"
|
||||
|
||||
#include "completion.hpp"
|
||||
#include "converters.hpp"
|
||||
#include "deck.hpp"
|
||||
#include "deck_keyword.hpp"
|
||||
#include "eclipse_3d_properties.hpp"
|
||||
#include "eclipse_config.hpp"
|
||||
#include "eclipse_grid.hpp"
|
||||
#include "eclipse_state.hpp"
|
||||
#include "group.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "schedule.hpp"
|
||||
#include "table_manager.hpp"
|
||||
#include "well.hpp"
|
||||
@@ -25,18 +29,18 @@ BOOST_PYTHON_MODULE(libsunbeam) {
|
||||
|
||||
py::register_exception_translator< key_error >( &key_error::translate );
|
||||
|
||||
eclipse_state::export_EclipseState();
|
||||
eclipse_config::export_EclipseConfig();
|
||||
completion::export_Completion();
|
||||
deck::export_Deck();
|
||||
deck_keyword::export_DeckKeyword();
|
||||
eclipse_3d_properties::export_Eclipse3DProperties();
|
||||
eclipse_config::export_EclipseConfig();
|
||||
eclipse_grid::export_EclipseGrid();
|
||||
eclipse_state::export_EclipseState();
|
||||
group::export_Group();
|
||||
parser::export_Parser();
|
||||
schedule::export_Schedule();
|
||||
table_manager::export_TableManager();
|
||||
well::export_Well();
|
||||
|
||||
// parser::export_Parser();
|
||||
// deck::export_Deck();
|
||||
// deck_keyword::export_DeckKeyword();
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ configure_file(spe3/SPE3CASE1.DATA spe3/SPE3CASE1.DATA COPYONLY)
|
||||
configure_file(data/CORNERPOINT_ACTNUM.DATA data/CORNERPOINT_ACTNUM.DATA COPYONLY)
|
||||
configure_file(data/JFUNC.DATA data/JFUNC.DATA COPYONLY)
|
||||
|
||||
foreach(prog parse state props schedule wells)
|
||||
foreach(prog parse_deck parse state props schedule wells)
|
||||
add_python_test(${prog} ${prog}.py)
|
||||
endforeach()
|
||||
|
||||
67
tests/parse_deck.py
Normal file
67
tests/parse_deck.py
Normal file
@@ -0,0 +1,67 @@
|
||||
import unittest
|
||||
import sunbeam
|
||||
import os.path
|
||||
|
||||
class TestParse(unittest.TestCase):
|
||||
|
||||
DECK_ADDITIONAL_KEYWORDS = """
|
||||
START -- 0
|
||||
10 MAI 2007 /
|
||||
RUNSPEC
|
||||
TESTKEY0
|
||||
1 2 3 4 5 6 7 8 /
|
||||
TESTKEY1
|
||||
TESTKEY2
|
||||
/
|
||||
DIMENS
|
||||
2 2 1 /
|
||||
GRID
|
||||
DX
|
||||
4*0.25 /
|
||||
DY
|
||||
4*0.25 /
|
||||
DZ
|
||||
4*0.25 /
|
||||
TOPS
|
||||
4*0.25 /
|
||||
REGIONS
|
||||
OPERNUM
|
||||
3 3 1 2 /
|
||||
FIPNUM
|
||||
1 1 2 3 /
|
||||
"""
|
||||
|
||||
KEYWORDS = [{
|
||||
"name" : "TESTKEY0",
|
||||
"sections" : [ "RUNSPEC" ],
|
||||
"data" : {"value_type" : "INT" }
|
||||
}, {
|
||||
"name" : "TESTKEY1",
|
||||
"sections" : [ "RUNSPEC" ],
|
||||
"size":0
|
||||
}, {
|
||||
"name" : "TESTKEY2",
|
||||
"sections" : [ "RUNSPEC" ],
|
||||
"size":1,
|
||||
"items":[
|
||||
{ "name":"TEST", "value_type":"STRING", "default":"DEFAULT" }
|
||||
]
|
||||
}, ]
|
||||
|
||||
def setUp(self):
|
||||
self.spe3fn = 'spe3/SPE3CASE1.DATA'
|
||||
self.norne_fname = os.path.abspath('../../examples/data/norne/NORNE_ATW2013.DATA')
|
||||
|
||||
def testParerFailWithoutExtension(self):
|
||||
action = ("PARSE_RANDOM_SLASH", sunbeam.action.ignore)
|
||||
with self.assertRaises(ValueError):
|
||||
sunbeam.parse_deck(self.DECK_ADDITIONAL_KEYWORDS,
|
||||
actions=action )
|
||||
|
||||
def testParerExtension(self):
|
||||
action = ("PARSE_RANDOM_SLASH", sunbeam.action.ignore)
|
||||
deck = sunbeam.parse_deck(self.DECK_ADDITIONAL_KEYWORDS,
|
||||
keywords=self.KEYWORDS, actions=action )
|
||||
self.assertTrue( 'TESTKEY0' in deck )
|
||||
self.assertTrue( 'TESTKEY1' in deck )
|
||||
self.assertTrue( 'TESTKEY2' in deck )
|
||||
Reference in New Issue
Block a user