Introduction to Python
Python is a object oriented scripting language. The language is used in a wide range of a applications. Beeing a interpreted scripting language it is not blazingly fast in itself, but it is quite easy to extend with compiled code written in e.g. C, that is how we are making parts of the ERT functionality available in Python. The Python language has good documentation, including tutorials on http://www.python.org, however some language concepts which will be much used in the documentation will be briefly explained here. The rest of this section will focus on the following little example which contains the class defintion of a circle object.
1 class Circle : 2 @classmethod <----------------------------------------------\ 3 def CircleArea(cls , r): | 4 return math.pi * r * r | 5 | 6 def __init__( self , x0 , y0 , r): /---------------------------------------\ 7 "Create a new circle with radius r centered at (x0,y0)." | The implementation of method area() | 8 self.x0 = x0 | based on classmethod CircleArea is | 9 self.y0 = y0 | mainly to demonstrate the concept of | 10 self.r = r | classmethods, and not meant to be any | 11 | example of best practice. | 12 def shift(self, dx , dy): \---------------------------------------/ 13 "Shift the position of the circle (dx,dy)." | 14 self.x0 += dx | 15 self.y0 += dy | 16 | 17 @property | 18 def area( self ): <---------------------------------------------------/ 19 "Calculate the area of the circle." 20 return self.CircleArea( self.r ) 21 22 def distance( self , other ): 23 "Calculate the distance between two circles." 24 if isinstance( other , Circle ): 25 dx = self.x0 - other.x0 26 dy = self.y0 - other.y0 27 return math.sqrt( dx*dx + dy*dy) 28 else: 29 raise TypeError("Second argument must be Circle instance.") 30 31 32 # Create two circles 33 circle1 = Circle( 2,2,3 ) 34 circle2 = Circle( 5,5,7 ) 35 36 # Calculate the area of the circles, and the distance between them 37 print "Area of circle1:%10.3f" % circle1.area 38 print "Area of circle2:%10.3f" % circle2.area 39 print "Distance between circles: %10.3f" % circle1.distance( circle2 ) 40 41 # Move one of the circles and calculate new distance 42 circle1.shift( 3,3 ) 43 print "Distance between circles: %10.3f" % circle1.distance( circle2 )
In Python whitespace matters - the logical codeblocks are defined by the indentation level. Observe for instance the if test on line 24. If the if test evaluates to True the codeblock in lines 25-27 is executed, otherwise the statement on line 29 is executed.
Functions and methods
Functions and methods are declared with the keyword
def and the name of the function. Functions take ordinary named arguments, but can in addition have optional arguments. If an argument is optional the default value must be specified in the statement defining the function. In the function below the a gross price is calculated based on a net price and a vat rate, the vat rate is given as an optional variable:
def gross_price( net_price , vat_rate = 0.25): return net_price * (1 + vat_rate) total = gross_price( 100 ) total_taxfree = gross_price( 100 , vat_rate = 0.0 )
Optional arguments with suitable defaults is used quite a lot in the
Comments in Python are prefixed with #; everything from a # to the end of the current line is disregarded as a comment.
It is possible to add documentation strings right after the def statement, in the example above we have documentation strings on lines 7,13,19 and 23. These documentation strings will be picked up by the Python documentation tool pydoc. In the example below we use pydoc to print the documentation of the area property:
bash% pydoc XXX.Circle.area Calculate the area of the circle.
Where XXX should be the name of the module containing the Circle class. Most of the modules and classes in the ert package have reasonably good documentation strings.
Python is an object oriented language. An object is a composite variable which consists of normal variables (often called members) and functions to operate on the members (often called) methods. The class members are not declared anywhere, but automagically comes into life when referenced. In the __init__() method we create the member variables x0,y0 and r. The Circle class has ordinary methods shift() and distance; the __init__() method is quite ordinary, but it is invoked automatically by the Python runtime system when a new object is instantiated. The CircleArea is an example of a classmethod and the area method implements a property. When programming we implement a Class, and the class can viewed as a recipee to create an object. The process of creating an object is often called instantiation, and an object of a particular type is called an instance.
Classmethods are methods which can be invoked without a class instance. The example is rather contrived, but the method CircleArea in the example above can be used to calculate the area of a circle without creating a circle object first:
area = Circle.CircleArea( 10 )
In other object oriented languages these methods are often called static methods. Apart from the default constructor all the constructor methods are examples of classmethods.
The constructor is used to create a new object of a given type. The default constructor is invoked by giving the class name, as shown on lines 33 and 34 where we create two circle instances. In most cases the default Python constructor is used, but after the constructor has been called the __init__() function is called with the arguments we have supplied. In addition to the default constructor it is possible to write your own constructor based on the builtin function __new__(), this is done for some of the ert classes. When discussing the constructor in this documentation the focus is typically on the arguments which should be passed to the constructor, i.e. x0,y0 and r in this case.
The functions which give the object behaviour are called methods. The methods are quite ordinary functions, but observe the first argument self which should always be first argument in the method implementation. The self argument is a reference to the current class instance, which is used to access the the member variables and methods of the object. In Python you must always use the self handle in the class implementation; other object oriented languages like C++, C# and Java(??) use an instance variable called this; which is typically only used in situations of ambiguity.
The Circle class above has three methods: __init__(),shift() and distance(). The methods can return a value, like distance(), or just manipulate the internal state of the object like shift(). Observe that the self argument is not entered explicitly when invoking the methods, see for instance how the distance method is invoked on lines 39 and 43.
Properties are essentially methods which are invoked without the (). In the current example we have a property called area which returns the surface area of the circle. Since the surface area can be calculated based only on the internal properties of the circle instance we do not need to pass any arguments to the area function, and for syntactic sugar. Instead of the area property we could have used a 100% normal method:
def get_area(self): return math.pi * self.r * self.r ... ... print "Circle area: %10.3f" % circle.get_area()
The area property is a get property; but in general you can also have set properties which are used to modify the internal structure of the object. The ert code contains many get properties, and a few set properties.
Special functions / operators
There are several methods which can be added to the class definition which will integrate your objects with the built in operators of the language; i.e. if we defined the __add__() method for the circle we could write:
circle1 += circle2
or if we implemented the __getitem__ method we could use  operator to look up elements in the object. In the case of the circle class neither the + operator nor the  make much sense, but for many of the ert classes the algebraic operators +-*/ and/or the index operator  have been implemented.
Modules and packages
Python code is written in files and stored in directories; this is the foundation for modules and packages.
Python code is written in files, one file of Python code is called a module, and can be imported into other Python code. In the small example below we create a module which contains a class C, a function add and a scalar variable var. The whole thing is saved in a fila called module.py:
class C: def __init__(self , arg ): self.arg = arg def calc( self , v ): return ... def add( a , b): return a+b var = "Bjarne"
This module now contains three symbols: C, add and var which can be reused from another context. To be able to use the symbols from the module we must import the module, there are several minor variations over the import statement which determine which the name the symbols should be given in the importing scope.
Import all symbols under the module namespace
import module c = module.C( 10 ) print "Variable is:%s" % module.var print "Adding 10,20:%g" % module.add(10,20)
I.e. all the symbols from module.py have become available, but we must prefix them with module to use them.
Import all the symbols from module into the current namespace
from module import * c = C( 10 ) print "Variable is:%s" % var print "Adding 10,20:%g" % add(10,20)
Again all the symbols from module.py are available, but now they have been imported all the way into the current namespace and can be accessed without the module prefix. This method leads to a massive namespace pollution and is not generally recommended.
Selectively importing some symbols
from module import C import module.add c = C( 10 ) print "Adding 10,20:%g" % module.add(10,20)
We have imported the C and add symbols; the C symbol has been imported all the way into the current namespace, whereas the add symbol is available under the module namespace. The var symbol has not been imported, and if we try to access that, using either var or module.var will we get an error.
Import and rename
import module as myModule c = myModule.C( 10 ) print "Adding 10,20:%g" % myModule.add(10,20)
Here we have imported all of module, but we access it internally as myModule. In the ert documentation the import statement
import ert.ecl.ecl as ecl
is used quite frequently, the 'as ecl' is just to simplify so that symbols in the ert.ecl.ecl module can be accessed as ecl.XXXX instead of ert.ecl.ecl.XXXX.
Modules are just ordinary Python files, in the same way packages are just ordinary directories, with the special file __init__.py in them; the __init__.py file can be empty - but it must exist in the directory. The files and directories contained in the package directory will then be modules and subpackages. In the ert distribution the package, subpackage, module looks 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.
More details about modules and packages can be found in import.txt.
The ert package
All of ert Python is organized as a Python package. The top level package ert has nearly no content, but contains subpackages ert.ecl, ert.job_queue and ert.util. Each of the subpackages contain many modules, as the module ert.ecl.ecl_grid which implements functionality to work with ECLIPSE grids. Each of the modules typically implement one or a few related classes, like the class EclGrid which is implemented in the ert.ecl.ecl_grid module.
Getting started with ert Python
If the ert python package, the shared libraries libecl.so, libutil.so, libconfig.co and libjob_queue.so and Python itself have all been correctly installed at your site, the following script should work:
#!/usr/bin/env python import ert print "Python: the ert package has been sucessfuly loaded"
If this does not work; find someone to blame or read install.txt to fix the mess yourself.
This wiki contains quite a lot of documentation of the classes and modules of the ert Python distribution, however the Python code has some builtin documentation which can be accessed with the application pydoc; at least for fast lookups this can be quite convenient. E.g. to get an overview of the ert.ecl package you can type pydoc ert.ecl at the command prompt:
bash% pydoc ert.ecl Help on package ert.ecl in ert: NAME ert.ecl - Package for working with ECLIPSE files. FILE /project/res/x86_64_RH_5.4/lib/ert/trunk/python/ert/ecl/__init__.py DESCRIPTION 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. .... ....
The sub packages
The ert.ecl subpackage
The most mature, and probably most generally useful part of the library is the python wrapping of the libecl library: ert.ecl
The ert.job_queue subpackage
The system for administrating external jobs is implemented in the libjob_queue library: ert.job_queue
The ert.util subpackage
Many small utility functions and datatypes - you might need some of these when interfacing to the rest of the library: ert.util
How ert python works
If you want modify the ert Python code itself there is some documentation for the aspiring hacker here: hacking ert.py