source: inundation/ga/storm_surge/pyvolution-parallel/shallow_water.py @ 1452

Last change on this file since 1452 was 1264, checked in by steve, 20 years ago

Using vpython faces to speed up visualisation

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