mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-13 00:42:12 -05:00
#804 Updated ERT based on 6287acdb0d039ce13eb9956b67035280a3de9539
This commit is contained in:
1 parent
7269e6e165
commit
3b4f1620a5
4288 files changed
+19102
-16971
No files matched your search
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
include(cmake/find_python_module.cmake)
|
||||
include(cmake/python_module_versions.cmake) # finds version
|
||||
|
||||
FIND_PACKAGE(PythonInterp 2.7)
|
||||
if (NOT DEFINED PYTHON_EXECUTABLE)
|
||||
message("Python2.7 interpreter not found - Python wrappers not enabled")
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
python_module(numpy 1.7.1)
|
||||
if (NOT DEFINED PY_numpy)
|
||||
message("numpy module not found - Python wrappers not enabled")
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
if (ERT_BUILD_GUI)
|
||||
python_module(PyQt4 4.8.0)
|
||||
if(NOT DEFINED PY_PyQt4)
|
||||
message(SEND_ERROR "Cannot build GUI without PyQt4")
|
||||
endif()
|
||||
python_module( matplotlib 1.2.0 )
|
||||
python_module( pandas 0.15.1 )
|
||||
python_module( scipy 0.16.1 )
|
||||
endif()
|
||||
|
||||
if (EXISTS "/etc/debian_version")
|
||||
set( PYTHON_PACKAGE_PATH "dist-packages")
|
||||
else()
|
||||
set( PYTHON_PACKAGE_PATH "site-packages")
|
||||
endif()
|
||||
set(PYTHON_INSTALL_PREFIX "lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/${PYTHON_PACKAGE_PATH}" CACHE STRING "Subdirectory to install Python modules in")
|
||||
|
||||
|
||||
if (BUILD_TESTS)
|
||||
add_subdirectory( tests )
|
||||
endif()
|
||||
add_subdirectory( python )
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
Some of the code in the ERT codebase has been wrapped with with
|
||||
Python.
|
||||
|
||||
|
||||
|
||||
More detailed documentation of the wrapping can be found in
|
||||
docs/devel.txt.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Found from: github user ivansafrin
|
||||
#
|
||||
# Find if a Python module is installed
|
||||
# Found at http://www.cmake.org/pipermail/cmake/2011-January/041666.html
|
||||
# To use do: find_python_module(PyQt4 REQUIRED)
|
||||
function(find_python_module module)
|
||||
string(TOUPPER ${module} module_upper)
|
||||
if(NOT PY_${module_upper})
|
||||
if(ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED")
|
||||
set(${module}_FIND_REQUIRED TRUE)
|
||||
endif()
|
||||
# A module's location is usually a directory, but for binary modules
|
||||
# it's a .so file.
|
||||
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
|
||||
"import re, ${module}; print(re.compile('/__init__.py.*').sub('',${module}.__file__))"
|
||||
RESULT_VARIABLE _${module}_status
|
||||
OUTPUT_VARIABLE _${module}_location
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
if(NOT _${module}_status)
|
||||
set(PY_${module_upper} ${_${module}_location} CACHE STRING
|
||||
"Location of Python module ${module}")
|
||||
endif()
|
||||
endif(NOT PY_${module_upper})
|
||||
find_package_handle_standard_args(PY_${module} DEFAULT_MSG PY_${module_upper})
|
||||
endfunction(find_python_module)
|
||||
@@ -0,0 +1,40 @@
|
||||
# try import python module, if success, check its version, store as PY_module.
|
||||
# the module is imported as-is, hence the case (e.g. PyQt4) must be correct.
|
||||
function(python_module_version module)
|
||||
set(PY_VERSION_ACCESSOR "__version__")
|
||||
set(PY_module_name ${module})
|
||||
|
||||
if(${module} MATCHES "PyQt4")
|
||||
set(PY_module_name "PyQt4.Qt")
|
||||
set(PY_VERSION_ACCESSOR "PYQT_VERSION_STR")
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" "import ${PY_module_name} as py_m; print(py_m.${PY_VERSION_ACCESSOR})"
|
||||
RESULT_VARIABLE _${module}_fail# error code 0 if success
|
||||
OUTPUT_VARIABLE _${module}_version# major.minor.patch
|
||||
ERROR_VARIABLE stderr_output
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
if(NOT _${module}_fail)
|
||||
set(PY_${module} ${_${module}_version})# local scope, for message
|
||||
set(PY_${module} ${_${module}_version} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
# If we find the correct module and new enough version, set PY_package, where
|
||||
# "package" is the given argument to the version we found else, display warning
|
||||
# and do not set any variables.
|
||||
function(python_module package version)
|
||||
python_module_version(${package})
|
||||
|
||||
if(NOT DEFINED PY_${package})
|
||||
message("Could not find Python module " ${package})
|
||||
elseif(${PY_${package}} VERSION_LESS ${version})
|
||||
message(WARNING "Python module ${package} too old. "
|
||||
"Wanted ${version}, found ${PY_${package}}")
|
||||
else()
|
||||
message(STATUS "Found ${package}. ${PY_${package}} >= ${version}")
|
||||
set(PY_${package} ${version} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
Vendored
+429
@@ -0,0 +1,429 @@
|
||||
Developer documentation
|
||||
-----------------------
|
||||
|
||||
1 Summary
|
||||
2 About ctypes
|
||||
2.1 Loading the shared library
|
||||
2.2 Type mappings
|
||||
2.2.1 Fundamental types
|
||||
2.2.2 User defined types and classes
|
||||
3 The C code
|
||||
4 Common structure of the wrapping
|
||||
4.1 Loading the shared library
|
||||
4.2 Prototyping
|
||||
4.3 Python classes
|
||||
5 Garbage collection and 'data_owner'
|
||||
6 Installation in Statoil
|
||||
|
||||
|
||||
|
||||
1 Summary
|
||||
|
||||
The ert Python classes are based on wrapping (some) of the C libraries
|
||||
which constitute the ERT application. The wrapping is based on the
|
||||
python ctypes module which does the wrapping dynamically. The python
|
||||
classes are quite thin, most of the actual code is in C, however a
|
||||
user of the ert Python should NOT need to know that there is some C
|
||||
code deep down.
|
||||
|
||||
|
||||
2 About ctypes
|
||||
|
||||
ctypes is a standard Python module for dynamic wrapping of C code. The
|
||||
C code should compiled into a shared library. The ctypes library works
|
||||
by loading the shared library with dlopen(). In addition there is a
|
||||
quite good system for transparently mapping between Python types and C
|
||||
types. More extensive documentation of ctypes is available at:
|
||||
http://www.python.org/org/library/ctypes.html
|
||||
|
||||
2.1 Loading the shared library
|
||||
|
||||
In the ert Python wrapping loading the shared library is handled by
|
||||
the ert.util.clib function load(). When the shared library has been
|
||||
loaded, all the symbols of the shared library are available as
|
||||
attributes of the ctypes load handle. At the lowest level the loading
|
||||
of shared libraries is through the ctypes.CDLL() call like:
|
||||
|
||||
lib_handle = ctypes.CDLL( "libname" )
|
||||
|
||||
however in the ert py library this is wrapped by the function load()
|
||||
in the ert.util.clib module:
|
||||
|
||||
import ert.util.clib as clib
|
||||
lib_handle = clib.load("name1" , "name2" , "name3")
|
||||
|
||||
The clib.load() function will only load one library, but it can take
|
||||
several names to that library and will try loading until one of them
|
||||
succeeds. This is quite useful because several of the standard
|
||||
libraries are identified with different names on the different RedHat
|
||||
releases, for instance zlib is loaded like this:
|
||||
|
||||
zlib_handle = clib.load("libz", "libz.so.1")
|
||||
|
||||
When the library has been loaded, all functions of the shared library
|
||||
will be available as python function attributes of the library
|
||||
handle. I.e. if loading the standard library like:
|
||||
|
||||
clib_handle = clib.load( "libc" )
|
||||
|
||||
The attribute clib_handle.getenv will be a pointer to a Python
|
||||
function object which wraps the standard library getenv()
|
||||
function. Before this is really usable we have to "tell" the function
|
||||
object which input arguments and return to expect, this is done with
|
||||
the restype attribute and the argtypes attribute of the function
|
||||
object, more details of the prototype process can be found in section
|
||||
4.2
|
||||
|
||||
|
||||
2.2 Type mappings
|
||||
|
||||
The ctypes library automagically handles conversion between common
|
||||
C-types and Python types, so for the example above we would be able to
|
||||
write:
|
||||
|
||||
PATH = clib_handle.getenv( "PATH" )
|
||||
|
||||
And the conversion between Python strings and NULL based char* in C is
|
||||
handled transparently. In the ert wrapping setting the type of return
|
||||
values and input values is handled by the function prototype() in
|
||||
ert.cwrap.cwrap.
|
||||
|
||||
The type mappings necessary to get prototype() function to work is
|
||||
maintained in the CWrapper class in ert.cwrap.cwrap.py. During
|
||||
initialization of the ert Python code there are many calls
|
||||
associating the name of a C type you whish to use in the prototype
|
||||
process (as a string) and the corresponding Python type:
|
||||
|
||||
CWrapper.registerType( "int" , ctypes.c_int )
|
||||
CWrapper.registerType( "double" , ctypes.c_double )
|
||||
CWrapper.registerType( "char*" , ctypes.c_char_p )
|
||||
CWrapper.registerType( "int*" , ctypes.POINTER( ctypes.c_int ))
|
||||
|
||||
|
||||
|
||||
|
||||
2.2.1 Fundamental types
|
||||
|
||||
All the fundamental C types are known to the ctypes library, and
|
||||
transparently converted <-> the correct Python type.
|
||||
|
||||
C type ctypes Python type
|
||||
--------------------------------------------------------
|
||||
int c_int int
|
||||
float c_float float
|
||||
double c_double double
|
||||
.....
|
||||
char * (NULL terminated) c_char_p string
|
||||
--------------------------------------------------------
|
||||
|
||||
All these types can be created by calling them with an optional
|
||||
initializer of the correct type and value:
|
||||
|
||||
cint = ctypes.c_int( 42 )
|
||||
|
||||
With the function ctypes.POINTER() you can create pointers to the
|
||||
fundamental types - but this is approaching dangerous territory!
|
||||
|
||||
|
||||
2.2.2 User defined types and classes
|
||||
|
||||
Let us say you have a C function which expects a pointer to an
|
||||
abstract data type as input:
|
||||
|
||||
void abstract_method( abstract_type * at );
|
||||
|
||||
And that you have implemented the Python class AT which wraps a
|
||||
pointer to a at instance in a "c_ptr" attribute. If the AT class
|
||||
contains a method:
|
||||
|
||||
def from_param( self ):
|
||||
return ctypes.c_void_p( self.c_ptr )
|
||||
|
||||
The AT class can then be used as a Python type in the argtypes
|
||||
attributes. To make it available in the prototyping as well you must
|
||||
register the type:
|
||||
|
||||
CWrapper.registerType( "at" , AT )
|
||||
|
||||
Observe that the AT class should NOT be used as restype, i.e. for the
|
||||
return value:
|
||||
|
||||
/-- Warning: ----------------------------------------------\
|
||||
| The mapping of types generally works quite well, however |
|
||||
| observe that the return type of C functions which return |
|
||||
| an instance pointer is NOT the current class, but |
|
||||
| c_void_p, that is because this return value is |
|
||||
| internalized in the c_ptr attribute of the object. |
|
||||
\----------------------------------------------------------/
|
||||
|
||||
|
||||
3 The C code
|
||||
|
||||
The C libraries are to a large extent developed with abstract types
|
||||
like:
|
||||
|
||||
typedef struct {
|
||||
.....
|
||||
.....
|
||||
} abs_type;
|
||||
|
||||
and "methods" like:
|
||||
|
||||
abs_type * abs_type_alloc( ) { }
|
||||
void abs_type_free( abs_type * at ) {}
|
||||
int abs_type_get_an_int( const abs_type * at) {}
|
||||
void abs_type_set_an_int( abs_type * at , int value) {}
|
||||
|
||||
it has therefor been relatively easy to map this onto Python classes
|
||||
and give a pythonic feel to the whole thing. As a ground rule each C
|
||||
file implements one struct; this struct is wrapped in a Python module
|
||||
with the same name and a Python class with CamelCaps naming:
|
||||
|
||||
C Python
|
||||
ecl_kw.c implements ecl_kw_type ecl_kw.py implements EclKW
|
||||
|
||||
|
||||
|
||||
|
||||
4 Common structure of the wrapping
|
||||
|
||||
The wrapping of the libraries (currently only libecl is anything close
|
||||
to complete) follow roughly the same structure:
|
||||
|
||||
1. Load the shared library
|
||||
|
||||
2. For each C "class" you wish to wrap {
|
||||
|
||||
2.1. Prototype the C functions you wish to use by creating Python
|
||||
function objects with correct restype and argtypes
|
||||
attributes for the functions you are interested in, using
|
||||
the CWrapper.prototype() function.
|
||||
|
||||
2.2. Create the Python class - based on the prototype functions
|
||||
from 2.1 above.
|
||||
|
||||
}
|
||||
|
||||
The following excerpt from ecl_kw.y illustrates this:
|
||||
|
||||
------------------------------------------------------------
|
||||
# Load the shared library
|
||||
import libecl <--- Step 1
|
||||
|
||||
Class EclKW: <-------------·
|
||||
# Pure Python code to implement the EclKW class, |
|
||||
# based on the functions prototyped below. | Step 3
|
||||
.... <-------------·
|
||||
|
||||
# Create a wrapper instance which wraps the libecl library. <--------·
|
||||
cwrapper = CWrapper( libecl.lib ) |
|
||||
|
|
||||
# Register the type mapping "ecl_kw" <-> EclKW | Step 2
|
||||
cwrapper.registerType( "ecl_kw" , EclKW ) |
|
||||
|
|
||||
# Prototype the functions needed to implement the Python class |
|
||||
cfunc.alloc_new = cwrapper.prototype("long ecl_kw_alloc( char* , int , int )")
|
||||
cfunc.free = cwrapper.prototype("void ecl_kw_free( ecl_kw )") |
|
||||
.... <--------·
|
||||
------------------------------------------------------------
|
||||
|
||||
These three steps are described in more detail in 4.1 - 4.3 below.
|
||||
|
||||
|
||||
4.1 Loading the shared library
|
||||
|
||||
For loading the shared library 'libecl.so' there is a module
|
||||
'libecl.py' which calls the actual loading function, load() in
|
||||
ert.cwrap.clib.py. Subsequently the library instance will be a 'lib'
|
||||
attribute of the the libecl module, so that subsequent functions which
|
||||
need access to the function objects pointing to C functions, like
|
||||
e.g. the prototype function in the CWrapper class, will use this
|
||||
attribute.
|
||||
|
||||
If you take a detailed look at some of the "import libxxx" statements
|
||||
they might seem superfluous, in the sense that the active scope does
|
||||
not reference the imported library, however in many cases the "import
|
||||
libxxxx" statements are for side-effects only.
|
||||
|
||||
|
||||
4.2 Prototyping
|
||||
|
||||
As mentioned in section 2.1 we must "tell" the function pointers the
|
||||
type of the return value and the input arguments. This is done by
|
||||
setting the restype and argtypes attributes. For the getenv() function
|
||||
which takes one (char *) input and returns a (char *) pointer this
|
||||
would be:
|
||||
|
||||
clib_handle.getenv.restype = ctypes.c_char_p
|
||||
clib_handle.getenv.argtypes = [ ctypes.c_char_p ]
|
||||
|
||||
In the ert python code this is achieved with the helper class CWrapper
|
||||
implemented in the module ert.cwrap.cwrap:
|
||||
|
||||
from ert.cwrap.cwrap import CWrapper
|
||||
|
||||
lib = clib.load( "libc" )
|
||||
wrapper = CWRapper( lib )
|
||||
getenv = wrapper.prototype("char* getenv( char* )")
|
||||
|
||||
The prototype() method of the CWrapper class is essentially one
|
||||
massive regular expression which parses the "char* getenv( char* )"
|
||||
string and assigns the correct restype and argtypes attributes.
|
||||
|
||||
|
||||
All the calls to the prototype() method are typically assembled in the
|
||||
bottom of the file implementing a class defintion. E.g. the bottom of
|
||||
the ecl_kw.py file looks like this:
|
||||
|
||||
# 1: Load the shared library
|
||||
cwrapper = CWrapper( libecl.lib )
|
||||
|
||||
# 2: Register type mapping ecl_kw <--> EclKW
|
||||
cwrapper.registerType( "ecl_kw" , EclKW )
|
||||
|
||||
# 3: Prototype the C functions we will use from Python
|
||||
cfunc = CWrapperNameSpace("ecl_kw")
|
||||
cfunc.get_size = cwrapper.prototype("int ecl_kw_get_size( ecl_kw )")
|
||||
cfunc.get_type = cwrapper.prototype("int ecl_kw_get_type( ecl_kw )")
|
||||
cfunc.iget_char_ptr = cwrapper.prototype("char* ecl_kw_iget_char_ptr( ecl_kw , int )")
|
||||
cfunc.iset_char_ptr = cwrapper.prototype("void ecl_kw_iset_char_ptr( ecl_kw , int , char*)")
|
||||
cfunc.iget_bool = cwrapper.prototype("bool ecl_kw_iget_bool( ecl_kw , int)")
|
||||
cfunc.iset_bool = cwrapper.prototype("bool ecl_kw_iset_bool( ecl_kw , int, bool)")
|
||||
cfunc.alloc_new = cwrapper.prototype("c_void_p ecl_kw_alloc( char* , int , int )")
|
||||
....
|
||||
....
|
||||
|
||||
This makes the function cfunc.get_size, cfunc.get_type,
|
||||
cfunc.iget_char_ptr, ... available as first class Python functions,
|
||||
and the Python classes can be implemented based on these
|
||||
functions. Observe that the return value from "ecl_kw_alloc()" is
|
||||
c_void_p' and not ecl_kw - that is because this return value is
|
||||
internalized in the c_ptr of the object (this is not particularly
|
||||
elegant, and could probably be improved upon..??).
|
||||
|
||||
The prototyped functions is quite low-level, and they should __NOT__
|
||||
be used outside the file scope where they are defined.
|
||||
|
||||
|
||||
4.3 Python classes
|
||||
|
||||
All the main Python classes wrap a C based structure, and contain
|
||||
roughly the same structure:
|
||||
|
||||
* The underlying C structure is 'held' with the attribute c_ptr,
|
||||
c_ptr is just a c_void_p instance which holds the pointer value of
|
||||
the underlying C structure. The name c_ptr is just convention, and
|
||||
could be anything. The type of c_ptr is 'c_void_p'.
|
||||
|
||||
Mapping between a class instance and the C pointer held by this
|
||||
instance is handled with the 'from_param' function:
|
||||
|
||||
def from_param( self ):
|
||||
return self.c_ptr
|
||||
|
||||
The name 'from_param' is goverened by ctypes; the from_param
|
||||
method is called automatically by the ctypes runtime.
|
||||
|
||||
* The __init__() function, or alternatively a classmethod
|
||||
constructor calls the C based constructor, like
|
||||
e.g. ecl_grid_alloc() and internalize the return value in the
|
||||
attribute 'c_ptr'; alternatively the c_ptr might come as a shared
|
||||
reference from another function.
|
||||
|
||||
* The __del__ function is called when the Python object is garbage
|
||||
collected, then the corresponding C free function should be
|
||||
called, e.g. ecl_grid_free().
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
5 Garbage collection and 'data_owner'
|
||||
|
||||
The Python language has garbage collection, and as a user of the ert
|
||||
python code you should just assume that the garbage collection works
|
||||
for the ert py types. However, if you want to wrap new types (or maybe
|
||||
fix a bug ...) you should understand how the the ert py classes
|
||||
interact with garbage collection.
|
||||
|
||||
When the Python interpreter decides that an object will be collected,
|
||||
the __del__() method of that object is called, and any non-standard
|
||||
cleanup code can be called here. In the case of ert py, this is where
|
||||
the C based destructor can be called. The important point is that it
|
||||
differs from case to case whether the C based constructor should
|
||||
indeed be called.
|
||||
|
||||
The EclKW class illustrates this quite well. Broadly speaking you can
|
||||
instantiate an EclKW instance in two ways:
|
||||
|
||||
* Through the EclwKW.new() function
|
||||
* As a reference to an existing ecl_kw through an EclFile instance.
|
||||
|
||||
In the first case the c_ptr of the EclKW instance will point to fresh
|
||||
storage dedicated to this EclKW, and when the EclKW instance goes out
|
||||
of scope the memory should be freed with a call to the C function
|
||||
ecl_kw_free().
|
||||
|
||||
EclKW (Python) ecl_kw (C)
|
||||
c_ptr
|
||||
| ---------
|
||||
\--------------->| PORO |
|
||||
| REAL |
|
||||
| 10000 |
|
||||
|-------|
|
||||
| 0.15 |
|
||||
| 0.19 |
|
||||
| ... |
|
||||
---------
|
||||
|
||||
|
||||
In the second case an EclFile instance has been created, and this
|
||||
object points to ecl_file C struct which is essentially a container
|
||||
containing many ecl_kw instances. We can then query the EclFile
|
||||
instance to get reference to the various ecl_kw keywords, i.e. for a
|
||||
small restart file:
|
||||
|
||||
|
||||
EclFile (Python) ecl_file (C) EclKW (Python)
|
||||
c_ptr c_ptr
|
||||
| ------------ |
|
||||
\---------------->| PRESSURE | |
|
||||
| SGAS <---+------------------/
|
||||
| SWAT |
|
||||
| .... |
|
||||
------------
|
||||
|
||||
The situaton above (which is quite typical) could for instance come
|
||||
about from:
|
||||
|
||||
restart_file = ecl.EclFile( "ECLIPSE.X0057" )
|
||||
sgas_kw = restart_file.iget_named_kw( "SGAS" , 0 )
|
||||
|
||||
Now, when the sgas_kw object goes out of scope it is important that
|
||||
the SGAS ecl_kw in the ecl_file file container is _NOT_ deleted, that
|
||||
keyword is owned by the ecl_file container and the whole thing will
|
||||
crash and burn if the ecl_kw is destroyed bypassing the container. The
|
||||
sgas keyword will be destroyed when the restart_file object goes out
|
||||
scope at a later stage.
|
||||
|
||||
To facilitate this difference in behaviour when an EclKW goes out
|
||||
scope the object contains a field 'data_owner', and the __del__()
|
||||
method looks like this:
|
||||
|
||||
def __del__( self ):
|
||||
if self.data_owner:
|
||||
cfunc.free( self )
|
||||
|
||||
I.e. the C based free() function is only called if the object is the
|
||||
owner of the underlying C structure. This technique is implemented in
|
||||
many of the objects. In the current code the data_owner field is set
|
||||
alongside with the c_ptr field, and not modified during the lifetime
|
||||
of the object (i.e. the implementation does not support 'hostile
|
||||
takeover' or 'orphaning' of objects).
|
||||
|
||||
|
||||
|
||||
6 Installation in Statoil
|
||||
|
||||
In Statoil the ert Python libraries are installed in the /project/res
|
||||
hierarchy.
|
||||
Vendored
+231
@@ -0,0 +1,231 @@
|
||||
Python has a system for packages and sub packages which maps to the
|
||||
filesystem; there are many different ways to import symbols into the
|
||||
Python interpreter context. These things are exploited in the ert python
|
||||
bindings, here we try to document the python apporach to packages and
|
||||
importing in general, and the conventions used in ert python in special.
|
||||
|
||||
Example module (file: example.py)
|
||||
--------------
|
||||
|
||||
def example_sum(a,b):
|
||||
return a+b
|
||||
|
||||
example_variable = 100
|
||||
|
||||
class ExampleClass:
|
||||
def __init__(self , **kwargs):
|
||||
....
|
||||
|
||||
def method( self , arg1 , arg2 )
|
||||
....
|
||||
|
||||
We will use this example module in the following.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1. Modules and symbols
|
||||
----------------------
|
||||
|
||||
A module is a python source file (or alternatively a shared library with
|
||||
a prescribed structure). When importing a module all the symbols in the
|
||||
module will become available in calling scope, with the module name as
|
||||
namespace prefix:
|
||||
|
||||
import example
|
||||
|
||||
var = example.example_variable
|
||||
sum = example.example_sum( 100 , 100)
|
||||
instance = example.ExampleClass()
|
||||
|
||||
Observe that even though we have imported the 'example' module, we
|
||||
still need to use the 'example' prefix when referring to the symbols
|
||||
found in the example module. It is important to remember that the
|
||||
module object is only a collection of symbols - you can not use the
|
||||
module itself, only the symbols contained in the module can be put to
|
||||
useful work.
|
||||
|
||||
It is also possible to import the symbols from 'example' directly into
|
||||
the current namespace, without creating the intermediate namespace
|
||||
'example', that is done using the 'from <module> import <symbol_list>'
|
||||
statement:
|
||||
|
||||
from example import example_variable,example_sum,ExampleClass
|
||||
|
||||
var = example_variable
|
||||
sum = example_sum
|
||||
inst = ExampleClass()
|
||||
|
||||
With this approach the symbols from 'example' are imported directly into
|
||||
the current namespace, and the 'example' prefix should not be used when
|
||||
referring to the 'example_variable','example_sum' and 'ExampleClass'
|
||||
symbols. In the example above we have explicitly named the symbols we are
|
||||
after, it is also possible to import all symbols undiscriminately:
|
||||
|
||||
from example import *
|
||||
|
||||
....
|
||||
....
|
||||
|
||||
Finally it is possible to perform name transliteration during the
|
||||
import:
|
||||
|
||||
from example import example_variable as EXAMPLE_VARIABLE
|
||||
|
||||
print "100 = %s??" % EXAMPLE_VARIABLE
|
||||
|
||||
Here we have imported the example_variable symbol from the 'example'
|
||||
module and renamed it to 'EXAMPLE_VARIABLE' in the calling scope.
|
||||
|
||||
|
||||
|
||||
2. Packages
|
||||
-----------
|
||||
|
||||
It is convenient to group related modules together in a package
|
||||
structure; a module is just a python source file. In the same manner a
|
||||
package is just a directory - WITH THE MAGICAL FILE __init.py__.
|
||||
|
||||
For instance for ert the top level package is just the the 'ert'
|
||||
directory. Looking on the filesystem for an ert installation it will
|
||||
look something like this:
|
||||
|
||||
ert/ <-- Top level package / directory.
|
||||
ert/__init__.py <-- Magic file signalling that 'ert' is a package.
|
||||
ert/ecl/ <-- ecl is subpackage.
|
||||
ert/ecl/__init__.py <-- Magic file signalling that 'ecl' is a package.
|
||||
ert/ecl/ecl_grid.py <-- Normal module in the ecl package.
|
||||
ert/ecl/ecl_sum.py <-- Normal module in the ecl package.
|
||||
....
|
||||
ert/util/ <-- The util subpackage.
|
||||
ert/util/__init__.py <-- Magic file that 'util' is a package.
|
||||
ert/util/stringlist.py <-- Normal module in the util package.
|
||||
ert/util/tvector.py <-- Normal module in the util package.
|
||||
|
||||
ert/
|
||||
|
||||
The important thing about packages is that they are normal filesystem
|
||||
directories, with the magic __init__.py file which signals that this is
|
||||
indeed a package. When importing a package the python code in the
|
||||
corresponding __init__.py file will be loaded and imported, that can be
|
||||
used as a means to perform package initialisation of various kinds.
|
||||
|
||||
The statement:
|
||||
|
||||
import ert
|
||||
|
||||
works, but as long as ert is a package (i.e. only a directory) this is
|
||||
not immediately very interesting[1], we have to continue importing until
|
||||
we have actually imported a module with real content:
|
||||
|
||||
import ert.ecl
|
||||
|
||||
Observe how the directories/subdirectories of the filesystem is
|
||||
translated to a dotted notation in package/module space. The statement
|
||||
'import ert.ecl' will import the ert.ecl subpackage, but this is still
|
||||
not any symbols usable for anything. Observe that the 'import ert.ecl'
|
||||
statement will evaluate the '__init__.py' files found along the
|
||||
way. ecl_grid.py is a module in the ecl package, so this statement will
|
||||
finally give us something usable:
|
||||
|
||||
import ert.ecl.ecl_grid
|
||||
|
||||
The module ecl_grid contains the implementation of the EclGrid class
|
||||
which can actually be used for something. To instantiate an EclGrid
|
||||
instance we have to go through the following hoops:
|
||||
|
||||
import ert.ecl.ecl_grid
|
||||
....
|
||||
....
|
||||
grid = ert.ecl.ecl_grid.EclGrid( "ECLIPSE.EGRID" )
|
||||
| | | |
|
||||
| | | |
|
||||
Package--/ | | \-------- Symbol (Class definition)
|
||||
| |
|
||||
Subpackage ---/ \---- Module
|
||||
|
||||
The 'ert.ecl.ecl_grid' namespace is quite massive. It is easy to
|
||||
simplify this using the from <module> import <symbol_list> as:
|
||||
|
||||
from ert.ecl.ecl_grid import EclGrid <-- Explicitly import symbol
|
||||
.... into current namespace.
|
||||
....
|
||||
grid = EclGrid( "ECLIPSE.EGRID" )
|
||||
|
||||
By convention the ert python distribution offers some limited
|
||||
simplifications of this procedure.
|
||||
|
||||
|
||||
3. Interaction with PYTHONPATH
|
||||
|
||||
When issuing the import statement:
|
||||
|
||||
import ert.ecl.ecl_grid
|
||||
|
||||
The directory 'ert/' must be on PYTHONPATH. In principle it is possible
|
||||
to circumvent the lowest level packge, and go directly to the 'ecl'
|
||||
subpackage:
|
||||
|
||||
import ecl.ecl_grid
|
||||
|
||||
but then the 'ecl/' directory must be on PYTHONPATH. For the default
|
||||
ert-python distribution only the 'ert/' directory will be on the import
|
||||
path, i.e. the "import ecl.ecl_grid' will not work directly.
|
||||
|
||||
|
||||
4. Conventions applied in ert
|
||||
|
||||
In the ert-python distribution some conventions have been adhered to;
|
||||
they can of course be changed:
|
||||
|
||||
1. All the __init__.py files are empty, i.e. no automagic namespace
|
||||
manipulations. It is tempting to fill up the __init__.py files
|
||||
with further import statements in an attempt to shorten and
|
||||
simplify the imports. Some of that was done in the initial
|
||||
dsitributions of ert, but it gets very easy to be too smart and
|
||||
shoot oneself in the foot.
|
||||
|
||||
2. All subpackages have a module with the same name as the package,
|
||||
i.e. the ecl package has a module ecl - this module imports all
|
||||
the symbols from all the modules in the package with no namespace
|
||||
prefix, i.e. the ecl module looks like:
|
||||
|
||||
from ecl_grid import EclGrid
|
||||
from ecl_region import EclRegion
|
||||
from ecl_kw import EclKW
|
||||
.....
|
||||
|
||||
The point of this is a minor simplification, you can then issue
|
||||
e.g.
|
||||
|
||||
from ert.ecl.ecl import *
|
||||
....
|
||||
grid = EclGrid("ECLIPSE.EGRID")
|
||||
|
||||
To get all the symbols from the ecl package directly into the
|
||||
current namespace, or alternatively you can use
|
||||
|
||||
import ert.ecl.ecl as ecl
|
||||
....
|
||||
grid = ecl.EclGrid("ECLIPSE.EGRID")
|
||||
|
||||
and then use all the symbols from the ecl package under the
|
||||
common 'ecl' prefix. The functionality of the xxx module in the
|
||||
xxx package could have been achieved with the __init__.py - but
|
||||
then you would get it unconditionally.
|
||||
|
||||
3. The ert python distribution is organized with one subpackage per
|
||||
library in the ERT C distribution. In each subpackage there is a
|
||||
module with the same name as the library which this package
|
||||
wraps, i.e. the ecl package, which wraps the libecl library,
|
||||
contains a module libecl.py. This module again contains the
|
||||
ctypes trickery to actually load the libecl.so shared library.
|
||||
|
||||
|
||||
[1]: The big disclaimer here is that you can use the '__init__.py' to
|
||||
perform additional action when importing the 'ert' package. In
|
||||
particular all symbols defined in the '__init__.py' module/file
|
||||
will be part of the ert packages namespace. As described in the
|
||||
chapter '4. Conventions applied in ert' no such trickery is applied
|
||||
in the ert distribution, all the __init__.py files are empty.
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
Installing the ert-python code
|
||||
==============================
|
||||
There is currently no functionality provided to install the ert python
|
||||
code in a way which ensures that it automagically works; however the
|
||||
required manual process is quite simple. Essentially you need to
|
||||
install the python package ert/ in a location where the python
|
||||
interpreter can find it, and the shared libraries must be installed so
|
||||
that the runtime linker can find them.
|
||||
|
||||
Installing the python modules
|
||||
-----------------------------
|
||||
The package directory ert/ and all it's subdirectories must be
|
||||
installed somewhere where your python interpreter will find it. That
|
||||
is either in one of the default locations[1], or you can put it
|
||||
in an arbitrary location and subsequently update your PYTHONPATH
|
||||
environment variable to contain the directory where you put the ert/
|
||||
directory.
|
||||
|
||||
[1]: To find out what your default locations are you can run the
|
||||
following code in an interactive python shell:
|
||||
|
||||
>>> import sys
|
||||
>>> print sys.path
|
||||
|
||||
All the python modules should be installed as world readable; for a
|
||||
minor performance gain it is possible to bytecompile during the
|
||||
install with a small python script based on the compile function in
|
||||
the py_compile module.
|
||||
|
||||
Installing the shared libraries
|
||||
-------------------------------
|
||||
The ctypes module is based on the dlopen() system call to open shared
|
||||
libraries libecl.so, libert_util.so and so on. The final dlopen() call is
|
||||
not passed any path information, i.e. the runtime linker must be able
|
||||
to locate the shared libraries based solely on the normal runtime
|
||||
linking conventions. This means that you have two alternatives when
|
||||
installing the shared libraries:
|
||||
|
||||
1. Install all the libraries in one of the standard library locations
|
||||
like /usr/local/lib.
|
||||
|
||||
2. Install the libraries in an arbitrary directory, and then
|
||||
subsequently update the LD_LIBRARY_PATH variable to point to this
|
||||
directory.
|
||||
|
||||
The shared libraries you will need are: libert_util.so, libecl.so,
|
||||
librms.so, libsched.so, libenkf.so, libjob_queue.so, libconfig.so and libgeometry.so
|
||||
|
||||
The test environment
|
||||
--------------------
|
||||
In the test/ directory there are two small shell scripts local_bash
|
||||
and local_csh which will modify the current shell (i.e. local_xxx
|
||||
should be sourced) so that the tests in the test/ directory can
|
||||
be run on the source distribution. These two shell scripts illustrate
|
||||
the necessary modifications to PYTHONPATH and LD_LIBRARY_PATH when
|
||||
installing in non-standard locations.
|
||||
|
||||
Problems
|
||||
--------
|
||||
Apart from normal bugs there are two possible problems when installing
|
||||
ert-python:
|
||||
|
||||
1. The python interpreter can not find the ert package. In this case
|
||||
the error will be something like this:
|
||||
|
||||
>>> import ert
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
ImportError: No module named ert
|
||||
|
||||
The ert/ directory is not located in the right place, or the
|
||||
PYTHONPATH variable has not been updated correctly.
|
||||
|
||||
2. The runtime linker can not find one of the shared libraries:
|
||||
|
||||
import ert.ecl.ecl_grid
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "ert/ecl/ecl_grid.py", line 30, in <module>
|
||||
from ert.util.tvector import DoubleVector # Requires merging of typemaps ....
|
||||
File "ert/util/tvector.py", line 46, in <module>
|
||||
import libutil
|
||||
File "ert/util/libutil.py", line 31, in <module>
|
||||
clib.load("libg2c.so.0")
|
||||
File "ert/cwrap/clib.py", line 49, in load
|
||||
raise ImportError( error_msg )
|
||||
ImportError: Sorry - failed to load shared library:libg2c.so.0
|
||||
|
||||
Tried in:
|
||||
You might need to update your LD_LIBRARY_PATH variable
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
add_subdirectory(cwrap)
|
||||
add_subdirectory( ert )
|
||||
if (BUILD_ERT)
|
||||
if (ERT_BUILD_GUI)
|
||||
add_subdirectory( ert_gui )
|
||||
endif()
|
||||
endif()
|
||||
add_subdirectory( bin )
|
||||
@@ -0,0 +1,16 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
basecclass.py
|
||||
basecenum.py
|
||||
basecvalue.py
|
||||
cclass.py
|
||||
cenum.py
|
||||
cfile.py
|
||||
clib.py
|
||||
cnamespace.py
|
||||
cwrap.py
|
||||
metacwrap.py
|
||||
prototype.py
|
||||
)
|
||||
|
||||
add_python_package("cwrap" ${PYTHON_INSTALL_PREFIX}/cwrap "${PYTHON_SOURCES}" True)
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
"""
|
||||
The cwrap package contains several small utility modules to simplify
|
||||
the process of interacting with a C library:
|
||||
|
||||
cenum: This module contains a function 'create_enum()' which can
|
||||
internalize enum symbols with the corresponding values in the
|
||||
calling scope. The enum symbols are not part of the shared
|
||||
library as such, and the working of create_enum() requires a
|
||||
special function for 'enum-introspection' to be available in the
|
||||
shared library.
|
||||
|
||||
clib: This module contains the function load() which will load a
|
||||
shared library using the ctypes.CDLL(); the function has
|
||||
facilities for trying several different names when loading the
|
||||
library.
|
||||
|
||||
cwrap: This module contains support for a Python <-> C type map. The
|
||||
whole type mapping in the ert python bindings is based on this
|
||||
module.
|
||||
|
||||
cfile: This module implemenets the class CFILE which can be used to
|
||||
extract the underlying FILE pointer from a Python filehandle, to
|
||||
facilitate use of Python filehandles for functions expecting a
|
||||
FILE pointer.
|
||||
"""
|
||||
|
||||
__author__ = 'Jean-Paul Balabanian, Joakim Hove, and PG Drange'
|
||||
__copyright__ = 'Copyright 2016, Statoil ASA'
|
||||
__credits__ = __author__
|
||||
__license__ = 'GPL'
|
||||
__version__ = '0.0.1'
|
||||
__maintainer__ = __author__
|
||||
__email__ = __author__
|
||||
__status__ = 'Prototype'
|
||||
|
||||
from .basecclass import BaseCClass
|
||||
from .basecenum import BaseCEnum
|
||||
from .basecvalue import BaseCValue
|
||||
|
||||
from .cclass import CClass
|
||||
from .cenum import create_enum
|
||||
from .cfile import CFILE
|
||||
from .clib import load, lib_name
|
||||
|
||||
from .cnamespace import CNamespace
|
||||
|
||||
from .cwrap import CWrapper, CWrapperNameSpace, CWrapError
|
||||
|
||||
from .metacwrap import MetaCWrap
|
||||
from .prototype import REGISTERED_TYPES, Prototype, PrototypeError
|
||||
|
||||
__all__ = ['BaseCClass', 'BaseCEnum', 'BaseCValue', 'CClass', 'CFILE',
|
||||
'CNamespace', 'CWrapper', 'CWrapperNameSpace', 'CWrapError',
|
||||
'MetaCWrap', 'Prototype', 'load', 'lib_name']
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import ctypes
|
||||
from .metacwrap import MetaCWrap
|
||||
from .cnamespace import CNamespace
|
||||
|
||||
class BaseCClass(object):
|
||||
__metaclass__ = MetaCWrap
|
||||
|
||||
namespaces = {}
|
||||
|
||||
def __init__(self, c_pointer, parent=None, is_reference=False):
|
||||
if c_pointer == 0 or c_pointer is None:
|
||||
raise ValueError("Must have a valid (not null) pointer value!")
|
||||
|
||||
if c_pointer < 0:
|
||||
raise ValueError("The pointer value is negative! This may be correct, but usually is not!")
|
||||
|
||||
self.__c_pointer = c_pointer
|
||||
self.__parent = parent
|
||||
self.__is_reference = is_reference
|
||||
|
||||
def __new__(cls, *more, **kwargs):
|
||||
obj = super(BaseCClass, cls).__new__(cls)
|
||||
obj.__c_pointer = None
|
||||
obj.__parent = None
|
||||
obj.__is_reference = False
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
@classmethod
|
||||
def cNamespace(cls):
|
||||
""" @rtype: CNamespace """
|
||||
if cls not in BaseCClass.namespaces:
|
||||
BaseCClass.namespaces[cls] = CNamespace(cls.__name__)
|
||||
return BaseCClass.namespaces[cls]
|
||||
|
||||
@classmethod
|
||||
def from_param(cls, c_class_object):
|
||||
if c_class_object is not None and not isinstance(c_class_object, BaseCClass):
|
||||
raise ValueError("c_class_object must be a BaseCClass instance!")
|
||||
|
||||
if c_class_object is None:
|
||||
return ctypes.c_void_p()
|
||||
else:
|
||||
return ctypes.c_void_p(c_class_object.__c_pointer)
|
||||
|
||||
@classmethod
|
||||
def createPythonObject(cls, c_pointer):
|
||||
if c_pointer is not None:
|
||||
new_obj = cls.__new__(cls)
|
||||
BaseCClass.__init__(new_obj, c_pointer=c_pointer, parent=None, is_reference=False)
|
||||
return new_obj
|
||||
else:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def createCReference(cls, c_pointer, parent=None):
|
||||
if c_pointer is not None:
|
||||
new_obj = cls.__new__(cls)
|
||||
BaseCClass.__init__(new_obj, c_pointer=c_pointer, parent=parent, is_reference=True)
|
||||
return new_obj
|
||||
else:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def storageType(cls):
|
||||
return ctypes.c_void_p
|
||||
|
||||
def convertToCReference(self, parent):
|
||||
self.__is_reference = True
|
||||
self.__parent = parent
|
||||
|
||||
|
||||
def setParent(self, parent=None):
|
||||
if self.__is_reference:
|
||||
self.__parent = parent
|
||||
else:
|
||||
raise UserWarning("Can only set parent on reference types!")
|
||||
|
||||
return self
|
||||
|
||||
def isReference(self):
|
||||
""" @rtype: bool """
|
||||
return self.__is_reference
|
||||
|
||||
def parent(self):
|
||||
return self.__parent
|
||||
|
||||
def __eq__(self, other):
|
||||
# This is the last resort comparison function; it will do a
|
||||
# plain pointer comparison on the underlying C object; or
|
||||
# Python is-same-object comparison.
|
||||
if isinstance(other, BaseCClass):
|
||||
return self.__c_pointer == other.__c_pointer
|
||||
else:
|
||||
return super(BaseCClass , self).__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
# Similar to last resort comparison; this returns the hash of the
|
||||
# underlying C pointer.
|
||||
return hash(self.__c_pointer)
|
||||
|
||||
def free(self):
|
||||
raise NotImplementedError("A BaseCClass requires a free method implementation!")
|
||||
|
||||
|
||||
def __del__(self):
|
||||
if self.free is not None:
|
||||
if not self.__is_reference:
|
||||
# Important to check the c_pointer; in the case of failed object creation
|
||||
# we can have a Python object with c_pointer == None.
|
||||
if self.__c_pointer > 0:
|
||||
self.free()
|
||||
|
||||
def _invalidateCPointer(self):
|
||||
self.__c_pointer = None
|
||||
|
||||
|
||||
def __bool__(self):
|
||||
"""The BaseCClass instance will evaluate to true if it is bound to an
|
||||
underlying C object, otherwise it will evaluate to False. More
|
||||
elaborate bool tests should be implemented in the derived
|
||||
class.
|
||||
"""
|
||||
if self.__c_pointer:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def __nonzero__(self):
|
||||
return self.__bool__( )
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import ctypes
|
||||
from .metacwrap import MetaCWrap
|
||||
from .cwrap import CWrapper
|
||||
|
||||
|
||||
class BaseCEnum(object):
|
||||
__metaclass__ = MetaCWrap
|
||||
enum_namespace = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if not self in self.enum_namespace[self.__class__]:
|
||||
raise NotImplementedError("Can not be instantiated directly!")
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if len(args) == 1:
|
||||
enum = cls.__resolveEnum(args[0])
|
||||
|
||||
if enum is None:
|
||||
raise ValueError("Unknown enum value: %i" % args[0])
|
||||
|
||||
return enum
|
||||
else:
|
||||
obj = super(BaseCEnum, cls).__new__(cls, *args)
|
||||
obj.name = None
|
||||
obj.value = None
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def from_param(cls, c_class_object):
|
||||
if not isinstance(c_class_object, BaseCEnum):
|
||||
raise ValueError("c_class_object must be an BaseCEnum instance!")
|
||||
return c_class_object.value
|
||||
|
||||
@classmethod
|
||||
def addEnum(cls, name, value):
|
||||
if not isinstance(value, int):
|
||||
raise ValueError("Value must be an integer!")
|
||||
|
||||
enum = cls.__new__(cls)
|
||||
enum.name = name
|
||||
enum.value = value
|
||||
|
||||
setattr(cls, name, enum)
|
||||
|
||||
if cls not in cls.enum_namespace:
|
||||
cls.enum_namespace[cls] = []
|
||||
|
||||
cls.enum_namespace[cls].append(enum)
|
||||
|
||||
@classmethod
|
||||
def enums(cls):
|
||||
return list(cls.enum_namespace[cls])
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, self.__class__):
|
||||
return self.value == other.value
|
||||
|
||||
if isinstance(other, int):
|
||||
return self.value == other
|
||||
|
||||
return False
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.value)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __add__(self, other):
|
||||
self.__assertOtherIsSameType(other)
|
||||
value = self.value + other.value
|
||||
return self.__resolveOrCreateEnum(value)
|
||||
|
||||
def __or__(self, other):
|
||||
self.__assertOtherIsSameType(other)
|
||||
value = self.value | other.value
|
||||
return self.__resolveOrCreateEnum(value)
|
||||
|
||||
|
||||
def __xor__(self, other):
|
||||
self.__assertOtherIsSameType(other)
|
||||
value = self.value ^ other.value
|
||||
return self.__resolveOrCreateEnum(value)
|
||||
|
||||
def __and__(self, other):
|
||||
self.__assertOtherIsSameType(other)
|
||||
value = self.value & other.value
|
||||
return self.__resolveOrCreateEnum(value)
|
||||
|
||||
def __int__(self):
|
||||
return self.value
|
||||
|
||||
def __contains__(self, item):
|
||||
return self & item == item
|
||||
|
||||
@classmethod
|
||||
def __createEnum(cls, value):
|
||||
enum = cls.__new__(cls)
|
||||
enum.name = "Unnamed '%s' enum with value: %i" % (str(cls.__name__), value)
|
||||
enum.value = value
|
||||
return enum
|
||||
|
||||
@classmethod
|
||||
def __resolveOrCreateEnum(cls, value):
|
||||
enum = cls.__resolveEnum(value)
|
||||
|
||||
if enum is not None:
|
||||
return enum
|
||||
|
||||
return cls.__createEnum(value)
|
||||
|
||||
@classmethod
|
||||
def __resolveEnum(cls, value):
|
||||
for enum in cls.enum_namespace[cls]:
|
||||
if enum.value == value:
|
||||
return enum
|
||||
return None
|
||||
|
||||
def __assertOtherIsSameType(self, other):
|
||||
assert isinstance(other, self.__class__), "Can only operate on enums of same type: %s =! %s" % (
|
||||
self.__class__.__name__, other.__class__.__name__)
|
||||
|
||||
|
||||
@classmethod
|
||||
def populateEnum(cls, library, enum_provider_function):
|
||||
try:
|
||||
func = getattr(library, enum_provider_function)
|
||||
except AttributeError:
|
||||
raise ValueError("Could not find enum description function: %s - can not load enum: %s." % (enum_provider_function, cls.__name__))
|
||||
|
||||
func.restype = ctypes.c_char_p
|
||||
func.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_int)]
|
||||
|
||||
index = 0
|
||||
while True:
|
||||
value = ctypes.c_int()
|
||||
name = func(index, ctypes.byref(value))
|
||||
|
||||
if name:
|
||||
cls.addEnum(name, value.value)
|
||||
index += 1
|
||||
else:
|
||||
break
|
||||
|
||||
@classmethod
|
||||
def registerEnum(cls, library, enum_name):
|
||||
cwrapper = CWrapper(library)
|
||||
cwrapper.registerType(enum_name, cls)
|
||||
@@ -0,0 +1,62 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from ctypes import pointer, c_long, c_int, c_bool, c_float, c_double, c_byte, \
|
||||
c_short, c_char, c_ubyte, c_ushort, c_uint, c_ulong
|
||||
|
||||
from .metacwrap import MetaCWrap
|
||||
|
||||
class BaseCValue(object):
|
||||
__metaclass__ = MetaCWrap
|
||||
DATA_TYPE = None
|
||||
LEGAL_TYPES = [c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, c_long, c_ulong, c_bool, c_char, c_float, c_double]
|
||||
|
||||
def __init__(self, value):
|
||||
super(BaseCValue, self).__init__()
|
||||
|
||||
if not self.DATA_TYPE in self.LEGAL_TYPES:
|
||||
raise ValueError("DATA_TYPE must be one of these CTypes classes: %s" % BaseCValue.LEGAL_TYPES)
|
||||
|
||||
self.__value = self.cast(value)
|
||||
|
||||
|
||||
def value(self):
|
||||
return self.__value.value
|
||||
|
||||
@classmethod
|
||||
def storageType(cls):
|
||||
return cls.type()
|
||||
|
||||
@classmethod
|
||||
def type(cls):
|
||||
return cls.DATA_TYPE
|
||||
|
||||
@classmethod
|
||||
def cast(cls, value):
|
||||
return cls.DATA_TYPE(value)
|
||||
|
||||
def setValue(self, value):
|
||||
self.__value = self.cast(value)
|
||||
|
||||
def asPointer(self):
|
||||
return pointer(self.__value)
|
||||
|
||||
@classmethod
|
||||
def from_param(cls, c_value_object):
|
||||
if c_value_object is not None and not isinstance(c_value_object, BaseCValue):
|
||||
raise ValueError("c_class_object must be a BaseCValue instance!")
|
||||
|
||||
return c_value_object.__value
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import ctypes
|
||||
|
||||
|
||||
class CClass(object):
|
||||
"""
|
||||
Base class used by all the classes wrapping a C 'class'.
|
||||
All Python classes which wrap a C 'class', like e.g. the
|
||||
ecl_kw_type structure, need a from_param() classmethod which is
|
||||
used to 'translate' between the python object and the pointer to
|
||||
corresponding underlying C structure. The CClass class contains an
|
||||
implementation of such a from_param() method; all Python classes
|
||||
which wrap a C 'class' should inherit from this class.
|
||||
Python objects are managed with garbage collection, whereas the C
|
||||
structures which are wrapped must be managed manually. Users of
|
||||
the ert Python wrappers should assume 100% normal Python memory
|
||||
management, and /NOT/ take any special care due to the fact that
|
||||
the underlying C datastructures must be managed manually. This is
|
||||
achieved, but in the wrapping layer some care must be taken.
|
||||
|
||||
- Each of the Python classes is centered around an opaque handle
|
||||
to the underlying C object; the c_ptr. This handle is passed
|
||||
as a 'self' argument when calling the C functions.
|
||||
- When a Python object goes out of scope the __del__() method is
|
||||
called, and this method can be used to call the xxx_free()
|
||||
method of the underlying C object.
|
||||
There are essentially two categories of C objects; either objects
|
||||
which are created with a xxxx_alloc() call - these objects should
|
||||
be destroyed when they go out scope. The other class of objects
|
||||
are references which are managed by another (container) class,
|
||||
e.g. an ecl_kw instance might be managed by an ecl_file
|
||||
container. In the case of of references the C object should not
|
||||
bee freed when the Python object goes out of scope, on the other
|
||||
hand we must ascertain that the container does /NOT/ go out of
|
||||
scope as long the child reference exists, otherwise the object
|
||||
will be destroyed under the reference.
|
||||
The handling of references and objects is performed with the
|
||||
init_cref() and init_cobj() methods below:
|
||||
- init_cref(): This method will store a 'pointer' to the Python
|
||||
container managing the reference. This will ensure that the
|
||||
container is not recycle prematurely. Consider the example:
|
||||
def load_poro( base ):
|
||||
file = ecl.EclFile( "%s.INIT" % base )
|
||||
poro = file["PORO"][0]
|
||||
return poro
|
||||
poro = load_poro( "CASE" )
|
||||
|
||||
In this example the scope of the 'file' object is limited to
|
||||
the 'load_poro' function, whereas the poro keyword object is
|
||||
returned to the calling scope. If we did not take care to
|
||||
store a pointer to the file container that would be destroyed
|
||||
when returning from the load_poro function, and the poro
|
||||
object would be invalid.
|
||||
When a reference, like the poro variable in the example is
|
||||
destroyed no cleanup is performed on the underlying C
|
||||
storage, and the init_cref() method sets the cfree function
|
||||
pointer to None.
|
||||
- init_cobj(): This method is used for 'self-managed' objects,
|
||||
when the Python object goes out of scope the underlying C
|
||||
object should be destroyed as well. This is achieved by
|
||||
storing a function pointer to the xxxx_free() function of the
|
||||
C object.
|
||||
|
||||
"""
|
||||
c_ptr = None
|
||||
cfree = None
|
||||
parent = None
|
||||
|
||||
@classmethod
|
||||
def from_param( cls, obj ):
|
||||
if obj is None:
|
||||
return ctypes.c_void_p()
|
||||
else:
|
||||
return ctypes.c_void_p(obj.c_ptr)
|
||||
|
||||
@classmethod
|
||||
def asPythonObject( cls, c_ptr, cfree):
|
||||
assert cfree is not None
|
||||
obj = cls()
|
||||
obj.init_cobj(c_ptr, cfree)
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def asPythonReference( cls, c_ptr, parent ):
|
||||
obj = cls()
|
||||
obj.init_cref(c_ptr, parent)
|
||||
return obj
|
||||
|
||||
|
||||
def init_cref(self, c_ptr, parent):
|
||||
self.c_ptr = c_ptr
|
||||
self.parent = parent
|
||||
self.cfree = None
|
||||
|
||||
|
||||
def init_cobj( self, c_ptr, cfree):
|
||||
self.c_ptr = c_ptr
|
||||
self.parent = None
|
||||
self.cfree = cfree
|
||||
|
||||
|
||||
def __del__(self):
|
||||
if self.cfree:
|
||||
# Important to check the c_ptr; in the case of failed object creation
|
||||
# we can have a Python object with c_ptr == None.
|
||||
if self.c_ptr:
|
||||
self.cfree(self)
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
"""
|
||||
Convenience module for loading enum symbols and values.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import ctypes
|
||||
import sys
|
||||
|
||||
|
||||
def make_enum(name, attributes):
|
||||
class cls(object):
|
||||
pass
|
||||
|
||||
cls.__name__ = name
|
||||
cls.enum_names = []
|
||||
for key in attributes.keys():
|
||||
setattr(cls, key, attributes[key])
|
||||
cls.enum_names.append(key)
|
||||
|
||||
return cls
|
||||
|
||||
|
||||
def create_enum( lib, func_name, enum_name, name_space=None):
|
||||
"""
|
||||
Create and insert enum values as integer constants.
|
||||
This function is based on iteratively querying the C library for
|
||||
symbol name and enum value for an enum. The @lib argument should
|
||||
be a ctypes libhandle, i.e. the return value from a CDLL()
|
||||
call. The @func_name argument should be the name (i.e. a string)
|
||||
of a C-function in the library which can provide symbol name and
|
||||
numerical value for the elements in an enum.
|
||||
For an enum definition like:
|
||||
enum my_enum {
|
||||
INVALID = 0,
|
||||
VALUE1 = 1,
|
||||
VALUE2 = 2
|
||||
}
|
||||
The @func_name function in @lib should return
|
||||
'INVALID' , 0
|
||||
'VALUE1' , 1
|
||||
'VALUE2' , 2
|
||||
for index arguments 0,1 and 2 respectively. The symbols are
|
||||
installed in the dictionary @dict[1], so after the enum above has
|
||||
been internalized the dict scope will have symols like:
|
||||
INVALID = 0
|
||||
VALUE1 = 1
|
||||
VALUE2 = 2
|
||||
|
||||
The @dict dictionary should typically be the globals() dictionary
|
||||
from the calling scope. In addition to updating the @dict
|
||||
dictionary with enum defintions a dictionary of name -> value
|
||||
mappings will be returned.
|
||||
The ecl_util.h header file contains the following emum definition:
|
||||
typedef enum { ECL_OTHER_FILE = 0 ,
|
||||
ECL_RESTART_FILE = 1 ,
|
||||
ECL_UNIFIED_RESTART_FILE = 2 ,
|
||||
ECL_SUMMARY_FILE = 4 ,
|
||||
ECL_UNIFIED_SUMMARY_FILE = 8 ,
|
||||
ECL_SUMMARY_HEADER_FILE = 16 ,
|
||||
ECL_GRID_FILE = 32 ,
|
||||
ECL_EGRID_FILE = 64 ,
|
||||
ECL_INIT_FILE = 128 ,
|
||||
ECL_RFT_FILE = 256 ,
|
||||
ECL_DATA_FILE = 512 } ecl_file_enum;
|
||||
In the ecl_util.py module this enum is loaded as:
|
||||
file_enum = create_enum( libecl.lib , "ecl_util_file_enum_iget" , "ecl_file_enum" , globals())
|
||||
This will install the symbols ECL_OTHER_FILE, ECL_RESTART_FILE,
|
||||
... , ECL_DATA_FILE in the global namespace of the calling module
|
||||
and in additional return a dictionary with the same mapping:
|
||||
for enum_elm in file_enum.keys():
|
||||
print('%s -> %d' % ( enum_elm , file_enum[ enum_elm ] ))
|
||||
"""
|
||||
|
||||
try:
|
||||
func = getattr(lib, func_name)
|
||||
except AttributeError:
|
||||
sys.exit("Could not find enum description function:%s - can not load enum:%s." % (func_name, enum_name))
|
||||
|
||||
func.restype = ctypes.c_char_p
|
||||
func.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_int)]
|
||||
enum = {}
|
||||
index = 0
|
||||
while True:
|
||||
value = ctypes.c_int()
|
||||
name = func(index, ctypes.byref(value))
|
||||
if name:
|
||||
if name_space:
|
||||
name_space[name] = value.value
|
||||
enum[name] = value.value
|
||||
index += 1
|
||||
else:
|
||||
break
|
||||
enum = make_enum(enum_name, enum)
|
||||
if name_space:
|
||||
name_space[enum_name] = enum
|
||||
return enum
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import ctypes
|
||||
from .prototype import Prototype
|
||||
from .basecclass import BaseCClass
|
||||
|
||||
class CFILE(BaseCClass):
|
||||
"""
|
||||
Utility class to map a Python file handle <-> FILE* in C
|
||||
"""
|
||||
TYPE_NAME = "FILE"
|
||||
|
||||
_as_file = Prototype(ctypes.pythonapi, "void* PyFile_AsFile(py_object)")
|
||||
|
||||
def __init__(self, py_file):
|
||||
"""
|
||||
Takes a python file handle and looks up the underlying FILE *
|
||||
|
||||
The purpose of the CFILE class is to be able to use python
|
||||
file handles when calling C functions which expect a FILE
|
||||
pointer. A CFILE instance should be created based on the
|
||||
Python file handle, and that should be passed to the function
|
||||
expecting a FILE pointer.
|
||||
|
||||
The implementation is based on the ctypes object
|
||||
pythonapi which is ctypes wrapping of the CPython api.
|
||||
|
||||
C-function:
|
||||
void fprintf_hello(FILE * stream , const char * msg);
|
||||
|
||||
Python wrapper:
|
||||
lib = clib.load( "lib.so" )
|
||||
fprintf_hello = Prototype(lib, "void fprintf_hello( FILE , char* )")
|
||||
|
||||
Python use:
|
||||
py_fileH = open("file.txt" , "w")
|
||||
fprintf_hello( CFILE( py_fileH ) , "Message ...")
|
||||
py_fileH.close()
|
||||
|
||||
If the supplied argument is not of type py_file the function
|
||||
will raise a TypeException.
|
||||
|
||||
Examples: ert.ecl.ecl_kw.EclKW.fprintf_grdecl()
|
||||
"""
|
||||
c_ptr = self._as_file(py_file)
|
||||
try:
|
||||
super(CFILE, self).__init__(c_ptr)
|
||||
except ValueError as e:
|
||||
raise TypeError("Sorry - the supplied argument is not a valid Python file handle!")
|
||||
|
||||
self.py_file = py_file
|
||||
|
||||
|
||||
def __del__(self):
|
||||
pass
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'clib.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""Convenience module for loading shared library.
|
||||
|
||||
Observe that to ensure that all libraries are loaded through the same
|
||||
code path, all required libraries should be loaded explicitly through
|
||||
the use of import statements; i.e. the ert.geo package requires the
|
||||
libert_util librarary, to ensure that the correct version of the
|
||||
libert_util.so library file is loaded we should manually load that
|
||||
first as:
|
||||
|
||||
import ert.util
|
||||
GEO_LIB = ert.load("libert_geometry")
|
||||
|
||||
Otherwise the standard operating system dependency resolve code will
|
||||
be invoked when loading libert_geometry, and that could in principle
|
||||
lead to loading a different version of libert_util.so
|
||||
"""
|
||||
|
||||
import platform
|
||||
import ctypes
|
||||
import os
|
||||
|
||||
so_extension = {"linux" : "so",
|
||||
"linux2" : "so",
|
||||
"linux3" : "so",
|
||||
"win32" : "dll",
|
||||
"win64" : "dll",
|
||||
"darwin" : "dylib" }
|
||||
|
||||
|
||||
# Passing None to the CDLL() function means to open a lib handle to
|
||||
# the current runnning process, i.e. like dlopen( NULL ). We must
|
||||
# special case this to avoid creating the bogus argument 'None.so'.
|
||||
|
||||
def lib_name(lib , path = None , so_version = ""):
|
||||
if lib is None:
|
||||
return None
|
||||
else:
|
||||
platform_key = platform.system().lower()
|
||||
|
||||
if platform_key == "darwin":
|
||||
so_name = "%s%s.%s" % (lib, so_version, so_extension[ platform_key ])
|
||||
else:
|
||||
so_name = "%s.%s%s" % (lib, so_extension[ platform_key ], so_version)
|
||||
|
||||
if path:
|
||||
return os.path.join( path , so_name )
|
||||
else:
|
||||
return so_name
|
||||
|
||||
|
||||
|
||||
|
||||
def load( lib, so_version = None, path = None):
|
||||
"""Thin wrapper around the ctypes.CDLL function for loading shared
|
||||
library.
|
||||
|
||||
If the path argument is non Null the function will first try to
|
||||
load with full path. If that fails it wil also try to load without
|
||||
a path component, invoking normal dlopen() semantics.
|
||||
"""
|
||||
|
||||
dll = None
|
||||
lib_files = [ lib_name( lib , path = path , so_version = so_version) ]
|
||||
if path:
|
||||
lib_files.append( lib_name( lib , path = None , so_version = so_version) )
|
||||
|
||||
for lib_file in lib_files:
|
||||
try:
|
||||
dll = ctypes.CDLL(lib_file , ctypes.RTLD_GLOBAL)
|
||||
return dll
|
||||
except Exception, exc:
|
||||
error = exc
|
||||
|
||||
error_msg = "\nFailed to load shared library:%s\n\ndlopen() error: %s\n" % (lib , error)
|
||||
|
||||
LD_LIBRARY_PATH = os.getenv("LD_LIBRARY_PATH")
|
||||
if not LD_LIBRARY_PATH:
|
||||
LD_LIBRARY_PATH = ""
|
||||
|
||||
error_msg += """
|
||||
The runtime linker has searched through the default location of shared
|
||||
libraries, and also the locations mentioned in your LD_LIBRARY_PATH
|
||||
variable. Your current LD_LIBRARY_PATH setting is:
|
||||
|
||||
LD_LIBRARY_PATH: %s
|
||||
|
||||
You might need to update this variable?
|
||||
""" % LD_LIBRARY_PATH
|
||||
raise ImportError(error_msg)
|
||||
@@ -0,0 +1,39 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
class CNamespace(object):
|
||||
def __init__(self, name):
|
||||
object.__setattr__(self, "_name", name)
|
||||
object.__setattr__(self, "_functions", {})
|
||||
|
||||
def __str__(self):
|
||||
return "%s wrapper" % self._name
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.__setValue(key, value)
|
||||
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self._functions[item]
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
self.__setValue(key, value)
|
||||
|
||||
def __setValue(self, key, value):
|
||||
assert not hasattr(self, key), "The namespace %s already contains a function named %s!" % (self._name, key)
|
||||
|
||||
self._functions[key] = value
|
||||
object.__setattr__(self, key, value)
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
"""
|
||||
Module implementing type map between C types and Python classes.
|
||||
The prototype_pattern is a major regexp which is used to set the
|
||||
correct restype and argtypes attributes of the function objects.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import ctypes
|
||||
import re
|
||||
import sys
|
||||
import inspect
|
||||
|
||||
from .basecclass import BaseCClass
|
||||
from .basecvalue import BaseCValue
|
||||
from .prototype import REGISTERED_TYPES
|
||||
|
||||
|
||||
|
||||
prototype_pattern = "(?P<return>[a-zA-Z][a-zA-Z0-9_*]*) +(?P<function>[a-zA-Z]\w*) *[(](?P<arguments>[a-zA-Z0-9_*, ]*)[)]"
|
||||
|
||||
def isBoundMethod(func, bound_to_class=None):
|
||||
if bound_to_class is None:
|
||||
return hasattr(func, "__self__")
|
||||
else:
|
||||
return hasattr(func, "__self__") and issubclass(func.__self__, bound_to_class)
|
||||
|
||||
|
||||
class CWrapError(Exception):
|
||||
pass
|
||||
|
||||
class CWrapper:
|
||||
# Observe that registered_types is a class attribute, shared
|
||||
# between all CWrapper instances.
|
||||
registered_types = {}
|
||||
pattern = re.compile(prototype_pattern)
|
||||
|
||||
def __init__(self, lib):
|
||||
self.__lib = lib
|
||||
|
||||
@classmethod
|
||||
def registerType(cls, type_name, value):
|
||||
"""Register a type against a legal ctypes type or a callable (or class)"""
|
||||
# if type_name in cls.registered_types:
|
||||
# print("Type %s already exists!" % type_name)
|
||||
cls.registered_types[type_name] = value
|
||||
|
||||
@classmethod
|
||||
def registerObjectType(cls, type_name, base_c_class):
|
||||
"""
|
||||
Automatically registers a class type with object and reference versions.
|
||||
For example:
|
||||
string_list -> StringList
|
||||
string_list_ref -> StringList.createCReference
|
||||
string_list_obj -> StringList.createPythonObject
|
||||
@type type_name: str
|
||||
@type base_c_class: BaseCClass
|
||||
"""
|
||||
assert issubclass(base_c_class, BaseCClass)
|
||||
|
||||
cls.registerType(type_name, base_c_class)
|
||||
cls.registerType("%s_ref" % type_name, base_c_class.createCReference)
|
||||
cls.registerType("%s_obj" % type_name, base_c_class.createPythonObject)
|
||||
|
||||
|
||||
@classmethod
|
||||
def registerDefaultTypes(cls):
|
||||
"""Registers the default available types for prototyping."""
|
||||
cls.registerType("void", None)
|
||||
cls.registerType("void*", ctypes.c_void_p)
|
||||
cls.registerType("uint", ctypes.c_uint)
|
||||
cls.registerType("uint*", ctypes.POINTER(ctypes.c_uint))
|
||||
cls.registerType("int", ctypes.c_int)
|
||||
cls.registerType("int*", ctypes.POINTER(ctypes.c_int))
|
||||
cls.registerType("int64", ctypes.c_int64)
|
||||
cls.registerType("int64*", ctypes.POINTER(ctypes.c_int64))
|
||||
cls.registerType("size_t", ctypes.c_size_t)
|
||||
cls.registerType("size_t*", ctypes.POINTER(ctypes.c_size_t))
|
||||
cls.registerType("bool", ctypes.c_bool)
|
||||
cls.registerType("bool*", ctypes.POINTER(ctypes.c_bool))
|
||||
cls.registerType("long", ctypes.c_long)
|
||||
cls.registerType("long*", ctypes.POINTER(ctypes.c_long))
|
||||
cls.registerType("char", ctypes.c_char)
|
||||
cls.registerType("char*", ctypes.c_char_p)
|
||||
cls.registerType("char**", ctypes.POINTER(ctypes.c_char_p))
|
||||
cls.registerType("float", ctypes.c_float)
|
||||
cls.registerType("float*", ctypes.POINTER(ctypes.c_float))
|
||||
cls.registerType("double", ctypes.c_double)
|
||||
cls.registerType("double*", ctypes.POINTER(ctypes.c_double))
|
||||
|
||||
|
||||
def __parseType(self, type_name):
|
||||
"""Convert a prototype definition type from string to a ctypes legal type."""
|
||||
type_name = type_name.strip()
|
||||
|
||||
if type_name in CWrapper.registered_types:
|
||||
return CWrapper.registered_types[type_name]
|
||||
elif type_name in REGISTERED_TYPES:
|
||||
return REGISTERED_TYPES[type_name].type_class_or_function
|
||||
else:
|
||||
return getattr(ctypes, type_name)
|
||||
|
||||
|
||||
def prototype(self, prototype, lib=None):
|
||||
"""
|
||||
Defines the return type and arguments for a C-function
|
||||
prototype expects a string formatted like this:
|
||||
"type functionName(type, ... ,type)"
|
||||
where type is a type available to ctypes
|
||||
Some type are automatically converted:
|
||||
int -> c_int
|
||||
long -> c_long
|
||||
char -> c_char_p
|
||||
bool -> c_int
|
||||
void -> None
|
||||
double -> c_double
|
||||
float -> c_float
|
||||
There are also pointer versions of these:
|
||||
long* -> POINTER(c_long)
|
||||
bool* -> POINTER(c_int)
|
||||
double* -> POINTER(c_double)
|
||||
char* -> c_char_p
|
||||
...
|
||||
In addition, user register types are recognized and any type
|
||||
registered as a reference to BaseCClass createCReference and
|
||||
createPythonObject are treated as pointers and converted
|
||||
automatically.
|
||||
"""
|
||||
|
||||
match = re.match(CWrapper.pattern, prototype)
|
||||
if not match:
|
||||
raise CWrapError("Illegal prototype definition: %s\n" % prototype)
|
||||
else:
|
||||
restype = match.groupdict()["return"]
|
||||
function_name = match.groupdict()["function"]
|
||||
arguments = match.groupdict()["arguments"].split(",")
|
||||
|
||||
try:
|
||||
func = getattr(self.__lib, function_name)
|
||||
except AttributeError:
|
||||
raise CWrapError("Can not find function: %s in library: %s" % (function_name , self.__lib))
|
||||
|
||||
return_type = self.__parseType(restype)
|
||||
|
||||
if inspect.isclass(return_type) and issubclass(return_type, BaseCClass):
|
||||
sys.stderr.write("BaseCClass can not be used as a return type in prototype definition: %s\n" % prototype)
|
||||
sys.stderr.write(" Correct return type may be: %s_ref or %s_obj" % (restype, restype))
|
||||
return None
|
||||
|
||||
func.restype = return_type
|
||||
|
||||
if inspect.isclass(return_type):
|
||||
if issubclass(return_type, BaseCValue):
|
||||
self.setReturnBehavior(func, return_type)
|
||||
else:
|
||||
pass # Use default behavior for BaseCEnum and ctypes classes
|
||||
elif callable(return_type):
|
||||
if isBoundMethod(return_type, BaseCClass) or not isBoundMethod(return_type):
|
||||
self.setReturnBehavior(func, return_type)
|
||||
else:
|
||||
pass #Methods bound to anything else than BaseCClass
|
||||
else:
|
||||
if return_type is not None:
|
||||
raise CWrapError("Unknown return type: %s" % return_type)
|
||||
|
||||
if len(arguments) == 1 and arguments[0].strip() == "":
|
||||
func.argtypes = []
|
||||
else:
|
||||
argtypes = [self.__parseType(arg) for arg in arguments]
|
||||
if len(argtypes) == 1 and argtypes[0] is None:
|
||||
argtypes = []
|
||||
func.argtypes = argtypes
|
||||
|
||||
return func
|
||||
|
||||
@staticmethod
|
||||
def setReturnBehavior(func, return_type):
|
||||
if inspect.isclass(return_type) and issubclass(return_type, BaseCValue):
|
||||
func.restype = return_type.type()
|
||||
else:
|
||||
func.restype = ctypes.c_void_p
|
||||
|
||||
def returnFunction(result, func, arguments):
|
||||
return return_type(result)
|
||||
|
||||
func.errcheck = returnFunction
|
||||
|
||||
def printTypes(self):
|
||||
for ctype in self.registered_types.keys():
|
||||
print('%16s -> %s' % (ctype, self.registered_types[ctype]))
|
||||
|
||||
|
||||
class CWrapperNameSpace:
|
||||
def __init__( self, name ):
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
return "%s wrapper" % self.name
|
||||
|
||||
|
||||
CWrapper.registerDefaultTypes()
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import re
|
||||
from types import MethodType
|
||||
|
||||
from .prototype import registerType, Prototype
|
||||
|
||||
|
||||
def snakeCase(name):
|
||||
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
|
||||
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
|
||||
|
||||
|
||||
class MetaCWrap(type):
|
||||
def __init__(cls, name, bases, attrs):
|
||||
super(MetaCWrap, cls).__init__(name, bases, attrs)
|
||||
|
||||
is_return_type = False
|
||||
storage_type = None
|
||||
|
||||
if "TYPE_NAME" in attrs:
|
||||
type_name = attrs["TYPE_NAME"]
|
||||
else:
|
||||
type_name = snakeCase(name)
|
||||
|
||||
if hasattr(cls, "DATA_TYPE") or hasattr(cls, "enums"):
|
||||
is_return_type = True
|
||||
|
||||
if hasattr(cls, "storageType"):
|
||||
storage_type = cls.storageType()
|
||||
|
||||
registerType(type_name, cls, is_return_type=is_return_type, storage_type=storage_type)
|
||||
|
||||
if hasattr(cls, "createCReference"):
|
||||
registerType("%s_ref" % type_name, cls.createCReference, is_return_type=True, storage_type=storage_type)
|
||||
|
||||
if hasattr(cls, "createPythonObject"):
|
||||
registerType("%s_obj" % type_name, cls.createPythonObject, is_return_type=True, storage_type=storage_type)
|
||||
|
||||
|
||||
for key, attr in attrs.items():
|
||||
if isinstance(attr, Prototype):
|
||||
attr.resolve()
|
||||
attr.__name__ = key
|
||||
|
||||
if attr.shouldBeBound():
|
||||
method = MethodType(attr, None, cls)
|
||||
setattr(cls, key, method)
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import ctypes
|
||||
import inspect
|
||||
import re
|
||||
|
||||
import sys
|
||||
|
||||
class TypeDefinition(object):
|
||||
def __init__(self, type_class_or_function, is_return_type, storage_type):
|
||||
self.storage_type = storage_type
|
||||
self.is_return_type = is_return_type
|
||||
self.type_class_or_function = type_class_or_function
|
||||
|
||||
|
||||
REGISTERED_TYPES = {}
|
||||
""":type: dict[str,TypeDefinition]"""
|
||||
|
||||
|
||||
def registerType(type_name, type_class_or_function, is_return_type=True, storage_type=None):
|
||||
if type_name in REGISTERED_TYPES:
|
||||
raise PrototypeError("Type: '%s' already registered!" % type_name)
|
||||
|
||||
REGISTERED_TYPES[type_name] = TypeDefinition(type_class_or_function, is_return_type, storage_type)
|
||||
|
||||
# print("Registered: %s for class: %s" % (type_name, repr(type_class_or_function)))
|
||||
|
||||
registerType("void", None)
|
||||
registerType("void*", ctypes.c_void_p)
|
||||
registerType("uint", ctypes.c_uint)
|
||||
registerType("uint*", ctypes.POINTER(ctypes.c_uint))
|
||||
registerType("int", ctypes.c_int)
|
||||
registerType("int*", ctypes.POINTER(ctypes.c_int))
|
||||
registerType("int64", ctypes.c_int64)
|
||||
registerType("int64*", ctypes.POINTER(ctypes.c_int64))
|
||||
registerType("size_t", ctypes.c_size_t)
|
||||
registerType("size_t*", ctypes.POINTER(ctypes.c_size_t))
|
||||
registerType("bool", ctypes.c_bool)
|
||||
registerType("bool*", ctypes.POINTER(ctypes.c_bool))
|
||||
registerType("long", ctypes.c_long)
|
||||
registerType("long*", ctypes.POINTER(ctypes.c_long))
|
||||
registerType("char", ctypes.c_char)
|
||||
registerType("char*", ctypes.c_char_p)
|
||||
registerType("char**", ctypes.POINTER(ctypes.c_char_p))
|
||||
registerType("float", ctypes.c_float)
|
||||
registerType("float*", ctypes.POINTER(ctypes.c_float))
|
||||
registerType("double", ctypes.c_double)
|
||||
registerType("double*", ctypes.POINTER(ctypes.c_double))
|
||||
registerType("py_object", ctypes.py_object)
|
||||
|
||||
PROTOTYPE_PATTERN = "(?P<return>[a-zA-Z][a-zA-Z0-9_*]*) +(?P<function>[a-zA-Z]\w*) *[(](?P<arguments>[a-zA-Z0-9_*, ]*)[)]"
|
||||
|
||||
class PrototypeError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Prototype(object):
|
||||
pattern = re.compile(PROTOTYPE_PATTERN)
|
||||
|
||||
def __init__(self, lib, prototype, bind=False):
|
||||
super(Prototype, self).__init__()
|
||||
self._lib = lib
|
||||
self._prototype = prototype
|
||||
self._bind = bind
|
||||
self._func = None
|
||||
self.__name__ = prototype
|
||||
self._resolved = False
|
||||
|
||||
|
||||
def _parseType(self, type_name):
|
||||
"""Convert a prototype definition type from string to a ctypes legal type."""
|
||||
type_name = type_name.strip()
|
||||
|
||||
if type_name in REGISTERED_TYPES:
|
||||
type_definition = REGISTERED_TYPES[type_name]
|
||||
return type_definition.type_class_or_function, type_definition.storage_type
|
||||
raise ValueError("Unknown type: %s" % type_name)
|
||||
|
||||
|
||||
def shouldBeBound(self):
|
||||
return self._bind
|
||||
|
||||
def resolve(self):
|
||||
match = re.match(Prototype.pattern, self._prototype)
|
||||
if not match:
|
||||
raise PrototypeError("Illegal prototype definition: %s\n" % self._prototype)
|
||||
else:
|
||||
restype = match.groupdict()["return"]
|
||||
function_name = match.groupdict()["function"]
|
||||
self.__name__ = function_name
|
||||
arguments = match.groupdict()["arguments"].split(",")
|
||||
|
||||
try:
|
||||
func = getattr(self._lib, function_name)
|
||||
except AttributeError:
|
||||
raise PrototypeError("Can not find function: %s in library: %s" % (function_name , self._lib))
|
||||
|
||||
if not restype in REGISTERED_TYPES or not REGISTERED_TYPES[restype].is_return_type:
|
||||
sys.stderr.write("The type used as return type: %s is not registered as a return type.\n" % restype)
|
||||
|
||||
return_type = self._parseType(restype)
|
||||
|
||||
if inspect.isclass(return_type):
|
||||
sys.stderr.write(" Correct type may be: %s_ref or %s_obj.\n" % (restype, restype))
|
||||
|
||||
return None
|
||||
|
||||
return_type, storage_type = self._parseType(restype)
|
||||
|
||||
func.restype = return_type
|
||||
|
||||
if storage_type is not None:
|
||||
func.restype = storage_type
|
||||
|
||||
def returnFunction(result, func, arguments):
|
||||
return return_type(result)
|
||||
|
||||
func.errcheck = returnFunction
|
||||
|
||||
if len(arguments) == 1 and arguments[0].strip() == "":
|
||||
func.argtypes = []
|
||||
else:
|
||||
argtypes = [self._parseType(arg)[0] for arg in arguments]
|
||||
if len(argtypes) == 1 and argtypes[0] is None:
|
||||
argtypes = []
|
||||
func.argtypes = argtypes
|
||||
|
||||
self._func = func
|
||||
|
||||
|
||||
def __call__(self, *args):
|
||||
if not self._resolved:
|
||||
self.resolve()
|
||||
self._resolved = True
|
||||
|
||||
if self._func is None:
|
||||
raise PrototypeError("Prototype has not been properly resolved!")
|
||||
return self._func(*args)
|
||||
|
||||
def __repr__(self):
|
||||
bound = ""
|
||||
if self.shouldBeBound():
|
||||
bound = ", bind=True"
|
||||
|
||||
return 'Prototype("%s"%s)' % (self._prototype, bound)
|
||||
|
||||
@classmethod
|
||||
def registerType(cls, type_name, type_class_or_function, is_return_type=True, storage_type=None):
|
||||
registerType(type_name, type_class_or_function, is_return_type=is_return_type, storage_type=storage_type)
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
)
|
||||
add_python_package("python.ert" ${PYTHON_INSTALL_PREFIX}/ert "${PYTHON_SOURCES}" True)
|
||||
|
||||
add_subdirectory(ecl)
|
||||
add_subdirectory(geo)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(util)
|
||||
add_subdirectory(well)
|
||||
|
||||
IF (BUILD_ERT)
|
||||
set( ERT_SITE_INIT_TARGET "" CACHE FILE "Name of optional external site init module for top level ert package")
|
||||
if (EXISTS ${ERT_SITE_INIT_TARGET})
|
||||
message(STATUS "Using ert site init module: ${ERT_SITE_INIT_TARGET}")
|
||||
install(FILES ${ERT_SITE_INIT_TARGET} DESTINATION ${PYTHON_INSTALL_PREFIX}/ert RENAME ert_site_init.py)
|
||||
endif()
|
||||
add_subdirectory(analysis)
|
||||
add_subdirectory(config)
|
||||
add_subdirectory(enkf)
|
||||
add_subdirectory(job_queue)
|
||||
add_subdirectory(rms)
|
||||
add_subdirectory(sched)
|
||||
add_subdirectory(server)
|
||||
ENDIF()
|
||||
|
||||
|
||||
configure_file(ert_lib_info_build.py.in ${PROJECT_BINARY_DIR}/${PYTHON_INSTALL_PREFIX}/ert/__ert_lib_info.py )
|
||||
configure_file(ert_lib_info_install.py.in ${PROJECT_BINARY_DIR}/${PYTHON_INSTALL_PREFIX}/ert_lib_info_install.py )
|
||||
install(FILES ${PROJECT_BINARY_DIR}/${PYTHON_INSTALL_PREFIX}/ert_lib_info_install.py DESTINATION ${PYTHON_INSTALL_PREFIX}/ert RENAME __ert_lib_info.py)
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file '__init__.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""
|
||||
ert - Ensemble Reservoir Tool - a package for reservoir modeling.
|
||||
|
||||
The ert package itself has no code, but contains several subpackages:
|
||||
|
||||
ert.ecl: Package for working with ECLIPSE files. The far most mature
|
||||
package in ert.
|
||||
|
||||
ert.job_queue:
|
||||
|
||||
ert.util:
|
||||
|
||||
The ert package is based on wrapping the libriaries from the ERT C
|
||||
code with ctypes; an essential part of ctypes approach is to load the
|
||||
shared libraries with the ctypes.CDLL() function. The ctypes.CDLL()
|
||||
function uses the standard methods of the operating system,
|
||||
i.e. standard locations configured with ld.so.conf and the environment
|
||||
variable LD_LIBRARY_PATH.
|
||||
|
||||
To avoid conflict with other application using the ert libraries the
|
||||
Python code should be able to locate the shared libraries without
|
||||
(necessarily) using the LD_LIBRARY_PATH variable. The default
|
||||
behaviour is to try to load from the library ../../lib64, but by using
|
||||
the enviornment variable ERT_LIBRARY_PATH you can alter how ert looks
|
||||
for shared libraries.
|
||||
|
||||
1. By default the code will try to load the shared libraries from
|
||||
'../../lib64' relative to the location of this file.
|
||||
|
||||
2. Depending on the value of ERT_LIBRARY_PATH two different
|
||||
behaviours can be imposed:
|
||||
|
||||
Existing path: the package will look in the path pointed to
|
||||
by ERT_LIBRARY_PATH for shared libraries.
|
||||
|
||||
Arbitrary value: the package will use standard load order for
|
||||
the operating system.
|
||||
|
||||
If the fixed path, given by the default ../../lib64 or ERT_LIBRARY_PATH
|
||||
alternative fails, the loader will try the default load behaviour
|
||||
before giving up completely.
|
||||
"""
|
||||
import os.path
|
||||
import sys
|
||||
import warnings
|
||||
from cwrap import load as cwrapload
|
||||
|
||||
try:
|
||||
import ert_site_init
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
required_version_hex = 0x02060000
|
||||
|
||||
ert_lib_path = None
|
||||
ert_so_version = ""
|
||||
__version__ = "0.0.0"
|
||||
|
||||
|
||||
# 1. Try to load the __ert_lib_info module; this module has been
|
||||
# configured by cmake during the build configuration process. The
|
||||
# module should contain the variable lib_path pointing to the
|
||||
# directory with shared object files.
|
||||
try:
|
||||
import __ert_lib_info
|
||||
ert_lib_path = __ert_lib_info.lib_path
|
||||
ert_so_version = __ert_lib_info.so_version
|
||||
__version__ = __ert_lib_info.__version__
|
||||
except ImportError:
|
||||
pass
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
# 2. Using the environment variable ERT_LIBRARY_PATH it is possible to
|
||||
# override the default algorithms. If the ERT_LIBRARY_PATH is set
|
||||
# to a non existing directory a warning will go to stderr and the
|
||||
# setting will be ignored.
|
||||
env_lib_path = os.getenv("ERT_LIBRARY_PATH")
|
||||
if env_lib_path:
|
||||
if os.path.isdir( env_lib_path ):
|
||||
ert_lib_path = os.getenv("ERT_LIBRARY_PATH")
|
||||
else:
|
||||
sys.stderr.write("Warning: Environment variable ERT_LIBRARY_PATH points to nonexisting directory:%s - ignored" % env_lib_path)
|
||||
|
||||
|
||||
# Check that the final ert_lib_path setting corresponds to an existing
|
||||
# directory.
|
||||
if ert_lib_path:
|
||||
if not os.path.isdir( ert_lib_path ):
|
||||
ert_lib_path = None
|
||||
|
||||
|
||||
if sys.hexversion < required_version_hex:
|
||||
raise Exception("ERT Python requires at least version 2.6 of Python")
|
||||
|
||||
# This load() function is *the* function actually loading shared
|
||||
# libraries.
|
||||
|
||||
def load(name):
|
||||
return cwrapload( name , path = ert_lib_path , so_version = ert_so_version)
|
||||
|
||||
|
||||
from ert.util import Version
|
||||
from ert.util import updateAbortSignals
|
||||
|
||||
updateAbortSignals( )
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
@@ -0,0 +1,9 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
analysis_module.py
|
||||
linalg.py
|
||||
)
|
||||
|
||||
add_python_package("python.ert.analysis." ${PYTHON_INSTALL_PREFIX}/ert/analysis/ "${PYTHON_SOURCES}" True)
|
||||
|
||||
add_subdirectory(enums)
|
||||
@@ -0,0 +1,35 @@
|
||||
# Copyright (C) 2013 Statoil ASA, Norway.
|
||||
#
|
||||
# The file '__init__.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
|
||||
import ert
|
||||
from cwrap import Prototype
|
||||
import ert.util
|
||||
|
||||
class AnalysisPrototype(Prototype):
|
||||
lib = ert.load("libanalysis")
|
||||
|
||||
def __init__(self, prototype, bind=True):
|
||||
super(AnalysisPrototype, self).__init__(AnalysisPrototype.lib, prototype, bind=bind)
|
||||
|
||||
|
||||
|
||||
ANALYSIS_LIB = ert.load("libanalysis")
|
||||
|
||||
from .enums import AnalysisModuleOptionsEnum, AnalysisModuleLoadStatusEnum
|
||||
|
||||
from .analysis_module import AnalysisModule
|
||||
from .linalg import Linalg
|
||||
@@ -0,0 +1,175 @@
|
||||
# Copyright (C) 2013 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'analysis_module.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from cwrap import BaseCClass
|
||||
from ert.util.rng import RandomNumberGenerator
|
||||
from ert.analysis import AnalysisPrototype
|
||||
|
||||
|
||||
class AnalysisModule(BaseCClass):
|
||||
TYPE_NAME = "analysis_module"
|
||||
|
||||
_alloc_external = AnalysisPrototype("void* analysis_module_alloc_external(rng, char*)" , bind = False)
|
||||
_alloc_internal = AnalysisPrototype("void* analysis_module_alloc_internal(rng, char*)" , bind = False)
|
||||
_free = AnalysisPrototype("void analysis_module_free(analysis_module)")
|
||||
_get_lib_name = AnalysisPrototype("char* analysis_module_get_lib_name(analysis_module)")
|
||||
_get_module_internal = AnalysisPrototype("bool analysis_module_internal(analysis_module)")
|
||||
_set_var = AnalysisPrototype("bool analysis_module_set_var(analysis_module, char*, char*)")
|
||||
_get_table_name = AnalysisPrototype("char* analysis_module_get_table_name(analysis_module)")
|
||||
_get_name = AnalysisPrototype("char* analysis_module_get_name(analysis_module)")
|
||||
_check_option = AnalysisPrototype("bool analysis_module_check_option(analysis_module, long)")
|
||||
_has_var = AnalysisPrototype("bool analysis_module_has_var(analysis_module, char*)")
|
||||
_get_double = AnalysisPrototype("double analysis_module_get_double(analysis_module, char*)")
|
||||
_get_int = AnalysisPrototype("int analysis_module_get_int(analysis_module, char*)")
|
||||
_get_bool = AnalysisPrototype("bool analysis_module_get_bool(analysis_module, char*)")
|
||||
_get_str = AnalysisPrototype("char* analysis_module_get_ptr(analysis_module, char*)")
|
||||
_init_update = AnalysisPrototype("void analysis_module_init_update(analysis_module, bool_vector , matrix , matrix , matrix , matrix, matrix)")
|
||||
_updateA = AnalysisPrototype("void analysis_module_updateA(analysis_module, matrix , matrix , matrix , matrix, matrix, matrix, void*)")
|
||||
_initX = AnalysisPrototype("void analysis_module_initX(analysis_module, matrix , matrix , matrix , matrix , matrix, matrix, matrix)")
|
||||
|
||||
|
||||
# The VARIABLE_NAMES field is a completly broken special case
|
||||
# which only applies to the rml module.
|
||||
VARIABLE_NAMES = {
|
||||
"LAMBDA0": {"type": float, "description": "Initial Lambda"},
|
||||
"USE_PRIOR": {"type": bool, "description": "Use both Prior and Observation Variability"},
|
||||
"LAMBDA_REDUCE": {"type": float, "description": "Lambda Reduction Factor"},
|
||||
"LAMBDA_INCREASE": {"type": float, "description": "Lambda Incremental Factor"},
|
||||
"LAMBDA_MIN": {"type": float, "description": "Minimum Lambda"},
|
||||
"LOG_FILE": {"type": str, "description": "Log File"},
|
||||
"CLEAR_LOG": {"type": bool, "description": "Clear Existing Log File"},
|
||||
"LAMBDA_RECALCULATE": {"type": bool, "description": "Recalculate Lambda after each Iteration"},
|
||||
"ENKF_TRUNCATION": {"type": float, "description": "Singular value truncation"},
|
||||
"ENKF_NCOMP": {"type": int, "description": "ENKF_NCOMP"},
|
||||
"CV_NFOLDS": {"type": int, "description": "CV_NFOLDS"},
|
||||
"FWD_STEP_R2_LIMIT": {"type": float, "description": "FWD_STEP_R2_LIMIT"},
|
||||
"CV_PEN_PRESS": {"type": bool, "description": "CV_PEN_PRESS"}
|
||||
}
|
||||
|
||||
def __init__(self, rng , name = None , lib_name = None):
|
||||
if name is None and lib_name is None:
|
||||
raise ValueError("Must supply exactly one of lib or lib_name")
|
||||
|
||||
if name and lib_name:
|
||||
raise ValueError("Must supply exactly one of name or lib_name")
|
||||
|
||||
if lib_name:
|
||||
c_ptr = self._alloc_external(rng, lib_name )
|
||||
else:
|
||||
c_ptr = self._alloc_internal( rng , name )
|
||||
if not c_ptr:
|
||||
raise KeyError("Failed to load internal module:%s" % name)
|
||||
|
||||
super(AnalysisModule, self).__init__(c_ptr)
|
||||
|
||||
|
||||
def getVariableNames(self):
|
||||
""" @rtype: list of str """
|
||||
items = []
|
||||
for name in AnalysisModule.VARIABLE_NAMES:
|
||||
if self.hasVar(name):
|
||||
items.append(name)
|
||||
return items
|
||||
|
||||
def getVariableValue(self, name):
|
||||
""" @rtype: int or float or bool or str """
|
||||
variable_type = self.getVariableType(name)
|
||||
if variable_type == float:
|
||||
return self.getDouble(name)
|
||||
elif variable_type == bool:
|
||||
return self.getBool(name)
|
||||
elif variable_type == str:
|
||||
return self.getStr(name)
|
||||
elif variable_type == int:
|
||||
return self.getInt(name)
|
||||
|
||||
def getVariableType(self, name):
|
||||
""" :rtype: type """
|
||||
return AnalysisModule.VARIABLE_NAMES[name]["type"]
|
||||
|
||||
def getVariableDescription(self, name):
|
||||
""" :rtype: str """
|
||||
return AnalysisModule.VARIABLE_NAMES[name]["description"]
|
||||
|
||||
def getVar(self, name):
|
||||
return self.getVariableValue( name )
|
||||
|
||||
def free(self):
|
||||
self._free( )
|
||||
|
||||
def getLibName(self):
|
||||
return self._get_lib_name( )
|
||||
|
||||
def getInternal(self):
|
||||
return self._get_module_internal( )
|
||||
|
||||
def __assertVar(self , var_name):
|
||||
if not self.hasVar(var_name):
|
||||
raise KeyError("Module does not support key:%s" % var_name)
|
||||
|
||||
def setVar(self, var_name, value):
|
||||
self.__assertVar( var_name )
|
||||
string_value = str(value)
|
||||
return self._set_var(var_name, string_value)
|
||||
|
||||
|
||||
def getTableName(self):
|
||||
return self._get_table_name( )
|
||||
|
||||
def getName(self):
|
||||
""" :rtype: str """
|
||||
return self._get_name( )
|
||||
|
||||
def checkOption(self, flag):
|
||||
return self._check_option(flag)
|
||||
|
||||
def hasVar(self, var):
|
||||
""" :rtype: bool """
|
||||
return self._has_var(var)
|
||||
|
||||
def getDouble(self, var):
|
||||
""" :rtype: float """
|
||||
self.__assertVar( var )
|
||||
return self._get_double(var)
|
||||
|
||||
def getInt(self, var):
|
||||
""" :rtype: int """
|
||||
self.__assertVar( var )
|
||||
return self._get_int(var)
|
||||
|
||||
def getBool(self, var):
|
||||
""" :rtype: bool """
|
||||
self.__assertVar( var )
|
||||
return self._get_bool(var)
|
||||
|
||||
def getStr(self, var):
|
||||
""" :rtype: str """
|
||||
self.__assertVar( var )
|
||||
return self._get_str(var)
|
||||
|
||||
|
||||
def initUpdate(self, mask, S, R, dObs, E, D):
|
||||
self._init_update(mask, S, R, dObs, E, D)
|
||||
|
||||
|
||||
def updateA(self, A, S, R, dObs, E, D):
|
||||
self._updateA(A, S, R, dObs, E, D, None)
|
||||
|
||||
|
||||
def initX(self, A, S, R, dObs, E, D):
|
||||
X = Matrix( A.columns() , A.columns())
|
||||
self._initX(X, A, S, R, dObs, E, D)
|
||||
return X
|
||||
@@ -0,0 +1,10 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
analysis_module_options_enum.py
|
||||
analysis_module_load_status_enum.py
|
||||
)
|
||||
|
||||
add_python_package("python.ert.analysis.enums" ${PYTHON_INSTALL_PREFIX}/ert/analysis/enums "${PYTHON_SOURCES}" True)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from .analysis_module_options_enum import AnalysisModuleOptionsEnum
|
||||
from .analysis_module_load_status_enum import AnalysisModuleLoadStatusEnum
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
|
||||
# Copyright (C) 2013 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'analysis_module_load_status_enum.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
|
||||
from cwrap import BaseCEnum
|
||||
from ert.analysis import ANALYSIS_LIB
|
||||
|
||||
|
||||
class AnalysisModuleLoadStatusEnum(BaseCEnum):
|
||||
LOAD_OK = None
|
||||
DLOPEN_FAILURE = None
|
||||
LOAD_SYMBOL_TABLE_NOT_FOUND = None
|
||||
|
||||
AnalysisModuleLoadStatusEnum.addEnum("LOAD_OK", 0)
|
||||
AnalysisModuleLoadStatusEnum.addEnum("DLOPEN_FAILURE", 1)
|
||||
AnalysisModuleLoadStatusEnum.addEnum("LOAD_SYMBOL_TABLE_NOT_FOUND", 2)
|
||||
AnalysisModuleLoadStatusEnum.registerEnum(ANALYSIS_LIB, "analysis_module_load_status_enum")
|
||||
|
||||
|
||||
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Copyright (C) 2013 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'analysis_module_options_enum.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import BaseCEnum
|
||||
from ert.analysis import ANALYSIS_LIB
|
||||
|
||||
|
||||
class AnalysisModuleOptionsEnum(BaseCEnum):
|
||||
ANALYSIS_NEED_ED = None
|
||||
ANALYSIS_USE_A = None
|
||||
ANALYSIS_UPDATE_A = None
|
||||
ANALYSIS_SCALE_DATA = None
|
||||
ANALYSIS_ITERABLE = None
|
||||
|
||||
AnalysisModuleOptionsEnum.populateEnum(ANALYSIS_LIB , "analysis_module_flag_enum_iget")
|
||||
AnalysisModuleOptionsEnum.registerEnum(ANALYSIS_LIB , "analysis_module_options_enum")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Copyright (C) 2015 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'linalg.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from cwrap import BaseCClass
|
||||
from ert.analysis import AnalysisPrototype
|
||||
|
||||
|
||||
__all__ = ["numPC"]
|
||||
|
||||
class Linalg(BaseCClass):
|
||||
"""
|
||||
The linalg class is a purely static class which mainly serves as a
|
||||
namespace for a collection of ensemble based linear algebra
|
||||
methods.
|
||||
"""
|
||||
_get_num_PC = AnalysisPrototype("int enkf_linalg_num_PC( matrix , double)" , bind = False)
|
||||
|
||||
@staticmethod
|
||||
def numPC(S , truncation):
|
||||
if 0 < truncation <= 1:
|
||||
return Linalg._get_num_PC( S , truncation )
|
||||
else:
|
||||
raise ValueError("truncation must be in the interval (0,1]")
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
schema_item.py
|
||||
config_error.py
|
||||
config_parser.py
|
||||
config_content.py
|
||||
content_type_enum.py
|
||||
unrecognized_enum.py
|
||||
)
|
||||
|
||||
add_python_package("python.ert.config" ${PYTHON_INSTALL_PREFIX}/ert/config "${PYTHON_SOURCES}" True)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (C) 2013 Statoil ASA, Norway.
|
||||
#
|
||||
# The file '__init__.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import Prototype
|
||||
import ert
|
||||
import ert.util
|
||||
|
||||
class ConfigPrototype(Prototype):
|
||||
lib = ert.load("libconfig")
|
||||
|
||||
def __init__(self, prototype, bind=True):
|
||||
super(ConfigPrototype, self).__init__(ConfigPrototype.lib, prototype, bind=bind)
|
||||
|
||||
from .unrecognized_enum import UnrecognizedEnum
|
||||
from .content_type_enum import ContentTypeEnum
|
||||
from .config_error import ConfigError
|
||||
from .schema_item import SchemaItem
|
||||
from .config_content import ConfigContent , ContentItem, ContentNode
|
||||
from .config_parser import ConfigParser
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
# Copyright (C) 2015 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'config_content.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import os.path
|
||||
|
||||
from ert.config import UnrecognizedEnum, ContentTypeEnum, ConfigError, ConfigPrototype, SchemaItem
|
||||
from cwrap import BaseCClass
|
||||
|
||||
|
||||
class ContentNode(BaseCClass):
|
||||
TYPE_NAME = "content_node"
|
||||
|
||||
_iget = ConfigPrototype("char* config_content_node_iget( content_node , int)")
|
||||
_size = ConfigPrototype("int config_content_node_get_size( content_node )")
|
||||
_get_full_string = ConfigPrototype("char* config_content_node_get_full_string( content_node , char* )")
|
||||
_iget_type = ConfigPrototype("config_content_type_enum config_content_node_iget_type( content_node , int)")
|
||||
_iget_as_abspath = ConfigPrototype("char* config_content_node_iget_as_abspath( content_node , int)")
|
||||
_iget_as_relpath = ConfigPrototype("char* config_content_node_iget_as_relpath( content_node , int)")
|
||||
_iget_as_string = ConfigPrototype("char* config_content_node_iget( content_node , int)")
|
||||
_iget_as_int = ConfigPrototype("int config_content_node_iget_as_int( content_node , int)")
|
||||
_iget_as_double = ConfigPrototype("double config_content_node_iget_as_double( content_node , int)")
|
||||
_iget_as_path = ConfigPrototype("char* config_content_node_iget_as_path( content_node , int)")
|
||||
_iget_as_bool = ConfigPrototype("bool config_content_node_iget_as_bool( content_node , int)")
|
||||
_iget_as_isodate = ConfigPrototype("time_t config_content_node_iget_as_isodate( content_node , int)")
|
||||
|
||||
typed_get = {
|
||||
ContentTypeEnum.CONFIG_STRING: _iget_as_string,
|
||||
ContentTypeEnum.CONFIG_INT: _iget_as_int,
|
||||
ContentTypeEnum.CONFIG_FLOAT: _iget_as_double,
|
||||
ContentTypeEnum.CONFIG_PATH: _iget_as_path,
|
||||
ContentTypeEnum.CONFIG_EXISTING_PATH: _iget_as_path,
|
||||
ContentTypeEnum.CONFIG_BOOL: _iget_as_bool,
|
||||
ContentTypeEnum.CONFIG_ISODATE: _iget_as_isodate
|
||||
}
|
||||
|
||||
|
||||
def __init__(self):
|
||||
raise NotImplementedError("Class can not be instantiated directly!")
|
||||
|
||||
def __len__(self):
|
||||
return self._size()
|
||||
|
||||
def __assertIndex(self , index):
|
||||
if isinstance(index, int):
|
||||
if index < 0:
|
||||
index += len(self)
|
||||
|
||||
if not 0 <= index < len(self):
|
||||
raise IndexError
|
||||
return index
|
||||
else:
|
||||
raise TypeError("Invalid argument type: %s" % index)
|
||||
|
||||
|
||||
def __getitem__(self, index):
|
||||
index = self.__assertIndex(index)
|
||||
|
||||
content_type = self._iget_type(index)
|
||||
typed_get = self.typed_get[content_type]
|
||||
return typed_get(self, index)
|
||||
|
||||
def getPath(self , index = 0, absolute = True , relative_start = None):
|
||||
index = self.__assertIndex(index)
|
||||
content_type = self._iget_type(index)
|
||||
if content_type in [ContentTypeEnum.CONFIG_EXISTING_PATH , ContentTypeEnum.CONFIG_PATH]:
|
||||
if absolute:
|
||||
return self._iget_as_abspath(index)
|
||||
else:
|
||||
if relative_start is None:
|
||||
return self._iget_as_relpath(index)
|
||||
else:
|
||||
abs_path = self._iget_as_abspath(index)
|
||||
return os.path.relpath( abs_path , relative_start )
|
||||
else:
|
||||
raise TypeError("The getPath() method can only be called on PATH items")
|
||||
|
||||
def content(self, sep=" "):
|
||||
return self._get_full_string(sep)
|
||||
|
||||
|
||||
def igetString(self , index):
|
||||
index = self.__assertIndex(index)
|
||||
return self._iget(index )
|
||||
|
||||
|
||||
def asList(self):
|
||||
return [x for x in self]
|
||||
|
||||
|
||||
|
||||
class ContentItem(BaseCClass):
|
||||
TYPE_NAME = "content_item"
|
||||
|
||||
_alloc = ConfigPrototype("void* config_content_item_alloc( schema_item , void* )" , bind = False )
|
||||
_size = ConfigPrototype("int config_content_item_get_size( content_item )")
|
||||
_iget_content_node = ConfigPrototype("content_node_ref config_content_item_iget_node( content_item , int)")
|
||||
_free = ConfigPrototype("void config_content_item_free( content_item )")
|
||||
|
||||
def __init__(self , schema_item):
|
||||
path_elm = None
|
||||
c_ptr = self._alloc( schema_item , path_elm)
|
||||
super( ContentItem, self).__init__(c_ptr)
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return self._size()
|
||||
|
||||
|
||||
def __getitem__(self, index):
|
||||
if isinstance(index, int):
|
||||
if index < 0:
|
||||
index += len(self)
|
||||
|
||||
if (index >= 0) and (index < len(self)):
|
||||
return self._iget_content_node(index).setParent(self)
|
||||
else:
|
||||
raise IndexError
|
||||
else:
|
||||
raise TypeError("[] operator must have integer index")
|
||||
|
||||
def last(self):
|
||||
return self[-1]
|
||||
|
||||
def getValue(self , item_index = -1 , node_index = 0):
|
||||
node = self[item_index]
|
||||
return node[node_index]
|
||||
|
||||
|
||||
def free(self):
|
||||
self._free( )
|
||||
|
||||
|
||||
|
||||
class ConfigContent(BaseCClass):
|
||||
TYPE_NAME = "config_content"
|
||||
|
||||
_free = ConfigPrototype("void config_content_free( config_content )")
|
||||
_is_valid = ConfigPrototype("bool config_content_is_valid( config_content )")
|
||||
_has_key = ConfigPrototype("bool config_content_has_item( config_content , char*)")
|
||||
_get_item = ConfigPrototype("content_item_ref config_content_get_item( config_content , char*)")
|
||||
_get_errors = ConfigPrototype("config_error_ref config_content_get_errors( config_content )")
|
||||
|
||||
def __init__(self):
|
||||
raise NotImplementedError("Class can not be instantiated directly!")
|
||||
|
||||
def __contains__(self , key):
|
||||
return self._has_key(key)
|
||||
|
||||
def setParser(self , parser):
|
||||
self._parser = parser
|
||||
|
||||
|
||||
def __getitem__(self , key):
|
||||
if key in self:
|
||||
item = self._get_item(key)
|
||||
item.setParent( self )
|
||||
return item
|
||||
else:
|
||||
if key in self._parser:
|
||||
schema_item = SchemaItem( key )
|
||||
return ContentItem( schema_item )
|
||||
else:
|
||||
raise KeyError("No such key: %s" % key)
|
||||
|
||||
|
||||
def hasKey(self,key):
|
||||
return key in self
|
||||
|
||||
|
||||
def getValue(self , key , item_index = -1 , node_index = 0):
|
||||
item = self[key]
|
||||
return item.getValue( item_index , node_index )
|
||||
|
||||
|
||||
def isValid(self):
|
||||
return self._is_valid()
|
||||
|
||||
|
||||
def free(self):
|
||||
self._free()
|
||||
|
||||
|
||||
def getErrors(self):
|
||||
""" @rtype: ConfigError """
|
||||
return self._get_errors()
|
||||
@@ -0,0 +1,47 @@
|
||||
# Copyright (C) 2012 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'enkf_fs.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from ert.config import ConfigPrototype
|
||||
from cwrap import BaseCClass
|
||||
|
||||
|
||||
|
||||
class ConfigError(BaseCClass):
|
||||
TYPE_NAME = "config_error"
|
||||
_free = ConfigPrototype("void config_error_free(config_error)")
|
||||
_count = ConfigPrototype("int config_error_count(config_error)")
|
||||
_iget = ConfigPrototype("char* config_error_iget(config_error, int)")
|
||||
|
||||
def __init__(self):
|
||||
raise NotImplementedError("Class can not be instantiated directly!")
|
||||
|
||||
def __getitem__(self, index):
|
||||
""" @rtype: str """
|
||||
if not isinstance(index, int):
|
||||
raise TypeError("Expected an integer")
|
||||
|
||||
size = len(self)
|
||||
if index >= size:
|
||||
raise IndexError("Index out of range: %d < %d" % (index, size))
|
||||
|
||||
return self._iget(index)
|
||||
|
||||
def __len__(self):
|
||||
""" @rtype: int """
|
||||
return self._count()
|
||||
|
||||
def free(self):
|
||||
self._free()
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Copyright (C) 2013 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'config_parser.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
from ert.config import UnrecognizedEnum, ContentTypeEnum , ConfigContent, ConfigPrototype
|
||||
from cwrap import BaseCClass
|
||||
|
||||
|
||||
class ConfigParser(BaseCClass):
|
||||
TYPE_NAME = "config_parser"
|
||||
|
||||
_alloc = ConfigPrototype("void* config_alloc( )", bind=False)
|
||||
_add = ConfigPrototype("schema_item_ref config_add_schema_item( config_parser , char* , bool)")
|
||||
_free = ConfigPrototype("void config_free( config_parser )")
|
||||
_parse = ConfigPrototype("config_content_obj config_parse( config_parser , char* , char* , char* , char* , hash , config_unrecognized_enum , bool )")
|
||||
_get_schema_item = ConfigPrototype("schema_item_ref config_get_schema_item( config_parser , char*)")
|
||||
_has_schema_item = ConfigPrototype("bool config_has_schema_item( config_parser , char*)")
|
||||
|
||||
def __init__(self):
|
||||
c_ptr = self._alloc()
|
||||
super(ConfigParser, self).__init__(c_ptr)
|
||||
|
||||
|
||||
def __contains__(self , keyword):
|
||||
return self._has_schema_item( keyword )
|
||||
|
||||
|
||||
def add(self, keyword, required=False , value_type = None):
|
||||
item = self._add(keyword, required).setParent( self )
|
||||
if value_type:
|
||||
item.iset_type( 0 , value_type )
|
||||
|
||||
return item
|
||||
|
||||
|
||||
def getSchemaItem(self , keyword):
|
||||
if keyword in self:
|
||||
item = self._get_schema_item(keyword)
|
||||
item.setParent( self )
|
||||
else:
|
||||
raise KeyError("Config parser does not have item:%s" % keyword)
|
||||
|
||||
|
||||
def parse(self, config_file, comment_string="--", include_kw="INCLUDE", define_kw="DEFINE",
|
||||
pre_defined_kw_map=None, unrecognized=UnrecognizedEnum.CONFIG_UNRECOGNIZED_WARN, validate=True):
|
||||
""" @rtype: ConfigContent """
|
||||
|
||||
assert isinstance(unrecognized, UnrecognizedEnum)
|
||||
|
||||
|
||||
if os.path.exists(config_file):
|
||||
config_content = self._parse(config_file, comment_string, include_kw, define_kw, pre_defined_kw_map, unrecognized, validate)
|
||||
config_content.setParser( self )
|
||||
|
||||
if not config_content.isValid():
|
||||
if validate:
|
||||
sys.stderr.write("Errors parsing:%s \n" % config_file)
|
||||
for count, error in enumerate(config_content.getErrors()):
|
||||
sys.stderr.write(" %02d:%s\n" % (count , error))
|
||||
|
||||
raise ValueError("Parsing:%s failed" % config_file)
|
||||
|
||||
return config_content
|
||||
else:
|
||||
raise IOError("File: %s does not exists" % config_file)
|
||||
|
||||
|
||||
def free(self):
|
||||
self._free()
|
||||
@@ -0,0 +1,43 @@
|
||||
# Copyright (C) 2013 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'content_type_enum.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from cwrap import BaseCEnum
|
||||
|
||||
class ContentTypeEnum(BaseCEnum):
|
||||
TYPE_NAME = "config_content_type_enum"
|
||||
CONFIG_STRING = None
|
||||
CONFIG_INT = None
|
||||
CONFIG_FLOAT = None
|
||||
CONFIG_PATH = None
|
||||
CONFIG_EXISTING_PATH = None
|
||||
CONFIG_BOOL = None
|
||||
CONFIG_CONFIG = None
|
||||
CONFIG_BYTESIZE = None
|
||||
CONFIG_EXECUTABLE = None
|
||||
CONFIG_ISODATE = None
|
||||
CONFIG_INVALID = None
|
||||
|
||||
ContentTypeEnum.addEnum("CONFIG_STRING", 1)
|
||||
ContentTypeEnum.addEnum("CONFIG_INT", 2)
|
||||
ContentTypeEnum.addEnum("CONFIG_FLOAT", 4)
|
||||
ContentTypeEnum.addEnum("CONFIG_PATH", 8)
|
||||
ContentTypeEnum.addEnum("CONFIG_EXISTING_PATH", 16)
|
||||
ContentTypeEnum.addEnum("CONFIG_BOOL", 32)
|
||||
ContentTypeEnum.addEnum("CONFIG_CONFIG", 64)
|
||||
ContentTypeEnum.addEnum("CONFIG_BYTESIZE", 128)
|
||||
ContentTypeEnum.addEnum("CONFIG_EXECUTABLE", 256)
|
||||
ContentTypeEnum.addEnum("CONFIG_ISODATE", 512)
|
||||
ContentTypeEnum.addEnum("CONFIG_INVALID", 1024)
|
||||
@@ -0,0 +1,62 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from ert.config import ContentTypeEnum , ConfigPrototype
|
||||
from cwrap import BaseCClass
|
||||
|
||||
|
||||
class SchemaItem(BaseCClass):
|
||||
TYPE_NAME = "schema_item"
|
||||
|
||||
_alloc = ConfigPrototype("void* config_schema_item_alloc( char* , bool )", bind=False)
|
||||
_free = ConfigPrototype("void config_schema_item_free( schema_item )")
|
||||
_iget_type = ConfigPrototype("config_content_type_enum config_schema_item_iget_type( schema_item, int)")
|
||||
_iset_type = ConfigPrototype("void config_schema_item_iset_type( schema_item , int , config_content_type_enum)")
|
||||
_set_argc_minmax = ConfigPrototype("void config_schema_item_set_argc_minmax( schema_item , int , int)")
|
||||
_add_alternative = ConfigPrototype("void config_schema_item_add_indexed_alternative(schema_item , int , char*)")
|
||||
|
||||
def __init__(self, keyword, required=False):
|
||||
c_ptr = self._alloc(keyword, required)
|
||||
super(SchemaItem, self).__init__(c_ptr)
|
||||
|
||||
|
||||
def iget_type( self, index):
|
||||
""" @rtype: ContentTypeEnum """
|
||||
return self._iget_type(index)
|
||||
|
||||
def iset_type( self, index, schema_type ):
|
||||
"""
|
||||
@type schema_type: ContentTypeEnum
|
||||
"""
|
||||
assert isinstance(schema_type, ContentTypeEnum)
|
||||
self._iset_type(index, schema_type)
|
||||
|
||||
def set_argc_minmax(self, minimum, maximum):
|
||||
self._set_argc_minmax(minimum, maximum)
|
||||
|
||||
|
||||
def initSelection(self , index , alternatives):
|
||||
for alt in alternatives:
|
||||
self.addAlternative( index , alt )
|
||||
|
||||
|
||||
def addAlternative(self , index , alt):
|
||||
self._add_alternative( index , alt )
|
||||
|
||||
|
||||
def free(self):
|
||||
self._free()
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2013 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'unrecognized_enum.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from cwrap import BaseCEnum
|
||||
|
||||
class UnrecognizedEnum(BaseCEnum):
|
||||
TYPE_NAME = "config_unrecognized_enum"
|
||||
CONFIG_UNRECOGNIZED_IGNORE = None
|
||||
CONFIG_UNRECOGNIZED_WARN = None
|
||||
CONFIG_UNRECOGNIZED_ERROR = None
|
||||
|
||||
UnrecognizedEnum.addEnum("CONFIG_UNRECOGNIZED_IGNORE", 0)
|
||||
UnrecognizedEnum.addEnum("CONFIG_UNRECOGNIZED_WARN", 1)
|
||||
UnrecognizedEnum.addEnum("CONFIG_UNRECOGNIZED_ERROR", 2)
|
||||
@@ -0,0 +1,35 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
ecl.py
|
||||
ecl_3d_file.py
|
||||
ecl_3dkw.py
|
||||
ecl_case.py
|
||||
ecl_file.py
|
||||
ecl_grav.py
|
||||
ecl_grav_calc.py
|
||||
ecl_grid.py
|
||||
ecl_init_file.py
|
||||
ecl_kw.py
|
||||
ecl_npv.py
|
||||
ecl_region.py
|
||||
ecl_restart_file.py
|
||||
ecl_rft.py
|
||||
ecl_rft_cell.py
|
||||
ecl_smspec_node.py
|
||||
ecl_subsidence.py
|
||||
ecl_sum.py
|
||||
ecl_sum_keyword_vector.py
|
||||
ecl_sum_node.py
|
||||
ecl_sum_tstep.py
|
||||
ecl_sum_vector.py
|
||||
ecl_util.py
|
||||
fortio.py
|
||||
ecl_sum_keyword_vector.py
|
||||
ecl_cmp.py
|
||||
ecl_sum_var_type.py
|
||||
)
|
||||
|
||||
add_python_package("python.ert.ecl" ${PYTHON_INSTALL_PREFIX}/ert/ecl "${PYTHON_SOURCES}" True)
|
||||
|
||||
add_subdirectory(faults)
|
||||
add_subdirectory(rft)
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file '__init__.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""
|
||||
Package for working with ECLIPSE files.
|
||||
|
||||
The ecl package contains several classes for working with ECLIPSE
|
||||
files. The ecl package is a wrapper around the libecl library from the
|
||||
ERT distribution. Mainly the package is organized with modules
|
||||
ecl_xxx.py with a class EclXXX. The module ecl_xxx.py will generaly
|
||||
wrap the content of the c-file ecl_xxx.c The main content is:
|
||||
|
||||
fortio/FortIO: This is functionality to read and write binary
|
||||
fortran files.
|
||||
|
||||
ecl_kw/EclKW: This class holds one ECLIPSE keyword, like SWAT, in
|
||||
restart format.
|
||||
|
||||
ecl_file/EclFile: This class is used to load an ECLIPSE file in
|
||||
restart format, alternatively only parts of the file can be
|
||||
loaded. Internally it consists of a collection of EclKW
|
||||
instances.
|
||||
|
||||
ecl_grid/EclGrid: This will load an ECLIPSE GRID or EGRID file, and
|
||||
can then subsequently be used for queries about the grid.
|
||||
|
||||
ecl_sum/EclSum: This will load summary results from an ECLIPSE run;
|
||||
both data file(s) and the SMSPEC file. The EclSum object can be
|
||||
used as basis for queries on summary vectors.
|
||||
|
||||
ecl_rft/[EclRFTFile , EclRFT , EclRFTCell]: Loads an ECLIPSE RFT/PLT
|
||||
file, and can afterwords be used to support various queries.
|
||||
|
||||
ecl_region/EclRegion: Convenience class to support selecting cells
|
||||
in a grid based on a wide range of criteria. Can be used as a
|
||||
mask in operations on EclKW instances.
|
||||
|
||||
ecl_grav/EclGrav: Class used to simplify evaluation of ECLIPSE
|
||||
modelling time-lapse gravitational surveys.
|
||||
|
||||
ecl_subsidence/EclSubsidence: Small class used to evaluate simulated
|
||||
subsidence from ECLIPSE simulations; analogous to the EcLGrav
|
||||
functionality.
|
||||
|
||||
In addition there are some modules which do not follow the one class
|
||||
per module organization:
|
||||
|
||||
ecl_util: This is mainly a collection of constants, and a few
|
||||
stateless functions.
|
||||
|
||||
ecl: This module is purely for convenience, all the symbols in the
|
||||
package are explicitly imported into this module, ensuring that
|
||||
all symbols in the package are available under the common
|
||||
namespace 'ecl'.
|
||||
|
||||
"""
|
||||
import ert.util
|
||||
import ert.geo
|
||||
|
||||
from cwrap import Prototype
|
||||
|
||||
|
||||
class EclPrototype(Prototype):
|
||||
lib = ert.load("libecl")
|
||||
|
||||
def __init__(self, prototype, bind=True):
|
||||
super(EclPrototype, self).__init__(EclPrototype.lib, prototype, bind=bind)
|
||||
|
||||
ECL_LIB = ert.load("libecl")
|
||||
|
||||
from .ecl_util import EclFileEnum, EclFileFlagEnum, EclPhaseEnum, EclTypeEnum, EclUnitTypeEnum , EclUtil
|
||||
from .ecl_sum_var_type import EclSumVarType
|
||||
from .ecl_sum_tstep import EclSumTStep
|
||||
from .ecl_sum import EclSum #, EclSumVector, EclSumNode, EclSMSPECNode
|
||||
from .ecl_sum_keyword_vector import EclSumKeyWordVector
|
||||
from .ecl_rft_cell import EclPLTCell, EclRFTCell
|
||||
from .ecl_rft import EclRFT, EclRFTFile
|
||||
from .fortio import FortIO, openFortIO
|
||||
from .ecl_kw import EclKW
|
||||
from .ecl_3dkw import Ecl3DKW
|
||||
from .ecl_file import EclFile , openEclFile
|
||||
from .ecl_3d_file import Ecl3DFile
|
||||
from .ecl_init_file import EclInitFile
|
||||
from .ecl_restart_file import EclRestartFile
|
||||
from .ecl_grid import EclGrid
|
||||
from .ecl_region import EclRegion
|
||||
from .ecl_subsidence import EclSubsidence
|
||||
from .ecl_grav_calc import phase_deltag, deltag
|
||||
from .ecl_grav import EclGrav
|
||||
from .ecl_sum_node import EclSumNode
|
||||
from .ecl_sum_vector import EclSumVector
|
||||
from .ecl_npv import EclNPV , NPVPriceVector
|
||||
from .ecl_cmp import EclCmp
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""
|
||||
Convenience module importing all symbols in the ecl package.
|
||||
|
||||
This module is purely for convenience; it explicitly imports all the
|
||||
symbols from all the other modules into the common 'ecl'
|
||||
namespace. The whole point of this excercise is to facilitate the
|
||||
following construction:
|
||||
|
||||
import ert.ecl.ecl as ecl <-- Import this module; and create the
|
||||
namespace 'ecl'
|
||||
...
|
||||
...
|
||||
sum = ecl.EclSum( "ECLIPSE.DATA") <-- Classes EclSum and EclGrid are now
|
||||
grid = ecl.EclGrid("ECLIPSE.EGRID") accessible under the ecl namespace.
|
||||
|
||||
It is not necessary to use the this module. See the doc/import.txt
|
||||
document in the ert-python source distribution for more details of
|
||||
module import and namespace resolution.
|
||||
# """
|
||||
# import libecl
|
||||
# from ecl_kw import EclKW
|
||||
# from ecl_case import EclCase
|
||||
# from ecl_file import EclFile
|
||||
# from ecl_sum import EclSum
|
||||
# from ecl_rft import EclRFTFile , EclRFT
|
||||
# from ecl_rft_cell import EclRFTCell, EclPLTCell
|
||||
# from ecl_grid import EclGrid
|
||||
# from ecl_grav import EclGrav
|
||||
# from ecl_subsidence import EclSubsidence
|
||||
# from ecl_region import EclRegion
|
||||
# from fortio import FortIO
|
||||
# import ecl_util
|
||||
# from ecl_util import *
|
||||
|
||||
import os
|
||||
|
||||
warning_message = """
|
||||
|
||||
The ecl namespace is deprecated and will go away. You are advised to
|
||||
use one of these alternatives when importing the ert.ecl modules and
|
||||
classes:
|
||||
|
||||
---------------------------------------------------------------------
|
||||
Common prefix:
|
||||
import ert.ecl as ecl
|
||||
|
||||
Direct import of classes:
|
||||
from ert.ecl import EclGrid,EclSum
|
||||
---------------------------------------------------------------------
|
||||
|
||||
The environment variable ERT_SILENCE_WARNINGS is a colon separated
|
||||
list which can be used to silence warnings. To silence this particular
|
||||
warning set:
|
||||
|
||||
ERT_SILENCE_WARNINGS = \"eclimport\"
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from warnings import warn
|
||||
|
||||
ert_silence_warnings = os.environ.get("ERT_SILENCE_WARNINGS")
|
||||
if ert_silence_warnings:
|
||||
silent_warnings = set( ert_silence_warnings.split(":"))
|
||||
else:
|
||||
silent_warnings = set()
|
||||
|
||||
if not "eclimport" in silent_warnings:
|
||||
warn(warning_message , DeprecationWarning)
|
||||
|
||||
|
||||
from .ecl_sum import EclSum #, EclSumVector, EclSumNode, EclSMSPECNode
|
||||
from .ecl_rft_cell import EclPLTCell, EclRFTCell
|
||||
from .ecl_rft import EclRFT, EclRFTFile
|
||||
from .ecl_kw import EclKW
|
||||
from .ecl_file import EclFile
|
||||
from .fortio import FortIO
|
||||
from .ecl_grid import EclGrid
|
||||
from .ecl_region import EclRegion
|
||||
from .ecl_case import EclCase
|
||||
from .ecl_subsidence import EclSubsidence
|
||||
from .ecl_grav_calc import deltag, phase_deltag
|
||||
from .ecl_grav import EclGrav
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright (C) 2015 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_init_file.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from ert.ecl import EclFile, Ecl3DKW
|
||||
|
||||
|
||||
class Ecl3DFile(EclFile):
|
||||
def __init__(self , grid , filename , flags = 0):
|
||||
self.grid = grid
|
||||
super(Ecl3DFile, self).__init__(filename , flags)
|
||||
|
||||
|
||||
def __getitem__(self , index):
|
||||
return_arg = super(Ecl3DFile, self).__getitem__( index )
|
||||
if isinstance(return_arg,list):
|
||||
kw_list = return_arg
|
||||
else:
|
||||
kw_list = [ return_arg ]
|
||||
|
||||
# Go through all the keywords and try inplace promotion to Ecl3DKW
|
||||
for kw in kw_list:
|
||||
try:
|
||||
Ecl3DKW.castFromKW( kw , self.grid )
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
return return_arg
|
||||
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
# Copyright (C) 2015 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_3dkw.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from __future__ import print_function
|
||||
from ecl_kw import EclKW
|
||||
|
||||
class Ecl3DKW(EclKW):
|
||||
"""
|
||||
Class for working with Eclipse keywords defined over a grid
|
||||
|
||||
The Ecl3DKW class is derived from the EclKW class, and most of the
|
||||
methods are implemented in the EclKW base class. The purpose of
|
||||
the Ecl3DKW class is to simplify working with 3D properties like
|
||||
PORO or SATNUM.
|
||||
|
||||
The Ecl3DKW class has an attached EclGrid which is used to support
|
||||
[i,j,k] indexing, and a defined default value which is used when
|
||||
reading an inactive value. The Ecl3DKW keyword instances are
|
||||
returned from the EclInitFile and EclRestartFile classes, in
|
||||
addition you can excplicitly \"cast\" a EclKW keyword to Ecl3DKW
|
||||
with the Ecl3DKW.castFromKW() classmethod.
|
||||
|
||||
Usage example:
|
||||
|
||||
from ert.ecl import EclInitFile,EclGrid
|
||||
|
||||
grid = EclGrid("ECLIPSE.EGRID")
|
||||
file = EclInitFile(grid , "ECLIPSE.INIT")
|
||||
|
||||
permx_kw = file["PORO"][0]
|
||||
porv_kw = file["PORV"][0]
|
||||
|
||||
permx_kw.setDefault( -1 )
|
||||
for k in range(grid.getNZ()):
|
||||
for j in range(grid.getNY()):
|
||||
for i in range(grid.getNX()):
|
||||
print('"(%d,%d,%d) Permx:%g Porv:%g"' % (i,j,k,permx_kw[i,j,k] , porv_kw[i,j,k]))
|
||||
|
||||
In the example we open an ECLIPSE INIT file and extract the PERMX
|
||||
and PORV properties, and then iterate over all the cells in the
|
||||
grid.
|
||||
|
||||
In the INIT file the PORV keyword is stored with all cells,
|
||||
whereas the PERMX keyword typically only has the active cells
|
||||
stored, this active/inactive gymnastics is handled
|
||||
transparently. With the call:
|
||||
|
||||
permx_kw.setDefault( -1 )
|
||||
|
||||
we say that we want the value -1 for all inactive cells in the
|
||||
PERMX property.
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def create(cls , kw , grid , value_type , default_value = 0 , global_active = False):
|
||||
if global_active:
|
||||
size = grid.getGlobalSize()
|
||||
else:
|
||||
size = grid.getNumActive( )
|
||||
|
||||
new_kw = super(Ecl3DKW , cls).create( kw , size , value_type)
|
||||
new_kw.grid = grid
|
||||
new_kw.global_active = global_active
|
||||
new_kw.setDefault( default_value )
|
||||
return new_kw
|
||||
|
||||
@classmethod
|
||||
def read_grdecl( cls , grid , fileH , kw , strict = True , ecl_type = None):
|
||||
"""
|
||||
Will load an Ecl3DKW instance from a grdecl formatted filehandle.
|
||||
|
||||
See the base class EclKW.read_grdecl() for more documentation.
|
||||
"""
|
||||
kw = super(Ecl3DKW , cls).read_grdecl( fileH , kw , strict , ecl_type)
|
||||
Ecl3DKW.castFromKW(kw , grid)
|
||||
return kw
|
||||
|
||||
|
||||
|
||||
def __getitem__(self , index):
|
||||
"""Will return item [g] or [i,j,k].
|
||||
|
||||
The __getitem__() methods supports both scalar indexing like
|
||||
[g] and tuples [i,j,k]. If the input argument is given as a
|
||||
[i,j,k] tuple it is converted to an active index before the
|
||||
final lookup.
|
||||
|
||||
If the [i,j,k] input corresponds to an inactive cell in a
|
||||
keyword with only nactive elements the default value will be
|
||||
returned. By default the default value will be 0, but another
|
||||
value can be assigned with the setDefault() method.
|
||||
"""
|
||||
if isinstance(index , tuple):
|
||||
global_index = self.grid.get_global_index( ijk = index )
|
||||
if self.global_active:
|
||||
index = global_index
|
||||
else:
|
||||
if not self.grid.active( global_index = global_index):
|
||||
return self.getDefault()
|
||||
else:
|
||||
index = self.grid.get_active_index( ijk = index )
|
||||
|
||||
|
||||
return super(Ecl3DKW , self).__getitem__( index )
|
||||
|
||||
|
||||
|
||||
|
||||
def __setitem__(self , index , value):
|
||||
"""Set the value of at index [g] or [i,j,k].
|
||||
|
||||
The __setitem__() methods supports both scalar indexing like
|
||||
[g] and tuples [i,j,k]. If the input argument is given as a
|
||||
[i,j,k] tuple it is converted to an active index before the
|
||||
final assignment.
|
||||
|
||||
If you try to assign an inactive cell in a keyword with only
|
||||
nactive elements a ValueError() exception will be raised.
|
||||
"""
|
||||
if isinstance(index , tuple):
|
||||
global_index = self.grid.get_global_index( ijk = index )
|
||||
if self.global_active:
|
||||
index = global_index
|
||||
else:
|
||||
if not self.grid.active( global_index = global_index):
|
||||
raise ValueError("Tried to assign value to inactive cell: (%d,%d,%d)" % index)
|
||||
else:
|
||||
index = self.grid.get_active_index( ijk = index )
|
||||
|
||||
|
||||
return super(Ecl3DKW , self).__setitem__( index , value )
|
||||
|
||||
|
||||
@classmethod
|
||||
def castFromKW(cls , kw , grid , default_value = 0):
|
||||
"""Will convert a normal EclKW to a Ecl3DKW.
|
||||
|
||||
The method will convert a normal EclKW instance to Ecl3DKw
|
||||
instance with an attached grid and a default value.
|
||||
|
||||
The method will check that size of the keyword is compatible
|
||||
with the grid dimensions, i.e. the keyword must have either
|
||||
nactive or nx*ny*nz elements. If the size of the keyword is
|
||||
not compatible with the grid dimensions a ValueError exception
|
||||
is raised.
|
||||
|
||||
Example:
|
||||
|
||||
1. Load the poro keyword from a grdecl formatted file.
|
||||
2. Convert the keyword to a 3D keyword.
|
||||
|
||||
|
||||
from ert.ecl import EclGrid,EclKW,Ecl3DKW
|
||||
|
||||
grid = EclGrid("ECLIPSE.EGRID")
|
||||
poro = EclKW.read_grdecl(open("poro.grdecl") , "PORO")
|
||||
Ecl3DKW.castFromKW( poro , grid )
|
||||
|
||||
print('Porosity in cell (10,11,12):%g' % poro[10,11,12])
|
||||
"""
|
||||
if len(kw) == grid.getGlobalSize():
|
||||
kw.global_active = True
|
||||
elif len(kw) == grid.getNumActive():
|
||||
kw.global_active = False
|
||||
else:
|
||||
raise ValueError("Size mismatch - must have size matching global/active size of grid")
|
||||
|
||||
|
||||
kw.__class__ = cls
|
||||
kw.default_value = default_value
|
||||
kw.grid = grid
|
||||
if len(kw) == grid.getGlobalSize():
|
||||
kw.global_active = True
|
||||
else:
|
||||
kw.global_active = False
|
||||
|
||||
kw.setDefault( default_value )
|
||||
return kw
|
||||
|
||||
|
||||
def compressedCopy(self):
|
||||
"""Will return a EclKW copy with nactive elements.
|
||||
|
||||
The returned copy will be of type EclKW; i.e. no default
|
||||
interpolation and only linear access in the [] operator. The
|
||||
main purpose of this is to facilitate iteration over the
|
||||
active index, and for writing binary files.
|
||||
"""
|
||||
return self.grid.compressedKWCopy( self )
|
||||
|
||||
|
||||
def globalCopy(self):
|
||||
"""Will return a EclKW copy with nx*ny*nz elements.
|
||||
|
||||
The returned copy will be of type EclKW; i.e. no default
|
||||
interpolation and only linear access in the [] operator. The
|
||||
main purpose of this is to facilitate iteration over the
|
||||
global index, and for writing binary files.
|
||||
"""
|
||||
return self.grid.globalKWCopy( self , self.getDefault() )
|
||||
|
||||
|
||||
|
||||
def dims(self):
|
||||
return (self.grid.getNX() , self.grid.getNY() , self.grid.getNZ())
|
||||
|
||||
|
||||
def setDefault(self, default_value):
|
||||
self.default_value = default_value
|
||||
|
||||
|
||||
def getDefault(self):
|
||||
return self.default_value
|
||||
|
||||
|
||||
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_case.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""
|
||||
Implements EclCase class which is a container for one ECLIPSE case.
|
||||
|
||||
This module is a pure Python module which does not directly invoke any
|
||||
C based functions.
|
||||
"""
|
||||
import os
|
||||
import warnings
|
||||
from ert.ecl import EclRFTFile, EclGrid, EclSum, EclUtil
|
||||
|
||||
|
||||
|
||||
class EclCase:
|
||||
"""
|
||||
Small container for one ECLIPSE case.
|
||||
|
||||
Mostly a wrapper around an ECLIPSE datafile, along with properties
|
||||
to load the corresponding summary, grid and rft files. In addition
|
||||
there are methods run() and submit() to run the ECLIPSE
|
||||
simulation.
|
||||
"""
|
||||
def __init__(self , input_case):
|
||||
"""
|
||||
Create a new case based on path/basename.
|
||||
|
||||
The @input_case argument should be the basename of the ECLIPSE
|
||||
case, it can contain an additional path component. The
|
||||
@input_case argument can contain an extension, but that is not
|
||||
necessary; it does not need to point an existing file.
|
||||
|
||||
These are all valid:
|
||||
|
||||
case1 = EclCase( "ECLIPSE" )
|
||||
case2 = EclCase( "relative/path/ECLIPSE.SMSPEC" )
|
||||
case3 = EclCase( "/absolute/path/simulation/ECLIPSE_3.xxx" )
|
||||
|
||||
"""
|
||||
warnings.warn("The EclCase class is deprecated - instantiate the EclSum / EclGrid / ... classes directly." , DeprecationWarning)
|
||||
|
||||
self.case = input_case
|
||||
(path , tmp) = os.path.split( input_case )
|
||||
if path:
|
||||
self.__path = os.path.abspath( path )
|
||||
else:
|
||||
self.__path = os.getcwd()
|
||||
(self.__base , self.ext) = os.path.splitext( tmp )
|
||||
|
||||
self.__sum = None
|
||||
self.__grid = None
|
||||
self.__data_file = None
|
||||
self.__rft = None
|
||||
|
||||
|
||||
@property
|
||||
def datafile( self ):
|
||||
"""
|
||||
Will return the full path to the ECLIPSE data file.
|
||||
|
||||
Observe that this method is purely about string manipulation;
|
||||
i.e. it is not checked if the datafile actually exists.
|
||||
"""
|
||||
|
||||
if not self.__data_file:
|
||||
self.__data_file = "%s/%s.DATA" % ( self.__path , self.__base )
|
||||
return self.__data_file
|
||||
|
||||
|
||||
@property
|
||||
def sum( self ):
|
||||
"""
|
||||
An EclSum instance for the current case; or None.
|
||||
|
||||
Observe that accessing the summary of a running ECLIPSE
|
||||
simulation, i.e. to check progress, is extremely error
|
||||
prone. During the actual simulation the summary files on disk
|
||||
are invalid a large part of the time, and an attempt to load
|
||||
at these times will lead to an instant 'crash and burn'.
|
||||
"""
|
||||
if not self.__sum:
|
||||
self.__sum = EclSum( self.case )
|
||||
return self.__sum
|
||||
|
||||
|
||||
@property
|
||||
def grid( self ):
|
||||
"""
|
||||
An EclGrid instance for the current case; or None.
|
||||
"""
|
||||
if not self.__grid:
|
||||
self.__grid = EclGrid( self.case )
|
||||
return self.__grid
|
||||
|
||||
|
||||
@property
|
||||
def rft_file( self ):
|
||||
"""
|
||||
An EclRFTFile instance for the current case; or None.
|
||||
"""
|
||||
if not self.__rft:
|
||||
self.__rft = EclRFTFile( self.case )
|
||||
return self.__rft
|
||||
|
||||
|
||||
@property
|
||||
def base( self ):
|
||||
"""
|
||||
Will return the ECLIPSE basename of the current case.
|
||||
"""
|
||||
return self.__base
|
||||
|
||||
|
||||
@property
|
||||
def path( self ):
|
||||
"""
|
||||
Will return the absolute path of the current case.
|
||||
"""
|
||||
return self.__path
|
||||
|
||||
|
||||
def run( self,
|
||||
ecl_cmd = None,
|
||||
ecl_version = None,
|
||||
driver = None,
|
||||
driver_type = None,
|
||||
driver_options = None,
|
||||
blocking = False ):
|
||||
"""
|
||||
Will start an ECLIPSE simulation of the current case.
|
||||
|
||||
The method has a long and nasty argument list, but all
|
||||
arguments have sensible defaults, and you probably do not need
|
||||
to enter any at all. The arguments are as follows:
|
||||
|
||||
ecl_cmd: The command used to run ECLIPSE. This will
|
||||
typically be a script of some kind. The command will be
|
||||
called with three commandline arguments: version datafile
|
||||
num_cpu
|
||||
|
||||
ecl_version: The eclipse version you want to use, this
|
||||
should be a string of the type "2010.2".
|
||||
|
||||
driver: This should be an instance of Driver() from
|
||||
ert.job_queue.driver. If driver is None the method will
|
||||
create a new driver instance.
|
||||
|
||||
driver_type: If the driver is none the method will create a
|
||||
new driver instance, it will create a driver of this type
|
||||
(i.e. LOCAL, LSF or RSH).
|
||||
|
||||
driver_options: When creating a new driver, these options
|
||||
will be used.
|
||||
|
||||
blocking: If blocking is True the method will not return
|
||||
before the simulation is complete, otherwise the method
|
||||
will return immediately. If blocking is False the ECLIPSE
|
||||
simulation will continue even if the python script
|
||||
exits.
|
||||
|
||||
Observe that there are some dependencies between the arguments:
|
||||
|
||||
* If both driver and driver_type are present the existing
|
||||
driver instance will be used, and the driver_type argument
|
||||
will be ignored.
|
||||
|
||||
* The driver_options argument will only be used when
|
||||
creating a new driver instance, and will not be used to
|
||||
modify an existing driver instance.
|
||||
"""
|
||||
import ert.job_queue.driver as queue_driver
|
||||
|
||||
num_cpu = EclUtil.get_num_cpu( self.datafile )
|
||||
argv = [ecl_version , self.datafile , num_cpu]
|
||||
|
||||
job = driver.submit( self.base , ecl_cmd , self.path , argv , blocking = blocking)
|
||||
return job
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
# Copyright (C) 2015 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_cmp.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from ert.ecl import EclSum
|
||||
|
||||
|
||||
class EclCase(object):
|
||||
def __init__(self , case):
|
||||
self.case = case
|
||||
|
||||
self.grid = None
|
||||
self.restart = None
|
||||
self.init = None
|
||||
self.summary = None
|
||||
|
||||
self.loadSummary( )
|
||||
|
||||
|
||||
def __contains__(self , key):
|
||||
return key in self.summary
|
||||
|
||||
|
||||
def keys(self):
|
||||
return self.summary.keys()
|
||||
|
||||
|
||||
def wells(self):
|
||||
return self.summary.wells( )
|
||||
|
||||
def loadSummary(self):
|
||||
self.summary = EclSum( self.case )
|
||||
|
||||
|
||||
def startTimeEqual(self , other):
|
||||
if self.summary.getDataStartTime() == other.summary.getDataStartTime():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def endTimeEqual(self , other):
|
||||
if self.summary.getEndTime() == other.summary.getEndTime():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def cmpSummaryVector(self , other , key , sample = 100):
|
||||
if key in self and key in other:
|
||||
days_total = min( self.summary.getSimulationLength() , other.summary.getSimulationLength() )
|
||||
dt = days_total / (sample - 1)
|
||||
days = [ x * dt for x in range(sample) ]
|
||||
|
||||
ref_data = self.summary.get_interp_vector( key , days_list = days )
|
||||
test_data = other.summary.get_interp_vector( key , days_list = days )
|
||||
diff_data = ref_data - test_data
|
||||
|
||||
ref_sum = sum(ref_data)
|
||||
diff_sum = sum( abs(diff_data) )
|
||||
return (diff_sum , ref_sum)
|
||||
else:
|
||||
raise KeyError("Key:%s was not present in both cases" % key)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class EclCmp(object):
|
||||
|
||||
def __init__(self , test_case , ref_case):
|
||||
"""Class to compare to simulation cases with Eclipse formatted result files.
|
||||
|
||||
The first argument is supposed to be the test_case and the
|
||||
second argument is the reference case. The arguemnts should be
|
||||
the basenames of the simulation, with an optional path
|
||||
prefix - an extension is accepted, but will be ignored.
|
||||
|
||||
The constructor will start be calling the method initCheck()
|
||||
to check that the two cases are 'in the same ballpark'.
|
||||
"""
|
||||
self.test_case = EclCase( test_case )
|
||||
self.ref_case = EclCase( ref_case )
|
||||
|
||||
self.initCheck( )
|
||||
|
||||
|
||||
def initCheck(self):
|
||||
"""A crude initial check to verify that the cases can be meaningfully
|
||||
compared.
|
||||
"""
|
||||
if not self.test_case.startTimeEqual( self.ref_case ):
|
||||
raise ValueError("The two cases do not start at the same time - can not be compared")
|
||||
|
||||
|
||||
def hasSummaryVector(self , key):
|
||||
"""
|
||||
Will check if both test and refernce have @key.
|
||||
"""
|
||||
return (key in self.test_case , key in self.ref_case)
|
||||
|
||||
|
||||
def endTimeEqual(self):
|
||||
"""
|
||||
Will check that ref_case and test_case are equally long.
|
||||
"""
|
||||
return self.test_case.endTimeEqual( self.ref_case )
|
||||
|
||||
|
||||
|
||||
def cmpSummaryVector(self , key , sample = 100):
|
||||
"""Will compare the summary vectors according to @key.
|
||||
|
||||
The comparison is based on evaluating the integrals:
|
||||
|
||||
I0 = \int R(t) dt
|
||||
|
||||
delta = \int | R(t) - T(t)| dt
|
||||
|
||||
numericall. R(t) is the reference solution and T(t) is
|
||||
testcase solution. The return value is a tuple:
|
||||
|
||||
(delta , I0)
|
||||
|
||||
So that a natural way to evaluate the check for numerical
|
||||
equality, based on the relative error could be:
|
||||
|
||||
delta , scale = ecl_cmp.cmpSummaryVector( "WWCT:OP_1" )
|
||||
|
||||
if delta/scale < 0.0001:
|
||||
print("Equal enough")
|
||||
else:
|
||||
print("Different ..")
|
||||
|
||||
The upper limit for the integrals is:
|
||||
|
||||
max( length(ref_case) , length(test_case))
|
||||
|
||||
meaning that two simulations which don't have the same
|
||||
end-time will compare as equal if they compare equal in the
|
||||
common part. If that is not OK you should call the
|
||||
endTimeEqual( ) method independently.
|
||||
"""
|
||||
return self.test_case.cmpSummaryVector( self.ref_case , key , sample = sample )
|
||||
|
||||
|
||||
def testKeys(self):
|
||||
"""
|
||||
Will return a list of summary keys in the test case.
|
||||
"""
|
||||
return self.test_case.keys()
|
||||
|
||||
|
||||
def testWells(self):
|
||||
"""
|
||||
Will return a list of wells keys in the test case.
|
||||
"""
|
||||
return self.test_case.wells()
|
||||
|
||||
+808
@@ -0,0 +1,808 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_file.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""
|
||||
The ecl_file module contains functionality to load a an ECLIPSE file
|
||||
in 'restart format'. Files of 'restart format' include restart files,
|
||||
init files, grid files, summary files and RFT files.
|
||||
|
||||
The ecl_file implementation is agnostic[1] to the content and
|
||||
structure of the file; more specialized classes like EclSum and
|
||||
EclGrid use the EclFile functionality for low level file loading.
|
||||
|
||||
The typical usage involves loading a complete file, and then
|
||||
subsequently querying for various keywords. In the example below we
|
||||
load a restart file, and ask for the SWAT keyword:
|
||||
|
||||
file = EclFile( "ECLIPSE.X0067" )
|
||||
swat_kw = file.iget_named_kw( "SWAT" , 0 )
|
||||
|
||||
The ecl_file module is a thin wrapper around the ecl_file.c
|
||||
implementation from the libecl library.
|
||||
|
||||
[1]: In particular for restart files, which do not have a special
|
||||
RestartFile class, there is some specialized functionality.
|
||||
"""
|
||||
import re
|
||||
import types
|
||||
import datetime
|
||||
import ctypes
|
||||
import warnings
|
||||
from cwrap import BaseCClass
|
||||
from ert.ecl import EclPrototype, EclKW, EclFileEnum
|
||||
from ert.util import CTime
|
||||
|
||||
|
||||
class EclFile(BaseCClass):
|
||||
TYPE_NAME = "ecl_file"
|
||||
_open = EclPrototype("void* ecl_file_open( char* , int )" , bind = False)
|
||||
_new = EclPrototype("void* ecl_file_alloc_empty( )" , bind = False)
|
||||
_get_file_type = EclPrototype("ecl_file_enum ecl_util_get_file_type( char* , bool* , int*)" , bind = False)
|
||||
_writable = EclPrototype("bool ecl_file_writable( ecl_file )")
|
||||
_save_kw = EclPrototype("void ecl_file_save_kw( ecl_file , ecl_kw )")
|
||||
_select_block = EclPrototype("bool ecl_file_select_block( ecl_file , char* , int )")
|
||||
_restart_block_time = EclPrototype("bool ecl_file_select_rstblock_sim_time( ecl_file , time_t )")
|
||||
_restart_block_step = EclPrototype("bool ecl_file_select_rstblock_report_step( ecl_file , int )")
|
||||
_restart_block_iselect = EclPrototype("bool ecl_file_iselect_rstblock( ecl_file , int )")
|
||||
_select_global = EclPrototype("void ecl_file_select_global( ecl_file )")
|
||||
_iget_kw = EclPrototype("ecl_kw_ref ecl_file_iget_kw( ecl_file , int)")
|
||||
_iget_named_kw = EclPrototype("ecl_kw_ref ecl_file_iget_named_kw( ecl_file , char* , int)")
|
||||
_close = EclPrototype("void ecl_file_close( ecl_file )")
|
||||
_get_size = EclPrototype("int ecl_file_get_size( ecl_file )")
|
||||
_get_unique_size = EclPrototype("int ecl_file_get_num_distinct_kw( ecl_file )")
|
||||
_get_num_named_kw = EclPrototype("int ecl_file_get_num_named_kw( ecl_file , char* )")
|
||||
_iget_restart_time = EclPrototype("time_t ecl_file_iget_restart_sim_date( ecl_file , int )")
|
||||
_iget_restart_days = EclPrototype("double ecl_file_iget_restart_sim_days( ecl_file , int )")
|
||||
_get_restart_index = EclPrototype("int ecl_file_get_restart_index( ecl_file , time_t)")
|
||||
_get_src_file = EclPrototype("char* ecl_file_get_src_file( ecl_file )")
|
||||
_replace_kw = EclPrototype("void ecl_file_replace_kw( ecl_file , ecl_kw , ecl_kw , bool)")
|
||||
_fwrite = EclPrototype("void ecl_file_fwrite_fortio( ecl_file , fortio , int)")
|
||||
_has_instance = EclPrototype("bool ecl_file_has_kw_ptr(ecl_file , ecl_kw)")
|
||||
_has_report_step = EclPrototype("bool ecl_file_has_report_step( ecl_file , int)")
|
||||
_has_sim_time = EclPrototype("bool ecl_file_has_sim_time( ecl_file , time_t )")
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def getFileType(filename):
|
||||
fmt_file = ctypes.c_bool()
|
||||
report_step = ctypes.c_int()
|
||||
|
||||
file_type = EclFile._get_file_type( filename , ctypes.byref( fmt_file ) , ctypes.byref(report_step))
|
||||
if file_type in [EclFileEnum.ECL_RESTART_FILE , EclFileEnum.ECL_SUMMARY_FILE]:
|
||||
report_step = report_step.value
|
||||
else:
|
||||
report_step = None
|
||||
|
||||
if file_type in [EclFileEnum.ECL_OTHER_FILE , EclFileEnum.ECL_DATA_FILE]:
|
||||
fmt_file = None
|
||||
else:
|
||||
fmt_file = fmt_file.value
|
||||
|
||||
|
||||
return (file_type , report_step , fmt_file )
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def restart_block( cls , filename , dtime = None , report_step = None):
|
||||
"""
|
||||
Load one report step from unified restart file.
|
||||
|
||||
Unified restart files can be prohibitively large; with this
|
||||
class method it is possible to load only one report
|
||||
step. Which report step you are interested in must be
|
||||
specified with either one of the optional arguments
|
||||
@report_step or @dtime. If present @dtime should be a normal
|
||||
python datetime instance:
|
||||
|
||||
block1 = EclFile.restart_block( "ECLIPSE.UNRST" , dtime = datetime.datetime( year , month , day ))
|
||||
block2 = EclFile.restart_block( "ECLIPSE.UNRST" , report_step = 67 )
|
||||
|
||||
If the block you are asking for can not be found the method
|
||||
will return None.
|
||||
"""
|
||||
obj = EclFile( filename )
|
||||
|
||||
if dtime:
|
||||
OK = obj._restart_block_time( CTime( dtime ))
|
||||
elif not report_step is None:
|
||||
OK = obj._restart_block_step( report_step )
|
||||
else:
|
||||
raise TypeError("restart_block() requires either dtime or report_step argument - none given.")
|
||||
|
||||
if not OK:
|
||||
if dtime:
|
||||
raise ValueError('Could not locate date:%02d/%02d/%4d in restart file "%s".' % (dtime.day , dtime.month , dtime.year , filename))
|
||||
else:
|
||||
raise ValueError('Could not locate report step:%d in restart file: "%s".' % (report_step , filename))
|
||||
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
@classmethod
|
||||
def contains_report_step( cls , filename , report_step ):
|
||||
"""
|
||||
Will check if the @filename contains @report_step.
|
||||
|
||||
This classmethod works by opening the file @filename and
|
||||
searching through linearly to see if an ecl_kw with value
|
||||
corresponding to @report_step can be found. Since this is a
|
||||
classmethod it is invoked like this:
|
||||
|
||||
import ert.ecl.ecl as ecl
|
||||
....
|
||||
if ecl.EclFile.contains_report_step("ECLIPSE.UNRST" , 20):
|
||||
print "OK - file contains report step 20"
|
||||
else:
|
||||
print "File does not contain report step 20"
|
||||
|
||||
If you have already loaded the file into an EclFile instance
|
||||
you should use the has_report_step() method instead.
|
||||
"""
|
||||
obj = EclFile( filename )
|
||||
return obj.has_report_step( report_step )
|
||||
|
||||
|
||||
@classmethod
|
||||
def contains_sim_time( cls , filename , dtime ):
|
||||
"""
|
||||
Will check if the @filename contains simulation at @dtime.
|
||||
|
||||
This classmethod works by opening the file @filename and
|
||||
searching through linearly to see if a result block at the
|
||||
time corresponding to @dtime can be found. Since this is a
|
||||
classmethod it is invoked like this:
|
||||
|
||||
import ert.ecl.ecl as ecl
|
||||
....
|
||||
if ecl.EclFile.contains_sim_time("ECLIPSE.UNRST" , datetime.datetime( 2007 , 10 , 10) ):
|
||||
print "OK - file contains 10th of October 2007"
|
||||
else:
|
||||
print "File does not contain 10th of October 2007"
|
||||
|
||||
If you have already loaded the file into an EclFile instance
|
||||
you should use the has_sim_time() method instead.
|
||||
"""
|
||||
obj = EclFile( filename )
|
||||
return obj.has_sim_time( dtime )
|
||||
|
||||
@property
|
||||
def report_list(self):
|
||||
report_steps = []
|
||||
try:
|
||||
seqnum_list = self["SEQNUM"]
|
||||
for s in seqnum_list:
|
||||
report_steps.append( s[0] )
|
||||
except KeyError:
|
||||
# OK - we did not have seqnum; that might be because this
|
||||
# a non-unified restart file; or because this is not a
|
||||
# restart file at all.
|
||||
fname = self.getFilename( )
|
||||
matchObj = re.search("\.[XF](\d{4})$" , fname)
|
||||
if matchObj:
|
||||
report_steps.append( int(matchObj.group(1)) )
|
||||
else:
|
||||
raise TypeError('Tried get list of report steps from file "%s" - which is not a restart file' % fname)
|
||||
|
||||
|
||||
return report_steps
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def file_report_list( cls , filename ):
|
||||
"""
|
||||
Will identify the available report_steps from @filename.
|
||||
"""
|
||||
|
||||
file = EclFile( filename )
|
||||
return file.report_list
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return "EclFile: %s" % self.getFilename( )
|
||||
|
||||
|
||||
def __init__( self , filename , flags = 0):
|
||||
"""
|
||||
Loads the complete file @filename.
|
||||
|
||||
Will create a new EclFile instance with the content of file
|
||||
@filename. The file @filename must be in 'restart format' -
|
||||
otherwise it will be crash and burn.
|
||||
|
||||
The optional argument flags can be an or'ed combination of the
|
||||
flags:
|
||||
|
||||
ecl.ECL_FILE_WRITABLE : It is possible to update the
|
||||
content of the keywords in the file.
|
||||
|
||||
ecl.ECL_FILE_CLOSE_STREAM : The underlying FILE * is closed
|
||||
when not used; to save number of open file descriptors
|
||||
in cases where a high number of EclFile instances are
|
||||
open concurrently.
|
||||
|
||||
When the file has been loaded the EclFile instance can be used
|
||||
to query for and get reference to the EclKW instances
|
||||
constituting the file, like e.g. SWAT from a restart file or
|
||||
FIPNUM from an INIT file.
|
||||
"""
|
||||
c_ptr = self._open( filename , flags )
|
||||
if c_ptr is None:
|
||||
raise IOError('Failed to open file "%s"' % filename)
|
||||
else:
|
||||
super(EclFile , self).__init__(c_ptr)
|
||||
|
||||
|
||||
|
||||
def save_kw( self , kw ):
|
||||
"""
|
||||
Will write the @kw back to file.
|
||||
|
||||
This function should typically be used in situations like this:
|
||||
|
||||
1. Create an EclFile instance around an ECLIPSE output file.
|
||||
2. Extract a keyword of interest and modify it.
|
||||
3. Call this method to save the modifications to disk.
|
||||
|
||||
There are several restrictions to the use of this function:
|
||||
|
||||
1. The EclFile instance must have been created with the
|
||||
optional read_only flag set to False.
|
||||
|
||||
2. You can only modify the content of the keyword; if you
|
||||
try to modify the header in any way (i.e. size, datatype
|
||||
or name) the function will fail.
|
||||
|
||||
3. The keyword you are trying to save must be exactly the
|
||||
keyword you got from this EclFile instance, otherwise the
|
||||
function will fail.
|
||||
"""
|
||||
if self._writable( ):
|
||||
self._save_kw( kw )
|
||||
else:
|
||||
raise IOError('save_kw: the file "%s" has been opened read only.' % self.getFilename( ))
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return self._get_size( )
|
||||
|
||||
|
||||
def close(self):
|
||||
if self:
|
||||
self._close( )
|
||||
self._invalidateCPointer( )
|
||||
|
||||
|
||||
def free(self):
|
||||
self.close()
|
||||
|
||||
|
||||
def select_block( self, kw , kw_index):
|
||||
OK = self._select_block( kw , kw_index )
|
||||
if not OK:
|
||||
raise ValueError('Could not find block "%s" (%d)' % (kw , kw_index))
|
||||
|
||||
|
||||
def select_global( self ):
|
||||
self._select_global( )
|
||||
|
||||
|
||||
def select_restart_section( self, index = None , report_step = None , sim_time = None):
|
||||
"""
|
||||
Will select a restart section as the active section.
|
||||
|
||||
You must specify a report step with the @report_step argument,
|
||||
a true time with the @sim_time argument or a plain index to
|
||||
select restart block. If none of arguments are given exception
|
||||
TypeError will be raised. If present the @sim_time argument
|
||||
should be a datetime instance.
|
||||
|
||||
If the restart section you ask for can not be found the method
|
||||
will raise a ValueError exeception. To protect against this
|
||||
you can query first with the has_report_step(),
|
||||
has_sim_time() or num_report_steps() methods.
|
||||
|
||||
This method should be used when you have already loaded the
|
||||
complete file; if you only want to load a section from the
|
||||
file you can use the classmethod restart_block().
|
||||
|
||||
The method will return 'self' which can be used to aid
|
||||
readability.
|
||||
"""
|
||||
|
||||
OK = False
|
||||
if not report_step is None:
|
||||
OK = self._restart_block_step( report_step )
|
||||
elif not sim_time is None:
|
||||
OK = self._restart_block_time( CTime( sim_time ) )
|
||||
elif not index is None:
|
||||
OK = self._restart_block_iselect( index )
|
||||
else:
|
||||
raise TypeError("select_restart_section() requires either dtime or report_step argument - none given")
|
||||
|
||||
|
||||
if not OK:
|
||||
raise TypeError("select_restart_section() Could not locate report_step/dtime")
|
||||
return self
|
||||
|
||||
|
||||
def select_last_restart( self ):
|
||||
"""
|
||||
Will select the last SEQNUM block in restart file.
|
||||
|
||||
Works by searching for the last SEQNUM keyword; the SEQNUM
|
||||
Keywords are only present in unified restart files. If this
|
||||
is a non-unified restart file (or not a restart file at all),
|
||||
the method will do nothing and return False.
|
||||
"""
|
||||
if "SEQNUM" in self:
|
||||
self.select_restart_section( index = self.num_report_steps() - 1)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def __iget(self , index):
|
||||
return self._iget_kw( index ).setParent( parent = self )
|
||||
|
||||
|
||||
def __iget_named(self, kw_name , index):
|
||||
return self._iget_named_kw( kw_name , index ).setParent( parent = self )
|
||||
|
||||
|
||||
|
||||
def __getitem__(self , index):
|
||||
"""
|
||||
Implements [] operator; index can be integer or key.
|
||||
|
||||
Will look up EclKW instances from the current EclFile
|
||||
instance. The @index argument can either be an integer, in
|
||||
which case the method will return EclKW number @index, or
|
||||
alternatively a keyword string, in which case the method will
|
||||
return a list of EclKW instances with that keyword:
|
||||
|
||||
restart_file = ecl_file.EclFile("ECLIPSE.UNRST")
|
||||
kw9 = restart_file[9]
|
||||
swat_list = restart_file["SWAT"]
|
||||
|
||||
The keyword based lookup can be combined with an extra [] to
|
||||
get EclKW instance nr:
|
||||
|
||||
swat9 = restart_file["SWAT"][9]
|
||||
|
||||
Will return the 10'th SWAT keyword from the restart file. The
|
||||
following example will iterate over all the SWAT keywords in a
|
||||
restart file:
|
||||
|
||||
restart_file = ecl_file.EclFile("ECLIPSE.UNRST")
|
||||
for swat in restart_file["SWAT"]:
|
||||
....
|
||||
"""
|
||||
|
||||
if isinstance( index , types.IntType):
|
||||
if index < 0 or index >= len(self):
|
||||
raise IndexError
|
||||
else:
|
||||
kw = self.__iget( index )
|
||||
return kw
|
||||
|
||||
if isinstance( index , slice ):
|
||||
indices = index.indices( len(self) )
|
||||
kw_list = []
|
||||
for i in range(*indices):
|
||||
kw_list.append( self[i] )
|
||||
return kw_list
|
||||
else:
|
||||
if isinstance( index , types.StringType):
|
||||
if self.has_kw( index ):
|
||||
kw_index = index
|
||||
kw_list = []
|
||||
for index in range( self.num_named_kw( kw_index )):
|
||||
kw_list.append( self.iget_named_kw( kw_index , index))
|
||||
return kw_list
|
||||
else:
|
||||
raise KeyError('Unrecognized keyword "%s".' % index)
|
||||
else:
|
||||
raise TypeError("Index must be integer or string (keyword)")
|
||||
|
||||
|
||||
|
||||
def iget_kw( self , index , copy = False):
|
||||
"""
|
||||
Will return EclKW instance nr @index.
|
||||
|
||||
In the files loaded with the EclFile implementation the
|
||||
ECLIPSE keywords come sequentially in a long series, an INIT
|
||||
file might have the following keywords:
|
||||
|
||||
INTEHEAD
|
||||
LOGIHEAD
|
||||
DOUBHEAD
|
||||
PORV
|
||||
DX
|
||||
DY
|
||||
DZ
|
||||
PERMX
|
||||
PERMY
|
||||
PERMZ
|
||||
MULTX
|
||||
MULTY
|
||||
.....
|
||||
|
||||
The iget_kw() method will give you a EclKW reference to
|
||||
keyword nr @index. This functionality is also available
|
||||
through the index operator []:
|
||||
|
||||
file = EclFile( "ECLIPSE.INIT" )
|
||||
permx = file.iget_kw( 7 )
|
||||
permz = file[ 9 ]
|
||||
|
||||
Observe that the returned EclKW instance is only a reference
|
||||
to the data owned by the EclFile instance.
|
||||
|
||||
The method iget_named_kw() which lets you specify the name of
|
||||
the keyword you are interested in is in general more useful
|
||||
than this method.
|
||||
"""
|
||||
kw = self[index]
|
||||
if copy:
|
||||
return EclKW.copy( kw )
|
||||
else:
|
||||
return kw
|
||||
|
||||
|
||||
def iget_named_kw( self , kw_name , index , copy = False):
|
||||
"""
|
||||
Will return EclKW nr @index reference with header @kw_name.
|
||||
|
||||
The keywords in a an ECLIPSE file are organized in a long
|
||||
linear array; keywords with the same name can occur many
|
||||
times. For instance a summary data[1] file might look like
|
||||
this:
|
||||
|
||||
SEQHDR
|
||||
MINISTEP
|
||||
PARAMS
|
||||
MINISTEP
|
||||
PARAMS
|
||||
MINISTEP
|
||||
PARAMS
|
||||
....
|
||||
|
||||
To get the third 'PARAMS' keyword you can use the method call:
|
||||
|
||||
params_kw = file.iget_named_kw( "PARAMS" , 2 )
|
||||
|
||||
The functionality of the iget_named_kw() method is also
|
||||
available through the __getitem__() method as:
|
||||
|
||||
params_kw = file["PARAMS"][2]
|
||||
|
||||
Observe that the returned EclKW instance is only a reference
|
||||
to the data owned by the EclFile instance.
|
||||
|
||||
Observe that syntactically this is equivalent to
|
||||
file[kw_name][index], however the latter form will imply that
|
||||
all the keywords of this type are loaded from the file. If you
|
||||
know that only a few will actually be used it will be faster
|
||||
to use this method.
|
||||
|
||||
[1]: For working with summary data you are probably better off
|
||||
using the EclSum class.
|
||||
"""
|
||||
if index < self.num_named_kw( kw_name ):
|
||||
kw = self.__iget_named( kw_name , index )
|
||||
if copy:
|
||||
return EclKW.copy( kw )
|
||||
else:
|
||||
return kw
|
||||
else:
|
||||
raise KeyError('Asked for occurence:%d of %d of keyword "%s".'
|
||||
% (index , self.num_named_kw( kw_name ), kw_name))
|
||||
|
||||
|
||||
def restart_get_kw( self , kw_name , dtime , copy = False):
|
||||
"""Will return EclKW @kw_name from restart file at time @dtime.
|
||||
|
||||
This function assumes that the current EclFile instance
|
||||
represents a restart file. It will then look for keyword
|
||||
@kw_name exactly at the time @dtime; @dtime is a datetime
|
||||
instance:
|
||||
|
||||
file = EclFile( "ECLIPSE.UNRST" )
|
||||
swat2010 = file.restart_get_kw( "SWAT" , datetime.datetime( 2000 , 1 , 1 ))
|
||||
|
||||
By default the returned kw instance is a reference to the
|
||||
ecl_kw still contained in the EclFile instance; i.e. the kw
|
||||
will become a dangling reference if the EclFile instance goes
|
||||
out of scope. If the optional argument @copy is True the
|
||||
returned kw will be a true copy.
|
||||
|
||||
If the file does not have the keyword at the specified time
|
||||
the function will raise IndexError(); if the file does not
|
||||
have the keyword at all - KeyError will be raised.
|
||||
"""
|
||||
index = self._get_restart_index( CTime( dtime ) )
|
||||
if index >= 0:
|
||||
if self.num_named_kw(kw_name) > index:
|
||||
kw = self.iget_named_kw( kw_name , index )
|
||||
if copy:
|
||||
return EclKW.copy( kw )
|
||||
else:
|
||||
return kw
|
||||
else:
|
||||
if self.has_kw(kw_name):
|
||||
raise IndexError('Does not have keyword "%s" at time:%s.' % (kw_name , dtime))
|
||||
else:
|
||||
raise KeyError('Keyword "%s" not recognized.' % kw_name)
|
||||
else:
|
||||
raise IndexError('Does not have keyword "%s" at time:%s.' % (kw_name , dtime))
|
||||
|
||||
|
||||
def replace_kw( self , old_kw , new_kw):
|
||||
"""
|
||||
Will replace @old_kw with @new_kw in current EclFile instance.
|
||||
|
||||
This method can be used to replace one of the EclKW instances
|
||||
in the current EclFile. The @old_kw reference must be to the
|
||||
actual EclKW instance in the current EclFile instance (the
|
||||
final comparison is based on C pointer equality!), i.e. it
|
||||
must be a reference (not a copy) from one of the ??get_kw??
|
||||
methods of the EclFile class. In the example below we replace
|
||||
the SWAT keyword from a restart file:
|
||||
|
||||
swat = file.iget_named_kw( "SWAT" , 0 )
|
||||
new_swat = swat * 0.25
|
||||
file.replace_kw( swat , new_swat )
|
||||
|
||||
|
||||
The C-level ecl_file_type structure takes full ownership of
|
||||
all installed ecl_kw instances; mixing the garbage collector
|
||||
into it means that this is quite low level - and potentially
|
||||
dangerous!
|
||||
"""
|
||||
|
||||
# We ensure that this scope owns the new_kw instance; the
|
||||
# new_kw will be handed over to the ecl_file instance, and we
|
||||
# can not give away something we do not alreeady own.
|
||||
if not new_kw.data_owner:
|
||||
new_kw = EclKW.copy( new_kw )
|
||||
|
||||
# The ecl_file instance will take responsability for freeing
|
||||
# this ecl_kw instance.
|
||||
new_kw.data_owner = False
|
||||
self._replace_kw( old_kw , new_kw , False )
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
"""
|
||||
The number of keywords in the current EclFile object.
|
||||
"""
|
||||
return len(self)
|
||||
|
||||
@property
|
||||
def unique_size( self ):
|
||||
"""
|
||||
The number of unique keyword (names) in the current EclFile object.
|
||||
"""
|
||||
return self._get_unique_size( )
|
||||
|
||||
|
||||
def keys(self):
|
||||
"""
|
||||
Will return a list of unique kw names - like keys() on a dict.
|
||||
"""
|
||||
header_dict = {}
|
||||
for index in range(len(self)):
|
||||
kw = self[index]
|
||||
header_dict[ kw.name ] = True
|
||||
return header_dict.keys()
|
||||
|
||||
|
||||
@property
|
||||
def headers(self):
|
||||
"""
|
||||
Will return a list of the headers of all the keywords.
|
||||
"""
|
||||
header_list = []
|
||||
for index in range(self.size):
|
||||
kw = self[index]
|
||||
header_list.append( kw.header )
|
||||
return header_list
|
||||
|
||||
@property
|
||||
def report_steps( self ):
|
||||
"""
|
||||
Will return a list of all report steps.
|
||||
|
||||
The method works by iterating through the whole restart file
|
||||
looking for 'SEQNUM' keywords; if the current EclFile instance
|
||||
is not a restart file it will not contain any 'SEQNUM'
|
||||
keywords and the method will simply return an empty list.
|
||||
"""
|
||||
steps = []
|
||||
seqnum_list = self["SEQNUM"]
|
||||
for kw in self["SEQNUM"]:
|
||||
steps.append( kw[0] )
|
||||
return steps
|
||||
|
||||
@property
|
||||
def report_dates( self ):
|
||||
"""
|
||||
Will return a list of the dates for all report steps.
|
||||
|
||||
The method works by iterating through the whole restart file
|
||||
looking for 'SEQNUM/INTEHEAD' keywords; the method can
|
||||
probably be tricked by other file types also containing an
|
||||
INTEHEAD keyword.
|
||||
"""
|
||||
dates = []
|
||||
if self.has_kw('SEQNUM'):
|
||||
for index in range( self.num_named_kw( 'SEQNUM' )):
|
||||
dates.append( self.iget_restart_sim_time( index ))
|
||||
else:
|
||||
# This is a uber-hack; should export the ecl_rsthead
|
||||
# object as ctypes structure.
|
||||
intehead = self["INTEHEAD"][0]
|
||||
year = intehead[66]
|
||||
month = intehead[65]
|
||||
day = intehead[64]
|
||||
date = datetime.datetime( year , month , day )
|
||||
dates = [ date ]
|
||||
return dates
|
||||
|
||||
|
||||
@property
|
||||
def dates( self ):
|
||||
"""
|
||||
Will return a list of the dates for all report steps.
|
||||
"""
|
||||
return self.report_dates
|
||||
|
||||
|
||||
def num_named_kw( self , kw):
|
||||
"""
|
||||
The number of keywords with name == @kw in the current EclFile object.
|
||||
"""
|
||||
return self._get_num_named_kw( kw )
|
||||
|
||||
def has_kw( self , kw , num = 0):
|
||||
"""
|
||||
Check if current EclFile instance has a keyword @kw.
|
||||
|
||||
If the optional argument @num is given it will check if the
|
||||
EclFile has at least @num occurences of @kw.
|
||||
"""
|
||||
num_named_kw = self.num_named_kw( kw )
|
||||
if num_named_kw > num:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __contains__(self , kw):
|
||||
"""
|
||||
Check if the current file contains keyword @kw.
|
||||
"""
|
||||
return self.has_kw( kw )
|
||||
|
||||
def has_report_step( self , report_step ):
|
||||
"""
|
||||
Checks if the current EclFile has report step @report_step.
|
||||
|
||||
If the EclFile in question is not a restart file, you will
|
||||
just get False. If you want to check if the file contains the
|
||||
actual report_step before loading the file, you should use the
|
||||
classmethod contains_report_step() instead.
|
||||
"""
|
||||
return self._has_report_step( report_step )
|
||||
|
||||
def num_report_steps( self ):
|
||||
"""
|
||||
Returns the total number of report steps in the restart file.
|
||||
|
||||
Works by counting the number of 'SEQNUM' instances, and will
|
||||
happily return 0 for a non-restart file. Observe that the
|
||||
report_steps present in a unified restart file are in general
|
||||
not consecutive, i.e. the last report step will typically be
|
||||
much higher than the return value from this function.
|
||||
"""
|
||||
return len( self["SEQNUM"] )
|
||||
|
||||
|
||||
|
||||
def has_sim_time( self , dtime ):
|
||||
"""
|
||||
Checks if the current EclFile has data for time @dtime.
|
||||
|
||||
The implementation goes through all the INTEHEAD headers in
|
||||
the EclFile, i.e. it can be fooled (and probably crash and
|
||||
burn) if the EclFile instance in question is has INTEHEAD
|
||||
keyword(s), but is still not a restart file. The @dtime
|
||||
argument should be a normal python datetime instance.
|
||||
"""
|
||||
return self._has_sim_time( CTime(dtime) )
|
||||
|
||||
|
||||
def iget_restart_sim_time( self , index ):
|
||||
"""
|
||||
Will locate restart block nr @index and return the true time
|
||||
as a datetime instance.
|
||||
"""
|
||||
ct = CTime(self._iget_restart_time( index ))
|
||||
return ct.datetime()
|
||||
|
||||
|
||||
def iget_restart_sim_days( self , index ):
|
||||
"""
|
||||
Will locate restart block nr @index and return the number of days
|
||||
(in METRIC at least ...) since the simulation started.
|
||||
|
||||
"""
|
||||
return self._iget_restart_days( index )
|
||||
|
||||
|
||||
def getFilename(self):
|
||||
"""
|
||||
Name of the file currently loaded.
|
||||
"""
|
||||
return self._get_src_file( )
|
||||
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
warnings.warn("The name property is deprecated - use getFilename( )" , DeprecationWarning)
|
||||
return self.getFilename()
|
||||
|
||||
|
||||
def fwrite( self , fortio ):
|
||||
"""
|
||||
Will write current EclFile instance to fortio stream.
|
||||
|
||||
ECLIPSE is written in Fortran; and a "special" handle for
|
||||
Fortran IO must be used when reading and writing these files.
|
||||
This method will write the current EclFile instance to a
|
||||
FortIO stream already opened for writing:
|
||||
|
||||
import ert.ecl.ecl as ecl
|
||||
...
|
||||
fortio = ecl.FortIO( "FILE.XX" )
|
||||
file.fwrite( fortio )
|
||||
fortio.close()
|
||||
|
||||
"""
|
||||
self._fwrite( fortio , 0 )
|
||||
|
||||
|
||||
class EclFileContextManager(object):
|
||||
|
||||
def __init__(self , ecl_file):
|
||||
self.__ecl_file = ecl_file
|
||||
|
||||
def __enter__(self):
|
||||
return self.__ecl_file
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.__ecl_file.close()
|
||||
return False
|
||||
|
||||
|
||||
def openEclFile( file_name , flags = 0):
|
||||
return EclFileContextManager( EclFile( file_name , flags ))
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_grav.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""
|
||||
Calculate dynamic change in gravitational strength.
|
||||
|
||||
The ecl_grav module contains functionality to load time-lapse ECLIPSE
|
||||
results and calculate the change in gravitational strength between the
|
||||
different surveys. The implementation is a thin wrapper around the
|
||||
ecl_grav.c implementation in the libecl library.
|
||||
"""
|
||||
from cwrap import BaseCClass
|
||||
from ert.ecl import EclPhaseEnum, EclPrototype
|
||||
|
||||
|
||||
class EclGrav(BaseCClass):
|
||||
"""
|
||||
Holding ECLIPSE results for calculating gravity changes.
|
||||
|
||||
The EclGrav class is a collection class holding the results from
|
||||
ECLIPSE forward modelling of gravity surveys. Observe that the
|
||||
class is focused on the ECLIPSE side of things, and does not have
|
||||
any notion of observed values or measurement locations; that
|
||||
should be handled by the scope using the EclGrav class.
|
||||
|
||||
Typical use of the EclGrav class involves the following steps:
|
||||
|
||||
1. Create the EclGrav instance.
|
||||
2. Add surveys with the add_survey_XXXX() methods.
|
||||
3. Evalute the gravitational response with the eval() method.
|
||||
"""
|
||||
TYPE_NAME = "ecl_grav"
|
||||
_grav_alloc = EclPrototype("void* ecl_grav_alloc( ecl_grid , ecl_file )" , bind = False)
|
||||
_free = EclPrototype("void ecl_grav_free( ecl_grav )")
|
||||
_add_survey_RPORV = EclPrototype("void* ecl_grav_add_survey_RPORV( ecl_grav , char* , ecl_file )")
|
||||
_add_survey_PORMOD = EclPrototype("void* ecl_grav_add_survey_PORMOD( ecl_grav , char* , ecl_file )")
|
||||
_add_survey_FIP = EclPrototype("void* ecl_grav_add_survey_FIP( ecl_grav , char* , ecl_file )")
|
||||
_add_survey_RFIP = EclPrototype("void* ecl_grav_add_survey_RFIP( ecl_grav , char* , ecl_file )")
|
||||
_new_std_density = EclPrototype("void ecl_grav_new_std_density( ecl_grav , int , double)")
|
||||
_add_std_density = EclPrototype("void ecl_grav_add_std_density( ecl_grav , int , int , double)")
|
||||
_eval = EclPrototype("double ecl_grav_eval( ecl_grav , char* , char* , ecl_region , double , double , double, int)")
|
||||
|
||||
|
||||
|
||||
|
||||
def __init__( self, grid, init_file ):
|
||||
"""
|
||||
Creates a new EclGrav instance.
|
||||
|
||||
The input arguments @grid and @init_file should be instances
|
||||
of EclGrid and EclFile respectively.
|
||||
"""
|
||||
self.init_file = init_file # Inhibit premature garbage collection of init_file
|
||||
|
||||
c_ptr = self._grav_alloc( grid , init_file )
|
||||
super(EclGrav , self).__init__( c_ptr )
|
||||
|
||||
|
||||
|
||||
def add_survey_RPORV( self, survey_name, restart_file ):
|
||||
"""
|
||||
Add new survey based on RPORV keyword.
|
||||
|
||||
Add a new survey; in this context a survey is the state of
|
||||
reservoir, i.e. an ECLIPSE restart file. The @survey_name
|
||||
input argument will be used when refering to this survey at a
|
||||
later stage. The @restart_file input argument should be an
|
||||
EclFile instance with data from one report step. A typical way
|
||||
to load the @restart_file argument is:
|
||||
|
||||
import datetime
|
||||
import ert.ecl.ecl as ecl
|
||||
...
|
||||
...
|
||||
date = datetime.datetime( year , month , day )
|
||||
restart_file1 = ecl.EclFile.restart_block( "ECLIPSE.UNRST" , dtime = date)
|
||||
restart_file2 = ecl.EclFile.restart_block( "ECLIPSE.UNRST" , report_step = 67 )
|
||||
|
||||
The pore volume of each cell will be calculated based on the
|
||||
RPORV keyword from the restart files. The methods
|
||||
add_survey_PORMOD() and add_survey_FIP() are alternatives
|
||||
which are based on other keywords.
|
||||
"""
|
||||
self._add_survey_RPORV(survey_name, restart_file)
|
||||
|
||||
def add_survey_PORMOD( self, survey_name, restart_file ):
|
||||
"""
|
||||
Add new survey based on PORMOD keyword.
|
||||
|
||||
The pore volum is calculated from the initial pore volume and
|
||||
the PORV_MOD keyword from the restart file; see
|
||||
add_survey_RPORV() for further details.
|
||||
"""
|
||||
self._add_survey_PORMOD(survey_name, restart_file)
|
||||
|
||||
def add_survey_FIP( self, survey_name, restart_file ):
|
||||
"""
|
||||
Add new survey based on FIP keywords.
|
||||
|
||||
This method adds a survey as add_survey_RPORV() and
|
||||
add_survey_PORMOD; but the mass content in each cell is
|
||||
calculated based on the FIPxxx keyword along with the mass
|
||||
density at standard conditions of the respective phases.
|
||||
|
||||
The mass density at standard conditions must be specified with
|
||||
the new_std_density() (and possibly also add_std_density())
|
||||
method before calling the add_survey_FIP() method.
|
||||
"""
|
||||
self._add_survey_FIP(survey_name, restart_file)
|
||||
|
||||
def add_survey_RFIP( self, survey_name, restart_file ):
|
||||
"""
|
||||
Add new survey based on RFIP keywords.
|
||||
|
||||
This method adds a survey as add_survey_RPORV() and
|
||||
add_survey_PORMOD; but the mass content in each cell is
|
||||
calculated based on the RFIPxxx keyword along with the
|
||||
per-cell mass density of the respective phases.
|
||||
"""
|
||||
self._add_survey_RFIP( survey_name, restart_file)
|
||||
|
||||
|
||||
def eval(self, base_survey, monitor_survey, pos, region=None,
|
||||
phase_mask=EclPhaseEnum.ECL_OIL_PHASE + EclPhaseEnum.ECL_GAS_PHASE + EclPhaseEnum.ECL_WATER_PHASE):
|
||||
"""
|
||||
Calculates the gravity change between two surveys.
|
||||
|
||||
This is the method everything is leading up to; will calculate
|
||||
the change in gravitational strength, in units of micro Gal,
|
||||
between the two surveys named @base_survey and
|
||||
@monitor_survey.
|
||||
|
||||
The monitor survey can be 'None' - the resulting answer has
|
||||
nothing whatsovever to do with gravitation, but can be
|
||||
interesting to determine the numerical size of the quantities
|
||||
which are subtracted in a 4D study.
|
||||
|
||||
The @pos argument should be a tuple of three elements with the
|
||||
(utm_x , utm_y , depth) position where we want to evaluate the
|
||||
change in gravitational strength.
|
||||
|
||||
If supplied the optional argument @region should be an
|
||||
EclRegion() instance; this region will be used to limit the
|
||||
part of the reserviour included in the gravity calculations.
|
||||
|
||||
The optional argument @phase_mask is an integer flag to
|
||||
indicate which phases you are interested in. It should be a
|
||||
sum of the relevant integer constants 'ECL_OIL_PHASE',
|
||||
'ECL_GAS_PHASE' and 'ECL_WATER_PHASE'.
|
||||
"""
|
||||
return self._eval(base_survey, monitor_survey, region, pos[0], pos[1], pos[2], phase_mask)
|
||||
|
||||
|
||||
def new_std_density( self, phase_enum, default_density):
|
||||
"""
|
||||
Adds a new phase with a corresponding density.
|
||||
|
||||
@phase_enum is one of the integer constants ECL_OIL_PHASE,
|
||||
ECL_GAS_PHASE or ECL_WATER_PHASE, all available in the
|
||||
ecl_util and also ecl modules.
|
||||
|
||||
@default_density is the density, at standard conditions, for
|
||||
this particular phase. By default @default_density will be
|
||||
used for all the cells in the model; by using the
|
||||
add_std_density() method you can specify different densities
|
||||
for different PVT regions.
|
||||
|
||||
The new_std_density() and add_std_density() methods must be
|
||||
used before you use the add_survey_FIP() method to add a
|
||||
survey based on the FIP keyword.
|
||||
"""
|
||||
self._new_std_density( phase_enum, default_density)
|
||||
|
||||
def add_std_density( self, phase_enum, pvtnum, density):
|
||||
"""
|
||||
Add standard conditions density for PVT region @pvtnum.
|
||||
|
||||
The new_std_density() method will add a standard conditions
|
||||
density which applies to all cells in the model. Using the
|
||||
add_std_density() method it is possible to add standard
|
||||
conditions densities on a per PVT region basis. You can add
|
||||
densities for as many PVT regions as you like, and then fall
|
||||
back to the default density for the others.
|
||||
|
||||
The new_std_density() method must be called before calling the
|
||||
add_std_density() method.
|
||||
|
||||
The new_std_density() and add_std_density() methods must be
|
||||
used before you use the add_survey_FIP() method to add a
|
||||
survey based on the FIP keyword.
|
||||
"""
|
||||
self._add_std_density(phase_enum, pvtnum, density)
|
||||
|
||||
|
||||
def free(self):
|
||||
self._free( )
|
||||
@@ -0,0 +1,84 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_grav.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from ert.ecl import EclPrototype
|
||||
_phase_deltag = EclPrototype("double ecl_grav_phase_deltag( double, double ,double , ecl_grid , ecl_file , ecl_kw , ecl_kw , ecl_kw , ecl_kw , ecl_kw , ecl_kw")
|
||||
|
||||
|
||||
|
||||
def phase_deltag( xyz, grid, init, sat1, rho1, porv1, sat2, rho2, porv2):
|
||||
|
||||
return _phase_deltag(xyz[0], xyz[1], xyz[2],
|
||||
grid.c_ptr, init.c_ptr,
|
||||
sat1.c_ptr, rho1.c_ptr, porv1.c_ptr,
|
||||
sat2.c_ptr, rho2.c_ptr, porv2.c_ptr)
|
||||
|
||||
|
||||
# 1. All restart files should have water, i.e. the SWAT keyword.
|
||||
# 2. All phases present in the restart file should also be present as densities,
|
||||
# in addition the model must contain one additional phase - which should have a density.
|
||||
# 3. The restart files can never contain oil saturation.
|
||||
|
||||
def deltag( xyz, grid, init_file, restart_file1, restart_file2):
|
||||
swat1 = restart_file1.iget_named_kw("SWAT", 0)
|
||||
swat2 = restart_file2.iget_named_kw("SWAT", 0)
|
||||
|
||||
phase_list = [( swat1, swat2)]
|
||||
|
||||
if restart_file1.has_kw("SGAS"):
|
||||
# This is a three phase Water / Gas / Oil system
|
||||
sgas1 = restart_file1.iget_named_kw("SGAS", 0)
|
||||
sgas2 = restart_file2.iget_named_kw("SGAS", 0)
|
||||
|
||||
soil1 = 1 - (sgas1 + swat1)
|
||||
soil2 = 1 - (sgas2 + swat2)
|
||||
soil1.name = "SOIL"
|
||||
soil2.name = "SOIL"
|
||||
phase_list += [(sgas1, sgas2),
|
||||
(soil1, soil2)]
|
||||
else:
|
||||
# This is a two phase Water / xxx System. Will look for
|
||||
# OIL_DEN and GAS_DEN keywords to determine whether it is a
|
||||
# Water / Oil or Water / Gas system.
|
||||
|
||||
if restart_file1.has_kw("OIL_DEN"):
|
||||
# Oil / Water system
|
||||
soil1 = 1 - swat1
|
||||
soil2 = 1 - swat2
|
||||
soil1.name = "SOIL"
|
||||
soil2.name = "SOIL"
|
||||
phase_list += [(soil1, soil2)]
|
||||
else:
|
||||
# Gas / Water system
|
||||
sgas1 = 1 - swat1
|
||||
sgas2 = 1 - swat2
|
||||
sgas1.name = "SGAS"
|
||||
sgas2.name = "SGAS"
|
||||
phase_list += [(sgas1, sgas2)]
|
||||
|
||||
porv1 = restart_file1.iget_named_kw("RPORV", 0)
|
||||
porv2 = restart_file2.iget_named_kw("RPORV", 0)
|
||||
|
||||
deltag = 0
|
||||
for (sat1, sat2) in phase_list:
|
||||
rho_name = "%s_DEN" % sat1.name[1:]
|
||||
rho1 = restart_file1.iget_named_kw(rho_name, 0)
|
||||
rho2 = restart_file2.iget_named_kw(rho_name, 0)
|
||||
deltag += phase_deltag(xyz, grid, init_file, sat1, rho1, porv1, sat2, rho2, porv2)
|
||||
return deltag
|
||||
|
||||
|
||||
|
||||
+1225
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,29 @@
|
||||
# Copyright (C) 2015 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_init_file.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from ert.ecl import EclFileEnum , EclFile, Ecl3DKW , Ecl3DFile
|
||||
|
||||
|
||||
class EclInitFile(Ecl3DFile):
|
||||
def __init__(self , grid , filename , flags = 0):
|
||||
file_type , report_step , fmt_file = EclFile.getFileType( filename )
|
||||
if file_type == EclFileEnum.ECL_INIT_FILE:
|
||||
super(EclInitFile , self).__init__( grid, filename , flags)
|
||||
else:
|
||||
raise ValueError("The input filename:%s does not correspond to a restart file - please follow the Eclipse naming conventions" % filename)
|
||||
|
||||
|
||||
|
||||
+1197
File diff suppressed because it is too large.
Load diff
+206
@@ -0,0 +1,206 @@
|
||||
# Copyright (C) 2013 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_npv.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import re
|
||||
import datetime
|
||||
import numbers
|
||||
from ert.ecl import EclSum
|
||||
|
||||
|
||||
class NPVParseKey(object):
|
||||
def __init__(self , eclNPV):
|
||||
self.baseCase = eclNPV.baseCase
|
||||
self.NPV = eclNPV
|
||||
|
||||
|
||||
def __call__(self , matchObject):
|
||||
key = matchObject.group(1)
|
||||
smspecNode = self.baseCase.smspec_node( key )
|
||||
if smspecNode.is_total:
|
||||
var = key.replace(":" , "_")
|
||||
self.NPV.addKey( key , var )
|
||||
return var + "[i]"
|
||||
else:
|
||||
raise ValueError("Sorry - the key: %s is not a total key - aborting" % key)
|
||||
|
||||
|
||||
class NPVPriceVector(object):
|
||||
def __init__(self, argList):
|
||||
self.dateList = []
|
||||
if isinstance(argList , list):
|
||||
for item in argList:
|
||||
if isinstance(item , tuple) and len(item) == 2:
|
||||
self.addItem(item)
|
||||
else:
|
||||
raise ValueError("Each element in list must be tuple with two elements")
|
||||
else:
|
||||
raise ValueError("Constructor argument must be list")
|
||||
|
||||
|
||||
|
||||
def addItem(self , item):
|
||||
dateItem = item[0]
|
||||
try:
|
||||
year = dateItem.year
|
||||
month = dateItem.month
|
||||
day = dateItem.day
|
||||
date = datetime.date( year , month , day )
|
||||
except AttributeError:
|
||||
try:
|
||||
tmp = re.split("[ .-/]" , dateItem)
|
||||
day = int(tmp[0])
|
||||
month = int(tmp[1])
|
||||
year = int(tmp[2])
|
||||
|
||||
date = datetime.date( year , month , day)
|
||||
except Exception:
|
||||
raise ValueError("First element was invalid date item")
|
||||
|
||||
|
||||
if len(self.dateList):
|
||||
prevItem = self.dateList[-1]
|
||||
if prevItem[0] >= date:
|
||||
raise ValueError("The dates must be in a strictly increasing order")
|
||||
|
||||
value = item[1]
|
||||
if isinstance(value , numbers.Real) or callable(value):
|
||||
self.dateList.append( (date , value) )
|
||||
else:
|
||||
raise ValueError("The value argument must be a scalar number or callable")
|
||||
|
||||
@classmethod
|
||||
def assertDate(cls , date):
|
||||
try:
|
||||
year = date.year
|
||||
month = date.month
|
||||
day = date.day
|
||||
date = datetime.date( year , month , day )
|
||||
|
||||
return date
|
||||
except AttributeError:
|
||||
return date.date()
|
||||
|
||||
|
||||
|
||||
def evalDate(self , dateItem , date):
|
||||
value = dateItem[1]
|
||||
if callable(value):
|
||||
td = date - dateItem[0]
|
||||
return value(td.days)
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
def eval(self , date):
|
||||
date = self.assertDate( date )
|
||||
startDate = self.dateList[0][0]
|
||||
if date >= startDate:
|
||||
endDate = self.dateList[-1][0]
|
||||
if date >= endDate:
|
||||
return self.evalDate( self.dateList[-1] , date)
|
||||
else:
|
||||
index1 = 0
|
||||
index2 = len(self.dateList) - 1
|
||||
while True:
|
||||
if (index2 - index1) == 1:
|
||||
index = index1
|
||||
break
|
||||
|
||||
index = (index1 + index2) >> 1
|
||||
item = self.dateList[index]
|
||||
if date >= item[0]:
|
||||
index1 = index
|
||||
else:
|
||||
index2 = index
|
||||
return self.evalDate( self.dateList[index] , date)
|
||||
else:
|
||||
raise ValueError("Input date:%s before start of vector" % date)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class EclNPV(object):
|
||||
sumKeyRE = re.compile("[[]([\w:,]+)[]]")
|
||||
|
||||
|
||||
def __init__(self , baseCase):
|
||||
sum = EclSum( baseCase )
|
||||
if sum:
|
||||
self.baseCase = sum
|
||||
else:
|
||||
raise Error("Failed to open ECLIPSE sumamry case:%s" % baseCase)
|
||||
self.expression = None
|
||||
self.keyList = {}
|
||||
self.start = None
|
||||
self.end = None
|
||||
self.interval = "1Y"
|
||||
|
||||
|
||||
def eval(self):
|
||||
if self.expression is None:
|
||||
raise ValueError("Can not eval with an expression to evaluate")
|
||||
pass
|
||||
|
||||
|
||||
def getExpression(self):
|
||||
return self.expression
|
||||
|
||||
|
||||
def setExpression(self , expression):
|
||||
self.compiled_expr = self.compile( expression )
|
||||
self.expression = expression
|
||||
|
||||
|
||||
def getKeyList(self):
|
||||
return self.keyList.keys()
|
||||
|
||||
|
||||
def addKey(self , key , var):
|
||||
self.keyList[key] = var
|
||||
|
||||
|
||||
def parseExpression(self , expression):
|
||||
self.keyList = {}
|
||||
if expression.count("[") != expression.count("]"):
|
||||
raise ValueError("Expression:%s invalid - not matching [ and ]" % expression)
|
||||
|
||||
replaceKey = NPVParseKey( self )
|
||||
parsedExpression = self.sumKeyRE.sub( replaceKey , expression )
|
||||
return parsedExpression
|
||||
|
||||
|
||||
def compile(self , expression):
|
||||
parsedExpression = self.parseExpression( expression )
|
||||
self.code = "trange = self.baseCase.timeRange( self.start , self.end , self.interval)\n"
|
||||
for (key,var) in self.keyList.items():
|
||||
self.code += "%s = self.baseCase.blockedProduction( \"%s\" , trange )\n" % (var , key)
|
||||
|
||||
self.code += "npv = 0\n"
|
||||
self.code += """
|
||||
for i in range(len(trange) - 1):
|
||||
npv += %s
|
||||
varDict[\"npv\"] = npv
|
||||
""" % parsedExpression
|
||||
|
||||
|
||||
|
||||
def evalNPV(self):
|
||||
byteCode = compile( self.code , "<string>" , 'exec')
|
||||
varDict = {}
|
||||
eval(byteCode)
|
||||
return varDict["npv"]
|
||||
|
||||
Whitespace-only changes.
+1057
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,135 @@
|
||||
# Copyright (C) 2015 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_restart_file.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from ert.util import CTime
|
||||
from ert.ecl import ECL_LIB , EclFile, Ecl3DKW , Ecl3DFile, EclFileEnum
|
||||
from cwrap import CWrapper, BaseCClass
|
||||
|
||||
class EclRestartHead(BaseCClass):
|
||||
def __init__(self , kw_arg = None , rst_arg = None):
|
||||
if kw_arg is None and rst_arg is None:
|
||||
raise Exception("Invalid arguments")
|
||||
|
||||
if not kw_arg is None:
|
||||
report_step , intehead_kw , doubhead_kw , logihead_kw = kw_arg
|
||||
c_ptr = EclRestartHead.cNamespace().alloc_from_kw( report_step , intehead_kw , doubhead_kw , logihead_kw )
|
||||
else:
|
||||
rst_file , occurence = rst_arg
|
||||
c_ptr = EclRestartHead.cNamespace().alloc( rst_file , occurence )
|
||||
|
||||
super(EclRestartHead, self).__init__(c_ptr)
|
||||
|
||||
|
||||
def free(self):
|
||||
EclRestartHead.cNamespace().free( self )
|
||||
|
||||
def getReportStep(self):
|
||||
return EclRestartHead.cNamespace().get_report_step( self )
|
||||
|
||||
def getSimDate(self):
|
||||
ct = CTime( EclRestartHead.cNamespace().get_sim_time( self ) )
|
||||
return ct.datetime( )
|
||||
|
||||
def getSimDays(self):
|
||||
return EclRestartHead.cNamespace().get_sim_days( self )
|
||||
|
||||
|
||||
|
||||
class EclRestartFile(Ecl3DFile):
|
||||
|
||||
def __init__(self , grid , filename , flags = 0):
|
||||
"""Will open an Eclipse restart file.
|
||||
|
||||
The EclRestartFile class will open an eclipse restart file, in
|
||||
unified or non unified format. The constructor will infer the
|
||||
file type based on the filename, and will raise a ValueError
|
||||
exception if the file type is not ECL_RESTART_FILE or
|
||||
ECL_UNIFIED_RESTART_FILE.
|
||||
|
||||
The EclRestartFile will use a grid reference to create Ecl3DKw
|
||||
instances for all the keyword elements which have either
|
||||
'nactive' or 'nx*ny*nz' elements.
|
||||
"""
|
||||
|
||||
file_type , report_step , fmt_file = EclFile.getFileType( filename )
|
||||
if not file_type in [EclFileEnum.ECL_RESTART_FILE, EclFileEnum.ECL_UNIFIED_RESTART_FILE]:
|
||||
raise ValueError("The input filename:%s does not correspond to a restart file - please follow the Eclipse naming conventions" % filename)
|
||||
|
||||
super(EclRestartFile , self).__init__( grid, filename , flags)
|
||||
self.rst_headers = None
|
||||
if file_type == EclFileEnum.ECL_RESTART_FILE:
|
||||
self.is_unified = False
|
||||
self.report_step = report_step
|
||||
else:
|
||||
self.is_unified = True
|
||||
|
||||
|
||||
|
||||
def unified(self):
|
||||
"""
|
||||
Will return True if the file we have opened is unified.
|
||||
"""
|
||||
return self.is_unified
|
||||
|
||||
|
||||
def assertHeaders(self):
|
||||
if self.rst_headers is None:
|
||||
self.rst_headers = []
|
||||
if self.unified():
|
||||
for index in range(self.num_named_kw("SEQNUM")):
|
||||
self.rst_headers.append( EclRestartHead( rst_arg = (self , index )))
|
||||
else:
|
||||
intehead_kw = self["INTEHEAD"][0]
|
||||
doubhead_kw = self["DOUBHEAD"][0]
|
||||
if "LOGIHEAD" in self:
|
||||
logihead_kw = self["LOGIHEAD"][0]
|
||||
else:
|
||||
logihead_kw = None
|
||||
|
||||
self.rst_headers.append( EclRestartHead( kw_arg = (self.report_step , intehead_kw , doubhead_kw , logihead_kw) ))
|
||||
|
||||
|
||||
def timeList(self):
|
||||
"""Will return a list of report_step, simulation time and days.
|
||||
|
||||
The return value will be a list tuples. For a unified restart
|
||||
file with the three report steps {10,15,20} it can look like:
|
||||
|
||||
[ (10, datetime.datetime( 2010 , 1 , 1 , 0 , 0 , 0 ) , 100.0),
|
||||
(15, datetime.datetime( 2010 , 3 , 1 , 0 , 0 , 0 ) , 160.0),
|
||||
(20, datetime.datetime( 2010 , 5 , 1 , 0 , 0 , 0 ) , 220.0) ]
|
||||
|
||||
For a non-unified restart file the list will have only one element.
|
||||
"""
|
||||
|
||||
self.assertHeaders()
|
||||
time_list = []
|
||||
for header in self.rst_headers:
|
||||
time_list.append( (header.getReportStep() , header.getSimDate( ) , header.getSimDays( )) )
|
||||
|
||||
return time_list
|
||||
|
||||
|
||||
|
||||
|
||||
CWrapper.registerObjectType("ecl_rsthead", EclRestartHead)
|
||||
cwrapper = CWrapper(ECL_LIB)
|
||||
EclRestartHead.cNamespace().alloc = cwrapper.prototype("c_void_p ecl_rsthead_ialloc(ecl_file , int )")
|
||||
EclRestartHead.cNamespace().alloc_from_kw = cwrapper.prototype("c_void_p ecl_rsthead_alloc_from_kw(int , ecl_kw , ecl_kw , ecl_kw )")
|
||||
EclRestartHead.cNamespace().free = cwrapper.prototype("void ecl_rsthead_free(ecl_rsthead)")
|
||||
EclRestartHead.cNamespace().get_report_step = cwrapper.prototype("int ecl_rsthead_get_report_step(ecl_rsthead)")
|
||||
EclRestartHead.cNamespace().get_sim_time = cwrapper.prototype("time_t ecl_rsthead_get_sim_time(ecl_rsthead)")
|
||||
EclRestartHead.cNamespace().get_sim_days = cwrapper.prototype("double ecl_rsthead_get_sim_days(ecl_rsthead)")
|
||||
+382
@@ -0,0 +1,382 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_rft.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""
|
||||
Module for loading ECLIPSE RFT files.
|
||||
"""
|
||||
|
||||
import types
|
||||
import warnings
|
||||
from cwrap import BaseCClass
|
||||
from ert.ecl import EclRFTCell, EclPLTCell, EclPrototype
|
||||
from ert.util import CTime
|
||||
|
||||
class EclRFT(BaseCClass):
|
||||
"""The EclRFT class contains the information for *one* RFT.
|
||||
|
||||
The ECLIPSE RFT file can contain three different types of RFT like
|
||||
objects which are lumped together; the EclRFTClass is a container
|
||||
for such objects. The three different object types which can be
|
||||
found in an RFT file are:
|
||||
|
||||
RFT: This is old-fashioned RFT which contains measurements of
|
||||
saturations for each of the completed cells.
|
||||
|
||||
PLT: This contains production and flow rates for each phase in
|
||||
each cell.
|
||||
|
||||
SEGMENT: Not implemented.
|
||||
|
||||
In addition to the measurements specific for RFT and PLT each cell
|
||||
has coordinates, pressure and depth.
|
||||
"""
|
||||
TYPE_NAME = "ecl_rft"
|
||||
_alloc = EclPrototype("void* ecl_rft_node_alloc_new( char* , char* , time_t , double)" , bind = False)
|
||||
_free = EclPrototype("void ecl_rft_node_free( ecl_rft )")
|
||||
_get_type = EclPrototype("int ecl_rft_node_get_type( ecl_rft )")
|
||||
_get_well = EclPrototype("char* ecl_rft_node_get_well_name( ecl_rft )")
|
||||
_get_date = EclPrototype("time_t ecl_rft_node_get_date( ecl_rft )")
|
||||
_get_size = EclPrototype("int ecl_rft_node_get_size( ecl_rft )")
|
||||
_iget_cell = EclPrototype("void* ecl_rft_node_iget_cell( ecl_rft )")
|
||||
_iget_cell_sorted = EclPrototype("void* ecl_rft_node_iget_cell_sorted( ecl_rft )")
|
||||
_sort_cells = EclPrototype("void* ecl_rft_node_inplace_sort_cells( ecl_rft )")
|
||||
_iget_depth = EclPrototype("double ecl_rft_node_iget_depth( ecl_rft )")
|
||||
_iget_pressure = EclPrototype("double ecl_rft_node_iget_pressure(ecl_rft)")
|
||||
_iget_ijk = EclPrototype("void ecl_rft_node_iget_ijk( ecl_rft , int , int*, int*, int*)")
|
||||
_iget_swat = EclPrototype("double ecl_rft_node_iget_swat(ecl_rft)")
|
||||
_iget_sgas = EclPrototype("double ecl_rft_node_iget_sgas(ecl_rft)")
|
||||
_iget_orat = EclPrototype("double ecl_rft_node_iget_orat(ecl_rft)")
|
||||
_iget_wrat = EclPrototype("double ecl_rft_node_iget_wrat(ecl_rft)")
|
||||
_iget_grat = EclPrototype("double ecl_rft_node_iget_grat(ecl_rft)")
|
||||
_lookup_ijk = EclPrototype("void* ecl_rft_node_lookup_ijk( ecl_rft , int , int , int)")
|
||||
_is_RFT = EclPrototype("bool ecl_rft_node_is_RFT( ecl_rft )")
|
||||
_is_PLT = EclPrototype("bool ecl_rft_node_is_PLT( ecl_rft )")
|
||||
_is_SEGMENT = EclPrototype("bool ecl_rft_node_is_SEGMENT( ecl_rft )")
|
||||
_is_MSW = EclPrototype("bool ecl_rft_node_is_MSW( ecl_rft )")
|
||||
|
||||
|
||||
def __init__(self , name , type_string , date , days):
|
||||
c_ptr = self._alloc( name , type_string , CTime( date ) , days )
|
||||
super(EclRFT , self).__init__( c_ptr )
|
||||
|
||||
|
||||
def free(self):
|
||||
self._free( )
|
||||
|
||||
def __len__(self):
|
||||
"""
|
||||
The number of completed cells in this RFT.
|
||||
"""
|
||||
return self._get_size( )
|
||||
|
||||
def is_RFT(self):
|
||||
"""
|
||||
Is instance an RFT; in that case all the cells will be EclRFTCell instances.
|
||||
"""
|
||||
return self._is_RFT( )
|
||||
|
||||
def is_PLT(self):
|
||||
"""
|
||||
Is instance a PLT; in that case all the cells will be EclPLTCell instances.
|
||||
"""
|
||||
return self._is_PLT( )
|
||||
|
||||
def is_SEGMENT(self):
|
||||
"""
|
||||
Is this a SEGMENT - not implemented.
|
||||
"""
|
||||
return self._is_SEGMENT( )
|
||||
|
||||
def is_MSW(self):
|
||||
"""
|
||||
Is this well a MSW well. Observe that the test ONLY applies to PLTs.
|
||||
"""
|
||||
return self._is_MSW( )
|
||||
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
# Enum: ecl_rft_enum from ecl_rft_node.h
|
||||
# RFT = 1
|
||||
# PLT = 2
|
||||
# Segment = 3 -- Not properly implemented
|
||||
"""
|
||||
Deprecated - use query methods: is_RFT(), is_PLT() and is_SEGMENT() instead.
|
||||
"""
|
||||
warnings.warn("The property type is deprecated, use the query methods is_RFT(), is_PLT() and is_SEGMENT() instead." , DeprecationWarning)
|
||||
return self._get_type( )
|
||||
|
||||
|
||||
def getWellName(self):
|
||||
"""
|
||||
The name of the well we are considering.
|
||||
"""
|
||||
return self._get_well( )
|
||||
|
||||
@property
|
||||
def well(self):
|
||||
warnings.warn("The property well is deprecated, use the getWellName() method instead." , DeprecationWarning)
|
||||
return self.getWellName( )
|
||||
|
||||
|
||||
def getDate(self):
|
||||
"""
|
||||
The date when this RFT/PLT/... was recorded.
|
||||
"""
|
||||
ct = CTime(self._get_date( ))
|
||||
return ct.date()
|
||||
|
||||
|
||||
@property
|
||||
def date(self):
|
||||
warnings.warn("The property date is deprecated, use the getDate() instead." , DeprecationWarning)
|
||||
return self.getDate()
|
||||
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
"""
|
||||
The number of completed cells.
|
||||
"""
|
||||
warnings.warn("The property size is deprecated, use the built in len( ) function instead." , DeprecationWarning)
|
||||
return len(self)
|
||||
|
||||
|
||||
def __cell_ref( self , cell_ptr ):
|
||||
if self.is_RFT():
|
||||
return EclRFTCell.createCReference( cell_ptr , self )
|
||||
elif self.is_PLT():
|
||||
return EclPLTCell.createCReference( cell_ptr , self )
|
||||
else:
|
||||
raise NotImplementedError("Only RFT and PLT cells are implemented")
|
||||
|
||||
|
||||
def assert_cell_index( self , index ):
|
||||
if isinstance( index , types.IntType):
|
||||
length = self.__len__()
|
||||
if index < 0 or index >= length:
|
||||
raise IndexError
|
||||
else:
|
||||
raise TypeError("Index should be integer type")
|
||||
|
||||
|
||||
def __getitem__(self , index):
|
||||
"""Implements the [] operator to return the cells.
|
||||
|
||||
To get the object related to cell nr 5:
|
||||
|
||||
cell = rft[4]
|
||||
|
||||
The return value from the __getitem__() method is either an
|
||||
EclRFTCell instance or a EclPLTCell instance, depending on the
|
||||
type of this particular RFT object.
|
||||
"""
|
||||
self.assert_cell_index( index )
|
||||
cell_ptr = self._iget_cell( index )
|
||||
return self.__cell_ref( cell_ptr )
|
||||
|
||||
|
||||
def iget( self , index ):
|
||||
return self[index]
|
||||
|
||||
|
||||
def iget_sorted( self , index ):
|
||||
"""
|
||||
Will return the cell nr @index in the list of sorted cells.
|
||||
|
||||
See method sort() for further documentation.
|
||||
"""
|
||||
self.assert_cell_index( index )
|
||||
cell_ptr = self._iget_cell_sorted( index )
|
||||
return self.__cell_ref( cell_ptr )
|
||||
|
||||
|
||||
def sort(self):
|
||||
"""
|
||||
Will sort cells in RFT; currently only applies to MSW wells.
|
||||
|
||||
By default the cells in the RFT come in the order they are
|
||||
specified in the ECLIPSE input file; that is not necessarily
|
||||
in a suitable order. In the case of MSW wells it is possible
|
||||
to sort the connections after distance along the wellpath. To
|
||||
access the cells in sort order you have two options:
|
||||
|
||||
1. Sort the cells using the sort() method, and then
|
||||
subsequently access them sequentially:
|
||||
|
||||
rft.sort()
|
||||
for cell in rft:
|
||||
print cell
|
||||
|
||||
2. Let the rft object stay unsorted, but access the cells
|
||||
using the iget_sorted() method:
|
||||
|
||||
for i in range(len(rft)):
|
||||
cell = rft.iget_sorted( i )
|
||||
|
||||
Currently only MSW/PLTs are sorted, based on the CONLENST
|
||||
keyword; for other wells the sort() method does nothing.
|
||||
"""
|
||||
self._sort_cells( )
|
||||
|
||||
|
||||
# ijk are zero offset
|
||||
def ijkget( self , ijk ):
|
||||
"""
|
||||
Will look up the cell based on (i,j,k).
|
||||
|
||||
If the cell (i,j,k) is not part of this RFT/PLT None will be
|
||||
returned. The (i,j,k) input values should be zero offset,
|
||||
i.e. you must subtract 1 from the (i,j,k) values given in the ECLIPSE input.
|
||||
"""
|
||||
cell_ptr = self._lookup_ijk( ijk[0] , ijk[1] , ijk[2])
|
||||
if cell_ptr:
|
||||
return self.__cell_ref( cell_ptr )
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
class EclRFTFile(BaseCClass):
|
||||
TYPE_NAME = "ecl_rft_file"
|
||||
_load = EclPrototype("void* ecl_rft_file_alloc_case( char* )", bind = False)
|
||||
_iget = EclPrototype("ecl_rft_ref ecl_rft_file_iget_node( ecl_rft_file , int )")
|
||||
_get_rft = EclPrototype("ecl_rft_ref ecl_rft_file_get_well_time_rft( ecl_rft_file , char* , time_t)")
|
||||
_has_rft = EclPrototype("bool ecl_rft_file_case_has_rft( char* )", bind = False)
|
||||
_free = EclPrototype("void ecl_rft_file_free( ecl_rft_file )")
|
||||
_get_size = EclPrototype("int ecl_rft_file_get_size__( ecl_rft_file , char* , time_t)")
|
||||
_get_num_wells = EclPrototype("int ecl_rft_file_get_num_wells( ecl_rft_file )")
|
||||
|
||||
|
||||
"""
|
||||
The EclRFTFile class is used to load an ECLIPSE RFT file.
|
||||
|
||||
The EclRFTFile serves as a container which can load and hold the
|
||||
content of an ECLIPSE RFT file. The RFT files will in general
|
||||
contain data for several wells and several times in one large
|
||||
container. The EclRFTClass class contains methods get the the RFT
|
||||
results for a specific time and/or well.
|
||||
|
||||
The EclRFTFile class can in general contain a mix of RFT and PLT
|
||||
measurements. The class does not really differentiate between
|
||||
these.
|
||||
"""
|
||||
|
||||
def __init__(self , case):
|
||||
c_ptr = self._load( case )
|
||||
super(EclRFTFile , self).__init__(c_ptr)
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return self._get_size( None , CTime(-1))
|
||||
|
||||
|
||||
def __getitem__(self, index):
|
||||
if isinstance(index, int):
|
||||
if 0 <= index < len(self):
|
||||
rft = self._iget(index)
|
||||
rft.setParent( self )
|
||||
return rft
|
||||
else:
|
||||
raise IndexError("Index '%d' must be in range: [0, %d]" % (index, len(self) - 1))
|
||||
else:
|
||||
raise TypeError("Index must be integer type")
|
||||
|
||||
|
||||
def size(self, well=None, date=None):
|
||||
"""
|
||||
The number of elements in EclRFTFile container.
|
||||
|
||||
By default the size() method will return the total number of
|
||||
RFTs/PLTs in the container, but by specifying the optional
|
||||
arguments date and/or well the function will only count the
|
||||
number of well measurements matching that time or well
|
||||
name. The well argument can contain wildcards.
|
||||
|
||||
rftFile = ecl.EclRFTFile( "ECLIPSE.RFT" )
|
||||
print "Total number of RFTs : %d" % rftFile.size( )
|
||||
print "RFTs matching OP* : %d" % rftFile.size( well = "OP*" )
|
||||
print "RFTs at 01/01/2010 : %d" % rftFile.size( date = datetime.date( 2010 , 1 , 1 ))
|
||||
|
||||
"""
|
||||
if date:
|
||||
cdate = CTime( date )
|
||||
else:
|
||||
cdate = CTime( -1 )
|
||||
|
||||
return self._get_size( well , cdate)
|
||||
|
||||
|
||||
def getNumWells(self):
|
||||
"""
|
||||
Returns the total number of distinct wells in the RFT file.
|
||||
"""
|
||||
return self._get_num_wells( )
|
||||
|
||||
@property
|
||||
def num_wells( self ):
|
||||
warnings.warn("The property num_wells is deprecated, use the getNumWells() instead." , DeprecationWarning)
|
||||
return self.getNumWells()
|
||||
|
||||
|
||||
def getHeaders(self):
|
||||
"""
|
||||
Returns a list of two tuples (well_name , date) for the whole file.
|
||||
"""
|
||||
header_list = []
|
||||
for i in (range(self._get_size( None , CTime(-1)))):
|
||||
rft = self.iget( i )
|
||||
header_list.append( (rft.well , rft.date) )
|
||||
return header_list
|
||||
|
||||
@property
|
||||
def headers(self):
|
||||
warnings.warn("The property headers is deprecated, use the getHeaders() instead." , DeprecationWarning)
|
||||
return self.getHeaders()
|
||||
|
||||
|
||||
def iget(self , index):
|
||||
"""
|
||||
Will lookup RFT @index - equivalent to [@index].
|
||||
"""
|
||||
return self[index]
|
||||
|
||||
|
||||
def get(self , well_name , date ):
|
||||
"""
|
||||
Will look up the RFT object corresponding to @well and @date.
|
||||
|
||||
Raise Exception if not found.
|
||||
"""
|
||||
if self.size( well = well_name , date = date) == 0:
|
||||
raise KeyError("No RFT for well:%s at %s" % (well_name , date))
|
||||
|
||||
rft = self._get_rft( well_name , CTime( date ))
|
||||
rft.setParent( self )
|
||||
return rft
|
||||
|
||||
def free(self):
|
||||
self._free( )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_rft_cell.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import warnings
|
||||
from cwrap import BaseCClass
|
||||
from ert.ecl import EclPrototype
|
||||
|
||||
|
||||
class RFTCell(BaseCClass):
|
||||
"""The RFTCell is a base class for the cells which are part of an RFT/PLT.
|
||||
|
||||
The RFTCell class contains the elements which are common to both
|
||||
RFT and PLT. The list of common elements include the corrdinates
|
||||
(i,j,k) the pressure and the depth of the cell. Actual user access
|
||||
should be based on the derived classes EclRFTCell and EclPLTCell.
|
||||
|
||||
Observe that from june 2013 the properties i,j and k which return
|
||||
offset 1 coordinate values are deprecated, and you should rather
|
||||
use the methods get_i(), get_j() and get_k() which return offset 0
|
||||
coordinate values.
|
||||
"""
|
||||
TYPE_NAME = "rft_cell"
|
||||
_free = EclPrototype("void ecl_rft_cell_free( rft_cell )")
|
||||
_get_pressure = EclPrototype("double ecl_rft_cell_get_pressure( rft_cell )")
|
||||
_get_depth = EclPrototype("double ecl_rft_cell_get_depth( rft_cell )")
|
||||
_get_i = EclPrototype("int ecl_rft_cell_get_i( rft_cell )")
|
||||
_get_j = EclPrototype("int ecl_rft_cell_get_j( rft_cell )")
|
||||
_get_k = EclPrototype("int ecl_rft_cell_get_k( rft_cell )")
|
||||
|
||||
|
||||
|
||||
|
||||
def warn(self , old , new):
|
||||
msg = """
|
||||
|
||||
The cell property:%s has been deprecated, and the method:%s() should
|
||||
be used instead. Observe that the new method %s() returns coordinate
|
||||
values starting at 0, whereas the old property %s returned values
|
||||
starting at 1; hence you must adapt the calling code when you change
|
||||
from %s -> %s()
|
||||
""" % (old , new , new , old , old , new)
|
||||
warnings.warn( msg , DeprecationWarning )
|
||||
|
||||
def free(self):
|
||||
self._free( )
|
||||
|
||||
@property
|
||||
def i(self):
|
||||
self.warn("i" , "get_i")
|
||||
return self.get_i() + 1
|
||||
|
||||
@property
|
||||
def j(self):
|
||||
self.warn("j" , "get_j")
|
||||
return self.get_j() + 1
|
||||
|
||||
@property
|
||||
def k(self):
|
||||
self.warn("k" , "get_k")
|
||||
return self.get_k() + 1
|
||||
|
||||
@property
|
||||
def ijk(self):
|
||||
self.warn("ijk" , "get_ijk")
|
||||
return (self.get_i() + 1 , self.get_j() + 1 , self.get_k() + 1)
|
||||
|
||||
def get_i(self):
|
||||
return self._get_i( )
|
||||
|
||||
def get_j(self):
|
||||
return self._get_j( )
|
||||
|
||||
def get_k(self):
|
||||
return self._get_k( )
|
||||
|
||||
def get_ijk(self):
|
||||
return (self.get_i( ) , self.get_j( ) , self.get_k( ))
|
||||
|
||||
@property
|
||||
def pressure(self):
|
||||
return self._get_pressure( )
|
||||
|
||||
@property
|
||||
def depth(self):
|
||||
return self._get_depth( )
|
||||
|
||||
|
||||
#################################################################
|
||||
|
||||
|
||||
class EclRFTCell(RFTCell):
|
||||
TYPE_NAME = "ecl_rft_cell"
|
||||
_alloc_RFT = EclPrototype("void* ecl_rft_cell_alloc_RFT( int, int , int , double , double , double , double)", bind = False)
|
||||
_get_swat = EclPrototype("double ecl_rft_cell_get_swat( ecl_rft_cell )")
|
||||
_get_soil = EclPrototype("double ecl_rft_cell_get_soil( ecl_rft_cell )")
|
||||
_get_sgas = EclPrototype("double ecl_rft_cell_get_sgas( ecl_rft_cell )")
|
||||
|
||||
def __init__(self , i , j , k , depth , pressure , swat , sgas):
|
||||
c_ptr = self._alloc_RFT( i , j , k , depth , pressure , swat , sgas )
|
||||
super(RFTCell , self).__init__( c_ptr )
|
||||
|
||||
@property
|
||||
def swat(self):
|
||||
return self._get_swat( )
|
||||
|
||||
@property
|
||||
def sgas(self):
|
||||
return self._get_sgas( )
|
||||
|
||||
@property
|
||||
def soil(self):
|
||||
return 1 - (self._get_sgas( ) + self._get_swat( ))
|
||||
|
||||
|
||||
#################################################################
|
||||
|
||||
|
||||
class EclPLTCell(RFTCell):
|
||||
TYPE_NAME = "ecl_plt_cell"
|
||||
_alloc_PLT = EclPrototype("void* ecl_rft_cell_alloc_PLT( int, int , int , double , double , double, double , double, double , double , double , double , double , double )" , bind = False)
|
||||
_get_orat = EclPrototype("double ecl_rft_cell_get_orat( ecl_plt_cell )")
|
||||
_get_grat = EclPrototype("double ecl_rft_cell_get_grat( ecl_plt_cell )")
|
||||
_get_wrat = EclPrototype("double ecl_rft_cell_get_wrat( ecl_plt_cell )")
|
||||
|
||||
_get_flowrate = EclPrototype("double ecl_rft_cell_get_flowrate( ecl_plt_cell )")
|
||||
_get_oil_flowrate = EclPrototype("double ecl_rft_cell_get_oil_flowrate( ecl_plt_cell )")
|
||||
_get_gas_flowrate = EclPrototype("double ecl_rft_cell_get_gas_flowrate( ecl_plt_cell )")
|
||||
_get_water_flowrate = EclPrototype("double ecl_rft_cell_get_water_flowrate( ecl_plt_cell )")
|
||||
|
||||
_get_conn_start = EclPrototype("double ecl_rft_cell_get_connection_start( ecl_plt_cell )")
|
||||
_get_conn_end = EclPrototype("double ecl_rft_cell_get_connection_end( ecl_plt_cell )")
|
||||
|
||||
|
||||
def __init__(self , i , j , k , depth , pressure , orat , grat , wrat , conn_start ,conn_end, flowrate , oil_flowrate , gas_flowrate , water_flowrate ):
|
||||
c_ptr = self._alloc_PLT( i , j , k , depth , pressure , orat , grat , wrat , conn_start ,conn_end, flowrate , oil_flowrate , gas_flowrate , water_flowrate )
|
||||
super( RFTCell , self).__init__( c_ptr )
|
||||
|
||||
|
||||
@property
|
||||
def orat(self):
|
||||
return self._get_orat( )
|
||||
|
||||
@property
|
||||
def grat(self):
|
||||
return self._get_grat( )
|
||||
|
||||
@property
|
||||
def wrat(self):
|
||||
return self._get_wrat( )
|
||||
|
||||
@property
|
||||
def conn_start(self):
|
||||
"""Will return the length from wellhead(?) to connection.
|
||||
|
||||
For MSW wells this property will return the distance from a
|
||||
fixed point (wellhead) to the current connection. This value
|
||||
will be used to sort the completed cells along the well
|
||||
path. In the case of non MSW wells this will just return a
|
||||
fixed default value.
|
||||
"""
|
||||
return self._get_conn_start( )
|
||||
|
||||
@property
|
||||
def conn_end(self):
|
||||
"""Will return the length from wellhead(?) to connection end.
|
||||
|
||||
For MSW wells this property will return the distance from a
|
||||
fixed point (wellhead) to the current connection end. This value
|
||||
will be used to sort the completed cells along the well
|
||||
path. In the case of non MSW wells this will just return a
|
||||
fixed default value.
|
||||
"""
|
||||
return self._get_conn_end( )
|
||||
|
||||
@property
|
||||
def flowrate(self):
|
||||
return self._get_flowrate( )
|
||||
|
||||
@property
|
||||
def oil_flowrate(self):
|
||||
return self._get_oil_flowrate( )
|
||||
|
||||
@property
|
||||
def gas_flowrate(self):
|
||||
return self._get_gas_flowrate( )
|
||||
|
||||
@property
|
||||
def water_flowrate(self):
|
||||
return self._get_water_flowrate( )
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
from cwrap import BaseCClass
|
||||
from ert.ecl import EclPrototype
|
||||
|
||||
|
||||
class EclSMSPECNode(BaseCClass):
|
||||
"""
|
||||
Small class with some meta information about a summary variable.
|
||||
|
||||
The summary variables have different attributes, like if they
|
||||
represent a total quantity, a rate or a historical quantity. These
|
||||
quantities, in addition to the underlying values like WGNAMES,
|
||||
KEYWORD and NUMS taken from the the SMSPEC file are stored in this
|
||||
structure.
|
||||
"""
|
||||
TYPE_NAME = "smspec_node"
|
||||
_node_is_total = EclPrototype("bool smspec_node_is_total( smspec_node )")
|
||||
_node_is_historical = EclPrototype("bool smspec_node_is_historical( smspec_node )")
|
||||
_node_is_rate = EclPrototype("bool smspec_node_is_rate( smspec_node )")
|
||||
_node_unit = EclPrototype("char* smspec_node_get_unit( smspec_node )")
|
||||
_node_wgname = EclPrototype("char* smspec_node_get_wgname( smspec_node )")
|
||||
_node_keyword = EclPrototype("char* smspec_node_get_keyword( smspec_node )")
|
||||
_node_num = EclPrototype("int smspec_node_get_num( smspec_node )")
|
||||
_node_need_num = EclPrototype("bool smspec_node_need_nums( smspec_node )")
|
||||
_gen_key1 = EclPrototype("char* smspec_node_get_gen_key1( smspec_node )")
|
||||
_gen_key2 = EclPrototype("char* smspec_node_get_gen_key2( smspec_node )")
|
||||
_var_type = EclPrototype("ecl_sum_var_type smspec_node_get_var_type( smspec_node )")
|
||||
|
||||
|
||||
def __init__(self):
|
||||
super(EclSMSPECNode, self).__init__(0) # null pointer
|
||||
raise NotImplementedError("Class can not be instantiated directly!")
|
||||
|
||||
@property
|
||||
def is_total(self):
|
||||
"""
|
||||
Will check if the node corresponds to a total quantity.
|
||||
|
||||
The question of whether a variable corresponds to a 'total'
|
||||
quantity or not can be interesting for e.g. interpolation
|
||||
purposes. The actual question whether a quantity is total or
|
||||
not is based on a hardcoded list in smspec_node_set_flags() in
|
||||
smspec_node.c; this list again is based on the tables 2.7 -
|
||||
2.11 in the ECLIPSE fileformat documentation.
|
||||
"""
|
||||
return self._node_is_total( )
|
||||
|
||||
@property
|
||||
def is_rate(self):
|
||||
"""
|
||||
Will check if the variable in question is a rate variable.
|
||||
|
||||
The conecpt of rate variabel is important (internally) when
|
||||
interpolation values to arbitrary times.
|
||||
"""
|
||||
return self._node_is_rate()
|
||||
|
||||
|
||||
@property
|
||||
def is_historical(self):
|
||||
"""
|
||||
Checks if the key corresponds to a historical variable.
|
||||
|
||||
The check is only based on the last character; all variables
|
||||
ending with 'H' are considered historical.
|
||||
"""
|
||||
return self._node_is_historical( )
|
||||
|
||||
|
||||
@property
|
||||
def unit(self):
|
||||
"""
|
||||
Returns the unit of this node as a string.
|
||||
"""
|
||||
return self._node_unit( )
|
||||
|
||||
@property
|
||||
def wgname(self):
|
||||
"""
|
||||
Returns the WGNAME property for this node.
|
||||
|
||||
Many variables do not have the WGNAME property, i.e. the field
|
||||
related variables like FOPT and the block properties like
|
||||
BPR:10,10,10. For these variables the function will return
|
||||
None, and not the ECLIPSE dummy value: ":+:+:+:+".
|
||||
"""
|
||||
return self._node_wgname( )
|
||||
|
||||
|
||||
@property
|
||||
def keyword(self):
|
||||
"""
|
||||
Returns the KEYWORD property for this node.
|
||||
|
||||
The KEYWORD property is the main classification property in
|
||||
the ECLIPSE SMSPEC file. The properties of a variable can be
|
||||
read from the KEWYORD value; see table 3.4 in the ECLIPSE file
|
||||
format reference manual.
|
||||
"""
|
||||
return self._node_keyword( )
|
||||
|
||||
@property
|
||||
def num(self):
|
||||
"""
|
||||
Returns the NUMS value for this keyword; or None.
|
||||
|
||||
Many of the summary keywords have an integer stored in the
|
||||
vector NUMS as an attribute, i.e. the block properties have
|
||||
the global index of the cell in the nums vector. If the
|
||||
variable in question makes use of the NUMS value this property
|
||||
will return the value, otherwise it will return None:
|
||||
|
||||
sum.smspec_node("FOPT").num => None
|
||||
sum.smspec_node("BPR:1000").num => 1000
|
||||
"""
|
||||
if self._node_need_num( ):
|
||||
return self._node_num( )
|
||||
else:
|
||||
return None
|
||||
|
||||
def getKey1(self):
|
||||
"""
|
||||
Returns the primary composite key, i.e. like 'WOPR:OPX' for this
|
||||
node.
|
||||
"""
|
||||
return self._gen_key1( )
|
||||
|
||||
|
||||
def getKey2(self):
|
||||
"""Returns the secondary composite key for this node.
|
||||
|
||||
Most variables have only one composite key, but in particular
|
||||
nodes which involve (i,j,k) coordinates will contain two
|
||||
forms:
|
||||
|
||||
getKey1() => "BPR:10,11,6"
|
||||
getKey2() => "BPR:52423"
|
||||
|
||||
Where the '52423' in getKey2() corresponds to i + j*nx +
|
||||
k*nx*ny.
|
||||
"""
|
||||
return self._gen_key2( )
|
||||
|
||||
|
||||
def varType(self):
|
||||
return self._var_type( )
|
||||
@@ -0,0 +1,116 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_subsidence.py' is part of ERT - Ensemble based
|
||||
# Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""
|
||||
Calculate dynamic change in gravitational strength.
|
||||
|
||||
The ecl_subsidence module contains functionality to load time-lapse ECLIPSE
|
||||
results and calculate the change in seafloor subsidence between the
|
||||
different surveys. The implementation is a thin wrapper around the
|
||||
ecl_subsidence.c implementation in the libecl library.
|
||||
"""
|
||||
from cwrap import BaseCClass
|
||||
from ert.ecl import EclPrototype
|
||||
|
||||
|
||||
class EclSubsidence(BaseCClass):
|
||||
"""
|
||||
Holding ECLIPSE results for calculating subsidence changes.
|
||||
|
||||
The EclSubsidence class is a collection class holding the results from
|
||||
ECLIPSE forward modelling of subsidence surveys. Observe that the
|
||||
class is focused on the ECLIPSE side of things, and does not have
|
||||
any notion of observed values or measurement locations; that
|
||||
should be handled by the scope using the EclSubsidence class.
|
||||
|
||||
Typical use of the EclSubsidence class involves the following steps:
|
||||
|
||||
1. Create the EclSubsidence instance.
|
||||
2. Add surveys with the add_survey_XXXX() methods.
|
||||
3. Evalute the subsidence response with the eval() method.
|
||||
"""
|
||||
TYPE_NAME = "ecl_subsidence"
|
||||
_alloc = EclPrototype("void* ecl_subsidence_alloc( ecl_grid , ecl_file )" , bind = False)
|
||||
_free = EclPrototype("void ecl_subsidence_free( ecl_subsidence )")
|
||||
_add_survey_PRESSURE = EclPrototype("void* ecl_subsidence_add_survey_PRESSURE( ecl_subsidence , char* , ecl_file )")
|
||||
_eval = EclPrototype("double ecl_subsidence_eval( ecl_subsidence , char* , char* , ecl_region , double , double , double, double, double)")
|
||||
|
||||
|
||||
def __init__( self, grid, init_file ):
|
||||
"""
|
||||
Creates a new EclSubsidence instance.
|
||||
|
||||
The input arguments @grid and @init_file should be instances
|
||||
of EclGrid and EclFile respectively.
|
||||
"""
|
||||
self.init_file = init_file # Inhibit premature garbage collection of init_file
|
||||
c_ptr = self._alloc( grid , init_file )
|
||||
super( EclSubsidence , self ).__init__( c_ptr )
|
||||
|
||||
|
||||
|
||||
def add_survey_PRESSURE( self, survey_name, restart_file ):
|
||||
"""
|
||||
Add new survey based on PRESSURE keyword.
|
||||
|
||||
Add a new survey; in this context a survey is the state of
|
||||
reservoir, i.e. an ECLIPSE restart file. The @survey_name
|
||||
input argument will be used when refering to this survey at a
|
||||
later stage. The @restart_file input argument should be an
|
||||
EclFile instance with data from one report step. A typical way
|
||||
to load the @restart_file argument is:
|
||||
|
||||
import datetime
|
||||
import ert.ecl.ecl as ecl
|
||||
...
|
||||
...
|
||||
date = datetime.datetime( year , month , day )
|
||||
restart_file1 = ecl.EclFile.restart_block( "ECLIPSE.UNRST" , dtime = date)
|
||||
restart_file2 = ecl.EclFile.restart_block( "ECLIPSE.UNRST" , report_step = 67 )
|
||||
|
||||
The pore volume is calculated from the initial pore volume and
|
||||
the PRESSURE keyword from the restart file.
|
||||
"""
|
||||
self._add_survey_PRESSURE( survey_name, restart_file)
|
||||
|
||||
|
||||
def eval(self, base_survey, monitor_survey, pos, compressibility, poisson_ratio, region=None):
|
||||
"""
|
||||
Calculates the subsidence change between two surveys.
|
||||
|
||||
This is the method everything is leading up to; will calculate
|
||||
the change in subsidence, in centimeters,
|
||||
between the two surveys named @base_survey and
|
||||
@monitor_survey.
|
||||
|
||||
The monitor survey can be 'None' - the resulting answer has
|
||||
nothing whatsovever to do with subsidence, but can be
|
||||
interesting to determine the numerical size of the quantities
|
||||
which are subtracted in a 4D study.
|
||||
|
||||
The @pos argument should be a tuple of three elements with the
|
||||
(utm_x , utm_y , depth) position where we want to evaluate the
|
||||
change in subsidence.
|
||||
|
||||
If supplied the optional argument @region should be an
|
||||
EclRegion() instance; this region will be used to limit the
|
||||
part of the reserviour included in the subsidence calculations.
|
||||
|
||||
The argument @compressibility is the total reservoir compressibility.
|
||||
"""
|
||||
return self._eval(self, base_survey, monitor_survey, region, pos[0], pos[1], pos[2], compressibility,
|
||||
poisson_ratio)
|
||||
|
||||
+1218
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,61 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_sum_keyword_vector.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
|
||||
import numpy
|
||||
import datetime
|
||||
|
||||
# Observe that there is some convention conflict with the C code
|
||||
# regarding order of arguments: The C code generally takes the time
|
||||
# index as the first argument and the key/key_index as second
|
||||
# argument. In the python code this order has been reversed.
|
||||
|
||||
from cwrap import BaseCClass
|
||||
from ert.ecl import EclPrototype
|
||||
|
||||
|
||||
|
||||
class EclSumKeyWordVector(BaseCClass):
|
||||
TYPE_NAME = "ecl_sum_vector"
|
||||
_alloc = EclPrototype("void* ecl_sum_vector_alloc(ecl_sum )" , bind = False)
|
||||
_free = EclPrototype("void ecl_sum_vector_free(ecl_sum_vector )")
|
||||
_add = EclPrototype("bool ecl_sum_vector_add_key( ecl_sum_vector , char* )")
|
||||
_add_multiple = EclPrototype("void ecl_sum_vector_add_keys( ecl_sum_vector , char* )")
|
||||
_get_size = EclPrototype("int ecl_sum_vector_get_size( ecl_sum_vector )")
|
||||
|
||||
|
||||
|
||||
def __init__(self, ecl_sum):
|
||||
c_pointer = self._alloc(ecl_sum)
|
||||
super(EclSumKeyWordVector, self).__init__(c_pointer)
|
||||
|
||||
def __len__(self):
|
||||
return self._get_size( )
|
||||
|
||||
def free(self):
|
||||
self._free( )
|
||||
|
||||
def addKeyword(self, keyword):
|
||||
success = self._add(keyword)
|
||||
if not success:
|
||||
raise KeyError("Failed to add keyword to vector")
|
||||
|
||||
def addKeywords(self, keyword_pattern):
|
||||
self._add_multiple(keyword_pattern)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
class EclSumNode:
|
||||
def __init__(self, mini_step, report_step, days, date, mpl_date, value):
|
||||
"""
|
||||
EclSumNode is a 'struct' with a summary value and time.
|
||||
|
||||
EclSumNode - a small 'struct' with a summary value and time in
|
||||
several formats. When iterating over a EclSumVector instance
|
||||
you will get EclSumNode instances. The content of the
|
||||
EclSumNode type is stored as plain attributes:
|
||||
|
||||
value : The actual value
|
||||
report_step : The report step
|
||||
mini_step : The ministep
|
||||
days : Days since simulation start
|
||||
date : The simulation date
|
||||
mpl_date : A date format suitable for matplotlib
|
||||
|
||||
"""
|
||||
self.value = value
|
||||
self.report_step = report_step
|
||||
self.mini_step = mini_step
|
||||
self.days = days
|
||||
self.date = date
|
||||
self.mpl_date = mpl_date
|
||||
|
||||
def __str__(self):
|
||||
return "<EclSumNode: days:%g value:%g>" % ( self.days, self.value )
|
||||
@@ -0,0 +1,61 @@
|
||||
from cwrap import BaseCClass
|
||||
from ert.ecl import EclPrototype
|
||||
from ert.util import CTime
|
||||
|
||||
|
||||
class EclSumTStep(BaseCClass):
|
||||
TYPE_NAME = "ecl_sum_tstep"
|
||||
_alloc = EclPrototype("void* ecl_sum_tstep_alloc_new(int, int, float, void*)" , bind = False)
|
||||
_free = EclPrototype("void ecl_sum_tstep_free(ecl_sum_tstep)")
|
||||
_get_sim_days = EclPrototype("double ecl_sum_tstep_get_sim_days(ecl_sum_tstep)")
|
||||
_get_sim_time = EclPrototype("time_t ecl_sum_tstep_get_sim_time(ecl_sum_tstep)")
|
||||
_get_report = EclPrototype("int ecl_sum_tstep_get_report(ecl_sum_tstep)")
|
||||
_get_ministep = EclPrototype("int ecl_sum_tstep_get_ministep(ecl_sum_tstep)")
|
||||
_set_from_key = EclPrototype("void ecl_sum_tstep_set_from_key(ecl_sum_tstep, char*, float)")
|
||||
_get_from_key = EclPrototype("double ecl_sum_tstep_get_from_key(ecl_sum_tstep, char*)")
|
||||
_has_key = EclPrototype("bool ecl_sum_tstep_has_key(ecl_sum_tstep)")
|
||||
|
||||
|
||||
|
||||
def __init__(self, report_step, mini_step, sim_days, smspec):
|
||||
sim_seconds = sim_days * 24 * 60 * 60
|
||||
c_pointer = self._alloc(report_step, mini_step, sim_seconds, smspec)
|
||||
super(EclSumTStep, self).__init__(c_pointer)
|
||||
|
||||
|
||||
def getSimDays(self):
|
||||
""" @rtype: double """
|
||||
return self._get_sim_days( )
|
||||
|
||||
def getReport(self):
|
||||
""" @rtype: int """
|
||||
return self._get_report( )
|
||||
|
||||
def getMiniStep(self):
|
||||
""" @rtype: int """
|
||||
return self._get_ministep( )
|
||||
|
||||
def getSimTime(self):
|
||||
""" @rtype: CTime """
|
||||
return self._get_sim_time( )
|
||||
|
||||
def __getitem__(self, key):
|
||||
""" @rtype: double """
|
||||
if not key in self:
|
||||
raise KeyError("Key '%s' is not available." % key)
|
||||
|
||||
return self._get_from_key(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if not key in self:
|
||||
raise KeyError("Key '%s' is not available." % key)
|
||||
|
||||
self._set_from_key(key, value)
|
||||
|
||||
def __contains__(self, key):
|
||||
return self._has_key(key)
|
||||
|
||||
def free(self):
|
||||
self._free(self)
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# Copyright (C) 2016 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_sum_var_type.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import BaseCEnum
|
||||
from ert.ecl import ECL_LIB
|
||||
|
||||
|
||||
class EclSumVarType(BaseCEnum):
|
||||
ECL_SMSPEC_INVALID_VAR = None
|
||||
ECL_SMSPEC_AQUIFER_VAR = None
|
||||
ECL_SMSPEC_WELL_VAR = None
|
||||
ECL_SMSPEC_REGION_VAR = None
|
||||
ECL_SMSPEC_FIELD_VAR = None
|
||||
ECL_SMSPEC_GROUP_VAR = None
|
||||
ECL_SMSPEC_BLOCK_VAR = None
|
||||
ECL_SMSPEC_COMPLETION_VAR = None
|
||||
ECL_SMSPEC_LOCAL_BLOCK_VAR = None
|
||||
ECL_SMSPEC_LOCAL_COMPLETION_VAR = None
|
||||
ECL_SMSPEC_LOCAL_WELL_VAR = None
|
||||
ECL_SMSPEC_NETWORK_VAR = None
|
||||
ECL_SMSPEC_REGION_2_REGION_VAR = None
|
||||
ECL_SMSPEC_SEGMENT_VAR = None
|
||||
ECL_SMSPEC_MISC_VAR = None
|
||||
|
||||
|
||||
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_INVALID_VAR", 0)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_AQUIFER_VAR", 1)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_WELL_VAR", 2)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_REGION_VAR", 3)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_FIELD_VAR", 4)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_GROUP_VAR", 5)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_BLOCK_VAR", 6)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_COMPLETION_VAR", 7)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_LOCAL_BLOCK_VAR", 8)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_LOCAL_COMPLETION_VAR", 9)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_LOCAL_WELL_VAR", 10)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_NETWORK_VAR", 11)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_REGION_2_REGION_VAR", 12)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_SEGMENT_VAR", 13)
|
||||
EclSumVarType.addEnum("ECL_SMSPEC_MISC_VAR", 14)
|
||||
|
||||
EclSumVarType.registerEnum(ECL_LIB, "ecl_sum_var_type")
|
||||
@@ -0,0 +1,286 @@
|
||||
from __future__ import print_function
|
||||
import warnings
|
||||
from ert.ecl.ecl_sum_node import EclSumNode
|
||||
|
||||
|
||||
class EclSumVector(object):
|
||||
def __init__(self, parent, key, report_only = False):
|
||||
"""
|
||||
A summary vector with a vector of values and time.
|
||||
|
||||
A summary vector contains the the full time history of one
|
||||
key, along with the corresponding time vectors in several
|
||||
different time formats. Depending on the report_only argument
|
||||
the data vectors in the EclSumVector can either contain all
|
||||
the time values, or only those corresponding to report_steps.
|
||||
|
||||
The EclSumVector contains a reference to the parent EclSum
|
||||
structure and this is used to implement several of the
|
||||
properties and methods of the object; the EclSum vector
|
||||
instances should therefor only be instantiated through the
|
||||
EclSum.get_vector() method, and not manually with the
|
||||
EclSumVector() constructor.
|
||||
"""
|
||||
self.parent = parent
|
||||
self.key = key
|
||||
self.report_only = report_only
|
||||
|
||||
if report_only:
|
||||
warnings.warn("The report_only flag to the EclSumVector will be removed" , DeprecationWarning)
|
||||
|
||||
self.__dates = parent.get_dates(report_only)
|
||||
self.__days = parent.get_days(report_only)
|
||||
self.__mpl_dates = parent.get_mpl_dates(report_only)
|
||||
self.__mini_step = parent.get_mini_step(report_only)
|
||||
self.__report_step = parent.get_report_step(report_only)
|
||||
self.__values = None
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return "<Summary vector: %s>" % self.key
|
||||
|
||||
|
||||
@property
|
||||
def unit( self ):
|
||||
"""
|
||||
The unit of this vector.
|
||||
"""
|
||||
return self.parent.get_unit(self.key)
|
||||
|
||||
def assert_values( self ):
|
||||
"""
|
||||
This function will load and internalize all the values.
|
||||
"""
|
||||
if self.__values is None:
|
||||
self.__values = self.parent.get_values(self.key, self.report_only)
|
||||
|
||||
@property
|
||||
def values( self ):
|
||||
"""
|
||||
All the summary values of the vector, as a numpy vector.
|
||||
"""
|
||||
self.assert_values()
|
||||
return self.__values
|
||||
|
||||
@property
|
||||
def dates( self ):
|
||||
"""
|
||||
All the dates of the vector, list of datetime() instances.
|
||||
"""
|
||||
return self.__dates
|
||||
|
||||
@property
|
||||
def days( self ):
|
||||
"""
|
||||
The time in days as a numpy vector.
|
||||
|
||||
In the case of lab unit this will be hours.
|
||||
"""
|
||||
return self.__days
|
||||
|
||||
@property
|
||||
def mpl_dates( self ):
|
||||
"""
|
||||
All the dates as numpy vector of dates in matplotlib format.
|
||||
"""
|
||||
return self.__mpl_dates
|
||||
|
||||
@property
|
||||
def mini_step( self ):
|
||||
"""
|
||||
All the ministeps of the vector.
|
||||
|
||||
Ministeps is the ECLIPSE notion of timesteps. The ministeps
|
||||
are numbered sequentially starting at zero; if you have loaded
|
||||
the entire simulation the ministep number will correspond to
|
||||
the natural indexing. The length of each ministep is
|
||||
determined by the convergence properties of the ECLIPSE
|
||||
simulation.
|
||||
"""
|
||||
return self.__mini_step
|
||||
|
||||
@property
|
||||
def report_step( self ):
|
||||
"""
|
||||
All the report_step of the vector.
|
||||
"""
|
||||
return self.__report_step
|
||||
|
||||
|
||||
def __iget__( self, index ):
|
||||
"""
|
||||
Will return an EclSumNode for element @index; should be called
|
||||
through the [] operator, otherwise you can come across
|
||||
unitialized data.
|
||||
"""
|
||||
return EclSumNode(self.__mini_step[index],
|
||||
self.__report_step[index],
|
||||
self.__days[index],
|
||||
self.__dates[index],
|
||||
self.__mpl_dates[index],
|
||||
self.__values[index])
|
||||
|
||||
|
||||
def __len__(self):
|
||||
"""
|
||||
The length of the vector - used for the len() builtin.
|
||||
"""
|
||||
return len(self.__days)
|
||||
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""
|
||||
Implements the [] operator.
|
||||
|
||||
Will return EclSumNode instance according to @index. The index
|
||||
value will be interpreted as in a normal python [] lookup,
|
||||
i.e. negative values will be interpreted as starting from the
|
||||
right and also slice notation is allowed[*].
|
||||
|
||||
[*] Observe that in the case of slices the return value will
|
||||
not be a proper EclSumVector instance, but rather a normal
|
||||
Python list of EclSumNode instances.
|
||||
"""
|
||||
self.assert_values()
|
||||
length = len(self.values)
|
||||
if isinstance(index, int):
|
||||
if index < 0:
|
||||
index += len(self.__values)
|
||||
if index < 0 or index > length:
|
||||
raise KeyError("Invalid index:%d out of range [0:%d)" % ( index, length))
|
||||
else:
|
||||
return self.__iget__(index)
|
||||
elif isinstance(index, slice):
|
||||
# Observe that the slice based lookup does __not__ return
|
||||
# a proper EclSumVector instance; it will merely return
|
||||
# a simple Python list with EclSumNode instances.
|
||||
(start, stop, step) = index.indices(length)
|
||||
index = start
|
||||
sub_vector = []
|
||||
while index < stop:
|
||||
sub_vector.append(self.__iget__(index))
|
||||
index += step
|
||||
return sub_vector
|
||||
|
||||
raise KeyError("Invalid index:%s - must have integer or slice." % index)
|
||||
|
||||
@property
|
||||
def first( self ):
|
||||
"""
|
||||
Will return the first EclSumNode in this vector.
|
||||
"""
|
||||
self.assert_values()
|
||||
return self.__iget__(0)
|
||||
|
||||
@property
|
||||
def last( self ):
|
||||
"""
|
||||
Will return the last EclSumNode in this vector.
|
||||
"""
|
||||
self.assert_values()
|
||||
|
||||
index = len(self.__values) - 1
|
||||
return self.__iget__(index)
|
||||
|
||||
@property
|
||||
def last_value( self ):
|
||||
"""
|
||||
Will return the last value in this vector.
|
||||
"""
|
||||
self.assert_values()
|
||||
|
||||
index = len(self.__values) - 1
|
||||
return self.__iget__(index).value
|
||||
|
||||
|
||||
def get_interp( self, days=None, date=None):
|
||||
"""
|
||||
Will lookup value interpolated to @days or @date.
|
||||
|
||||
The function requires one, and only one, time indicator in
|
||||
terms of @days or @date. If the @date variable is given that
|
||||
should be Python datetime instance.
|
||||
|
||||
vec = sum["WWCT:A-3"]
|
||||
vec.get_interp( days = 100 )
|
||||
vec.get_interp( date = datetime.date( year , month , day ))
|
||||
|
||||
This function will crash and burn if the time arguments are
|
||||
invalid; if in doubt you should check first.
|
||||
"""
|
||||
return self.parent.get_interp(self.key, days, date)
|
||||
|
||||
|
||||
def get_interp_vector( self, days_list=None, date_list=None):
|
||||
"""
|
||||
Will return Python list of interpolated values.
|
||||
|
||||
See get_interp() for further details.
|
||||
"""
|
||||
return self.parent.get_interp_vector(self.key, days_list, date_list)
|
||||
|
||||
|
||||
def get_from_report( self, report_step ):
|
||||
"""
|
||||
Will lookup the value based on @report_step.
|
||||
"""
|
||||
return self.parent.get_from_report(self.key, report_step)
|
||||
|
||||
#################################################################
|
||||
|
||||
def first_gt_index( self, limit ):
|
||||
"""
|
||||
Locates first index where the value is above @limit.
|
||||
|
||||
Observe that this method will raise an exception if it is
|
||||
called from a vector instance with report_only = True.
|
||||
"""
|
||||
if not self.report_only:
|
||||
key_index = self.parent._get_general_var_index(self.key)
|
||||
time_index = self.parent._get_first_gt(key_index, limit)
|
||||
return time_index
|
||||
else:
|
||||
raise Exception("Sorry - first_gt_index() can not be called for vectors with report_only=True")
|
||||
|
||||
def first_gt( self, limit ):
|
||||
"""
|
||||
Locate the first EclSumNode where value is above @limit.
|
||||
|
||||
vec = sum["WWCT:A-3"]
|
||||
w = vec.first_gt( 0.50 )
|
||||
print('Water cut above 0.50 in well A-3 at: %s' % w.date)
|
||||
|
||||
Uses first_gt_index() internally and can not be called for
|
||||
vectors with report_only = True.
|
||||
"""
|
||||
time_index = self.first_gt_index(limit)
|
||||
print(time_index)
|
||||
if time_index >= 0:
|
||||
return self.__iget__(time_index)
|
||||
else:
|
||||
return None
|
||||
|
||||
def first_lt_index( self, limit ):
|
||||
"""
|
||||
Locates first index where the value is below @limit.
|
||||
|
||||
See first_gt_index() for further details.
|
||||
"""
|
||||
if not self.report_only:
|
||||
key_index = self.parent._get_general_var_index(self.key)
|
||||
time_index = self.parent._get_first_lt(key_index, limit)
|
||||
return time_index
|
||||
else:
|
||||
raise Exception("Sorry - first_lt_index() can not be called for vectors with report_only=True")
|
||||
|
||||
def first_lt( self, limit ):
|
||||
"""
|
||||
Locates first element where the value is below @limit.
|
||||
|
||||
See first_gt() for further details.
|
||||
"""
|
||||
time_index = self.first_lt_index(limit)
|
||||
if time_index >= 0:
|
||||
return self.__iget__(time_index)
|
||||
else:
|
||||
return None
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'ecl_util.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""
|
||||
Constants from the header ecl_util.h - some stateless functions.
|
||||
|
||||
This module does not contain any class definitions; it mostly consists
|
||||
of enum definitions/values from ecl_util.h; the enum values are
|
||||
extracted from the shared library using the
|
||||
cwrap.cenum.create_enum() function in a semi-automagic manner.
|
||||
|
||||
In addition to the enum definitions there are a few stateless
|
||||
functions from ecl_util.c which are not bound to any class type.
|
||||
"""
|
||||
import ctypes
|
||||
from cwrap import BaseCEnum
|
||||
from ert.ecl import EclPrototype, ECL_LIB
|
||||
|
||||
class EclFileEnum(BaseCEnum):
|
||||
ECL_OTHER_FILE = None
|
||||
ECL_RESTART_FILE = None
|
||||
ECL_UNIFIED_RESTART_FILE = None
|
||||
ECL_SUMMARY_FILE = None
|
||||
ECL_UNIFIED_SUMMARY_FILE = None
|
||||
ECL_GRID_FILE = None
|
||||
ECL_EGRID_FILE = None
|
||||
ECL_INIT_FILE = None
|
||||
ECL_RFT_FILE = None
|
||||
ECL_DATA_FILE = None
|
||||
|
||||
|
||||
EclFileEnum.addEnum("ECL_OTHER_FILE", 0)
|
||||
EclFileEnum.addEnum("ECL_RESTART_FILE", 1)
|
||||
EclFileEnum.addEnum("ECL_UNIFIED_RESTART_FILE", 2)
|
||||
EclFileEnum.addEnum("ECL_SUMMARY_FILE", 4)
|
||||
EclFileEnum.addEnum("ECL_UNIFIED_SUMMARY_FILE", 8)
|
||||
EclFileEnum.addEnum("ECL_SUMMARY_HEADER_FILE", 16)
|
||||
EclFileEnum.addEnum("ECL_GRID_FILE", 32)
|
||||
EclFileEnum.addEnum("ECL_EGRID_FILE", 64)
|
||||
EclFileEnum.addEnum("ECL_INIT_FILE", 128)
|
||||
EclFileEnum.addEnum("ECL_RFT_FILE", 256)
|
||||
EclFileEnum.addEnum("ECL_DATA_FILE", 512)
|
||||
|
||||
EclFileEnum.registerEnum(ECL_LIB, "ecl_file_enum")
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
class EclPhaseEnum(BaseCEnum):
|
||||
ECL_OIL_PHASE = None
|
||||
ECL_GAS_PHASE = None
|
||||
ECL_WATER_PHASE = None
|
||||
|
||||
EclPhaseEnum.addEnum("ECL_OIL_PHASE" , 1 )
|
||||
EclPhaseEnum.addEnum("ECL_GAS_PHASE" , 2 )
|
||||
EclPhaseEnum.addEnum("ECL_WATER_PHASE" , 4 )
|
||||
|
||||
EclPhaseEnum.registerEnum(ECL_LIB, "ecl_phase_enum")
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
class EclTypeEnum(BaseCEnum):
|
||||
ECL_CHAR_TYPE = None
|
||||
ECL_FLOAT_TYPE = None
|
||||
ECL_DOUBLE_TYPE = None
|
||||
ECL_INT_TYPE = None
|
||||
ECL_BOOL_TYPE = None
|
||||
ECL_MESS_TYPE = None
|
||||
|
||||
EclTypeEnum.addEnum("ECL_CHAR_TYPE" , 0 )
|
||||
EclTypeEnum.addEnum("ECL_FLOAT_TYPE" , 1 )
|
||||
EclTypeEnum.addEnum("ECL_DOUBLE_TYPE" , 2 )
|
||||
EclTypeEnum.addEnum("ECL_INT_TYPE" , 3 )
|
||||
EclTypeEnum.addEnum("ECL_BOOL_TYPE" , 4 )
|
||||
EclTypeEnum.addEnum("ECL_MESS_TYPE" , 5 )
|
||||
|
||||
|
||||
EclTypeEnum.registerEnum(ECL_LIB, "ecl_type_enum")
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
class EclUnitTypeEnum(BaseCEnum):
|
||||
TYPE_NAME = "ecl_unit_enum"
|
||||
|
||||
ERT_ECL_METRIC_UNITS = None
|
||||
ERT_ECL_FIELD_UNITS = None
|
||||
ERT_ECL_LAB_UNITS = None
|
||||
|
||||
EclUnitTypeEnum.addEnum("ERT_ECL_METRIC_UNITS" , 0 )
|
||||
EclUnitTypeEnum.addEnum("ERT_ECL_FIELD_UNITS" , 1 )
|
||||
EclUnitTypeEnum.addEnum("ERT_ECL_LAB_UNITS" , 2 )
|
||||
|
||||
EclUnitTypeEnum.registerEnum(ECL_LIB, "ecl_unit_enum")
|
||||
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
class EclFileFlagEnum(BaseCEnum):
|
||||
ECL_FILE_CLOSE_STREAM = None
|
||||
ECL_FILE_WRITABLE = None
|
||||
|
||||
EclFileFlagEnum.addEnum("ECL_FILE_CLOSE_STREAM" , 1 )
|
||||
EclFileFlagEnum.addEnum("ECL_FILE_WRITABLE" , 2 )
|
||||
|
||||
EclFileFlagEnum.registerEnum(ECL_LIB, "ecl_file_flag_enum")
|
||||
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
class EclUtil(object):
|
||||
_get_num_cpu = EclPrototype("int ecl_util_get_num_cpu( char* )", bind = False)
|
||||
_get_file_type = EclPrototype("ecl_file_enum ecl_util_get_file_type( char* , bool* , int*)" , bind = False)
|
||||
_get_type_name = EclPrototype("char* ecl_util_get_type_name( int )" , bind = False)
|
||||
_get_start_date = EclPrototype("time_t ecl_util_get_start_date( char* )" , bind = False)
|
||||
_get_report_step = EclPrototype("int ecl_util_filename_report_nr( char* )" , bind = False)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_num_cpu( datafile ):
|
||||
"""
|
||||
Parse ECLIPSE datafile and determine how many CPUs are needed.
|
||||
|
||||
Will look for the "PARALLELL" keyword, and then read off the
|
||||
number of CPUs required. Will return one if no PARALLELL keyword
|
||||
is found.
|
||||
"""
|
||||
return EclUtil._get_num_cpu(datafile)
|
||||
|
||||
@staticmethod
|
||||
def get_file_type( filename ):
|
||||
"""
|
||||
Will inspect an ECLIPSE filename and return an integer type flag.
|
||||
"""
|
||||
file_type , fmt , step = EclUtil.inspectExtension( filename )
|
||||
return file_type
|
||||
|
||||
@staticmethod
|
||||
def type_name(ecl_type):
|
||||
return EclUtil._get_type_name(ecl_type)
|
||||
|
||||
@staticmethod
|
||||
def get_start_date(datafile):
|
||||
return EclUtil._get_start_date(datafile).datetime()
|
||||
|
||||
@staticmethod
|
||||
def inspectExtension( filename ):
|
||||
"""Will inspect an ECLIPSE filename and return a tuple consisting of
|
||||
file type (EclFileEnum), a bool for formatted or not, and an
|
||||
integer for the step number.
|
||||
|
||||
"""
|
||||
fmt_file = ctypes.c_bool()
|
||||
report_step = ctypes.c_int(-1)
|
||||
file_type = EclUtil._get_file_type(filename, ctypes.byref(fmt_file) , ctypes.byref(report_step))
|
||||
if report_step.value == -1:
|
||||
step = None
|
||||
else:
|
||||
step = report_step.value
|
||||
|
||||
return (file_type , fmt_file.value , step)
|
||||
|
||||
@staticmethod
|
||||
def reportStep(filename):
|
||||
report_step = EclUtil._get_report_step(filename)
|
||||
if report_step < 0:
|
||||
raise ValueError("Could not infer report step from: %s" % filename)
|
||||
|
||||
return report_step
|
||||
|
||||
|
||||
|
||||
get_num_cpu = EclUtil.get_num_cpu
|
||||
get_file_type = EclUtil.get_file_type
|
||||
type_name = EclUtil.type_name
|
||||
get_start_date = EclUtil.get_start_date
|
||||
@@ -0,0 +1,12 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
fault_block.py
|
||||
fault_block_layer.py
|
||||
fault_collection.py
|
||||
fault.py
|
||||
fault_line.py
|
||||
fault_segments.py
|
||||
layer.py
|
||||
)
|
||||
|
||||
add_python_package("python.ert.ecl.faults" ${PYTHON_INSTALL_PREFIX}/ert/ecl/faults "${PYTHON_SOURCES}" True)
|
||||
@@ -0,0 +1,7 @@
|
||||
from .layer import Layer
|
||||
from .fault_collection import FaultCollection
|
||||
from .fault import Fault
|
||||
from .fault_line import FaultLine
|
||||
from .fault_segments import FaultSegment , SegmentMap
|
||||
from .fault_block import FaultBlock , FaultBlockCell
|
||||
from .fault_block_layer import FaultBlockLayer
|
||||
@@ -0,0 +1,644 @@
|
||||
# Copyright (C) 2014 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'fault.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from ert.ecl import EclTypeEnum
|
||||
from ert.geo import Polyline , CPolyline , GeometryTools
|
||||
from ert.util import stat
|
||||
from ert.util import Matrix
|
||||
|
||||
from .fault_line import FaultLine
|
||||
from .fault_segments import FaultSegment , SegmentMap
|
||||
|
||||
|
||||
class Layer(object):
|
||||
def __init__(self, grid , K):
|
||||
assert( isinstance(K , int) )
|
||||
self.__grid = grid
|
||||
self.__K = K
|
||||
self.__fault_lines = []
|
||||
self.__segment_map = SegmentMap()
|
||||
self.__processed = False
|
||||
|
||||
|
||||
def addSegment(self , segment):
|
||||
self.__segment_map.addSegment( segment )
|
||||
self.__processed = False
|
||||
|
||||
def __len__(self):
|
||||
self.processSegments()
|
||||
return len(self.__fault_lines)
|
||||
|
||||
def __iter__(self):
|
||||
self.processSegments()
|
||||
return iter(self.__fault_lines)
|
||||
|
||||
def __getitem__(self , index):
|
||||
self.processSegments()
|
||||
return self.__fault_lines[index]
|
||||
|
||||
def getK(self):
|
||||
return self.__K
|
||||
|
||||
|
||||
def getNeighborCells(self):
|
||||
neighbor_cells = []
|
||||
for fl in self:
|
||||
neighbor_cells += fl.getNeighborCells()
|
||||
return neighbor_cells
|
||||
|
||||
def getPolyline(self , name = None):
|
||||
polyline = CPolyline( name = name)
|
||||
for fl in self:
|
||||
polyline += fl.getPolyline()
|
||||
return polyline
|
||||
|
||||
|
||||
def getIJPolyline(self):
|
||||
"""
|
||||
Will return a python list of (int,int) tuple.
|
||||
"""
|
||||
polyline = []
|
||||
for fl in self:
|
||||
polyline += fl.getIJPolyline()
|
||||
return polyline
|
||||
|
||||
|
||||
def numLines(self):
|
||||
return len(self)
|
||||
|
||||
|
||||
# A fault can typically consist of several non connected fault
|
||||
# segments; right after reading the fault input these can be in a
|
||||
# complete mess:
|
||||
#
|
||||
# 1. The different part of the fault can be in random order.
|
||||
#
|
||||
# 2. Within each fault line the micro segments can be ordered in
|
||||
# reverse.
|
||||
#
|
||||
# This method goes through some desparate heuristics trying to
|
||||
# sort things out.
|
||||
|
||||
def __sortFaultLines(self):
|
||||
N = len(self.__fault_lines)
|
||||
x = Matrix(N , 1)
|
||||
y = Matrix(N , 1)
|
||||
|
||||
for index,line in enumerate(self.__fault_lines):
|
||||
xc,yc = line.center()
|
||||
|
||||
x[index,0] = xc
|
||||
y[index,0] = yc
|
||||
|
||||
# y = beta[0] + beta[1] * x
|
||||
# = a + b * x
|
||||
beta = stat.polyfit(2 , x , y)
|
||||
a = beta[0]
|
||||
b = beta[1]
|
||||
|
||||
perm_list = []
|
||||
for index,line in enumerate(self.__fault_lines):
|
||||
x0 , y0 = line.center()
|
||||
d = x0 + b*(y0 - a)
|
||||
perm_list.append((index , d))
|
||||
perm_list.sort(key = lambda x: x[1])
|
||||
|
||||
fault_lines = []
|
||||
for (index,d) in perm_list:
|
||||
fault_lines.append( self.__fault_lines[ index ])
|
||||
self.__fault_lines = fault_lines
|
||||
|
||||
|
||||
for line in self.__fault_lines:
|
||||
x1,y1 = line.startPoint()
|
||||
x2,y2 = line.endPoint()
|
||||
d1 = x1 + b*(y1 - a)
|
||||
d2 = x2 + b*(y2 - a)
|
||||
|
||||
if d1 > d2:
|
||||
line.reverse()
|
||||
|
||||
|
||||
|
||||
|
||||
def processSegments(self):
|
||||
if self.__processed:
|
||||
return
|
||||
|
||||
while self.__segment_map:
|
||||
fault_line = FaultLine(self.__grid , self.__K)
|
||||
self.__fault_lines.append( fault_line )
|
||||
|
||||
current_segment = self.__segment_map.popStart()
|
||||
while current_segment:
|
||||
append = fault_line.tryAppend(current_segment)
|
||||
if not append:
|
||||
fault_line = FaultLine(self.__grid , self.__K)
|
||||
self.__fault_lines.append( fault_line )
|
||||
fault_line.tryAppend(current_segment)
|
||||
|
||||
current_segment.next_segment = self.__segment_map.popNext( current_segment )
|
||||
current_segment = current_segment.next_segment
|
||||
|
||||
if len(self.__fault_lines) > 1:
|
||||
self.__sortFaultLines()
|
||||
|
||||
self.__processed = True
|
||||
|
||||
|
||||
#################################################################
|
||||
|
||||
|
||||
class Fault(object):
|
||||
allowed_faces = ["X","Y","Z","I","J","K","X-","Y-","Z-","I-","J-","K-"]
|
||||
|
||||
def __init__(self, grid , name):
|
||||
self.__grid = grid
|
||||
self.__name = name
|
||||
self.__layer_map = {}
|
||||
self.__layer_list = []
|
||||
(self.nx , self.ny , self.nz , nactive) = grid.dims
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return "Fault:%s" % self.__name
|
||||
|
||||
def __getitem__(self , K):
|
||||
if not self.hasLayer(K):
|
||||
self.addLayer(K)
|
||||
layer = self.__layer_map[K]
|
||||
return layer
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__layer_map)
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
for layer in self.__layer_list:
|
||||
yield layer
|
||||
|
||||
|
||||
def hasLayer(self , K):
|
||||
return self.__layer_map.has_key( K )
|
||||
|
||||
|
||||
def addLayer(self , K):
|
||||
layer = Layer(self.__grid , K)
|
||||
self.__layer_map[K] = layer
|
||||
self.__layer_list.append( layer )
|
||||
|
||||
|
||||
def createSegment(self , I1 , I2 , J1 , J2 , face):
|
||||
if face in ["X" , "I"]:
|
||||
C1 = I1 + 1 + J1*(self.nx + 1)
|
||||
C2 = C1 + (1 + J2 - J1) * (self.nx + 1)
|
||||
elif face in ["X-" , "I-"]:
|
||||
C1 = I1 + J1*(self.nx + 1)
|
||||
C2 = C1 + (1 + J2 - J1) * (self.nx + 1)
|
||||
elif face in ["Y" , "J"]:
|
||||
C1 = I1 + (J1 + 1) * (self.nx + 1)
|
||||
C2 = C1 + (1 + I2 - I1)
|
||||
elif face in ["Y-" , "J-"]:
|
||||
C1 = I1 + J1 * (self.nx + 1)
|
||||
C2 = C1 + (1 + I2 - I1)
|
||||
else:
|
||||
return None
|
||||
|
||||
return FaultSegment(C1,C2)
|
||||
|
||||
|
||||
|
||||
def addRecord(self , I1 , I2 , J1 , J2 , K1 , K2 , face):
|
||||
if not face in Fault.allowed_faces:
|
||||
raise ValueError("Invalid face:%s" % face)
|
||||
|
||||
if I1 > I2:
|
||||
raise ValueError("Invalid I1 I2 indices")
|
||||
|
||||
if J1 > J2:
|
||||
raise ValueError("Invalid J1 J2 indices")
|
||||
|
||||
if K1 > K2:
|
||||
raise ValueError("Invalid K1 K2 indices")
|
||||
|
||||
if I1 < 0 or I1 >= self.nx:
|
||||
raise ValueError("Invalid I1:%d" % I1)
|
||||
if I2 < 0 or I2 >= self.nx:
|
||||
raise ValueError("Invalid I2:%d" % I2)
|
||||
|
||||
if J1 < 0 or J1 >= self.ny:
|
||||
raise ValueError("Invalid J1:%d" % J1)
|
||||
if J2 < 0 or J2 >= self.ny:
|
||||
raise ValueError("Invalid J2:%d" % J2)
|
||||
|
||||
if K1 < 0 or K1 >= self.nz:
|
||||
raise ValueError("Invalid K1:%d" % K1)
|
||||
if K2 < 0 or K2 >= self.nz:
|
||||
raise ValueError("Invalid K2:%d" % K2)
|
||||
|
||||
if face in ["X","I"]:
|
||||
if I1 != I2:
|
||||
raise ValueError("For face:%s we must have I1 == I2" % face)
|
||||
|
||||
if face in ["Y","J"]:
|
||||
if J1 != J2:
|
||||
raise ValueError("For face:%s we must have J1 == J2" % face)
|
||||
|
||||
if face in ["Z","K"]:
|
||||
if K1 != K2:
|
||||
raise ValueError("For face:%s we must have K1 == K2" % face)
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
for K in range(K1,K2+1):
|
||||
if not self.hasLayer(K):
|
||||
self.addLayer(K)
|
||||
layer = self.__layer_map[K]
|
||||
segment = self.createSegment(I1,I2,J1,J2,face)
|
||||
if segment:
|
||||
layer.addSegment( segment )
|
||||
|
||||
|
||||
def getName(self):
|
||||
return self.__name
|
||||
|
||||
|
||||
def getNeighborCells(self):
|
||||
neighbor_cells = []
|
||||
for layer in self:
|
||||
neighbor_cells += layer.getNeighborCells()
|
||||
return neighbor_cells
|
||||
|
||||
|
||||
def getPolyline(self , k):
|
||||
layer = self[k]
|
||||
return layer.getPolyline( name = "Polyline[%s]" % self.getName() )
|
||||
|
||||
|
||||
def getIJPolyline(self , k):
|
||||
layer = self[k]
|
||||
return layer.getIJPolyline()
|
||||
|
||||
|
||||
def numLines( self , k):
|
||||
layer = self[k]
|
||||
return layer.numLines()
|
||||
|
||||
|
||||
def extendToFault(self , other_fault , k):
|
||||
polyline = self.getIJPolyline(k)
|
||||
|
||||
p0 = polyline[-2]
|
||||
p1 = polyline[-1]
|
||||
ray_dir = GeometryTools.lineToRay( p0 , p1 )
|
||||
intersections = GeometryTools.rayPolygonIntersections( p1 , ray_dir , other_fault.getIJPolyline(k))
|
||||
if intersections:
|
||||
p2 = intersections[0][1]
|
||||
return [p1 , (int(p2[0]) , int(p2[1])) ]
|
||||
|
||||
p0 = polyline[1]
|
||||
p1 = polyline[0]
|
||||
ray_dir = GeometryTools.lineToRay( p0 , p1 )
|
||||
intersections = GeometryTools.rayPolygonIntersections( p1 , ray_dir , other_fault.getIJPolyline(k))
|
||||
if intersections:
|
||||
if len(intersections) > 1:
|
||||
d_list = [ GeometryTools.distance( p1 , p[1] ) for p in intersections ]
|
||||
index = d_list.index( min(d_list) )
|
||||
else:
|
||||
index = 0
|
||||
p2 = intersections[index][1]
|
||||
return [p1 , (int(p2[0]) , int(p2[1])) ]
|
||||
|
||||
raise ValueError("The fault %s can not be extended to intersect with:%s in layer:%d" % (self.getName() , other_fault.getName() , k+1 ))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __rayIntersect(p0, p1 , polyline):
|
||||
ray_dir = GeometryTools.lineToRay( p0 , p1 )
|
||||
intersections = GeometryTools.rayPolygonIntersections( p1 , ray_dir , polyline)
|
||||
if intersections:
|
||||
if len(intersections) > 1:
|
||||
d_list = [ GeometryTools.distance( p1 , p[1] ) for p in intersections ]
|
||||
index = d_list.index( min(d_list) )
|
||||
else:
|
||||
index = 0
|
||||
p2 = intersections[index][1]
|
||||
return [p1 , p2]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def connectWithPolyline(self , polyline , k):
|
||||
"""
|
||||
"""
|
||||
if self.intersectsPolyline( polyline , k ):
|
||||
return None
|
||||
else:
|
||||
self_polyline = self.getPolyline( k )
|
||||
if len(self_polyline) > 0:
|
||||
return self_polyline.connect( polyline )
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def connect(self , target , k):
|
||||
if isinstance(target, Fault):
|
||||
polyline = target.getPolyline(k)
|
||||
else:
|
||||
polyline = target
|
||||
return self.connectWithPolyline( polyline , k )
|
||||
|
||||
|
||||
|
||||
def extendToPolyline(self , polyline , k):
|
||||
"""Extends the fault until it intersects @polyline in layer @k.
|
||||
|
||||
The return value is a list [(x1,y1) , (x2,y2)] where (x1,y1)
|
||||
is on the tip of the fault, and (x2,y2) is on the polyline. If
|
||||
the fault already intersects polyline None is returned, if no
|
||||
intersection is found a ValueError exception is raised.
|
||||
|
||||
The method will try four different strategies for finding an
|
||||
intersection between the extension of the fault and the
|
||||
polyline. Assume the fault and the polyline looks like:
|
||||
|
||||
|
||||
Polyline: ----------------------------------------------
|
||||
|
||||
+------------+ D
|
||||
| | |
|
||||
| +-------C
|
||||
B-----+
|
||||
|
|
||||
A
|
||||
|
||||
The algorithm will then try to intersect the following rays
|
||||
with the polyline, the first match will return:
|
||||
|
||||
1. (Pc , Pd)
|
||||
2. (Pb , Pa)
|
||||
3. (Pa , Pd)
|
||||
4. (Pd , Pa)
|
||||
|
||||
The fault object is not directed in any way; i.e. in the case
|
||||
both (Pc,Pd) and (Pb,Pa) intersects the polyline it is
|
||||
impossible to know which intersection is returned, without
|
||||
actually consulting the construction of the fault object.
|
||||
"""
|
||||
if self.intersectsPolyline(polyline , k):
|
||||
return None
|
||||
|
||||
fault_polyline = self.getPolyline( k )
|
||||
p0 = fault_polyline[-2]
|
||||
p1 = fault_polyline[-1]
|
||||
extension = self.__rayIntersect(p0 , p1 , polyline)
|
||||
if extension:
|
||||
return extension
|
||||
|
||||
p0 = fault_polyline[1]
|
||||
p1 = fault_polyline[0]
|
||||
extension = self.__rayIntersect(p0 , p1 , polyline)
|
||||
if extension:
|
||||
return extension
|
||||
|
||||
p0 = fault_polyline[0]
|
||||
p1 = fault_polyline[-1]
|
||||
extension = self.__rayIntersect(p0 , p1 , polyline)
|
||||
if extension:
|
||||
return extension
|
||||
|
||||
p0 = fault_polyline[-1]
|
||||
p1 = fault_polyline[0]
|
||||
extension = self.__rayIntersect(p0 , p1 , polyline)
|
||||
if extension:
|
||||
return extension
|
||||
|
||||
raise ValueError("The fault %s can not be extended to intersect with polyline:%s in layer:%d" % (self.getName() , polyline.getName() , k+1))
|
||||
|
||||
|
||||
|
||||
def intersectsPolyline(self , polyline , k):
|
||||
fault_line = self.getPolyline(k)
|
||||
return fault_line.intersects( polyline )
|
||||
|
||||
|
||||
def intersectsFault(self , other_fault , k):
|
||||
fault_line = other_fault.getPolyline(k)
|
||||
return self.intersectsPolyline( fault_line , k )
|
||||
|
||||
|
||||
def extendToFault(self , fault , k):
|
||||
fault_line = fault.getPolyline(k)
|
||||
return self.extendToPolyline(fault_line , k)
|
||||
|
||||
def extendToEdge(self, edge , k):
|
||||
if isinstance(edge , Fault):
|
||||
return self.extendToFault( edge , k )
|
||||
else:
|
||||
return self.extendToPolyline( edge , k )
|
||||
|
||||
|
||||
def extendToBBox(self , bbox , k , start = True):
|
||||
fault_polyline = self.getPolyline(k)
|
||||
if start:
|
||||
p0 = fault_polyline[1]
|
||||
p1 = fault_polyline[0]
|
||||
else:
|
||||
p0 = fault_polyline[-2]
|
||||
p1 = fault_polyline[-1]
|
||||
|
||||
ray_dir = GeometryTools.lineToRay(p0,p1)
|
||||
intersections = GeometryTools.rayPolygonIntersections( p1 , ray_dir , bbox)
|
||||
if intersections:
|
||||
p2 = intersections[0][1]
|
||||
if self.getName():
|
||||
name = "Extend:%s" % self.getName()
|
||||
else:
|
||||
name = None
|
||||
|
||||
return CPolyline( name = name , init_points = [(p1[0] , p1[1]) , p2])
|
||||
else:
|
||||
raise Exception("Logical error - must intersect with bounding box")
|
||||
|
||||
|
||||
def endJoin(self , other , k):
|
||||
fault_polyline = self.getPolyline(k)
|
||||
|
||||
if isinstance(other , Fault):
|
||||
other_polyline = other.getPolyline(k)
|
||||
else:
|
||||
other_polyline = other
|
||||
|
||||
return GeometryTools.joinPolylines( fault_polyline , other_polyline )
|
||||
|
||||
|
||||
|
||||
def connectPolylineOnto(self , polyline , k):
|
||||
if self.intersectsPolyline( polyline , k):
|
||||
return None
|
||||
|
||||
self_polyline = self.getPolyline(k)
|
||||
return polyline.connect( self_polyline )
|
||||
|
||||
|
||||
|
||||
def extendPolylineOnto(self , polyline , k):
|
||||
if self.intersectsPolyline( polyline , k):
|
||||
return None
|
||||
|
||||
if len(polyline) > 1:
|
||||
fault_polyline = self.getPolyline(k)
|
||||
ext1 = self.__rayIntersect( polyline[-2] , polyline[-1] , fault_polyline )
|
||||
ext2 = self.__rayIntersect( polyline[0] , polyline[1] , fault_polyline )
|
||||
|
||||
if ext1 and ext2:
|
||||
d1 = GeometryTools.distance( ext1[0] , ext1[1] )
|
||||
d2 = GeometryTools.distance( ext2[0] , ext2[1] )
|
||||
|
||||
if d1 < d2:
|
||||
return ext1
|
||||
else:
|
||||
return ext2
|
||||
|
||||
if ext1:
|
||||
return ext1
|
||||
else:
|
||||
return ext2
|
||||
else:
|
||||
raise ValueError("Polyline must have length >= 2")
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def intersectFaultRays(ray1 , ray2):
|
||||
p1,dir1 = ray1
|
||||
p2,dir2 = ray2
|
||||
if p1 == p2:
|
||||
return []
|
||||
|
||||
dx = p2[0] - p1[0]
|
||||
dy = p2[1] - p1[1]
|
||||
if dx != 0:
|
||||
if dir1[0] * dx <= 0 and dir2[0] * dx >= 0:
|
||||
raise ValueError("Rays will never intersect")
|
||||
|
||||
if dy != 0:
|
||||
if dir1[1] * dy <= 0 and dir2[1] * dy >= 0:
|
||||
raise ValueError("Rays will never intersect")
|
||||
|
||||
if dx*dy != 0:
|
||||
if dir1[0] != 0:
|
||||
xc = p2[0]
|
||||
yc = p1[1]
|
||||
else:
|
||||
xc = p1[0]
|
||||
yc = p2[1]
|
||||
|
||||
coord_list = [p1 , (xc,yc) , p2]
|
||||
else:
|
||||
coord_list = [p1,p2]
|
||||
|
||||
return coord_list
|
||||
|
||||
|
||||
@staticmethod
|
||||
def intRay(p1,p2):
|
||||
if p1 == p2:
|
||||
raise Exception("Can not form ray from coincident points")
|
||||
|
||||
if p1[0] == p2[0]:
|
||||
# Vertical line
|
||||
dx = 0
|
||||
if p2[1] > p1[1]:
|
||||
dy = 1
|
||||
elif p2[1] < p1[1]:
|
||||
dy = -1
|
||||
else:
|
||||
# Horizontal line
|
||||
if p2[1] != p1[1]:
|
||||
raise Exception("Invalid direction")
|
||||
|
||||
dy = 0
|
||||
if p2[0] > p1[0]:
|
||||
dx = 1
|
||||
else:
|
||||
dx = -1
|
||||
|
||||
return [p2 , (dx,dy)]
|
||||
|
||||
|
||||
|
||||
def getEndRays(self , k):
|
||||
polyline = self.getIJPolyline(k)
|
||||
|
||||
p0 = polyline[0]
|
||||
p1 = polyline[1]
|
||||
p2 = polyline[-2]
|
||||
p3 = polyline[-1]
|
||||
|
||||
return (Fault.intRay(p1,p0) , Fault.intRay(p2,p3))
|
||||
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def joinFaults(fault1 , fault2 , k):
|
||||
fault1_rays = fault1.getEndRays(k)
|
||||
fault2_rays = fault2.getEndRays(k)
|
||||
|
||||
if fault1.intersectsFault( fault2 , k ):
|
||||
return None
|
||||
|
||||
count = 0
|
||||
join = None
|
||||
try:
|
||||
join = Fault.intersectFaultRays( fault1_rays[0] , fault2_rays[0] )
|
||||
count += 1
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
join = Fault.intersectFaultRays( fault1_rays[0] , fault2_rays[1] )
|
||||
count += 1
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
join = Fault.intersectFaultRays( fault1_rays[1] , fault2_rays[0] )
|
||||
count += 1
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
join = Fault.intersectFaultRays( fault1_rays[1] , fault2_rays[1] )
|
||||
count += 1
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if count == 1:
|
||||
xy_list = []
|
||||
for ij in join:
|
||||
xyz = fault1.__grid.getNodeXYZ( ij[0] , ij[1] , k )
|
||||
xy_list.append( (xyz[0] , xyz[1]) )
|
||||
|
||||
return xy_list
|
||||
else:
|
||||
return fault1.endJoin( fault2 , k )
|
||||
#raise ValueError("Faults %s and %s could not be uniquely joined" % (fault1.getName() , fault2.getName()))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# Copyright (C) 2014 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'fault_block.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import ctypes
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.geo import Polyline, GeometryTools , CPolylineCollection
|
||||
from ert.util import DoubleVector , IntVector
|
||||
from ert.ecl import ECL_LIB
|
||||
|
||||
|
||||
class FaultBlockCell(object):
|
||||
def __init__(self , i,j,k ,x,y,z):
|
||||
|
||||
self.i = i
|
||||
self.j = j
|
||||
self.k = k
|
||||
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return "(%d,%d)" % (self.i , self.j)
|
||||
|
||||
|
||||
|
||||
class FaultBlock(BaseCClass):
|
||||
|
||||
def __init__(self , *args , **kwargs):
|
||||
raise NotImplementedError("Class can not be instantiated directly!")
|
||||
|
||||
|
||||
def __getitem__(self , index):
|
||||
if isinstance(index, int):
|
||||
if index < 0:
|
||||
index += len(self)
|
||||
|
||||
if 0 <= index < len(self):
|
||||
x = ctypes.c_double()
|
||||
y = ctypes.c_double()
|
||||
z = ctypes.c_double()
|
||||
|
||||
i = ctypes.c_int()
|
||||
j = ctypes.c_int()
|
||||
k = ctypes.c_int()
|
||||
|
||||
self.cNamespace().export_cell(self , index , ctypes.byref(i) , ctypes.byref(j) , ctypes.byref(k) , ctypes.byref(x) , ctypes.byref(y) , ctypes.byref(z))
|
||||
return FaultBlockCell( i.value , j.value , k.value , x.value , y.value , z.value )
|
||||
else:
|
||||
raise IndexError("Index:%d out of range: [0,%d)" % (index , len(self)))
|
||||
else:
|
||||
raise TypeError("Index:%s wrong type - integer expected")
|
||||
|
||||
def __str__(self):
|
||||
return "Block ID: %d" % self.getBlockID()
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return self.cNamespace().get_size( self )
|
||||
|
||||
def free(self):
|
||||
self.cNamespace().free(self)
|
||||
|
||||
def getCentroid(self):
|
||||
xc = self.cNamespace().get_xc( self )
|
||||
yc = self.cNamespace().get_yc( self )
|
||||
return (xc,yc)
|
||||
|
||||
|
||||
def countInside(self , polygon):
|
||||
"""
|
||||
Will count the number of points in block which are inside polygon.
|
||||
"""
|
||||
inside = 0
|
||||
for p in self:
|
||||
if GeometryTools.pointInPolygon( (p.x , p.y) , polygon ):
|
||||
inside += 1
|
||||
|
||||
return inside
|
||||
|
||||
|
||||
def getBlockID(self):
|
||||
return self.cNamespace().get_block_id(self)
|
||||
|
||||
|
||||
def assignToRegion(self , region_id):
|
||||
self.cNamespace().assign_to_region(self , region_id)
|
||||
|
||||
|
||||
def getRegionList(self):
|
||||
regionList = self.cNamespace().get_region_list(self)
|
||||
return regionList.copy()
|
||||
|
||||
def addCell(self, i , j):
|
||||
self.cNamespace().add_cell( self , i , j )
|
||||
|
||||
def getGlobalIndexList(self):
|
||||
return self.cNamespace().get_global_index_list( self )
|
||||
|
||||
|
||||
def getEdgePolygon(self):
|
||||
x_list = DoubleVector()
|
||||
y_list = DoubleVector()
|
||||
cell_list = IntVector()
|
||||
|
||||
self.cNamespace().trace_edge( self , x_list , y_list , cell_list )
|
||||
p = Polyline()
|
||||
for (x,y) in zip(x_list , y_list):
|
||||
p.addPoint(x,y)
|
||||
return p
|
||||
|
||||
|
||||
def containsPolyline(self, polyline):
|
||||
"""
|
||||
Will return true if at least one point from the polyline is inside the block.
|
||||
"""
|
||||
edge_polyline = self.getEdgePolygon()
|
||||
for p in polyline:
|
||||
if GeometryTools.pointInPolygon( p , edge_polyline ):
|
||||
return True
|
||||
else:
|
||||
edge_polyline.assertClosed()
|
||||
return GeometryTools.polylinesIntersect( edge_polyline , polyline )
|
||||
|
||||
|
||||
def getNeighbours(self, polylines = None , connected_only = True):
|
||||
"""
|
||||
Will return a list of FaultBlock instances which are in direct
|
||||
contact with this block.
|
||||
"""
|
||||
neighbour_id_list = IntVector()
|
||||
if polylines is None:
|
||||
polylines = CPolylineCollection()
|
||||
|
||||
self.cNamespace().get_neighbours( self , connected_only , polylines , neighbour_id_list )
|
||||
|
||||
parent_layer = self.getParentLayer()
|
||||
neighbour_list = []
|
||||
for id in neighbour_id_list:
|
||||
neighbour_list.append( parent_layer.getBlock( id ))
|
||||
return neighbour_list
|
||||
|
||||
|
||||
def getParentLayer(self):
|
||||
return self.parent()
|
||||
|
||||
|
||||
|
||||
cwrapper = CWrapper(ECL_LIB)
|
||||
CWrapper.registerObjectType("fault_block", FaultBlock)
|
||||
|
||||
FaultBlock.cNamespace().get_xc = cwrapper.prototype("double fault_block_get_xc(fault_block)")
|
||||
FaultBlock.cNamespace().get_yc = cwrapper.prototype("double fault_block_get_yc(fault_block)")
|
||||
FaultBlock.cNamespace().get_block_id = cwrapper.prototype("int fault_block_get_id(fault_block)")
|
||||
FaultBlock.cNamespace().get_size = cwrapper.prototype("int fault_block_get_size(fault_block)")
|
||||
FaultBlock.cNamespace().export_cell = cwrapper.prototype("void fault_block_export_cell(fault_block , int , int* , int* , int* , double* , double* , double*)")
|
||||
FaultBlock.cNamespace().assign_to_region = cwrapper.prototype("void fault_block_assign_to_region(fault_block , int)")
|
||||
FaultBlock.cNamespace().get_region_list = cwrapper.prototype("int_vector_ref fault_block_get_region_list(fault_block)")
|
||||
FaultBlock.cNamespace().add_cell = cwrapper.prototype("void fault_block_add_cell(fault_block, int , int)")
|
||||
FaultBlock.cNamespace().get_global_index_list = cwrapper.prototype("int_vector_ref fault_block_get_global_index_list(fault_block)")
|
||||
FaultBlock.cNamespace().trace_edge = cwrapper.prototype("void fault_block_trace_edge( fault_block, double_vector , double_vector , int_vector)")
|
||||
FaultBlock.cNamespace().get_neighbours = cwrapper.prototype("void fault_block_list_neighbours( fault_block , bool , geo_polygon_collection , int_vector)")
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# Copyright (C) 2014 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'fault_block_collection.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.ecl import ECL_LIB
|
||||
|
||||
|
||||
class FaultBlockCollection(BaseCClass):
|
||||
|
||||
def __init__(self , grid):
|
||||
c_pointer = self.cNamespace().alloc( grid)
|
||||
if c_pointer:
|
||||
super(FaultBlockCollection, self).__init__(c_pointer)
|
||||
else:
|
||||
raise ValueError("Invalid input - failed to create FaultBlockCollection")
|
||||
|
||||
# The underlying C implementation uses lazy evaluation and
|
||||
# needs to hold on to the grid reference. We therefor take
|
||||
# references to it here, to protect against premature garbage
|
||||
# collection.
|
||||
self.grid_ref = grid
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return self.cNamespace().num_layers(self)
|
||||
|
||||
|
||||
def __getitem__(self , index):
|
||||
"""
|
||||
@rtype: FaultBlockLayer
|
||||
"""
|
||||
if isinstance(index, int):
|
||||
if 0 <= index < len(self):
|
||||
return self.cNamespace().get_layer( self , index ).setParent(self)
|
||||
else:
|
||||
raise IndexError("Index:%d out of range [0,%d)" % (index , len(self)))
|
||||
else:
|
||||
raise TypeError("Index should be integer type")
|
||||
|
||||
|
||||
def getLayer(self , k):
|
||||
"""
|
||||
@rtype: FaultBlockLayer
|
||||
"""
|
||||
return self[k]
|
||||
|
||||
def free(self):
|
||||
self.cNamespace().free(self)
|
||||
|
||||
|
||||
def scanKeyword(self , fault_block_kw):
|
||||
ok = self.cNamespace().scan_keyword( self , fault_block_kw )
|
||||
if not ok:
|
||||
raise ValueError("The fault block keyword had wrong type/size")
|
||||
|
||||
|
||||
|
||||
cwrapper = CWrapper(ECL_LIB)
|
||||
CWrapper.registerObjectType("fault_block_collection", FaultBlockCollection)
|
||||
|
||||
|
||||
FaultBlockCollection.cNamespace().alloc = cwrapper.prototype("c_void_p fault_block_collection_alloc(ecl_grid )")
|
||||
FaultBlockCollection.cNamespace().free = cwrapper.prototype("void fault_block_collection_free(fault_block_collection)")
|
||||
FaultBlockCollection.cNamespace().num_layers = cwrapper.prototype("int fault_block_collection_num_layers(fault_block_collection)")
|
||||
FaultBlockCollection.cNamespace().get_layer = cwrapper.prototype("fault_block_layer_ref fault_block_collection_get_layer(fault_block_collection, int)")
|
||||
FaultBlockCollection.cNamespace().scan_keyword = cwrapper.prototype("bool fault_block_collection_scan_kw(fault_block_collection, ecl_kw)")
|
||||
@@ -0,0 +1,211 @@
|
||||
# Copyright (C) 2014 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'fault_block_layer.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from __future__ import print_function
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.ecl import ECL_LIB
|
||||
from ert.ecl import EclTypeEnum
|
||||
from ert.ecl.faults import Fault
|
||||
|
||||
class FaultBlockLayer(BaseCClass):
|
||||
|
||||
def __init__(self , grid , k):
|
||||
c_pointer = self.cNamespace().alloc( grid , k)
|
||||
if c_pointer:
|
||||
super(FaultBlockLayer, self).__init__(c_pointer)
|
||||
else:
|
||||
raise ValueError("Invalid input - failed to create FaultBlockLayer")
|
||||
|
||||
# The underlying C implementation uses lazy evaluation and
|
||||
# needs to hold on to the grid reference. We therefor take
|
||||
# references to it here, to protect against premature garbage
|
||||
# collection.
|
||||
self.grid_ref = grid
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return self.cNamespace().size(self)
|
||||
|
||||
def __getitem__(self , index):
|
||||
"""
|
||||
@rtype: FaultBlock
|
||||
"""
|
||||
if isinstance(index, int):
|
||||
if index < 0:
|
||||
index += len(self)
|
||||
|
||||
if 0 <= index < len(self):
|
||||
return self.cNamespace().iget_block( self , index ).setParent(self)
|
||||
else:
|
||||
raise IndexError("Index:%d out of range: [0,%d)" % (index , len(self)))
|
||||
elif isinstance(index,tuple):
|
||||
i,j = index
|
||||
if 0 <= i < self.grid_ref.getNX() and 0 <= j < self.grid_ref.getNY():
|
||||
geo_layer = self.getGeoLayer()
|
||||
block_id = geo_layer[i,j]
|
||||
if block_id == 0:
|
||||
raise ValueError("No fault block defined for location (%d,%d)" % (i,j))
|
||||
else:
|
||||
return self.getBlock( block_id )
|
||||
else:
|
||||
raise IndexError("Invalid i,j : (%d,%d)" % (i,j))
|
||||
else:
|
||||
raise TypeError("Index should be integer type")
|
||||
|
||||
def __contains__(self , block_id):
|
||||
return self.cNamespace().has_block( self , block_id)
|
||||
|
||||
|
||||
def scanKeyword(self , fault_block_kw):
|
||||
"""
|
||||
Will reorder the block ids, and ensure single connectedness. Assign block_id to zero blocks.
|
||||
"""
|
||||
ok = self.cNamespace().scan_keyword( self , fault_block_kw )
|
||||
if not ok:
|
||||
raise ValueError("The fault block keyword had wrong type/size: type:%s size:%d grid_size:%d" % (fault_block_kw.typeName() , len(fault_block_kw) , self.grid_ref.getGlobalSize()))
|
||||
|
||||
|
||||
def loadKeyword(self , fault_block_kw):
|
||||
"""
|
||||
Will load directly from keyword - without reorder; ignoring zero.
|
||||
"""
|
||||
ok = self.cNamespace().load_keyword( self , fault_block_kw )
|
||||
if not ok:
|
||||
raise ValueError("The fault block keyword had wrong type/size: type:%s size:%d grid_size:%d" % (fault_block_kw.typeName() , len(fault_block_kw) , self.grid_ref.getGlobalSize()))
|
||||
|
||||
|
||||
def getBlock(self , block_id):
|
||||
"""
|
||||
@rtype: FaultBlock
|
||||
"""
|
||||
if block_id in self:
|
||||
return self.cNamespace().get_block( self , block_id).setParent(self)
|
||||
else:
|
||||
raise KeyError("No blocks with ID:%d in this layer" % block_id)
|
||||
|
||||
|
||||
def deleteBlock(self , block_id):
|
||||
if block_id in self:
|
||||
self.cNamespace().del_block( self , block_id)
|
||||
else:
|
||||
raise KeyError("No blocks with ID:%d in this layer" % block_id)
|
||||
|
||||
def addBlock(self , block_id = None):
|
||||
if block_id is None:
|
||||
block_id = self.getNextID()
|
||||
|
||||
if block_id in self:
|
||||
raise KeyError("Layer already contains block with ID:%s" % block_id)
|
||||
else:
|
||||
return self.cNamespace().add_block( self , block_id).setParent(self)
|
||||
|
||||
def getNextID(self):
|
||||
return self.cNamespace().get_next_id( self )
|
||||
|
||||
|
||||
def getK(self):
|
||||
return self.cNamespace().getK( self )
|
||||
|
||||
|
||||
def free(self):
|
||||
self.cNamespace().free(self)
|
||||
|
||||
|
||||
def scanLayer( self , layer):
|
||||
self.cNamespace().scan_layer(self , layer)
|
||||
|
||||
|
||||
def insertBlockContent(self , block):
|
||||
self.cNamespace().insert_block_content(self , block)
|
||||
|
||||
def exportKeyword(self , kw):
|
||||
if len(kw) != self.grid_ref.getGlobalSize():
|
||||
raise ValueError("The size of the target keyword must be equal to the size of the grid. Got:%d Expected:%d" % (len(kw) , self.grid_ref.getGlobalSize()))
|
||||
|
||||
if kw.getEclType() != EclTypeEnum.ECL_INT_TYPE:
|
||||
raise TypeError("The target kewyord must be of integer type")
|
||||
|
||||
self.cNamespace().export_kw( self , kw )
|
||||
|
||||
|
||||
def addFaultBarrier(self , fault , link_segments = False):
|
||||
layer = self.getGeoLayer( )
|
||||
layer.addFaultBarrier( fault , self.getK() , link_segments )
|
||||
|
||||
|
||||
def addFaultLink(self , fault1 , fault2 ):
|
||||
if not fault1.intersectsFault( fault2 , self.getK()):
|
||||
layer = self.getGeoLayer()
|
||||
layer.addIJBarrier( fault1.extendToFault( fault2 , self.getK() ) )
|
||||
|
||||
|
||||
def joinFaults(self , fault1 , fault2):
|
||||
if not fault1.intersectsFault( fault2 , self.getK()):
|
||||
layer = self.getGeoLayer()
|
||||
try:
|
||||
layer.addIJBarrier( Fault.joinFaults( fault1 , fault2 , self.getK()) )
|
||||
except ValueError:
|
||||
print('Failed to join faults %s and %s' % (fault1.getName() , fault2.getName()))
|
||||
raise ValueError("")
|
||||
|
||||
|
||||
def addPolylineBarrier(self , polyline):
|
||||
layer = self.getGeoLayer()
|
||||
p0 = polyline[0]
|
||||
c0 = self.grid_ref.findCellCornerXY( p0[0] , p0[1] , self.getK() )
|
||||
i,j = self.grid_ref.findCellXY( p0[0] , p0[1] , self.getK() )
|
||||
print('%g,%g -> %d,%d %d' % (p0[0] , p0[1] , i,j,c0))
|
||||
for index in range(1,len(polyline)):
|
||||
p1 = polyline[index]
|
||||
c1 = self.grid_ref.findCellCornerXY( p1[0] , p1[1] , self.getK() )
|
||||
i,j = self.grid_ref.findCellXY( p1[0] , p1[1] , self.getK() )
|
||||
layer.addInterpBarrier( c0 , c1 )
|
||||
print('%g,%g -> %d,%d %d' % (p1[0] , p1[1] , i,j,c1))
|
||||
print('Adding barrier %d -> %d' % (c0 , c1))
|
||||
c0 = c1
|
||||
|
||||
|
||||
def getGeoLayer(self):
|
||||
"""Returns the underlying geometric layer."""
|
||||
return self.cNamespace().get_layer( self )
|
||||
|
||||
|
||||
def cellContact(self , p1 , p2):
|
||||
layer = self.getGeoLayer()
|
||||
return layer.cellContact(p1,p2)
|
||||
|
||||
|
||||
|
||||
cwrapper = CWrapper(ECL_LIB)
|
||||
CWrapper.registerObjectType("fault_block_layer", FaultBlockLayer)
|
||||
|
||||
|
||||
FaultBlockLayer.cNamespace().alloc = cwrapper.prototype("c_void_p fault_block_layer_alloc(ecl_grid , int)")
|
||||
FaultBlockLayer.cNamespace().free = cwrapper.prototype("void fault_block_layer_free(fault_block_layer)")
|
||||
FaultBlockLayer.cNamespace().size = cwrapper.prototype("int fault_block_layer_get_size(fault_block_layer)")
|
||||
FaultBlockLayer.cNamespace().iget_block = cwrapper.prototype("fault_block_ref fault_block_layer_iget_block(fault_block_layer, int)")
|
||||
FaultBlockLayer.cNamespace().add_block = cwrapper.prototype("fault_block_ref fault_block_layer_add_block(fault_block_layer, int)")
|
||||
FaultBlockLayer.cNamespace().get_block = cwrapper.prototype("fault_block_ref fault_block_layer_get_block(fault_block_layer, int)")
|
||||
FaultBlockLayer.cNamespace().del_block = cwrapper.prototype("void fault_block_layer_del_block(fault_block_layer, int)")
|
||||
FaultBlockLayer.cNamespace().has_block = cwrapper.prototype("bool fault_block_layer_has_block(fault_block_layer, int)")
|
||||
FaultBlockLayer.cNamespace().scan_keyword = cwrapper.prototype("bool fault_block_layer_scan_kw(fault_block_layer, ecl_kw)")
|
||||
FaultBlockLayer.cNamespace().load_keyword = cwrapper.prototype("bool fault_block_layer_load_kw(fault_block_layer, ecl_kw)")
|
||||
FaultBlockLayer.cNamespace().getK = cwrapper.prototype("int fault_block_layer_get_k(fault_block_layer)")
|
||||
FaultBlockLayer.cNamespace().get_next_id = cwrapper.prototype("int fault_block_layer_get_next_id(fault_block_layer)")
|
||||
FaultBlockLayer.cNamespace().scan_layer = cwrapper.prototype("void fault_block_layer_scan_layer( fault_block_layer , layer)")
|
||||
FaultBlockLayer.cNamespace().insert_block_content = cwrapper.prototype("void fault_block_layer_insert_block_content( fault_block_layer , fault_block)")
|
||||
FaultBlockLayer.cNamespace().export_kw = cwrapper.prototype("bool fault_block_layer_export( fault_block_layer , ecl_kw )")
|
||||
FaultBlockLayer.cNamespace().get_layer = cwrapper.prototype("layer_ref fault_block_layer_get_layer( fault_block_layer )")
|
||||
@@ -0,0 +1,128 @@
|
||||
# Copyright (C) 2014 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'fault_collection.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
import re
|
||||
|
||||
from .fault import Fault
|
||||
from ert.ecl import EclGrid
|
||||
|
||||
comment_regexp = re.compile("--.*")
|
||||
|
||||
def dequote(s):
|
||||
if s[0] in ["'" , '"']:
|
||||
if s[0] == s[-1]:
|
||||
return s[1:-1]
|
||||
else:
|
||||
raise ValueError("Quote fuckup")
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
class FaultCollection(object):
|
||||
def __init__(self , grid = None , *file_list):
|
||||
self.__fault_list = []
|
||||
self.__fault_map = {}
|
||||
|
||||
if grid is not None:
|
||||
if not isinstance(grid , EclGrid):
|
||||
raise ValueError("When supplying a list of files to load - you must have a grid")
|
||||
|
||||
for file in file_list:
|
||||
self.load(grid , file)
|
||||
|
||||
|
||||
|
||||
def __contains__(self , fault_name):
|
||||
return self.__fault_map.has_key( fault_name )
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__fault_list)
|
||||
|
||||
|
||||
def __getitem__(self , index):
|
||||
if isinstance(index , str):
|
||||
return self.__fault_map[index]
|
||||
elif isinstance(index , int):
|
||||
return self.__fault_list[index]
|
||||
else:
|
||||
raise TypeError("Argument must be fault name or number")
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.__fault_list)
|
||||
|
||||
|
||||
def getGrid(self):
|
||||
return self.__grid
|
||||
|
||||
|
||||
def getFault(self , name):
|
||||
return self.__getitem__(name)
|
||||
|
||||
|
||||
def hasFault(self , fault_name):
|
||||
return fault_name in self
|
||||
|
||||
|
||||
def addFault(self, fault):
|
||||
self.__fault_map[fault.getName()] = fault
|
||||
self.__fault_list.append( fault )
|
||||
|
||||
|
||||
def splitLine(self , line):
|
||||
tmp = line.split()
|
||||
if not tmp[-1] == "/":
|
||||
raise ValueError("Line:%s does not end with /" % line)
|
||||
|
||||
if len(tmp) != 9:
|
||||
raise ValueError("Line:%s not correct number of items" % line)
|
||||
|
||||
fault_name = dequote(tmp[0])
|
||||
I1 = int(tmp[1]) - 1
|
||||
I2 = int(tmp[2]) - 1
|
||||
J1 = int(tmp[3]) - 1
|
||||
J2 = int(tmp[4]) - 1
|
||||
K1 = int(tmp[5]) - 1
|
||||
K2 = int(tmp[6]) - 1
|
||||
face = dequote(tmp[7])
|
||||
|
||||
return (fault_name , I1,I2,J1,J2,K1,K2 , face)
|
||||
|
||||
|
||||
|
||||
def loadFaults(self , grid , fileH):
|
||||
for line in fileH:
|
||||
line = comment_regexp.sub("" , line)
|
||||
line = line.strip()
|
||||
if line == "/":
|
||||
break
|
||||
|
||||
if line:
|
||||
(name , I1 , I2 , J1 , J2 , K1 , K2 , face) = self.splitLine( line )
|
||||
if not self.hasFault(name):
|
||||
fault = Fault( grid , name)
|
||||
self.addFault( fault )
|
||||
else:
|
||||
fault = self.getFault(name)
|
||||
|
||||
fault.addRecord(I1 , I2 , J1 , J2 , K1 , K2 , face)
|
||||
|
||||
|
||||
def load(self , grid , file_name):
|
||||
with open(file_name) as fileH:
|
||||
for line in fileH:
|
||||
if line.startswith("FAULTS"):
|
||||
self.loadFaults(grid , fileH)
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
# Copyright (C) 2014 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'fault_line.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
from ert.util import DoubleVector,stat
|
||||
from ert.geo import CPolyline
|
||||
from .fault_segments import FaultSegment
|
||||
|
||||
|
||||
|
||||
def cmpIndexPair(p1,p2):
|
||||
if p1[0] == p2[0]:
|
||||
return cmp(p1[1] , p2[1])
|
||||
else:
|
||||
return cmp(p1[0] , p2[0])
|
||||
|
||||
|
||||
|
||||
class FaultLine(object):
|
||||
def __init__(self , grid , k):
|
||||
self.__grid = grid
|
||||
self.__k = k
|
||||
self.__segment_list = []
|
||||
self.__polyline = None
|
||||
self.__ijpolyline = None
|
||||
self.__neighborCells = None
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__segment_list)
|
||||
|
||||
|
||||
def __getitem__(self , index):
|
||||
return self.__segment_list[index]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.__segment_list)
|
||||
|
||||
|
||||
def verify(self):
|
||||
if len(self.__segment_list) > 1:
|
||||
current = self.__segment_list[0]
|
||||
for next_segment in self.__segment_list[1:]:
|
||||
if not current.getC2( ) == next_segment.getC1( ):
|
||||
sys.stdout.write("Current: %d ---- %d \n" % (current.getC1() , current.getC2()))
|
||||
sys.stdout.write("Next : %d ---- %d \n" % (next_segment.getC1() , next_segment.getC2()))
|
||||
assert current.getC2( ) == next_segment.getC1( )
|
||||
current = next_segment
|
||||
|
||||
|
||||
def tryAppend(self , segment):
|
||||
if len(self.__segment_list) > 0:
|
||||
tail = self.__segment_list[-1]
|
||||
if tail.getC2() != segment.getC1():
|
||||
if len(self.__segment_list) == 1:
|
||||
if tail.getC2() == segment.getC2():
|
||||
segment.swap()
|
||||
else:
|
||||
tail.swap()
|
||||
if tail.getC2() == segment.getC2():
|
||||
segment.swap()
|
||||
else:
|
||||
segment.swap()
|
||||
|
||||
if not tail.getC2( ) == segment.getC1( ):
|
||||
return False
|
||||
|
||||
self.__segment_list.append( segment )
|
||||
self.__polyline = None
|
||||
self.__ijpolyline = None
|
||||
return True
|
||||
|
||||
|
||||
def getK(self):
|
||||
return self.__k
|
||||
|
||||
|
||||
def __initIJPolyline(self):
|
||||
pl = []
|
||||
nx = self.__grid.getNX()
|
||||
ny = self.__grid.getNY()
|
||||
for segment in self:
|
||||
corner = segment.getC1( )
|
||||
i = corner % (nx + 1)
|
||||
j = corner / (nx + 1)
|
||||
pl.append( (i,j) )
|
||||
|
||||
segment = self[-1]
|
||||
corner = segment.getC2( )
|
||||
i = corner % (nx + 1)
|
||||
j = corner / (nx + 1)
|
||||
pl.append( (i,j) )
|
||||
|
||||
self.__ijpolyline = pl
|
||||
|
||||
|
||||
|
||||
def __initPolyline(self):
|
||||
pl = CPolyline()
|
||||
for (i,j) in self.getIJPolyline():
|
||||
x,y,z = self.__grid.getNodeXYZ(i,j,self.__k)
|
||||
pl.addPoint( x, y)
|
||||
self.__polyline = pl
|
||||
|
||||
|
||||
|
||||
def getPolyline(self):
|
||||
if self.__polyline is None:
|
||||
self.__initPolyline()
|
||||
return self.__polyline
|
||||
|
||||
|
||||
def getIJPolyline(self):
|
||||
if self.__ijpolyline is None:
|
||||
self.__initIJPolyline()
|
||||
return self.__ijpolyline
|
||||
|
||||
|
||||
|
||||
def __initNeighborCells(self):
|
||||
self.__neighborCells = []
|
||||
nx = self.__grid.getNX()
|
||||
ny = self.__grid.getNY()
|
||||
k = self.__k
|
||||
|
||||
for segment in self:
|
||||
(j1,i1) = divmod(segment.getC1() , (nx + 1 ))
|
||||
(j2,i2) = divmod(segment.getC2() , (nx + 1 ))
|
||||
|
||||
if j1 > j2:
|
||||
j1,j2 = j2,j1
|
||||
|
||||
if i1 > i2:
|
||||
i1,i2 = i2,i1
|
||||
|
||||
|
||||
if i1 == i2:
|
||||
i = i1
|
||||
for j in range(j1 , j2):
|
||||
g2 = i + j * nx + k * nx*ny
|
||||
if i == 0:
|
||||
g1 = -1
|
||||
else:
|
||||
g1 = g2 - 1
|
||||
|
||||
if i == nx:
|
||||
g2 = -1
|
||||
|
||||
self.__neighborCells.append( (g1,g2) )
|
||||
elif j1 == j2:
|
||||
j = j1
|
||||
for i in range(i1,i2):
|
||||
g2 = i + j * nx + k * nx*ny
|
||||
if j == 0:
|
||||
g1 = -1
|
||||
else:
|
||||
g1 = g2 - nx
|
||||
|
||||
if j == ny:
|
||||
g2 = -1
|
||||
|
||||
self.__neighborCells.append( (g1,g2) )
|
||||
else:
|
||||
raise Exception("Internal error: found fault segment with variation in two directions")
|
||||
self.__neighborCells.sort( cmpIndexPair )
|
||||
|
||||
|
||||
def getNeighborCells(self):
|
||||
if self.__neighborCells is None:
|
||||
self.__initNeighborCells()
|
||||
|
||||
return self.__neighborCells
|
||||
|
||||
|
||||
def center(self):
|
||||
xlist = DoubleVector( )
|
||||
ylist = DoubleVector( )
|
||||
for segment in self:
|
||||
C1 = segment.getC1()
|
||||
C2 = segment.getC2()
|
||||
(J1 , I1) = divmod(C1 , self.__grid.getNX() + 1)
|
||||
(J2 , I2) = divmod(C2 , self.__grid.getNX() + 1)
|
||||
|
||||
(x1,y1,z) = self.__grid.getNodePos( I1 , J1 , self.__k )
|
||||
(x2,y2,z) = self.__grid.getNodePos( I2 , J2 , self.__k )
|
||||
|
||||
xlist.append( x1 )
|
||||
xlist.append( x2 )
|
||||
|
||||
ylist.append( y1 )
|
||||
ylist.append( y2 )
|
||||
|
||||
|
||||
N = len(xlist)
|
||||
return (xlist.elementSum()/N , ylist.elementSum()/N )
|
||||
|
||||
|
||||
|
||||
def reverse(self):
|
||||
reverse_list = reversed( self.__segment_list )
|
||||
self.__segment_list = []
|
||||
for segment in reverse_list:
|
||||
C1 = segment.getC1()
|
||||
C2 = segment.getC2()
|
||||
|
||||
rseg = FaultSegment(C2 , C1)
|
||||
self.tryAppend( rseg )
|
||||
|
||||
|
||||
def startPoint(self):
|
||||
pl = self.getPolyline()
|
||||
return pl[0]
|
||||
|
||||
def endPoint(self):
|
||||
pl = self.getPolyline()
|
||||
return pl[-1]
|
||||
|
||||
def dump(self):
|
||||
print('-----------------------------------------------------------------')
|
||||
for segment in self:
|
||||
C1 = segment.getC1()
|
||||
C2 = segment.getC2()
|
||||
(J1 , I1) = divmod(C1 , self.__grid.getNX() + 1)
|
||||
(J2 , I2) = divmod(C2 , self.__grid.getNX() + 1)
|
||||
print('[Corner:%5d IJ:(%3d,%d)] -> [Corner:%5d IJ:(%3d,%d)]'
|
||||
% (C1, I1, J1, C2, I2, J2))
|
||||
@@ -0,0 +1,151 @@
|
||||
# Copyright (C) 2014. Statoil ASA, Norway.
|
||||
#
|
||||
# This file is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
class FaultSegment(object):
|
||||
def __init__(self , C1 , C2 ):
|
||||
self.__C1 = C1
|
||||
self.__C2 = C2
|
||||
self.__next_segment = None
|
||||
|
||||
|
||||
def __eq__(self , other):
|
||||
if self.__C1 == other.__C1 and self.__C2 == other.__C2:
|
||||
return True
|
||||
elif self.__C1 == other.__C2 and self.__C2 == other.__C1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __hash__(self):
|
||||
return hash(hash(self.__C1) + hash(self.__C2) + hash(self.__next_segment))
|
||||
|
||||
def getCorners(self):
|
||||
return (self.__C1 , self.__C2)
|
||||
|
||||
def joins(self , other):
|
||||
if self.__C1 == other.__C1:
|
||||
return True
|
||||
if self.__C1 == other.__C2:
|
||||
return True
|
||||
if self.__C2 == other.__C1:
|
||||
return True
|
||||
if self.__C2 == other.__C2:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def getC1(self):
|
||||
return self.__C1
|
||||
|
||||
def getC2(self):
|
||||
return self.__C2
|
||||
|
||||
def swap(self):
|
||||
C1 = self.__C1
|
||||
self.__C1 = self.__C2
|
||||
self.__C2 = C1
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return "%d -> %d" % (self.__C1 , self.__C2)
|
||||
|
||||
|
||||
|
||||
class SegmentMap(object):
|
||||
def __init__(self):
|
||||
self.__segment_map = {}
|
||||
self.__count_map = {}
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__segment_map)
|
||||
|
||||
def __str__(self):
|
||||
return self.__segment_map.__str__()
|
||||
|
||||
def verify(self):
|
||||
for (C, count) in self.__count_map.iteritems():
|
||||
if count > 0:
|
||||
d = self.__segment_map[C]
|
||||
if len(d) != count:
|
||||
print('CornerPoint:%d count:%d len(d):%d map:%s' % (C , count , len(d) , d))
|
||||
assert len(d) == count
|
||||
else:
|
||||
assert self.__segment_map.get(C) is None
|
||||
|
||||
|
||||
def addSegment(self , segment):
|
||||
(C1,C2) = segment.getCorners()
|
||||
if not self.__segment_map.has_key(C1):
|
||||
self.__segment_map[C1] = {}
|
||||
self.__count_map[C1] = 0
|
||||
if not self.__segment_map.has_key(C2):
|
||||
self.__segment_map[C2] = {}
|
||||
self.__count_map[C2] = 0
|
||||
|
||||
if not self.__segment_map[C1].has_key(C2):
|
||||
self.__segment_map[C1][C2] = segment
|
||||
self.__count_map[C1] += 1
|
||||
|
||||
if not self.__segment_map[C2].has_key(C1):
|
||||
self.__segment_map[C2][C1] = segment
|
||||
self.__count_map[C2] += 1
|
||||
|
||||
|
||||
|
||||
def delSegment(self , segment):
|
||||
(C1,C2) = segment.getCorners()
|
||||
self.__count_map[C1] -= 1
|
||||
self.__count_map[C2] -= 1
|
||||
del self.__segment_map[C1][C2]
|
||||
del self.__segment_map[C2][C1]
|
||||
|
||||
if len(self.__segment_map[C1]) == 0:
|
||||
del self.__segment_map[C1]
|
||||
|
||||
if len(self.__segment_map[C2]) == 0:
|
||||
del self.__segment_map[C2]
|
||||
|
||||
|
||||
def popStart(self):
|
||||
end_segments = []
|
||||
for (C, count) in self.__count_map.iteritems():
|
||||
if count == 1:
|
||||
end_segments.append(self.__segment_map[C].values()[0])
|
||||
|
||||
start_segment = end_segments[0]
|
||||
self.delSegment( start_segment )
|
||||
return start_segment
|
||||
|
||||
def popNext(self , segment):
|
||||
(C1,C2) = segment.getCorners()
|
||||
if self.__count_map[C1] >= 1:
|
||||
next_segment = self.__segment_map[C1].values()[0]
|
||||
elif self.__count_map[C2] >= 1:
|
||||
next_segment = self.__segment_map[C2].values()[0]
|
||||
else:
|
||||
next_segment = None
|
||||
|
||||
if next_segment:
|
||||
self.delSegment( next_segment )
|
||||
return next_segment
|
||||
|
||||
|
||||
def printContent(self):
|
||||
for d in self.__segment_map.values():
|
||||
for (C,S) in d.iteritems():
|
||||
print(S)
|
||||
@@ -0,0 +1,256 @@
|
||||
# Copyright (C) 2014 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'layer.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import ctypes
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.ecl import ECL_LIB
|
||||
from ert.util import IntVector
|
||||
|
||||
|
||||
class Layer(BaseCClass):
|
||||
|
||||
def __init__(self , nx , ny):
|
||||
c_pointer = self.cNamespace().alloc( nx , ny )
|
||||
if c_pointer:
|
||||
super( Layer , self).__init__(c_pointer)
|
||||
else:
|
||||
raise ValueError("Invalid input - no Layer object created")
|
||||
|
||||
@classmethod
|
||||
def copy(cls , src):
|
||||
layer = Layer( src.getNX() , src.getNY())
|
||||
Layer.cNamespace().copy( layer , src )
|
||||
return layer
|
||||
|
||||
|
||||
def __assertIJ(self , i,j):
|
||||
if i < 0 or i >= self.getNX():
|
||||
raise ValueError("Invalid layer i:%d" % i)
|
||||
|
||||
if j < 0 or j >= self.getNY():
|
||||
raise ValueError("Invalid layer j:%d" % j)
|
||||
|
||||
|
||||
|
||||
def __unpackIndex(self , index):
|
||||
try:
|
||||
(i,j) = index
|
||||
except TypeError:
|
||||
raise ValueError("Index:%s is invalid - must have two integers" % str(index))
|
||||
|
||||
self.__assertIJ(i,j)
|
||||
|
||||
return (i,j)
|
||||
|
||||
|
||||
|
||||
def __setitem__(self , index , value):
|
||||
(i,j) = self.__unpackIndex(index)
|
||||
self.cNamespace().set_cell(self , i , j , value )
|
||||
|
||||
def activeCell(self , i,j):
|
||||
self.__assertIJ(i,j)
|
||||
return self.cNamespace().active_cell(self , i , j)
|
||||
|
||||
|
||||
def updateActive(self , grid , k):
|
||||
if grid.getNX() != self.getNX():
|
||||
raise ValueError("NX dimension mismatch. Grid:%d layer:%d" % (grid.getNX() , self.getNX()))
|
||||
|
||||
if grid.getNY() != self.getNY():
|
||||
raise ValueError("NY dimension mismatch. Grid:%d layer:%d" % (grid.getNY() , self.getNY()))
|
||||
|
||||
if k >= grid.getNZ():
|
||||
raise ValueError("K value invalid: Grid range [0,%d)" % grid.getNZ())
|
||||
|
||||
self.cNamespace().update_active(self , grid , k)
|
||||
|
||||
|
||||
|
||||
|
||||
def __getitem__(self , index):
|
||||
(i,j) = self.__unpackIndex(index)
|
||||
return self.cNamespace().get_cell(self , i , j)
|
||||
|
||||
def bottomBarrier(self , i,j):
|
||||
self.__assertIJ(i,j)
|
||||
return self.cNamespace().get_bottom_barrier(self , i , j)
|
||||
|
||||
def leftBarrier(self , i,j):
|
||||
self.__assertIJ(i,j)
|
||||
return self.cNamespace().get_left_barrier(self , i , j)
|
||||
|
||||
def getNX(self):
|
||||
return self.cNamespace().get_nx(self)
|
||||
|
||||
def getNY(self):
|
||||
return self.cNamespace().get_ny(self)
|
||||
|
||||
def free(self):
|
||||
self.cNamespace().free(self)
|
||||
|
||||
def cellContact(self , p1 , p2):
|
||||
i1,j1 = p1
|
||||
i2,j2 = p2
|
||||
|
||||
if not 0 <= i1 < self.getNX():
|
||||
raise IndexError("Invalid i1:%d" % i1)
|
||||
|
||||
if not 0 <= i2 < self.getNX():
|
||||
raise IndexError("Invalid i2:%d" % i2)
|
||||
|
||||
if not 0 <= j1 < self.getNY():
|
||||
raise IndexError("Invalid i1:%d" % j1)
|
||||
|
||||
if not 0 <= j2 < self.getNY():
|
||||
raise IndexError("Invalid i2:%d" % j2)
|
||||
|
||||
return self.cNamespace().cell_contact(self , i1,j1,i2,j2)
|
||||
|
||||
|
||||
def addInterpBarrier(self , c1 , c2):
|
||||
self.cNamespace().add_interp_barrier( self , c1 , c2 )
|
||||
|
||||
|
||||
def addPolylineBarrier(self , polyline , grid , k):
|
||||
if len(polyline) > 1:
|
||||
for i in range(len(polyline) - 1):
|
||||
x1,y1 = polyline[i]
|
||||
x2,y2 = polyline[i + 1]
|
||||
|
||||
c1 = grid.findCellCornerXY( x1 , y1 , k )
|
||||
c2 = grid.findCellCornerXY( x2 , y2 , k )
|
||||
|
||||
self.addInterpBarrier( c1 , c2 )
|
||||
|
||||
|
||||
|
||||
def addFaultBarrier(self , fault , K , link_segments = True ):
|
||||
fault_layer = fault[K]
|
||||
num_lines = len(fault_layer)
|
||||
for index , fault_line in enumerate(fault_layer):
|
||||
for segment in fault_line:
|
||||
c1 , c2 = segment.getCorners()
|
||||
self.cNamespace().add_barrier(self , c1 , c2)
|
||||
|
||||
if index < num_lines - 1:
|
||||
next_line = fault_layer[index + 1]
|
||||
next_segment = next_line[0]
|
||||
next_c1 , next_c2 = next_segment.getCorners()
|
||||
|
||||
if link_segments:
|
||||
self.addInterpBarrier( c2 , next_c1 )
|
||||
|
||||
|
||||
def addIJBarrier(self , ij_list):
|
||||
if len(ij_list) < 2:
|
||||
raise ValueError("Must have at least two (i,j) points")
|
||||
|
||||
nx = self.getNX()
|
||||
ny = self.getNY()
|
||||
p1 = ij_list[0]
|
||||
i1,j1 = p1
|
||||
for p2 in ij_list[1:]:
|
||||
i2,j2 = p2
|
||||
if i1 == i2 or j1 == j2:
|
||||
if not 0 <= i2 <= nx:
|
||||
raise ValueError("i value:%d invalid. Valid range: [0,%d] " % (i , i2))
|
||||
|
||||
if not 0 <= j2 <= ny:
|
||||
raise ValueError("i value:%d invalid. Valid range: [0,%d] " % (j , j2))
|
||||
|
||||
Layer.cNamespace().add_ijbarrier( self , i1 , j1 , i2 , j2 )
|
||||
p1 = p2
|
||||
i1,j1 = p1
|
||||
else:
|
||||
raise ValueError("Must have i1 == i2 or j1 == j2")
|
||||
|
||||
|
||||
def cellSum(self):
|
||||
return Layer.cNamespace().cell_sum( self )
|
||||
|
||||
|
||||
def clearCells(self):
|
||||
"""
|
||||
Will reset all cell and edge values to zero. Barriers will be left
|
||||
unchanged.
|
||||
"""
|
||||
Layer.cNamespace().clear_cells( self )
|
||||
|
||||
|
||||
def assign(self , value):
|
||||
"""
|
||||
Will set the cell value to @value in all cells. Barriers will not be changed
|
||||
"""
|
||||
Layer.cNamespace().assign( self , value )
|
||||
|
||||
|
||||
def updateConnected(self , ij , new_value , org_value = None):
|
||||
"""
|
||||
Will update cell value of all cells in contact with cell ij to the
|
||||
value @new_value. If org_value is not supplied, the current
|
||||
value in cell ij is used.
|
||||
"""
|
||||
if org_value is None:
|
||||
org_value = self[ij]
|
||||
|
||||
if self[ij] == org_value:
|
||||
Layer.cNamespace().update_connected( self , ij[0] , ij[1] , org_value , new_value )
|
||||
else:
|
||||
raise ValueError("Cell %s is not equal to %d \n" % (ij , org_value))
|
||||
|
||||
|
||||
def cellsEqual(self , value):
|
||||
"""
|
||||
Will return a list [(i1,j1),(i2,j2) , ...(in,jn)] of all cells with value @value.
|
||||
"""
|
||||
i_list = IntVector()
|
||||
j_list = IntVector()
|
||||
Layer.cNamespace().cells_equal( self , value , i_list , j_list)
|
||||
ij_list= []
|
||||
for (i,j) in zip(i_list , j_list):
|
||||
ij_list.append( (i,j) )
|
||||
return ij_list
|
||||
|
||||
|
||||
def countEqual(self , value):
|
||||
return Layer.cNamespace().count_equal( self , value )
|
||||
|
||||
|
||||
|
||||
cwrapper = CWrapper(ECL_LIB)
|
||||
CWrapper.registerObjectType("layer", Layer)
|
||||
Layer.cNamespace().alloc = cwrapper.prototype("c_void_p layer_alloc(int, int)")
|
||||
Layer.cNamespace().copy = cwrapper.prototype("void layer_memcpy(layer , layer)")
|
||||
Layer.cNamespace().free = cwrapper.prototype("void layer_free(layer)")
|
||||
Layer.cNamespace().get_nx = cwrapper.prototype("int layer_get_nx(layer)")
|
||||
Layer.cNamespace().get_ny = cwrapper.prototype("int layer_get_ny(layer)")
|
||||
Layer.cNamespace().set_cell = cwrapper.prototype("void layer_iset_cell_value(layer , int , int , int)")
|
||||
Layer.cNamespace().get_cell = cwrapper.prototype("int layer_iget_cell_value(layer , int , int )")
|
||||
Layer.cNamespace().get_bottom_barrier = cwrapper.prototype("bool layer_iget_bottom_barrier(layer , int , int )")
|
||||
Layer.cNamespace().get_left_barrier = cwrapper.prototype("bool layer_iget_left_barrier(layer , int , int )")
|
||||
Layer.cNamespace().cell_contact = cwrapper.prototype("bool layer_cell_contact(layer , int , int , int , int)")
|
||||
Layer.cNamespace().add_barrier = cwrapper.prototype("void layer_add_barrier(layer , int , int)")
|
||||
Layer.cNamespace().add_ijbarrier = cwrapper.prototype("void layer_add_ijbarrier(layer , int , int, int , int)")
|
||||
Layer.cNamespace().add_interp_barrier = cwrapper.prototype("void layer_add_interp_barrier(layer , int , int)")
|
||||
Layer.cNamespace().clear_cells = cwrapper.prototype("void layer_clear_cells(layer)")
|
||||
Layer.cNamespace().assign = cwrapper.prototype("void layer_assign(layer , int)")
|
||||
Layer.cNamespace().cell_sum = cwrapper.prototype("int layer_get_cell_sum(layer)")
|
||||
Layer.cNamespace().update_connected = cwrapper.prototype("void layer_update_connected_cells(layer,int,int,int,int)")
|
||||
Layer.cNamespace().cells_equal = cwrapper.prototype("void layer_cells_equal( layer, int,int_vector,int_vector)")
|
||||
Layer.cNamespace().count_equal = cwrapper.prototype("int layer_count_equal( layer, int)")
|
||||
Layer.cNamespace().active_cell = cwrapper.prototype("bool layer_iget_active( layer, int,int)")
|
||||
Layer.cNamespace().update_active = cwrapper.prototype("bool layer_update_active( layer, ecl_grid , int)")
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'fortio.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
"""
|
||||
Module to support transparent binary IO of Fortran created files.
|
||||
|
||||
Fortran is a a funny language; when writing binary blobs of data to
|
||||
file the Fortran runtime will silently add a header and footer around
|
||||
the date. The Fortran code:
|
||||
|
||||
integer array(100)
|
||||
write(unit) array
|
||||
|
||||
it actually writes a head and tail in addition to the actual
|
||||
data. The header and tail is a 4 byte integer, which value is the
|
||||
number of bytes in the immediately following record. I.e. what is
|
||||
actually found on disk after the Fortran code above is:
|
||||
|
||||
| 400 | array ...... | 400 |
|
||||
|
||||
The fortio.c file implements the fortio_type C structure which can be
|
||||
used to read and write these structures transparently. The current
|
||||
python module is a minimal wrapping of this datastructure; mainly to
|
||||
support passing of FortIO handles to the underlying C functions. A
|
||||
more extensive wrapping of the fortio implementation would be easy.
|
||||
"""
|
||||
import ctypes
|
||||
import os
|
||||
import sys
|
||||
from cwrap import BaseCClass
|
||||
from ert.ecl import EclPrototype
|
||||
|
||||
|
||||
class FortIO(BaseCClass):
|
||||
TYPE_NAME = "fortio"
|
||||
|
||||
READ_MODE = 1
|
||||
WRITE_MODE = 2
|
||||
READ_AND_WRITE_MODE = 3
|
||||
APPEND_MODE = 4
|
||||
|
||||
_open_reader = EclPrototype("void* fortio_open_reader(char*, bool, bool)" , bind = False)
|
||||
_open_writer = EclPrototype("void* fortio_open_writer(char*, bool, bool)" , bind = False)
|
||||
_open_readwrite = EclPrototype("void* fortio_open_readwrite(char*, bool, bool)" , bind = False)
|
||||
_open_append = EclPrototype("void* fortio_open_append(char*, bool, bool)" , bind = False)
|
||||
_guess_fortran = EclPrototype("bool fortio_looks_like_fortran_file(char* , bool)" , bind = False)
|
||||
|
||||
_write_record = EclPrototype("void fortio_fwrite_record(fortio, char*, int)")
|
||||
_get_position = EclPrototype("long fortio_ftell(fortio)")
|
||||
_seek = EclPrototype("void fortio_fseek(fortio, long, int)")
|
||||
_close = EclPrototype("bool fortio_fclose(fortio)")
|
||||
_truncate = EclPrototype("bool fortio_ftruncate(fortio , long)")
|
||||
_filename = EclPrototype("char* fortio_filename_ref(fortio)")
|
||||
|
||||
|
||||
def __init__(self, file_name, mode=READ_MODE, fmt_file=False, endian_flip_header=True):
|
||||
"""Will open a new FortIO handle to @file_name - default for reading.
|
||||
|
||||
The newly created FortIO handle will open the underlying FILE*
|
||||
for reading, but if you pass the flag mode=FortIO.WRITE_MODE
|
||||
the file will be opened for writing.
|
||||
|
||||
Observe that the flag @endian_flip_header will only affect the
|
||||
interpretation of the block size markers in the file, endian
|
||||
flipping of the actual data blocks must be handled at a higher
|
||||
level.
|
||||
|
||||
When you are finished working with the FortIO instance you can
|
||||
manually close it with the close() method, alternatively that
|
||||
will happen automagically when it goes out of scope.
|
||||
|
||||
Small example script opening a restart file, and then writing
|
||||
all the pressure keywords to another file:
|
||||
|
||||
import sys
|
||||
from ert.ecl import FortIO,ElcFile
|
||||
|
||||
rst_file = EclFile(sys.argv[1])
|
||||
fortio = FortIO( "PRESSURE" , mode=FortIO.WRITE_MODE)
|
||||
|
||||
for kw in rst_file:
|
||||
if kw.name() == "PRESSURE":
|
||||
kw.write( fortio )
|
||||
|
||||
fortio.close()
|
||||
|
||||
See the documentation of openFortIO() for an alternative
|
||||
method based on a context manager and the with statement.
|
||||
|
||||
"""
|
||||
if mode == FortIO.READ_MODE or mode == FortIO.APPEND_MODE or mode == FortIO.READ_AND_WRITE_MODE:
|
||||
if not os.path.exists(file_name):
|
||||
raise IOError("File '%s' does not exist!" % file_name)
|
||||
|
||||
if mode == FortIO.READ_MODE:
|
||||
c_pointer = self._open_reader(file_name, fmt_file, endian_flip_header)
|
||||
elif mode == FortIO.WRITE_MODE:
|
||||
c_pointer = self._open_writer(file_name, fmt_file, endian_flip_header)
|
||||
elif mode == FortIO.READ_AND_WRITE_MODE:
|
||||
c_pointer = self._open_readwrite(file_name, fmt_file, endian_flip_header)
|
||||
elif mode == FortIO.APPEND_MODE:
|
||||
c_pointer = self._open_append(file_name, fmt_file, endian_flip_header)
|
||||
else:
|
||||
raise UserWarning("Unknown mode: %d" % mode)
|
||||
|
||||
self.__mode = mode
|
||||
|
||||
super(FortIO, self).__init__(c_pointer)
|
||||
|
||||
|
||||
|
||||
def close(self):
|
||||
if self:
|
||||
self._close( )
|
||||
self._invalidateCPointer( )
|
||||
|
||||
|
||||
def getPosition(self):
|
||||
""" @rtype: long """
|
||||
return self._get_position( )
|
||||
|
||||
|
||||
def truncate(self , size = None):
|
||||
"""Will truncate the file to new size.
|
||||
|
||||
If the method is called without a size argument the stream
|
||||
will be truncated to the current position.
|
||||
"""
|
||||
if size is None:
|
||||
size = self.getPosition( )
|
||||
|
||||
if not self._truncate( size ):
|
||||
raise IOError("Truncate of fortran filehandle:%s failed" % self.filename() )
|
||||
|
||||
|
||||
def filename(self):
|
||||
return self._filename( )
|
||||
|
||||
|
||||
def seek(self, position , whence = 0):
|
||||
# SEEK_SET = 0
|
||||
# SEEK_CUR = 1
|
||||
# SEEK_END = 2
|
||||
self._seek(position , whence)
|
||||
|
||||
|
||||
@classmethod
|
||||
def isFortranFile(cls , filename , endian_flip = True):
|
||||
|
||||
"""@rtype: bool
|
||||
@type filename: str
|
||||
|
||||
|
||||
Will use heuristics to try to guess if @filename is a binary
|
||||
file written in fortran style. ASCII files will return false,
|
||||
even if they are structured as ECLIPSE keywords.
|
||||
"""
|
||||
return cls._guess_fortran( filename , endian_flip )
|
||||
|
||||
|
||||
def free(self):
|
||||
self.close()
|
||||
|
||||
|
||||
class FortIOContextManager(object):
|
||||
|
||||
def __init__(self , fortio):
|
||||
self.__fortio = fortio
|
||||
|
||||
def __enter__(self):
|
||||
return self.__fortio
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.__fortio.close()
|
||||
return False
|
||||
|
||||
|
||||
def openFortIO( file_name , mode = FortIO.READ_MODE , fmt_file = False , endian_flip_header = True):
|
||||
"""Will create FortIO based context manager for use with with.
|
||||
|
||||
The with: statement and context managers is a good alternative in
|
||||
the situation where you need to ensure resource cleanup.
|
||||
|
||||
import sys
|
||||
from ert.ecl import FortIO,ElcFile
|
||||
|
||||
rst_file = EclFile(sys.argv[1])
|
||||
with openFortIO( "PRESSURE" , mode = FortIO.WRITE_MODE) as fortio:
|
||||
for kw in rst_file:
|
||||
if kw.name() == "PRESSURE":
|
||||
kw.write( fortio )
|
||||
|
||||
"""
|
||||
return FortIOContextManager( FortIO( file_name , mode = mode , fmt_file = fmt_file , endian_flip_header = endian_flip_header ))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
set( ECL_LOCAL_TARGET "" CACHE FILE "Name of optional external ecl_local module")
|
||||
|
||||
if (EXISTS ${ECL_LOCAL_TARGET})
|
||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ecl_local.py")
|
||||
EXECUTE_PROCESS( COMMAND ${CMAKE_COMMAND} -E remove "${CMAKE_CURRENT_SOURCE_DIR}/ecl_local.py")
|
||||
endif()
|
||||
|
||||
EXECUTE_PROCESS( COMMAND ${CMAKE_COMMAND} -E create_symlink "${ECL_LOCAL_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}/ecl_local.py")
|
||||
endif()
|
||||
|
||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ecl_local.py")
|
||||
add_python_package( "Python ert.ecl.ecl_local" ${PYTHON_INSTALL_PREFIX}/ert/ecl "ecl_local.py" True)
|
||||
endif()
|
||||
@@ -0,0 +1,6 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
well_trajectory.py
|
||||
)
|
||||
|
||||
add_python_package("python.ert.ecl.rft" ${PYTHON_INSTALL_PREFIX}/ert/ecl/rft "${PYTHON_SOURCES}" True)
|
||||
@@ -0,0 +1 @@
|
||||
from .well_trajectory import WellTrajectory
|
||||
@@ -0,0 +1,63 @@
|
||||
# Copyright (C) 2015 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'well_trajectory.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
import sys
|
||||
import os
|
||||
from collections import namedtuple
|
||||
|
||||
TrajectoryPoint = namedtuple("TrajectoryPoint", "utm_x utm_y measured_depth true_vertical_depth zone")
|
||||
|
||||
class WellTrajectory:
|
||||
|
||||
def __init__(self , filename):
|
||||
if os.path.isfile(filename):
|
||||
self.points = []
|
||||
with open(filename) as fileH:
|
||||
for line in fileH.readlines():
|
||||
line = line.partition("--")[0]
|
||||
line = line.strip()
|
||||
if line:
|
||||
point = line.split()
|
||||
if len(point) < 4 or len(point) > 5:
|
||||
raise UserWarning("Trajectory data file not on correct format: \"utm_x utm_y md tvd [zone]\" - zone is optional")
|
||||
|
||||
try:
|
||||
utm_x = float(point[0])
|
||||
utm_y = float(point[1])
|
||||
md = float(point[2])
|
||||
tvd = float(point[3])
|
||||
if len(point) > 4:
|
||||
zone = point[4]
|
||||
else:
|
||||
zone = None
|
||||
except ValueError:
|
||||
raise UserWarning("Error: Failed to extract data from line %s\n" % line)
|
||||
|
||||
self.points.append(TrajectoryPoint(utm_x , utm_y , md , tvd , zone))
|
||||
|
||||
else:
|
||||
raise IOError("File not found:%s" % filename)
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return len(self.points)
|
||||
|
||||
|
||||
def __getitem__(self , index):
|
||||
if index < 0:
|
||||
index += len(self)
|
||||
|
||||
return self.points[index]
|
||||
@@ -0,0 +1,56 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
active_list.py
|
||||
analysis_config.py
|
||||
analysis_iter_config.py
|
||||
custom_kw_config_set.py
|
||||
ecl_config.py
|
||||
enkf_fs.py
|
||||
enkf_fs_manager.py
|
||||
enkf_linalg.py
|
||||
enkf_main.py
|
||||
enkf_obs.py
|
||||
enkf_simulation_runner.py
|
||||
enkf_state.py
|
||||
ensemble_config.py
|
||||
ert_log.py
|
||||
ert_run_context.py
|
||||
ert_template.py
|
||||
ert_templates.py
|
||||
ert_workflow_list.py
|
||||
key_manager.py
|
||||
local_config.py
|
||||
local_dataset.py
|
||||
local_ministep.py
|
||||
local_obsdata.py
|
||||
local_obsdata_node.py
|
||||
local_updatestep.py
|
||||
meas_block.py
|
||||
meas_data.py
|
||||
model_config.py
|
||||
node_id.py
|
||||
obs_block.py
|
||||
obs_data.py
|
||||
plot_config.py
|
||||
hook_manager.py
|
||||
hook_workflow.py
|
||||
run_arg.py
|
||||
runpath_list.py
|
||||
site_config.py
|
||||
state_map.py
|
||||
summary_key_matcher.py
|
||||
summary_key_set.py
|
||||
forward_load_context.py
|
||||
es_update.py
|
||||
)
|
||||
|
||||
add_python_package("python.ert.enkf" ${PYTHON_INSTALL_PREFIX}/ert/enkf "${PYTHON_SOURCES}" True)
|
||||
|
||||
add_subdirectory(config)
|
||||
add_subdirectory(data)
|
||||
add_subdirectory(enums)
|
||||
add_subdirectory(export)
|
||||
add_subdirectory(observations)
|
||||
add_subdirectory(plot)
|
||||
add_subdirectory(plot_data)
|
||||
add_subdirectory(util)
|
||||
@@ -0,0 +1,96 @@
|
||||
# Copyright (C) 2011 Statoil ASA, Norway.
|
||||
#
|
||||
# The file '__init__.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
|
||||
import ert
|
||||
from cwrap import Prototype
|
||||
import ert.util
|
||||
import ert.geo
|
||||
import ert.ecl
|
||||
import ert.rms
|
||||
import ert.analysis
|
||||
import ert.sched
|
||||
import ert.config
|
||||
import ert.job_queue
|
||||
|
||||
class EnkfPrototype(Prototype):
|
||||
lib = ert.load("libenkf")
|
||||
|
||||
def __init__(self, prototype, bind=True):
|
||||
super(EnkfPrototype, self).__init__(EnkfPrototype.lib, prototype, bind=bind)
|
||||
|
||||
|
||||
|
||||
ENKF_LIB = ert.load("libenkf")
|
||||
|
||||
from .enums import *
|
||||
|
||||
from .node_id import NodeId
|
||||
|
||||
from .enkf_linalg import EnkfLinalg
|
||||
from .util import TimeMap
|
||||
from .state_map import StateMap
|
||||
from .summary_key_set import SummaryKeySet
|
||||
from .summary_key_matcher import SummaryKeyMatcher
|
||||
from .custom_kw_config_set import CustomKWConfigSet
|
||||
from .enkf_fs import EnkfFs
|
||||
|
||||
from .ert_workflow_list import ErtWorkflowList
|
||||
from .active_list import ActiveList
|
||||
from .config import *
|
||||
from .data import *
|
||||
|
||||
from .local_dataset import LocalDataset
|
||||
from .local_obsdata_node import LocalObsdataNode
|
||||
from .local_obsdata import LocalObsdata
|
||||
from .local_ministep import LocalMinistep
|
||||
from .local_updatestep import LocalUpdateStep
|
||||
|
||||
from .observations import *
|
||||
|
||||
from .obs_block import ObsBlock
|
||||
from .obs_data import ObsData
|
||||
from .meas_block import MeasBlock
|
||||
from .meas_data import MeasData
|
||||
|
||||
from .analysis_iter_config import AnalysisIterConfig
|
||||
from .analysis_config import AnalysisConfig
|
||||
from .ecl_config import EclConfig
|
||||
|
||||
from .ensemble_config import EnsembleConfig
|
||||
from .enkf_obs import EnkfObs
|
||||
from .enkf_state import EnKFState
|
||||
from .ert_template import ErtTemplate
|
||||
from .ert_templates import ErtTemplates
|
||||
from .local_config import LocalConfig
|
||||
from .model_config import ModelConfig
|
||||
from .plot_config import PlotConfig
|
||||
from .site_config import SiteConfig
|
||||
from .runpath_list import RunpathList, RunpathNode
|
||||
from .hook_workflow import HookWorkflow
|
||||
from .hook_manager import HookManager
|
||||
|
||||
from .es_update import ESUpdate
|
||||
from .enkf_simulation_runner import EnkfSimulationRunner
|
||||
from .enkf_fs_manager import EnkfFsManager
|
||||
from .run_arg import RunArg
|
||||
from .ert_run_context import ErtRunContext
|
||||
from .enkf_main import EnKFMain
|
||||
from .ert_log import ErtLog
|
||||
from .forward_load_context import ForwardLoadContext
|
||||
|
||||
from ert.job_queue import ErtScript as ErtScript
|
||||
from ert.job_queue import ErtPlugin as ErtPlugin, CancelPluginException as CancelPluginException
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (C) 2015 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'active_list.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.enkf import ENKF_LIB
|
||||
|
||||
class ActiveList(BaseCClass):
|
||||
def __init__(self):
|
||||
c_ptr = ActiveList.cNamespace().alloc()
|
||||
super(ActiveList, self).__init__(c_ptr)
|
||||
|
||||
|
||||
def getMode(self):
|
||||
return ActiveList.cNamespace().get_mode(self)
|
||||
|
||||
def addActiveIndex(self, index):
|
||||
ActiveList.cNamespace().add_index(self , index)
|
||||
|
||||
|
||||
def free(self):
|
||||
ActiveList.cNamespace().free(self)
|
||||
|
||||
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerObjectType("active_list", ActiveList)
|
||||
|
||||
|
||||
ActiveList.cNamespace().alloc = cwrapper.prototype("c_void_p active_list_alloc()")
|
||||
ActiveList.cNamespace().free = cwrapper.prototype("void active_list_free(active_list)")
|
||||
ActiveList.cNamespace().get_mode = cwrapper.prototype("active_mode_enum active_list_get_mode(active_list)")
|
||||
ActiveList.cNamespace().add_index = cwrapper.prototype("void active_list_add_index(active_list , int)")
|
||||
@@ -0,0 +1,152 @@
|
||||
# Copyright (C) 2012 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'analysis_config.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.enkf import EnkfPrototype
|
||||
from ert.enkf import ENKF_LIB
|
||||
from ert.enkf import AnalysisIterConfig
|
||||
from ert.analysis import AnalysisModule
|
||||
from ert.util import StringList
|
||||
|
||||
|
||||
class AnalysisConfig(BaseCClass):
|
||||
TYPE_NAME = "analysis_config"
|
||||
_alloc = EnkfPrototype("void* analysis_config_alloc()", bind = False)
|
||||
_free = EnkfPrototype("void analysis_config_free( analysis_config )")
|
||||
_get_rerun = EnkfPrototype("int analysis_config_get_rerun( analysis_config )")
|
||||
_set_rerun = EnkfPrototype("void analysis_config_set_rerun( analysis_config, bool)")
|
||||
_get_rerun_start = EnkfPrototype("int analysis_config_get_rerun_start( analysis_config )")
|
||||
_set_rerun_start = EnkfPrototype("void analysis_config_set_rerun_start( analysis_config, int)")
|
||||
_get_log_path = EnkfPrototype("char* analysis_config_get_log_path( analysis_config)")
|
||||
_set_log_path = EnkfPrototype("void analysis_config_set_log_path( analysis_config, char*)")
|
||||
_get_merge_observations = EnkfPrototype("bool analysis_config_get_merge_observations(analysis_config)")
|
||||
_set_merge_observations = EnkfPrototype("void analysis_config_set_merge_observations(analysis_config, bool)")
|
||||
_get_iter_config = EnkfPrototype("analysis_iter_config_ref analysis_config_get_iter_config(analysis_config)")
|
||||
_have_enough_realisations = EnkfPrototype("bool analysis_config_have_enough_realisations(analysis_config, int, int)")
|
||||
_get_max_runtime = EnkfPrototype("int analysis_config_get_max_runtime(analysis_config)")
|
||||
_set_max_runtime = EnkfPrototype("void analysis_config_set_max_runtime(analysis_config, int)")
|
||||
_get_stop_long_running = EnkfPrototype("bool analysis_config_get_stop_long_running(analysis_config)")
|
||||
_set_stop_long_running = EnkfPrototype("void analysis_config_set_stop_long_running(analysis_config, bool)")
|
||||
_get_active_module_name = EnkfPrototype("char* analysis_config_get_active_module_name(analysis_config)")
|
||||
_get_module_list = EnkfPrototype("stringlist_obj analysis_config_alloc_module_names(analysis_config)")
|
||||
_get_module = EnkfPrototype("analysis_module_ref analysis_config_get_module(analysis_config, char*)")
|
||||
_select_module = EnkfPrototype("bool analysis_config_select_module(analysis_config, char*)")
|
||||
_has_module = EnkfPrototype("bool analysis_config_has_module(analysis_config, char*)")
|
||||
_get_alpha = EnkfPrototype("double analysis_config_get_alpha(analysis_config)")
|
||||
_set_alpha = EnkfPrototype("void analysis_config_set_alpha(analysis_config, double)")
|
||||
_get_std_cutoff = EnkfPrototype("double analysis_config_get_std_cutoff(analysis_config)")
|
||||
_set_std_cutoff = EnkfPrototype("void analysis_config_set_std_cutoff(analysis_config, double)")
|
||||
_set_global_std_scaling = EnkfPrototype("void analysis_config_set_global_std_scaling(analysis_config, double)")
|
||||
_get_global_std_scaling = EnkfPrototype("double analysis_config_get_global_std_scaling(analysis_config)")
|
||||
|
||||
def __init__(self):
|
||||
c_ptr = AnalysisConfig._alloc()
|
||||
super(AnalysisConfig , self).__init__(c_ptr)
|
||||
|
||||
|
||||
def get_rerun(self):
|
||||
return self._get_rerun()
|
||||
|
||||
def set_rerun(self, rerun):
|
||||
self._set_rerun(rerun)
|
||||
|
||||
def get_rerun_start(self):
|
||||
return self._get_rerun_start()
|
||||
|
||||
def set_rerun_start(self, index):
|
||||
self._set_rerun_start(index)
|
||||
|
||||
def get_log_path(self):
|
||||
return self._get_log_path()
|
||||
|
||||
def set_log_path(self, path):
|
||||
self._set_log_path(path)
|
||||
|
||||
def getEnkfAlpha(self):
|
||||
""" :rtype: float """
|
||||
return self._get_alpha()
|
||||
|
||||
def setEnkfAlpha(self, alpha):
|
||||
self._set_alpha(alpha)
|
||||
|
||||
def getStdCutoff(self):
|
||||
""" :rtype: float """
|
||||
return self._get_std_cutoff()
|
||||
|
||||
def setStdCutoff(self, std_cutoff):
|
||||
self._set_std_cutoff(std_cutoff)
|
||||
|
||||
def get_merge_observations(self):
|
||||
return self._get_merge_observations()
|
||||
|
||||
def set_merge_observations(self, merge_observations):
|
||||
return self._set_merge_observations(merge_observations)
|
||||
|
||||
def getAnalysisIterConfig(self):
|
||||
""" @rtype: AnalysisIterConfig """
|
||||
return self._get_iter_config().setParent(self)
|
||||
|
||||
def get_stop_long_running(self):
|
||||
""" @rtype: bool """
|
||||
return self._get_stop_long_running()
|
||||
|
||||
def set_stop_long_running(self, stop_long_running):
|
||||
self._set_stop_long_running(stop_long_running)
|
||||
|
||||
def get_max_runtime(self):
|
||||
""" @rtype: int """
|
||||
return self._get_max_runtime()
|
||||
|
||||
def set_max_runtime(self, max_runtime):
|
||||
self._set_max_runtime(max_runtime)
|
||||
|
||||
def free(self):
|
||||
self._free()
|
||||
|
||||
def activeModuleName(self):
|
||||
""" :rtype: str """
|
||||
return self._get_active_module_name()
|
||||
|
||||
def getModuleList(self):
|
||||
""" :rtype: StringList """
|
||||
return self._get_module_list()
|
||||
|
||||
def getModule(self, module_name):
|
||||
""" @rtype: AnalysisModule """
|
||||
return self._get_module(module_name)
|
||||
|
||||
def hasModule(self, module_name):
|
||||
""" @rtype: bool """
|
||||
return self._has_module(module_name)
|
||||
|
||||
|
||||
def selectModule(self, module_name):
|
||||
""" @rtype: bool """
|
||||
return self._select_module(module_name)
|
||||
|
||||
def getActiveModule(self):
|
||||
""" :rtype: AnalysisModule """
|
||||
return self.getModule(self.activeModuleName())
|
||||
|
||||
def setGlobalStdScaling(self, std_scaling):
|
||||
self._set_global_std_scaling(std_scaling)
|
||||
|
||||
def getGlobalStdScaling(self):
|
||||
return self._get_global_std_scaling()
|
||||
|
||||
def haveEnoughRealisations(self, realizations, ensemble_size):
|
||||
return self._have_enough_realisations(realizations, ensemble_size)
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# Copyright (C) 2013 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'analysis_iter_config.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.enkf import ENKF_LIB
|
||||
|
||||
|
||||
class AnalysisIterConfig(BaseCClass):
|
||||
def __init__(self):
|
||||
c_ptr = AnalysisIterConfig.cNamespace().alloc()
|
||||
super(AnalysisIterConfig , self).__init__(c_ptr)
|
||||
|
||||
def getNumIterations(self):
|
||||
""" @rtype: int """
|
||||
return AnalysisIterConfig.cNamespace().get_num_iterations(self)
|
||||
|
||||
def setNumIterations(self, num_iterations):
|
||||
AnalysisIterConfig.cNamespace().set_num_iterations(self, num_iterations)
|
||||
|
||||
def numIterationsSet(self):
|
||||
return AnalysisIterConfig.cNamespace().num_iterations_set(self)
|
||||
|
||||
def getNumRetries(self):
|
||||
""" @rtype: int """
|
||||
return AnalysisIterConfig.cNamespace().get_num_retries(self)
|
||||
|
||||
def getCaseFormat(self):
|
||||
""" @rtype: str """
|
||||
return AnalysisIterConfig.cNamespace().get_case_fmt(self)
|
||||
|
||||
def setCaseFormat(self, case_fmt):
|
||||
AnalysisIterConfig.cNamespace().set_case_fmt(self, case_fmt)
|
||||
|
||||
def caseFormatSet(self):
|
||||
return AnalysisIterConfig.cNamespace().case_fmt_set(self)
|
||||
|
||||
def free(self):
|
||||
AnalysisIterConfig.cNamespace().free(self)
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerObjectType("analysis_iter_config", AnalysisIterConfig)
|
||||
|
||||
|
||||
AnalysisIterConfig.cNamespace().alloc = cwrapper.prototype("c_void_p analysis_iter_config_alloc( )")
|
||||
AnalysisIterConfig.cNamespace().free = cwrapper.prototype("void analysis_iter_config_free( analysis_iter_config )")
|
||||
AnalysisIterConfig.cNamespace().set_num_iterations = cwrapper.prototype("void analysis_iter_config_set_num_iterations(analysis_iter_config, int)")
|
||||
AnalysisIterConfig.cNamespace().get_num_iterations = cwrapper.prototype("int analysis_iter_config_get_num_iterations(analysis_iter_config)")
|
||||
AnalysisIterConfig.cNamespace().get_num_retries = cwrapper.prototype("int analysis_iter_config_get_num_retries_per_iteration(analysis_iter_config)")
|
||||
AnalysisIterConfig.cNamespace().num_iterations_set = cwrapper.prototype("bool analysis_iter_config_num_iterations_set(analysis_iter_config)")
|
||||
AnalysisIterConfig.cNamespace().set_case_fmt = cwrapper.prototype("void analysis_iter_config_set_case_fmt( analysis_iter_config , char* )")
|
||||
AnalysisIterConfig.cNamespace().get_case_fmt = cwrapper.prototype("char* analysis_iter_config_get_case_fmt( analysis_iter_config)")
|
||||
AnalysisIterConfig.cNamespace().case_fmt_set = cwrapper.prototype("bool analysis_iter_config_case_fmt_set(analysis_iter_config)")
|
||||
@@ -0,0 +1,12 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
custom_kw_config.py
|
||||
enkf_config_node.py
|
||||
field_config.py
|
||||
gen_data_config.py
|
||||
gen_kw_config.py
|
||||
summary_config.py
|
||||
)
|
||||
|
||||
add_python_package("python.ert.enkf.config" ${PYTHON_INSTALL_PREFIX}/ert/enkf/config "${PYTHON_SOURCES}" True)
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
from .custom_kw_config import CustomKWConfig
|
||||
from .field_config import FieldConfig
|
||||
from .gen_data_config import GenDataConfig
|
||||
from .gen_kw_config import GenKwConfig
|
||||
from .summary_config import SummaryConfig
|
||||
from .enkf_config_node import EnkfConfigNode
|
||||
|
||||
__all__ = ["FieldConfig",
|
||||
"CustomKWConfig",
|
||||
"GenKwConfig",
|
||||
"GenDataConfig",
|
||||
"EnkfConfigNode",]
|
||||
@@ -0,0 +1,117 @@
|
||||
# Copyright (C) 2012 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'field_config.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
import sys
|
||||
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.enkf import ENKF_LIB
|
||||
from ert.util import StringList, IntegerHash
|
||||
|
||||
|
||||
class CustomKWConfig(BaseCClass):
|
||||
def __init__(self, key, result_file, output_file=None, definition=None):
|
||||
"""
|
||||
@type key: str
|
||||
@type result_file: str
|
||||
@type output_file: str
|
||||
@type definition: dict
|
||||
"""
|
||||
|
||||
if definition is not None:
|
||||
if result_file is not None and output_file is not None:
|
||||
sys.stderr.write("[%s] Will ignore result file and output file when constructing with a definition." % self.__class__.__name__)
|
||||
|
||||
type_hash = CustomKWConfig.convertDefinition(definition)
|
||||
c_ptr = CustomKWConfig.cNamespace().alloc_with_definition(key, type_hash)
|
||||
else:
|
||||
c_ptr = CustomKWConfig.cNamespace().alloc_empty(key, result_file, output_file)
|
||||
super(CustomKWConfig, self).__init__(c_ptr)
|
||||
|
||||
def getName(self):
|
||||
""" @rtype: str """
|
||||
return CustomKWConfig.cNamespace().get_name(self)
|
||||
|
||||
def getResultFile(self):
|
||||
""" @rtype: str """
|
||||
return CustomKWConfig.cNamespace().get_result_file(self)
|
||||
|
||||
def getOutputFile(self):
|
||||
""" @rtype: str """
|
||||
return CustomKWConfig.cNamespace().get_output_file(self)
|
||||
|
||||
def parseResultFile(self, result_file, result):
|
||||
""" @rtype: bool """
|
||||
return CustomKWConfig.cNamespace().parse_result_file(self, result_file, result)
|
||||
|
||||
def keyIsDouble(self, key):
|
||||
""" @rtype: bool """
|
||||
return CustomKWConfig.cNamespace().key_is_double(self, key)
|
||||
|
||||
def indexOfKey(self, key):
|
||||
""" @rtype: int """
|
||||
return CustomKWConfig.cNamespace().index_of_key(self, key)
|
||||
|
||||
def __contains__(self, item):
|
||||
""" @rtype: bool """
|
||||
return CustomKWConfig.cNamespace().has_key(self, item)
|
||||
|
||||
def __len__(self):
|
||||
""" @rtype: int """
|
||||
return CustomKWConfig.cNamespace().size(self)
|
||||
|
||||
def __iter__(self):
|
||||
keys = self.getKeys()
|
||||
index = 0
|
||||
while index < len(keys):
|
||||
yield keys[index]
|
||||
index += 1
|
||||
|
||||
def free(self):
|
||||
CustomKWConfig.cNamespace().free(self)
|
||||
|
||||
def getKeys(self):
|
||||
""" @rtype: StringList """
|
||||
return CustomKWConfig.cNamespace().keys(self)
|
||||
|
||||
@classmethod
|
||||
def convertDefinition(cls, definition):
|
||||
""" @rtype: IntegerHash """
|
||||
type_hash = IntegerHash()
|
||||
|
||||
for key, value_type in definition.iteritems():
|
||||
if value_type == float:
|
||||
value_type = 1
|
||||
else:
|
||||
value_type = 0 #str
|
||||
type_hash[key] = value_type
|
||||
return type_hash
|
||||
|
||||
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerObjectType("custom_kw_config", CustomKWConfig)
|
||||
|
||||
CustomKWConfig.cNamespace().free = cwrapper.prototype("void custom_kw_config_free(custom_kw_config)")
|
||||
CustomKWConfig.cNamespace().alloc_empty = cwrapper.prototype("void* custom_kw_config_alloc_empty(char*, char*, char*)")
|
||||
CustomKWConfig.cNamespace().alloc_with_definition = cwrapper.prototype("void* custom_kw_config_alloc_with_definition(char*, integer_hash)")
|
||||
CustomKWConfig.cNamespace().get_name = cwrapper.prototype("char* custom_kw_config_get_name(custom_kw_config)")
|
||||
CustomKWConfig.cNamespace().get_result_file = cwrapper.prototype("char* custom_kw_config_get_result_file(custom_kw_config)")
|
||||
CustomKWConfig.cNamespace().get_output_file = cwrapper.prototype("char* custom_kw_config_get_output_file(custom_kw_config)")
|
||||
CustomKWConfig.cNamespace().parse_result_file = cwrapper.prototype("bool custom_kw_config_parse_result_file(custom_kw_config, char*, stringlist)")
|
||||
CustomKWConfig.cNamespace().has_key = cwrapper.prototype("bool custom_kw_config_has_key(custom_kw_config, char*)")
|
||||
CustomKWConfig.cNamespace().key_is_double = cwrapper.prototype("bool custom_kw_config_key_is_double(custom_kw_config, char*)")
|
||||
CustomKWConfig.cNamespace().index_of_key = cwrapper.prototype("int custom_kw_config_index_of_key(custom_kw_config, char*)")
|
||||
CustomKWConfig.cNamespace().size = cwrapper.prototype("int custom_kw_config_size(custom_kw_config)")
|
||||
CustomKWConfig.cNamespace().keys = cwrapper.prototype("stringlist_obj custom_kw_config_get_keys(custom_kw_config)")
|
||||
@@ -0,0 +1,137 @@
|
||||
# Copyright (C) 2012 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'enkf_config_node.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.enkf import ENKF_LIB
|
||||
from ert.enkf.config import FieldConfig, GenDataConfig, GenKwConfig, SummaryConfig, CustomKWConfig
|
||||
from ert.enkf.enums import EnkfTruncationType, ErtImplType, LoadFailTypeEnum, EnkfVarType
|
||||
from ert.ecl import EclGrid
|
||||
|
||||
|
||||
|
||||
class EnkfConfigNode(BaseCClass):
|
||||
|
||||
def __init__(self):
|
||||
raise NotImplementedError("Class can not be instantiated directly!")
|
||||
|
||||
def getImplementationType( self ):
|
||||
""" @rtype: ErtImplType """
|
||||
return EnkfConfigNode.cNamespace().get_impl_type(self)
|
||||
|
||||
def getVariableType( self ):
|
||||
return EnkfConfigNode.cNamespace().get_var_type(self)
|
||||
|
||||
def getPointerReference(self):
|
||||
return EnkfConfigNode.cNamespace().get_ref(self)
|
||||
|
||||
# def get_min_std_file(self):
|
||||
# return EnkfConfigNode.cNamespace().get_min_std_file(self)
|
||||
|
||||
# def get_enkf_outfile(self):
|
||||
# return EnkfConfigNode.cNamespace().get_enkf_outfile(self)
|
||||
|
||||
def getFieldModelConfig(self):
|
||||
""" @rtype: FieldConfig """
|
||||
return FieldConfig.createCReference(EnkfConfigNode.cNamespace().get_ref(self), parent=self)
|
||||
|
||||
def getDataModelConfig(self):
|
||||
""" @rtype: GenDataConfig """
|
||||
return GenDataConfig.createCReference(EnkfConfigNode.cNamespace().get_ref(self), parent=self)
|
||||
|
||||
def getKeywordModelConfig(self):
|
||||
""" @rtype: GenKWConfig """
|
||||
return GenKwConfig.createCReference(EnkfConfigNode.cNamespace().get_ref(self), parent=self)
|
||||
|
||||
def getCustomKeywordModelConfig(self):
|
||||
""" @rtype: CustomKWConfig """
|
||||
return CustomKWConfig.createCReference(EnkfConfigNode.cNamespace().get_ref(self), parent=self)
|
||||
|
||||
# def get_enkf_infile(self):
|
||||
# return EnkfConfigNode.cNamespace().get_enkf_infile(self)
|
||||
|
||||
# def alloc_node(self):
|
||||
# return EnkfNode(self)
|
||||
|
||||
# def get_init_file_fmt(self):
|
||||
# return EnkfConfigNode.cNamespace().get_init_file_fmt(self)
|
||||
|
||||
def getObservationKeys(self):
|
||||
""" @rtype: StringList """
|
||||
return EnkfConfigNode.cNamespace().get_obs_keys(self).setParent(self)
|
||||
|
||||
def updateStateField(self, truncation, value_min, value_max):
|
||||
assert isinstance(truncation, EnkfTruncationType)
|
||||
EnkfConfigNode.cNamespace().update_state_field(self, truncation, value_min, value_max)
|
||||
|
||||
@classmethod
|
||||
def createSummaryConfigNode(cls, key, load_fail_type):
|
||||
"""
|
||||
@type key: str
|
||||
@type load_fail_type: LoadFailTypeEnum
|
||||
@rtype: EnkfConfigNode
|
||||
"""
|
||||
|
||||
assert isinstance(load_fail_type, LoadFailTypeEnum)
|
||||
return EnkfConfigNode.cNamespace().alloc_summary_node(key, load_fail_type)
|
||||
|
||||
@classmethod
|
||||
def createFieldConfigNode(cls, key, grid, trans_table = None, forward_init = False):
|
||||
"""
|
||||
@type grid: EclGrid
|
||||
@rtype: EnkfConfigNode
|
||||
"""
|
||||
return EnkfConfigNode.cNamespace().alloc_field_node(key, grid, trans_table, forward_init)
|
||||
|
||||
def free(self):
|
||||
EnkfConfigNode.cNamespace().free(self)
|
||||
|
||||
def getModelConfig(self):
|
||||
implementation_type = self.getImplementationType()
|
||||
|
||||
if implementation_type == ErtImplType.FIELD:
|
||||
return self.getFieldModelConfig()
|
||||
elif implementation_type == ErtImplType.GEN_DATA:
|
||||
return self.getDataModelConfig()
|
||||
elif implementation_type == ErtImplType.GEN_KW:
|
||||
return self.getKeywordModelConfig()
|
||||
elif implementation_type == ErtImplType.CUSTOM_KW:
|
||||
return self.getCustomKeywordModelConfig()
|
||||
elif implementation_type == ErtImplType.SUMMARY:
|
||||
return SummaryConfig.createCReference(self.getPointerReference(), parent=self)
|
||||
else:
|
||||
print("[EnkfConfigNode::getModelConfig()] Unhandled implementation model type: %i" % implementation_type)
|
||||
# raise NotImplementedError("Unknown model type: %i" % type)
|
||||
|
||||
def getKey(self):
|
||||
return EnkfConfigNode.cNamespace().get_key( self )
|
||||
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerObjectType("enkf_config_node", EnkfConfigNode)
|
||||
|
||||
EnkfConfigNode.cNamespace().free = cwrapper.prototype("void enkf_config_node_free(enkf_config_node)")
|
||||
EnkfConfigNode.cNamespace().get_ref = cwrapper.prototype("c_void_p enkf_config_node_get_ref(enkf_config_node)") #todo: fix return type
|
||||
EnkfConfigNode.cNamespace().get_impl_type = cwrapper.prototype("ert_impl_type_enum enkf_config_node_get_impl_type(enkf_config_node)")
|
||||
|
||||
EnkfConfigNode.cNamespace().get_enkf_outfile = cwrapper.prototype("char* enkf_config_node_get_enkf_outfile(enkf_config_node)")
|
||||
EnkfConfigNode.cNamespace().get_min_std_file = cwrapper.prototype("char* enkf_config_node_get_min_std_file(enkf_config_node)")
|
||||
EnkfConfigNode.cNamespace().get_enkf_infile = cwrapper.prototype("char* enkf_config_node_get_enkf_infile(enkf_config_node)")
|
||||
EnkfConfigNode.cNamespace().get_init_file_fmt = cwrapper.prototype("char* enkf_config_node_get_init_file_fmt(enkf_config_node)")
|
||||
EnkfConfigNode.cNamespace().get_var_type = cwrapper.prototype("enkf_var_type_enum enkf_config_node_get_var_type(enkf_config_node)") #todo: fix return type as enum
|
||||
EnkfConfigNode.cNamespace().get_key = cwrapper.prototype("char* enkf_config_node_get_key(enkf_config_node)")
|
||||
EnkfConfigNode.cNamespace().get_obs_keys = cwrapper.prototype("stringlist_ref enkf_config_node_get_obs_keys(enkf_config_node)")
|
||||
EnkfConfigNode.cNamespace().alloc_summary_node = cwrapper.prototype("enkf_config_node_obj enkf_config_node_alloc_summary(char*, load_fail_type)")
|
||||
EnkfConfigNode.cNamespace().alloc_field_node = cwrapper.prototype("enkf_config_node_obj enkf_config_node_alloc_field(char*, ecl_grid, c_void_p, bool)")
|
||||
EnkfConfigNode.cNamespace().update_state_field = cwrapper.prototype("void enkf_config_node_update_state_field(enkf_config_node, enkf_truncation_type_enum, double, double)")
|
||||
@@ -0,0 +1,89 @@
|
||||
# Copyright (C) 2012 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'field_config.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
|
||||
from ert.enkf import ENKF_LIB
|
||||
from ert.enkf.enums import EnkfFieldFileFormatEnum
|
||||
|
||||
class FieldConfig(BaseCClass):
|
||||
def __init__(self , kw , grid):
|
||||
c_ptr = FieldConfig.cNamespace().alloc( kw , grid , None , False )
|
||||
super(FieldConfig, self).__init__(c_ptr)
|
||||
|
||||
@classmethod
|
||||
def exportFormat(cls , filename):
|
||||
export_format = FieldConfig.cNamespace().export_format( filename )
|
||||
if export_format in [ EnkfFieldFileFormatEnum.ECL_GRDECL_FILE , EnkfFieldFileFormatEnum.RMS_ROFF_FILE ]:
|
||||
return export_format
|
||||
else:
|
||||
raise ValueError("Couuld not determine grdecl / roff format from:%s" % filename)
|
||||
|
||||
|
||||
def get_type(self):
|
||||
return FieldConfig.cNamespace().get_type(self)
|
||||
|
||||
def get_truncation_mode(self):
|
||||
return FieldConfig.cNamespace().get_truncation_mode(self)
|
||||
|
||||
def get_truncation_min(self):
|
||||
return FieldConfig.cNamespace().get_truncation_min(self)
|
||||
|
||||
def get_init_transform_name(self):
|
||||
return FieldConfig.cNamespace().get_init_transform_name(self)
|
||||
|
||||
def get_output_transform_name(self):
|
||||
return FieldConfig.cNamespace().get_output_transform_name(self)
|
||||
|
||||
def get_truncation_max(self):
|
||||
return FieldConfig.cNamespace().get_truncation_max(self)
|
||||
|
||||
def get_nx(self):
|
||||
return FieldConfig.cNamespace().get_nx(self)
|
||||
|
||||
def get_ny(self):
|
||||
return FieldConfig.cNamespace().get_ny(self)
|
||||
|
||||
def get_nz(self):
|
||||
return FieldConfig.cNamespace().get_nz(self)
|
||||
|
||||
def ijk_active(self, i, j, k):
|
||||
return FieldConfig.cNamespace().ijk_active(self, i, j, k)
|
||||
|
||||
def free(self):
|
||||
FieldConfig.cNamespace().free(self)
|
||||
|
||||
|
||||
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerObjectType("field_config", FieldConfig)
|
||||
|
||||
FieldConfig.cNamespace().alloc = cwrapper.prototype("c_void_p field_config_alloc_empty(char* , ecl_grid , c_void_p , bool)")
|
||||
FieldConfig.cNamespace().free = cwrapper.prototype("void field_config_free( field_config )")
|
||||
FieldConfig.cNamespace().get_type = cwrapper.prototype("int field_config_get_type(field_config)")
|
||||
FieldConfig.cNamespace().get_truncation_mode = cwrapper.prototype("int field_config_get_truncation_mode(field_config)")
|
||||
FieldConfig.cNamespace().get_truncation_min = cwrapper.prototype("double field_config_get_truncation_min(field_config)")
|
||||
FieldConfig.cNamespace().get_truncation_max = cwrapper.prototype("double field_config_get_truncation_max(field_config)")
|
||||
FieldConfig.cNamespace().get_init_transform_name = cwrapper.prototype("char* field_config_get_init_transform_name(field_config)")
|
||||
FieldConfig.cNamespace().get_output_transform_name = cwrapper.prototype("char* field_config_get_output_transform_name(field_config)")
|
||||
FieldConfig.cNamespace().ijk_active = cwrapper.prototype("bool field_config_ijk_active(field_config, int, int, int)")
|
||||
FieldConfig.cNamespace().get_nx = cwrapper.prototype("int field_config_get_nx(field_config)")
|
||||
FieldConfig.cNamespace().get_ny = cwrapper.prototype("int field_config_get_ny(field_config)")
|
||||
FieldConfig.cNamespace().get_nz = cwrapper.prototype("int field_config_get_nz(field_config)")
|
||||
FieldConfig.cNamespace().get_grid = cwrapper.prototype("c_void_p field_config_get_grid(field_config)") #todo: fix return type
|
||||
|
||||
FieldConfig.cNamespace().export_format = cwrapper.prototype("enkf_field_file_format_enum field_config_default_export_format(char*)")
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# Copyright (C) 2012 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'gen_data_config.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.enkf import ENKF_LIB
|
||||
from ert.enkf.enums import GenDataFileType
|
||||
|
||||
|
||||
class GenDataConfig(BaseCClass):
|
||||
def __init__(self, key , input_format = GenDataFileType.ASCII):
|
||||
# Can currently only create GEN_DATA instances which should be used
|
||||
# as result variables.
|
||||
c_pointer = GenDataConfig.cNamespace().alloc( key , input_format )
|
||||
super(GenDataConfig, self).__init__(c_pointer)
|
||||
|
||||
|
||||
def get_template_file(self):
|
||||
return GenDataConfig.cNamespace().get_template_file(self)
|
||||
|
||||
def get_template_key(self):
|
||||
return GenDataConfig.cNamespace().get_template_key(self)
|
||||
|
||||
def getDataSize(self , report_step):
|
||||
data_size = GenDataConfig.cNamespace().get_data_size(self , report_step)
|
||||
if data_size < 0:
|
||||
raise ValueError("No data has been loaded for %s at report step:%d " % (self.getName() , report_step))
|
||||
else:
|
||||
return data_size
|
||||
|
||||
def getActiveMask(self):
|
||||
return GenDataConfig.cNamespace().get_active_mask(self)
|
||||
|
||||
def getName(self):
|
||||
return GenDataConfig.cNamespace().get_key(self)
|
||||
|
||||
|
||||
def get_initial_size(self):
|
||||
return GenDataConfig.cNamespace().get_initial_size(self)
|
||||
|
||||
def getOutputFormat(self):
|
||||
return GenDataConfig.cNamespace().get_output_format(self)
|
||||
|
||||
def getInputFormat(self):
|
||||
return GenDataConfig.cNamespace().get_input_format(self)
|
||||
|
||||
def free(self):
|
||||
GenDataConfig.cNamespace().free(self)
|
||||
|
||||
def hasReportStep(self, report_step):
|
||||
""" @rtype: bool """
|
||||
return GenDataConfig.cNamespace().has_report_step(self, report_step)
|
||||
|
||||
def getNumReportStep(self):
|
||||
""" @rtype: int """
|
||||
return GenDataConfig.cNamespace().get_num_report_step(self)
|
||||
|
||||
def getReportStep(self, index):
|
||||
""" @rtype: int """
|
||||
return GenDataConfig.cNamespace().iget_report_step(self, index)
|
||||
|
||||
def getReportSteps(self):
|
||||
""" @rtype: list of int """
|
||||
return [self.getReportStep(index) for index in range(self.getNumReportStep())]
|
||||
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerObjectType("gen_data_config", GenDataConfig)
|
||||
|
||||
|
||||
GenDataConfig.cNamespace().alloc = cwrapper.prototype("c_void_p gen_data_config_alloc_GEN_DATA_result( char* , gen_data_file_format_type)")
|
||||
GenDataConfig.cNamespace().free = cwrapper.prototype("void gen_data_config_free( gen_data_config )")
|
||||
GenDataConfig.cNamespace().get_output_format = cwrapper.prototype("gen_data_file_format_type gen_data_config_get_output_format(gen_data_config)")
|
||||
GenDataConfig.cNamespace().get_input_format = cwrapper.prototype("gen_data_file_format_type gen_data_config_get_input_format(gen_data_config)")
|
||||
GenDataConfig.cNamespace().get_template_file = cwrapper.prototype("char* gen_data_config_get_template_file(gen_data_config)")
|
||||
GenDataConfig.cNamespace().get_template_key = cwrapper.prototype("char* gen_data_config_get_template_key(gen_data_config)")
|
||||
GenDataConfig.cNamespace().get_initial_size = cwrapper.prototype("int gen_data_config_get_initial_size(gen_data_config)")
|
||||
GenDataConfig.cNamespace().has_report_step = cwrapper.prototype("bool gen_data_config_has_report_step(gen_data_config, int)")
|
||||
GenDataConfig.cNamespace().get_data_size = cwrapper.prototype("int gen_data_config_get_data_size__(gen_data_config , int)")
|
||||
GenDataConfig.cNamespace().get_key = cwrapper.prototype("char* gen_data_config_get_key(gen_data_config)")
|
||||
GenDataConfig.cNamespace().get_active_mask = cwrapper.prototype("bool_vector_ref gen_data_config_get_active_mask(gen_data_config)")
|
||||
|
||||
GenDataConfig.cNamespace().get_num_report_step = cwrapper.prototype("int gen_data_config_num_report_step(gen_data_config)")
|
||||
GenDataConfig.cNamespace().iget_report_step = cwrapper.prototype("int gen_data_config_iget_report_step(gen_data_config, int)")
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Copyright (C) 2012 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'gen_kw_config.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.enkf import ENKF_LIB
|
||||
from ert.util import StringList
|
||||
|
||||
|
||||
class GenKwConfig(BaseCClass):
|
||||
def __init__(self, key, template_file , parameter_file , tag_fmt = "<%s>"):
|
||||
"""
|
||||
@type key: str
|
||||
@type tag_fmt: str
|
||||
"""
|
||||
c_ptr = GenKwConfig.cNamespace().alloc_empty(key, tag_fmt)
|
||||
super(GenKwConfig, self).__init__(c_ptr)
|
||||
|
||||
self.setTemplateFile(template_file)
|
||||
self.setParameterFile(parameter_file)
|
||||
|
||||
|
||||
def setTemplateFile(self, template_file):
|
||||
GenKwConfig.cNamespace().set_template_file(self , template_file)
|
||||
|
||||
def getTemplateFile(self):
|
||||
return GenKwConfig.cNamespace().get_template_file(self)
|
||||
|
||||
def getParameterFile(self):
|
||||
return GenKwConfig.cNamespace().get_parameter_file(self)
|
||||
|
||||
def setParameterFile(self, parameter_file):
|
||||
GenKwConfig.cNamespace().set_parameter_file(self, parameter_file)
|
||||
|
||||
def getKeyWords(self):
|
||||
""" @rtype: StringList """
|
||||
return GenKwConfig.cNamespace().alloc_name_list(self)
|
||||
|
||||
def shouldUseLogScale(self, index):
|
||||
""" @rtype: bool """
|
||||
return GenKwConfig.cNamespace().should_use_log_scale(self, index)
|
||||
|
||||
def free(self):
|
||||
GenKwConfig.cNamespace().free(self)
|
||||
|
||||
def getKey(self):
|
||||
""" @rtype: str """
|
||||
return GenKwConfig.cNamespace().get_key(self)
|
||||
|
||||
def __len__(self):
|
||||
return GenKwConfig.cNamespace().size(self)
|
||||
|
||||
def __getitem__(self, index):
|
||||
""" @rtype: str """
|
||||
return GenKwConfig.cNamespace().iget_name(self, index)
|
||||
|
||||
def __iter__(self):
|
||||
index = 0
|
||||
while index < len(self):
|
||||
yield self[index]
|
||||
index += 1
|
||||
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerObjectType("gen_kw_config", GenKwConfig)
|
||||
|
||||
GenKwConfig.cNamespace().free = cwrapper.prototype("void gen_kw_config_free( gen_kw_config )")
|
||||
GenKwConfig.cNamespace().alloc_empty = cwrapper.prototype("c_void_p gen_kw_config_alloc_empty( char*, char* )")
|
||||
GenKwConfig.cNamespace().get_template_file = cwrapper.prototype("char* gen_kw_config_get_template_file(gen_kw_config)")
|
||||
GenKwConfig.cNamespace().set_template_file = cwrapper.prototype("void gen_kw_config_set_template_file(gen_kw_config , char*)")
|
||||
GenKwConfig.cNamespace().get_parameter_file = cwrapper.prototype("char* gen_kw_config_get_parameter_file(gen_kw_config)")
|
||||
GenKwConfig.cNamespace().set_parameter_file = cwrapper.prototype("void gen_kw_config_set_parameter_file( gen_kw_config, char* )")
|
||||
GenKwConfig.cNamespace().alloc_name_list = cwrapper.prototype("stringlist_obj gen_kw_config_alloc_name_list(gen_kw_config)")
|
||||
|
||||
GenKwConfig.cNamespace().should_use_log_scale = cwrapper.prototype("bool gen_kw_config_should_use_log_scale(gen_kw_config, int)")
|
||||
GenKwConfig.cNamespace().get_key = cwrapper.prototype("char* gen_kw_config_get_key(gen_kw_config)")
|
||||
GenKwConfig.cNamespace().size = cwrapper.prototype("int gen_kw_config_get_data_size(gen_kw_config)")
|
||||
GenKwConfig.cNamespace().iget_name = cwrapper.prototype("char* gen_kw_config_iget_name(gen_kw_config, int)")
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright (C) 2012 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'field_config.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.enkf import ENKF_LIB
|
||||
|
||||
|
||||
class SummaryConfig(BaseCClass):
|
||||
def __init__(self):
|
||||
raise NotImplementedError("Class can not be instantiated directly!")
|
||||
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerType("summary_config", SummaryConfig)
|
||||
cwrapper.registerType("summary_config", SummaryConfig.createPythonObject)
|
||||
cwrapper.registerType("summary_config", SummaryConfig.createCReference)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
from cwrap import CWrapper, BaseCClass
|
||||
from ert.enkf import ENKF_LIB
|
||||
from ert.enkf.config.custom_kw_config import CustomKWConfig
|
||||
|
||||
|
||||
class CustomKWConfigSet(BaseCClass):
|
||||
|
||||
def __init__(self, filename=None):
|
||||
if filename is None:
|
||||
c_ptr = CustomKWConfigSet.cNamespace().alloc()
|
||||
else:
|
||||
c_ptr = CustomKWConfigSet.cNamespace().alloc_from_file(filename)
|
||||
|
||||
super(CustomKWConfigSet, self).__init__(c_ptr)
|
||||
|
||||
|
||||
def addConfig(self, config):
|
||||
""" @type config: CustomKWConfig """
|
||||
assert isinstance(config, CustomKWConfig)
|
||||
CustomKWConfigSet.cNamespace().add_config(self, config)
|
||||
|
||||
def getStoredConfigKeys(self):
|
||||
""" @rtype: StringList """
|
||||
return CustomKWConfigSet.cNamespace().get_keys(self)
|
||||
|
||||
def updateConfig(self, config):
|
||||
""" @type config: CustomKWConfig """
|
||||
CustomKWConfigSet.cNamespace().update_config(self, config)
|
||||
|
||||
def fwrite(self, filename):
|
||||
""" @type filename: str """
|
||||
CustomKWConfigSet.cNamespace().fwrite(self, filename)
|
||||
|
||||
def reset(self):
|
||||
CustomKWConfigSet.cNamespace().reset(self)
|
||||
|
||||
def free(self):
|
||||
CustomKWConfigSet.cNamespace().free(self)
|
||||
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerObjectType("custom_kw_config_set", CustomKWConfigSet)
|
||||
|
||||
CustomKWConfigSet.cNamespace().alloc = cwrapper.prototype("c_void_p custom_kw_config_set_alloc()")
|
||||
CustomKWConfigSet.cNamespace().alloc_from_file = cwrapper.prototype("c_void_p custom_kw_config_set_alloc_from_file(char*)")
|
||||
CustomKWConfigSet.cNamespace().free = cwrapper.prototype("void custom_kw_config_set_free(custom_kw_config_set)")
|
||||
CustomKWConfigSet.cNamespace().reset = cwrapper.prototype("void custom_kw_config_set_reset(custom_kw_config_set)")
|
||||
CustomKWConfigSet.cNamespace().add_config = cwrapper.prototype("void custom_kw_config_set_add_config(custom_kw_config_set, custom_kw_config)")
|
||||
CustomKWConfigSet.cNamespace().update_config = cwrapper.prototype("void custom_kw_config_set_update_config(custom_kw_config_set, custom_kw_config)")
|
||||
CustomKWConfigSet.cNamespace().get_keys = cwrapper.prototype("stringlist_obj custom_kw_config_set_get_keys_alloc(custom_kw_config_set)")
|
||||
CustomKWConfigSet.cNamespace().fwrite = cwrapper.prototype("void custom_kw_config_set_fwrite(custom_kw_config_set, char*)")
|
||||
@@ -0,0 +1,11 @@
|
||||
set(PYTHON_SOURCES
|
||||
__init__.py
|
||||
custom_kw.py
|
||||
enkf_node.py
|
||||
field.py
|
||||
gen_data.py
|
||||
gen_kw.py
|
||||
)
|
||||
|
||||
add_python_package("python.ert.enkf.data" ${PYTHON_INSTALL_PREFIX}/ert/enkf/data "${PYTHON_SOURCES}" True)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
from .custom_kw import CustomKW
|
||||
from .field import Field
|
||||
from .gen_data import GenData
|
||||
from .gen_kw import GenKw
|
||||
from .enkf_node import EnkfNode
|
||||
|
||||
__all__ = ["Field",
|
||||
"CustomKW",
|
||||
"GenKw",
|
||||
"GenData",
|
||||
"EnkfNode",]
|
||||
@@ -0,0 +1,87 @@
|
||||
# Copyright (C) 2012 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'field_config.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.enkf import ENKF_LIB
|
||||
from ert.enkf.config import CustomKWConfig
|
||||
|
||||
|
||||
class CustomKW(BaseCClass):
|
||||
def __init__(self, custom_kw_config):
|
||||
assert isinstance(custom_kw_config, CustomKWConfig)
|
||||
c_ptr = CustomKW.cNamespace().alloc(custom_kw_config)
|
||||
super(CustomKW, self).__init__(c_ptr)
|
||||
|
||||
def fload(self, filename):
|
||||
"""
|
||||
@type filename: str
|
||||
@rtype: bool
|
||||
"""
|
||||
return CustomKW.cNamespace().fload(self, filename)
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
""" @rtype: str or float """
|
||||
config = self.getConfig()
|
||||
|
||||
if not key in config:
|
||||
raise KeyError("The key: '%s' is not available!" % key)
|
||||
|
||||
index = config.indexOfKey(key)
|
||||
|
||||
if CustomKW.cNamespace().key_is_null(self, key):
|
||||
return None
|
||||
|
||||
if config.keyIsDouble(key):
|
||||
return CustomKW.cNamespace().iget_as_double(self, index)
|
||||
|
||||
return CustomKW.cNamespace().iget_as_string(self, index)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""
|
||||
@type key: str
|
||||
@type value: float|int|str
|
||||
"""
|
||||
|
||||
config = self.getConfig()
|
||||
|
||||
if not key in config:
|
||||
raise KeyError("The key: '%s' is not available!" % key)
|
||||
|
||||
if isinstance(value, (float, int, long)):
|
||||
CustomKW.cNamespace().set_double(self, key, value)
|
||||
else:
|
||||
CustomKW.cNamespace().set_string(self, key, str(value))
|
||||
|
||||
|
||||
def getConfig(self):
|
||||
""" @rtype: CustomKWConfig """
|
||||
return CustomKW.cNamespace().get_config(self)
|
||||
|
||||
def free(self):
|
||||
CustomKW.cNamespace().free(self)
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerObjectType("custom_kw", CustomKW)
|
||||
|
||||
CustomKW.cNamespace().free = cwrapper.prototype("void custom_kw_free(custom_kw)")
|
||||
CustomKW.cNamespace().alloc = cwrapper.prototype("void* custom_kw_alloc(custom_kw_config)")
|
||||
CustomKW.cNamespace().fload = cwrapper.prototype("bool custom_kw_fload(custom_kw, char*)")
|
||||
CustomKW.cNamespace().get_config = cwrapper.prototype("custom_kw_config_ref custom_kw_get_config(custom_kw)")
|
||||
CustomKW.cNamespace().key_is_null = cwrapper.prototype("bool custom_kw_key_is_null(custom_kw, char*)")
|
||||
CustomKW.cNamespace().iget_as_double = cwrapper.prototype("double custom_kw_iget_as_double(custom_kw, int)")
|
||||
CustomKW.cNamespace().iget_as_string = cwrapper.prototype("char* custom_kw_iget_as_string(custom_kw, int)")
|
||||
CustomKW.cNamespace().set_string = cwrapper.prototype("void custom_kw_set_string(custom_kw, char*, char*)")
|
||||
CustomKW.cNamespace().set_double = cwrapper.prototype("void custom_kw_set_double(custom_kw, char*, double)")
|
||||
@@ -0,0 +1,131 @@
|
||||
# Copyright (C) 2012 Statoil ASA, Norway.
|
||||
#
|
||||
# The file 'enkf_node.py' is part of ERT - Ensemble based Reservoir Tool.
|
||||
#
|
||||
# ERT 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.
|
||||
#
|
||||
# ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
# for more details.
|
||||
import sys
|
||||
from ert.enkf.enums import ErtImplType
|
||||
from cwrap import BaseCClass, CWrapper
|
||||
from ert.enkf import ENKF_LIB, EnkfFs, NodeId
|
||||
from ert.enkf.data import GenKw, GenData, CustomKW, Field
|
||||
|
||||
class EnkfNode(BaseCClass):
|
||||
def __init__(self, config_node, private=False):
|
||||
if private:
|
||||
c_pointer = EnkfNode.cNamespace().alloc_private(config_node)
|
||||
else:
|
||||
c_pointer = EnkfNode.cNamespace().alloc(config_node)
|
||||
|
||||
super(EnkfNode, self).__init__(c_pointer, config_node, True)
|
||||
|
||||
@classmethod
|
||||
def exportMany(cls , config_node , file_format , fs , iens_list , report_step = 0 , file_type = None , arg = None):
|
||||
node = EnkfNode( config_node )
|
||||
for iens in iens_list:
|
||||
filename = file_format % iens
|
||||
node_id = NodeId( report_step , iens )
|
||||
if node.tryLoad(fs , node_id):
|
||||
if node.export( filename , file_type = file_type , arg = arg):
|
||||
print("%s[%03d] -> %s" % (config_node.getKey() , iens , filename))
|
||||
else:
|
||||
sys.stderr.write("** ERROR: Could not load realisation:%d - export failed" % iens)
|
||||
|
||||
|
||||
def export(self , filename , file_type = None , arg = None):
|
||||
impl_type = self.getImplType()
|
||||
if impl_type == ErtImplType.FIELD:
|
||||
field_node = self.asField( )
|
||||
return field_node.export( filename , file_type = file_type , init_file = arg)
|
||||
else:
|
||||
raise NotImplementedError("The export method is only implemented for field")
|
||||
|
||||
|
||||
|
||||
def valuePointer(self):
|
||||
return EnkfNode.cNamespace().value_ptr(self)
|
||||
|
||||
def getImplType(self):
|
||||
""" @rtype: ert.enkf.enums.ert_impl_type_enum.ErtImplType """
|
||||
return EnkfNode.cNamespace().get_impl_type(self)
|
||||
|
||||
|
||||
def asGenData(self):
|
||||
""" @rtype: GenData """
|
||||
impl_type = self.getImplType( )
|
||||
assert impl_type == ErtImplType.GEN_DATA
|
||||
|
||||
return GenData.createCReference(self.valuePointer(), self)
|
||||
|
||||
def asGenKw(self):
|
||||
""" @rtype: GenKw """
|
||||
impl_type = self.getImplType( )
|
||||
assert impl_type == ErtImplType.GEN_KW
|
||||
|
||||
return GenKw.createCReference(self.valuePointer(), self)
|
||||
|
||||
def asCustomKW(self):
|
||||
""" @rtype: CustomKW """
|
||||
impl_type = self.getImplType( )
|
||||
assert impl_type == ErtImplType.CUSTOM_KW
|
||||
|
||||
return CustomKW.createCReference(self.valuePointer(), self)
|
||||
|
||||
def asField(self):
|
||||
""" @rtype: CustomKW """
|
||||
impl_type = self.getImplType( )
|
||||
assert impl_type == ErtImplType.FIELD
|
||||
|
||||
return Field.createCReference(self.valuePointer(), self)
|
||||
|
||||
def tryLoad(self, fs, node_id):
|
||||
"""
|
||||
@type fs: EnkfFS
|
||||
@type node_id: NodeId
|
||||
@rtype: bool
|
||||
"""
|
||||
assert isinstance(fs, EnkfFs)
|
||||
assert isinstance(node_id, NodeId)
|
||||
|
||||
return EnkfNode.cNamespace().try_load(self, fs, node_id)
|
||||
|
||||
def name(self):
|
||||
""" @rtype: str """
|
||||
return EnkfNode.cNamespace().get_name(self)
|
||||
|
||||
def load(self, fs, node_id):
|
||||
if not self.tryLoad(fs, node_id):
|
||||
raise Exception("Could not load node: %s iens: %d report: %d" % (self.name(), node_id.iens, node_id.report_step))
|
||||
|
||||
def save(self, fs, node_id):
|
||||
assert isinstance(fs, EnkfFs)
|
||||
assert isinstance(node_id, NodeId)
|
||||
|
||||
return EnkfNode.cNamespace().store(self, fs, False, node_id)
|
||||
|
||||
def free(self):
|
||||
EnkfNode.cNamespace().free(self)
|
||||
|
||||
|
||||
cwrapper = CWrapper(ENKF_LIB)
|
||||
cwrapper.registerObjectType("enkf_node", EnkfNode)
|
||||
|
||||
EnkfNode.cNamespace().free = cwrapper.prototype("void enkf_node_free(enkf_node)")
|
||||
EnkfNode.cNamespace().alloc = cwrapper.prototype("void* enkf_node_alloc(enkf_config_node)")
|
||||
EnkfNode.cNamespace().alloc_private = cwrapper.prototype("void* enkf_node_alloc_private_container(enkf_config_node)")
|
||||
EnkfNode.cNamespace().get_name = cwrapper.prototype("char* enkf_node_get_key(enkf_node)")
|
||||
|
||||
EnkfNode.cNamespace().value_ptr = cwrapper.prototype("void* enkf_node_value_ptr(enkf_node)")
|
||||
|
||||
EnkfNode.cNamespace().try_load = cwrapper.prototype("bool enkf_node_try_load(enkf_node, enkf_fs, node_id)")
|
||||
EnkfNode.cNamespace().get_impl_type = cwrapper.prototype("ert_impl_type_enum enkf_node_get_impl_type(enkf_node)")
|
||||
EnkfNode.cNamespace().store = cwrapper.prototype("bool enkf_node_store(enkf_node, enkf_fs, bool, node_id)")
|
||||
Loaded 100 of 4288 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user