Changeset 4769


Ignore:
Timestamp:
Oct 30, 2007, 5:13:17 PM (17 years ago)
Author:
ole
Message:

Retired all Python code which is superseded by C implementations.
This has made the code base much smaller and there is less confusion when people try to understand the algorithms.

Location:
anuga_core/source/anuga
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • anuga_core/source/anuga/abstract_2d_finite_volumes/quantity.py

    r4768 r4769  
    332332
    333333
    334         #General input checks
     334        # General input checks
    335335        L = [numeric, quantity, function, geospatial_data, points, filename]
    336336        msg = 'Exactly one of the arguments '+\
     
    360360
    361361
    362         #Determine which 'set_values_from_...' to use
     362        # Determine which 'set_values_from_...' to use
    363363
    364364        if numeric is not None:
     
    13371337   
    13381338
    1339 def update(quantity, timestep):
    1340     """Update centroid values based on values stored in
    1341     explicit_update and semi_implicit_update as well as given timestep
    1342 
    1343     Function implementing forcing terms must take on argument
    1344     which is the domain and they must update either explicit
    1345     or implicit updates, e,g,:
    1346 
    1347     def gravity(domain):
    1348         ....
    1349         domain.quantities['xmomentum'].explicit_update = ...
    1350         domain.quantities['ymomentum'].explicit_update = ...
    1351 
    1352 
    1353 
    1354     Explicit terms must have the form
    1355 
    1356         G(q, t)
    1357 
    1358     and explicit scheme is
    1359 
    1360        q^{(n+1}) = q^{(n)} + delta_t G(q^{n}, n delta_t)
    1361 
    1362 
    1363     Semi implicit forcing terms are assumed to have the form
    1364 
    1365        G(q, t) = H(q, t) q
    1366 
    1367     and the semi implicit scheme will then be
    1368 
    1369       q^{(n+1}) = q^{(n)} + delta_t H(q^{n}, n delta_t) q^{(n+1})
    1370 
    1371 
    1372     """
    1373 
    1374     from Numeric import sum, equal, ones, exp, Float
    1375 
    1376     N = quantity.centroid_values.shape[0]
    1377 
    1378 
    1379     # Divide H by conserved quantity to obtain G (see docstring above)
    1380 
    1381 
    1382     for k in range(N):
    1383         x = quantity.centroid_values[k]
    1384         if x == 0.0:
    1385             #FIXME: Is this right
    1386             quantity.semi_implicit_update[k] = 0.0
    1387         else:
    1388             quantity.semi_implicit_update[k] /= x
    1389 
    1390 
    1391     # Semi implicit updates
    1392     denominator = ones(N, Float)-timestep*quantity.semi_implicit_update
    1393 
    1394     if sum(less(denominator, 1.0)) > 0.0:
    1395         msg = 'denominator < 1.0 in semi implicit update. Call Stephen :-)'
    1396         raise msg
    1397 
    1398     if sum(equal(denominator, 0.0)) > 0.0:
    1399         msg = 'Zero division in semi implicit update. Call Stephen :-)'
    1400         raise msg
    1401     else:
    1402         #Update conserved_quantities from semi implicit updates
    1403         quantity.centroid_values /= denominator
    1404 
    1405 #    quantity.centroid_values = exp(timestep*quantity.semi_implicit_update)*quantity.centroid_values
    1406 
    1407     #Explicit updates
    1408     quantity.centroid_values += timestep*quantity.explicit_update
    1409 
    1410 def interpolate_from_vertices_to_edges(quantity):
    1411     """Compute edge values from vertex values using linear interpolation
    1412     """
    1413 
    1414     for k in range(quantity.vertex_values.shape[0]):
    1415         q0 = quantity.vertex_values[k, 0]
    1416         q1 = quantity.vertex_values[k, 1]
    1417         q2 = quantity.vertex_values[k, 2]
    1418 
    1419         quantity.edge_values[k, 0] = 0.5*(q1+q2)
    1420         quantity.edge_values[k, 1] = 0.5*(q0+q2)
    1421         quantity.edge_values[k, 2] = 0.5*(q0+q1)
    1422 
    1423 
    1424 
    1425 def backup_centroid_values(quantity):
    1426     """Copy centroid values to backup array"""
    1427 
    1428     qc = quantity.centroid_values
    1429     qb = quantity.centroid_backup_values
    1430 
    1431     #Check each triangle
    1432     for k in range(len(quantity.domain)):
    1433         qb[k] = qc[k]
    1434 
    1435 
    1436 def saxpy_centroid_values(quantity,a,b):
    1437     """saxpy operation between centroid value and backup"""
    1438 
    1439     qc = quantity.centroid_values
    1440     qb = quantity.centroid_backup_values
    1441 
    1442     #Check each triangle
    1443     for k in range(len(quantity.domain)):
    1444         qc[k] = a*qc[k]+b*qb[k]       
    1445 
    1446 
    1447 def compute_gradients(quantity):
    1448     """Compute gradients of triangle surfaces defined by centroids of
    1449     neighbouring volumes.
    1450     If one edge is on the boundary, use own centroid as neighbour centroid.
    1451     If two or more are on the boundary, fall back to first order scheme.
    1452     """
    1453 
    1454     from Numeric import zeros, Float
    1455     from utilitites.numerical_tools import gradient
    1456 
    1457     centroid_coordinates = quantity.domain.centroid_coordinates
    1458     surrogate_neighbours = quantity.domain.surrogate_neighbours
    1459     centroid_values = quantity.centroid_values
    1460     number_of_boundaries = quantity.domain.number_of_boundaries
    1461 
    1462     N = centroid_values.shape[0]
    1463 
    1464     a = zeros(N, Float)
    1465     b = zeros(N, Float)
    1466 
    1467     for k in range(N):
    1468         if number_of_boundaries[k] < 2:
    1469             #Two or three true neighbours
    1470 
    1471             #Get indices of neighbours (or self when used as surrogate)
    1472             k0, k1, k2 = surrogate_neighbours[k,:]
    1473 
    1474             #Get data
    1475             q0 = centroid_values[k0]
    1476             q1 = centroid_values[k1]
    1477             q2 = centroid_values[k2]
    1478 
    1479             x0, y0 = centroid_coordinates[k0] #V0 centroid
    1480             x1, y1 = centroid_coordinates[k1] #V1 centroid
    1481             x2, y2 = centroid_coordinates[k2] #V2 centroid
    1482 
    1483             #Gradient
    1484             a[k], b[k] = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
    1485 
    1486         elif number_of_boundaries[k] == 2:
    1487             #One true neighbour
    1488 
    1489             #Get index of the one neighbour
    1490             for k0 in surrogate_neighbours[k,:]:
    1491                 if k0 != k: break
    1492             assert k0 != k
    1493 
    1494             k1 = k  #self
    1495 
    1496             #Get data
    1497             q0 = centroid_values[k0]
    1498             q1 = centroid_values[k1]
    1499 
    1500             x0, y0 = centroid_coordinates[k0] #V0 centroid
    1501             x1, y1 = centroid_coordinates[k1] #V1 centroid
    1502 
    1503             #Gradient
    1504             a[k], b[k] = gradient2(x0, y0, x1, y1, q0, q1)
    1505         else:
    1506             #No true neighbours -
    1507             #Fall back to first order scheme
    1508             pass
    1509 
    1510 
    1511     return a, b
    1512 
    1513 
    1514 
    1515 def limit(quantity):
    1516     """Limit slopes for each volume to eliminate artificial variance
    1517     introduced by e.g. second order extrapolator
    1518 
    1519     This is an unsophisticated limiter as it does not take into
    1520     account dependencies among quantities.
    1521 
    1522     precondition:
    1523     vertex values are estimated from gradient
    1524     postcondition:
    1525     vertex values are updated
    1526     """
    1527 
    1528     from Numeric import zeros, Float
    1529 
    1530     N = quantity.domain.number_of_nodes
    1531 
    1532     beta_w = quantity.domain.beta_w
    1533 
    1534     qc = quantity.centroid_values
    1535     qv = quantity.vertex_values
    1536 
    1537     #Find min and max of this and neighbour's centroid values
    1538     qmax = zeros(qc.shape, Float)
    1539     qmin = zeros(qc.shape, Float)
    1540 
    1541     for k in range(N):
    1542         qmax[k] = qc[k]
    1543         qmin[k] = qc[k]
    1544         for i in range(3):
    1545             n = quantity.domain.neighbours[k,i]
    1546             if n >= 0:
    1547                 qn = qc[n] #Neighbour's centroid value
    1548 
    1549                 qmin[k] = min(qmin[k], qn)
    1550                 qmax[k] = max(qmax[k], qn)
    1551         qmax[k] = min(qmax[k], 2.0*qc[k])
    1552         qmin[k] = max(qmin[k], 0.5*qc[k])
    1553 
    1554 
    1555     #Diffences between centroids and maxima/minima
    1556     dqmax = qmax - qc
    1557     dqmin = qmin - qc
    1558 
    1559     #Deltas between vertex and centroid values
    1560     dq = zeros(qv.shape, Float)
    1561     for i in range(3):
    1562         dq[:,i] = qv[:,i] - qc
    1563 
    1564     #Phi limiter
    1565     for k in range(N):
    1566 
    1567         #Find the gradient limiter (phi) across vertices
    1568         phi = 1.0
    1569         for i in range(3):
    1570             r = 1.0
    1571             if (dq[k,i] > 0): r = dqmax[k]/dq[k,i]
    1572             if (dq[k,i] < 0): r = dqmin[k]/dq[k,i]
    1573 
    1574             phi = min( min(r*beta_w, 1), phi )
    1575 
    1576         #Then update using phi limiter
    1577         for i in range(3):
    1578             qv[k,i] = qc[k] + phi*dq[k,i]
    1579 
    1580 
    15811339
    15821340from anuga.utilities import compile
    1583 if compile.can_use_C_extension('quantity_ext.c'):
    1584     #Replace python version with c implementations
     1341if compile.can_use_C_extension('quantity_ext.c'):   
     1342    # Underlying C implementations can be accessed
    15851343
    15861344    from quantity_ext import average_vertex_values, backup_centroid_values, \
     
    15881346
    15891347    from quantity_ext import compute_gradients, limit,\
    1590     extrapolate_second_order, interpolate_from_vertices_to_edges, update
     1348    extrapolate_second_order, interpolate_from_vertices_to_edges, update   
     1349else:
     1350    msg = 'C implementations could not be accessed by %s.\n ' %__file__
     1351    msg += 'Make sure compile_all.py has been run as described in '
     1352    msg += 'the ANUGA installation guide.'
     1353    raise Exception, msg
     1354
     1355
     1356
  • anuga_core/source/anuga/abstract_2d_finite_volumes/quantity_ext.c

    r4728 r4769  
    288288// Gateways to Python
    289289PyObject *update(PyObject *self, PyObject *args) {
     290  // FIXME (Ole): It would be great to turn this text into a Python DOC string
     291
     292  /*"""Update centroid values based on values stored in
     293    explicit_update and semi_implicit_update as well as given timestep
     294
     295    Function implementing forcing terms must take on argument
     296    which is the domain and they must update either explicit
     297    or implicit updates, e,g,:
     298
     299    def gravity(domain):
     300        ....
     301        domain.quantities['xmomentum'].explicit_update = ...
     302        domain.quantities['ymomentum'].explicit_update = ...
     303
     304
     305
     306    Explicit terms must have the form
     307
     308        G(q, t)
     309
     310    and explicit scheme is
     311
     312       q^{(n+1}) = q^{(n)} + delta_t G(q^{n}, n delta_t)
     313
     314
     315    Semi implicit forcing terms are assumed to have the form
     316
     317       G(q, t) = H(q, t) q
     318
     319    and the semi implicit scheme will then be
     320
     321      q^{(n+1}) = q^{(n)} + delta_t H(q^{n}, n delta_t) q^{(n+1})
     322
     323  */
    290324
    291325        PyObject *quantity;
     
    397431
    398432PyObject *interpolate_from_vertices_to_edges(PyObject *self, PyObject *args) {
    399 
     433        //
     434        //Compute edge values from vertex values using linear interpolation
     435        //
     436       
    400437        PyObject *quantity;
    401438        PyArrayObject *vertex_values, *edge_values;
     
    476513
    477514PyObject *compute_gradients(PyObject *self, PyObject *args) {
     515  //"""Compute gradients of triangle surfaces defined by centroids of
     516  //neighbouring volumes.
     517  //If one edge is on the boundary, use own centroid as neighbour centroid.
     518  //If two or more are on the boundary, fall back to first order scheme.
     519  //"""
     520
    478521
    479522        PyObject *quantity, *domain, *R;
     
    650693
    651694PyObject *limit(PyObject *self, PyObject *args) {
     695  //Limit slopes for each volume to eliminate artificial variance
     696  //introduced by e.g. second order extrapolator
     697
     698  //This is an unsophisticated limiter as it does not take into
     699  //account dependencies among quantities.
     700
     701  //precondition:
     702  //  vertex values are estimated from gradient
     703  //postcondition:
     704  //  vertex values are updated
     705  //
    652706
    653707        PyObject *quantity, *domain, *Tmp;
  • anuga_core/source/anuga/shallow_water/shallow_water_domain.py

    r4754 r4769  
    7474"""
    7575
    76 #Subversion keywords:
     76# Subversion keywords:
    7777#
    78 #$LastChangedDate$
    79 #$LastChangedRevision$
    80 #$LastChangedBy$
     78# $LastChangedDate$
     79# $LastChangedRevision$
     80# $LastChangedBy$
    8181
    8282from Numeric import zeros, ones, Float, array, sum, size
     
    104104from anuga.config import optimise_dry_cells
    105105
    106 
    107 #Shallow water domain
     106#---------------------
     107# Shallow water domain
     108#---------------------
    108109class Domain(Generic_Domain):
    109110
     
    149150                                number_of_full_triangles=number_of_full_triangles)
    150151
    151         #self.minimum_allowed_height = minimum_allowed_height
    152         #self.H0 = minimum_allowed_height
     152
    153153        self.set_minimum_allowed_height(minimum_allowed_height)
    154154       
     
    167167        self.optimise_dry_cells = optimise_dry_cells
    168168
    169         self.flux_function = flux_function_central
    170         #self.flux_function = flux_function_kinetic
    171        
    172         self.forcing_terms.append(manning_friction)
     169        self.forcing_terms.append(manning_friction_implicit)
    173170        self.forcing_terms.append(gravity)
    174171
     
    385382
    386383    def distribute_to_vertices_and_edges(self):
    387         #Call correct module function
    388         #(either from this module or C-extension)
     384        # Call correct module function
     385        # (either from this module or C-extension)
    389386        distribute_to_vertices_and_edges(self)
    390387
    391 
    392     #FIXME: Under construction
    393 #     def set_defaults(self):
    394 #         """Set default values for uninitialised quantities.
    395 #         This is specific to the shallow water wave equation
    396 #         Defaults for 'elevation', 'friction', 'xmomentum' and 'ymomentum'
    397 #         are 0.0. Default for 'stage' is whatever the value of 'elevation'.
    398 #         """
    399 
    400 #         for name in self.other_quantities + self.conserved_quantities:
    401 #             print name
    402 #             print self.quantities.keys()
    403 #             if not self.quantities.has_key(name):
    404 #                 if name == 'stage':
    405 
    406 #                     if self.quantities.has_key('elevation'):
    407 #                         z = self.quantities['elevation'].vertex_values
    408 #                         self.set_quantity(name, z)
    409 #                     else:
    410 #                         self.set_quantity(name, 0.0)
    411 #                 else:
    412 #                     self.set_quantity(name, 0.0)
    413 
    414 
    415 
    416 #         #Lift negative heights up
    417 #         #z = self.quantities['elevation'].vertex_values
    418 #         #w = self.quantities['stage'].vertex_values
    419 
    420 #         #h = w-z
    421 
    422 #         #for k in range(h.shape[0]):
    423 #         #    for i in range(3):
    424 #         #        if h[k, i] < 0.0:
    425 #         #            w[k, i] = z[k, i]
    426 
    427 
    428 #         #self.quantities['stage'].interpolate()
    429388
    430389
     
    437396        """
    438397
    439         #Call check integrity here rather than from user scripts
    440         #self.check_integrity()
     398        # Call check integrity here rather than from user scripts
     399        # self.check_integrity()
    441400
    442401        msg = 'Parameter beta_h must be in the interval [0, 1['
     
    446405
    447406
    448         #Initial update of vertex and edge values before any STORAGE
    449         #and or visualisation
    450         #This is done again in the initialisation of the Generic_Domain
    451         #evolve loop but we do it here to ensure the values are ok for storage
     407        # Initial update of vertex and edge values before any STORAGE
     408        # and or visualisation
     409        # This is done again in the initialisation of the Generic_Domain
     410        # evolve loop but we do it here to ensure the values are ok for storage
    452411        self.distribute_to_vertices_and_edges()
    453412
    454413        if self.store is True and self.time == 0.0:
    455414            self.initialise_storage()
    456             #print 'Storing results in ' + self.writer.filename
     415            # print 'Storing results in ' + self.writer.filename
    457416        else:
    458417            pass
    459             #print 'Results will not be stored.'
    460             #print 'To store results set domain.store = True'
    461             #FIXME: Diagnostic output should be controlled by
     418            # print 'Results will not be stored.'
     419            # print 'To store results set domain.store = True'
     420            # FIXME: Diagnostic output should be controlled by
    462421            # a 'verbose' flag living in domain (or in a parent class)
    463422
    464         #Call basic machinery from parent class
     423        # Call basic machinery from parent class
    465424        for t in Generic_Domain.evolve(self,
    466425                                       yieldstep=yieldstep,
     
    469428                                       skip_initial_step=skip_initial_step):
    470429
    471             #Store model data, e.g. for subsequent visualisation
     430            # Store model data, e.g. for subsequent visualisation
    472431            if self.store is True:
    473432                self.store_timestep(self.quantities_to_be_stored)
    474433
    475             #FIXME: Could maybe be taken from specified list
    476             #of 'store every step' quantities
    477 
    478             #Pass control on to outer loop for more specific actions
     434            # FIXME: Could maybe be taken from specified list
     435            # of 'store every step' quantities
     436
     437            # Pass control on to outer loop for more specific actions
    479438            yield(t)
     439
    480440
    481441    def initialise_storage(self):
     
    486446        from anuga.shallow_water.data_manager import get_dataobject
    487447
    488         #Initialise writer
     448        # Initialise writer
    489449        self.writer = get_dataobject(self, mode = 'w')
    490450
    491         #Store vertices and connectivity
     451        # Store vertices and connectivity
    492452        self.writer.store_connectivity()
    493453
     
    502462
    503463
    504 #=============== End of Shallow Water Domain ===============================
    505 
    506 
    507 
    508 # Rotation of momentum vector
    509 def rotate(q, normal, direction = 1):
    510     """Rotate the momentum component q (q[1], q[2])
    511     from x,y coordinates to coordinates based on normal vector.
    512 
    513     If direction is negative the rotation is inverted.
    514 
    515     Input vector is preserved
    516 
    517     This function is specific to the shallow water wave equation
    518     """
    519 
    520     assert len(q) == 3,\
    521            'Vector of conserved quantities must have length 3'\
    522            'for 2D shallow water equation'
    523 
    524     try:
    525         l = len(normal)
    526     except:
    527         raise 'Normal vector must be an Numeric array'
    528 
    529     assert l == 2, 'Normal vector must have 2 components'
    530 
    531 
    532     n1 = normal[0]
    533     n2 = normal[1]
    534 
    535     r = zeros(len(q), Float) #Rotated quantities
    536     r[0] = q[0]              #First quantity, height, is not rotated
    537 
    538     if direction == -1:
    539         n2 = -n2
    540 
    541 
    542     r[1] =  n1*q[1] + n2*q[2]
    543     r[2] = -n2*q[1] + n1*q[2]
    544 
    545     return r
    546 
    547 
    548 
    549 ####################################
     464#=============== End of class Shallow Water Domain ===============================
     465
     466
     467#-----------------
    550468# Flux computation
    551 # FIXME (Ole): Delete Python versions of code that is
    552 # always used in C-extensions - they get out of sync anyway
    553 def flux_function_central(normal, ql, qr, zl, zr):
    554     """Compute fluxes between volumes for the shallow water wave equation
    555     cast in terms of w = h+z using the 'central scheme' as described in
    556 
    557     Kurganov, Noelle, Petrova. 'Semidiscrete Central-Upwind Schemes For
    558     Hyperbolic Conservation Laws and Hamilton-Jacobi Equations'.
    559     Siam J. Sci. Comput. Vol. 23, No. 3, pp. 707-740.
    560 
    561     The implemented formula is given in equation (3.15) on page 714
    562 
    563     Conserved quantities w, uh, vh are stored as elements 0, 1 and 2
    564     in the numerical vectors ql and qr.
    565 
    566     Bed elevations zl and zr.
    567     """
    568 
    569     from math import sqrt
    570 
    571     #Align momentums with x-axis
    572     q_left  = rotate(ql, normal, direction = 1)
    573     q_right = rotate(qr, normal, direction = 1)
    574 
    575     z = (zl+zr)/2 #Take average of field values
    576 
    577     w_left  = q_left[0]   #w=h+z
    578     h_left  = w_left-z
    579     uh_left = q_left[1]
    580 
    581     if h_left < epsilon:
    582         u_left = 0.0  #Could have been negative
    583         h_left = 0.0
    584     else:
    585         u_left  = uh_left/h_left
    586 
    587 
    588     w_right  = q_right[0]  #w=h+z
    589     h_right  = w_right-z
    590     uh_right = q_right[1]
    591 
    592 
    593     if h_right < epsilon:
    594         u_right = 0.0  #Could have been negative
    595         h_right = 0.0
    596     else:
    597         u_right  = uh_right/h_right
    598 
    599     vh_left  = q_left[2]
    600     vh_right = q_right[2]
    601 
    602     soundspeed_left  = sqrt(g*h_left)
    603     soundspeed_right = sqrt(g*h_right)
    604 
    605     #Maximal wave speed
    606     s_max = max(u_left+soundspeed_left, u_right+soundspeed_right, 0)
    607 
    608     #Minimal wave speed
    609     s_min = min(u_left-soundspeed_left, u_right-soundspeed_right, 0)
    610 
    611     #Flux computation
    612 
    613     #FIXME(Ole): Why is it again that we don't
    614     #use uh_left and uh_right directly in the first entries?
    615     flux_left  = array([u_left*h_left,
    616                         u_left*uh_left + 0.5*g*h_left**2,
    617                         u_left*vh_left])
    618     flux_right = array([u_right*h_right,
    619                         u_right*uh_right + 0.5*g*h_right**2,
    620                         u_right*vh_right])
    621 
    622     denom = s_max-s_min
    623     if denom == 0.0:
    624         edgeflux = array([0.0, 0.0, 0.0])
    625         max_speed = 0.0
    626     else:
    627         edgeflux = (s_max*flux_left - s_min*flux_right)/denom
    628         edgeflux += s_max*s_min*(q_right-q_left)/denom
    629 
    630         edgeflux = rotate(edgeflux, normal, direction=-1)
    631         max_speed = max(abs(s_max), abs(s_min))
    632 
    633     return edgeflux, max_speed
    634 
    635 def erfcc(x):
    636 
    637     from math import fabs, exp
    638 
    639     z=fabs(x)
    640     t=1.0/(1.0+0.5*z)
    641     result=t*exp(-z*z-1.26551223+t*(1.00002368+t*(.37409196+
    642          t*(.09678418+t*(-.18628806+t*(.27886807+t*(-1.13520398+
    643          t*(1.48851587+t*(-.82215223+t*.17087277)))))))))
    644     if x < 0.0:
    645         result = 2.0-result
    646 
    647     return result
    648 
    649 def flux_function_kinetic(normal, ql, qr, zl, zr):
    650     """Compute fluxes between volumes for the shallow water wave equation
    651     cast in terms of w = h+z using the 'central scheme' as described in
    652 
    653     Zhang et. al., Advances in Water Resources, 26(6), 2003, 635-647.
    654 
    655 
    656     Conserved quantities w, uh, vh are stored as elements 0, 1 and 2
    657     in the numerical vectors ql an qr.
    658 
    659     Bed elevations zl and zr.
    660     """
    661 
    662     from math import sqrt
    663     from Numeric import array
    664 
    665     #Align momentums with x-axis
    666     q_left  = rotate(ql, normal, direction = 1)
    667     q_right = rotate(qr, normal, direction = 1)
    668 
    669     z = (zl+zr)/2 #Take average of field values
    670 
    671     w_left  = q_left[0]   #w=h+z
    672     h_left  = w_left-z
    673     uh_left = q_left[1]
    674 
    675     if h_left < epsilon:
    676         u_left = 0.0  #Could have been negative
    677         h_left = 0.0
    678     else:
    679         u_left  = uh_left/h_left
    680 
    681 
    682     w_right  = q_right[0]  #w=h+z
    683     h_right  = w_right-z
    684     uh_right = q_right[1]
    685 
    686 
    687     if h_right < epsilon:
    688         u_right = 0.0  #Could have been negative
    689         h_right = 0.0
    690     else:
    691         u_right  = uh_right/h_right
    692 
    693     vh_left  = q_left[2]
    694     vh_right = q_right[2]
    695 
    696     soundspeed_left  = sqrt(g*h_left)
    697     soundspeed_right = sqrt(g*h_right)
    698 
    699     #Maximal wave speed
    700     s_max = max(u_left+soundspeed_left, u_right+soundspeed_right, 0)
    701 
    702     #Minimal wave speed
    703     s_min = min(u_left-soundspeed_left, u_right-soundspeed_right, 0)
    704 
    705     #Flux computation
    706 
    707     F_left  = 0.0
    708     F_right = 0.0
    709     from math import sqrt, pi, exp
    710     if h_left > 0.0:
    711         F_left = u_left/sqrt(g*h_left)
    712     if h_right > 0.0:
    713         F_right = u_right/sqrt(g*h_right)
    714 
    715     edgeflux = array([0.0, 0.0, 0.0])
    716 
    717     edgeflux[0] = h_left*u_left/2.0*erfcc(-F_left) +  \
    718           h_left*sqrt(g*h_left)/2.0/sqrt(pi)*exp(-(F_left**2)) + \
    719           h_right*u_right/2.0*erfcc(F_right) -  \
    720           h_right*sqrt(g*h_right)/2.0/sqrt(pi)*exp(-(F_right**2))
    721 
    722     edgeflux[1] = (h_left*u_left**2 + g/2.0*h_left**2)/2.0*erfcc(-F_left) + \
    723           u_left*h_left*sqrt(g*h_left)/2.0/sqrt(pi)*exp(-(F_left**2)) + \
    724           (h_right*u_right**2 + g/2.0*h_right**2)/2.0*erfcc(F_right) -  \
    725           u_right*h_right*sqrt(g*h_right)/2.0/sqrt(pi)*exp(-(F_right**2))
    726 
    727     edgeflux[2] = vh_left*u_left/2.0*erfcc(-F_left) + \
    728           vh_left*sqrt(g*h_left)/2.0/sqrt(pi)*exp(-(F_left**2)) + \
    729           vh_right*u_right/2.0*erfcc(F_right) - \
    730           vh_right*sqrt(g*h_right)/2.0/sqrt(pi)*exp(-(F_right**2))
    731 
    732 
    733     edgeflux = rotate(edgeflux, normal, direction=-1)
    734     max_speed = max(abs(s_max), abs(s_min))
    735 
    736     return edgeflux, max_speed
    737 
    738 
     469#-----------------
    739470
    740471def compute_fluxes(domain):
     
    756487      domain.explicit_update is reset to computed flux values
    757488      domain.timestep is set to the largest step satisfying all volumes.
     489   
     490
     491    This wrapper calls the underlying C version of compute fluxes
    758492    """
    759493
     
    762496    N = len(domain) # number_of_triangles
    763497
    764     #Shortcuts
     498    # Shortcuts
    765499    Stage = domain.quantities['stage']
    766500    Xmom = domain.quantities['xmomentum']
     
    768502    Bed = domain.quantities['elevation']
    769503
    770     #Arrays
    771     stage = Stage.edge_values
    772     xmom =  Xmom.edge_values
    773     ymom =  Ymom.edge_values
    774     bed =   Bed.edge_values
    775 
    776     stage_bdry = Stage.boundary_values
    777     xmom_bdry =  Xmom.boundary_values
    778     ymom_bdry =  Ymom.boundary_values
    779 
    780     flux = zeros(3, Float) #Work array for summing up fluxes
    781 
    782 
    783     #Loop
    784504    timestep = float(sys.maxint)
    785     for k in range(N):
    786 
    787         flux[:] = 0.  #Reset work array
    788         for i in range(3):
    789             #Quantities inside volume facing neighbour i
    790             ql = [stage[k, i], xmom[k, i], ymom[k, i]]
    791             zl = bed[k, i]
    792 
    793             #Quantities at neighbour on nearest face
    794             n = domain.neighbours[k,i]
    795             if n < 0:
    796                 m = -n-1 #Convert negative flag to index
    797                 qr = [stage_bdry[m], xmom_bdry[m], ymom_bdry[m]]
    798                 zr = zl #Extend bed elevation to boundary
    799             else:
    800                 m = domain.neighbour_edges[k,i]
    801                 qr = [stage[n, m], xmom[n, m], ymom[n, m]]
    802                 zr = bed[n, m]
    803 
    804 
    805             #Outward pointing normal vector
    806             normal = domain.normals[k, 2*i:2*i+2]
    807 
    808             #Flux computation using provided function
    809             edgeflux, max_speed = domain.flux_function(normal, ql, qr, zl, zr)
    810             flux -= edgeflux * domain.edgelengths[k,i]
    811 
    812             #Update optimal_timestep on full cells
    813             if  domain.tri_full_flag[k] == 1:
    814                 try:
    815                     timestep = min(timestep, 0.5*domain.radii[k]/max_speed)
    816                 except ZeroDivisionError:
    817                     pass
    818 
    819         #Normalise by area and store for when all conserved
    820         #quantities get updated
    821         flux /= domain.areas[k]
    822         Stage.explicit_update[k] = flux[0]
    823         Xmom.explicit_update[k] = flux[1]
    824         Ymom.explicit_update[k] = flux[2]
    825         domain.max_speed[k] = max_speed
    826 
    827 
    828     domain.flux_timestep = timestep
    829 
    830 #MH090605 The following method belongs to the shallow_water domain class
    831 #see comments in the corresponding method in shallow_water_ext.c
    832 def extrapolate_second_order_sw_c(domain):
     505    from shallow_water_ext import\
     506         compute_fluxes_ext_central as compute_fluxes_ext
     507
     508
     509    flux_timestep = compute_fluxes_ext(timestep,
     510                                       domain.epsilon,
     511                                       domain.H0,
     512                                       domain.g,
     513                                       domain.neighbours,
     514                                       domain.neighbour_edges,
     515                                       domain.normals,
     516                                       domain.edgelengths,
     517                                       domain.radii,
     518                                       domain.areas,
     519                                       domain.tri_full_flag,
     520                                       Stage.edge_values,
     521                                       Xmom.edge_values,
     522                                       Ymom.edge_values,
     523                                       Bed.edge_values,
     524                                       Stage.boundary_values,
     525                                       Xmom.boundary_values,
     526                                       Ymom.boundary_values,
     527                                       Stage.explicit_update,
     528                                       Xmom.explicit_update,
     529                                       Ymom.explicit_update,
     530                                       domain.already_computed_flux,
     531                                       domain.max_speed,
     532                                       int(domain.optimise_dry_cells))
     533
     534    domain.flux_timestep = flux_timestep
     535
     536
     537
     538#---------------------------------------
     539# Module functions for gradient limiting
     540#---------------------------------------
     541
     542
     543# MH090605 The following method belongs to the shallow_water domain class
     544# see comments in the corresponding method in shallow_water_ext.c
     545def extrapolate_second_order_sw(domain):
    833546    """Wrapper calling C version of extrapolate_second_order_sw
    834547    """
     
    843556    Elevation = domain.quantities['elevation']
    844557
    845     from shallow_water_ext import extrapolate_second_order_sw
    846     extrapolate_second_order_sw(domain,
    847                                 domain.surrogate_neighbours,
    848                                 domain.number_of_boundaries,
    849                                 domain.centroid_coordinates,
    850                                 Stage.centroid_values,
    851                                 Xmom.centroid_values,
    852                                 Ymom.centroid_values,
    853                                 Elevation.centroid_values,
    854                                 domain.vertex_coordinates,
    855                                 Stage.vertex_values,
    856                                 Xmom.vertex_values,
    857                                 Ymom.vertex_values,
    858                                 Elevation.vertex_values,
    859                                 int(domain.optimise_dry_cells))
    860 
    861 def compute_fluxes_c(domain):
    862     """Wrapper calling C version of compute fluxes
    863     """
    864 
    865     import sys
    866 
    867     N = len(domain) # number_of_triangles
    868 
    869     #Shortcuts
    870     Stage = domain.quantities['stage']
    871     Xmom = domain.quantities['xmomentum']
    872     Ymom = domain.quantities['ymomentum']
    873     Bed = domain.quantities['elevation']
    874 
    875     timestep = float(sys.maxint)
    876     from shallow_water_ext import\
    877          compute_fluxes_ext_central as compute_fluxes_ext
    878 
    879     domain.flux_timestep = compute_fluxes_ext(timestep, domain.epsilon,
    880                                          domain.H0,
    881                                          domain.g,
    882                                          domain.neighbours,
    883                                          domain.neighbour_edges,
    884                                          domain.normals,
    885                                          domain.edgelengths,
    886                                          domain.radii,
    887                                          domain.areas,
    888                                          domain.tri_full_flag,
    889                                          Stage.edge_values,
    890                                          Xmom.edge_values,
    891                                          Ymom.edge_values,
    892                                          Bed.edge_values,
    893                                          Stage.boundary_values,
    894                                          Xmom.boundary_values,
    895                                          Ymom.boundary_values,
    896                                          Stage.explicit_update,
    897                                          Xmom.explicit_update,
    898                                          Ymom.explicit_update,
    899                                          domain.already_computed_flux,
    900                                          domain.max_speed,
    901                                          int(domain.optimise_dry_cells))
    902 
    903 
    904 ####################################
    905 # Module functions for gradient limiting (distribute_to_vertices_and_edges)
     558    from shallow_water_ext import extrapolate_second_order_sw as extrapol2
     559    extrapol2(domain,
     560              domain.surrogate_neighbours,
     561              domain.number_of_boundaries,
     562              domain.centroid_coordinates,
     563              Stage.centroid_values,
     564              Xmom.centroid_values,
     565              Ymom.centroid_values,
     566              Elevation.centroid_values,
     567              domain.vertex_coordinates,
     568              Stage.vertex_values,
     569              Xmom.vertex_values,
     570              Ymom.vertex_values,
     571              Elevation.vertex_values,
     572              int(domain.optimise_dry_cells))
     573
    906574
    907575def distribute_to_vertices_and_edges(domain):
     
    930598    from anuga.config import optimised_gradient_limiter
    931599
    932     #Remove very thin layers of water
     600    # Remove very thin layers of water
    933601    protect_against_infinitesimal_and_negative_heights(domain)
    934602
    935     #Extrapolate all conserved quantities
     603    # Extrapolate all conserved quantities
    936604    if optimised_gradient_limiter:
    937         #MH090605 if second order,
    938         #perform the extrapolation and limiting on
    939         #all of the conserved quantities
     605        # MH090605 if second order,
     606        # perform the extrapolation and limiting on
     607        # all of the conserved quantities
    940608
    941609        if (domain._order_ == 1):
     
    948616            raise 'Unknown order'
    949617    else:
    950         #old code:
     618        # Old code:
    951619        for name in domain.conserved_quantities:
    952620            Q = domain.quantities[name]
     
    986654    """
    987655
    988     #Shortcuts
     656    # Shortcuts
    989657    wc = domain.quantities['stage'].centroid_values
    990658    zc = domain.quantities['elevation'].centroid_values
    991659    xmomc = domain.quantities['xmomentum'].centroid_values
    992660    ymomc = domain.quantities['ymomentum'].centroid_values
    993     hc = wc - zc  #Water depths at centroids
    994 
    995     #Update
    996     #FIXME: Modify according to c-version - or discard altogether.
    997     for k in range(len(domain)):
    998 
    999         if hc[k] < domain.minimum_allowed_height:
    1000             #Control stage
    1001             if hc[k] < domain.epsilon:
    1002                 wc[k] = zc[k] # Contain 'lost mass' error
    1003 
    1004             #Control momentum
    1005             xmomc[k] = ymomc[k] = 0.0
    1006 
    1007 
    1008 def protect_against_infinitesimal_and_negative_heights_c(domain):
    1009     """Protect against infinitesimal heights and associated high velocities
    1010     """
    1011 
    1012     #Shortcuts
    1013     wc = domain.quantities['stage'].centroid_values
    1014     zc = domain.quantities['elevation'].centroid_values
    1015     xmomc = domain.quantities['xmomentum'].centroid_values
    1016     ymomc = domain.quantities['ymomentum'].centroid_values
    1017661
    1018662    from shallow_water_ext import protect
     
    1020664    protect(domain.minimum_allowed_height, domain.maximum_allowed_speed,
    1021665            domain.epsilon, wc, zc, xmomc, ymomc)
    1022 
    1023666
    1024667
     
    1031674    This limiter depends on two quantities (w,z) so it resides within
    1032675    this module rather than within quantity.py
    1033     """
    1034 
    1035     N = len(domain)
     676
     677    Wrapper for c-extension
     678    """
     679
     680    N = len(domain) # number_of_triangles
    1036681    beta_h = domain.beta_h
    1037682
    1038     #Shortcuts
     683    # Shortcuts
    1039684    wc = domain.quantities['stage'].centroid_values
    1040685    zc = domain.quantities['elevation'].centroid_values
     
    1043688    wv = domain.quantities['stage'].vertex_values
    1044689    zv = domain.quantities['elevation'].vertex_values
    1045     hv = wv-zv
    1046 
    1047     hvbar = zeros(hv.shape, Float) #h-limited values
    1048 
    1049     #Find min and max of this and neighbour's centroid values
    1050     hmax = zeros(hc.shape, Float)
    1051     hmin = zeros(hc.shape, Float)
    1052 
    1053     for k in range(N):
    1054         hmax[k] = hmin[k] = hc[k]
    1055         for i in range(3):
    1056             n = domain.neighbours[k,i]
    1057             if n >= 0:
    1058                 hn = hc[n] #Neighbour's centroid value
    1059 
    1060                 hmin[k] = min(hmin[k], hn)
    1061                 hmax[k] = max(hmax[k], hn)
    1062 
    1063 
    1064     #Diffences between centroids and maxima/minima
    1065     dhmax = hmax - hc
    1066     dhmin = hmin - hc
    1067 
    1068     #Deltas between vertex and centroid values
    1069     dh = zeros(hv.shape, Float)
    1070     for i in range(3):
    1071         dh[:,i] = hv[:,i] - hc
    1072 
    1073     #Phi limiter
    1074     for k in range(N):
    1075 
    1076         #Find the gradient limiter (phi) across vertices
    1077         phi = 1.0
    1078         for i in range(3):
    1079             r = 1.0
    1080             if (dh[k,i] > 0): r = dhmax[k]/dh[k,i]
    1081             if (dh[k,i] < 0): r = dhmin[k]/dh[k,i]
    1082 
    1083             phi = min( min(r*beta_h, 1), phi )
    1084 
    1085         #Then update using phi limiter
    1086         for i in range(3):
    1087             hvbar[k,i] = hc[k] + phi*dh[k,i]
    1088 
    1089     return hvbar
    1090 
    1091 
    1092 
    1093 def h_limiter_c(domain):
    1094     """Limit slopes for each volume to eliminate artificial variance
    1095     introduced by e.g. second order extrapolator
    1096 
    1097     limit on h = w-z
    1098 
    1099     This limiter depends on two quantities (w,z) so it resides within
    1100     this module rather than within quantity.py
    1101 
    1102     Wrapper for c-extension
    1103     """
    1104 
    1105     N = len(domain) # number_of_triangles
    1106     beta_h = domain.beta_h
    1107 
    1108     #Shortcuts
    1109     wc = domain.quantities['stage'].centroid_values
    1110     zc = domain.quantities['elevation'].centroid_values
    1111     hc = wc - zc
    1112 
    1113     wv = domain.quantities['stage'].vertex_values
    1114     zv = domain.quantities['elevation'].vertex_values
    1115690    hv = wv - zv
    1116691
    1117692    #Call C-extension
    1118     from shallow_water_ext import h_limiter_sw as h_limiter
    1119     hvbar = h_limiter(domain, hc, hv)
     693    from shallow_water_ext import h_limiter_sw
     694    hvbar = h_limiter_sw(domain, hc, hv)
    1120695
    1121696    return hvbar
     
    1133708    modes.
    1134709
    1135     The h-limiter is always applied irrespective of the order.
    1136     """
    1137 
    1138     #Shortcuts
    1139     wc = domain.quantities['stage'].centroid_values
    1140     zc = domain.quantities['elevation'].centroid_values
    1141     hc = wc - zc
    1142 
    1143     wv = domain.quantities['stage'].vertex_values
    1144     zv = domain.quantities['elevation'].vertex_values
    1145     hv = wv-zv
    1146 
    1147     #Limit h
    1148     hvbar = h_limiter(domain)
    1149 
    1150     for k in range(len(domain)):
    1151         #Compute maximal variation in bed elevation
    1152         #  This quantitiy is
    1153         #    dz = max_i abs(z_i - z_c)
    1154         #  and it is independent of dimension
    1155         #  In the 1d case zc = (z0+z1)/2
    1156         #  In the 2d case zc = (z0+z1+z2)/3
    1157 
    1158         dz = max(abs(zv[k,0]-zc[k]),
    1159                  abs(zv[k,1]-zc[k]),
    1160                  abs(zv[k,2]-zc[k]))
    1161 
    1162 
    1163         hmin = min( hv[k,:] )
    1164 
    1165         #Create alpha in [0,1], where alpha==0 means using the h-limited
    1166         #stage and alpha==1 means using the w-limited stage as
    1167         #computed by the gradient limiter (both 1st or 2nd order)
    1168 
    1169         #If hmin > dz/2 then alpha = 1 and the bed will have no effect
    1170         #If hmin < 0 then alpha = 0 reverting to constant height above bed.
    1171 
    1172         if dz > 0.0:
    1173             alpha = max( min( 2*hmin/dz, 1.0), 0.0 )
    1174         else:
    1175             #Flat bed
    1176             alpha = 1.0
    1177 
    1178         #Let
    1179         #
    1180         #  wvi be the w-limited stage (wvi = zvi + hvi)
    1181         #  wvi- be the h-limited state (wvi- = zvi + hvi-)
    1182         #
    1183         #
    1184         #where i=0,1,2 denotes the vertex ids
    1185         #
    1186         #Weighted balance between w-limited and h-limited stage is
    1187         #
    1188         #  wvi := (1-alpha)*(zvi+hvi-) + alpha*(zvi+hvi)
    1189         #
    1190         #It follows that the updated wvi is
    1191         #  wvi := zvi + (1-alpha)*hvi- + alpha*hvi
    1192         #
    1193         # Momentum is balanced between constant and limited
    1194 
    1195 
    1196         #for i in range(3):
    1197         #    wv[k,i] = zv[k,i] + hvbar[k,i]
    1198 
    1199         #return
    1200 
    1201         if alpha < 1:
    1202 
    1203             for i in range(3):
    1204                 wv[k,i] = zv[k,i] + (1-alpha)*hvbar[k,i] + alpha*hv[k,i]
    1205 
    1206             #Momentums at centroids
    1207             xmomc = domain.quantities['xmomentum'].centroid_values
    1208             ymomc = domain.quantities['ymomentum'].centroid_values
    1209 
    1210             #Momentums at vertices
    1211             xmomv = domain.quantities['xmomentum'].vertex_values
    1212             ymomv = domain.quantities['ymomentum'].vertex_values
    1213 
    1214             # Update momentum as a linear combination of
    1215             # xmomc and ymomc (shallow) and momentum
    1216             # from extrapolator xmomv and ymomv (deep).
    1217             xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:]
    1218             ymomv[k,:] = (1-alpha)*ymomc[k] + alpha*ymomv[k,:]
    1219 
    1220 
    1221 def balance_deep_and_shallow_c(domain):
    1222     """Wrapper for C implementation
     710    Wrapper for C implementation
    1223711    """
    1224712
     
    1229717    # Omit updating xmomv
    1230718    #
    1231     from shallow_water_ext import balance_deep_and_shallow
     719    from shallow_water_ext import balance_deep_and_shallow as balance_deep_and_shallow_c
    1232720   
    1233721    # Shortcuts
    1234722    wc = domain.quantities['stage'].centroid_values
    1235723    zc = domain.quantities['elevation'].centroid_values
    1236     #hc = wc - zc
    1237724
    1238725    wv = domain.quantities['stage'].vertex_values
    1239726    zv = domain.quantities['elevation'].vertex_values
    1240     #hv = wv - zv
    1241727
    1242728    # Momentums at centroids
     
    1248734    ymomv = domain.quantities['ymomentum'].vertex_values
    1249735
    1250     #Limit h
     736    # Limit h
    1251737    if domain.beta_h > 0:
    1252738        hvbar = h_limiter(domain)
    1253739       
    1254         balance_deep_and_shallow(domain, domain.beta_h,
    1255                                  wc, zc, wv, zv, hvbar,
    1256                                  xmomc, ymomc, xmomv, ymomv)       
     740        balance_deep_and_shallow_c(domain, domain.beta_h,
     741                                   wc, zc, wv, zv, hvbar,
     742                                   xmomc, ymomc, xmomv, ymomv)       
    1257743    else:
    1258744        # print 'Using first order h-limiter'
    1259745        # FIXME: Pass wc in for now - it will be ignored.
    1260746       
    1261         #This is how one would make a first order h_limited value
    1262         #as in the old balancer (pre 17 Feb 2005):
    1263         # If we wish to hard wire this, one should modify the C-code
    1264         #from Numeric import zeros, Float
    1265         #hvbar = zeros( (len(wc), 3), Float)
    1266         #for i in range(3):
    1267         #    hvbar[:,i] = wc[:] - zc[:]
    1268 
    1269         balance_deep_and_shallow(domain, domain.beta_h,
    1270                                  wc, zc, wv, zv, wc,
    1271                                  xmomc, ymomc, xmomv, ymomv)
    1272 
    1273 
    1274 
    1275 
    1276 
    1277 ###############################################
    1278 #Boundaries - specific to the shallow water wave equation
     747        # This is how one would make a first order h_limited value
     748        # as in the old balancer (pre 17 Feb 2005):
     749        #  If we wish to hard wire this, one should modify the C-code
     750        # from Numeric import zeros, Float
     751        # hvbar = zeros( (len(wc), 3), Float)
     752        # for i in range(3):
     753        #     hvbar[:,i] = wc[:] - zc[:]
     754
     755        balance_deep_and_shallow_c(domain, domain.beta_h,
     756                                   wc, zc, wv, zv, wc,
     757                                   xmomc, ymomc, xmomv, ymomv)
     758
     759
     760
     761
     762#------------------------------------------------------------------
     763# Boundary conditions - specific to the shallow water wave equation
     764#------------------------------------------------------------------
    1279765class Reflective_boundary(Boundary):
    1280766    """Reflective boundary returns same conserved quantities as
     
    1293779            raise msg
    1294780
    1295         #Handy shorthands
     781        # Handy shorthands
    1296782        self.stage   = domain.quantities['stage'].edge_values
    1297783        self.xmom    = domain.quantities['xmomentum'].edge_values
     
    1377863
    1378864
    1379         #FIXME: Consider this (taken from File_boundary) to allow
    1380         #spatial variation
    1381         #if vol_id is not None and edge_id is not None:
    1382         #    i = self.boundary_indices[ vol_id, edge_id ]
    1383         #    return self.F(t, point_id = i)
    1384         #else:
    1385         #    return self.F(t)
     865        # FIXME: Consider this (taken from File_boundary) to allow
     866        # spatial variation
     867        # if vol_id is not None and edge_id is not None:
     868        #     i = self.boundary_indices[ vol_id, edge_id ]
     869        #     return self.F(t, point_id = i)
     870        # else:
     871        #     return self.F(t)
    1386872
    1387873
     
    1424910
    1425911
    1426         #FIXME: Consider this (taken from File_boundary) to allow
    1427         #spatial variation
    1428         #if vol_id is not None and edge_id is not None:
    1429         #    i = self.boundary_indices[ vol_id, edge_id ]
    1430         #    return self.F(t, point_id = i)
    1431         #else:
    1432         #    return self.F(t)
     912        # FIXME: Consider this (taken from File_boundary) to allow
     913        # spatial variation
     914        # if vol_id is not None and edge_id is not None:
     915        #     i = self.boundary_indices[ vol_id, edge_id ]
     916        #     return self.F(t, point_id = i)
     917        # else:
     918        #     return self.F(t)
    1433919
    1434920
     
    1511997   
    1512998
    1513 #########################
    1514 #Standard forcing terms:
    1515 #
     999#-----------------------
     1000# Standard forcing terms
     1001#-----------------------
     1002
    15161003def gravity(domain):
    15171004    """Apply gravitational pull in the presence of bed slope
     1005    Wrapper calls underlying C implementation
    15181006    """
    15191007
     
    15211009    ymom = domain.quantities['ymomentum'].explicit_update
    15221010
    1523     Stage = domain.quantities['stage']
    1524     Elevation = domain.quantities['elevation']
    1525     h = Stage.edge_values - Elevation.edge_values
    1526     v = Elevation.vertex_values
     1011    stage = domain.quantities['stage']
     1012    elevation = domain.quantities['elevation']
     1013
     1014    h = stage.centroid_values - elevation.centroid_values
     1015    z = elevation.vertex_values
    15271016
    15281017    x = domain.get_vertex_coordinates()
    15291018    g = domain.g
    1530 
    1531     for k in range(len(domain)):
    1532         avg_h = sum( h[k,:] )/3
    1533 
    1534         #Compute bed slope
    1535         x0, y0, x1, y1, x2, y2 = x[k,:]
    1536         z0, z1, z2 = v[k,:]
    1537 
    1538         zx, zy = gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2)
    1539 
    1540         #Update momentum
    1541         xmom[k] += -g*zx*avg_h
    1542         ymom[k] += -g*zy*avg_h
    1543 
    1544 
    1545 def gravity_c(domain):
    1546     """Wrapper calling C version
    1547     """
    1548 
    1549     xmom = domain.quantities['xmomentum'].explicit_update
    1550     ymom = domain.quantities['ymomentum'].explicit_update
    1551 
    1552     stage = domain.quantities['stage']
    1553     elevation = domain.quantities['elevation']
    1554 
    1555     h = stage.centroid_values - elevation.centroid_values
    1556     z = elevation.vertex_values
    1557 
    1558     x = domain.get_vertex_coordinates()
    1559     g = domain.g
    1560    
    1561 
    1562     from shallow_water_ext import gravity
    1563     gravity(g, h, z, x, xmom, ymom) #, 1.0e-6)
    1564 
    1565 
    1566 
    1567 def manning_friction(domain):
    1568     """Apply (Manning) friction to water momentum
    1569     (Python version)
    1570     """
    1571 
    1572     from math import sqrt
     1019   
     1020
     1021    from shallow_water_ext import gravity as gravity_c
     1022    gravity_c(g, h, z, x, xmom, ymom) #, 1.0e-6)
     1023
     1024
     1025
     1026def manning_friction_implicit(domain):
     1027    """Apply (Manning) friction to water momentum   
     1028    Wrapper for c version
     1029    """
     1030
     1031
     1032    #print 'Implicit friction'
     1033
     1034    xmom = domain.quantities['xmomentum']
     1035    ymom = domain.quantities['ymomentum']
    15731036
    15741037    w = domain.quantities['stage'].centroid_values
    15751038    z = domain.quantities['elevation'].centroid_values
    1576     h = w-z
    1577 
    1578     uh = domain.quantities['xmomentum'].centroid_values
    1579     vh = domain.quantities['ymomentum'].centroid_values
     1039
     1040    uh = xmom.centroid_values
     1041    vh = ymom.centroid_values
    15801042    eta = domain.quantities['friction'].centroid_values
    15811043
    1582     xmom_update = domain.quantities['xmomentum'].semi_implicit_update
    1583     ymom_update = domain.quantities['ymomentum'].semi_implicit_update
     1044    xmom_update = xmom.semi_implicit_update
     1045    ymom_update = ymom.semi_implicit_update
    15841046
    15851047    N = len(domain)
     
    15871049    g = domain.g
    15881050
    1589     for k in range(N):
    1590         if eta[k] >= eps:
    1591             if h[k] >= eps:
    1592                 S = -g * eta[k]**2 * sqrt((uh[k]**2 + vh[k]**2))
    1593                 S /= h[k]**(7.0/3)
    1594 
    1595                 #Update momentum
    1596                 xmom_update[k] += S*uh[k]
    1597                 ymom_update[k] += S*vh[k]
    1598 
    1599 
    1600 def manning_friction_implicit_c(domain):
    1601     """Wrapper for c version
    1602     """
    1603 
    1604 
    1605     #print 'Implicit friction'
     1051    from shallow_water_ext import manning_friction as manning_friction_c
     1052    manning_friction_c(g, eps, w, z, uh, vh, eta, xmom_update, ymom_update)
     1053
     1054
     1055def manning_friction_explicit(domain):
     1056    """Apply (Manning) friction to water momentum   
     1057    Wrapper for c version
     1058    """
     1059
     1060    # print 'Explicit friction'
    16061061
    16071062    xmom = domain.quantities['xmomentum']
     
    16151070    eta = domain.quantities['friction'].centroid_values
    16161071
    1617     xmom_update = xmom.semi_implicit_update
    1618     ymom_update = ymom.semi_implicit_update
     1072    xmom_update = xmom.explicit_update
     1073    ymom_update = ymom.explicit_update
    16191074
    16201075    N = len(domain)
     
    16221077    g = domain.g
    16231078
    1624     from shallow_water_ext import manning_friction
    1625     manning_friction(g, eps, w, z, uh, vh, eta, xmom_update, ymom_update)
    1626 
    1627 
    1628 def manning_friction_explicit_c(domain):
    1629     """Wrapper for c version
    1630     """
    1631 
    1632     #print 'Explicit friction'
    1633 
    1634     xmom = domain.quantities['xmomentum']
    1635     ymom = domain.quantities['ymomentum']
    1636 
    1637     w = domain.quantities['stage'].centroid_values
    1638     z = domain.quantities['elevation'].centroid_values
    1639 
    1640     uh = xmom.centroid_values
    1641     vh = ymom.centroid_values
    1642     eta = domain.quantities['friction'].centroid_values
    1643 
    1644     xmom_update = xmom.explicit_update
    1645     ymom_update = ymom.explicit_update
    1646 
    1647     N = len(domain)
    1648     eps = domain.minimum_allowed_height
    1649     g = domain.g
    1650 
    1651     from shallow_water_ext import manning_friction
    1652     manning_friction(g, eps, w, z, uh, vh, eta, xmom_update, ymom_update)
    1653 
    1654 
     1079    from shallow_water_ext import manning_friction as manning_friction_c
     1080    manning_friction_c(g, eps, w, z, uh, vh, eta, xmom_update, ymom_update)
     1081
     1082
     1083# FIXME (Ole): This was implemented for use with one of the analytical solutions (Sampson?)
     1084# Is it still needed (30 Oct 2007)?
    16551085def linear_friction(domain):
    16561086    """Apply linear friction to water momentum
     
    16871117
    16881118
     1119#---------------------------------
     1120# Experimental auxiliary functions
     1121#---------------------------------
    16891122def check_forcefield(f):
    16901123    """Check that f is either
     
    17031136        except Exception, e:
    17041137            msg = 'Function %s could not be executed:\n%s' %(f, e)
    1705             #FIXME: Reconsider this semantics
     1138            # FIXME: Reconsider this semantics
    17061139            raise msg
    17071140
     
    17141147            raise msg
    17151148
    1716         #Is this really what we want?
     1149        # Is this really what we want?
    17171150        msg = 'Return vector from function %s ' %f
    17181151        msg += 'must have same lenght as input vectors'
     
    17811214            phi = args[1]
    17821215        elif len(args) == 1:
    1783             #Assume vector function returning (s, phi)(t,x,y)
     1216            # Assume vector function returning (s, phi)(t,x,y)
    17841217            vector_function = args[0]
    17851218            s = lambda t,x,y: vector_function(t,x=x,y=y)[0]
    17861219            phi = lambda t,x,y: vector_function(t,x=x,y=y)[1]
    17871220        else:
    1788            #Assume info is in 2 keyword arguments
     1221           # Assume info is in 2 keyword arguments
    17891222
    17901223           if len(kwargs) == 2:
     
    18171250            s_vec = self.speed(t, xc[:,0], xc[:,1])
    18181251        else:
    1819             #Assume s is a scalar
     1252            # Assume s is a scalar
    18201253
    18211254            try:
     
    18301263            phi_vec = self.phi(t, xc[:,0], xc[:,1])
    18311264        else:
    1832             #Assume phi is a scalar
     1265            # Assume phi is a scalar
    18331266
    18341267            try:
     
    18541287        phi = phi_vec[k]
    18551288
    1856         #Convert to radians
     1289        # Convert to radians
    18571290        phi = phi*pi/180
    18581291
    1859         #Compute velocity vector (u, v)
     1292        # Compute velocity vector (u, v)
    18601293        u = s*cos(phi)
    18611294        v = s*sin(phi)
    18621295
    1863         #Compute wind stress
     1296        # Compute wind stress
    18641297        S = const * sqrt(u**2 + v**2)
    18651298        xmom_update[k] += S*u
     
    20411474#------------------
    20421475
     1476
    20431477from anuga.utilities import compile
    20441478if compile.can_use_C_extension('shallow_water_ext.c'):
    2045     # Replace python version with c implementations
     1479    # Underlying C implementations can be accessed
    20461480
    20471481    from shallow_water_ext import rotate, assign_windfield_values
    2048     compute_fluxes = compute_fluxes_c
    2049     extrapolate_second_order_sw=extrapolate_second_order_sw_c
    2050     gravity = gravity_c
    2051     manning_friction = manning_friction_implicit_c
    2052     h_limiter = h_limiter_c
    2053     balance_deep_and_shallow = balance_deep_and_shallow_c
    2054     protect_against_infinitesimal_and_negative_heights =\
    2055                     protect_against_infinitesimal_and_negative_heights_c
    2056 
    2057     #distribute_to_vertices_and_edges =\
    2058     #              distribute_to_vertices_and_edges_c #(like MH's)
    2059 
     1482else:
     1483    msg = 'C implementations could not be accessed by %s.\n ' %__file__
     1484    msg += 'Make sure compile_all.py has been run as described in '
     1485    msg += 'the ANUGA installation guide.'
     1486    raise Exception, msg
    20601487
    20611488
  • anuga_core/source/anuga/shallow_water/shallow_water_ext.c

    r4733 r4769  
    194194
    195195// Innermost flux function (using stage w=z+h)
    196 int flux_function_central(double *q_left, double *q_right,
    197                           double z_left, double z_right,
    198                           double n1, double n2,
    199                           double epsilon, double H0, double g,
    200                           double *edgeflux, double *max_speed) {
     196int _flux_function_central(double *q_left, double *q_right,
     197                           double z_left, double z_right,
     198                           double n1, double n2,
     199                           double epsilon, double H0, double g,
     200                           double *edgeflux, double *max_speed) {
    201201
    202202  /*Compute fluxes between volumes for the shallow water wave equation
     
    737737///////////////////////////////////////////////////////////////////
    738738// Gateways to Python
     739
     740
     741
     742PyObject *flux_function_central(PyObject *self, PyObject *args) {
     743  //
     744  // Gateway to innermost flux function.
     745  // This is only used by the unit tests as the c implementation is
     746  // normally called by compute_fluxes in this module.
     747
     748
     749  PyArrayObject *normal, *ql, *qr,  *edgeflux;
     750  double g, epsilon, max_speed, H0, zl, zr;
     751
     752  if (!PyArg_ParseTuple(args, "OOOddOddd",
     753                        &normal, &ql, &qr, &zl, &zr, &edgeflux,
     754                        &epsilon, &g, &H0)) {
     755    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: flux_function_central could not parse input arguments");
     756    return NULL;
     757  }
     758
     759 
     760  _flux_function_central((double*) ql -> data,
     761                         (double*) qr -> data,
     762                         zl,
     763                         zr,                                             
     764                         ((double*) normal -> data)[0],                                                                         
     765                         ((double*) normal -> data)[1],                 
     766                         epsilon, H0, g,
     767                         (double*) edgeflux -> data,
     768                         &max_speed);
     769 
     770  return Py_BuildValue("d", max_speed); 
     771}
     772
     773
    739774
    740775PyObject *gravity(PyObject *self, PyObject *args) {
     
    14941529  } 
    14951530
    1496   //Input checks (convert sequences into numeric arrays)
     1531  // Input checks (convert sequences into numeric arrays)
    14971532  q = (PyArrayObject *)
    14981533    PyArray_ContiguousFromObject(Q, PyArray_DOUBLE, 0, 0);
     
    15061541  }
    15071542
    1508   //Allocate space for return vector r (don't DECREF)
     1543  // Allocate space for return vector r (don't DECREF)
    15091544  dimensions[0] = 3;
    15101545  r = (PyArrayObject *) PyArray_FromDims(1, dimensions, PyArray_DOUBLE);
    15111546
    1512   //Copy
     1547  // Copy
    15131548  for (i=0; i<3; i++) {
    15141549    ((double *) (r -> data))[i] = ((double *) (q -> data))[i];
    15151550  }
    15161551
    1517   //Get normal and direction
     1552  // Get normal and direction
    15181553  n1 = ((double *) normal -> data)[0];
    15191554  n2 = ((double *) normal -> data)[1];
    15201555  if (direction == -1) n2 = -n2;
    15211556
    1522   //Rotate
     1557  // Rotate
    15231558  _rotate((double *) r -> data, n1, n2);
    15241559
    1525   //Release numeric arrays
     1560  // Release numeric arrays
    15261561  Py_DECREF(q);
    15271562  Py_DECREF(normal);
    15281563
    1529   //return result using PyArray to avoid memory leak
     1564  // Return result using PyArray to avoid memory leak
    15301565  return PyArray_Return(r);
    15311566}
     
    16421677
    16431678      // Edge flux computation (triangle k, edge i)
    1644       flux_function_central(ql, qr, zl, zr,
    1645                             normals[ki2], normals[ki2+1],
    1646                             epsilon, H0, g,
    1647                             edgeflux, &max_speed);
     1679      _flux_function_central(ql, qr, zl, zr,
     1680                             normals[ki2], normals[ki2+1],
     1681                             epsilon, H0, g,
     1682                             edgeflux, &max_speed);
    16481683     
    16491684     
     
    18981933    *xmom_explicit_update,
    18991934    *ymom_explicit_update,
    1900     *already_computed_flux;//tracks whether the flux across an edge has already been computed
    1901 
    1902 
    1903   //Local variables
     1935    *already_computed_flux; // Tracks whether the flux across an edge has already been computed
     1936
     1937
     1938  // Local variables
    19041939  double timestep, max_speed, epsilon, g, H0;
    19051940  double normal[2], ql[3], qr[3], zl, zr;
     
    20602095
    20612096PyObject *balance_deep_and_shallow(PyObject *self, PyObject *args) {
     2097  // Compute linear combination between stage as computed by
     2098  // gradient-limiters limiting using w, and stage computed by
     2099  // gradient-limiters limiting using h (h-limiter).
     2100  // The former takes precedence when heights are large compared to the
     2101  // bed slope while the latter takes precedence when heights are
     2102  // relatively small.  Anything in between is computed as a balanced
     2103  // linear combination in order to avoid numerical disturbances which
     2104  // would otherwise appear as a result of hard switching between
     2105  // modes.
    20622106  //
    20632107  //    balance_deep_and_shallow(beta_h, wc, zc, wv, zv,
     
    21602204  int k, i, n, N, k3;
    21612205  int dimensions[2];
    2162   double beta_h; //Safety factor (see config.py)
     2206  double beta_h; // Safety factor (see config.py)
    21632207  double *hmin, *hmax, hn;
    21642208
     
    22012245      n = ((long*) neighbours -> data)[k3+i];
    22022246
    2203       //Initialise hvbar with values from hv
     2247      // Initialise hvbar with values from hv
    22042248      ((double*) hvbar -> data)[k3+i] = ((double*) hv -> data)[k3+i];
    22052249
    22062250      if (n >= 0) {
    2207         hn = ((double*) hc -> data)[n]; //Neighbour's centroid value
     2251        hn = ((double*) hc -> data)[n]; // Neighbour's centroid value
    22082252
    22092253        hmin[k] = min(hmin[k], hn);
     
    22162260  _limit(N, beta_h, (double*) hc -> data, (double*) hvbar -> data, hmin, hmax);
    22172261
    2218   // // //Py_DECREF(domain); //FIXME: NEcessary?
     2262  // // //Py_DECREF(domain); //FIXME: Necessary?
    22192263  free(hmin);
    22202264  free(hmax);
    22212265
    2222   //return result using PyArray to avoid memory leak
     2266  // Return result using PyArray to avoid memory leak
    22232267  return PyArray_Return(hvbar);
    2224   //return Py_BuildValue("");
    22252268}
    22262269
     
    23372380
    23382381
    2339 //////////////////////////////////////////
     2382//-------------------------------
    23402383// Method table for python module
     2384//-------------------------------
    23412385static struct PyMethodDef MethodTable[] = {
    23422386  /* The cast of the function is necessary since PyCFunction values
     
    23512395  {"gravity", gravity, METH_VARARGS, "Print out"},
    23522396  {"manning_friction", manning_friction, METH_VARARGS, "Print out"},
     2397  {"flux_function_central", flux_function_central, METH_VARARGS, "Print out"}, 
    23532398  {"balance_deep_and_shallow", balance_deep_and_shallow,
    23542399   METH_VARARGS, "Print out"},
     
    23602405  {"assign_windfield_values", assign_windfield_values,
    23612406   METH_VARARGS | METH_KEYWORDS, "Print out"},
    2362   //{"distribute_to_vertices_and_edges",
    2363   // distribute_to_vertices_and_edges, METH_VARARGS},
    2364   //{"update_conserved_quantities",
    2365   // update_conserved_quantities, METH_VARARGS},
    2366   //{"set_initialcondition",
    2367   // set_initialcondition, METH_VARARGS},
    23682407  {NULL, NULL}
    23692408};
  • anuga_core/source/anuga/shallow_water/test_shallow_water_domain.py

    r4733 r4769  
    1010
    1111from shallow_water_domain import *
    12 from shallow_water_domain import flux_function_central as flux_function
     12
     13# Get gateway to C implementation of flux function for direct testing
     14from shallow_water_ext import flux_function_central as flux_function
    1315
    1416class Weir:
     
    252254
    253255
    254     #FIXME (Ole): Individual flux tests do NOT test C implementation directly.   
     256    # Individual flux tests
    255257    def test_flux_zero_case(self):
    256258        ql = zeros( 3, Float )
    257259        qr = zeros( 3, Float )
    258260        normal = zeros( 2, Float )
     261        edgeflux = zeros( 3, Float )
    259262        zl = zr = 0.
    260         flux, max_speed = flux_function(normal, ql, qr, zl, zr)
    261 
    262         assert allclose(flux, [0,0,0])
     263        H0 = 0.0
     264       
     265        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux, epsilon, g, H0)
     266
     267        assert allclose(edgeflux, [0,0,0])
    263268        assert max_speed == 0.
    264269
     
    269274        ql = array([w, 0, 0])
    270275        qr = array([w, 0, 0])
     276        edgeflux = zeros(3, Float)       
    271277        zl = zr = 0.
    272278        h = w - (zl+zr)/2
    273 
    274         flux, max_speed = flux_function(normal, ql, qr, zl, zr)
    275 
    276         assert allclose(flux, [0., 0.5*g*h**2, 0.])
     279        H0 = 0.0
     280
     281        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux, epsilon, g, H0)       
     282        assert allclose(edgeflux, [0., 0.5*g*h**2, 0.])
    277283        assert max_speed == sqrt(g*h)
    278284
     
    299305        qr = array([-0.2, 2, 3])
    300306        zl = zr = -0.5
    301         flux, max_speed = flux_function(normal, ql, qr, zl, zr)
    302 
    303         assert allclose(flux, [2.,13.77433333, 20.])
     307        edgeflux = zeros(3, Float)               
     308
     309        H0 = 0.0
     310
     311        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux, epsilon, g, H0)       
     312
     313        assert allclose(edgeflux, [2.,13.77433333, 20.])
    304314        assert allclose(max_speed, 8.38130948661)
    305315
     
    311321        qr = array([-0.075, 2, 3])
    312322        zl = zr = -0.375
    313         flux, max_speed = flux_function(normal, ql, qr, zl, zr)
    314 
    315         assert allclose(flux, [-3.,-20.0, -30.441])
     323
     324        edgeflux = zeros(3, Float)               
     325        H0 = 0.0
     326        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux, epsilon, g, H0)       
     327
     328        assert allclose(edgeflux, [-3.,-20.0, -30.441])
    316329        assert allclose(max_speed, 11.7146428199)
    317330
     
    322335        qr = array([-0.075, 2, 3])
    323336        zl = zr = -0.375
    324         flux, max_speed = flux_function(normal, ql, qr, zl, zr)
    325 
    326         assert allclose(flux, [sqrt(2)/2, 4.40221112, 7.3829019])
     337
     338        edgeflux = zeros(3, Float)               
     339        H0 = 0.0
     340        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux, epsilon, g, H0)       
     341
     342        assert allclose(edgeflux, [sqrt(2)/2, 4.40221112, 7.3829019])
    327343        assert allclose(max_speed, 4.0716654239)
    328344
     
    333349        qr = array([-0.30683287, 0.1071986, 0.05930515])
    334350        zl = zr = -0.375
    335         flux, max_speed = flux_function(normal, ql, qr, zl, zr)
    336 
    337         assert allclose(flux, [-0.04072676, -0.07096636, -0.01604364])
     351
     352        edgeflux = zeros(3, Float)               
     353        H0 = 0.0
     354        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux, epsilon, g, H0)               
     355
     356        assert allclose(edgeflux, [-0.04072676, -0.07096636, -0.01604364])
    338357        assert allclose(max_speed, 1.31414103233)
    339358
     
    532551
    533552    def test_compute_fluxes0(self):
    534         #Do a full triangle and check that fluxes cancel out for
    535         #the constant stage case
     553        # Do a full triangle and check that fluxes cancel out for
     554        # the constant stage case
    536555
    537556        a = [0.0, 0.0]
     
    554573        assert allclose(domain.neighbour_edges, [[-1,2,-1], [2,0,1], [-1,-1,0],[1,-1,-1]])
    555574
    556         zl=zr=0. #Assume flat bed
    557 
    558         #Flux across right edge of volume 1
     575        zl=zr=0. # Assume flat bed
     576
     577        edgeflux = zeros(3, Float)       
     578        edgeflux0 = zeros(3, Float)
     579        edgeflux1 = zeros(3, Float)
     580        edgeflux2 = zeros(3, Float)                               
     581        H0 = 0.0       
     582
     583        # Flux across right edge of volume 1
    559584        normal = domain.get_normal(1,0)
    560585        ql = domain.get_conserved_quantities(vol_id=1, edge=0)
    561586        qr = domain.get_conserved_quantities(vol_id=2, edge=2)
    562         flux0, max_speed = flux_function(normal, ql, qr, zl, zr)
    563 
    564         #Check that flux seen from other triangles is inverse
     587        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux0, epsilon, g, H0)                       
     588
     589        # Check that flux seen from other triangles is inverse
    565590        tmp = qr; qr=ql; ql=tmp
    566591        normal = domain.get_normal(2,2)
    567         flux, max_speed = flux_function(normal, ql, qr, zl, zr)
    568         assert allclose(flux + flux0, 0.)
    569 
    570         #Flux across upper edge of volume 1
     592        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux, epsilon, g, H0)                               
     593
     594        assert allclose(edgeflux0 + edgeflux, 0.)
     595
     596        # Flux across upper edge of volume 1
    571597        normal = domain.get_normal(1,1)
    572598        ql = domain.get_conserved_quantities(vol_id=1, edge=1)
    573599        qr = domain.get_conserved_quantities(vol_id=3, edge=0)
    574         flux1, max_speed = flux_function(normal, ql, qr, zl, zr)
    575 
    576         #Check that flux seen from other triangles is inverse
     600        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux1, epsilon, g, H0)                                       
     601
     602        # Check that flux seen from other triangles is inverse
    577603        tmp = qr; qr=ql; ql=tmp
    578604        normal = domain.get_normal(3,0)
    579         flux, max_speed = flux_function(normal, ql, qr, zl, zr)
    580         assert allclose(flux + flux1, 0.)
    581 
    582         #Flux across lower left hypotenuse of volume 1
     605        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux, epsilon, g, H0)                                               
     606
     607        assert allclose(edgeflux1 + edgeflux, 0.)       
     608       
     609
     610        # Flux across lower left hypotenuse of volume 1
    583611        normal = domain.get_normal(1,2)
    584612        ql = domain.get_conserved_quantities(vol_id=1, edge=2)
    585613        qr = domain.get_conserved_quantities(vol_id=0, edge=1)
    586         flux2, max_speed = flux_function(normal, ql, qr, zl, zr)
    587 
    588         #Check that flux seen from other triangles is inverse
     614        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux2, epsilon, g, H0)                                                               
     615
     616        # Check that flux seen from other triangles is inverse
    589617        tmp = qr; qr=ql; ql=tmp
    590618        normal = domain.get_normal(0,1)
    591         flux, max_speed = flux_function(normal, ql, qr, zl, zr)
    592         assert allclose(flux + flux2, 0.)
    593 
    594 
    595         #Scale by edgelengths, add up anc check that total flux is zero
     619        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux, epsilon, g, H0)                                                       
     620        assert allclose(edgeflux2 + edgeflux, 0.)
     621
     622
     623        # Scale by edgelengths, add up anc check that total flux is zero
    596624        e0 = domain.edgelengths[1, 0]
    597625        e1 = domain.edgelengths[1, 1]
    598626        e2 = domain.edgelengths[1, 2]
    599627
    600         assert allclose(e0*flux0+e1*flux1+e2*flux2, 0.)
    601 
    602         #Now check that compute_flux yields zeros as well
     628        assert allclose(e0*edgeflux0+e1*edgeflux1+e2*edgeflux2, 0.)
     629
     630        # Now check that compute_flux yields zeros as well
    603631        domain.compute_fluxes()
    604632
     
    635663        zl=zr=0. #Assume flat bed
    636664
    637         #Flux across right edge of volume 1
     665        edgeflux = zeros(3, Float)       
     666        edgeflux0 = zeros(3, Float)
     667        edgeflux1 = zeros(3, Float)
     668        edgeflux2 = zeros(3, Float)                               
     669        H0 = 0.0       
     670       
     671
     672        # Flux across right edge of volume 1
    638673        normal = domain.get_normal(1,0) #Get normal 0 of triangle 1
    639674        assert allclose(normal, [1, 0])
     
    644679        qr = domain.get_conserved_quantities(vol_id=2, edge=2)
    645680        assert allclose(qr, [val2, 0, 0])
    646        
    647         flux0, max_speed = flux_function(normal, ql, qr, zl, zr)
    648 
    649         #Flux across edge in the east direction (as per normal vector)
    650         assert allclose(flux0, [-15.3598804, 253.71111111, 0.])
     681
     682        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux0, epsilon, g, H0)                                                       
     683
     684        # Flux across edge in the east direction (as per normal vector)
     685        assert allclose(edgeflux0, [-15.3598804, 253.71111111, 0.])
    651686        assert allclose(max_speed, 9.21592824046)
    652687
     
    655690        normal_opposite = domain.get_normal(2,2) #Get normal 2 of triangle 2
    656691        assert allclose(normal_opposite, [-1, 0])
    657         flux_opposite, max_speed = flux_function([-1, 0], ql, qr, zl, zr)
    658         assert allclose(flux_opposite, [-15.3598804, -253.71111111, 0.])
     692
     693        max_speed = flux_function(normal_opposite, ql, qr, zl, zr, edgeflux, epsilon, g, H0)                                             
     694        #flux_opposite, max_speed = flux_function([-1, 0], ql, qr, zl, zr)
     695        assert allclose(edgeflux, [-15.3598804, -253.71111111, 0.])
    659696       
    660697
     
    663700        ql = domain.get_conserved_quantities(vol_id=1, edge=1)
    664701        qr = domain.get_conserved_quantities(vol_id=3, edge=0)
    665         flux1, max_speed = flux_function(normal, ql, qr, zl, zr)
    666         assert allclose(flux1, [2.4098563, 0., 123.04444444])
     702        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux1, epsilon, g, H0)                                                               
     703
     704        assert allclose(edgeflux1, [2.4098563, 0., 123.04444444])
    667705        assert allclose(max_speed, 7.22956891292)
    668706
     
    671709        ql = domain.get_conserved_quantities(vol_id=1, edge=2)
    672710        qr = domain.get_conserved_quantities(vol_id=0, edge=1)
    673         flux2, max_speed = flux_function(normal, ql, qr, zl, zr)
    674 
    675         assert allclose(flux2, [9.63942522, -61.59685738, -61.59685738])
     711        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux2, epsilon, g, H0)       
     712
     713        assert allclose(edgeflux2, [9.63942522, -61.59685738, -61.59685738])
    676714        assert allclose(max_speed, 7.22956891292)
    677715
     
    681719        e2 = domain.edgelengths[1, 2]
    682720
    683         total_flux = -(e0*flux0+e1*flux1+e2*flux2)/domain.areas[1]
     721        total_flux = -(e0*edgeflux0+e1*edgeflux1+e2*edgeflux2)/domain.areas[1]
    684722        assert allclose(total_flux, [-0.68218178, -166.6, -35.93333333])
    685723
     
    733771
    734772        zl=zr=0 #Assume flat zero bed
     773        edgeflux = zeros(3, Float)       
     774        edgeflux0 = zeros(3, Float)
     775        edgeflux1 = zeros(3, Float)
     776        edgeflux2 = zeros(3, Float)                               
     777        H0 = 0.0       
     778       
    735779
    736780        domain.set_quantity('elevation', zl*ones( (4,3) ))
     
    759803        ql = domain.get_conserved_quantities(vol_id=1, edge=0)
    760804        qr = domain.get_conserved_quantities(vol_id=2, edge=2)
    761         flux0, max_speed = flux_function(normal, ql, qr, zl, zr)
     805        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux0, epsilon, g, H0)               
    762806
    763807        #Flux across upper edge of volume 1
     
    765809        ql = domain.get_conserved_quantities(vol_id=1, edge=1)
    766810        qr = domain.get_conserved_quantities(vol_id=3, edge=0)
    767         flux1, max_speed = flux_function(normal, ql, qr, zl, zr)
     811        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux1, epsilon, g, H0)                       
    768812
    769813        #Flux across lower left hypotenuse of volume 1
     
    771815        ql = domain.get_conserved_quantities(vol_id=1, edge=2)
    772816        qr = domain.get_conserved_quantities(vol_id=0, edge=1)
    773         flux2, max_speed = flux_function(normal, ql, qr, zl, zr)
     817        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux2, epsilon, g, H0)               
    774818
    775819        #Scale, add up and check that compute_fluxes is correct for vol 1
     
    778822        e2 = domain.edgelengths[1, 2]
    779823
    780         total_flux = -(e0*flux0+e1*flux1+e2*flux2)/domain.areas[1]
     824        total_flux = -(e0*edgeflux0+e1*edgeflux1+e2*edgeflux2)/domain.areas[1]
    781825
    782826
     
    813857
    814858
     859        edgeflux = zeros(3, Float)       
     860        edgeflux0 = zeros(3, Float)
     861        edgeflux1 = zeros(3, Float)
     862        edgeflux2 = zeros(3, Float)                               
     863        H0 = 0.0       
     864       
     865
     866
    815867        domain.set_quantity('stage', [[val0, val0-1, val0-2],
    816868                                      [val1, val1+1, val1],
     
    835887        ql = domain.get_conserved_quantities(vol_id=1, edge=0)
    836888        qr = domain.get_conserved_quantities(vol_id=2, edge=2)
    837         flux0, max_speed = flux_function(normal, ql, qr, zl, zr)
     889        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux0, epsilon, g, H0)
    838890
    839891        #Flux across upper edge of volume 1
     
    841893        ql = domain.get_conserved_quantities(vol_id=1, edge=1)
    842894        qr = domain.get_conserved_quantities(vol_id=3, edge=0)
    843         flux1, max_speed = flux_function(normal, ql, qr, zl, zr)
     895        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux1, epsilon, g, H0)       
    844896
    845897        #Flux across lower left hypotenuse of volume 1
     
    847899        ql = domain.get_conserved_quantities(vol_id=1, edge=2)
    848900        qr = domain.get_conserved_quantities(vol_id=0, edge=1)
    849         flux2, max_speed = flux_function(normal, ql, qr, zl, zr)
     901        max_speed = flux_function(normal, ql, qr, zl, zr, edgeflux2, epsilon, g, H0)       
    850902
    851903        #Scale, add up and check that compute_fluxes is correct for vol 1
     
    854906        e2 = domain.edgelengths[1, 2]
    855907
    856         total_flux = -(e0*flux0+e1*flux1+e2*flux2)/domain.areas[1]
     908        total_flux = -(e0*edgeflux0+e1*edgeflux1+e2*edgeflux2)/domain.areas[1]
    857909
    858910        domain.compute_fluxes()
     
    51365188if __name__ == "__main__":
    51375189
    5138     suite = unittest.makeSuite(Test_Shallow_Water,'test')   
     5190    suite = unittest.makeSuite(Test_Shallow_Water,'test')
    51395191    #suite = unittest.makeSuite(Test_Shallow_Water,'test_extrema')   
    51405192    #suite = unittest.makeSuite(Test_Shallow_Water,'test_tight_slope_limiters')
  • anuga_core/source/anuga/utilities/polygon.py

    r4768 r4769  
    254254    http://www.alienryderflex.com/polygon/
    255255
    256     """
     256    Uses underlying C-implementation in polygon_ext.c
     257    """
     258
     259
     260    if verbose: print 'Checking input to separate_points_by_polygon'
     261
    257262
    258263    #Input checks
     264
     265    assert isinstance(closed, bool), 'Keyword argument "closed" must be boolean'
     266    assert isinstance(verbose, bool), 'Keyword argument "verbose" must be boolean'
    259267
    260268
     
    267275        raise msg
    268276
     277    #if verbose: print 'Checking input to separate_points_by_polygon 2'
    269278    try:
    270279        polygon = ensure_numeric(polygon, Float)
     
    275284        raise msg
    276285
     286    #if verbose: print 'check'
     287
    277288    assert len(polygon.shape) == 2,\
    278289       'Polygon array must be a 2d array of vertices'
     
    290301    M = points.shape[0]  #Number of points
    291302
    292     px = polygon[:,0]
    293     py = polygon[:,1]
    294 
    295     #Used for an optimisation when points are far away from polygon
    296     minpx = min(px); maxpx = max(px)
    297     minpy = min(py); maxpy = max(py)
    298 
    299 
    300     #Begin main loop
    301     indices = zeros(M, Int)
    302 
    303     inside_index = 0    #Keep track of points inside
    304     outside_index = M-1 #Keep track of points outside (starting from end)
    305 
    306     if verbose: print 'Separating %d points' %M       
    307     for k in range(M):
    308 
    309         if verbose:
    310             if k %((M+10)/10)==0: print 'Doing %d of %d' %(k, M)
    311 
    312         #for each point
    313         x = points[k, 0]
    314         y = points[k, 1]
    315 
    316         inside = False
    317 
    318         if not x > maxpx or x < minpx or y > maxpy or y < minpy:
    319             #Check polygon
    320             for i in range(N):
    321                 j = (i+1)%N
    322 
    323                 #Check for case where point is contained in line segment
    324                 if point_on_line(x, y, px[i], py[i], px[j], py[j]):
    325                     if closed:
    326                         inside = True
    327                     else:
    328                         inside = False
    329                     break
    330                 else:
    331                     #Check if truly inside polygon
    332                     if py[i] < y and py[j] >= y or\
    333                        py[j] < y and py[i] >= y:
    334                         if px[i] + (y-py[i])/(py[j]-py[i])*(px[j]-px[i]) < x:
    335                             inside = not inside
    336 
    337         if inside:
    338             indices[inside_index] = k
    339             inside_index += 1
    340         else:
    341             indices[outside_index] = k
    342             outside_index -= 1
    343 
    344     return indices, inside_index
    345 
    346 
    347 def separate_points_by_polygon_c(points, polygon,
    348                                  closed = True, verbose = False):
    349     """Determine whether points are inside or outside a polygon
    350 
    351     C-wrapper
    352     """
    353 
    354 
    355     if verbose: print 'Checking input to separate_points_by_polygon'
    356 
    357 
    358     #Input checks
    359 
    360     assert isinstance(closed, bool), 'Keyword argument "closed" must be boolean'
    361     assert isinstance(verbose, bool), 'Keyword argument "verbose" must be boolean'
    362 
    363 
    364     try:
    365         points = ensure_numeric(points, Float)
    366     except NameError, e:
    367         raise NameError, e
    368     except:
    369         msg = 'Points could not be converted to Numeric array'
    370         raise msg
    371 
    372     #if verbose: print 'Checking input to separate_points_by_polygon 2'
    373     try:
    374         polygon = ensure_numeric(polygon, Float)
    375     except NameError, e:
    376         raise NameError, e
    377     except:
    378         msg = 'Polygon could not be converted to Numeric array'
    379         raise msg
    380 
    381     #if verbose: print 'check'
    382 
    383     assert len(polygon.shape) == 2,\
    384        'Polygon array must be a 2d array of vertices'
    385 
    386     assert polygon.shape[1] == 2,\
    387        'Polygon array must have two columns'
    388 
    389     assert len(points.shape) == 2,\
    390        'Points array must be a 2d array'
    391 
    392     assert points.shape[1] == 2,\
    393        'Points array must have two columns'
    394 
    395     N = polygon.shape[0] #Number of vertices in polygon
    396     M = points.shape[0]  #Number of points
    397 
    398     from polygon_ext import separate_points_by_polygon
     303
    399304
    400305    if verbose: print 'Allocating array for indices'
     
    404309    #if verbose: print 'Calling C-version of inside poly'
    405310
    406     count = separate_points_by_polygon(points, polygon, indices,
    407                                        int(closed), int(verbose))
     311    from polygon_ext import separate_points_by_polygon as sep_points
     312    count = sep_points(points, polygon, indices,
     313                       int(closed), int(verbose))
    408314
    409315    if verbose: print 'Found %d points (out of %d) inside polygon'\
     
    792698from anuga.utilities.compile import can_use_C_extension
    793699if can_use_C_extension('polygon_ext.c'):
     700    # Underlying C implementations can be accessed
     701
    794702    from polygon_ext import point_on_line
    795     separate_points_by_polygon = separate_points_by_polygon_c
     703else:
     704    msg = 'C implementations could not be accessed by %s.\n ' %__file__
     705    msg += 'Make sure compile_all.py has been run as described in '
     706    msg += 'the ANUGA installation guide.'
     707    raise Exception, msg
    796708
    797709
  • anuga_core/source/anuga/utilities/sparse.py

    r3832 r4769  
    320320
    321321
    322 
    323 def csr_mv(self, B):
    324     """Python version of sparse (CSR) matrix multiplication
    325     """
    326 
    327     from Numeric import zeros, Float
    328 
    329 
    330     #Assume numeric types from now on
    331        
    332     if len(B.shape) == 0:
    333         #Scalar - use __rmul__ method
    334         R = B*self
    335        
    336     elif len(B.shape) == 1:
    337         #Vector
    338         assert B.shape[0] == self.N, 'Mismatching dimensions'
    339        
    340         R = zeros(self.M, Float) #Result
    341        
    342         #Multiply nonzero elements
    343         for i in range(self.M):
    344             for ckey in range(self.row_ptr[i],self.row_ptr[i+1]):
    345                 j = self.colind[ckey]
    346                 R[i] += self.data[ckey]*B[j]           
    347                
    348     elif len(B.shape) == 2:
    349        
    350         R = zeros((self.M, B.shape[1]), Float) #Result matrix
    351        
    352         #Multiply nonzero elements
    353        
    354         for col in range(R.shape[1]):
    355             #For each column
    356             for i in range(self.M):
    357                 for ckey in range(self.row_ptr[i],self.row_ptr[i+1]):
    358                     j = self.colind[ckey]
    359                     R[i, col] += self.data[ckey]*B[j,col]           
    360                    
    361     else:
    362         raise ValueError, 'Dimension too high: d=%d' %len(B.shape)
    363    
    364     return R
    365 
    366 
    367 
    368 #Setup for C extensions
     322# Setup for C extensions
    369323from anuga.utilities import compile
    370324if compile.can_use_C_extension('sparse_ext.c'):
    371     #Replace python version with c implementation
     325    # Access underlying c implementations
    372326    from sparse_ext import csr_mv
    373327
     328
    374329if __name__ == '__main__':
    375 
     330    # A little selftest
     331   
    376332    from Numeric import allclose, array, Float
    377333   
Note: See TracChangeset for help on using the changeset viewer.