#804 Updated ERT based on 6287acdb0d039ce13eb9956b67035280a3de9539

This commit is contained in:
Magne Sjaastad committed 2016-10-13 13:40:43 +02:00
1 parent 7269e6e165
commit 3b4f1620a5
4288 files changed
+19102 -16971

No files matched your search

+39
View File
@@ -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 )
+7
View File
@@ -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.
+27
View File
@@ -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()
+429
View File
@@ -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.
+231
View File
@@ -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
View File
@@ -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
View File
@@ -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 )
+16
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+62
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+39
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+35
View File
@@ -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
@@ -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")
@@ -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")
+37
View File
@@ -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]")
+12
View File
@@ -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)
+32
View File
@@ -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()
+47
View File
@@ -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)
+62
View File
@@ -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)
+35
View File
@@ -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
View File
@@ -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
View File
@@ -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
+42
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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( )
+84
View File
@@ -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
File diff suppressed because it is too large. Load diff
+29
View File
@@ -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)
File diff suppressed because it is too large. Load diff
+206
View File
@@ -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"]
View File
Whitespace-only changes.
File diff suppressed because it is too large. Load diff
+135
View File
@@ -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
View File
@@ -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( )
+203
View File
@@ -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( )
+145
View File
@@ -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( )
+116
View File
@@ -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)
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)
+27
View File
@@ -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 )
+61
View File
@@ -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")
+286
View File
@@ -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
View File
@@ -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
+644
View File
@@ -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)
+256
View File
@@ -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
View File
@@ -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 ))
+13
View File
@@ -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)
+1
View File
@@ -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]
+56
View File
@@ -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)
+96
View File
@@ -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
+45
View File
@@ -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)")
+152
View File
@@ -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)
+11
View File
@@ -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",]
+87
View File
@@ -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)")
+131
View File
@@ -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