Index: /anuga_work/development/2010-projects/anuga_1d/base/__init__.py
===================================================================
--- /anuga_work/development/2010-projects/anuga_1d/base/__init__.py	(revision 7850)
+++ /anuga_work/development/2010-projects/anuga_1d/base/__init__.py	(revision 7850)
@@ -0,0 +1,9 @@
+#! /usr/bin/python
+
+# To change this template, choose Tools | Templates
+# and open the template in the editor.
+
+__author__="Stephen Roberts"
+__date__ ="$14/06/2010 7:50:20 PM$"
+
+pass
Index: /anuga_work/development/2010-projects/anuga_1d/base/config.py
===================================================================
--- /anuga_work/development/2010-projects/anuga_1d/base/config.py	(revision 7850)
+++ /anuga_work/development/2010-projects/anuga_1d/base/config.py	(revision 7850)
@@ -0,0 +1,112 @@
+"""Module where global model parameters are set for anuga_1d
+"""
+
+epsilon = 1.0e-12
+h0 = 1.0e-12
+
+default_boundary_tag = 'exterior'
+
+
+time_format = '%d/%m/%y %H:%M:%S'
+
+min_timestep = 1.0e-6 #Should be computed based on geometry
+max_timestep = 1.0e3
+max_smallsteps = 50  # Max number of degenerate steps allowed b4 trying first order
+#This is how:
+#Define maximal possible speed in open water v_max, e.g. 500m/s (soundspeed?)
+#Then work out minimal internal distance in mesh r_min and set
+#min_timestep = r_min/v_max
+#
+#Max speeds are calculated in the flux function as
+#
+#lambda = v +/- sqrt(gh)
+#
+# so with 500 m/s, h ~ 500^2/g = 2500 m well out of the domain of the
+# shallow water wave equation
+#
+#The actual soundspeed can be as high as 1530m/s
+#(see http://staff.washington.edu/aganse/public.projects/clustering/clustering.html),
+#but that would only happen with h>225000m in this equation. Why ?
+#The maximal speed we specify is really related to the max speed
+#of surface pertubation
+#
+
+
+v_max = 100 #For use in domain_ext.c
+sound_speed = 500
+
+
+max_smallsteps = 50  #Max number of degenerate steps allowed b4 trying first order
+
+manning = 0.0  #Manning's friction coefficient
+g = 9.8       #Gravity
+#g(phi) = 9780313 * (1 + 0.0053024 sin(phi)**2 - 0.000 0059 sin(2*phi)**2) micro m/s**2, where phi is the latitude
+#The 'official' average is 9.80665
+
+
+
+
+eta_w = 3.0e-3 #Wind stress coefficient
+rho_a = 1.2e-3 #Atmospheric density
+rho_w = 1023   #Fluid density [kg/m^3] (rho_w = 1023 for salt water)
+
+
+#Betas [0;1] control the allowed steepness of gradient for second order
+#extrapolations. Values of 1 allow the steepes gradients while
+#lower values are more conservative. Values of 0 correspond to
+#1'st order extrapolations.
+#
+# Large values of beta_h may cause simulations to require more timesteps
+# as surface will 'hug' closer to the bed.
+# Small values of beta_h will make code faster, but one may experience
+# artificial momenta caused by discontinuities in water depths in
+# the presence of steep slopes. One example of this would be
+# stationary water 'lapping' upwards to a higher point on the coast.
+#
+#
+#
+#There are separate betas for the w-limiter and the h-limiter
+#
+#
+#
+#
+#Good values are:
+#beta_w = 0.9
+#beta_h = 0.2
+
+
+
+beta_w = 1.5
+beta_h = 0.2
+timestepping_method = 'euler'
+
+CFL = 1.0  #FIXME (ole): Is this in use yet??
+           #(Steve) yes, change domain.CFL to
+           #make changes
+
+
+pmesh_filename = '.\\pmesh'
+
+
+import os, sys
+
+if sys.platform == 'win32':
+    #default_datadir = 'C:\grohm_output'
+    default_datadir = '.'
+else:
+    #default_datadir = os.path.expanduser('~'+os.sep+'grohm_output')
+    default_datadir = '.'
+
+
+use_extensions = True    #Try to use C-extensions
+#use_extensions = False   #Do not use C-extensions
+
+use_psyco = True  #Use psyco optimisations
+#use_psyco = False  #Do not use psyco optimisations
+
+
+optimised_gradient_limiter = True #Use hardwired gradient limiter
+
+#Specific to shallow water W.E.
+h_min = minimum_allowed_height = 1.0e-6 #1.0e-6 #Water depth below which it is considered to be 0
+maximum_allowed_speed = 1000.0
Index: /anuga_work/development/2010-projects/anuga_1d/base/generic_boundary_conditions.py
===================================================================
--- /anuga_work/development/2010-projects/anuga_1d/base/generic_boundary_conditions.py	(revision 7850)
+++ /anuga_work/development/2010-projects/anuga_1d/base/generic_boundary_conditions.py	(revision 7850)
@@ -0,0 +1,498 @@
+"""boundary.py - Classes for implementing boundary conditions
+"""
+
+import numpy
+
+class Boundary:
+    """Base class for boundary conditions.
+       Specific boundary conditions must provide values for
+       the conserved_quantities
+
+       A boundary object has one neighbour; the one it
+       serves.
+    """
+
+    def __init__(self):
+        pass
+
+    def evaluate(self, vol_id=None, edge_id=None):
+        msg = 'Generic class Boundary must be subclassed'
+        raise msg
+
+
+class Transmissive_boundary(Boundary):
+    """Transmissive boundary returns same conserved quantities as
+    those present in its neighbour volume.
+
+    Underlying domain must be specified when boundary is instantiated
+    """
+
+    def __init__(self, domain = None):
+        Boundary.__init__(self)
+
+        if domain is None:
+            msg = 'Domain must be specified for transmissive boundary'
+            raise msg
+
+        self.domain = domain
+
+    def __repr__(self):
+        return 'Transmissive_boundary(%s)' %self.domain
+
+    def evaluate(self, vol_id, edge_id):
+        """Transmissive boundaries return the edge values
+        of the volume they serve.
+        """
+
+        q = self.domain.get_conserved_quantities(vol_id, edge = edge_id)
+        return q
+
+
+class Dirichlet_boundary(Boundary):
+    """Dirichlet boundary returns constant values for the
+    conserved quantities
+    """
+
+
+    def __init__(self, conserved_quantities=None):
+        Boundary.__init__(self)
+
+        if conserved_quantities is None:
+            msg = 'Must specify one value for each conserved quantity'
+            raise msg
+
+        self.conserved_quantities=numpy.array(conserved_quantities, numpy.float)
+
+    def __repr__(self):
+        return 'Dirichlet boundary (%s)' %self.conserved_quantities
+
+    def evaluate(self, vol_id=None, edge_id=None):
+        return self.conserved_quantities
+
+
+
+class Time_boundary(Boundary):
+    """Time dependent boundary returns values for the
+    conserved quantities as a function of time.
+    Must specify domain to get access to model time and a function f(t)
+    which must return conserved quantities as a function time
+    """
+
+
+    def __init__(self, domain = None, f = None):
+        Boundary.__init__(self)
+
+        try:
+            q = f(0.0)
+        except Exception, e:
+            msg = 'Function for time boundary could not be executed:\n%s' %e
+            raise msg
+
+        try:
+            q = numpy.array(q, numpy.float)
+        except:
+            msg = 'Return value from time boundary function could '
+            msg += 'not be converted into a numpy array of numpy.floats.\n'
+            msg += 'Specified function should return either list or array.'
+            raise msg
+
+        msg = 'ERROR: Time boundary function must return a 1d list or array '
+        assert len(q.shape) == 1, msg
+
+        d = len(domain.conserved_quantities)
+        msg = 'Return value for f must be a list or an array of length %d' %d
+        assert len(q) == d, msg
+
+        self.f = f
+        self.domain = domain
+
+    def __repr__(self):
+        return 'Time boundary'
+
+    def evaluate(self, vol_id=None, edge_id=None):
+        return self.f(self.domain.time)
+
+
+class File_boundary_time(Boundary):
+    """Boundary values obtained from file and interpolated.
+    conserved quantities as a function of time.
+
+    Assumes that file contains a time series.
+
+    No spatial info assumed.
+    """
+
+    #FIXME: Is this still necessary
+
+    def __init__(self, filename, domain):
+        import time
+        from config import time_format
+        from util import File_function
+
+        Boundary.__init__(self)
+
+        self.F = File_function(filename, domain)
+        self.domain = domain
+
+        #Test
+        q = self.F(0)
+
+        d = len(domain.conserved_quantities)
+        msg = 'Values specified in file must be a list or an array of length %d' %d
+        assert len(q) == d, msg
+
+
+    def __repr__(self):
+        return 'File boundary'
+
+    def evaluate(self, vol_id=None, edge_id=None):
+        """Return linearly interpolated values based on domain.time
+
+        vol_id and edge_id are ignored
+        """
+
+        t = self.domain.time
+        return self.F(t)
+
+
+
+
+class File_boundary(Boundary):
+    """Boundary values obtained from file and interpolated.
+    conserved quantities as a function of time.
+
+    Assumes that file contains a time series and possibly
+    also spatial info.
+    See docstring for File_function in util.py for details about
+    admissible file formats
+
+    The full solution history is not exactly the same as if
+    the models were coupled:
+    File boundary must read and interpolate from *smoothed* version
+    as stored in sww and cannot work with the discontinuos triangles.
+
+    """
+
+    def __init__(self, filename, domain, verbose = False):
+        import time
+        from config import time_format
+        from util import file_function
+
+        Boundary.__init__(self)
+
+        #Get x,y vertex coordinates for all triangles
+        V = domain.vertex_coordinates
+
+        #Compute midpoint coordinates for all boundary elements
+        #Only a subset may be invoked when boundary is evaluated but
+        #we don't know which ones at this stage since this object can be attached to
+        #any tagged boundary later on.
+
+        if verbose: print 'Find midpoint coordinates of entire boundary'
+        self.midpoint_coordinates = numpy.zeros( (len(domain.boundary), 2), numpy.float)
+        boundary_keys = domain.boundary.keys()
+
+
+        xllcorner = domain.geo_reference.get_xllcorner()
+        yllcorner = domain.geo_reference.get_yllcorner()        
+        
+
+        #Make ordering unique #FIXME: should this happen in domain.py?
+        boundary_keys.sort()
+
+
+        #Record ordering #FIXME: should this also happen in domain.py?
+        self.boundary_indices = {}
+        for i, (vol_id, edge_id) in enumerate(boundary_keys):
+
+            x0 = V[vol_id, 0]; y0 = V[vol_id, 1]
+            x1 = V[vol_id, 2]; y1 = V[vol_id, 3]
+            x2 = V[vol_id, 4]; y2 = V[vol_id, 5]
+
+            #Compute midpoints
+            if edge_id == 0: m = array([(x1 + x2)/2, (y1 + y2)/2])
+            if edge_id == 1: m = array([(x0 + x2)/2, (y0 + y2)/2])
+            if edge_id == 2: m = array([(x1 + x0)/2, (y1 + y0)/2])
+
+            #Convert to absolute UTM coordinates
+            m[0] += xllcorner
+            m[1] += yllcorner
+            
+            #Register point and index
+            self.midpoint_coordinates[i,:] = m
+            self.boundary_indices[(vol_id, edge_id)] = i
+
+
+        if verbose: print 'Initialise file_function'
+        self.F = file_function(filename, domain,
+	                       interpolation_points=self.midpoint_coordinates,
+                               verbose = verbose)
+        self.domain = domain
+
+        #Test
+        q = self.F(0, point_id=0)
+
+        d = len(domain.conserved_quantities)
+        msg = 'Values specified in file %s must be ' %filename
+        msg += ' a list or an array of length %d' %d
+        assert len(q) == d, msg
+
+
+    def __repr__(self):
+        return 'File boundary'
+
+
+    def evaluate(self, vol_id=None, edge_id=None):
+        """Return linearly interpolated values based on domain.time
+
+        vol_id and edge_id are ignored
+        """
+
+        t = self.domain.time
+
+        if vol_id is not None and edge_id is not None:
+            i = self.boundary_indices[ vol_id, edge_id ]
+            return self.F(t, point_id = i)
+        else:
+            #raise 'Boundary call without point_id not implemented'
+            #FIXME: What should the semantics be?
+            return self.F(t)
+
+
+
+
+
+
+
+
+#THIS FAR (10/8/4)
+class Connective_boundary(Boundary):
+    """Connective boundary returns values for the
+    conserved quantities from a volume as defined by a connection table
+    mapping between tuples of (volume id, face id) for volumes that
+    have their boundaries connected.
+
+    FIXME: Perhaps include possibility of mapping between
+    different domains as well
+
+    FIXME: In case of shallow water we may want to have a
+    special version that casts this in terms of height rather than stage
+    """
+
+
+    def __init__(self, table):
+        from domain import Volume
+
+        Boundary.__init__(self)
+
+        self.connection_table = table
+        self.Volume = Volume
+
+    def __repr__(self):
+        return 'Connective boundary'
+
+    #FIXME: IF we ever need to get field_values from connected volume,
+    #that method could be overridden here (using same idea as in
+    #get_conserved_quantities
+    #def get_field_values()
+
+    def get_conserved_quantities(self, volume, face=0):
+
+        id = volume.id
+        if self.connection_table.has_key((id, face)):
+            other_id, other_face = self.connection_table[(id, face)]
+
+            other_volume = self.Volume.instances[other_id]
+            cmd = 'q = other_volume.conserved_quantities_face%d' %face;
+            exec(cmd)
+            return q
+        else:
+            msg = 'Volume, face tuple ($d, %d) has not been mapped'\
+                  %(id, face)
+            raise msg
+
+
+
+
+
+#FIXME: Add a boundary with a general function of x,y, and t
+
+#FIXME: Add periodic boundaries e.g.:
+# Attempt at periodic conditions from advection_spik. Remember this
+#
+#first = 2*(N-1)*N
+#for i in range(1,2*N+1,2):
+#    k = first + i-1#
+#
+#    print i,k
+#
+#    domain[i].faces[2].neighbour = domain[k].faces[1]
+#    domain[k].faces[1].neighbour = domain[i].faces[2]
+
+
+
+class General_boundary(Boundary):
+    """General boundary which can compute conserved quantities based on
+    their previous value, conserved quantities of its neighbour and model time.
+
+    Must specify initial conserved quantities,
+    neighbour,
+    domain to get access to model time
+    a function f(q_old, neighbours_q, t) which must return
+    new conserved quantities q as a function time
+
+    FIXME: COMPLETE UNTESTED - JUST AN IDEA
+    """
+
+    def __init__(self, neighbour=None, conserved_quantities=None, domain=None, f=None):
+        Boundary.__init__(self, neighbour=neighbour, conserved_quantities=conserved_quantities)
+
+        self.f = f
+        self.domain = domain
+
+
+    def get_conserved_quantities(self, volume=None, face=0):
+        return self.f(self.conserved_quantities,
+                      neighbour.conserved_quantities,
+                      self.domain.time)
+
+
+
+
+
+class File_boundary_old(Boundary):
+    """Boundary values obtained from file and interpolated.
+    conserved quantities as a function of time.
+
+    Assumes that file contains a time series.
+
+    No spatial info assumed.
+    """
+
+
+    def __init__(self, domain = None, filename = None):
+        import time
+        from config import time_format
+
+        Boundary.__init__(self)
+
+
+        try:
+            fid = open(filename)
+        except Exception, e:
+            msg = 'Boundary file %s could not be opened: %s\n' %(filename, e)
+            raise msg
+
+
+        line = fid.readline()
+        fid.close()
+        fields = line.split(',')
+        msg = 'File %s must have the format date, values'
+        assert len(fields) == 2, msg
+
+        try:
+            starttime = time.mktime(time.strptime(fields[0], time_format))
+        except ValueError:
+            msg = 'First field in file %s must be' %filename
+            msg += ' date-time with format %s.\n' %time_format
+            msg += 'I got %s instead.' %fields[0]
+            raise msg
+
+        #Split values
+        values = []
+        for value in fields[1].split():
+            values.append(numpy.float(value))
+
+        q = array(values)
+
+        msg = 'ERROR: File boundary function must return a 1d list or array '
+        assert len(q.shape) == 1, msg
+
+        d = len(domain.conserved_quantities)
+        msg = 'Values specified in file must be a list or an array of length %d' %d
+        assert len(q) == d, msg
+
+        self.filename = filename
+        self.domain = domain
+        self.starttime = starttime
+
+        if domain.starttime is None:
+            domain.starttime = starttime
+        else:
+            msg = 'Start time as specified in domain (%s) is earlier '
+            'than the starttime of file %s: %s'\
+                  %(domain.starttime, self.filename, self.starttime)
+            if self.starttime > domain.starttime:
+                raise msg
+
+        self.read_time_boundary() #Now read all times in.
+
+
+    def read_time_boundary(self):
+        from config import time_format
+        import time
+
+        fid = open(self.filename)
+        lines = fid.readlines()
+        fid.close()
+
+        N = len(lines)
+        d = len(self.domain.conserved_quantities)
+
+        T = numpy.zeros(N, numpy.float)
+        Q = numpy.zeros((N, d), numpy.float)
+
+
+        for i, line in enumerate(lines):
+            fields = line.split(',')
+            real_time = time.mktime(time.strptime(fields[0], time_format))
+
+            T[i] = real_time - self.starttime
+
+
+            for j, value in enumerate(fields[1].split()):
+                Q[i, j] = numpy.float(value)
+
+        msg = 'Time boundary must list time as a monotonuosly '
+        msg += 'increasing sequence'
+
+        assert alltrue( T[1:] - T[:-1] > 0 ), msg
+
+        self.T = T     #Time
+        self.Q = Q     #Boundary values
+        self.index = 0 #Initial index
+
+
+    def __repr__(self):
+        return 'File boundary'
+
+    def evaluate(self, vol_id=None, edge_id=None):
+        """Return linearly interpolated values based on domain.time
+        """
+
+        t = self.domain.time
+
+        msg = 'Time given in File boundary does not match model time'
+        if t < self.T[0]: raise msg
+        if t > self.T[-1]: raise msg
+
+        oldindex = self.index
+
+        #Find slot
+        while t > self.T[self.index]: self.index += 1
+        while t < self.T[self.index]: self.index -= 1
+
+        #if oldindex != self.index:
+        #    print 'Changing from %d to %d' %(oldindex, self.index)
+
+
+        #t is now between index and index+1
+        ratio = (t - self.T[self.index])/\
+                (self.T[self.index+1] - self.T[self.index])
+
+        return self.Q[self.index,:] +\
+               ratio*(self.Q[self.index+1,:] - self.Q[self.index,:])
+
+
+
+
Index: /anuga_work/development/2010-projects/anuga_1d/base/generic_domain.py
===================================================================
--- /anuga_work/development/2010-projects/anuga_1d/base/generic_domain.py	(revision 7850)
+++ /anuga_work/development/2010-projects/anuga_1d/base/generic_domain.py	(revision 7850)
@@ -0,0 +1,1361 @@
+"""Class Domain - 1D domains for finite-volume computations of
+   the shallow water wave equation
+
+
+   Copyright 2004
+   Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou
+   Geoscience Australia
+"""
+
+import numpy
+from generic_boundary_conditions import *
+
+
+class Generic_domain:
+
+    def __init__(self,
+                 coordinates,
+                 boundary = None,
+                 conserved_quantities = None,
+                 evolved_quantities = None,
+                 other_quantities = None,
+                 tagged_elements = None):
+        """
+        Build 1D elements from x coordinates
+        """
+
+        from config import timestepping_method
+        from config import CFL
+        
+        #Store Points
+        self.coordinates = numpy.array(coordinates, numpy.float)
+
+
+        #Register number of Elements
+        self.number_of_elements = N = len(self.coordinates)-1
+
+        self.beta = 1.0
+        self.set_limiter("minmod_kurganov")
+        self.set_CFL(CFL)
+        self.set_timestepping_method(timestepping_method)
+
+        self.wet_nodes = numpy.zeros((N,2), numpy.int) # should this be here
+
+        #Allocate space for neighbour and boundary structures
+        self.neighbours = numpy.zeros((N, 2), numpy.int)
+        #self.neighbour_edges = numpy.zeros((N, 2), numpy.int)
+        self.neighbour_vertices   = numpy.zeros((N, 2), numpy.int)
+        self.number_of_boundaries = numpy.zeros(N, numpy.int)
+        self.surrogate_neighbours = numpy.zeros((N, 2), numpy.int)
+        
+        #Allocate space for geometric quantities
+        self.vertices  = numpy.zeros((N, 2), numpy.float)
+        self.centroids = numpy.zeros(N, numpy.float)
+        self.areas     = numpy.zeros(N, numpy.float)
+
+        self.max_speed_array = numpy.zeros(N, numpy.float)
+      
+
+        self.normals = numpy.zeros((N, 2), numpy.float)
+        
+        for i in range(N):
+            xl = self.coordinates[i]
+            xr = self.coordinates[i+1]
+            self.vertices[i,0] = xl
+            self.vertices[i,1] = xr
+
+            centroid = (xl+xr)/2.0
+            self.centroids[i] = centroid
+
+            msg = 'Coordinates should be ordered, smallest to largest'
+            assert xr>xl, msg
+            
+            #The normal vectors
+            # - point outward from each edge
+            # - are orthogonal to the edge
+            # - have unit length
+            # - Are enumerated by left vertex then right vertex normals
+
+            nl = -1.0
+            nr =  1.0
+            self.normals[i,:] = [nl, nr]
+
+            self.areas[i] = (xr-xl)
+            
+# #         print 'N', N
+# #         print 'Centroid', self.centroids
+# #         print 'Areas', self.areas
+# #         print 'Vertex_Coordinates', self.vertices
+            
+            #Initialise Neighbours (-1 means that it is a boundary neighbour)
+            self.neighbours[i, :] = [-1, -1]
+            #Initialise edge ids of neighbours
+            #Initialise vertex ids of neighbours
+            #In case of boundaries this slot is not used
+            #self.neighbour_edges[i, :] = [-1, -1]
+            self.neighbour_vertices[i, :] = [-1, -1]
+
+        self.build_vertexlist()
+
+        #Build neighbour structure
+        self.build_neighbour_structure()
+
+        #Build surrogate neighbour structure
+        self.build_surrogate_neighbour_structure()          
+
+        #Build boundary dictionary mapping (id, vertex) to symbolic tags
+        self.build_boundary_dictionary(boundary)
+
+        #Build tagged element  dictionary mapping (tag) to array of elements
+        self.build_tagged_elements_dictionary(tagged_elements)
+        
+        from quantity import Quantity
+        #from quantity_domain import Quantity, Conserved_quantity
+        
+        #List of quantity names entering
+        #the conservation equations
+        #(Must be a subset of quantities)
+        if conserved_quantities is None:
+            self.conserved_quantities = []
+        else:
+            self.conserved_quantities = conserved_quantities
+
+        if evolved_quantities is None:
+            self.evolved_quantities = self.conserved_quantities
+        else:
+            self.evolved_quantities = evolved_quantities
+
+        if other_quantities is None:
+            self.other_quantities = []
+        else:
+            self.other_quantities = other_quantities
+
+
+        #Build dictionary of Quantity instances keyed by quantity names
+        self.quantities = {}
+
+        #print self.conserved_quantities
+        #print self.evolved_quantities
+        
+
+        #FIXME: remove later - maybe OK, though....
+        for name in self.evolved_quantities:
+            self.quantities[name] = Quantity(self)
+        for name in self.other_quantities:
+            self.quantities[name] = Quantity(self)
+
+        #Create an empty list for explicit forcing terms
+        self.forcing_terms = []
+
+        #Defaults
+        from config import max_smallsteps, beta_w, beta_h, epsilon, CFL
+        self.beta_w = beta_w
+        self.beta_h = beta_h
+        self.epsilon = epsilon
+
+        #FIXME: Maybe have separate orders for h-limiter and w-limiter?
+        #Or maybe get rid of order altogether and use beta_w and beta_h
+        self.default_order = 1
+        self.order = self.default_order
+
+        self.default_time_order = 1
+        self.time_order = self.default_time_order
+        
+        self.smallsteps = 0
+        self.max_smallsteps = max_smallsteps
+        self.number_of_steps = 0
+        self.number_of_first_order_steps = 0
+
+        #Model time
+        self.time = 0.0
+        self.finaltime = None
+        self.min_timestep = self.max_timestep = 0.0
+        self.starttime = 0 #Physical starttime if any (0 is 1 Jan 1970 00:00:00)
+        #Checkpointing and storage
+        from config import default_datadir
+        self.set_datadir(default_datadir)
+        self.filename = 'domain'
+        self.checkpoint = False
+
+    def __len__(self):
+        return self.number_of_elements
+
+    def build_vertexlist(self):
+        """Build vertexlist index by vertex ids and for each entry (point id)
+        build a list of (triangles, vertex_id) pairs that use the point
+        as vertex.
+
+        Preconditions:
+          self.coordinates and self.triangles are defined
+
+        Postcondition:
+          self.vertexlist is built
+        """
+
+        vertexlist = [None]*len(self.coordinates)
+        for i in range(self.number_of_elements):
+
+            #a = self.triangles[i, 0]
+            #b = self.triangles[i, 1]
+            #c = self.triangles[i, 2]
+            a = i
+            b = i + 1
+
+            #Register the vertices v as lists of
+            #(triangle_id, vertex_id) tuples associated with them
+            #This is used for smoothing
+            #for vertex_id, v in enumerate([a,b,c]):
+            for vertex_id, v in enumerate([a,b]):
+                if vertexlist[v] is None:
+                    vertexlist[v] = []
+
+                vertexlist[v].append( (i, vertex_id) )
+
+        self.vertexlist = vertexlist
+
+        
+    def build_neighbour_structure(self):
+        """Update all registered triangles to point to their neighbours.
+
+        Also, keep a tally of the number of boundaries for each triangle
+
+        Postconditions:
+          neighbours and neighbour_edges is populated
+          neighbours and neighbour_vertices is populated
+          number_of_boundaries integer array is defined.
+        """
+
+        #Step 1:
+        #Build dictionary mapping from segments (2-tuple of points)
+        #to left hand side edge (facing neighbouring triangle)
+
+        N = self.number_of_elements
+        neighbourdict = {}
+        #l_edge = 0
+        #r_edge = 1
+        l_vertex = 0
+        r_vertex = 1
+        for i in range(N):
+
+            #Register all segments as keys mapping to current triangle
+            #and segment id
+            #a = self.triangles[i, 0]
+            #b = self.triangles[i, 1]
+            #c = self.triangles[i, 2]
+            a = self.vertices[i,0]
+            b = self.vertices[i,1]
+            
+            """
+            if neighbourdict.has_key((a,b)):
+                    msg = "Edge 2 of triangle %d is duplicating edge %d of triangle %d.\n" %(i,neighbourdict[a,b][1],neighbourdict[a,b][0])
+                    raise msg
+            if neighbourdict.has_key((b,c)):
+                    msg = "Edge 0 of triangle %d is duplicating edge %d of triangle %d.\n" %(i,neighbourdict[b,c][1],neighbourdict[b,c][0])
+                    raise msg
+            if neighbourdict.has_key((c,a)):
+                    msg = "Edge 1 of triangle %d is duplicating edge %d of triangle %d.\n" %(i,neighbourdict[c,a][1],neighbourdict[c,a][0])
+                    raise msg
+                    """
+            #neighbourdict[a,b] = (i, 2) #(id, edge)
+            #neighbourdict[b,c] = (i, 0) #(id, edge)
+            #neighbourdict[c,a] = (i, 1) #(id, edge)
+            #neighbourdict[a,b] = (i, 1) #(id, edge)
+            #neighbourdict[b,a] = (i, 0) #(id, edge)
+            #neighbourdict[a,l_edge] = (i, 0) #(id, edge)
+            #neighbourdict[b,r_edge] = (i, 1) #(id, edge)
+            neighbourdict[a,l_vertex] = (i, 0) #(id, vertex)
+            neighbourdict[b,r_vertex] = (i, 1) #(id, vertex)
+
+
+        #Step 2:
+        #Go through triangles again, but this time
+        #reverse direction of segments and lookup neighbours.
+        for i in range(N):
+            #a = self.triangles[i, 0]
+            #b = self.triangles[i, 1]
+            #c = self.triangles[i, 2]
+            
+            a = self.vertices[i,0]
+            b = self.vertices[i,1]
+            
+            #self.number_of_boundaries[i] = 3
+            self.number_of_boundaries[i] = 2
+            #if neighbourdict.has_key((b,l_edge)):
+            if neighbourdict.has_key((b,l_vertex)):
+                #self.neighbours[i, 1] = neighbourdict[b,l_edge][0]
+                #self.neighbour_edges[i, 1] = neighbourdict[b,l_edge][1]
+                self.neighbours[i, 1] = neighbourdict[b,l_vertex][0]
+                self.neighbour_vertices[i, 1] = neighbourdict[b,l_vertex][1]
+                self.number_of_boundaries[i] -= 1
+
+            #if neighbourdict.has_key((a,r_edge)):
+            if neighbourdict.has_key((a,r_vertex)):
+                #self.neighbours[i, 0] = neighbourdict[a,r_edge][0]
+                #self.neighbour_edges[i, 0] = neighbourdict[a,r_edge][1]
+                self.neighbours[i, 0] = neighbourdict[a,r_vertex][0]
+                self.neighbour_vertices[i, 0] = neighbourdict[a,r_vertex][1]
+                self.number_of_boundaries[i] -= 1
+                
+            #if neighbourdict.has_key((b,a)):
+            #    self.neighbours[i, 1] = neighbourdict[b,a][0]
+            #    self.neighbour_edges[i, 1] = neighbourdict[b,a][1]
+            #    self.number_of_boundaries[i] -= 1
+
+            #if neighbourdict.has_key((c,b)):
+            #    self.neighbours[i, 0] = neighbourdict[c,b][0]
+            #    self.neighbour_edges[i, 0] = neighbourdict[c,b][1]
+            #    self.number_of_boundaries[i] -= 1
+
+            #if neighbourdict.has_key((a,b)):
+            #    self.neighbours[i, 0] = neighbourdict[a,b][0]
+            #    self.neighbour_edges[i, 0] = neighbourdict[a,b][1]
+            #    self.number_of_boundaries[i] -= 1
+
+    def build_surrogate_neighbour_structure(self):
+        """Build structure where each triangle edge points to its neighbours
+        if they exist. Otherwise point to the triangle itself.
+
+        The surrogate neighbour structure is useful for computing gradients
+        based on centroid values of neighbours.
+
+        Precondition: Neighbour structure is defined
+        Postcondition:
+          Surrogate neighbour structure is defined:
+          surrogate_neighbours: i0, i1, i2 where all i_k >= 0 point to
+          triangles.
+
+        """
+
+        N = self.number_of_elements
+        for i in range(N):
+            #Find all neighbouring volumes that are not boundaries
+            #for k in range(3):
+            for k in range(2):    
+                if self.neighbours[i, k] < 0:
+                    self.surrogate_neighbours[i, k] = i #Point this triangle
+                else:
+                    self.surrogate_neighbours[i, k] = self.neighbours[i, k]
+
+    def build_boundary_dictionary(self, boundary = None):
+        """Build or check the dictionary of boundary tags.
+         self.boundary is a dictionary of tags,
+         keyed by volume id and edge:
+         { (id, edge): tag, ... }
+
+         Postconditions:
+            self.boundary is defined.
+        """
+
+        from config import default_boundary_tag
+
+        if boundary is None:
+            boundary = {}
+            for vol_id in range(self.number_of_elements):
+                #for edge_id in range(0, 3):
+                #for edge_id in range(0, 2):
+                for vertex_id in range(0, 2):    
+                    #if self.neighbours[vol_id, edge_id] < 0:
+                    if self.neighbours[vol_id, vertex_id] < 0:   
+                        #boundary[(vol_id, edge_id)] = default_boundary_tag
+                        boundary[(vol_id, vertex_id)] = default_boundary_tag
+        else:
+            #Check that all keys in given boundary exist
+            #for vol_id, edge_id in boundary.keys():
+            for vol_id, vertex_id in boundary.keys():    
+                #msg = 'Segment (%d, %d) does not exist' %(vol_id, edge_id)
+                msg = 'Segment (%d, %d) does not exist' %(vol_id, vertex_id)
+                a, b = self.neighbours.shape
+                #assert vol_id < a and edge_id < b, msg
+                assert vol_id < a and vertex_id < b, msg
+
+                #FIXME: This assert violates internal boundaries (delete it)
+                #msg = 'Segment (%d, %d) is not a boundary' %(vol_id, edge_id)
+                #assert self.neighbours[vol_id, edge_id] < 0, msg
+
+            #Check that all boundary segments are assigned a tag
+            for vol_id in range(self.number_of_elements):
+                #for edge_id in range(0, 3):
+                #for edge_id in range(0, 2):
+                for vertex_id in range(0, 2):    
+                    #if self.neighbours[vol_id, edge_id] < 0:
+                    if self.neighbours[vol_id, vertex_id] < 0:    
+                        #if not boundary.has_key( (vol_id, edge_id) ):
+                        if not boundary.has_key( (vol_id, vertex_id) ):    
+                            msg = 'WARNING: Given boundary does not contain '
+                            #msg += 'tags for edge (%d, %d). '\
+                            #       %(vol_id, edge_id)
+                            msg += 'tags for vertex (%d, %d). '\
+                                   %(vol_id, vertex_id)
+                            msg += 'Assigning default tag (%s).'\
+                                   %default_boundary_tag
+
+                            #FIXME: Print only as per verbosity
+                            #print msg
+
+                            #FIXME: Make this situation an error in the future
+                            #and make another function which will
+                            #enable default boundary-tags where
+                            #tags a not specified
+                            #boundary[ (vol_id, edge_id) ] =\
+                            boundary[ (vol_id, vertex_id) ] =\
+                                      default_boundary_tag
+
+
+
+        self.boundary = boundary
+
+    def build_tagged_elements_dictionary(self, tagged_elements = None):
+        """Build the dictionary of element tags.
+         self.tagged_elements is a dictionary of element arrays,
+         keyed by tag:
+         { (tag): [e1, e2, e3..] }
+
+         Postconditions:
+            self.element_tag is defined
+        """
+
+        if tagged_elements is None:
+            tagged_elements = {}
+        else:
+            #Check that all keys in given boundary exist
+            for tag in tagged_elements.keys():
+                tagged_elements[tag] = array(tagged_elements[tag]).astype(numpy.int)
+
+                msg = 'Not all elements exist. '
+                assert max(tagged_elements[tag]) < self.number_of_elements, msg
+        #print "tagged_elements", tagged_elements
+        self.tagged_elements = tagged_elements
+
+
+    def set_quantities_to_be_stored(self, q):
+        """Specify which quantities will be stored in the sww file.
+
+        q must be either:
+          - the name of a quantity
+          - a list of quantity names
+          - None
+
+        In the two first cases, the named quantities will be stored at each
+        yieldstep
+        (This is in addition to the quantities elevation and friction)  
+        If q is None, storage will be switched off altogether.
+        """
+
+
+        if q is None:
+            self.quantities_to_be_stored = []            
+            self.store = False
+            return
+
+        if isinstance(q, basestring):
+            q = [q] # Turn argument into a list
+
+        #Check correcness    
+        for quantity_name in q:
+            msg = 'Quantity %s is not a valid conserved quantity' %quantity_name
+            assert quantity_name in self.conserved_quantities, msg 
+        
+        self.quantities_to_be_stored = q
+        
+
+
+
+
+    def get_boundary_tags(self):
+        """Return list of available boundary tags
+        """
+
+        tags = {}
+        for v in self.boundary.values():
+            tags[v] = 1
+
+        return tags.keys()
+        
+    def get_vertex_coordinates(self, obj = False):
+        """Return all vertex coordinates.
+        Return all vertex coordinates for all triangles as an Nx6 array
+        (ordered as x0, y0, x1, y1, x2, y2 for each triangle)
+
+        if obj is True, the x/y pairs are returned in a 3*N x 2 array.
+        FIXME, we might make that the default.
+        FIXME Maybe use keyword: continuous = False for this condition?
+
+        
+        """
+
+        if obj is True:
+        
+            #V = self.vertex_coordinates
+            V = self.vertices
+            #return concatenate( (V[:,0:2], V[:,2:4], V[:,4:6]), axis=0)
+
+            N = V.shape[0]
+            #return reshape(V, (3*N, 2))
+            return numpy.reshape(V, (N, 2))
+        else:    
+            #return self.vertex_coordinates
+            return self.vertices
+    
+    def get_conserved_quantities(self, vol_id, vertex=None):#, edge=None):
+        """Get conserved quantities at volume vol_id
+
+        If vertex is specified use it as index for vertex values
+        If edge is specified use it as index for edge values
+        If neither are specified use centroid values
+        If both are specified an exeception is raised
+
+        Return value: Vector of length == number_of_conserved quantities
+
+        """
+
+        #if not (vertex is None):# or edge is None):
+        #    msg = 'Values for both vertex and edge was specified.'
+        #    msg += 'Only one (or none) is allowed.'
+       #     raise msg
+
+        q = numpy.zeros( len(self.conserved_quantities), numpy.float)
+
+        for i, name in enumerate(self.conserved_quantities):
+            Q = self.quantities[name]
+            if vertex is not None:
+                q[i] = Q.vertex_values[vol_id, vertex]
+            #elif edge is not None:
+            #    q[i] = Q.edge_values[vol_id, edge]
+            else:
+                q[i] = Q.centroid_values[vol_id]
+
+        return q
+
+
+    def get_evolved_quantities(self, vol_id, vertex=None):#, edge=None):
+        """Get evolved quantities at volume vol_id
+
+        If vertex is specified use it as index for vertex values
+        If edge is specified use it as index for edge values
+        If neither are specified use centroid values
+        If both are specified an exeception is raised
+
+        Return value: Vector of length == number_of_evolved quantities
+
+        """
+
+
+
+        #if not (vertex is None):# or edge is None):
+        #    msg = 'Values for both vertex and edge was specified.'
+        #    msg += 'Only one (or none) is allowed.'
+       #     raise msg
+
+        q = numpy.zeros( len(self.evolved_quantities), numpy.float)
+
+        for i, name in enumerate(self.evolved_quantities):
+            Q = self.quantities[name]
+            if vertex is not None:
+                q[i] = Q.vertex_values[vol_id, vertex]
+            #elif edge is not None:
+            #    q[i] = Q.edge_values[vol_id, edge]
+            else:
+                q[i] = Q.centroid_values[vol_id]
+
+        return q
+        
+
+    def get_centroids(self):
+        """Return all coordinates of centroids
+        Return x coordinate of centroid for each element as a N array
+        """
+
+        return self.centroids
+
+    def get_vertices(self):
+        """Return all coordinates of centroids
+        Return x coordinate of centroid for each element as a N array
+        """
+
+        return self.vertices
+
+    def get_coordinate(self, elem_id, vertex=None):
+        """Return coordinate of centroid,
+        or left or right vertex.
+        Left vertex (vertex=0). Right vertex (vertex=1)
+        """
+
+        if vertex is None:
+            return self.centroids[elem_id]
+        else:
+            return self.vertices[elem_id,vertex]
+
+    def get_area(self, elem_id=None):
+        """Return area of element id
+        """
+
+        if elem_id is None:
+            return sum(self.areas)
+        else:
+            return self.areas[elem_id]
+
+
+    def get_quantity(self, name, location='vertices', indices = None):
+        """Get values for named quantity
+
+        name: Name of quantity
+
+        In case of location == 'centroids' the dimension values must
+        be a list of a numpyal array of length N, N being the number
+        of elements. Otherwise it must be of dimension Nx3.
+
+        Indices is the set of element ids that the operation applies to.
+
+        The values will be stored in elements following their
+        internal ordering.
+        """
+
+        return self.quantities[name].get_values( location, indices = indices)
+
+    def get_centroid_coordinates(self):
+        """Return all centroid coordinates.
+        Return all centroid coordinates for all triangles as an Nx2 array
+        (ordered as x0, y0 for each triangle)
+        """
+        return self.centroids
+
+
+    def get_timestepping_method(self):
+        return self.timestepping_method
+
+    def set_timestepping_method(self,timestepping_method):
+        
+        if timestepping_method in ['euler', 'rk2', 'rk3']:
+            self.timestepping_method = timestepping_method
+            return
+
+        msg = '%s is an incorrect timestepping type'% timestepping_method
+        raise Exception, msg
+
+
+    def set_quantity(self, name, *args, **kwargs):
+        """Set values for named quantity
+
+
+        One keyword argument is documented here:
+        expression = None, # Arbitrary expression
+
+        expression:
+          Arbitrary expression involving quantity names
+
+        See Quantity.set_values for further documentation.
+        """
+
+        #FIXME (Ole): Allow new quantities here
+        #from quantity import Quantity, Conserved_quantity
+        #Create appropriate quantity object
+        # #if name in self.conserved_quantities:
+        # #    self.quantities[name] = Conserved_quantity(self)
+        # #else:
+        # #    self.quantities[name] = Quantity(self)
+
+
+        #Do the expression stuff
+        if kwargs.has_key('expression'):
+            expression = kwargs['expression']
+            del kwargs['expression']
+
+            Q = self.create_quantity_from_expression(expression)
+            kwargs['quantity'] = Q
+        
+        #Assign values
+        self.quantities[name].set_values(*args, **kwargs)
+
+    def set_boundary(self, boundary_map):
+        """Associate boundary objects with tagged boundary segments.
+
+        Input boundary_map is a dictionary of boundary objects keyed
+        by symbolic tags to matched against tags in the internal dictionary
+        self.boundary.
+
+        As result one pointer to a boundary object is stored for each vertex
+        in the list self.boundary_objects.
+        More entries may point to the same boundary object
+
+        Schematically the mapping is from two dictionaries to one list
+        where the index is used as pointer to the boundary_values arrays
+        within each quantity.
+
+        self.boundary:          (vol_id, edge_id): tag
+        boundary_map (input):   tag: boundary_object
+        ----------------------------------------------
+        self.boundary_objects:  ((vol_id, edge_id), boundary_object)
+
+
+        Pre-condition:
+          self.boundary has been built.
+
+        Post-condition:
+          self.boundary_objects is built
+
+        If a tag from the domain doesn't appear in the input dictionary an
+        exception is raised.
+        However, if a tag is not used to the domain, no error is thrown.
+        FIXME: This would lead to implementation of a
+        default boundary condition
+
+        Note: If a segment is listed in the boundary dictionary and if it is
+        not None, it *will* become a boundary -
+        even if there is a neighbouring triangle.
+        This would be the case for internal boundaries
+
+        Boundary objects that are None will be skipped.
+
+        FIXME: If set_boundary is called multiple times and if Boundary
+        object is changed into None, the neighbour structure will not be
+        restored!!!
+        """
+
+        self.boundary_objects = []
+        self.boundary_map = boundary_map  #Store for use with eg. boundary_stats.
+
+        #FIXME: Try to remove the sorting and fix test_mesh.py
+        x = self.boundary.keys()
+        x.sort()
+
+        #Loop through edges that lie on the boundary and associate them with
+        #callable boundary objects depending on their tags
+        #for k, (vol_id, edge_id) in enumerate(x):
+        for k, (vol_id, vertex_id) in enumerate(x):
+            #tag = self.boundary[ (vol_id, edge_id) ]
+            tag = self.boundary[ (vol_id, vertex_id) ]
+
+            if boundary_map.has_key(tag):
+                B = boundary_map[tag]  #Get callable boundary object
+
+                if B is not None:
+                    #self.boundary_objects.append( ((vol_id, edge_id), B) )
+                    #self.neighbours[vol_id, edge_id] = -len(self.boundary_objects)
+                    self.boundary_objects.append( ((vol_id, vertex_id), B) )
+                    self.neighbours[vol_id, vertex_id] = -len(self.boundary_objects)
+                else:
+                    pass
+                    #FIXME: Check and perhaps fix neighbour structure
+
+            else:
+                msg = 'ERROR (domain.py): Tag "%s" has not been ' %tag
+                msg += 'bound to a boundary object.\n'
+                msg += 'All boundary tags defined in domain must appear '
+                msg += 'in the supplied dictionary.\n'
+                msg += 'The tags are: %s' %self.get_boundary_tags()
+                raise Exception, msg
+
+
+
+    def check_integrity(self):
+        #Mesh.check_integrity(self)
+
+        #print self.quantities
+        #print self.conserved_quantities
+        
+        for quantity in self.conserved_quantities:
+            msg = 'Conserved quantities must be a subset of all quantities'
+            assert quantity in self.quantities, msg
+
+        for quantity in self.evolved_quantities:
+            msg = 'Evolved quantities must be a subset of all quantities'
+            assert quantity in self.quantities, msg            
+
+        # #assert hasattr(self, 'boundary_objects')
+
+    def write_time(self):
+        print self.timestepping_statistics()
+
+    def timestepping_statistics(self):
+        """Return string with time stepping statistics for printing or logging
+        """
+
+        msg = ''
+        if self.min_timestep == self.max_timestep:
+            msg += 'Time = %.4f, delta t = %.8f, steps=%d (%d)'\
+                   %(self.time, self.min_timestep, self.number_of_steps,
+                     self.number_of_first_order_steps)
+        elif self.min_timestep > self.max_timestep:
+            msg += 'Time = %.4f, steps=%d (%d)'\
+                   %(self.time, self.number_of_steps,
+                     self.number_of_first_order_steps)
+        else:
+            msg += 'Time = %.4f, delta t in [%.8f, %.8f], steps=%d (%d)'\
+                   %(self.time, self.min_timestep,
+                     self.max_timestep, self.number_of_steps,
+                     self.number_of_first_order_steps)
+
+        return msg
+
+    def get_name(self):
+        return self.filename
+
+    def set_name(self, name):
+        self.filename = name
+
+    def get_datadir(self):
+        return self.datadir
+
+    def set_datadir(self, name):
+        self.datadir = name
+
+    def set_CFL(self, cfl):
+        if cfl > 1.0:
+            print 'WARNING: Setting CFL condition to %f which is greater than 1' % cfl
+        self.CFL = cfl
+
+    def get_CFL(self):
+        return self.CFL
+    
+    def set_filename(self, name):
+        self.filename = name
+
+    def get_filename(self):
+        return self.filename
+
+    def get_limiter(self):
+        return self.limiter
+
+    def set_limiter(self,limiter):
+
+        possible_limiters = \
+        ['pyvolution', 'minmod_steve', 'minmod', 'minmod_kurganov', 'superbee', 'vanleer', 'vanalbada']
+
+        if limiter in possible_limiters:
+            self.limiter = limiter
+            return
+
+        msg = '%s is an incorrect limiter type.\n'% limiter
+        msg += 'Possible types are: '+ ", ".join(["%s" % el for el in possible_limiters])
+        raise Exception, msg
+
+        
+    #--------------------------
+    # Main components of evolve
+    #--------------------------    
+
+    def evolve(self, yieldstep = None,
+               finaltime = None,
+               duration = None,
+               skip_initial_step = False):
+        """Evolve model through time starting from self.starttime.
+
+
+        yieldstep: numpy.interval between yields where results are stored,
+                   statistics written and domain inspected or
+                   possibly modified. If omitted the internal predefined
+                   max timestep is used.
+                   numpy.internally, smaller timesteps may be taken.
+
+        duration: Duration of simulation
+
+        finaltime: Time where simulation should end. This is currently
+        relative time.  So it's the same as duration.
+
+        If both duration and finaltime are given an exception is thrown.
+
+
+        skip_initial_step: Boolean flag that decides whether the first
+        yield step is skipped or not. This is useful for example to avoid
+        duplicate steps when multiple evolve processes are dove tailed.
+
+
+        Evolve is implemented as a generator and is to be called as such, e.g.
+
+        for t in domain.evolve(yieldstep, finaltime):
+            <Do something with domain and t>
+
+
+        All times are given in seconds
+
+        """
+
+        from config import min_timestep, max_timestep, epsilon
+
+        # FIXME: Maybe lump into a larger check prior to evolving
+        msg = 'Boundary tags must be bound to boundary objects before '
+        msg += 'evolving system, '
+        msg += 'e.g. using the method set_boundary.\n'
+        msg += 'This system has the boundary tags %s '\
+               %self.get_boundary_tags()
+        assert hasattr(self, 'boundary_objects'), msg
+
+
+        if yieldstep is None:
+            yieldstep = max_timestep
+        else:
+            yieldstep = numpy.float(yieldstep)
+
+        self._order_ = self.default_order
+
+
+        if finaltime is not None and duration is not None:
+            # print 'F', finaltime, duration
+            msg = 'Only one of finaltime and duration may be specified'
+            raise msg
+        else:
+            if finaltime is not None:
+                self.finaltime = numpy.float(finaltime)
+            if duration is not None:
+                self.finaltime = self.starttime + numpy.float(duration)
+
+
+
+        N = len(self) # Number of triangles
+        self.yieldtime = 0.0 # Track time between 'yields'
+
+        # Initialise interval of timestep sizes (for reporting only)
+        self.min_timestep = max_timestep
+        self.max_timestep = min_timestep
+        self.number_of_steps = 0
+        self.number_of_first_order_steps = 0
+
+
+        # Update ghosts
+        self.update_ghosts()
+
+        # Initial update of vertex and edge values
+        self.distribute_to_vertices_and_edges()
+
+        # Update extrema if necessary (for reporting)
+        self.update_extrema()
+        
+        # Initial update boundary values
+        self.update_boundary()
+
+        # Or maybe restore from latest checkpoint
+        if self.checkpoint is True:
+            self.goto_latest_checkpoint()
+
+        if skip_initial_step is False:
+            yield(self.time)  # Yield initial values
+
+        while True:
+
+            # Evolve One Step, using appropriate timestepping method
+            if self.get_timestepping_method() == 'euler':
+                self.evolve_one_euler_step(yieldstep,finaltime)
+                
+            elif self.get_timestepping_method() == 'rk2':
+                self.evolve_one_rk2_step(yieldstep,finaltime)
+
+            elif self.get_timestepping_method() == 'rk3':
+                self.evolve_one_rk3_step(yieldstep,finaltime)               
+
+            
+            # Update extrema if necessary (for reporting)
+            self.update_extrema()            
+
+
+            
+            self.yieldtime += self.timestep
+            self.number_of_steps += 1
+            if self._order_ == 1:
+                self.number_of_first_order_steps += 1
+
+
+            # Yield results
+            if finaltime is not None and self.time >= finaltime-epsilon:
+
+                if self.time > finaltime:
+                    # FIXME (Ole, 30 April 2006): Do we need this check?
+                    # Probably not (Ole, 18 September 2008). Now changed to
+                    # Exception
+                    msg = 'WARNING (domain.py): time overshot finaltime. '
+                    msg += 'Contact Ole.Nielsen@ga.gov.au'
+                    raise Exception, msg
+                    
+
+                # Yield final time and stop
+                self.time = finaltime
+                yield(self.time)
+                break
+
+            if self.yieldtime >= yieldstep:
+                # Yield (intermediate) time and allow inspection of domain
+
+                if self.checkpoint is True:
+                    self.store_checkpoint()
+                    self.delete_old_checkpoints()
+
+                # Pass control on to outer loop for more specific actions
+
+                yield(self.time)
+
+                # Reinitialise
+                self.yieldtime = 0.0
+                self.min_timestep = max_timestep
+                self.max_timestep = min_timestep
+                self.number_of_steps = 0
+                self.number_of_first_order_steps = 0
+                #self.max_speed_array = 0.0
+
+
+    def evolve_one_euler_step(self, yieldstep, finaltime):
+        """
+        One Euler Time Step
+        Q^{n+1} = E(h) Q^n
+        """
+
+
+        # Compute fluxes across each element edge
+        self.compute_fluxes()
+
+        # Update timestep to fit yieldstep and finaltime
+        self.update_timestep(yieldstep, finaltime)
+
+        # Update conserved quantities
+        self.update_conserved_quantities()
+
+        # Update ghosts
+        self.update_ghosts()
+
+        # Update vertex and edge values
+        self.distribute_to_vertices_and_edges()
+
+        # Update boundary values
+        self.update_boundary()
+
+        # Update time
+        self.time += self.timestep
+
+        
+
+
+    def evolve_one_rk2_step(self, yieldstep, finaltime):
+        """
+        One 2nd order RK timestep
+        Q^{n+1} = 0.5 Q^n + 0.5 E(h)^2 Q^n
+        """
+
+        # Save initial conserved quantities values
+        self.backup_conserved_quantities()            
+
+        #--------------------------------------
+        # First euler step
+        #--------------------------------------
+
+        # Compute fluxes across each element edge
+        self.compute_fluxes()
+
+        # Update timestep to fit yieldstep and finaltime
+        self.update_timestep(yieldstep, finaltime)
+
+        # Update conserved quantities
+        self.update_conserved_quantities()
+
+        # Update ghosts
+        self.update_ghosts()
+
+        # Update vertex and edge values
+        self.distribute_to_vertices_and_edges()
+
+        # Update boundary values
+        self.update_boundary()
+
+        # Update time
+        self.time += self.timestep
+
+        #------------------------------------
+        # Second Euler step
+        #------------------------------------
+            
+        # Compute fluxes across each element edge
+        self.compute_fluxes()
+
+        # Update conserved quantities
+        self.update_conserved_quantities()
+
+        #------------------------------------
+        # Combine initial and final values
+        # of conserved quantities and cleanup
+        #------------------------------------
+        
+        # Combine steps
+        self.saxpy_conserved_quantities(0.5, 0.5)
+
+        #-----------------------------------
+        # clean up vertex values
+        #-----------------------------------
+ 
+        # Update ghosts
+        self.update_ghosts()
+
+        # Update vertex and edge values
+        self.distribute_to_vertices_and_edges()
+
+        # Update boundary values
+        self.update_boundary()
+
+
+
+    def evolve_one_rk3_step(self, yieldstep, finaltime):
+        """
+        One 3rd order RK timestep
+        Q^(1) = 3/4 Q^n + 1/4 E(h)^2 Q^n  (at time t^n + h/2)
+        Q^{n+1} = 1/3 Q^n + 2/3 E(h) Q^(1) (at time t^{n+1})
+        """
+
+        # Save initial initial conserved quantities values
+        self.backup_conserved_quantities()            
+
+        initial_time = self.time
+        
+        #--------------------------------------
+        # First euler step
+        #--------------------------------------
+
+        # Compute fluxes across each element edge
+        self.compute_fluxes()
+
+        # Update timestep to fit yieldstep and finaltime
+        self.update_timestep(yieldstep, finaltime)
+
+        # Update conserved quantities
+        self.update_conserved_quantities()
+
+        # Update ghosts
+        self.update_ghosts()
+
+        # Update vertex and edge values
+        self.distribute_to_vertices_and_edges()
+
+        # Update boundary values
+        self.update_boundary()
+
+        # Update time
+        self.time += self.timestep
+
+        #------------------------------------
+        # Second Euler step
+        #------------------------------------
+            
+        # Compute fluxes across each element edge
+        self.compute_fluxes()
+
+        # Update conserved quantities
+        self.update_conserved_quantities()
+
+        #------------------------------------
+        #Combine steps to obtain intermediate
+        #solution at time t^n + 0.5 h
+        #------------------------------------
+
+        # Combine steps
+        self.saxpy_conserved_quantities(0.25, 0.75)
+ 
+        # Update ghosts
+        self.update_ghosts()
+
+        # Update vertex and edge values
+        self.distribute_to_vertices_and_edges()
+
+        # Update boundary values
+        self.update_boundary()
+
+        # Set substep time
+        self.time = initial_time + self.timestep*0.5
+
+        #------------------------------------
+        # Third Euler step
+        #------------------------------------
+            
+        # Compute fluxes across each element edge
+        self.compute_fluxes()
+
+        # Update conserved quantities
+        self.update_conserved_quantities()
+
+        #------------------------------------
+        # Combine final and initial values
+        # and cleanup
+        #------------------------------------
+        
+        # Combine steps
+        self.saxpy_conserved_quantities(2.0/3.0, 1.0/3.0)
+ 
+        # Update ghosts
+        self.update_ghosts()
+
+        # Update vertex and edge values
+        self.distribute_to_vertices_and_edges()
+
+        # Update boundary values
+        self.update_boundary()
+
+        # Set new time
+        self.time = initial_time + self.timestep       
+        
+
+    def backup_conserved_quantities(self):
+        N = len(self) # Number_of_triangles
+
+        # Backup conserved_quantities centroid values
+        for name in self.conserved_quantities:
+            Q = self.quantities[name]
+            Q.backup_centroid_values()        
+
+    def saxpy_conserved_quantities(self,a,b):
+        N = len(self) #number_of_triangles
+
+        # Backup conserved_quantities centroid values
+        for name in self.conserved_quantities:
+            Q = self.quantities[name]
+            Q.saxpy_centroid_values(a,b)        
+
+
+
+        
+    def distribute_to_vertices_and_edges(self):
+        """Extrapolate conserved quantities from centroid to
+        vertices and edge-midpoints for each volume
+
+        Default implementation is straight first order,
+        i.e. constant values throughout each element and
+        no reference to non-conserved quantities.
+        """
+
+        for name in self.conserved_quantities:
+            Q = self.quantities[name]
+            if self.order == 1:
+                Q.extrapolate_first_order()
+            elif self.order == 2:
+                Q.extrapolate_second_order()
+                #Q.limit()
+            else:
+                raise 'Unknown order'
+            #Q.interpolate_from_vertices_to_edges()
+
+
+    def update_ghosts(self):
+        pass
+    
+    def update_boundary(self):
+        """Go through list of boundary objects and update boundary values
+        for all conserved quantities on boundary.
+        """
+
+        #FIXME: Update only those that change (if that can be worked out)
+        #FIXME: Boundary objects should not include ghost nodes.
+        #for i, ((vol_id, edge_id), B) in enumerate(self.boundary_objects):
+        #    q = B.evaluate(vol_id, edge_id)
+        for i, ((vol_id, vertex_id), B) in enumerate(self.boundary_objects):
+            q = B.evaluate(vol_id, vertex_id)
+            #print 'q ',q
+            for j, name in enumerate(self.evolved_quantities):
+                #print 'name %s j = %f \n'%(name,j)
+                Q = self.quantities[name]
+
+                Q.boundary_values[i] = q[j]
+                #print 'Q=',Q
+    
+    def update_timestep(self, yieldstep, finaltime):
+
+        from config import min_timestep, max_timestep
+
+        # self.timestep is calculated from speed of characteristics
+        # Apply CFL condition here
+        timestep = min(self.CFL*self.flux_timestep, max_timestep)
+
+        #Record maximal and minimal values of timestep for reporting
+        self.max_timestep = max(timestep, self.max_timestep)
+        self.min_timestep = min(timestep, self.min_timestep)
+
+        #Protect against degenerate time steps
+        if timestep < min_timestep:
+
+            #Number of consecutive small steps taken b4 taking action
+            self.smallsteps += 1
+
+            if self.smallsteps > self.max_smallsteps:
+                self.smallsteps = 0 #Reset
+
+                if self.order == 1:
+                    msg = 'WARNING: Too small timestep %.16f reached '\
+                          %timestep
+                    msg += 'even after %d steps of 1 order scheme'\
+                           %self.max_smallsteps
+                    print msg
+                    timestep = min_timestep  #Try enforcing min_step
+
+                    #raise msg
+                else:
+                    #Try to overcome situation by switching to 1 order
+                    print "changing Order 1"
+                    self.order = 1
+
+        else:
+            self.smallsteps = 0
+            if self.order == 1 and self.default_order == 2:
+                self.order = 2
+
+
+        #Ensure that final time is not exceeded
+        if finaltime is not None and self.time + timestep > finaltime:
+            timestep = finaltime-self.time
+
+        #Ensure that model time is aligned with yieldsteps
+        if self.yieldtime + timestep > yieldstep:
+            timestep = yieldstep-self.yieldtime
+
+        self.timestep = timestep
+
+    def update_extrema(self):
+        pass
+
+    def compute_forcing_terms(self):
+        """If there are any forcing functions driving the system
+        they should be defined in Domain subclass and appended to
+        the list self.forcing_terms
+        """
+        #Clears explicit_update needed for second order method
+        if self.time_order == 2:
+            for name in self.conserved_quantities:
+                Q = self.quantities[name]
+                Q.explicit_update[:] = 0.0
+
+        for f in self.forcing_terms:
+            f(self)
+            
+
+    def update_derived_quantites(self):
+        pass
+    
+    #def update_conserved_quantities(self):
+    def update_conserved_quantities(self):
+        """Update vectors of conserved quantities using previously
+        computed fluxes specified forcing functions.
+        """
+
+
+        timestep = self.timestep
+
+        #Compute forcing terms
+        self.compute_forcing_terms()
+
+        #Update conserved_quantities
+        for name in self.conserved_quantities:
+            Q = self.quantities[name]
+            Q.update(timestep)
+
+
+
+if __name__ == "__main__":
+
+    points1 = [0.0, 1.0, 2.0, 3.0]
+    D1 = Domain(points1)
+
+    print D1.get_coordinate(0)
+    print D1.get_coordinate(0,1)
+    print 'Number of Elements = ',D1.number_of_elements
+
+    try:
+        print D1.get_coordinate(3)
+    except:
+        pass
+    else:
+        msg =  'Should have raised an out of bounds exception'
+        raise msg
+
+    #points2 = [0.0, 1.0, 2.0, 3.0, 2.5]
+    #D2 = Domain(points2)
Index: /anuga_work/development/2010-projects/anuga_1d/base/generic_mesh.py
===================================================================
--- /anuga_work/development/2010-projects/anuga_1d/base/generic_mesh.py	(revision 7850)
+++ /anuga_work/development/2010-projects/anuga_1d/base/generic_mesh.py	(revision 7850)
@@ -0,0 +1,23 @@
+#! /usr/bin/python
+
+# To change this template, choose Tools | Templates
+# and open the template in the editor.
+
+__author__="steve"
+__date__ ="$15/06/2010 3:08:41 PM$"
+
+
+import numpy
+
+def interval_mesh(n, x_0=0.0, x_1=1.0):
+    """Create points, and boundary for a uniform mesh with n sub-interval
+    ranging from x_0 to x_1
+    """
+
+    assert n>0
+
+
+    points  = x_0 + (x_1 - x_0)*numpy.arange(n+1,dtype=numpy.float)
+    boundary = {(0, 0): 'left', (n-1, 1): 'right'}
+
+    return points, boundary
Index: /anuga_work/development/2010-projects/anuga_1d/base/quantity.py
===================================================================
--- /anuga_work/development/2010-projects/anuga_1d/base/quantity.py	(revision 7850)
+++ /anuga_work/development/2010-projects/anuga_1d/base/quantity.py	(revision 7850)
@@ -0,0 +1,1215 @@
+"""
+Class Quantity - Implements values at each 1d element
+
+To create:
+
+   Quantity(domain, vertex_values)
+
+   domain: Associated domain structure. Required.
+
+   vertex_values: N x 2 array of values at each vertex for each element.
+                  Default None
+
+   If vertex_values are None Create array of zeros compatible with domain.
+   Otherwise check that it is compatible with dimenions of domain.
+   Otherwise raise an exception
+"""
+
+import numpy
+
+
+
+class Quantity:
+
+    
+    def __init__(self, domain, vertex_values=None):
+        #Initialise Quantity using optional vertex values.
+        
+        from generic_domain import Generic_domain
+
+        msg = 'First argument in Quantity.__init__ '
+        msg += 'must be of class Domain (or a subclass thereof)'
+        assert isinstance(domain, Generic_domain), msg
+
+        if vertex_values is None:
+            N = domain.number_of_elements
+            self.vertex_values = numpy.zeros((N, 2), numpy.float)
+        else:
+            self.vertex_values = numpy.array(vertex_values, numpy.float)
+
+            N, V = self.vertex_values.shape
+            assert V == 2,\
+                   'Two vertex values per element must be specified'
+
+
+            msg = 'Number of vertex values (%d) must be consistent with'\
+                  %N
+            msg += 'number of elements in specified domain (%d).'\
+                   %domain.number_of_elements
+
+            assert N == domain.number_of_elements, msg
+
+        self.domain = domain
+
+        #Allocate space for other quantities
+        self.centroid_values = numpy.zeros(N, numpy.float)
+        self.centroid_backup_values = numpy.zeros(N, numpy.float)
+        #self.edge_values = numpy.zeros((N, 2), numpy.float)
+        #edge values are values of the ends of each interval
+      
+        #Intialise centroid values
+        self.interpolate()
+
+
+        
+        #Allocate space for boundary values
+        #L = len(domain.boundary)
+        self.boundary_values = numpy.zeros(2, numpy.float) #assumes no parrellism
+
+        #Allocate space for updates of conserved quantities by
+        #flux calculations and forcing functions
+        
+        N = domain.number_of_elements
+        self.explicit_update = numpy.zeros(N, numpy.float )
+        self.semi_implicit_update = numpy.zeros(N, numpy.float )
+
+        self.gradients = numpy.zeros(N, numpy.float)
+        self.qmax = numpy.zeros(self.centroid_values.shape, numpy.float)
+        self.qmin = numpy.zeros(self.centroid_values.shape, numpy.float)
+
+        self.beta = domain.beta        
+
+
+    def __len__(self):
+        """
+        Returns number of intervals.
+        """
+        return self.centroid_values.shape[0]
+
+    def __neg__(self):
+        """Negate all values in this quantity giving meaning to the
+        expression -Q where Q is an instance of class Quantity
+        """
+
+        Q = Quantity(self.domain)
+        Q.set_values_from_numeric(-self.vertex_values)
+        return Q
+
+
+
+    def __add__(self, other):
+        """Add to self anything that could populate a quantity
+
+        E.g other can be a constant, an array, a function, another quantity
+        (except for a filename or points, attributes (for now))
+        - see set_values_from_numeric for details
+        """
+
+        Q = Quantity(self.domain)
+        Q.set_values_from_numeric(other)
+
+        result = Quantity(self.domain)
+        result.set_values_from_numeric(self.vertex_values + Q.vertex_values)
+        return result
+
+    def __radd__(self, other):
+        """Handle cases like 7+Q, where Q is an instance of class Quantity
+        """
+
+        return self + other
+
+    def __sub__(self, other):
+        return self + -other            # Invoke self.__neg__()
+
+    def __mul__(self, other):
+        """Multiply self with anything that could populate a quantity
+
+        E.g other can be a constant, an array, a function, another quantity
+        (except for a filename or points, attributes (for now))
+        - see set_values_from_numeric for details
+        """
+
+        if isinstance(other, Quantity):
+            Q = other
+        else:
+            Q = Quantity(self.domain)
+            Q.set_values_from_numeric(other)
+
+        result = Quantity(self.domain)
+
+        # The product of vertex_values, edge_values and centroid_values
+        # are calculated and assigned directly without using
+        # set_values_from_numeric (which calls interpolate). Otherwise
+        # centroid values wouldn't be products from q1 and q2
+        result.vertex_values = self.vertex_values * Q.vertex_values
+        result.centroid_values = self.centroid_values * Q.centroid_values
+
+        return result
+
+    def __rmul__(self, other):
+        """Handle cases like 3*Q, where Q is an instance of class Quantity
+        """
+
+        return self * other
+
+    def __div__(self, other):
+        """Divide self with anything that could populate a quantity
+
+        E.g other can be a constant, an array, a function, another quantity
+        (except for a filename or points, attributes (for now))
+        - see set_values_from_numeric for details
+
+        Zero division is dealt with by adding an epsilon to the divisore
+        FIXME (Ole): Replace this with native INF once we migrate to NumPy
+        """
+
+        if isinstance(other, Quantity):
+            Q = other
+        else:
+            Q = Quantity(self.domain)
+            Q.set_values_from_numeric(other)
+
+        result = Quantity(self.domain)
+
+        # The quotient of vertex_values, edge_values and centroid_values
+        # are calculated and assigned directly without using
+        # set_values_from_numeric (which calls interpolate). Otherwise
+        # centroid values wouldn't be quotient of q1 and q2
+        result.vertex_values = self.vertex_values/(Q.vertex_values + epsilon)
+        result.centroid_values = self.centroid_values/(Q.centroid_values + epsilon)
+
+        return result
+
+    def __rdiv__(self, other):
+        """Handle cases like 3/Q, where Q is an instance of class Quantity
+        """
+
+        return self / other
+
+    def __pow__(self, other):
+        """Raise quantity to (numerical) power
+
+        As with __mul__ vertex values are processed entry by entry
+        while centroid and edge values are re-interpolated.
+
+        Example using __pow__:
+          Q = (Q1**2 + Q2**2)**0.5
+        """
+
+        if isinstance(other, Quantity):
+            Q = other
+        else:
+            Q = Quantity(self.domain)
+            Q.set_values_from_numeric(other)
+
+        result = Quantity(self.domain)
+
+        # The power of vertex_values, edge_values and centroid_values
+        # are calculated and assigned directly without using
+        # set_values_from_numeric (which calls interpolate). Otherwise
+        # centroid values wouldn't be correct
+        result.vertex_values = self.vertex_values ** other
+        result.centroid_values = self.centroid_values ** other
+
+        return result
+
+    def set_values_from_numeric(self, numeric):
+        
+        x = numpy.array([1.0,2.0])
+        y = [1.0,2.0]
+
+        if type(numeric) == type(y):
+            self.set_values_from_array(numeric)
+        elif type(numeric) == type(x):
+            self.set_values_from_array(numeric)
+        elif callable(numeric):
+            self.set_values_from_function(numeric)
+        elif isinstance(numeric, Quantity):
+            self.set_values_from_quantity(numeric)
+        else:   # see if it's coercible to a float (float, int or long, etc)
+            try:
+                numeric = float(numeric)
+            except ValueError:
+                msg = ("Illegal type for variable 'numeric': %s" % type(numeric))
+                raise Exception(msg)
+            self.set_values_from_constant(numeric)
+
+    def set_values_from_constant(self,numeric):
+
+        self.vertex_values[:,:]   = numeric
+        self.centroid_values[:,] = numeric
+
+
+    def set_values_from_array(self,numeric):
+
+        self.vertex_values[:,:]   = numeric
+        self.interpolate()
+
+
+    def set_values_from_quantity(self,quantity):
+
+        self.vertex_values[:,:]   = quantity.vertex_values
+        self.centroid_values[:,] = quantity.centroid_values
+
+    def set_values_from_function(self,function):
+
+        self.vertex_values[:,:]   = map(function, self.domain.vertices)
+        self.centroid_values[:,] = map(function, self.domain.centroids)
+
+
+    def interpolate(self):
+        """
+        Compute interpolated values at centroid
+        Pre-condition: vertex_values have been set
+        """
+
+        N = self.vertex_values.shape[0]
+        for i in range(N):
+            v0 = self.vertex_values[i, 0]
+            v1 = self.vertex_values[i, 1]
+
+            self.centroid_values[i] = (v0 + v1)/2.0
+
+
+
+
+    def set_values(self, X, location='vertices'):
+        """Set values for quantity
+
+        X: Compatible list, Numeric array (see below), constant or function
+        location: Where values are to be stored.
+                  Permissible options are: vertices, centroid
+                  Default is "vertices"
+
+        In case of location == 'centroid' the dimension values must
+        be a list of a Numerical array of length N, N being the number
+        of elements in the mesh. Otherwise it must be of dimension Nx2
+
+        The values will be stored in elements following their
+        internal ordering.
+
+        If values are described a function, it will be evaluated at specified points
+
+        If selected location is vertices, values for centroid and edges
+        will be assigned interpolated values.
+        In any other case, only values for the specified locations
+        will be assigned and the others will be left undefined.
+        """
+
+        if location not in ['vertices', 'centroids', 'unique_vertices']:
+            msg = 'Invalid location: %s, (possible choices vertices, centroids, unique_vertices)' %location
+            raise Exception(msg)
+
+        if X is None:
+            msg = 'Given values are None'
+            raise Exception(msg)
+
+        import types
+
+        if callable(X):
+            #Use function specific method
+            self.set_function_values(X, location)
+          
+        elif type(X) in [types.FloatType, types.IntType, types.LongType]:
+            
+            self.centroid_values[:,] = float(X)
+            self.vertex_values[:,:] = float(X)
+
+        elif isinstance(X, Quantity):
+            self.set_array_values(X.vertex_values, location)
+
+        else:
+            #Use array specific method
+            self.set_array_values(X, location)
+
+        if location == 'vertices' or location == 'unique_vertices':
+            #Intialise centroid
+            self.interpolate()
+
+        if location == 'centroid':
+            self.extrapolate_first_order()
+
+
+
+
+
+    def set_function_values(self, f, location='vertices'):
+        """Set values for quantity using specified function
+
+        f: x -> z Function where x and z are arrays
+        location: Where values are to be stored.
+                  Permissible options are: vertices, centroid
+                  Default is "vertices"
+        """
+        
+        if location == 'centroids':
+         
+            P = self.domain.centroids
+            self.set_values(f(P), location)
+        else:
+            #Vertices
+            
+            P = self.domain.get_vertices()
+            
+            for i in range(2):
+               
+                self.vertex_values[:,i] = f(P[:,i])
+               
+    def set_array_values(self, values, location='vertices'):
+        """Set values for quantity
+
+        values: Numeric array
+        location: Where values are to be stored.
+                  Permissible options are: vertices, centroid, unique_vertices
+                  Default is "vertices"
+
+        In case of location == 'centroid' the dimension values must
+        be a list of a Numerical array of length N, N being the number
+        of elements in the mesh. If location == 'unique_vertices' the
+        dimension values must be a list or a Numeric array of length N+1.
+        Otherwise it must be of dimension Nx2
+
+        The values will be stored in elements following their
+        internal ordering.
+
+        If selected location is vertices, values for centroid
+        will be assigned interpolated values.
+        In any other case, only values for the specified locations
+        will be assigned and the others will be left undefined.
+        """
+
+
+        values = numpy.array(values).astype(numpy.float)
+
+        N = self.centroid_values.shape[0]
+
+
+        if location == 'centroids':
+            msg = 'Number of values must match number of elements'
+            assert values.shape[0] == N, msg
+            assert len(values.shape) == 1, 'Values array must be 1d'
+
+            for i in range(N):
+                self.centroid_values[i] = values[i]
+
+            self.vertex_values[:,0] = values
+            self.vertex_values[:,1] = values
+ 
+        if location == 'vertices':
+            msg = 'Number of values must match number of elements'
+            assert values.shape[0] == N, msg
+            assert len(values.shape) == 2, 'Values array must be 2d'
+            msg = 'Array must be N x 2'
+            assert values.shape[1] == 2, msg
+
+            self.vertex_values[:,:] = values[:,:]
+
+        if location == 'unique_vertices':
+            msg = 'Number of values must match number of elements +1'
+            assert values.shape[0] == N+1, msg
+            assert len(values.shape) == 1, 'Values array must be 1d'
+
+            self.vertex_values[:,0] = values[0:-1]
+            self.vertex_values[:,1] = values[1:N+1]  
+
+
+            
+
+
+    def get_values(self, location='vertices', indices = None):
+        """get values for quantity
+
+        return X, Compatible list, Numeric array (see below)
+        location: Where values are to be stored.
+                  Permissible options are: vertices, centroid
+                  and unique vertices. Default is 'vertices'
+
+        In case of location == 'centroids' the dimension values must
+        be a list of a Numerical array of length N, N being the number
+        of elements. Otherwise it must be of dimension Nx3
+
+        The returned values with be a list the length of indices
+        (N if indices = None).  Each value will be a list of the three
+        vertex values for this quantity.
+
+        Indices is the set of element ids that the operation applies to.
+
+        """
+
+
+        if location not in ['vertices', 'centroids', 'unique vertices']:
+            msg = 'Invalid location: %s' %location
+            raise Exception(msg)
+
+        import types, numpy
+        assert type(indices) in [types.ListType, types.NoneType,
+                                 numpy.ArrayType],\
+                                 'Indices must be a list or None'
+
+        if location == 'centroids':
+            if (indices ==  None):
+                indices = range(len(self))
+            return take(self.centroid_values,indices)
+        elif location == 'unique vertices':
+            if (indices ==  None):
+                indices=range(self.domain.coordinates.shape[0])
+            vert_values = []
+            #Go through list of unique vertices
+            for unique_vert_id in indices:
+                cells = self.domain.vertexlist[unique_vert_id]
+
+                #In case there are unused points
+                if cells is None:
+                    msg = 'Unique vertex not associated with cells'
+                    raise Exception(msg)
+
+                # Go through all cells, vertex pairs
+                # Average the values
+                sum = 0
+                for cell_id, vertex_id in cells:
+                    sum += self.vertex_values[cell_id, vertex_id]
+                vert_values.append(sum/len(cells))
+            return numpy.array(vert_values)
+        else:
+            if (indices ==  None):
+                indices = range(len(self))
+            return take(self.vertex_values,indices)
+
+
+    def get_vertex_values(self,
+                          x=True,
+                          smooth = None,
+                          precision = None,
+                          reduction = None):
+        """Return vertex values like an OBJ format
+
+        The vertex values are returned as one sequence in the 1D float array A.
+        If requested the coordinates will be returned in 1D arrays X.
+
+        The connectivity is represented as an integer array, V, of dimension
+        M x 2, where M is the number of volumes. Each row has two indices
+        into the X, A arrays defining the element.
+
+        if smooth is True, vertex values corresponding to one common
+        coordinate set will be smoothed according to the given
+        reduction operator. In this case vertex coordinates will be
+        de-duplicated.
+
+        If no smoothings is required, vertex coordinates and values will
+        be aggregated as a concatenation of values at
+        vertices 0, vertices 1
+
+
+        Calling convention
+        if x is True:
+           X,A,V = get_vertex_values
+        else:
+           A,V = get_vertex_values
+
+        """
+
+
+
+
+        if smooth is None:
+            smooth = self.domain.smooth
+
+        if precision is None:
+            precision = numpy.float
+
+        if reduction is None:
+            reduction = self.domain.reduction
+
+        #Create connectivity
+
+        if smooth == True:
+
+            V = self.domain.get_vertices()
+            N = len(self.domain.vertexlist)
+            #N = len(self.domain.vertices)
+            A = numpy.zeros(N, precision)
+
+            #Smoothing loop
+            for k in range(N):
+                L = self.domain.vertexlist[k]
+                #L = self.domain.vertices[k]
+
+                #Go through all triangle, vertex pairs
+                #contributing to vertex k and register vertex value
+
+                if L is None: continue #In case there are unused points
+
+                contributions = []
+                for volume_id, vertex_id in L:
+                    v = self.vertex_values[volume_id, vertex_id]
+                    contributions.append(v)
+
+                A[k] = reduction(contributions)
+
+            if x is True:
+                 #X = self.domain.coordinates[:,0].astype(precision)
+                 X = self.domain.coordinates[:].astype(precision)
+                 #Y = self.domain.coordinates[:,1].astype(precision)
+
+                 #return X, Y, A, V
+                 return X, A, V
+            
+            #else:
+            return A, V
+        else:
+            #Don't smooth
+            #obj machinery moved to general_mesh
+
+            # Create a V like [[0 1 2], [3 4 5]....[3*m-2 3*m-1 3*m]]
+            # These vert_id's will relate to the verts created below
+            #m = len(self.domain)  #Number of volumes
+            #M = 3*m        #Total number of unique vertices
+            #V = reshape(array(range(M)).astype(Int), (m,3))
+
+            #V = self.domain.get_triangles(obj=True)
+            V = self.domain.get_vertices
+            #FIXME use get_vertices, when ready
+
+            A = self.vertex_values.flat
+
+            #Do vertex coordinates
+            if x is True:
+                X = self.domain.get_vertex_coordinates()
+
+                #X = C[:,0:6:2].copy()
+                #Y = C[:,1:6:2].copy()
+
+                return X.flat, A, V
+            else:
+                return A, V
+
+    def get_integral(self):
+        """Compute the integral of quantity across entire domain
+        """
+        integral = 0
+        for k in range(self.domain.number_of_elements):
+            area = self.domain.areas[k]
+            qc = self.centroid_values[k]
+            integral += qc*area
+
+        return integral
+
+
+    def update(self, timestep):
+        """Update centroid values based on values stored in
+        explicit_update and semi_implicit_update as well as given timestep
+        """
+
+
+
+        N = self.centroid_values.shape[0]
+
+        #Explicit updates
+        self.centroid_values += timestep*self.explicit_update
+        
+        #Semi implicit updates
+        denominator = numpy.ones(N, numpy.float)-timestep*self.semi_implicit_update
+
+        if sum(numpy.equal(denominator, 0.0)) > 0.0:
+            msg = 'Zero division in semi implicit update. Call Stephen :-)'
+            raise Exception(msg)
+        else:
+            #Update conserved_quantities from semi implicit updates
+            self.centroid_values /= denominator
+
+
+    def compute_gradients(self):
+        """Compute gradients of piecewise linear function defined by centroids of
+        neighbouring volumes.
+        """
+
+
+
+
+        N = self.centroid_values.shape[0]
+
+
+        G = self.gradients
+        Q = self.centroid_values
+        X = self.domain.centroids
+
+        for k in range(N):
+
+    	    # first and last elements have boundaries
+
+            if k == 0:
+
+                #Get data
+                k0 = k
+                k1 = k+1
+                k2 = k+2
+
+                q0 = Q[k0]
+                q1 = Q[k1]
+                q2 = Q[k2]
+
+                x0 = X[k0] #V0 centroid
+                x1 = X[k1] #V1 centroid
+                x2 = X[k2]
+
+                #Gradient
+                #G[k] = (q1 - q0)/(x1 - x0)
+                
+                G[k] = (q1 - q0)*(x2 - x0)*(x2 - x0) - (q2 - q0)*(x1 - x0)*(x1 - x0)
+                G[k] /= (x1 - x0)*(x2 - x0)*(x2 - x1)
+
+            elif k == N-1:
+
+                #Get data
+                k0 = k
+                k1 = k-1
+                k2 = k-2
+
+                q0 = Q[k0]
+                q1 = Q[k1]
+                q2 = Q[k2]
+
+                x0 = X[k0] #V0 centroid
+                x1 = X[k1] #V1 centroid
+                x2 = X[k2]
+
+                #Gradient
+                #G[k] = (q1 - q0)/(x1 - x0)
+                
+                G[k] = (q1 - q0)*(x2 - x0)*(x2 - x0) - (q2 - q0)*(x1 - x0)*(x1 - x0)
+                G[k] /= (x1 - x0)*(x2 - x0)*(x2 - x1)
+
+##                q0 = Q[k0]
+##                q1 = Q[k1]
+##
+##                x0 = X[k0] #V0 centroid
+##                x1 = X[k1] #V1 centroid
+##
+##                #Gradient
+##                G[k] = (q1 - q0)/(x1 - x0)
+
+            else:
+                #Interior Volume (2 neighbours)
+
+                #Get data
+                k0 = k-1
+                k2 = k+1
+
+                q0 = Q[k0]
+                q1 = Q[k]
+                q2 = Q[k2]
+
+                x0 = X[k0] #V0 centroid
+                x1 = X[k]  #V1 centroid (Self)
+                x2 = X[k2] #V2 centroid
+
+                #Gradient
+                #G[k] = (q2-q0)/(x2-x0)
+                G[k] = ((q0-q1)/(x0-x1)*(x2-x1) - (q2-q1)/(x2-x1)*(x0-x1))/(x2-x0)
+
+
+    def compute_minmod_gradients(self):
+        """Compute gradients of piecewise linear function defined by centroids of
+        neighbouring volumes.
+        """
+
+        #print 'compute_minmod_gradients'
+        from numpy import sign
+
+        
+        def xmin(a,b):
+            return 0.5*(sign(a)+sign(b))*min(abs(a),abs(b))
+
+        def xmic(t,a,b):
+            return xmin(t*xmin(a,b), 0.50*(a+b) )
+
+
+
+        N = self.centroid_values.shape[0]
+
+
+        G = self.gradients
+        Q = self.centroid_values
+        X = self.domain.centroids
+
+        for k in range(N):
+
+    	    # first and last elements have boundaries
+
+            if k == 0:
+
+                #Get data
+                k0 = k
+                k1 = k+1
+                k2 = k+2
+
+                q0 = Q[k0]
+                q1 = Q[k1]
+                q2 = Q[k2]
+
+                x0 = X[k0] #V0 centroid
+                x1 = X[k1] #V1 centroid
+                x2 = X[k2]
+
+                #Gradient
+                #G[k] = (q1 - q0)/(x1 - x0)
+                
+                G[k] = (q1 - q0)*(x2 - x0)*(x2 - x0) - (q2 - q0)*(x1 - x0)*(x1 - x0)
+                G[k] /= (x1 - x0)*(x2 - x0)*(x2 - x1)
+
+            elif k == N-1:
+
+                #Get data
+                k0 = k
+                k1 = k-1
+                k2 = k-2
+
+                q0 = Q[k0]
+                q1 = Q[k1]
+                q2 = Q[k2]
+
+                x0 = X[k0] #V0 centroid
+                x1 = X[k1] #V1 centroid
+                x2 = X[k2]
+
+                #Gradient
+                #G[k] = (q1 - q0)/(x1 - x0)
+                
+                G[k] = (q1 - q0)*(x2 - x0)*(x2 - x0) - (q2 - q0)*(x1 - x0)*(x1 - x0)
+                G[k] /= (x1 - x0)*(x2 - x0)*(x2 - x1)
+
+##                #Get data
+##                k0 = k
+##                k1 = k-1
+##
+##                q0 = Q[k0]
+##                q1 = Q[k1]
+##
+##                x0 = X[k0] #V0 centroid
+##                x1 = X[k1] #V1 centroid
+##
+##                #Gradient
+##                G[k] = (q1 - q0)/(x1 - x0)
+
+            elif (self.domain.wet_nodes[k,0] == 2) & (self.domain.wet_nodes[k,1] == 2):
+                G[k] = 0.0
+
+            else:
+                #Interior Volume (2 neighbours)
+
+                #Get data
+                k0 = k-1
+                k2 = k+1
+
+                q0 = Q[k0]
+                q1 = Q[k]
+                q2 = Q[k2]
+
+                x0 = X[k0] #V0 centroid
+                x1 = X[k]  #V1 centroid (Self)
+                x2 = X[k2] #V2 centroid
+
+                # assuming uniform grid
+                d1 = (q1 - q0)/(x1-x0)
+                d2 = (q2 - q1)/(x2-x1)
+
+                #Gradient
+                #G[k] = (d1+d2)*0.5
+                #G[k] = (d1*(x2-x1) - d2*(x0-x1))/(x2-x0)                
+                G[k] = xmic( self.domain.beta, d1, d2 )
+        
+
+    def extrapolate_first_order(self):
+        """Extrapolate conserved quantities from centroid to
+        vertices for each volume using
+        first order scheme.
+        """
+
+        qc = self.centroid_values
+        qv = self.vertex_values
+
+        for i in range(2):
+            qv[:,i] = qc
+
+
+    def extrapolate_second_order(self):
+        """Extrapolate conserved quantities from centroid to
+        vertices for each volume using
+        second order scheme.
+        """
+        if self.domain.limiter == "pyvolution":
+            #Z = self.gradients
+            #print "gradients 1",Z
+            self.compute_gradients()
+            #print "gradients 2",Z
+
+            #Z = self.gradients
+            #print "gradients 1",Z
+            #self.compute_minmod_gradients()
+            #print "gradients 2", Z
+
+            G = self.gradients
+            V = self.domain.vertices
+            qc = self.centroid_values
+            qv = self.vertex_values        
+
+            #Check each triangle
+            for k in range(self.domain.number_of_elements):
+                #Centroid coordinates
+                x = self.domain.centroids[k]
+
+                #vertex coordinates
+                x0, x1 = V[k,:]
+
+                #Extrapolate
+                qv[k,0] = qc[k] + G[k]*(x0-x)
+                qv[k,1] = qc[k] + G[k]*(x1-x)
+            self.limit_pyvolution()
+
+        elif self.domain.limiter == "minmod_steve":
+            self.limit_minmod()
+        else:
+            self.limit_range()
+        
+        
+
+    def limit_minmod(self):
+        #Z = self.gradients
+        #print "gradients 1",Z
+        self.compute_minmod_gradients()
+        #print "gradients 2", Z
+
+        G = self.gradients
+        V = self.domain.vertices
+        qc = self.centroid_values
+        qv = self.vertex_values        
+        
+        #Check each triangle
+        for k in range(self.domain.number_of_elements):
+            #Centroid coordinates
+            x = self.domain.centroids[k]
+
+            #vertex coordinates
+            x0, x1 = V[k,:]
+
+            #Extrapolate
+            qv[k,0] = qc[k] + G[k]*(x0-x)
+            qv[k,1] = qc[k] + G[k]*(x1-x)
+
+ 
+    def limit_pyvolution(self):
+        """
+        Limit slopes for each volume to eliminate artificial variance
+        introduced by e.g. second order extrapolator
+
+        This is an unsophisticated limiter as it does not take into
+        account dependencies among quantities.
+
+        precondition:
+        vertex values are estimated from gradient
+        postcondition:
+        vertex values are updated
+        """
+
+
+        N = self.domain.number_of_elements
+        beta = self.domain.beta
+        #beta = 0.8
+
+        qc = self.centroid_values
+        qv = self.vertex_values
+
+        #Find min and max of this and neighbour's centroid values
+        qmax = self.qmax
+        qmin = self.qmin
+
+        for k in range(N):
+            qmax[k] = qmin[k] = qc[k]
+            for i in range(2):
+                n = self.domain.neighbours[k,i]
+                if n >= 0:
+                    qn = qc[n] #Neighbour's centroid value
+
+                    qmin[k] = min(qmin[k], qn)
+                    qmax[k] = max(qmax[k], qn)
+
+
+        #Diffences between centroids and maxima/minima
+        dqmax = qmax - qc
+        dqmin = qmin - qc
+
+        #Deltas between vertex and centroid values
+        dq = numpy.zeros(qv.shape, numpy.float)
+        for i in range(2):
+            dq[:,i] = qv[:,i] - qc
+
+        #Phi limiter
+        for k in range(N):
+
+            #Find the gradient limiter (phi) across vertices
+            phi = 1.0
+            for i in range(2):
+                r = 1.0
+                if (dq[k,i] > 0): r = dqmax[k]/dq[k,i]
+                if (dq[k,i] < 0): r = dqmin[k]/dq[k,i]
+
+                phi = min( min(r*beta, 1), phi )
+
+            #Then update using phi limiter
+            for i in range(2):
+                qv[k,i] = qc[k] + phi*dq[k,i]
+
+    def limit_range(self):
+        import sys
+
+        from util import minmod, minmod_kurganov, maxmod, vanleer, vanalbada
+
+        limiter = self.domain.limiter
+        #print limiter
+        
+        #print 'limit_range'
+        N = self.domain.number_of_elements
+        qc = self.centroid_values
+        qv = self.vertex_values
+        C = self.domain.centroids
+        X = self.domain.vertices
+        beta_p = numpy.zeros(N,numpy.float)
+        beta_m = numpy.zeros(N,numpy.float)
+        beta_x = numpy.zeros(N,numpy.float)
+        
+        for k in range(N):
+        
+            n0 = self.domain.neighbours[k,0]
+            n1 = self.domain.neighbours[k,1]
+            
+            if ( n0 >= 0) & (n1 >= 0):
+                #SLOPE DERIVATIVE LIMIT
+                beta_p[k] = (qc[k]-qc[k-1])/(C[k]-C[k-1])
+                beta_m[k] = (qc[k+1]-qc[k])/(C[k+1]-C[k])
+                beta_x[k] = (qc[k+1]-qc[k-1])/(C[k+1]-C[k-1])
+                
+        dq = numpy.zeros(qv.shape, numpy.float)
+        for i in range(2):
+            dq[:,i] =self.domain.vertices[:,i]-self.domain.centroids
+            
+        #Phi limiter
+        for k in range(N):
+            n0 = self.domain.neighbours[k,0]
+            n1 = self.domain.neighbours[k,1]
+            if n0 < 0:
+                phi = (qc[k+1] - qc[k])/(C[k+1] - C[k])
+            elif n1 < 0:
+                phi = (qc[k] - qc[k-1])/(C[k] - C[k-1])
+            #elif (self.domain.wet_nodes[k,0] == 2) & (self.domain.wet_nodes[k,1] == 2):
+            #    phi = 0.0
+            else:
+                if limiter == "minmod":
+                    phi = minmod(beta_p[k],beta_m[k])
+
+                elif limiter == "minmod_kurganov":#Change this
+                    # Also known as monotonized central difference limiter
+                    # if theta = 2.0
+                    theta = 2.0 
+                    phi = minmod_kurganov(theta*beta_p[k],theta*beta_m[k],beta_x[k])
+                elif limiter == "superbee":
+                    slope1 = minmod(beta_m[k],2.0*beta_p[k])
+                    slope2 = minmod(2.0*beta_m[k],beta_p[k])
+                    phi = maxmod(slope1,slope2)
+
+                elif limiter == "vanleer":
+                    phi = vanleer(beta_p[k],beta_m[k])
+
+                elif limiter == "vanalbada":
+                    phi = vanalbada(beta_m[k],beta_p[k])
+            
+            for i in range(2):
+                qv[k,i] = qc[k] + phi*dq[k,i]
+
+    def limit_steve_slope(self):    
+
+        import sys
+
+        from util import minmod, minmod_kurganov, maxmod, vanleer
+
+        N = self.domain.number_of_elements
+        limiter = self.domain.limiter
+        limiter_type = self.domain.limiter_type
+            
+        qc = self.centroid_values
+        qv = self.vertex_values
+
+        #Find min and max of this and neighbour's centroid values
+        beta_p = numpy.zeros(N,numpy.float)
+        beta_m = numpy.zeros(N,numpy.float)
+        beta_x = numpy.zeros(N,numpy.float)
+        C = self.domain.centroids
+        X = self.domain.vertices
+
+        for k in range(N):
+        
+            n0 = self.domain.neighbours[k,0]
+            n1 = self.domain.neighbours[k,1]
+            
+            if (n0 >= 0) & (n1 >= 0):
+                # Check denominator not zero
+                if (qc[k+1]-qc[k]) == 0.0:
+                    beta_p[k] = float(sys.maxint)
+                    beta_m[k] = float(sys.maxint)
+                else:
+                    #STEVE LIMIT
+                    beta_p[k] = (qc[k]-qc[k-1])/(qc[k+1]-qc[k])
+                    beta_m[k] = (qc[k+2]-qc[k+1])/(qc[k+1]-qc[k])
+
+        #Deltas between vertex and centroid values
+        dq = numpy.zeros(qv.shape, numpy.float)
+        for i in range(2):
+            dq[:,i] =self.domain.vertices[:,i]-self.domain.centroids
+            
+        #Phi limiter
+        for k in range(N):
+                
+            phi = 0.0
+            if limiter == "flux_minmod":
+                #FLUX MINMOD
+                phi = minmod_kurganov(1.0,beta_m[k],beta_p[k])
+            elif limiter == "flux_superbee":
+                #FLUX SUPERBEE
+                phi = max(0.0,min(1.0,2.0*beta_m[k]),min(2.0,beta_m[k]))+max(0.0,min(1.0,2.0*beta_p[k]),min(2.0,beta_p[k]))-1.0
+            elif limiter == "flux_muscl":
+                #FLUX MUSCL
+                phi = max(0.0,min(2.0,2.0*beta_m[k],2.0*beta_p[k],0.5*(beta_m[k]+beta_p[k])))
+            elif limiter == "flux_vanleer":
+                #FLUX VAN LEER
+                phi = (beta_m[k]+abs(beta_m[k]))/(1.0+abs(beta_m[k]))+(beta_p[k]+abs(beta_p[k]))/(1.0+abs(beta_p[k]))-1.0
+                
+                #Then update using phi limiter
+                n = self.domain.neighbours[k,1]
+                if n>=0:
+                    #qv[k,0] = qc[k] - 0.5*phi*(qc[k+1]-qc[k])
+                    #qv[k,1] = qc[k] + 0.5*phi*(qc[k+1]-qc[k])
+                    qv[k,0] = qc[k] + 0.5*phi*(qv[k,0]-qc[k])
+                    qv[k,1] = qc[k] + 0.5*phi*(qv[k,1]-qc[k])
+                else:
+                    qv[k,i] = qc[k]
+
+    def backup_centroid_values(self):
+        # Call correct module function
+        # (either from this module or C-extension)
+        #backup_centroid_values(self)
+
+        self.centroid_backup_values[:,] = (self.centroid_values).astype('f')
+
+    def saxpy_centroid_values(self,a,b):
+        # Call correct module function
+        # (either from this module or C-extension)
+        self.centroid_values[:,] = (a*self.centroid_values + b*self.centroid_backup_values).astype('f')
+        
+
+
+def newLinePlot(title='Simple Plot'):
+    import pylab as g
+    g.ion()
+    g.hold(False)
+    g.title(title)
+    g.xlabel('x')
+    g.ylabel('y')
+    
+
+def linePlot(x,y):
+    import pylab as g
+    g.plot(x.flat,y.flat)
+
+
+def closePlots():
+    import pylab as g
+    g.close('all')
+    
+if __name__ == "__main__":
+    #from domain import Domain
+    from generic_domain import Generic_domain as Domain
+    
+    points1 = [0.0, 1.0, 2.0, 3.0]
+    vertex_values = [[1.0,2.0],[4.0,5.0],[-1.0,2.0]]
+
+    D1 = Domain(points1)
+
+    Q1 = Quantity(D1, vertex_values)
+
+    print Q1.vertex_values
+    print Q1.centroid_values
+
+    new_vertex_values = [[2.0,1.0],[3.0,4.0],[-2.0,4.0]]
+
+    Q1.set_values(new_vertex_values)
+
+    print Q1.vertex_values
+    print Q1.centroid_values
+
+    new_centroid_values = [20,30,40]
+    Q1.set_values(new_centroid_values,'centroids')
+
+    print Q1.vertex_values
+    print Q1.centroid_values
+
+    class FunClass:
+        def __init__(self,value):
+            self.value = value
+
+        def __call__(self,x):
+            return self.value*(x**2)
+
+
+    fun = FunClass(1.0)
+    Q1.set_values(fun,'vertices')
+
+    print Q1.vertex_values
+    print Q1.centroid_values
+
+    Xc = Q1.domain.vertices
+    Qc = Q1.vertex_values
+    print Xc
+    print Qc
+
+    Qc[1,0] = 3
+
+    Q1.extrapolate_second_order()
+    #Q1.limit_minmod()
+
+    newLinePlot('plots')
+    linePlot(Xc,Qc)
+    raw_input('press return')
+
+    points2 = numpy.arange(10)
+    D2 = Domain(points2)
+
+    Q2 = Quantity(D2)
+    Q2.set_values(fun,'vertices')
+    Xc = Q2.domain.vertices
+    Qc = Q2.vertex_values
+    linePlot(Xc,Qc)
+    raw_input('press return')
+
+    
+    Q2.extrapolate_second_order()
+    #Q2.limit_minmod()
+    Xc = Q2.domain.vertices
+    Qc = Q2.vertex_values
+    print Q2.centroid_values
+    print Qc
+    linePlot(Xc,Qc)
+    raw_input('press return')
+
+
+    for i in range(10):
+        import pylab as g
+        g.hold(True)
+        fun = FunClass(i/10.0)
+        Q2.set_values(fun,'centroids')
+        Q2.extrapolate_second_order()
+        #Q2.limit_minmod()
+        Qc = Q2.vertex_values
+        linePlot(Xc,Qc)
+        raw_input('press return')
+
+    raw_input('press return to quit')
+closePlots()
Index: /anuga_work/development/2010-projects/anuga_1d/base/test_all.py
===================================================================
--- /anuga_work/development/2010-projects/anuga_1d/base/test_all.py	(revision 7850)
+++ /anuga_work/development/2010-projects/anuga_1d/base/test_all.py	(revision 7850)
@@ -0,0 +1,83 @@
+"""Regression testing framework
+This module will search for scripts in the same directory named
+test_*.py.  Each such script should be a test suite that tests a
+module through PyUnit. This script will aggregate all
+found test suites into one big test suite and run them all at once.
+"""
+
+# Author: Mark Pilgrim
+# Modified by Ole Nielsen
+
+import unittest
+import os
+
+
+#List files that should be excluded from the testing process.
+#E.g. if they are known to fail and under development
+exclude = [] 
+
+
+def get_test_files(path):
+
+    import sys
+
+    files = os.listdir(path)
+
+    #Check sub directories
+    test_files = []
+    for file in files:
+        if os.path.isdir(file):
+            sys.path.append(file)
+            #print 'Recursing into', file
+            test_files += get_test_files(path + os.sep + file)
+        elif file[:5] == 'test_' and file[-2:] == 'py':
+            #print 'Appending', file
+            test_files.append(file)
+        else:
+            pass
+    return test_files
+
+
+
+def regressionTest():
+    import sys, os, re, unittest
+    path = os.path.split(sys.argv[0])[0] or os.getcwd()
+
+
+    files = get_test_files(path)
+
+
+
+    #test = re.compile('^test_[\w]*.py$', re.IGNORECASE)
+    #files = filter(test.search, files)
+
+
+    try:
+        files.remove(__file__)  #Remove self from list (Ver 2.3. or later)
+    except:
+        files.remove('test_all.py')
+
+    print 'Testing:'
+    for file in files:
+        print '  ' + file
+
+    if globals().has_key('exclude'):
+        for file in exclude:
+            files.remove(file)
+            print 'WARNING: File '+ file + ' excluded from testing'
+
+
+    filenameToModuleName = lambda f: os.path.splitext(f)[0]
+    #print "files",files 
+    moduleNames = map(filenameToModuleName, files)
+    #print "moduleNames",moduleNames
+    modules = map(__import__, moduleNames)
+    load = unittest.defaultTestLoader.loadTestsFromModule
+    return unittest.TestSuite(map(load, modules))
+
+################################################################################
+
+if __name__ == '__main__':   
+    suite = regressionTest()
+    runner = unittest.TextTestRunner() #verbosity=2)
+    runner.run(suite)
Index: /anuga_work/development/2010-projects/anuga_1d/base/test_generic_domain.py
===================================================================
--- /anuga_work/development/2010-projects/anuga_1d/base/test_generic_domain.py	(revision 7850)
+++ /anuga_work/development/2010-projects/anuga_1d/base/test_generic_domain.py	(revision 7850)
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+
+import unittest
+from math import sqrt, pi
+
+
+from anuga_1d.generic.generic_domain import *
+import numpy
+
+
+class Test_Generic_Domain(unittest.TestCase):
+    def setUp(self):
+        self.points = [0.0, 1.0, 2.0, 3.0]
+        self.vertex_values = [[1.0,2.0],[4.0,5.0],[-1.0,2.0]]
+        self.points2 = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
+        
+    def tearDown(self):
+        pass
+        #print "  Tearing down"
+
+
+    def test_creation(self):
+        domain = Generic_domain(self.points)
+
+        assert numpy.allclose(domain.neighbours, \
+            [[-1 , 1],[ 0 , 2],[ 1 ,-1]])
+ 
+        assert numpy.allclose(domain.neighbour_vertices, \
+            [[-1 , 0],[ 1 , 0],[ 1 ,-1]])
+
+        assert numpy.allclose(domain.number_of_boundaries, \
+            [1, 0, 1])
+
+        assert numpy.allclose(domain.surrogate_neighbours, \
+            [[0, 1],[0, 2],[1, 2]])
+
+        assert numpy.allclose(domain.vertices, \
+            [[ 0.,  1.],[ 1.,  2.],[ 2.,  3.]])
+
+
+        assert numpy.allclose(domain.centroids, [0.5, 1.5, 2.5])
+
+        assert numpy.allclose(domain.areas, [ 1.,  1.,  1.])
+
+        assert numpy.allclose(domain.normals, \
+            [[-1.,  1.],[-1.,  1.],[-1.,  1.]])
+
+
+#-------------------------------------------------------------
+if __name__ == "__main__":
+    suite = unittest.makeSuite(Test_Generic_Domain, 'test')
+    #suite = unittest.makeSuite(Test_Shallow_Water, 'test_evolve_first_order')
+
+
+    runner = unittest.TextTestRunner()
+    runner.run(suite)
Index: /anuga_work/development/2010-projects/anuga_1d/base/test_quantity.py
===================================================================
--- /anuga_work/development/2010-projects/anuga_1d/base/test_quantity.py	(revision 7850)
+++ /anuga_work/development/2010-projects/anuga_1d/base/test_quantity.py	(revision 7850)
@@ -0,0 +1,604 @@
+import quantity
+#!/usr/bin/env python
+
+import unittest
+from math import sqrt, pi
+
+
+from generic_domain import Generic_domain as Domain
+#from shallow_water_domain import flux_function as domain_flux_function
+
+from quantity import *
+
+
+
+from numpy import allclose, array, ones, zeros
+import numpy
+
+
+class Test_Quantity(unittest.TestCase):
+    def setUp(self):
+        self.points3        = [0.0, 1.0, 2.0, 3.0]
+        self.vertex_values3 = [[1.0,2.0],[4.0,5.0],[-1.0,2.0]]
+        self.domain3        = Domain(self.points3)
+
+
+        
+        self.points4          = [0.0, 1.0, 2.5, 3.0, 5.0]
+        self.vertex_values4   = [[1.0,2.0],[4.0,5.0],[-1.0,2.0],[3.0,6.0]]
+        self.centroid_values4 = [1.5, 4.5, 0.5, 4.5]
+        self.boundary4        = {(0, 0): 'left', (3, 1): 'right'}
+        self.domain4          = Domain(self.points4,self.boundary4)
+
+        self.points10 = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
+        self.domain10 = Domain(self.points10)
+
+        self.points6 = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
+        self.domain6 = Domain(self.points6)
+
+
+    def tearDown(self):
+        pass
+        #print "  Tearing down"
+
+
+    def test_creat_with_boundary(self):
+
+        assert self.domain4.boundary  == {(0, 0): 'left', (3, 1): 'right'}
+        
+    def test_creation(self):
+
+        quantity = Quantity(self.domain3)
+        assert allclose(quantity.vertex_values, [[0.0,0.0],[0.0,0.0],[0.0,0.0]])
+
+        
+        try:
+            quantity = Quantity()
+        except:
+            pass
+        else:
+            raise 'Should have raised empty quantity exception'
+
+
+        try:
+            quantity = Quantity([1,2,3])
+        except AssertionError:
+            pass
+        except:
+            raise 'Should have raised "mising domain object" error'
+
+
+    def test_creation_zeros(self):
+
+        quantity = Quantity(self.domain3)
+        assert allclose(quantity.centroid_values, [[0.,0.,0.]])
+
+
+        quantity = Quantity(self.domain4)
+        assert allclose(quantity.vertex_values, [[0.,0.], [0.,0.],
+                                                 [0.,0.], [0.,0.]])
+
+
+    def test_interpolation(self):
+        quantity = Quantity(self.domain4, self.vertex_values4)
+        assert allclose(quantity.centroid_values, self.centroid_values4) #Centroid
+
+
+
+    def test_interpolation2(self):
+        quantity = Quantity(self.domain4, self.vertex_values4)
+        assert allclose(quantity.centroid_values, self.centroid_values4) #Centroid
+
+        quantity.extrapolate_second_order()
+
+        #print quantity.vertex_values
+        assert allclose(quantity.vertex_values,[[ 0.3,  2.7],
+                                                [ 4.5,  4.5],
+                                                [ 0.5,  0.5],
+                                                [ 1.3,  7.7]])
+
+ 
+
+ 
+    def test_boundary_allocation(self):
+        quantity = Quantity(self.domain4,
+                            [[1,2], [5,5], [0,9], [-6, 3]])
+
+
+        assert quantity.boundary_values.shape[0] == len(self.domain4.boundary)
+
+
+    def test_set_values(self):
+        quantity = Quantity(self.domain4)
+
+        # Test vertices
+        quantity.set_values([[1,2], [5,5], [0,9], [-6, 3]], location = 'vertices')
+        assert allclose(quantity.vertex_values,
+                        [[1,2], [5,5], [0,9], [-6, 3]])
+        assert allclose(quantity.centroid_values, [1.5, 5., 4.5, -1.5]) #Centroid
+
+
+
+        # Test unique_vertices
+        quantity.set_values([1,2,4,-5,6], location='unique_vertices')
+        assert allclose(quantity.vertex_values,
+                        [[1,2], [2,4], [4,-5], [-5,6]])
+        assert allclose(quantity.centroid_values, [1.5, 3., -.5, .5]) #Centroid
+
+
+        # Test centroids
+        quantity.set_values([1,2,3,4], location = 'centroids')
+        assert allclose(quantity.centroid_values, [1., 2., 3., 4.]) #Centroid
+
+        # Test exceptions
+        try:
+            quantity.set_values([[1,3], [5,5], [0,9], [-6, 3]],
+                                location = 'bas kamel tuba')
+        except:
+            pass
+
+
+        try:
+            quantity.set_values([[1,3], [0,9]])
+        except AssertionError:
+            pass
+        except:
+            raise 'should have raised Assertionerror'
+
+
+
+    def test_set_values_const(self):
+        quantity = Quantity(self.domain4)
+
+        quantity.set_values(1.0, location = 'vertices')
+
+        assert allclose(quantity.vertex_values, [[1,1], [1,1], [1,1], [1, 1]])
+        assert allclose(quantity.centroid_values, [1, 1, 1, 1]) #Centroid
+
+
+        quantity.set_values(2.0, location = 'centroids')
+
+        assert allclose(quantity.centroid_values, [2, 2, 2, 2])
+
+
+    def test_set_values_func(self):
+        quantity = Quantity(self.domain4)
+
+        def f(x):
+            return x*x
+
+
+
+        quantity.set_values(f, location = 'vertices')
+
+
+        assert allclose(quantity.vertex_values,
+                        [[0,1], [1,6.25], [6.25,9], [9,25]])
+
+        assert allclose(quantity.centroid_values,
+                        [0.5, 3.625, 7.625, 34*0.5])
+
+
+        quantity.set_values(f, location = 'centroids')
+
+        
+        assert allclose(quantity.centroid_values,
+                        [0.25, 3.0625, 7.5625, 16.0])
+
+
+    def test_integral(self):
+        quantity = Quantity(self.domain4)
+
+        #Try constants first
+        const = 5
+        quantity.set_values(const, location = 'vertices')
+        #print 'Q', quantity.get_integral()
+
+        assert allclose(quantity.get_integral(), self.domain4.get_area() * const)
+
+        #Try with a linear function
+        def f(x):
+            return x
+
+        quantity.set_values(f, location = 'vertices')
+
+ 
+        assert allclose (quantity.centroid_values,
+        [ 0.5,   1.75,  2.75,  4.  ])
+
+        assert allclose (quantity.vertex_values, [[ 0.,   1. ],
+                                                  [ 1.,   2.5],
+                                                  [ 2.5,  3. ],
+                                                  [ 3.,   5. ]])
+
+
+        ref_integral = 0.5 + 1.5*1.75 + 0.5*2.75 + 2.0*4.0
+
+        assert allclose (quantity.get_integral(), ref_integral)
+
+
+
+
+
+    def test_set_values_from_quantity(self):
+
+        quantity1 = Quantity(self.domain4)
+        quantity1.set_values([0,1,2,3,4], location='unique_vertices')
+
+        assert allclose(quantity1.vertex_values,
+                        [[0,1], [1,2], [2,3], [3,4]])
+
+
+        quantity2 = Quantity(self.domain4)
+        quantity2.set_values(quantity1)
+        assert allclose(quantity2.vertex_values,
+                        [[0,1], [1,2], [2,3], [3,4]])
+
+        quantity2.set_values(2*quantity1)
+
+        assert allclose(quantity2.vertex_values,
+                        [[0,2], [2,4], [4,6], [6,8]])
+
+        quantity2.set_values(2*quantity1 + 3)
+        assert allclose(quantity2.vertex_values,
+                        [[3,5], [5,7], [7,9], [9,11]])
+
+
+
+    def test_overloading(self):
+
+        quantity1 = Quantity(self.domain4)
+        quantity1.set_values( [[0,1],[1,2],[2,3],[3,4]],
+                            location = 'vertices')
+
+        assert allclose(quantity1.vertex_values,
+                        [[0,1], [1,2], [2,3], [3,4]])
+
+
+        quantity2 = Quantity(self.domain4)
+        quantity2.set_values([[1,2], [5,5], [0,9], [-6, 3]],
+                             location = 'vertices')
+
+
+
+        quantity3 = Quantity(self.domain4)
+        quantity3.set_values([[2,2], [7,8], [7,6], [3, -8]],
+                             location = 'vertices')
+
+
+        # Negation
+        Q = -quantity1
+        assert allclose(Q.vertex_values, -quantity1.vertex_values)
+        assert allclose(Q.centroid_values, -quantity1.centroid_values)
+
+
+        # Addition
+        Q = quantity1 + 7
+        assert allclose(Q.vertex_values, quantity1.vertex_values + 7)
+        assert allclose(Q.centroid_values, quantity1.centroid_values + 7)
+
+        Q = 7 + quantity1
+        assert allclose(Q.vertex_values, quantity1.vertex_values + 7)
+        assert allclose(Q.centroid_values, quantity1.centroid_values + 7)
+
+        Q = quantity1 + quantity2
+        assert allclose(Q.vertex_values,
+                        quantity1.vertex_values + quantity2.vertex_values)
+        assert allclose(Q.centroid_values,
+                        quantity1.centroid_values + quantity2.centroid_values)
+
+        Q = quantity1 + quantity2 - 3
+        assert allclose(Q.vertex_values,
+                        quantity1.vertex_values + quantity2.vertex_values - 3)
+
+        Q = quantity1 - quantity2
+        assert allclose(Q.vertex_values,
+                        quantity1.vertex_values - quantity2.vertex_values)
+
+        #Scaling
+        Q = quantity1*3
+        assert allclose(Q.vertex_values, quantity1.vertex_values*3)
+        assert allclose(Q.centroid_values, quantity1.centroid_values*3)
+
+        Q = 3*quantity1
+        assert allclose(Q.vertex_values, quantity1.vertex_values*3)
+
+        #Multiplication
+        Q = quantity1 * quantity2
+        assert allclose(Q.vertex_values,
+                        quantity1.vertex_values * quantity2.vertex_values)
+
+        #Linear combinations
+        Q = 4*quantity1 + 2
+        assert allclose(Q.vertex_values,
+                        4*quantity1.vertex_values + 2)
+
+        Q = quantity1*quantity2 + 2
+        assert allclose(Q.vertex_values,
+                        quantity1.vertex_values * quantity2.vertex_values + 2)
+
+        Q = quantity1*quantity2 + quantity3
+        assert allclose(Q.vertex_values,
+                        quantity1.vertex_values * quantity2.vertex_values +
+                        quantity3.vertex_values)
+        Q = quantity1*quantity2 + 3*quantity3
+        assert allclose(Q.vertex_values,
+                        quantity1.vertex_values * quantity2.vertex_values +
+                        3*quantity3.vertex_values)
+        Q = quantity1*quantity2 + 3*quantity3 + 5.0
+        assert allclose(Q.vertex_values,
+                        quantity1.vertex_values * quantity2.vertex_values +
+                        3*quantity3.vertex_values + 5)
+
+        Q = quantity1*quantity2 - quantity3
+        assert allclose(Q.vertex_values,
+                        quantity1.vertex_values * quantity2.vertex_values -
+                        quantity3.vertex_values)
+        Q = 1.5*quantity1*quantity2 - 3*quantity3 + 5.0
+        assert allclose(Q.vertex_values,
+                        1.5*quantity1.vertex_values * quantity2.vertex_values -
+                        3*quantity3.vertex_values + 5)
+
+        #Try combining quantities and arrays and scalars
+        Q = 1.5*quantity1*quantity2.vertex_values -\
+            3*quantity3.vertex_values + 5.0
+        assert allclose(Q.vertex_values,
+                        1.5*quantity1.vertex_values * quantity2.vertex_values -
+                        3*quantity3.vertex_values + 5)
+
+
+        #Powers
+        Q = quantity1**2
+        assert allclose(Q.vertex_values, quantity1.vertex_values**2)
+
+        Q = quantity1**2 +quantity2**2
+        assert allclose(Q.vertex_values,
+                        quantity1.vertex_values**2 + \
+                        quantity2.vertex_values**2)
+
+        Q = (quantity1**2 +quantity2**2)**0.5
+        assert allclose(Q.vertex_values,
+                        (quantity1.vertex_values**2 + \
+                        quantity2.vertex_values**2)**0.5)
+
+    def test_compute_gradient(self):
+        quantity = Quantity(self.domain6)
+
+        #Set up for a gradient of (2,0) at mid triangle
+        quantity.set_values([2.0, 4.0, 4.0, 5.0, 10.0, 12.0],
+                            location = 'centroids')
+
+
+        #Gradients
+        quantity.compute_gradients()
+
+        a = quantity.gradients
+
+        assert allclose(a, [ 3., 1., 0.5, 3., 3.5, 0.5])
+        
+        quantity.extrapolate_second_order()
+
+
+        assert allclose(quantity.vertex_values, [[ 1., 3. ],
+                                                 [ 4., 4. ],
+                                                 [ 4., 4. ],
+                                                 [ 4., 6.],
+                                                 [ 8.25, 11.75],
+                                                 [ 11.,  13. ]])
+
+                                                 
+
+    def test_second_order_extrapolation2(self):
+        quantity = Quantity(self.domain4)
+
+        #Set up for a gradient of (3,1), f(x) = 3x+y
+        quantity.set_values([2.0+2.0/3, 4.0+4.0/3, 8.0+2.0/3, 2.0+8.0/3],
+                            location = 'centroids')
+
+        #Gradients
+        quantity.compute_gradients()
+
+        a = quantity.gradients
+        
+
+        assert allclose(a[1], 2.8)
+
+        #Work out the others
+
+        quantity.extrapolate_second_order()
+        
+
+        assert allclose(quantity.vertex_values[1,0], 3.33333333)
+        assert allclose(quantity.vertex_values[1,1], 7.33333333)
+
+        assert allclose(quantity.centroid_values[1], 0.5*(7.33333333+3.33333333) )
+
+
+
+
+    def test_backup_saxpy_centroid_values(self):
+        quantity = Quantity(self.domain4)
+
+        #Set up for a gradient of (3,1), f(x) = 3x+y
+        c_values = array([2.0+2.0/3, 4.0+4.0/3, 8.0+2.0/3, 2.0+8.0/3])
+        d_values = array([1.0, 2.0, 3.0, 4.0])
+        quantity.set_values(c_values, location = 'centroids')
+
+        #Backup
+        quantity.backup_centroid_values()
+
+        #print quantity.vertex_values
+        assert allclose(quantity.centroid_values, quantity.centroid_backup_values)
+
+
+        quantity.set_values(d_values, location = 'centroids')
+
+        quantity.saxpy_centroid_values(2.0, 3.0)
+
+        assert allclose(quantity.centroid_values, 2.0*d_values + 3.0*c_values)
+
+
+
+    def test_first_order_extrapolator(self):
+        quantity = Quantity(self.domain4)
+
+        centroid_values = array([1.,2.,3.,4.])
+        quantity.set_values(centroid_values, location = 'centroids')
+        assert allclose(quantity.centroid_values, centroid_values) #Centroid
+
+        #Extrapolate
+        quantity.extrapolate_first_order()
+
+        #Check that gradient is zero
+        a = quantity.gradients
+        assert allclose(a, [0,0,0,0])
+       
+
+        #Check vertices but not edge values
+        assert allclose(quantity.vertex_values,
+                        [[1,1], [2,2], [3,3], [4,4]])
+
+
+    def test_second_order_extrapolator(self):
+        quantity = Quantity(self.domain4)
+
+        #Set up for a gradient of (3,0) at mid triangle
+        quantity.set_values([2.0, 4.0, 8.0, 2.0],
+                            location = 'centroids')
+
+
+
+        quantity.extrapolate_second_order()
+
+
+        #Assert that quantities are conserved
+        for k in range(quantity.centroid_values.shape[0]):
+            assert allclose (quantity.centroid_values[k],
+                             numpy.sum(quantity.vertex_values[k,:])/2.0)
+
+
+    def test_limit(self):
+        quantity = Quantity(self.domain4)
+
+        #Create a deliberate overshoot (e.g. from gradient computation)
+        quantity.set_values([[0,0], [2,20], [-20,3], [8,3]])
+
+        #Limit
+        quantity.limit_minmod()
+
+       
+        #cells 1 and 2 are local max and min
+        assert quantity.vertex_values[1][0] == quantity.centroid_values[1]
+        assert quantity.vertex_values[1][1] == quantity.centroid_values[1]
+
+        assert quantity.vertex_values[2][0] == quantity.centroid_values[2]
+        assert quantity.vertex_values[2][1] == quantity.centroid_values[2]
+
+
+
+    def test_distribute_first_order(self):
+        quantity = Quantity(self.domain4)
+
+        #Test centroids
+        centroid_values = array([1.,2.,3.,4.])
+        quantity.set_values(centroid_values, location = 'centroids')
+        assert allclose(quantity.centroid_values, centroid_values) #Centroid
+
+
+        #Extrapolate from centroid to vertices and edges
+        quantity.extrapolate_first_order()
+        
+        assert allclose(quantity.vertex_values,[[ 1.,  1.],
+                                                [ 2.,  2.],
+                                                [ 3.,  3.],
+                                                [ 4.,  4.]])
+ 
+
+
+    def test_distribute_second_order(self):
+        quantity = Quantity(self.domain4)
+
+        #Test centroids
+        centroid_values = array([2.,4.,8.,2.])
+        quantity.set_values(centroid_values, location = 'centroids')
+        assert allclose(quantity.centroid_values, centroid_values) #Centroid
+
+
+        #Extrapolate
+        quantity.extrapolate_second_order()
+
+        assert allclose(quantity.vertex_values, [[ 1.2,  2.8],
+                                                 [ 2.,  6. ],
+                                                 [ 8.,   8. ],
+                                                 [ 6.8, -2.8]])
+
+
+    def test_update_explicit(self):
+        quantity = Quantity(self.domain4)
+
+        #Test centroids
+        quantity.set_values([1.,2.,3.,4.], location = 'centroids')
+        assert allclose(quantity.centroid_values, [1, 2, 3, 4]) #Centroid
+
+        #Set explicit_update
+        quantity.explicit_update = array( [1.,1.,1.,1.] )
+
+        #Update with given timestep
+        quantity.update(0.1)
+
+        x = array([1, 2, 3, 4]) + array( [.1,.1,.1,.1] )
+        assert allclose( quantity.centroid_values, x)
+
+    def test_update_semi_implicit(self):
+        quantity = Quantity(self.domain4)
+
+        #Test centroids
+        quantity.set_values([1.,2.,3.,4.], location = 'centroids')
+        assert allclose(quantity.centroid_values, [1, 2, 3, 4]) #Centroid
+
+        #Set semi implicit update
+        quantity.semi_implicit_update = array([1.,1.,1.,1.])
+
+        #Update with given timestep
+        timestep = 0.1
+        quantity.update(timestep)
+
+        sem = array([1.,1.,1.,1.])
+        denom = ones(4, numpy.float)-timestep*sem
+
+        x = array([1, 2, 3, 4])/denom
+        assert allclose( quantity.centroid_values, x)
+
+
+    def test_both_updates(self):
+        quantity = Quantity(self.domain4)
+
+        #Test centroids
+        centroid_values = array( [1, 2, 3, 4] )
+        quantity.set_values(centroid_values, location = 'centroids')
+        assert allclose(quantity.centroid_values, centroid_values) #Centroid
+
+        #Set explicit_update
+        explicit_update = array( [4.,3.,2.,1.] )
+        quantity.explicit_update[:,] = explicit_update
+
+        #Set semi implicit update
+        semi_implicit_update = array( [1.,1.,1.,1.] )
+        quantity.semi_implicit_update[:,] = semi_implicit_update
+
+        #Update with given timestep
+        timestep = 0.1
+        quantity.update(0.1)
+
+        denom = 1.0-timestep*semi_implicit_update
+        x = (centroid_values + timestep*explicit_update)/denom
+ 
+        assert allclose( quantity.centroid_values, x)
+
+
+ 
+#-------------------------------------------------------------
+if __name__ == "__main__":
+    suite = unittest.makeSuite(Test_Quantity, 'test')
+    runner = unittest.TextTestRunner()
+    runner.run(suite)
Index: /anuga_work/development/2010-projects/anuga_1d/base/util.py
===================================================================
--- /anuga_work/development/2010-projects/anuga_1d/base/util.py	(revision 7850)
+++ /anuga_work/development/2010-projects/anuga_1d/base/util.py	(revision 7850)
@@ -0,0 +1,57 @@
+"""
+This module contains various auxiliary function used by pyvolution.
+"""
+
+
+import numpy
+
+def mean(x):
+    return numpy.sum(x)/len(x)
+
+
+def gradient(x0, x1, q0, q1):
+
+    if q1-q0 != 0:
+        a = (q1-q0)/(x1-x0)
+    else:
+        a = 0
+        
+    return a
+
+def  minmod(beta_p,beta_m):
+    if (abs(beta_p) < abs(beta_m)) & (beta_p*beta_m > 0.0):
+        phi = beta_p
+    elif (abs(beta_m) < abs(beta_p)) & (beta_p*beta_m > 0.0):
+        phi = beta_m
+    else:
+        phi = 0.0
+    return phi
+
+def  minmod_kurganov(a,b,c):
+    from numpy import sign
+    if sign(a)==sign(b)==sign(c):
+        return sign(a)*min(abs(a),abs(b),abs(c))
+    else:
+        return 0.0
+
+def  maxmod(a,b):
+    if (abs(a) > abs(b)) & (a*b > 0.0):
+        phi = a
+    elif (abs(b) > abs(a)) & (a*b > 0.0):
+        phi = b
+    else:
+        phi = 0.0
+    return phi
+
+def vanleer(a,b):
+    if abs(a)+abs(b) > 1e-12:
+        return (a*abs(b)+abs(a)*b)/(abs(a)+abs(b))
+    else:
+        return 0.0
+
+def vanalbada(a,b):
+    if a*a+b*b > 1e-12:
+        return (a*a*b+a*b*b)/(a*a+b*b)
+    else:
+        return 0.0
+
