7
Methods Note/ Writing Analytic Element Programs in Python by Mark Bakker 1 and Victor A. Kelson 2 Abstract The analytic element method is a mesh-free approach for modeling ground water flow at both the local and the regional scale. With the advent of the Python object-oriented programming language, it has become relatively easy to write analytic element programs. In this article, an introduction is given of the basic principles of the analytic element method and of the Python programming language. A simple, yet flexible, object-oriented design is presented for analytic element codes using multiple inheritance. New types of analytic elements may be added without the need for any changes in the existing part of the code. The presented code may be used to model flow to wells (with either a specified discharge or drawdown) and streams (with a specified head). The code may be extended by any hydrogeologist with a healthy appetite for writing computer code to solve more complicated ground water flow problems. Introduction In the not-so-distant past, many hydrogeologists were also skilled computer programmers. The originator of the analytic element method, Otto Strack, published a modu- lar FORTRAN77 code of one of the first analytic element programs in his book Groundwater Mechanics (Strack 1989). Since the introduction of graphical user interfaces, the programming skills of many hydrogeologists have declined, but the need for innovation in problem solving with models remains. The advent of object-oriented program designs and higher-level computer languages makes it possible for the next generation of ground water scientists to implement, document, and distribute their own innovations easily. In this article, we use Python, an object-oriented language with a powerful yet easy-to-understand syntax. As Python 1 Corresponding author: Water Resources Section, Faculty of Civil Engineering and Geosciences, Delft University of Technology, 2628 CN, Delft, The Netherlands, and KWR Watercycle Research Institute, 3433 PE, Nieuwegein, The Netherlands; [email protected]. 2 WHPA Inc., 320 West 8th St., Bloomington, IN 47404; [email protected] Received January 2009, accepted March 2009. Copyright © 2009 The Author(s) Journal compilation © 2009 National Ground Water Association. doi: 10.1111/j.1745-6584.2009.00583.x is an interpreted language, it is especially well suited for model development and has been applied in numer- ous scientific applications; on the downside, it generally runs slower than a compiled language such as FOR- TRAN. Python has a comprehensive standard library, and many third-party packages are available for specific tasks including scientific computing and high-quality interac- tive graphics. Python is an open-source programming language that is available free of charge, as are most of its packages (including all the packages used in this article), and is becoming increasingly popular (see http:// www.python.org/about/success). A good introduction is given by, for example, Lutz (2008). Instructions to install Python and run the programs presented in this article are given in the online Supporting Information. The analytic element method is applied to model ground water flow on both local and regional scales. As the analytic element method is a gridless method, it has the advantage that hydrogeologic features such as wells, streams, and canals may be positioned at arbitrary locations, and that their contribution to both the local detail and the regional flow field is evaluated analytically. Examples of analytic elements are wells (point sinks), which are used to model pumping or injection wells, and line-sinks, which are used to model sections of streams or canals. To the hydrogeologist, each analytic element is a hydrogeologic feature in the aquifer, while to the object-oriented programmer, each analytic element is a 828 Vol. 47, No. 6 – GROUND WATER – November-December 2009 (pages 828 – 834) NGWA.org

Writing Analytic Element Programs in Python

Embed Size (px)

Citation preview

Page 1: Writing Analytic Element Programs in Python

Methods Note/

Writing Analytic Element Programs in Pythonby Mark Bakker1 and Victor A. Kelson2

AbstractThe analytic element method is a mesh-free approach for modeling ground water flow at both the local and

the regional scale. With the advent of the Python object-oriented programming language, it has become relativelyeasy to write analytic element programs. In this article, an introduction is given of the basic principles of theanalytic element method and of the Python programming language. A simple, yet flexible, object-oriented designis presented for analytic element codes using multiple inheritance. New types of analytic elements may be addedwithout the need for any changes in the existing part of the code. The presented code may be used to modelflow to wells (with either a specified discharge or drawdown) and streams (with a specified head). The code maybe extended by any hydrogeologist with a healthy appetite for writing computer code to solve more complicatedground water flow problems.

IntroductionIn the not-so-distant past, many hydrogeologists were

also skilled computer programmers. The originator of theanalytic element method, Otto Strack, published a modu-lar FORTRAN77 code of one of the first analytic elementprograms in his book Groundwater Mechanics (Strack1989). Since the introduction of graphical user interfaces,the programming skills of many hydrogeologists havedeclined, but the need for innovation in problem solvingwith models remains.

The advent of object-oriented program designs andhigher-level computer languages makes it possible for thenext generation of ground water scientists to implement,document, and distribute their own innovations easily. Inthis article, we use Python, an object-oriented languagewith a powerful yet easy-to-understand syntax. As Python

1Corresponding author: Water Resources Section, Facultyof Civil Engineering and Geosciences, Delft University ofTechnology, 2628 CN, Delft, The Netherlands, and KWR WatercycleResearch Institute, 3433 PE, Nieuwegein, The Netherlands;[email protected].

2WHPA Inc., 320 West 8th St., Bloomington, IN 47404;[email protected]

Received January 2009, accepted March 2009.Copyright © 2009 The Author(s)Journal compilation ©2009NationalGroundWaterAssociation.doi: 10.1111/j.1745-6584.2009.00583.x

is an interpreted language, it is especially well suitedfor model development and has been applied in numer-ous scientific applications; on the downside, it generallyruns slower than a compiled language such as FOR-TRAN. Python has a comprehensive standard library, andmany third-party packages are available for specific tasksincluding scientific computing and high-quality interac-tive graphics. Python is an open-source programminglanguage that is available free of charge, as are mostof its packages (including all the packages used in thisarticle), and is becoming increasingly popular (see http://www.python.org/about/success). A good introduction isgiven by, for example, Lutz (2008). Instructions to installPython and run the programs presented in this article aregiven in the online Supporting Information.

The analytic element method is applied to modelground water flow on both local and regional scales.As the analytic element method is a gridless method,it has the advantage that hydrogeologic features such aswells, streams, and canals may be positioned at arbitrarylocations, and that their contribution to both the localdetail and the regional flow field is evaluated analytically.Examples of analytic elements are wells (point sinks),which are used to model pumping or injection wells, andline-sinks, which are used to model sections of streamsor canals. To the hydrogeologist, each analytic elementis a hydrogeologic feature in the aquifer, while to theobject-oriented programmer, each analytic element is a

828 Vol. 47, No. 6–GROUND WATER–November-December 2009 (pages 828–834) NGWA.org

Page 2: Writing Analytic Element Programs in Python

code object. Each code object stores information, andhas functions to perform operations on itself and otherobjects (e.g., Rumbaugh et al. 1991). In analytic elementmodels, computations are based on the superposition ofanalytic functions; because the influence of each elementis independent of the other elements, an objected-orienteddesign is a natural fit.

The objective of this article is to present a flex-ible object-oriented design for analytic element codesand to implement this design in a Python program; thedesign may be implemented alternatively in other object-oriented languages such as Java or C#+. This articlemay serve as an introduction to the analytic elementmethod and as an introduction to object-oriented pro-gramming in Python for hydrogeologists. After a reviewof the basic principles of the analytic element method,we present a small Python program consisting of threeclasses for simulating discharge-specified wells and uni-form flow. Next, this program is modified through theuse of the object-oriented concept of inheritance. Afterthe discussion of a procedure to solve for the unknownparameters in the model, the program is extended withhead-specified elements using the concept of multipleinheritance.

Analytic Element BasicsWe briefly discuss the principles of the analytic ele-

ment method; detailed descriptions are given in Strack(1989) and Haitjema (1995). Recent reviews of ana-lytic element formulations and applications are given inStrack (2003) and Hunt (2006), including references toformulations for unconfined flow, transient flow, three-dimensional flow and multiaquifer flow.

For steady confined Dupuit-Forchheimer flow ina homogeneous aquifer, analytic element formulationsare commonly written in terms of a discharge poten-tial �, which equals the head h multiplied by thetransmissivity T :

� = T h (1)

In the absence of areal recharge, the potential �

fulfills Laplace’s differential equation. The analytic ele-ment method is based on the superposition of ana-lytic functions. Each analytic function is called an ana-lytic element and has at least one free parameter; forsimplicity, we deal with elements with only one freeparameter in this article. The potential at a point (x, y)

may be obtained by superimposing the effects of allelements:

�(x, y) =N∑

i = 1

pi�i(x, y) (2)

where N is the number of elements, pi is the free param-eter of element i, and �i is the unit-influence functionof element i. Solutions for areal recharge may be super-imposed as well. When the model consists of Nw wells,

uniform flow, and an additive constant p0, the potentialbecomes

�(x, y) =Nw∑i = 1

pw,i�w,i(x, y) + pu�u(x, y) + p0�0

(3)

In this equation, pw,i is the discharge of well i, and�w,i is the potential influence of well i, which is theThiem solution for a unit discharge (e.g., Strack 1989,equation 6.28).

�w,i = 1

4πln[(x − xi)

2 + (y − yi)2] (4)

where (xi, yi) is the location of well i. The second termin Equation 3 is a uniform flow where the parameterpu equals the regional hydraulic gradient multiplied bythe transmissivity, and �u is the potential influence foruniform flow in a direction that makes an angle α withstraight east (the positive x direction).

�u = −x cos(α) − y sin(α) (5)

The influence function �0 of the additive constant is equalto unity. In the next section, p0 is set arbitrarily to zero;it will be included again after we present a methodologyto solve for unknown parameters so that p0 may becomputed, for example, from a head specified at a point.

A First Python Program for Wellsand Uniform Flow

Object-oriented computer programs are assemblagesof code objects. Each object stores data and processinginstructions that describe the operations that may beperformed on the objects and the interactions between oneobject and another. Take, for example, a well located at(xw, yw) = (0, 0) with a discharge Q = 1000 m3/d. Thiswell may be interpreted as an instance of the class of allwells. Each well has its own discharge and location, andknows how to calculate its potential influence at a point,using Equation 4.

A class may be considered to be a blueprint for all theinstances of the class, encapsulating information about thecommon capabilities of the class instances. The featuresof objects of the well class include attributes such asthe location and the discharge, and the various operations(called methods) that the object has at its disposal. Oneobvious method in nearly every class is the constructor.The constructor method provides instructions for creatinginstances of the class. In the well class, the constructorstores the input data as attributes and adds the objectto a model. Another method in the well class computesthe potential influence at a given (x, y) location. Whenan object-oriented program is designed well, it is clearto the programmer what each object can do, while theimplementation details are hidden. This improves use,development, and maintenance of the code.

NGWA.org M. Bakker and V.A. Kelson GROUND WATER 47, no. 6: 828–834 829

Page 3: Writing Analytic Element Programs in Python

A Python analytic element program for uniform flowand discharge-specified wells is presented in Figure 1.The program consists of three classes: Model, Well,and UniformFlow. First, consider the Well class. It hasa constructor method“__init__” that stores values forattributes such as the location of the well, its pumpingrate, and the square of its radius (the Python name of aconstructor is one of the few unfortunate choices in thelanguage). It has a “potinf” method to compute the unitpotential influence at any location with Equation 4, and a“potential” method to compute the total contribution of thewell to the potential at a point. Features of a class instancemay be accessed using “dot” notation. For example, theradius of Well object w may be accessed using the syntaxw.parameter. The radius may be changed by typing,for example, w.parameter=100.

All the instance methods in the Well class, __init__,potinf, and potential receive a parameter called “self ” asthe first entry in the parameter list. The parameter selfis a reference to the object that is to be manipulatedby the method. For example, consider the Well instancew. The code Well.potinf(w,100,100) calls thepotinf function of instance w. More commonly, how-ever, instance methods such as the potinf function arecalled with the much shorter w.potinf(100,100),which may be considered syntactic sugar for the longerexpression.

Figure 1. A first Python analytic element program for wellsand uniform flow.

The first class of the Python program in Figure 1is the Model class. The constructor of the Model classtakes one argument, the transmissivity, and creates twoattributes, the transmissivity and an empty list of elements.A list is a Python data type to store an ordered sequenceof objects and is specified between square brackets. Whenthe Model class is called with the transmissivity as anargument, the constructor is called and a Model objectis returned. For example, the syntax ml=Model(50)creates a model object called ml, which has an attributeml.T that is 50. The second method of the Model classis the head function, which we will discuss later.

The third class is the UniformFlow class. It has threemethods with the same names as the methods in theWell class. As in the Well class, in the constructor theelement is added to the model, and the parameter andsome element-specific attributes are stored. The potentialinfluence function, Equation 5, represents uniform flow.The potential method is identical to the one in the Wellclass.

Once the three classes shown in Figure 1 areimported in Python, an analytic element model may bebuilt by first defining a model and then adding uniformflow and an arbitrary number of wells to the model. Thehead at a point may be computed using the head functionof the Model class. In the head function, the potential ata point is computed by looping through all the elementsstored in the element list of the model and by adding thepotential due to each element. The head method returnsthe head value by dividing the potential by the transmis-sivity of the model. As an example, consider two wells ina uniform flow field. The input file consists of four lines:

ml = Model(200)Well(ml,0,0,600,.1)Well(ml,200,0,800,.1)UniformFlow(ml,0.01,45)

As the model is called ml, the head at (x, y) maybe computed as ml.head(x,y). A contour plot is shown inFigure 2. Python commands to create a contour plot aregiven in the Supporting Information.

InheritanceIn many cases, groups of classes have certain fea-

tures in common. For example, the Well and UniformFlowclasses both have a parameter, are added to a model inthe constructor, and have identical potential functions.Programmatically, it is often useful to combine the com-mon behaviors between classes into a base class. Specificclasses may then be derived from the base class to inheritall the attributes and methods of the base class. Object-oriented programmers refer to the process of redesigningthe relationships between classes as refactoring. The resultof refactoring the Well and UniformFlow classes is shownin Figure 3 (middle column), where the common behav-ior is combined in the Element base class. Note that thepotential method now belongs to the base class, and the

830 M. Bakker and V.A. Kelson GROUND WATER 47, no. 6: 828–834 NGWA.org

Page 4: Writing Analytic Element Programs in Python

Figure 2. Contour plot for two wells in uniform flow.

derived classes contain only the constructor and the unitinfluence methods. The Element base class has a newattribute “hasunknown” which is set to False; this attributewill be used in the next sections. The name of the baseclass appears between parentheses behind the name of theclass that is derived from the base class. In the constructormethod, the constructor of the base class is called.

Why use inheritance? For our example with twoderived classes, it seems that inheritance adds codecomplexity; however, it also reduces the need to duplicatecode. As our analytic element code gains capabilities, newelement classes will be needed, with methods that wecannot anticipate. By including as much of the commonbehavior between classes in a common base class, theamount of code required within the derived classesis reduced. Furthermore, enhancements to the commonbehaviors propagate to all the derived classes with noprogrammer effort.

When designing an object-oriented code, it is nec-essary to recognize whether related classes have “is a”or “has a”’ relationships. For example, an instance of theWell class is also an instance of the Element class, becauseevery Well is also an Element (obviously, many instancesof class Element may not be Wells). By contrast, it ispossible for an instance of one class to be a part of aninstance of another class. For example, the Model classcontains an element list with instances of class Element.

The System of EquationsIn the previous sections, we dealt with analytic

elements of which the parameter was specified by theuser. For many features, the value of the parameteris unknown a priori, but a condition is specified fromwhich the parameter may be computed. Here, we discussthe case that the head is specified at a point. Forexample, the desired head at a pumping well may bespecified, but the required discharge to obtain this headis unknown. Obviously, when there are two such wells,well interference must be accounted for: the head at each

well depends on the discharges of both wells. Hence, thedischarges of all wells, or in general all elements withunknown parameters, must be computed simultaneously.

To explain the general concept of building a systemof equations to solve for the unknown parameters, weconsider the case of two wells in a uniform flow field.The specified heads at the two wells are h1 and h2 withcorresponding potentials �1 and �2. The parameter of theuniform flow is known. Following Equation 3, we writethe potential as

�(x, y) = p1�w,1(x, y) + p2�w,2(x, y) + pu�u(x, y)

(6)

where p1 and p2 are the unknown discharges of thetwo wells. Application of the two conditions, and writingthe locations of wells 1 and 2 as (x1, y1) and (x2, y2),respectively, results in the following system of twoequations

p1�w,1(x1, y1) + p2�w,2(x1, y1) + pu�u(x1, y1) = �1

p1�w,1(x2, y2) + p2�w,2(x2, y2) + pu�u(x2, y2) = �2

(7)

Rearrangement into matrix form gives(

�w,1(x1, y1) �w,2(x1, y1)

�w,1(x2, y2) �w,2(x2, y2)

)(p1

p2

)

=(

�1 − pu�u(x1, y1)

�2 − pu�u(x1, y1)

)(8)

Solution of this linear system is straightforward. Whatmatters here is how we can build the matrix and right-hand side vector.

Note that each equation in Equation 8 is associatedwith the specified potential at one specific point, calledthe control point, corresponding to one specific element.Programmatically, we should be able to ask an elementwith an unknown parameter to create the equation forits own control point. For this purpose, each elementwith an unknown parameter will have a method calledequation. In the equation method, the element will buildone row of the matrix and a value for the right-handside (which is initially set to the specified potential) bylooping through all elements of the model. When anelement has an unknown parameter, its potinf functionis evaluated at the control point and the value is addedto the row of the matrix. If an element does not have anunknown parameter, the value of the potential function ofthe element is evaluated at the control point and subtractedfrom the right-hand side.

Implementation of Head-Specified Elementsby Multiple Inheritance

We will implement the described procedure to solvefor the free parameter of an element to create a head-specified well, where the user specifies the head at thewell, and the corresponding discharge will be calculated.(Skin effects are neglected here for brevity.) We create

NGWA.org M. Bakker and V.A. Kelson GROUND WATER 47, no. 6: 828–834 831

Page 5: Writing Analytic Element Programs in Python

Figure 3. An object-oriented analytic element program using multiple inheritance. The Model class (left), the Element baseclass and discharge-specified elements (middle), and the HeadEquation mix-in class and head-specified elements (right).

a new class called HeadWell (Figure 3, right column).Most of the functionality of HeadWell is the sameas the functionality of the Well class, and thus theHeadWell class is derived from the Well class. Additionalfunctionality of the HeadWell class is added through amix-in class. The mix-in class HeadEquation is created(Figure 3), which only contains a method to generate theequation and right-hand-side to be added to the matrix.The HeadEquation class does not even have a constructor,as it does not store any new attributes; the location of thecontrol point needs to be stored as xc, yc, and the specifiedpotential as pc. Mix-in classes are useful to collectfunctionality. The HeadWell class now inherits from twoclasses, Well and HeadEquation. This concept is referredto as multiple inheritance. As can be seen from Figure 3,multiple inheritance is a very efficient way to create theHeadWell class, which only contains a constructor withtwo lines of code. Mix-in multiple inheritance, as usedhere, is common in Python frameworks and may be usedin other languages, such as Java, via interfaces.

We also added a second head-specified class calledthe Constant (Figure 3). A constant value may be added tosolutions of Laplace’s equation (p0 in Equation 3); it maybe viewed as a reference head that is reached everywherein the aquifer when the parameters of all elements equalzero. The potential influence function is equal to unity.Rather than simply returning the value 1.0, it returns anarray of ones equal to the array size of x (so that thefunction can be called with an array rather than a singlepoint, as can be done with all the other potential influencefunctions in this article). The parameter value is unknownand is computed by specifying the value of the head atone point in the aquifer. This point is commonly calledthe reference point in the analytic element literature. Its

proper application is discussed in, for example, Haitjema(1995, p. 224). In large models with lots of elements, thereference point should be specified outside the modeledarea where it indeed represents some reference level. Ina small local model with few elements (for example, justa few wells and uniform flow), the reference point canbe very useful, as long as the modeler realizes that thereference point does not represent a physical feature butartificially fixes the head at the specified point.

Now that every head-specified element can computea row of the matrix and a right-hand side value, we need amethod that compiles the entire matrix and right-hand sidevector, solves the system, and stores the computed valuesof the unknown parameters. This method is called “solve”and forms part of the modified Model class (Figure 3, leftcolumn). The solve method of the Model class is a goodexample where Python looks like pseudocode. We startwith a matrix and a right-hand side vector that are emptylists (the square brackets). We loop through all elementsin the model and when an element has an unknownparameter, we call its equation method. The returned rowand right-hand side values are added to the matrix andright-hand side vector. Once the system of equations isbuilt, we solve the system by calling the linalg.solvefunction of the Python package numpy; the numpypackage provides functionality for scientific computing(Oliphant 2006). Once the solution vector is obtained,we again loop through all elements to set the unknownparameter values equal to the values in the solution vector.Note that the head method has been modified to be morepythonic than the code in Figure 1 (the head method couldbe one line, but that does not fit in the width of the figure).

832 M. Bakker and V.A. Kelson GROUND WATER 47, no. 6: 828–834 NGWA.org

Page 6: Writing Analytic Element Programs in Python

Head-Specified Line-SinksNow that it is easy to add head-specified elements

through inheritance from a mix-in class, we will add onemore: the line-sink. Line-sinks are versatile elements, andmay be used to model segments of rivers, streams, canals,drains, horizontal wells, or even aquifer boundaries.A line-sink is capable of discharging or recharging groundwater from a line segment. In general, the discharge ratemay vary along the line-sink (e.g., Strack 1989, Jankovicand Barnes 1999), but we limit our discussion here to aline-sink with uniform strength. Each line-sink has oneparameter, which is the amount of water discharged orrecharged per unit length. For a head-specified line-sink,the a priori unknown strength is determined by specifyingthe head (the surface water level) at the center of theline-sink in a manner similar to the head-specified welldiscussed earlier. It is noted that the HeadEquation mix-in class assumes that the head in the aquifer matches thehead at the line-sink with no entry resistance. Surfacewaters with leaky bottoms may be simulated with line-sinks, but a new mix-in class with a slightly differentequation method would be required. Note that when thewater level in a surface water feature is modeled withline-sinks, the feature must be discretized into severalsegments to account for the variation in the discharge ratealong the feature.

The potential influence function of a line-sink issomewhat more complicated and is written in terms ofa complex potential � of which the discharge potentialforms the real part, � = ��. The complex potential is

� = L

4π[(Z + 1) ln(Z + 1) − (Z − 1) ln(Z − 1)] (9)

where L is the length of the line-sink, and

Z = 2z − (z1 + z2)

z2 − z1(10)

where z = x+ jy is the complex coordinate, j is theimaginary unit (

√−1), and z1 and z2 are the complexcoordinates of the end points of the line-sink. Althoughthe complex potential may seem complicated, its imple-mentation in a Python class is straightforward. The Head-LineSink class is presented in Figure 4. More consis-tently, the HeadLineSink may be derived from a new classLineSink which has a specified inflow rate, but that is notincluded here for brevity. It is noted that 1j is the imag-inary unit in Python. The potinf function makes use ofthe nan_to_num function, which changes a not-a-numbervalue to zero. This is needed when potinf is evaluated atone of the end points of the line-sink. For example, at thefirst end point, the term (Z + 1) becomes zero and thusthe term ln(Z + 1) tends to infinity while their productapproaches zero. Python does not know about this limit,of course, and returns nan (not-a-number). An exampleof two stream segments (consisting of a total of 13 line-sinks), two head-specified wells, and a reference point isshown in Figure 5.

Figure 4. The HeadLineSink class.

Figure 5. Contour plot of an example with two streamsegments, two head-specified wells and a reference point.

ConclusionsWe have presented a flexible, object-oriented Python

design for analytic element models using multiple inher-itance. Because the Python syntax is concise yet easy toread, we were able to write classes in just a few linesof code. New element types (and thus new classes) maybe added without making any changes to the existingcode. Every element needs to be derived from the Elementbase class and needs a potential influence (potinf) method.Implementation details of the potential influence functionsare hidden: for the line-sink function a complex potentialwas used, but the other elements use real arithmetic. Asmay be seen from the examples, analytic element modelsare vector-based. For each aquifer feature (each analyticelement), an arbitrary location may be specified. Thisfacilitates the linkage with GIS systems. (Incidentally, thesoftware ArcGIS makes use of Python for their scriptinglanguage for geoprocessing [ESRI, www.esri.com]).

To make the described analytic element programeven more useful, it needs a function for the velocityvector. This means that every element needs a function tocompute its analytic contribution to the discharge vector,and the model needs a function that superimposes thecontributions of all elements, just like the head function.Once a function for the discharge vector is available,the implementation of a routine that traces path linesby taking small steps in the direction of the velocity is

NGWA.org M. Bakker and V.A. Kelson GROUND WATER 47, no. 6: 828–834 833

Page 7: Writing Analytic Element Programs in Python

straightforward (e.g., Strack 1989, Sec. 28). When arealrecharge and unconfined flow are added in addition topathline tracing, the functionality is similar to the programSLWL presented in Strack (1989).

Many analytic element programs are available, somewith graphical user interfaces. They are not reviewed here,but a list may be found on analyticelements.org/software.html. The only currently available analytic element codewritten in Python is TimML, which may be used tosimulate steady multiaquifer flow (Bakker and Strack2003; TimML is available from bakkerhydro.org). It isbased on a design that has many similarities to the designpresented in this article.

AcknowledgmentsThe authors are indebted to the demigods that cre-

ated Python, numpy, and matplotlib. Funding for ana-lytic element development in Python was provided by theU.S. EPA Ecological Research Division, Athens, Georgia;WHPA Inc., Bloomington, Indiana; and the Joint WaterResearch Program of the Dutch Water Supply Compa-nies. The comments by reviewers Matt Tonkin and JimRumbaugh were greatly appreciated.

Supporting InformationAdditional Supporting Information may be found in

the online version of this article.

Please note: Wiley-Blackwell are not responsible forthe content or functionality of any supporting materialssupplied by the authors. Any queries (other than missingmaterial) should be directed to the corresponding authorfor the article.

ReferencesBakker, M., and O.D.L. Strack. 2003. Analytic elements for

multiaquifer flow. Journal of Hydrology 271, no. 1–4:119–129.

Haitjema, H.M. 1995. Analytic Element Modeling of Ground-water Flow. San Diego, California: Academic Press.

Hunt, R.J. 2006. Ground water modeling applications using theanalytic element method. Ground Water 44, no. 1: 5–15.

Jankovic, I., and R. Barnes. 1999. High-order line elementsin modeling two-dimensional grounwater flow. Journal ofHydrology 226, no. 3–4: 211–223.

Lutz, M. 2008. Learning Python. Sebastopol, California:O’Reilly.

Oliphant, T. 2006. Guide to NumPy . Tregol Publishing. http://www.tramy.us.

Rumbaugh, J., M. Blaha, W. Premerlani, F. Eddy, and W.Lorensen. 1991. Object-Oriented Modeling and Design .Englewood Cliffs, New Jersey: Prentice Hall.

Strack, O.D.L. 2003. Theory and applications of the analyticelement method. Review of Geophysics 41, no. 2: 1005,doi:10.1029/ 2002RG000111.

Strack, O.D.L. 1989. Groundwater Mechanics . EnglewoodCliffs, New Jersey: Prentice Hall.

834 M. Bakker and V.A. Kelson GROUND WATER 47, no. 6: 828–834 NGWA.org