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

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

Work towards ensuring that this version (pyvolution mark 3) works as well as previous incarnation (pyvolution mark 2).
This is not true yet as some examples e.g. netherlands display 'blow-up'.

File size: 30.6 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
8FIXME: Write equations here!
9
10
11Conserved quantities are w (water level or stage), uh (x momentum)
12and vh (y momentum).
13
14
15Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou
16Geoscience Australia, 2004   
17"""
18
19from domain import *
20Generic_domain = Domain #Rename
21
22class Domain(Generic_domain):
23
24    def __init__(self, coordinates, vertices, boundary = None,
25                 tagged_elements = None):
26
27        conserved_quantities = ['level', 'xmomentum', 'ymomentum']
28        other_quantities = ['elevation', 'friction'] 
29       
30        Generic_domain.__init__(self, coordinates, vertices, boundary,
31                                conserved_quantities, other_quantities,
32                                tagged_elements)
33
34        from config import minimum_allowed_height, g
35        self.minimum_allowed_height = minimum_allowed_height
36        self.g = g
37
38        self.forcing_terms.append(gravity)
39        self.forcing_terms.append(manning_friction)
40
41        #Realtime visualisation
42        self.visualise = False
43
44        #Stored output
45        self.store=False
46        self.format = 'sww'   
47        self.smooth = True
48
49        #Reduction operation for get_vertex_values             
50        #from pytools.stats import mean
51        #self.reduction = mean
52        self.reduction = min  #Looks better near steep slopes
53       
54
55        #Establish shortcuts to relevant quantities (for efficiency)
56        self.w = self.quantities['level']
57        self.uh = self.quantities['xmomentum']                 
58        self.vh = self.quantities['ymomentum']                         
59        self.z = self.quantities['elevation']           
60        self.eta = self.quantities['friction']                 
61
62    def check_integrity(self):
63        Generic_domain.check_integrity(self)
64
65        #Check that we are solving the shallow water wave equation
66
67        msg = 'First conserved quantity must be "level"'
68        assert self.conserved_quantities[0] == 'level', msg
69        msg = 'Second conserved quantity must be "xmomentum"'
70        assert self.conserved_quantities[1] == 'xmomentum', msg
71        msg = 'Third conserved quantity must be "ymomentum"'
72        assert self.conserved_quantities[2] == 'ymomentum', msg
73       
74
75        #Check that levels are >= bed elevation
76        from Numeric import alltrue, greater_equal
77       
78        level = self.quantities['level']
79        bed = self.quantities['elevation']       
80
81        msg = 'All water levels must be greater than the bed elevation'
82        assert alltrue( greater_equal(
83            level.vertex_values, bed.vertex_values )), msg
84
85        assert alltrue( greater_equal(
86            level.edge_values, bed.edge_values )), msg
87
88        assert alltrue( greater_equal(
89            level.centroid_values, bed.centroid_values )), msg       
90
91
92    def compute_fluxes(self):
93        #Call correct module function
94        #(either from this module or C-extension)
95        compute_fluxes(self)
96
97    def distribute_to_vertices_and_edges(self):
98        #Call correct module function
99        #(either from this module or C-extension)       
100        distribute_to_vertices_and_edges(self)
101
102
103    def evolve(self, yieldstep = None, finaltime = None):
104        """Specialisation of basic evolve method from parent class
105        """
106       
107        #Initialise real time viz if requested
108        if self.visualise is True and self.time == 0.0:
109            import realtime_visualisation as visualise
110            visualise.create_surface(self)
111
112        #Store model data, e.g. for visualisation   
113        if self.store is True and self.time == 0.0:
114            self.initialise_storage()   
115           
116
117        #Call basic machinery from parent class
118        for t in Generic_domain.evolve(self, yieldstep, finaltime):
119            #Real time viz
120            if self.visualise is True:
121                visualise.update(self)
122
123            #Store model data, e.g. for subsequent visualisation   
124            if self.store is True:
125                self.store_timestep('level')
126                #FIXME: Could maybe be taken from specified list
127                #of 'store every step' quantities       
128
129
130            #Pass control on to outer loop for more specific actions   
131            yield(t)
132       
133
134    def initialise_storage(self):       
135        """Create and initialise self.writer object for storing data.
136        Also, save x,y and bed elevation
137        """
138
139        import data_manager
140       
141        #Initialise writer
142        self.writer = data_manager.get_dataobject(self, mode = 'w')
143
144        #Store vertices and connectivity
145        self.writer.store_connectivity()                   
146
147    def store_timestep(self, name):
148        """Store named quantity and time.
149
150        Precondition:
151           self.write has been initialised
152        """
153        self.writer.store_timestep(name)
154       
155#Rotation of momentum vector
156def rotate(q, normal, direction = 1):
157    """Rotate the momentum component q (q[1], q[2])
158    from x,y coordinates to coordinates based on normal vector.
159
160    If direction is negative the rotation is inverted.
161   
162    Input vector is preserved
163
164    This function is specific to the shallow water wave equation
165    """
166
167    #FIXME: Needs to be tested
168
169    from Numeric import zeros, Float
170   
171    assert len(q) == 3,\
172           'Vector of conserved quantities must have length 3'\
173           'for 2D shallow water equation'
174
175    try:
176        l = len(normal)
177    except:
178        raise 'Normal vector must be an Numeric array'
179
180    #FIXME: Put this test into C-extension as well
181    assert l == 2, 'Normal vector must have 2 components'
182
183 
184    n1 = normal[0]
185    n2 = normal[1]   
186   
187    r = zeros(len(q), Float) #Rotated quantities
188    r[0] = q[0]              #First quantity, height, is not rotated
189
190    if direction == -1:
191        n2 = -n2
192
193
194    r[1] =  n1*q[1] + n2*q[2]
195    r[2] = -n2*q[1] + n1*q[2]
196   
197    return r
198
199
200
201####################################
202# Flux computation       
203def flux_function(normal, ql, qr, zl, zr): 
204    """Compute fluxes between volumes for the shallow water wave equation
205    cast in terms of w = h+z using the 'central scheme' as described in
206
207    Kurganov, Noelle, Petrova. 'Semidiscrete Central-Upwind Schemes For
208    Hyperbolic Conservation Laws and Hamilton-Jacobi Equations'.
209    Siam J. Sci. Comput. Vol. 23, No. 3, pp. 707-740.
210
211    The implemented formula is given in equation (3.15) on page 714
212
213    Conserved quantities w, uh, vh are stored as elements 0, 1 and 2
214    in the numerical vectors ql an qr.
215
216    Bed elevations zl and zr.
217    """
218
219    from config import g, epsilon
220    from math import sqrt
221    from Numeric import array
222
223    #Align momentums with x-axis
224    q_left  = rotate(ql, normal, direction = 1)
225    q_right = rotate(qr, normal, direction = 1)
226
227    z = (zl+zr)/2 #Take average of field values
228
229    w_left  = q_left[0]   #w=h+z
230    h_left  = w_left-z
231    uh_left = q_left[1]
232
233    if h_left < epsilon:
234        u_left = 0.0  #Could have been negative
235        h_left = 0.0
236    else:   
237        u_left  = uh_left/h_left
238
239
240    w_right  = q_right[0]  #w=h+z
241    h_right  = w_right-z
242    uh_right = q_right[1]
243
244
245    if h_right < epsilon:
246        u_right = 0.0  #Could have been negative
247        h_right = 0.0
248    else:   
249        u_right  = uh_right/h_right
250
251    vh_left  = q_left[2]
252    vh_right = q_right[2]       
253
254    soundspeed_left  = sqrt(g*h_left) 
255    soundspeed_right = sqrt(g*h_right)
256
257    #Maximal wave speed
258    s_max = max(u_left+soundspeed_left, u_right+soundspeed_right, 0)
259   
260    #Minimal wave speed       
261    s_min = min(u_left-soundspeed_left, u_right-soundspeed_right, 0)
262
263    #Flux computation
264    flux_left  = array([u_left*h_left,
265                        u_left*uh_left + 0.5*g*h_left**2,
266                        u_left*vh_left])
267    flux_right = array([u_right*h_right,
268                        u_right*uh_right + 0.5*g*h_right**2,
269                        u_right*vh_right])   
270
271    denom = s_max-s_min
272    if denom == 0.0:
273        edgeflux = array([0.0, 0.0, 0.0])
274        max_speed = 0.0
275    else:   
276        edgeflux = (s_max*flux_left - s_min*flux_right)/denom
277        edgeflux += s_max*s_min*(q_right-q_left)/denom
278
279        edgeflux = rotate(edgeflux, normal, direction=-1)
280        max_speed = max(abs(s_max), abs(s_min))
281
282    return edgeflux, max_speed       
283
284
285def compute_fluxes(domain):
286    """Compute all fluxes and the timestep suitable for all volumes
287    in domain.
288
289    Compute total flux for each conserved quantity using "flux_function"
290
291    Fluxes across each edge are scaled by edgelengths and summed up
292    Resulting flux is then scaled by area and stored in
293    explicit_update for each of the three conserved quantities
294    level, xmomentum and ymomentum   
295
296    The maximal allowable speed computed by the flux_function for each volume
297    is converted to a timestep that must not be exceeded. The minimum of
298    those is computed as the next overall timestep.
299
300    Post conditions:
301      domain.explicit_update is reset to computed flux values
302      domain.timestep is set to the largest step satisfying all volumes.
303    """
304
305    import sys
306    from Numeric import zeros, Float
307
308    N = domain.number_of_elements
309   
310    #Shortcuts
311    Level = domain.quantities['level']
312    Xmom = domain.quantities['xmomentum']
313    Ymom = domain.quantities['ymomentum']
314    Bed = domain.quantities['elevation']       
315
316    #Arrays
317    level = Level.edge_values
318    xmom =  Xmom.edge_values
319    ymom =  Ymom.edge_values
320    bed =   Bed.edge_values   
321
322    level_bdry = Level.boundary_values
323    xmom_bdry =  Xmom.boundary_values
324    ymom_bdry =  Ymom.boundary_values
325   
326    flux = zeros(3, Float) #Work array for summing up fluxes
327
328    #Loop
329    timestep = float(sys.maxint)   
330    for k in range(N):
331
332        flux[:] = 0.  #Reset work array
333        for i in range(3):
334            #Quantities inside volume facing neighbour i
335            ql = [level[k, i], xmom[k, i], ymom[k, i]]
336            zl = bed[k, i]
337
338            #Quantities at neighbour on nearest face
339            n = domain.neighbours[k,i] 
340            if n < 0:
341                m = -n-1 #Convert negative flag to index
342                qr = [level_bdry[m], xmom_bdry[m], ymom_bdry[m]]
343                zr = zl #Extend bed elevation to boundary
344            else:   
345                m = domain.neighbour_edges[k,i]
346                qr = [level[n, m], xmom[n, m], ymom[n, m]]
347                zr = bed[n, m]               
348
349               
350            #Outward pointing normal vector   
351            normal = domain.normals[k, 2*i:2*i+2]
352
353            #Flux computation using provided function
354            edgeflux, max_speed = flux_function(normal, ql, qr, zl, zr)
355            flux -= edgeflux * domain.edgelengths[k,i]
356
357            #Update optimal_timestep
358            try:
359                timestep = min(timestep, domain.radii[k]/max_speed)
360            except ZeroDivisionError:
361                pass
362
363        #Normalise by area and store for when all conserved
364        #quantities get updated
365        flux /= domain.areas[k]
366        Level.explicit_update[k] = flux[0]
367        Xmom.explicit_update[k] = flux[1]
368        Ymom.explicit_update[k] = flux[2]
369
370    domain.timestep = timestep   
371
372
373def compute_fluxes_c(domain):
374    """Wrapper calling C version of compute fluxes
375    """
376
377    import sys
378    from Numeric import zeros, Float
379
380    N = domain.number_of_elements
381   
382    #Shortcuts
383    Level = domain.quantities['level']
384    Xmom = domain.quantities['xmomentum']
385    Ymom = domain.quantities['ymomentum']
386    Bed = domain.quantities['elevation']       
387
388    timestep = float(sys.maxint)   
389    from shallow_water_ext import compute_fluxes
390    domain.timestep = compute_fluxes(timestep, domain.epsilon, domain.g,
391                                     domain.neighbours,
392                                     domain.neighbour_edges,
393                                     domain.normals,
394                                     domain.edgelengths,                       
395                                     domain.radii,
396                                     domain.areas,
397                                     Level.edge_values, 
398                                     Xmom.edge_values, 
399                                     Ymom.edge_values, 
400                                     Bed.edge_values,   
401                                     Level.boundary_values,
402                                     Xmom.boundary_values,
403                                     Ymom.boundary_values,
404                                     Level.explicit_update,
405                                     Xmom.explicit_update,
406                                     Ymom.explicit_update)
407                                     
408
409####################################
410# Module functions for gradient limiting (distribute_to_vertices_and_edges)
411
412def distribute_to_vertices_and_edges(domain):
413    """Distribution from centroids to vertices specific to the
414    shallow water wave
415    equation.
416
417    It will ensure that h (w-z) is always non-negative even in the
418    presence of steep bed-slopes by taking a weighted average between shallow
419    and deep cases.
420   
421    In addition, all conserved quantities get distributed as per either a
422    constant (order==1) or a piecewise linear function (order==2).
423   
424    FIXME: more explanation about removal of artificial variability etc
425
426    Precondition:
427      All quantities defined at centroids and bed elevation defined at
428      vertices.
429
430    Postcondition
431      Conserved quantities defined at vertices
432   
433    """
434
435    #Remove very thin layers of water
436    protect_against_infinitesimal_and_negative_heights(domain)
437
438    #Extrapolate all conserved quantities
439    for name in domain.conserved_quantities:
440        Q = domain.quantities[name]
441        if domain.order == 1:
442            Q.extrapolate_first_order()
443        elif domain.order == 2:
444            Q.extrapolate_second_order()
445            Q.limit()                               
446        else:
447            raise 'Unknown order'
448
449    #Take bed elevation into account when water heights are small   
450    balance_deep_and_shallow(domain)
451
452    #Protect against infinitesimal depths at vertices   
453    ####dry(domain)
454   
455    #Compute edge values by interpolation
456    for name in domain.conserved_quantities:
457        Q = domain.quantities[name]
458        Q.interpolate_from_vertices_to_edges()
459                   
460
461
462def dry(domain):
463    """Protect against infinitesimal heights and associated high velocities
464    at vertices
465    """
466
467    #FIXME: Experimental
468   
469    #Shortcuts
470    wv = domain.quantities['level'].vertex_values
471    zv = domain.quantities['elevation'].vertex_values
472    xmomv = domain.quantities['xmomentum'].vertex_values
473    ymomv = domain.quantities['ymomentum'].vertex_values           
474    hv = wv - zv  #Water depths at vertices
475
476    #Update
477    for k in range(domain.number_of_elements):
478        hmax = max(hv[k, :])
479       
480        if hmax < domain.minimum_allowed_height:
481            #Control level
482            wv[k, :] = zv[k, :]
483
484            #Control momentum
485            xmomv[k,:] = ymomv[k,:] = 0.0
486   
487
488def protect_against_infinitesimal_and_negative_heights(domain):
489    """Protect against infinitesimal heights and associated high velocities
490    """
491   
492    #Shortcuts
493    wc = domain.quantities['level'].centroid_values
494    zc = domain.quantities['elevation'].centroid_values
495    xmomc = domain.quantities['xmomentum'].centroid_values
496    ymomc = domain.quantities['ymomentum'].centroid_values           
497    hc = wc - zc  #Water depths at centroids   
498
499    #Update
500    for k in range(domain.number_of_elements):
501
502        if hc[k] < domain.minimum_allowed_height: 
503            #Control level
504            wc[k] = zc[k]
505
506            #Control momentum
507            xmomc[k] = ymomc[k] = 0.0
508       
509        #From 'newstyle
510        #if hc[k] < domain.minimum_allowed_height:       
511        #     if hc[k] < 0.0:
512        #            #Control level and height
513        #            wc[k] = zc[k]
514        #
515        #        #Control momentum
516        #        xmomc[k] = ymomc[k] = 0.0
517        #else:
518           
519
520
521def protect_against_infinitesimal_and_negative_heights_c(domain):
522    """Protect against infinitesimal heights and associated high velocities
523    """
524   
525    #Shortcuts
526    wc = domain.quantities['level'].centroid_values
527    zc = domain.quantities['elevation'].centroid_values
528    xmomc = domain.quantities['xmomentum'].centroid_values
529    ymomc = domain.quantities['ymomentum'].centroid_values           
530
531    from shallow_water_ext import protect
532
533    protect(domain.minimum_allowed_height, wc, zc, xmomc, ymomc)
534   
535   
536def balance_deep_and_shallow(domain):
537    """Compute linear combination between stage as computed by
538    gradient-limiters and stage computed as constant height above bed.
539    The former takes precedence when heights are large compared to the
540    bed slope while the latter takes precedence when heights are
541    relatively small.  Anything in between is computed as a balanced
542    linear combination in order to avoid numerical disturbances which
543    would otherwise appear as a result of hard switching between
544    modes.
545    """
546   
547    #Shortcuts
548    wc = domain.quantities['level'].centroid_values
549    zc = domain.quantities['elevation'].centroid_values
550    hc = wc - zc
551   
552    wv = domain.quantities['level'].vertex_values
553    zv = domain.quantities['elevation'].vertex_values
554    hv = wv-zv
555
556
557    #Computed linear combination between constant levels and and
558    #levels parallel to the bed elevation.     
559    for k in range(domain.number_of_elements):
560        #Compute maximal variation in bed elevation
561        #  This quantitiy is
562        #    dz = max_i abs(z_i - z_c)
563        #  and it is independent of dimension
564        #  In the 1d case zc = (z0+z1)/2
565        #  In the 2d case zc = (z0+z1+z2)/3
566
567        dz = max(abs(zv[k,0]-zc[k]),
568                 abs(zv[k,1]-zc[k]),
569                 abs(zv[k,2]-zc[k]))
570
571       
572        hmin = min( hv[k,:] )
573
574       
575        #Create alpha in [0,1], where alpha==0 means using shallow
576        #first order scheme and alpha==1 means using the stage w as
577        #computed by the gradient limiter (1st or 2nd order)
578        #
579        #If hmin > dz/2 then alpha = 1 and the bed will have no effect
580        #If hmin < 0 then alpha = 0 reverting to constant height above bed.
581     
582        if dz > 0.0:
583            alpha = max( min( 2*hmin/dz, 1.0), 0.0 )
584        else:
585            #Flat bed
586            alpha = 1.0
587
588
589        #Weighted balance between stage parallel to bed elevation
590        #(wvi = zvi + hc) and stage as computed by 1st or 2nd
591        #order gradient limiter
592        #(wvi = zvi + hvi) where i=0,1,2 denotes the vertex ids
593        #
594        #It follows that the updated wvi is
595        #  wvi := (1-alpha)*(zvi+hc) + alpha*(zvi+hvi) =
596        #  zvi + hc + alpha*(hvi - hc)
597        #
598        #Note that hvi = zc+hc-zvi in the first order case (constant).
599
600        if alpha < 1:         
601            for i in range(3):
602                wv[k,i] = zv[k,i] + hc[k] + alpha*(hv[k,i]-hc[k])
603           
604
605            #Momentums at centroids
606            xmomc = domain.quantities['xmomentum'].centroid_values
607            ymomc = domain.quantities['ymomentum'].centroid_values       
608
609            #Momentums at vertices
610            xmomv = domain.quantities['xmomentum'].vertex_values
611            ymomv = domain.quantities['ymomentum'].vertex_values         
612
613            # Update momentum as a linear combination of
614            # xmomc and ymomc (shallow) and momentum
615            # from extrapolator xmomv and ymomv (deep).
616            xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:]           
617            ymomv[k,:] = (1-alpha)*ymomc[k] + alpha*ymomv[k,:]
618                                   
619           
620
621def balance_deep_and_shallow_c(domain):
622    """Wrapper for C implementation
623    """
624   
625    #Shortcuts
626    wc = domain.quantities['level'].centroid_values
627    zc = domain.quantities['elevation'].centroid_values
628    hc = wc - zc
629   
630    wv = domain.quantities['level'].vertex_values
631    zv = domain.quantities['elevation'].vertex_values
632    hv = wv-zv
633
634    #Momentums at centroids
635    xmomc = domain.quantities['xmomentum'].centroid_values
636    ymomc = domain.quantities['ymomentum'].centroid_values       
637
638    #Momentums at vertices
639    xmomv = domain.quantities['xmomentum'].vertex_values
640    ymomv = domain.quantities['ymomentum'].vertex_values         
641
642   
643
644    from shallow_water_ext import balance_deep_and_shallow
645    balance_deep_and_shallow(wc, zc, hc, wv, zv, hv,
646                             xmomc, ymomc, xmomv, ymomv)
647
648   
649
650
651###############################################
652#Boundary - specific to the shallow water wave equation
653class Reflective_boundary(Boundary):
654    """Reflective boundary returns same conserved quantities as
655    those present in its neighbour volume but reflected.
656
657    This class is specific to the shallow water equation as it
658    works with the momentum quantities assumed to be the second
659    and third conserved quantities.
660    """
661   
662    def __init__(self, domain = None):       
663        Boundary.__init__(self)
664
665        if domain is None:
666            msg = 'Domain must be specified for reflective boundary'
667            raise msg
668       
669        #Handy shorthands
670        self.level = domain.quantities['level'].edge_values
671        self.xmom = domain.quantities['xmomentum'].edge_values
672        self.ymom = domain.quantities['ymomentum'].edge_values         
673        self.normals = domain.normals
674       
675        from Numeric import zeros, Float       
676        self.conserved_quantities = zeros(3, Float) 
677       
678    def __repr__(self):
679        return 'Reflective_boundary'
680
681
682    def evaluate(self, vol_id, edge_id):
683        """Reflective boundaries reverses the outward momentum
684        of the volume they serve.
685        """
686
687        q = self.conserved_quantities
688        q[0] = self.level[vol_id, edge_id]
689        q[1] = self.xmom[vol_id, edge_id] 
690        q[2] = self.ymom[vol_id, edge_id] 
691       
692        normal = self.normals[vol_id, 2*edge_id:2*edge_id+2]     
693
694       
695        r = rotate(q, normal, direction = 1)
696        r[1] = -r[1]
697        q = rotate(r, normal, direction = -1)
698
699        return q
700
701
702#########################
703#Standard forcing terms:
704#
705def gravity(domain):
706    """Implement forcing function for bed slope working with
707    consecutive data structures of class Volume
708    """
709
710    from util import gradient
711    from Numeric import zeros, Float, array, sum
712
713    xmom = domain.quantities['xmomentum'].explicit_update
714    ymom = domain.quantities['ymomentum'].explicit_update
715
716    Level = domain.quantities['level']
717    Elevation = domain.quantities['elevation']   
718    h = Level.edge_values - Elevation.edge_values
719    v = Elevation.vertex_values
720   
721    x = domain.get_vertex_coordinates()
722    g = domain.g
723   
724    for k in range(domain.number_of_elements):   
725        avg_h = sum( h[k,:] )/3
726       
727        #Compute bed slope
728        x0, y0, x1, y1, x2, y2 = x[k,:]   
729        z0, z1, z2 = v[k,:]
730
731        zx, zy = gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2)
732
733        #Update momentum
734        xmom[k] += -g*zx*avg_h
735        ymom[k] += -g*zy*avg_h       
736
737
738def gravity_c(domain):
739    """Wrapper calling C version
740    """   
741
742    xmom = domain.quantities['xmomentum'].explicit_update
743    ymom = domain.quantities['ymomentum'].explicit_update
744
745    Level = domain.quantities['level']   
746    Elevation = domain.quantities['elevation']   
747    h = Level.edge_values - Elevation.edge_values
748    v = Elevation.vertex_values
749   
750    x = domain.get_vertex_coordinates()
751    g = domain.g
752   
753   
754    from shallow_water_ext import gravity
755    gravity(g, h, v, x, xmom, ymom)
756   
757       
758def manning_friction(domain):
759    """Apply (Manning) friction to water momentum
760    """
761
762    from math import sqrt
763
764    w = domain.quantities['level'].centroid_values
765    uh = domain.quantities['xmomentum'].centroid_values
766    vh = domain.quantities['ymomentum'].centroid_values
767    eta = domain.quantities['friction'].centroid_values   
768   
769    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
770    ymom_update = domain.quantities['ymomentum'].semi_implicit_update   
771   
772    N = domain.number_of_elements
773    eps = domain.minimum_allowed_height
774    g = domain.g
775   
776    for k in range(N):
777        if w[k] >= eps:
778            S = -g * eta[k]**2 * sqrt((uh[k]**2 + vh[k]**2))
779            S /= w[k]**(7.0/3)
780
781            #Update momentum
782            xmom_update[k] += S*uh[k]
783            ymom_update[k] += S*vh[k]
784           
785       
786def manning_friction_c(domain):
787    """Wrapper for c version
788    """
789
790
791    xmom = domain.quantities['xmomentum']
792    ymom = domain.quantities['ymomentum']   
793       
794    w = domain.quantities['level'].centroid_values
795    uh = xmom.centroid_values
796    vh = ymom.centroid_values
797    eta = domain.quantities['friction'].centroid_values   
798   
799    xmom_update = xmom.semi_implicit_update
800    ymom_update = ymom.semi_implicit_update   
801   
802    N = domain.number_of_elements
803    eps = domain.minimum_allowed_height
804    g = domain.g
805
806    from shallow_water_ext import manning_friction
807    manning_friction(g, eps, w, uh, vh, eta, xmom_update, ymom_update)
808       
809
810###########################
811###########################
812#Geometries
813
814
815#FIXME: Rethink this way of creating values.
816
817
818class Weir:
819    """Set a bathymetry for weir with a hole and a downstream gutter
820    x,y are assumed to be in the unit square
821    """
822   
823    def __init__(self, stage):
824        self.inflow_stage = stage
825
826    def __call__(self, x, y):   
827        from Numeric import zeros, Float
828        from math import sqrt
829   
830        N = len(x)
831        assert N == len(y)   
832
833        z = zeros(N, Float)
834        for i in range(N):
835            z[i] = -x[i]/2  #General slope
836
837            #Flattish bit to the left
838            if x[i] < 0.3:
839                z[i] = -x[i]/10 
840           
841            #Weir
842            if x[i] >= 0.3 and x[i] < 0.4:
843                z[i] = -x[i]+0.9
844
845            #Dip
846            x0 = 0.6
847            #depth = -1.3
848            depth = -1.0
849            #plateaux = -0.9
850            plateaux = -0.6           
851            if y[i] < 0.7:
852                if x[i] > x0 and x[i] < 0.9:
853                    z[i] = depth
854
855                #RHS plateaux
856                if x[i] >= 0.9:
857                    z[i] = plateaux
858                   
859                   
860            elif y[i] >= 0.7 and y[i] < 1.5:
861                #Restrict and deepen
862                if x[i] >= x0 and x[i] < 0.8:
863                    z[i] = depth-(y[i]/3-0.3)
864                    #z[i] = depth-y[i]/5
865                    #z[i] = depth
866                elif x[i] >= 0.8:   
867                    #RHS plateaux
868                    z[i] = plateaux
869                   
870            elif y[i] >= 1.5:
871                if x[i] >= x0 and x[i] < 0.8 + (y[i]-1.5)/1.2:
872                    #Widen up and stay at constant depth
873                    z[i] = depth-1.5/5
874                elif x[i] >= 0.8 + (y[i]-1.5)/1.2:                       
875                    #RHS plateaux
876                    z[i] = plateaux                                   
877                   
878
879            #Hole in weir (slightly higher than inflow condition)
880            if x[i] >= 0.3 and x[i] < 0.4 and y[i] > 0.2 and y[i] < 0.4:
881                z[i] = -x[i]+self.inflow_stage + 0.02
882
883            #Channel behind weir
884            x0 = 0.5
885            if x[i] >= 0.4 and x[i] < x0 and y[i] > 0.2 and y[i] < 0.4:
886                z[i] = -x[i]+self.inflow_stage + 0.02
887               
888            if x[i] >= x0 and x[i] < 0.6 and y[i] > 0.2 and y[i] < 0.4:
889                #Flatten it out towards the end
890                z[i] = -x0+self.inflow_stage + 0.02 + (x0-x[i])/5
891
892            #Hole to the east
893            x0 = 1.1; y0 = 0.35
894            #if x[i] < -0.2 and y < 0.5:
895            if sqrt((2*(x[i]-x0))**2 + (2*(y[i]-y0))**2) < 0.2:
896                z[i] = sqrt(((x[i]-x0))**2 + ((y[i]-y0))**2)-1.0
897
898            #Tiny channel draining hole
899            if x[i] >= 1.14 and x[i] < 1.2 and y[i] >= 0.4 and y[i] < 0.6:
900                z[i] = -0.9 #North south
901               
902            if x[i] >= 0.9 and x[i] < 1.18 and y[i] >= 0.58 and y[i] < 0.65:
903                z[i] = -1.0 + (x[i]-0.9)/3 #East west
904           
905           
906
907            #Stuff not in use
908           
909            #Upward slope at inlet to the north west
910            #if x[i] < 0.0: # and y[i] > 0.5:
911            #    #z[i] = -y[i]+0.5  #-x[i]/2
912            #    z[i] = x[i]/4 - y[i]**2 + 0.5
913
914            #Hole to the west
915            #x0 = -0.4; y0 = 0.35 # center
916            #if sqrt((2*(x[i]-x0))**2 + (2*(y[i]-y0))**2) < 0.2:
917            #    z[i] = sqrt(((x[i]-x0))**2 + ((y[i]-y0))**2)-0.2
918
919
920
921
922
923        return z/2
924
925class Weir_simple:
926    """Set a bathymetry for weir with a hole and a downstream gutter
927    x,y are assumed to be in the unit square
928    """
929   
930    def __init__(self, stage):
931        self.inflow_stage = stage
932
933    def __call__(self, x, y):   
934        from Numeric import zeros, Float
935   
936        N = len(x)
937        assert N == len(y)   
938
939        z = zeros(N, Float)
940        for i in range(N):
941            z[i] = -x[i]  #General slope
942
943            #Flat bit to the left
944            if x[i] < 0.3:
945                z[i] = -x[i]/10  #General slope
946           
947            #Weir
948            if x[i] > 0.3 and x[i] < 0.4:
949                z[i] = -x[i]+0.9
950
951            #Dip
952            if x[i] > 0.6 and x[i] < 0.9:
953                z[i] = -x[i]-0.5  #-y[i]/5
954
955            #Hole in weir (slightly higher than inflow condition)
956            if x[i] > 0.3 and x[i] < 0.4 and y[i] > 0.2 and y[i] < 0.4:
957                z[i] = -x[i]+self.inflow_stage + 0.05       
958           
959
960        return z/2
961       
962
963           
964class Constant_height:
965    """Set an initial condition with constant water height, e.g
966    stage s = z+h
967    """
968    def __init__(self, W, h):
969        self.W = W
970        self.h = h
971
972    def __call__(self, x, y):
973        if self.W is None:
974            from Numeric import ones, Float
975            return self.h*ones(len(x), Float)
976        else:
977            return self.W(x,y) + self.h
978
979
980
981##############################################
982#Initialise module
983
984
985import compile
986if compile.can_use_C_extension('shallow_water_ext.c'):
987    #Replace python version with c implementations
988   
989    from shallow_water_ext import rotate
990    compute_fluxes = compute_fluxes_c
991    gravity = gravity_c
992    manning_friction = manning_friction_c
993    balance_deep_and_shallow = balance_deep_and_shallow_c
994    protect_against_infinitesimal_and_negative_heights = protect_against_infinitesimal_and_negative_heights_c
995
996   
997    #distribute_to_vertices_and_edges = distribute_to_vertices_and_edges_c
998
999
1000#Optimisation with psyco
1001from config import use_psyco
1002if use_psyco:
1003    try:
1004        import psyco
1005    except:
1006        msg = 'WARNING: psyco (speedup) could not import'+\
1007              ', you may want to consider installing it'
1008        print msg
1009    else:
1010        psyco.bind(Domain.distribute_to_vertices_and_edges)
1011        psyco.bind(Domain.compute_fluxes)
1012       
1013if __name__ == "__main__":
1014    pass
Note: See TracBrowser for help on using the repository browser.