Introduction#
What is PyTOUGH?#
PyTOUGH (Python TOUGH) is a set of Python software routines for making it easier to use the TOUGH2 geothermal reservoir simulator. Using PyTOUGH, it is possible to automate the creation and editing of TOUGH2 model grids and data files, and the analysis and display of model simulation results.
What are TOUGH2 and AUTOUGH2?#
TOUGH2 is a general-purpose simulator for modelling subsurface fluid and heat flow, often used for simulating geothermal reservoirs.
AUTOUGH2 is the University of Auckland version of TOUGH2. The main differences between AUTOUGH2 and TOUGH2 are:
EOS handling: AUTOUGH2 includes all different equations of state (EOSes) in a single executable program, whereas TOUGH2 uses different executables for each EOS. As a result, the main input data file for an AUTOUGH2 simulation also includes extra data blocks to specify which EOS is to be used.
Generator types: AUTOUGH2 includes a variety of extra generator types developed for geothermal reservoir simulation (e.g. makeup and reinjection wells).
TOUGH2_MP is a multi-processor version of TOUGH2. TOUGH+ is a redeveloped version of TOUGH2, with a more modular code structure implemented in Fortran-95. TOUGH3 is another parallelized redevelopment of TOUGH2.
TOUGH2 data files#
TOUGH2 takes its main input from a data file, which contains information about the model grid, simulation parameters, time stepping, sources of heat and mass etc. The data file formats for TOUGH2 and AUTOUGH2 are almost identical, with minor differences. TOUGH2_MP can read TOUGH2 data files, but also supports some extensions (e.g. for 8-character instead of 5-character block names) to this format. PyTOUGH does not currently support the TOUGH2_MP extensions. TOUGH+ and TOUGH3 data files can also have some extensions, which PyTOUGH does not support as yet.
Because TOUGH2 uses a finite volume formulation, the only model grid data it needs are the volumes of the grid blocks and the distances and areas associated with the connections between blocks. Hence, the TOUGH2 data file need not contain any information about the specific locations of the blocks in space, and it contains no information about the locations of the vertices or edges of the blocks. This makes it easy to use TOUGH2 to simulate one-, two- or three-dimensional models, all with the same format of data file. However, this lack of reference to any coordinate system also makes it more difficult to generate model grids, and to visualise simulation results in space.
MULgraph geometry files#
For this reason, a separate geometry file can be used to create grids for TOUGH2 simulations and visualise simulation results. The geometry file contains information about the locations of the grid block vertices. The geometry file can be used to visualise results using the TIM graphical interface for TOUGH2 and AUTOUGH, developed at the University of Auckland. (This file format was originally designed for use with TIM’s predecessor, MULgraph).
The MULgraph geometry file assumes the grid has a layered structure, with blocks arranged in layers and columns, and the same arrangement of columns on each layer. (At the top of the model grid, blocks in some columns may be missing, to allow the grid to follow the surface topography.)
If you do not have a MULgraph geometry file for your model, it is easy to create one for a rectangular grid. In fact, PyTOUGH is able to reverse-engineer a MULgraph geometry from a TOUGH2 data file containing a rectangular grid.
A specification of the MULgraph geometry file format can be found here.
TOUGH2 listing files#
The output from TOUGH2 is written to a listing file, which is a text file containing tables of results for each time step (or only selected time steps, if preferred). At each time step there is an ‘element table’, containing results for block properties (e.g. pressure, temperature etc.). There may also be a ‘connection table’, containing results for flows between blocks, and a ‘generation table’, containing results (e.g. flow rates) at the generators in the model (e.g. wells).
The formats of the listing files produced by TOUGH2, AUTOUGH2, TOUGH2_MP, TOUGH+ and TOUGH3 are all slightly different, and also vary depending on the EOS used. However, PyTOUGH attempts to detect and read all of these formats.
What is Python?#
Python is a general-purpose programming language. It is free and open-source, and runs on many different computer operating systems (Linux, Windows, Mac OS X and others). Python can be downloaded from the Python website, which also contains detailed reference material about the Python language. If you are using Linux you probably already have Python, as it is included in most Linux distributions.
PyTOUGH should run on any version of Python 2.x newer than 2.4 (though version 2.6 or newer is recommended). PyTOUGH version 1.5 or later also runs on Python 3.x.
If you are unfamiliar with Python (even if you have used another programming language before), it is highly recommended that you do one of the many Python tutorials available online, e.g.
Python basics#
Objects#
Python is what is known as an object-oriented language, which means that it is possible to create special customised data types, or ‘classes’, to encapsulate all the properties and behaviour of the things (objects) we are dealing with in a program. This is a very useful way of simplifying complex programs. (In fact, in Python, everything is treated as an object, even simple things like integers and strings.)
For example, in a TOUGH2 model grid we have collections of grid blocks,
and we need to store the names of these blocks and their volumes and
rock types. In a non-object-oriented language, these could be stored in
three separate arrays: a string array for the names, a real (or ‘float’)
array for the volumes and another string array for the rock types. In an
object-oriented language like Python, we can define a new data type (or
‘class’) for blocks, which holds the name, volume and rock type of the
block. If we declare an object called blk
of this block class, we
can access or edit its volume by referring to blk.volume
. In this
way, we can store our blocks in one single array of block objects. When
we add or delete blocks from our grid, we can just add or delete block
objects from the array, rather than having to keep track of three
separate arrays.
In general, an object not only has properties (like
blk.volume
) but also methods, which are functions the object
can carry out. For example, if we wanted to rotate a MULgraph geometry
file by 30°, we could do this in PyTOUGH by declaring a MULgraph
geometry file object called geo
, and calling its rotate
method: geo.rotate(30)
. The methods of an object are accessed in
the same way that its properties are accessed: by adding a dot (.)
after the object’s name and then adding the name of the property or
method. Any arguments of the method (e.g. the angle in the rotate
function above) are added in parentheses afterwards.
Lists, dictionaries, tuples and sets#
Most programming languages have simple data types built in, e.g. float, double precision or integer numbers, strings, and arrays of these. Python has some other data types which are very useful and are used a lot.
The first of these is the list. A list can contain any ordered
collection of objects, of any type, or even of different types, and is
delimited by square brackets. So for example we can declare a list
things = [1, 'two',3.0]
containing an integer, a string and a float.
We can access the list’s elements in much the same way as we access the
elements of an array, for example things[1]
would return the value
'two'
(note that in Python, as in most other languages besides
Fortran, the indices of arrays and lists start at 0, not 1). Additional
elements can be added to a list at any time, without having to
re-declare the size of the list: for example, things.append('IV')
would add an extra element to the end of the list, giving it the value
[1, 'two', 3.0, 'IV']
. It is also possible to remove elements from a
list, e.g. things.remove(3.0)
, which would give our list the value
[1, 'two', 'IV']
.
Another useful Python data type is the dictionary. Dictionaries are
mainly used to store collections of objects (again, of any type or of
different types) that are referenced by name rather than by index (as in
an array or list). A dictionary is delimited by curly brackets. So for
example we can declare a dictionary
phone = {'Eric':8155, 'Fred':2350, 'Wilma':4667}
and then find
Fred’s phone number from phone['Fred']
, which would return 2350
.
For TOUGH2 models, blocks, generators, rock types and other objects are
often referred to by name rather than index, so dictionaries are an
appropriate way to store them.
A third Python data type, similar to a list, is the tuple. A tuple
is essentially a list that cannot be changed, and is often used just for
grouping objects together. A tuple is delimited by parentheses. For
example, things = (1, 'two', 3.0)
declares a tuple with three
elements. We can still refer to the elements of a tuple using e.g.
a[1]
, but we cannot assign new values to these elements or add or
remove elements from the tuple once it has been declared.
Python also has a set data type, which represents a mathematical set
- an unordered collection of objects. One of the useful aspects of sets
is that they cannot contain duplicate items. As a result, for example,
duplicate items can be removed from a list x
simply by converting it
to a set, and then back to a list: x = list(set(x))
.
How to run Python#
Python can be run either interactively or via scripts.
Running Python interactively#
The simplest way to run Python interactively is just by typing
python
(or possibly python3
) at the command line. (On Windows
the directory that Python was installed into may have to be added to
your PATH
environment variable first.) The command line then becomes
an interactive Python environment in which you can type Python commands
at the Python command prompt >>>
, e.g.:
bob@superbox:~$ python3
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> things = [1, 'two', 3.0]
>>> print(things[1])
two
>>> exit()
bob@superbox:~$
In the interactive Python environment, you can view help on the
properties and methods of any Python object by typing
help(objectname)
, where objectname
is the name of an object that
has been declared. This will list the properties and methods of the
object and a description of each one.
You can exit the interactive Python environment by typing exit()
or
Ctrl-Z
on Windows, or Ctrl-D
on Linux.
Python scripts#
The real power of Python, however, lies in using it to write scripts
to automate repetitive or complex tasks. You can just type Python
commands into a text file, save it with the file extension .py
, and
execute it by typing python filename.py
, where filename.py
is
the name of the file. (Once again, on Windows the directory that Python
was installed into may have to be added to your PATH
environment
variable first.)
You can also debug a Python script using the ‘pdb’ command-line
debugger. Typing python -m pdb filename.py
will start debugging the
script filename.py.
It is also possible to run a Python script from within the interactive
Python environment. From the Python environment command line, typing
execfile('filename.py')
will execute the script filename.py
.
Python libraries#
Python comes with a large number of features already built in, but for specialised tasks, additional libraries of Python software can be imported into Python as you need them. PyTOUGH itself is a set of such libraries, and it in turn makes use of some other third-party Python libraries. The most important of these are as follows:
Numerical Python (“NumPy”)#
NumPy adds a special numpy.array
class for
fast multi-dimensional arrays, which PyTOUGH makes heavy use of, and a
whole range of other features, e.g. linear algebra routines, Fourier
transforms and statistics.
Scientific Python (“SciPy”)#
SciPy is a library of advanced mathematical functions (e.g. interpolation, calculus, optimisation), needed for some PyTOUGH functionality.
Matplotlib#
Matplotlib is a library of graphical plotting routines, which can be used for 2-D PyTOUGH visualization tools like layer and slice plots.
Other libraries#
Some parts of PyTOUGH use other Python libraries. You do not need to install these libraries unless you are using the parts of PyTOUGH that depend on them. If you try to use parts of PyTOUGH that need these libraries, and you don’t have them installed, it will tell you so.
Examples:
VTK, a Python interface to the Visualization Tool Kit, a library for 3D visualisation of data via VTK itself, or software such as ParaView, Mayavi etc.
meshio, a library for 3D mesh handling – used for exporting PyTOUGH grids to other formats
Importing libraries#
To use any Python library, you just need to import it first. For
example, once you have installed Numerical Python, you can make it
available (in the interactive Python environment or in a Python script)
by typing the command import numpy
, or alternatively
from numpy import *
. This imports all classes and commands from
Numerical Python and makes them available for use. (You can also import
only parts of a library rather than the whole thing, e.g.
from numpy import linalg
just imports the linear algebra routines
from Numerical Python.)
When you import a library, you can also change its name. For example,
PyTOUGH imports Numerical Python using the command
import numpy as np
, which renames numpy
as the abbreviated
np
. This means it can, for example, access the Numerical Python
numpy.array
data type as np.array
. It also means you have access
to Numerical Python as np
in your own scripts and in the interactive
Python environment, without having to import it yourself.
Installing PyTOUGH#
From version 1.6.0, the easiest way to install PyTOUGH is via the
pip
Python package installer:
pip install PyTOUGH
or
python -m pip install PyTOUGH
either of which will install the latest version of PyTOUGH, together
with its main dependency libraries (numpy
, scipy
and
matplotlib
) if these are not already detected on your system.
You can also install a particular version of PyTOUGH, e.g. to install version 1.6.0:
pip install PyTOUGH==1.6.0
or upgrade your existing version of PyTOUGH:
pip install --upgrade PyTOUGH
There are various ways of configuring the installation of packages with
pip
, which may be suitable for your particular system – consult the
pip
documentation for details.
After installing, you should be able to import the PyTOUGH libraries
into the Python interactive environment or your Python scripts, from any
directory on your computer. For example, you can import the MULgraph
geometry library using from mulgrids import *
(see MULgraph geometry files).
To uninstall PyTOUGH:
pip uninstall PyTOUGH
Installing the testing branch#
The PyTOUGH code exists in two main “branches”: the master
branch,
which contains the latest stable release, and the testing
branch,
which includes the most recent changes being tested for inclusion in the
next stable release.
If you need these most recent changes and can’t wait for the next stable
release, it is possible to install the testing
branch of PyTOUGH
using e.g.:
pip install git+https://github.com/acroucher/PyTOUGH.git@testing
Testing PyTOUGH#
PyTOUGH includes a suite of “unit tests” which can be used to verify
that it is working correctly. These are located in the tests/
directory of the PyTOUGH repository, which includes a number of Python
scripts for testing individual PyTOUGH modules.
First you will the PyTOUGH repository on your machine. This is available
here. Click the Code
button
which gives various options for downloading the repository, via e.g. zip
file or Git clone.
The unit test modules in the tests/
directory may be run
individually, the same way as any other Python script would be run. If
the tests in the script all pass, the last message printed out to the
console will read OK
. If not, details will be output regarding which
tests did not pass.
It is also possible to run the unit tests for all modules by running the
following command in the tests/
directory:
python -m unittest discover
or with the -v
(verbose) flag to output more detail on which tests
are being run:
python -m unittest discover -v
Licensing#
PyTOUGH is free software, distributed under the GNU Lesser General Public License (LGPL). More information is available here.