source: inundation/ga/storm_surge/pyvolution/shallow_water.py @ 1065

Last change on this file since 1065 was 1045, checked in by ole, 20 years ago

FIXME about fluxes

  • Property svn:keywords set to LastChangedDate LastChangedRevision LastChangedBy HeadURL Id
File size: 47.2 KB
Line 
1"""Class Domain -
22D triangular domains for finite-volume computations of
3the shallow water wave equation.
4
5This module contains a specialisation of class Domain from module domain.py
6consisting of methods specific to the Shallow Water Wave Equation
7
8
9U_t + E_x + G_y = S
10
11where
12
13U = [w, uh, vh]
14E = [uh, u^2h + gh^2/2, uvh]
15G = [vh, uvh, v^2h + gh^2/2]
16S represents source terms forcing the system
17(e.g. gravity, friction, wind stress, ...)
18
19and _t, _x, _y denote the derivative with respect to t, x and y respectively.
20
21The quantities are
22
23symbol    variable name    explanation
24x         x                horizontal distance from origin [m]
25y         y                vertical distance from origin [m]
26z         elevation        elevation of bed on which flow is modelled [m]
27h         height           water height above z [m]
28w         stage            absolute water level, w = z+h [m]
29u                          speed in the x direction [m/s]
30v                          speed in the y direction [m/s]
31uh        xmomentum        momentum in the x direction [m^2/s]
32vh        ymomentum        momentum in the y direction [m^2/s]
33
34eta                        mannings friction coefficient [to appear]
35nu                         wind stress coefficient [to appear]
36
37The conserved quantities are w, uh, vh
38
39
40For details see e.g.
41Christopher Zoppou and Stephen Roberts,
42Catastrophic Collapse of Water Supply Reservoirs in Urban Areas,
43Journal of Hydraulic Engineering, vol. 127, No. 7 July 1999
44
45
46Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou
47Geoscience Australia, 2004
48"""
49
50#Subversion keywords:
51#
52#$LastChangedDate: 2005-03-08 12:02:21 +0000 (Tue, 08 Mar 2005) $
53#$LastChangedRevision: 1045 $
54#$LastChangedBy: steve $
55
56
57from domain import *
58from region import *
59Generic_domain = Domain #Rename
60
61#Shalow water domain
62class Domain(Generic_domain):
63
64    def __init__(self, coordinates, vertices, boundary = None,
65                 tagged_elements = None):
66
67        conserved_quantities = ['stage', 'xmomentum', 'ymomentum']
68        other_quantities = ['elevation', 'friction']
69
70        Generic_domain.__init__(self, coordinates, vertices, boundary,
71                                conserved_quantities, other_quantities,
72                                tagged_elements)
73
74        from config import minimum_allowed_height, g
75        self.minimum_allowed_height = minimum_allowed_height
76        self.g = g
77
78        self.forcing_terms.append(gravity)
79        self.forcing_terms.append(manning_friction)
80
81        #Realtime visualisation
82        self.visualise = False
83
84        #Stored output
85        self.store = False
86        self.format = 'sww'
87        self.smooth = True
88
89        #Reduction operation for get_vertex_values
90        #from util import mean
91        #self.reduction = mean
92        self.reduction = min  #Looks better near steep slopes
93
94        self.quantities_to_be_stored = ['stage']
95
96
97        #Establish shortcuts to relevant quantities (for efficiency)
98        #self.w = self.quantities['stage']
99        #self.uh = self.quantities['xmomentum']
100        #self.vh = self.quantities['ymomentum']
101        #self.z = self.quantities['elevation']
102        #self.eta = self.quantities['friction']
103
104    def check_integrity(self):
105        Generic_domain.check_integrity(self)
106
107        #Check that we are solving the shallow water wave equation
108
109        msg = 'First conserved quantity must be "stage"'
110        assert self.conserved_quantities[0] == 'stage', msg
111        msg = 'Second conserved quantity must be "xmomentum"'
112        assert self.conserved_quantities[1] == 'xmomentum', msg
113        msg = 'Third conserved quantity must be "ymomentum"'
114        assert self.conserved_quantities[2] == 'ymomentum', msg
115
116
117    def compute_fluxes(self):
118        #Call correct module function
119        #(either from this module or C-extension)
120        compute_fluxes(self)
121
122    def distribute_to_vertices_and_edges(self):
123        #Call correct module function
124        #(either from this module or C-extension)
125        distribute_to_vertices_and_edges(self)
126
127
128    #FIXME: Under construction
129#     def set_defaults(self):
130#         """Set default values for uninitialised quantities.
131#         This is specific to the shallow water wave equation
132#         Defaults for 'elevation', 'friction', 'xmomentum' and 'ymomentum'
133#         are 0.0. Default for 'stage' is whatever the value of 'elevation'.
134#         """
135
136#         for name in self.other_quantities + self.conserved_quantities:
137#             print name
138#             print self.quantities.keys()
139#             if not self.quantities.has_key(name):
140#                 if name == 'stage':
141
142#                     if self.quantities.has_key('elevation'):
143#                         z = self.quantities['elevation'].vertex_values
144#                         self.set_quantity(name, z)
145#                     else:
146#                         self.set_quantity(name, 0.0)
147#                 else:
148#                     self.set_quantity(name, 0.0)
149
150
151
152#         #Lift negative heights up
153#         #z = self.quantities['elevation'].vertex_values
154#         #w = self.quantities['stage'].vertex_values
155
156#         #h = w-z
157
158#         #for k in range(h.shape[0]):
159#         #    for i in range(3):
160#         #        if h[k, i] < 0.0:
161#         #            w[k, i] = z[k, i]
162
163
164#         #self.quantities['stage'].interpolate()
165
166
167
168    def evolve(self, yieldstep = None, finaltime = None):
169        """Specialisation of basic evolve method from parent class
170        """
171
172        #Call check integrity here rather than from user scripts
173        #self.check_integrity()
174
175
176        #Initial update of vertex and edge values before any storage
177        #and or visualisation
178        self.distribute_to_vertices_and_edges()
179
180
181        #Initialise real time viz if requested
182        if self.visualise is True and self.time == 0.0:
183            import realtime_visualisation as visualise
184            visualise.create_surface(self)
185
186        #Store model data, e.g. for visualisation
187        if self.store is True and self.time == 0.0:
188            self.initialise_storage()
189            #print 'Storing results in ' + self.writer.filename
190        else:
191            #print 'Results will not be stored.'
192            #print 'To store results set domain.store = True'
193            pass
194            #FIXME: Diagnostic output should be controlled by
195            # a 'verbose' flag living in domain (or in a parent class)
196
197        #Call basic machinery from parent class
198        for t in Generic_domain.evolve(self, yieldstep, finaltime):
199            #Real time viz
200            if self.visualise is True:
201                visualise.update(self)
202
203            #Store model data, e.g. for subsequent visualisation
204            if self.store is True:
205                self.store_timestep(self.quantities_to_be_stored)
206
207                #FIXME: Could maybe be taken from specified list
208                #of 'store every step' quantities
209
210            #Pass control on to outer loop for more specific actions
211            yield(t)
212
213
214    def initialise_storage(self):
215        """Create and initialise self.writer object for storing data.
216        Also, save x,y and bed elevation
217        """
218
219        import data_manager
220
221        #Initialise writer
222        self.writer = data_manager.get_dataobject(self, mode = 'w')
223
224        #Store vertices and connectivity
225        self.writer.store_connectivity()
226
227
228    def store_timestep(self, name):
229        """Store named quantity and time.
230
231        Precondition:
232           self.write has been initialised
233        """
234        self.writer.store_timestep(name)
235
236
237#Rotation of momentum vector
238def rotate(q, normal, direction = 1):
239    """Rotate the momentum component q (q[1], q[2])
240    from x,y coordinates to coordinates based on normal vector.
241
242    If direction is negative the rotation is inverted.
243
244    Input vector is preserved
245
246    This function is specific to the shallow water wave equation
247    """
248
249    from Numeric import zeros, Float
250
251    assert len(q) == 3,\
252           'Vector of conserved quantities must have length 3'\
253           'for 2D shallow water equation'
254
255    try:
256        l = len(normal)
257    except:
258        raise 'Normal vector must be an Numeric array'
259
260    assert l == 2, 'Normal vector must have 2 components'
261
262
263    n1 = normal[0]
264    n2 = normal[1]
265
266    r = zeros(len(q), Float) #Rotated quantities
267    r[0] = q[0]              #First quantity, height, is not rotated
268
269    if direction == -1:
270        n2 = -n2
271
272
273    r[1] =  n1*q[1] + n2*q[2]
274    r[2] = -n2*q[1] + n1*q[2]
275
276    return r
277
278
279
280####################################
281# Flux computation
282def flux_function(normal, ql, qr, zl, zr):
283    """Compute fluxes between volumes for the shallow water wave equation
284    cast in terms of w = h+z using the 'central scheme' as described in
285
286    Kurganov, Noelle, Petrova. 'Semidiscrete Central-Upwind Schemes For
287    Hyperbolic Conservation Laws and Hamilton-Jacobi Equations'.
288    Siam J. Sci. Comput. Vol. 23, No. 3, pp. 707-740.
289
290    The implemented formula is given in equation (3.15) on page 714
291
292    Conserved quantities w, uh, vh are stored as elements 0, 1 and 2
293    in the numerical vectors ql an qr.
294
295    Bed elevations zl and zr.
296    """
297
298    from config import g, epsilon
299    from math import sqrt
300    from Numeric import array
301
302    #Align momentums with x-axis
303    q_left  = rotate(ql, normal, direction = 1)
304    q_right = rotate(qr, normal, direction = 1)
305
306    z = (zl+zr)/2 #Take average of field values
307
308    w_left  = q_left[0]   #w=h+z
309    h_left  = w_left-z
310    uh_left = q_left[1]
311
312    if h_left < epsilon:
313        u_left = 0.0  #Could have been negative
314        h_left = 0.0
315    else:
316        u_left  = uh_left/h_left
317
318
319    w_right  = q_right[0]  #w=h+z
320    h_right  = w_right-z
321    uh_right = q_right[1]
322
323
324    if h_right < epsilon:
325        u_right = 0.0  #Could have been negative
326        h_right = 0.0
327    else:
328        u_right  = uh_right/h_right
329
330    vh_left  = q_left[2]
331    vh_right = q_right[2]
332
333    soundspeed_left  = sqrt(g*h_left)
334    soundspeed_right = sqrt(g*h_right)
335
336    #Maximal wave speed
337    s_max = max(u_left+soundspeed_left, u_right+soundspeed_right, 0)
338
339    #Minimal wave speed
340    s_min = min(u_left-soundspeed_left, u_right-soundspeed_right, 0)
341
342    #Flux computation
343   
344    #FIXME(Ole): Why is it again that we don't
345    #use uh_left and uh_right directly in the first entries?
346    flux_left  = array([u_left*h_left,
347                        u_left*uh_left + 0.5*g*h_left**2,
348                        u_left*vh_left])
349    flux_right = array([u_right*h_right,
350                        u_right*uh_right + 0.5*g*h_right**2,
351                        u_right*vh_right])
352
353    denom = s_max-s_min
354    if denom == 0.0:
355        edgeflux = array([0.0, 0.0, 0.0])
356        max_speed = 0.0
357    else:
358        edgeflux = (s_max*flux_left - s_min*flux_right)/denom
359        edgeflux += s_max*s_min*(q_right-q_left)/denom
360
361        edgeflux = rotate(edgeflux, normal, direction=-1)
362        max_speed = max(abs(s_max), abs(s_min))
363
364    return edgeflux, max_speed
365
366
367def compute_fluxes(domain):
368    """Compute all fluxes and the timestep suitable for all volumes
369    in domain.
370
371    Compute total flux for each conserved quantity using "flux_function"
372
373    Fluxes across each edge are scaled by edgelengths and summed up
374    Resulting flux is then scaled by area and stored in
375    explicit_update for each of the three conserved quantities
376    stage, xmomentum and ymomentum
377
378    The maximal allowable speed computed by the flux_function for each volume
379    is converted to a timestep that must not be exceeded. The minimum of
380    those is computed as the next overall timestep.
381
382    Post conditions:
383      domain.explicit_update is reset to computed flux values
384      domain.timestep is set to the largest step satisfying all volumes.
385    """
386
387    import sys
388    from Numeric import zeros, Float
389
390    N = domain.number_of_elements
391
392    #Shortcuts
393    Stage = domain.quantities['stage']
394    Xmom = domain.quantities['xmomentum']
395    Ymom = domain.quantities['ymomentum']
396    Bed = domain.quantities['elevation']
397
398    #Arrays
399    stage = Stage.edge_values
400    xmom =  Xmom.edge_values
401    ymom =  Ymom.edge_values
402    bed =   Bed.edge_values
403
404    stage_bdry = Stage.boundary_values
405    xmom_bdry =  Xmom.boundary_values
406    ymom_bdry =  Ymom.boundary_values
407
408    flux = zeros(3, Float) #Work array for summing up fluxes
409
410    #Loop
411    timestep = float(sys.maxint)
412    for k in range(N):
413
414        flux[:] = 0.  #Reset work array
415        for i in range(3):
416            #Quantities inside volume facing neighbour i
417            ql = [stage[k, i], xmom[k, i], ymom[k, i]]
418            zl = bed[k, i]
419
420            #Quantities at neighbour on nearest face
421            n = domain.neighbours[k,i]
422            if n < 0:
423                m = -n-1 #Convert negative flag to index
424                qr = [stage_bdry[m], xmom_bdry[m], ymom_bdry[m]]
425                zr = zl #Extend bed elevation to boundary
426            else:
427                m = domain.neighbour_edges[k,i]
428                qr = [stage[n, m], xmom[n, m], ymom[n, m]]
429                zr = bed[n, m]
430
431
432            #Outward pointing normal vector
433            normal = domain.normals[k, 2*i:2*i+2]
434
435            #Flux computation using provided function
436            edgeflux, max_speed = flux_function(normal, ql, qr, zl, zr)
437            flux -= edgeflux * domain.edgelengths[k,i]
438
439            #Update optimal_timestep
440            try:
441                timestep = min(timestep, 0.5*domain.radii[k]/max_speed)
442            except ZeroDivisionError:
443                pass
444
445        #Normalise by area and store for when all conserved
446        #quantities get updated
447        flux /= domain.areas[k]
448        Stage.explicit_update[k] = flux[0]
449        Xmom.explicit_update[k] = flux[1]
450        Ymom.explicit_update[k] = flux[2]
451
452
453    domain.timestep = timestep
454
455
456def compute_fluxes_c(domain):
457    """Wrapper calling C version of compute fluxes
458    """
459
460    import sys
461    from Numeric import zeros, Float
462
463    N = domain.number_of_elements
464
465    #Shortcuts
466    Stage = domain.quantities['stage']
467    Xmom = domain.quantities['xmomentum']
468    Ymom = domain.quantities['ymomentum']
469    Bed = domain.quantities['elevation']
470
471    timestep = float(sys.maxint)
472    from shallow_water_ext import compute_fluxes
473    domain.timestep = compute_fluxes(timestep, domain.epsilon, domain.g,
474                                     domain.neighbours,
475                                     domain.neighbour_edges,
476                                     domain.normals,
477                                     domain.edgelengths,
478                                     domain.radii,
479                                     domain.areas,
480                                     Stage.edge_values,
481                                     Xmom.edge_values,
482                                     Ymom.edge_values,
483                                     Bed.edge_values,
484                                     Stage.boundary_values,
485                                     Xmom.boundary_values,
486                                     Ymom.boundary_values,
487                                     Stage.explicit_update,
488                                     Xmom.explicit_update,
489                                     Ymom.explicit_update)
490
491
492####################################
493# Module functions for gradient limiting (distribute_to_vertices_and_edges)
494
495def distribute_to_vertices_and_edges(domain):
496    """Distribution from centroids to vertices specific to the
497    shallow water wave
498    equation.
499
500    It will ensure that h (w-z) is always non-negative even in the
501    presence of steep bed-slopes by taking a weighted average between shallow
502    and deep cases.
503
504    In addition, all conserved quantities get distributed as per either a
505    constant (order==1) or a piecewise linear function (order==2).
506
507    FIXME: more explanation about removal of artificial variability etc
508
509    Precondition:
510      All quantities defined at centroids and bed elevation defined at
511      vertices.
512
513    Postcondition
514      Conserved quantities defined at vertices
515
516    """
517
518    #Remove very thin layers of water
519    protect_against_infinitesimal_and_negative_heights(domain)
520
521    #Extrapolate all conserved quantities
522    for name in domain.conserved_quantities:
523        Q = domain.quantities[name]
524        if domain.order == 1:
525            Q.extrapolate_first_order()
526        elif domain.order == 2:
527            Q.extrapolate_second_order()
528            Q.limit()
529        else:
530            raise 'Unknown order'
531
532    #Take bed elevation into account when water heights are small
533    balance_deep_and_shallow(domain)
534
535    #Compute edge values by interpolation
536    for name in domain.conserved_quantities:
537        Q = domain.quantities[name]
538        Q.interpolate_from_vertices_to_edges()
539
540
541
542def dry(domain):
543    """Protect against infinitesimal heights and associated high velocities
544    at vertices
545    """
546
547    #FIXME: Experimental (from old version). Not in use at the moment
548
549    #Shortcuts
550    wv = domain.quantities['stage'].vertex_values
551    zv = domain.quantities['elevation'].vertex_values
552    xmomv = domain.quantities['xmomentum'].vertex_values
553    ymomv = domain.quantities['ymomentum'].vertex_values
554    hv = wv - zv  #Water depths at vertices
555
556    #Update
557    for k in range(domain.number_of_elements):
558        hmax = max(hv[k, :])
559
560        if hmax < domain.minimum_allowed_height:
561            #Control stage
562            wv[k, :] = zv[k, :]
563
564            #Control momentum
565            xmomv[k,:] = ymomv[k,:] = 0.0
566
567
568def protect_against_infinitesimal_and_negative_heights(domain):
569    """Protect against infinitesimal heights and associated high velocities
570    """
571
572    #FIXME: Look here for error
573
574    #Shortcuts
575    wc = domain.quantities['stage'].centroid_values
576    zc = domain.quantities['elevation'].centroid_values
577    xmomc = domain.quantities['xmomentum'].centroid_values
578    ymomc = domain.quantities['ymomentum'].centroid_values
579    hc = wc - zc  #Water depths at centroids
580
581    #print zc
582    #print '1', wc
583    #Update
584    for k in range(domain.number_of_elements):
585
586        if hc[k] < domain.minimum_allowed_height:
587            #Control stage
588            wc[k] = zc[k]
589
590            #Control momentum
591            xmomc[k] = ymomc[k] = 0.0
592
593    #print '2', wc
594
595
596def protect_against_infinitesimal_and_negative_heights_c(domain):
597    """Protect against infinitesimal heights and associated high velocities
598    """
599
600    #Shortcuts
601    wc = domain.quantities['stage'].centroid_values
602    zc = domain.quantities['elevation'].centroid_values
603    xmomc = domain.quantities['xmomentum'].centroid_values
604    ymomc = domain.quantities['ymomentum'].centroid_values
605
606    from shallow_water_ext import protect
607
608    protect(domain.minimum_allowed_height, wc, zc, xmomc, ymomc)
609
610
611
612def h_limiter(domain):
613    """Limit slopes for each volume to eliminate artificial variance
614    introduced by e.g. second order extrapolator
615
616    limit on h = w-z
617
618    This limiter depends on two quantities (w,z) so it resides within
619    this module rather than within quantity.py
620    """
621
622    from Numeric import zeros, Float
623
624    N = domain.number_of_elements
625    beta_h = domain.beta_h
626
627    #Shortcuts
628    wc = domain.quantities['stage'].centroid_values
629    zc = domain.quantities['elevation'].centroid_values
630    hc = wc - zc
631
632    wv = domain.quantities['stage'].vertex_values
633    zv = domain.quantities['elevation'].vertex_values
634    hv = wv-zv
635
636    hvbar = zeros(hv.shape, Float) #h-limited values
637
638    #Find min and max of this and neighbour's centroid values
639    hmax = zeros(hc.shape, Float)
640    hmin = zeros(hc.shape, Float)
641
642    for k in range(N):
643        hmax[k] = hmin[k] = hc[k]
644        for i in range(3):
645            n = domain.neighbours[k,i]
646            if n >= 0:
647                hn = hc[n] #Neighbour's centroid value
648
649                hmin[k] = min(hmin[k], hn)
650                hmax[k] = max(hmax[k], hn)
651
652
653    #Diffences between centroids and maxima/minima
654    dhmax = hmax - hc
655    dhmin = hmin - hc
656
657    #Deltas between vertex and centroid values
658    dh = zeros(hv.shape, Float)
659    for i in range(3):
660        dh[:,i] = hv[:,i] - hc
661
662    #Phi limiter
663    for k in range(N):
664
665        #Find the gradient limiter (phi) across vertices
666        phi = 1.0
667        for i in range(3):
668            r = 1.0
669            if (dh[k,i] > 0): r = dhmax[k]/dh[k,i]
670            if (dh[k,i] < 0): r = dhmin[k]/dh[k,i]
671
672            phi = min( min(r*beta_h, 1), phi )
673
674        #Then update using phi limiter
675        for i in range(3):
676            hvbar[k,i] = hc[k] + phi*dh[k,i]
677
678    return hvbar
679
680
681
682def h_limiter_c(domain):
683    """Limit slopes for each volume to eliminate artificial variance
684    introduced by e.g. second order extrapolator
685
686    limit on h = w-z
687
688    This limiter depends on two quantities (w,z) so it resides within
689    this module rather than within quantity.py
690
691    Wrapper for c-extension
692    """
693
694    from Numeric import zeros, Float
695
696    N = domain.number_of_elements
697    beta_h = domain.beta_h
698
699    #Shortcuts
700    wc = domain.quantities['stage'].centroid_values
701    zc = domain.quantities['elevation'].centroid_values
702    hc = wc - zc
703
704    wv = domain.quantities['stage'].vertex_values
705    zv = domain.quantities['elevation'].vertex_values
706    hv = wv - zv
707
708    #Call C-extension
709    from shallow_water_ext import h_limiter
710    hvbar = h_limiter(domain, hc, hv)
711
712    return hvbar
713
714
715def balance_deep_and_shallow(domain):
716    """Compute linear combination between stage as computed by
717    gradient-limiters limiting using w, and stage computed as
718    constant height above bed and limited using h.
719    The former takes precedence when heights are large compared to the
720    bed slope while the latter takes precedence when heights are
721    relatively small.  Anything in between is computed as a balanced
722    linear combination in order to avoid numerical disturbances which
723    would otherwise appear as a result of hard switching between
724    modes.
725
726    The h-limiter is always applied irrespective of the order.
727    """
728
729    #New idea.
730    #
731    # In the presence and near of bedslope it is necessary to
732    # limit slopes based on differences in h rather than w
733    # (which is what is needed away from the bed).
734    #
735    # So whether extrapolation was first order or second order,
736    # it will need to be balanced with a h-limited gradient.
737    #
738    # For this we will use the quantity alpha as before
739    #
740
741    #Shortcuts
742    wc = domain.quantities['stage'].centroid_values
743    zc = domain.quantities['elevation'].centroid_values
744    hc = wc - zc
745
746    wv = domain.quantities['stage'].vertex_values
747    zv = domain.quantities['elevation'].vertex_values
748    hv = wv-zv
749
750    #Limit h
751    hvbar = h_limiter(domain)
752
753    for k in range(domain.number_of_elements):
754        #Compute maximal variation in bed elevation
755        #  This quantitiy is
756        #    dz = max_i abs(z_i - z_c)
757        #  and it is independent of dimension
758        #  In the 1d case zc = (z0+z1)/2
759        #  In the 2d case zc = (z0+z1+z2)/3
760
761        dz = max(abs(zv[k,0]-zc[k]),
762                 abs(zv[k,1]-zc[k]),
763                 abs(zv[k,2]-zc[k]))
764
765
766        hmin = min( hv[k,:] )
767
768        #Create alpha in [0,1], where alpha==0 means using the h-limited
769        #stage and alpha==1 means using the w-limited stage as
770        #computed by the gradient limiter (both 1st or 2nd order)
771
772        #If hmin > dz/2 then alpha = 1 and the bed will have no effect
773        #If hmin < 0 then alpha = 0 reverting to constant height above bed.
774
775        if dz > 0.0:
776            alpha = max( min( 2*hmin/dz, 1.0), 0.0 )
777        else:
778            #Flat bed
779            alpha = 1.0
780
781        #Let
782        #
783        #  wvi be the w-limited stage (wvi = zvi + hvi)
784        #  wvi- be the h-limited state (wvi- = zvi + hvi-)
785        #
786        #
787        #where i=0,1,2 denotes the vertex ids
788        #
789        #Weighted balance between w-limited and h-limited stage is
790        #
791        #  wvi := (1-alpha)*(zvi+hvi-) + alpha*(zvi+hvi)
792        #
793        #It follows that the updated wvi is
794        #  wvi := zvi + (1-alpha)*hvi- + alpha*hvi
795        #
796        # Momentum is balanced between constant and limited
797
798
799        #for i in range(3):
800        #    wv[k,i] = zv[k,i] + hvbar[k,i]
801
802        #return
803
804        if alpha < 1:
805
806            for i in range(3):
807                wv[k,i] = zv[k,i] + (1-alpha)*hvbar[k,i] + alpha*hv[k,i]
808
809            #Momentums at centroids
810            xmomc = domain.quantities['xmomentum'].centroid_values
811            ymomc = domain.quantities['ymomentum'].centroid_values
812
813            #Momentums at vertices
814            xmomv = domain.quantities['xmomentum'].vertex_values
815            ymomv = domain.quantities['ymomentum'].vertex_values
816
817            # Update momentum as a linear combination of
818            # xmomc and ymomc (shallow) and momentum
819            # from extrapolator xmomv and ymomv (deep).
820            xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:]
821            ymomv[k,:] = (1-alpha)*ymomc[k] + alpha*ymomv[k,:]
822
823
824def balance_deep_and_shallow_c(domain):
825    """Wrapper for C implementation
826    """
827
828    #Shortcuts
829    wc = domain.quantities['stage'].centroid_values
830    zc = domain.quantities['elevation'].centroid_values
831    hc = wc - zc
832
833    wv = domain.quantities['stage'].vertex_values
834    zv = domain.quantities['elevation'].vertex_values
835    hv = wv - zv
836
837    #Momentums at centroids
838    xmomc = domain.quantities['xmomentum'].centroid_values
839    ymomc = domain.quantities['ymomentum'].centroid_values
840
841    #Momentums at vertices
842    xmomv = domain.quantities['xmomentum'].vertex_values
843    ymomv = domain.quantities['ymomentum'].vertex_values
844
845    #Limit h
846    hvbar = h_limiter(domain)
847
848    #This is how one would make a first order h_limited value
849    #as in the old balancer (pre 17 Feb 2005):
850    #from Numeric import zeros, Float
851    #hvbar = zeros( (len(hc), 3), Float)
852    #for i in range(3):
853    #    hvbar[:,i] = hc[:]
854
855    from shallow_water_ext import balance_deep_and_shallow
856    balance_deep_and_shallow(wc, zc, hc, wv, zv, hv, hvbar,
857                             xmomc, ymomc, xmomv, ymomv)
858
859
860
861
862###############################################
863#Boundaries - specific to the shallow water wave equation
864class Reflective_boundary(Boundary):
865    """Reflective boundary returns same conserved quantities as
866    those present in its neighbour volume but reflected.
867
868    This class is specific to the shallow water equation as it
869    works with the momentum quantities assumed to be the second
870    and third conserved quantities.
871    """
872
873    def __init__(self, domain = None):
874        Boundary.__init__(self)
875
876        if domain is None:
877            msg = 'Domain must be specified for reflective boundary'
878            raise msg
879
880        #Handy shorthands
881        self.stage = domain.quantities['stage'].edge_values
882        self.xmom = domain.quantities['xmomentum'].edge_values
883        self.ymom = domain.quantities['ymomentum'].edge_values
884        self.normals = domain.normals
885
886        from Numeric import zeros, Float
887        self.conserved_quantities = zeros(3, Float)
888
889    def __repr__(self):
890        return 'Reflective_boundary'
891
892
893    def evaluate(self, vol_id, edge_id):
894        """Reflective boundaries reverses the outward momentum
895        of the volume they serve.
896        """
897
898        q = self.conserved_quantities
899        q[0] = self.stage[vol_id, edge_id]
900        q[1] = self.xmom[vol_id, edge_id]
901        q[2] = self.ymom[vol_id, edge_id]
902
903        normal = self.normals[vol_id, 2*edge_id:2*edge_id+2]
904
905
906        r = rotate(q, normal, direction = 1)
907        r[1] = -r[1]
908        q = rotate(r, normal, direction = -1)
909
910        return q
911
912
913#class Spatio_temporal_boundary(Boundary):
914#    """The spatio-temporal boundary, reads values for the conserved
915#    quantities from an sww NetCDF file, and returns interpolated values
916#    at the midpoints of each associated boundaty segment.
917#    Time dependency is interpolated linearly as in util.File_function.#
918#
919#    Example:
920#    Bf = Spatio_temporal_boundary('source_file.sww', domain)
921#
922#    """
923Spatio_temporal_boundary = File_boundary
924
925
926
927
928#########################
929#Standard forcing terms:
930#
931def gravity(domain):
932    """Apply gravitational pull in the presence of bed slope
933    """
934
935    from util import gradient
936    from Numeric import zeros, Float, array, sum
937
938    xmom = domain.quantities['xmomentum'].explicit_update
939    ymom = domain.quantities['ymomentum'].explicit_update
940
941    Stage = domain.quantities['stage']
942    Elevation = domain.quantities['elevation']
943    h = Stage.edge_values - Elevation.edge_values
944    v = Elevation.vertex_values
945
946    x = domain.get_vertex_coordinates()
947    g = domain.g
948
949    for k in range(domain.number_of_elements):
950        avg_h = sum( h[k,:] )/3
951
952        #Compute bed slope
953        x0, y0, x1, y1, x2, y2 = x[k,:]
954        z0, z1, z2 = v[k,:]
955
956        zx, zy = gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2)
957
958        #Update momentum
959        xmom[k] += -g*zx*avg_h
960        ymom[k] += -g*zy*avg_h
961
962
963def gravity_c(domain):
964    """Wrapper calling C version
965    """
966
967    xmom = domain.quantities['xmomentum'].explicit_update
968    ymom = domain.quantities['ymomentum'].explicit_update
969
970    Stage = domain.quantities['stage']
971    Elevation = domain.quantities['elevation']
972    h = Stage.edge_values - Elevation.edge_values
973    v = Elevation.vertex_values
974
975    x = domain.get_vertex_coordinates()
976    g = domain.g
977
978
979    from shallow_water_ext import gravity
980    gravity(g, h, v, x, xmom, ymom)
981
982
983def manning_friction(domain):
984    """Apply (Manning) friction to water momentum
985    """
986
987    from math import sqrt
988
989    w = domain.quantities['stage'].centroid_values
990    z = domain.quantities['elevation'].centroid_values
991    h = w-z
992
993    uh = domain.quantities['xmomentum'].centroid_values
994    vh = domain.quantities['ymomentum'].centroid_values
995    eta = domain.quantities['friction'].centroid_values
996
997    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
998    ymom_update = domain.quantities['ymomentum'].semi_implicit_update
999
1000    N = domain.number_of_elements
1001    eps = domain.minimum_allowed_height
1002    g = domain.g
1003
1004    for k in range(N):
1005        if eta[k] >= eps:
1006            if h[k] >= eps:
1007                S = -g * eta[k]**2 * sqrt((uh[k]**2 + vh[k]**2))
1008                S /= h[k]**(7.0/3)
1009
1010                #Update momentum
1011                xmom_update[k] += S*uh[k]
1012                ymom_update[k] += S*vh[k]
1013
1014
1015def manning_friction_c(domain):
1016    """Wrapper for c version
1017    """
1018
1019
1020    xmom = domain.quantities['xmomentum']
1021    ymom = domain.quantities['ymomentum']
1022
1023    w = domain.quantities['stage'].centroid_values
1024    z = domain.quantities['elevation'].centroid_values
1025
1026    uh = xmom.centroid_values
1027    vh = ymom.centroid_values
1028    eta = domain.quantities['friction'].centroid_values
1029
1030    xmom_update = xmom.semi_implicit_update
1031    ymom_update = ymom.semi_implicit_update
1032
1033    N = domain.number_of_elements
1034    eps = domain.minimum_allowed_height
1035    g = domain.g
1036
1037    from shallow_water_ext import manning_friction
1038    manning_friction(g, eps, w, z, uh, vh, eta, xmom_update, ymom_update)
1039
1040
1041def linear_friction(domain):
1042    """Apply linear friction to water momentum
1043
1044    Assumes quantity: 'linear_friction' to be present
1045    """
1046
1047    from math import sqrt
1048
1049    w = domain.quantities['stage'].centroid_values
1050    z = domain.quantities['elevation'].centroid_values
1051    h = w-z
1052
1053    uh = domain.quantities['xmomentum'].centroid_values
1054    vh = domain.quantities['ymomentum'].centroid_values
1055    tau = domain.quantities['linear_friction'].centroid_values
1056
1057    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
1058    ymom_update = domain.quantities['ymomentum'].semi_implicit_update
1059
1060    N = domain.number_of_elements
1061    eps = domain.minimum_allowed_height
1062    g = domain.g #Not necessary? Why was this added?
1063
1064    for k in range(N):
1065        if tau[k] >= eps:
1066            if h[k] >= eps:
1067                S = -tau[k]/h[k]
1068
1069                #Update momentum
1070                xmom_update[k] += S*uh[k]
1071                ymom_update[k] += S*vh[k]
1072
1073
1074
1075def check_forcefield(f):
1076    """Check that f is either
1077    1: a callable object f(t,x,y), where x and y are vectors
1078       and that it returns an array or a list of same length
1079       as x and y
1080    2: a scalar
1081    """
1082
1083    from Numeric import ones, Float, array
1084
1085
1086    if callable(f):
1087        N = 3
1088        x = ones(3, Float)
1089        y = ones(3, Float)
1090        try:
1091            q = f(1.0, x, y)
1092        except Exception, e:
1093            msg = 'Function %s could not be executed:\n%s' %(f, e)
1094            #FIXME: Reconsider this semantics
1095            raise msg
1096
1097        try:
1098            q = array(q).astype(Float)
1099        except:
1100            msg = 'Return value from vector function %s could ' %f
1101            msg += 'not be converted into a Numeric array of floats.\n'
1102            msg += 'Specified function should return either list or array.'
1103            raise msg
1104
1105        msg = 'Return vector from function %s' %f
1106        msg += 'must have same lenght as input vectors'
1107        assert len(q) == N, msg
1108
1109    else:
1110        try:
1111            f = float(f)
1112        except:
1113            msg = 'Force field %s must be either a scalar' %f
1114            msg += ' or a vector function'
1115            raise msg
1116    return f
1117
1118
1119class Wind_stress:
1120    """Apply wind stress to water momentum in terms of
1121    wind speed [m/s] and wind direction [degrees]
1122    """
1123
1124    def __init__(self, *args, **kwargs):
1125        """Initialise windfield from wind speed s [m/s]
1126        and wind direction phi [degrees]
1127
1128        Inputs v and phi can be either scalars or Python functions, e.g.
1129
1130        W = Wind_stress(10, 178)
1131
1132        #FIXME - 'normal' degrees are assumed for now, i.e. the
1133        vector (1,0) has zero degrees.
1134        We may need to convert from 'compass' degrees later on and also
1135        map from True north to grid north.
1136
1137        Arguments can also be Python functions of t,x,y as in
1138
1139        def speed(t,x,y):
1140            ...
1141            return s
1142
1143        def angle(t,x,y):
1144            ...
1145            return phi
1146
1147        where x and y are vectors.
1148
1149        and then pass the functions in
1150
1151        W = Wind_stress(speed, angle)
1152
1153        The instantiated object W can be appended to the list of
1154        forcing_terms as in
1155
1156        Alternatively, one vector valued function for (speed, angle)
1157        can be applied, providing both quantities simultaneously.
1158        As in
1159        W = Wind_stress(F), where returns (speed, angle) for each t.
1160
1161        domain.forcing_terms.append(W)
1162        """
1163
1164        from config import rho_a, rho_w, eta_w
1165        from Numeric import array, Float
1166
1167        if len(args) == 2:
1168            s = args[0]
1169            phi = args[1]
1170        elif len(args) == 1:
1171            #Assume vector function returning (s, phi)(t,x,y)
1172            vector_function = args[0]
1173            s = lambda t,x,y: vector_function(t,x,y)[0]
1174            phi = lambda t,x,y: vector_function(t,x,y)[1]
1175        else:
1176           #Assume info is in 2 keyword arguments
1177
1178           if len(kwargs) == 2:
1179               s = kwargs['s']
1180               phi = kwargs['phi']
1181           else:
1182               raise 'Assumes two keyword arguments: s=..., phi=....'
1183
1184        self.speed = check_forcefield(s)
1185        self.phi = check_forcefield(phi)
1186
1187        self.const = eta_w*rho_a/rho_w
1188
1189
1190    def __call__(self, domain):
1191        """Evaluate windfield based on values found in domain
1192        """
1193
1194        from math import pi, cos, sin, sqrt
1195        from Numeric import Float, ones, ArrayType
1196
1197        xmom_update = domain.quantities['xmomentum'].explicit_update
1198        ymom_update = domain.quantities['ymomentum'].explicit_update
1199
1200        N = domain.number_of_elements
1201        t = domain.time
1202
1203        if callable(self.speed):
1204            xc = domain.get_centroid_coordinates()
1205            s_vec = self.speed(t, xc[:,0], xc[:,1])
1206        else:
1207            #Assume s is a scalar
1208
1209            try:
1210                s_vec = self.speed * ones(N, Float)
1211            except:
1212                msg = 'Speed must be either callable or a scalar: %s' %self.s
1213                raise msg
1214
1215
1216        if callable(self.phi):
1217            xc = domain.get_centroid_coordinates()
1218            phi_vec = self.phi(t, xc[:,0], xc[:,1])
1219        else:
1220            #Assume phi is a scalar
1221
1222            try:
1223                phi_vec = self.phi * ones(N, Float)
1224            except:
1225                msg = 'Angle must be either callable or a scalar: %s' %self.phi
1226                raise msg
1227
1228        assign_windfield_values(xmom_update, ymom_update,
1229                                s_vec, phi_vec, self.const)
1230
1231
1232def assign_windfield_values(xmom_update, ymom_update,
1233                            s_vec, phi_vec, const):
1234    """Python version of assigning wind field to update vectors.
1235    A c version also exists (for speed)
1236    """
1237    from math import pi, cos, sin, sqrt
1238
1239    N = len(s_vec)
1240    for k in range(N):
1241        s = s_vec[k]
1242        phi = phi_vec[k]
1243
1244        #Convert to radians
1245        phi = phi*pi/180
1246
1247        #Compute velocity vector (u, v)
1248        u = s*cos(phi)
1249        v = s*sin(phi)
1250
1251        #Compute wind stress
1252        S = const * sqrt(u**2 + v**2)
1253        xmom_update[k] += S*u
1254        ymom_update[k] += S*v
1255
1256
1257
1258##############################
1259#OBSOLETE STUFF
1260
1261def balance_deep_and_shallow_old(domain):
1262    """Compute linear combination between stage as computed by
1263    gradient-limiters and stage computed as constant height above bed.
1264    The former takes precedence when heights are large compared to the
1265    bed slope while the latter takes precedence when heights are
1266    relatively small.  Anything in between is computed as a balanced
1267    linear combination in order to avoid numerical disturbances which
1268    would otherwise appear as a result of hard switching between
1269    modes.
1270    """
1271
1272    #OBSOLETE
1273
1274    #Shortcuts
1275    wc = domain.quantities['stage'].centroid_values
1276    zc = domain.quantities['elevation'].centroid_values
1277    hc = wc - zc
1278
1279    wv = domain.quantities['stage'].vertex_values
1280    zv = domain.quantities['elevation'].vertex_values
1281    hv = wv-zv
1282
1283
1284    #Computed linear combination between constant stages and and
1285    #stages parallel to the bed elevation.
1286    for k in range(domain.number_of_elements):
1287        #Compute maximal variation in bed elevation
1288        #  This quantitiy is
1289        #    dz = max_i abs(z_i - z_c)
1290        #  and it is independent of dimension
1291        #  In the 1d case zc = (z0+z1)/2
1292        #  In the 2d case zc = (z0+z1+z2)/3
1293
1294        dz = max(abs(zv[k,0]-zc[k]),
1295                 abs(zv[k,1]-zc[k]),
1296                 abs(zv[k,2]-zc[k]))
1297
1298
1299        hmin = min( hv[k,:] )
1300
1301        #Create alpha in [0,1], where alpha==0 means using shallow
1302        #first order scheme and alpha==1 means using the stage w as
1303        #computed by the gradient limiter (1st or 2nd order)
1304        #
1305        #If hmin > dz/2 then alpha = 1 and the bed will have no effect
1306        #If hmin < 0 then alpha = 0 reverting to constant height above bed.
1307
1308        if dz > 0.0:
1309            alpha = max( min( 2*hmin/dz, 1.0), 0.0 )
1310        else:
1311            #Flat bed
1312            alpha = 1.0
1313
1314        #Weighted balance between stage parallel to bed elevation
1315        #(wvi = zvi + hc) and stage as computed by 1st or 2nd
1316        #order gradient limiter
1317        #(wvi = zvi + hvi) where i=0,1,2 denotes the vertex ids
1318        #
1319        #It follows that the updated wvi is
1320        #  wvi := (1-alpha)*(zvi+hc) + alpha*(zvi+hvi) =
1321        #  zvi + hc + alpha*(hvi - hc)
1322        #
1323        #Note that hvi = zc+hc-zvi in the first order case (constant).
1324
1325        if alpha < 1:
1326            for i in range(3):
1327                wv[k,i] = zv[k,i] + hc[k] + alpha*(hv[k,i]-hc[k])
1328
1329
1330            #Momentums at centroids
1331            xmomc = domain.quantities['xmomentum'].centroid_values
1332            ymomc = domain.quantities['ymomentum'].centroid_values
1333
1334            #Momentums at vertices
1335            xmomv = domain.quantities['xmomentum'].vertex_values
1336            ymomv = domain.quantities['ymomentum'].vertex_values
1337
1338            # Update momentum as a linear combination of
1339            # xmomc and ymomc (shallow) and momentum
1340            # from extrapolator xmomv and ymomv (deep).
1341            xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:]
1342            ymomv[k,:] = (1-alpha)*ymomc[k] + alpha*ymomv[k,:]
1343
1344
1345
1346###########################
1347###########################
1348#Geometries
1349
1350
1351#FIXME: Rethink this way of creating values.
1352
1353
1354class Weir:
1355    """Set a bathymetry for weir with a hole and a downstream gutter
1356    x,y are assumed to be in the unit square
1357    """
1358
1359    def __init__(self, stage):
1360        self.inflow_stage = stage
1361
1362    def __call__(self, x, y):
1363        from Numeric import zeros, Float
1364        from math import sqrt
1365
1366        N = len(x)
1367        assert N == len(y)
1368
1369        z = zeros(N, Float)
1370        for i in range(N):
1371            z[i] = -x[i]/2  #General slope
1372
1373            #Flattish bit to the left
1374            if x[i] < 0.3:
1375                z[i] = -x[i]/10
1376
1377            #Weir
1378            if x[i] >= 0.3 and x[i] < 0.4:
1379                z[i] = -x[i]+0.9
1380
1381            #Dip
1382            x0 = 0.6
1383            #depth = -1.3
1384            depth = -1.0
1385            #plateaux = -0.9
1386            plateaux = -0.6
1387            if y[i] < 0.7:
1388                if x[i] > x0 and x[i] < 0.9:
1389                    z[i] = depth
1390
1391                #RHS plateaux
1392                if x[i] >= 0.9:
1393                    z[i] = plateaux
1394
1395
1396            elif y[i] >= 0.7 and y[i] < 1.5:
1397                #Restrict and deepen
1398                if x[i] >= x0 and x[i] < 0.8:
1399                    z[i] = depth-(y[i]/3-0.3)
1400                    #z[i] = depth-y[i]/5
1401                    #z[i] = depth
1402                elif x[i] >= 0.8:
1403                    #RHS plateaux
1404                    z[i] = plateaux
1405
1406            elif y[i] >= 1.5:
1407                if x[i] >= x0 and x[i] < 0.8 + (y[i]-1.5)/1.2:
1408                    #Widen up and stay at constant depth
1409                    z[i] = depth-1.5/5
1410                elif x[i] >= 0.8 + (y[i]-1.5)/1.2:
1411                    #RHS plateaux
1412                    z[i] = plateaux
1413
1414
1415            #Hole in weir (slightly higher than inflow condition)
1416            if x[i] >= 0.3 and x[i] < 0.4 and y[i] > 0.2 and y[i] < 0.4:
1417                z[i] = -x[i]+self.inflow_stage + 0.02
1418
1419            #Channel behind weir
1420            x0 = 0.5
1421            if x[i] >= 0.4 and x[i] < x0 and y[i] > 0.2 and y[i] < 0.4:
1422                z[i] = -x[i]+self.inflow_stage + 0.02
1423
1424            if x[i] >= x0 and x[i] < 0.6 and y[i] > 0.2 and y[i] < 0.4:
1425                #Flatten it out towards the end
1426                z[i] = -x0+self.inflow_stage + 0.02 + (x0-x[i])/5
1427
1428            #Hole to the east
1429            x0 = 1.1; y0 = 0.35
1430            #if x[i] < -0.2 and y < 0.5:
1431            if sqrt((2*(x[i]-x0))**2 + (2*(y[i]-y0))**2) < 0.2:
1432                z[i] = sqrt(((x[i]-x0))**2 + ((y[i]-y0))**2)-1.0
1433
1434            #Tiny channel draining hole
1435            if x[i] >= 1.14 and x[i] < 1.2 and y[i] >= 0.4 and y[i] < 0.6:
1436                z[i] = -0.9 #North south
1437
1438            if x[i] >= 0.9 and x[i] < 1.18 and y[i] >= 0.58 and y[i] < 0.65:
1439                z[i] = -1.0 + (x[i]-0.9)/3 #East west
1440
1441
1442
1443            #Stuff not in use
1444
1445            #Upward slope at inlet to the north west
1446            #if x[i] < 0.0: # and y[i] > 0.5:
1447            #    #z[i] = -y[i]+0.5  #-x[i]/2
1448            #    z[i] = x[i]/4 - y[i]**2 + 0.5
1449
1450            #Hole to the west
1451            #x0 = -0.4; y0 = 0.35 # center
1452            #if sqrt((2*(x[i]-x0))**2 + (2*(y[i]-y0))**2) < 0.2:
1453            #    z[i] = sqrt(((x[i]-x0))**2 + ((y[i]-y0))**2)-0.2
1454
1455
1456
1457
1458
1459        return z/2
1460
1461class Weir_simple:
1462    """Set a bathymetry for weir with a hole and a downstream gutter
1463    x,y are assumed to be in the unit square
1464    """
1465
1466    def __init__(self, stage):
1467        self.inflow_stage = stage
1468
1469    def __call__(self, x, y):
1470        from Numeric import zeros, Float
1471
1472        N = len(x)
1473        assert N == len(y)
1474
1475        z = zeros(N, Float)
1476        for i in range(N):
1477            z[i] = -x[i]  #General slope
1478
1479            #Flat bit to the left
1480            if x[i] < 0.3:
1481                z[i] = -x[i]/10  #General slope
1482
1483            #Weir
1484            if x[i] > 0.3 and x[i] < 0.4:
1485                z[i] = -x[i]+0.9
1486
1487            #Dip
1488            if x[i] > 0.6 and x[i] < 0.9:
1489                z[i] = -x[i]-0.5  #-y[i]/5
1490
1491            #Hole in weir (slightly higher than inflow condition)
1492            if x[i] > 0.3 and x[i] < 0.4 and y[i] > 0.2 and y[i] < 0.4:
1493                z[i] = -x[i]+self.inflow_stage + 0.05
1494
1495
1496        return z/2
1497
1498
1499
1500class Constant_height:
1501    """Set an initial condition with constant water height, e.g
1502    stage s = z+h
1503    """
1504    def __init__(self, W, h):
1505        self.W = W
1506        self.h = h
1507
1508    def __call__(self, x, y):
1509        if self.W is None:
1510            from Numeric import ones, Float
1511            return self.h*ones(len(x), Float)
1512        else:
1513            return self.W(x,y) + self.h
1514
1515
1516
1517
1518def compute_fluxes_python(domain):
1519    """Compute all fluxes and the timestep suitable for all volumes
1520    in domain.
1521
1522    Compute total flux for each conserved quantity using "flux_function"
1523
1524    Fluxes across each edge are scaled by edgelengths and summed up
1525    Resulting flux is then scaled by area and stored in
1526    explicit_update for each of the three conserved quantities
1527    stage, xmomentum and ymomentum
1528
1529    The maximal allowable speed computed by the flux_function for each volume
1530    is converted to a timestep that must not be exceeded. The minimum of
1531    those is computed as the next overall timestep.
1532
1533    Post conditions:
1534      domain.explicit_update is reset to computed flux values
1535      domain.timestep is set to the largest step satisfying all volumes.
1536    """
1537
1538    import sys
1539    from Numeric import zeros, Float
1540
1541    N = domain.number_of_elements
1542
1543    #Shortcuts
1544    Stage = domain.quantities['stage']
1545    Xmom = domain.quantities['xmomentum']
1546    Ymom = domain.quantities['ymomentum']
1547    Bed = domain.quantities['elevation']
1548
1549    #Arrays
1550    stage = Stage.edge_values
1551    xmom =  Xmom.edge_values
1552    ymom =  Ymom.edge_values
1553    bed =   Bed.edge_values
1554
1555    stage_bdry = Stage.boundary_values
1556    xmom_bdry =  Xmom.boundary_values
1557    ymom_bdry =  Ymom.boundary_values
1558
1559    flux = zeros((N,3), Float) #Work array for summing up fluxes
1560
1561    #Loop
1562    timestep = float(sys.maxint)
1563    for k in range(N):
1564
1565        for i in range(3):
1566            #Quantities inside volume facing neighbour i
1567            ql = [stage[k, i], xmom[k, i], ymom[k, i]]
1568            zl = bed[k, i]
1569
1570            #Quantities at neighbour on nearest face
1571            n = domain.neighbours[k,i]
1572            if n < 0:
1573                m = -n-1 #Convert negative flag to index
1574                qr = [stage_bdry[m], xmom_bdry[m], ymom_bdry[m]]
1575                zr = zl #Extend bed elevation to boundary
1576            else:
1577                m = domain.neighbour_edges[k,i]
1578                qr = [stage[n, m], xmom[n, m], ymom[n, m]]
1579                zr = bed[n, m]
1580
1581
1582            #Outward pointing normal vector
1583            normal = domain.normals[k, 2*i:2*i+2]
1584
1585            #Flux computation using provided function
1586            edgeflux, max_speed = flux_function(normal, ql, qr, zl, zr)
1587
1588            flux[k,:] = edgeflux
1589
1590    return flux
1591
1592
1593
1594
1595
1596
1597
1598##############################################
1599#Initialise module
1600
1601
1602import compile
1603if compile.can_use_C_extension('shallow_water_ext.c'):
1604    #Replace python version with c implementations
1605
1606    from shallow_water_ext import rotate, assign_windfield_values
1607    compute_fluxes = compute_fluxes_c
1608    gravity = gravity_c
1609    manning_friction = manning_friction_c
1610    h_limiter = h_limiter_c
1611    balance_deep_and_shallow = balance_deep_and_shallow_c
1612    protect_against_infinitesimal_and_negative_heights = protect_against_infinitesimal_and_negative_heights_c
1613
1614
1615    #distribute_to_vertices_and_edges = distribute_to_vertices_and_edges_c
1616
1617
1618
1619#Optimisation with psyco
1620from config import use_psyco
1621if use_psyco:
1622    try:
1623        import psyco
1624    except:
1625        import os
1626        if os.uname()[4] == 'x86_64':
1627            pass
1628            #Psyco isn't supported on 64 bit systems, but it doesn't matter
1629        else:
1630            msg = 'WARNING: psyco (speedup) could not import'+\
1631                  ', you may want to consider installing it'
1632            print msg
1633    else:
1634        psyco.bind(Domain.distribute_to_vertices_and_edges)
1635        psyco.bind(Domain.compute_fluxes)
1636
1637if __name__ == "__main__":
1638    pass
Note: See TracBrowser for help on using the repository browser.