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

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

Bug huntin'

File size: 30.5 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    #Compute edge values by interpolation
453    for name in domain.conserved_quantities:
454        Q = domain.quantities[name]
455        Q.interpolate_from_vertices_to_edges()
456                   
457
458
459def dry(domain):
460    """Protect against infinitesimal heights and associated high velocities
461    at vertices
462    """
463
464    #FIXME: Experimental
465   
466    #Shortcuts
467    wv = domain.quantities['level'].vertex_values
468    zv = domain.quantities['elevation'].vertex_values
469    xmomv = domain.quantities['xmomentum'].vertex_values
470    ymomv = domain.quantities['ymomentum'].vertex_values           
471    hv = wv - zv  #Water depths at vertices
472
473    #Update
474    for k in range(domain.number_of_elements):
475        hmax = max(hv[k, :])
476       
477        if hmax < domain.minimum_allowed_height:
478            #Control level
479            wv[k, :] = zv[k, :]
480
481            #Control momentum
482            xmomv[k,:] = ymomv[k,:] = 0.0
483   
484
485def protect_against_infinitesimal_and_negative_heights(domain):
486    """Protect against infinitesimal heights and associated high velocities
487    """
488   
489    #Shortcuts
490    wc = domain.quantities['level'].centroid_values
491    zc = domain.quantities['elevation'].centroid_values
492    xmomc = domain.quantities['xmomentum'].centroid_values
493    ymomc = domain.quantities['ymomentum'].centroid_values           
494    hc = wc - zc  #Water depths at centroids   
495
496    #Update
497    for k in range(domain.number_of_elements):
498
499        if hc[k] < domain.minimum_allowed_height: 
500            #Control level
501            wc[k] = zc[k]
502
503            #Control momentum
504            xmomc[k] = ymomc[k] = 0.0
505       
506        #From 'newstyle
507        #if hc[k] < domain.minimum_allowed_height:       
508        #     if hc[k] < 0.0:
509        #            #Control level and height
510        #            wc[k] = zc[k]
511        #
512        #        #Control momentum
513        #        xmomc[k] = ymomc[k] = 0.0
514        #else:
515           
516
517
518def protect_against_infinitesimal_and_negative_heights_c(domain):
519    """Protect against infinitesimal heights and associated high velocities
520    """
521   
522    #Shortcuts
523    wc = domain.quantities['level'].centroid_values
524    zc = domain.quantities['elevation'].centroid_values
525    xmomc = domain.quantities['xmomentum'].centroid_values
526    ymomc = domain.quantities['ymomentum'].centroid_values           
527
528    from shallow_water_ext import protect
529
530    protect(domain.minimum_allowed_height, wc, zc, xmomc, ymomc)
531   
532   
533def balance_deep_and_shallow(domain):
534    """Compute linear combination between stage as computed by
535    gradient-limiters and stage computed as constant height above bed.
536    The former takes precedence when heights are large compared to the
537    bed slope while the latter takes precedence when heights are
538    relatively small.  Anything in between is computed as a balanced
539    linear combination in order to avoid numerical disturbances which
540    would otherwise appear as a result of hard switching between
541    modes.
542    """
543   
544    #Shortcuts
545    wc = domain.quantities['level'].centroid_values
546    zc = domain.quantities['elevation'].centroid_values
547    hc = wc - zc
548   
549    wv = domain.quantities['level'].vertex_values
550    zv = domain.quantities['elevation'].vertex_values
551    hv = wv-zv
552
553
554    #Computed linear combination between constant levels and and
555    #levels parallel to the bed elevation.     
556    for k in range(domain.number_of_elements):
557        #Compute maximal variation in bed elevation
558        #  This quantitiy is
559        #    dz = max_i abs(z_i - z_c)
560        #  and it is independent of dimension
561        #  In the 1d case zc = (z0+z1)/2
562        #  In the 2d case zc = (z0+z1+z2)/3
563
564        dz = max(abs(zv[k,0]-zc[k]),
565                 abs(zv[k,1]-zc[k]),
566                 abs(zv[k,2]-zc[k]))
567
568       
569        hmin = min( hv[k,:] )
570
571       
572        #Create alpha in [0,1], where alpha==0 means using shallow
573        #first order scheme and alpha==1 means using the stage w as
574        #computed by the gradient limiter (1st or 2nd order)
575        #
576        #If hmin > dz/2 then alpha = 1 and the bed will have no effect
577        #If hmin < 0 then alpha = 0 reverting to constant height above bed.
578     
579        if dz > 0.0:
580            alpha = max( min( 2*hmin/dz, 1.0), 0.0 )
581        else:
582            #Flat bed
583            alpha = 1.0
584
585
586        #Weighted balance between stage parallel to bed elevation
587        #(wvi = zvi + hc) and stage as computed by 1st or 2nd
588        #order gradient limiter
589        #(wvi = zvi + hvi) where i=0,1,2 denotes the vertex ids
590        #
591        #It follows that the updated wvi is
592        #  wvi := (1-alpha)*(zvi+hc) + alpha*(zvi+hvi) =
593        #  zvi + hc + alpha*(hvi - hc)
594        #
595        #Note that hvi = zc+hc-zvi in the first order case (constant).
596
597        if alpha < 1:         
598            for i in range(3):
599                wv[k,i] = zv[k,i] + hc[k] + alpha*(hv[k,i]-hc[k])
600           
601
602            #Momentums at centroids
603            xmomc = domain.quantities['xmomentum'].centroid_values
604            ymomc = domain.quantities['ymomentum'].centroid_values       
605
606            #Momentums at vertices
607            xmomv = domain.quantities['xmomentum'].vertex_values
608            ymomv = domain.quantities['ymomentum'].vertex_values         
609
610            # Update momentum as a linear combination of
611            # xmomc and ymomc (shallow) and momentum
612            # from extrapolator xmomv and ymomv (deep).
613            xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:]           
614            ymomv[k,:] = (1-alpha)*ymomc[k] + alpha*ymomv[k,:]
615                                   
616           
617
618def balance_deep_and_shallow_c(domain):
619    """Wrapper for C implementation
620    """
621   
622    #Shortcuts
623    wc = domain.quantities['level'].centroid_values
624    zc = domain.quantities['elevation'].centroid_values
625    hc = wc - zc
626   
627    wv = domain.quantities['level'].vertex_values
628    zv = domain.quantities['elevation'].vertex_values
629    hv = wv-zv
630
631    #Momentums at centroids
632    xmomc = domain.quantities['xmomentum'].centroid_values
633    ymomc = domain.quantities['ymomentum'].centroid_values       
634
635    #Momentums at vertices
636    xmomv = domain.quantities['xmomentum'].vertex_values
637    ymomv = domain.quantities['ymomentum'].vertex_values         
638
639   
640
641    from shallow_water_ext import balance_deep_and_shallow
642    balance_deep_and_shallow(wc, zc, hc, wv, zv, hv,
643                             xmomc, ymomc, xmomv, ymomv)
644
645   
646
647
648###############################################
649#Boundary - specific to the shallow water wave equation
650class Reflective_boundary(Boundary):
651    """Reflective boundary returns same conserved quantities as
652    those present in its neighbour volume but reflected.
653
654    This class is specific to the shallow water equation as it
655    works with the momentum quantities assumed to be the second
656    and third conserved quantities.
657    """
658   
659    def __init__(self, domain = None):       
660        Boundary.__init__(self)
661
662        if domain is None:
663            msg = 'Domain must be specified for reflective boundary'
664            raise msg
665       
666        #Handy shorthands
667        self.level = domain.quantities['level'].edge_values
668        self.xmom = domain.quantities['xmomentum'].edge_values
669        self.ymom = domain.quantities['ymomentum'].edge_values         
670        self.normals = domain.normals
671       
672        from Numeric import zeros, Float       
673        self.conserved_quantities = zeros(3, Float) 
674       
675    def __repr__(self):
676        return 'Reflective_boundary'
677
678
679    def evaluate(self, vol_id, edge_id):
680        """Reflective boundaries reverses the outward momentum
681        of the volume they serve.
682        """
683
684        q = self.conserved_quantities
685        q[0] = self.level[vol_id, edge_id]
686        q[1] = self.xmom[vol_id, edge_id] 
687        q[2] = self.ymom[vol_id, edge_id] 
688       
689        normal = self.normals[vol_id, 2*edge_id:2*edge_id+2]     
690
691       
692        r = rotate(q, normal, direction = 1)
693        r[1] = -r[1]
694        q = rotate(r, normal, direction = -1)
695
696        return q
697
698
699#########################
700#Standard forcing terms:
701#
702def gravity(domain):
703    """Implement forcing function for bed slope working with
704    consecutive data structures of class Volume
705    """
706
707    from util import gradient
708    from Numeric import zeros, Float, array, sum
709
710    xmom = domain.quantities['xmomentum'].explicit_update
711    ymom = domain.quantities['ymomentum'].explicit_update
712
713    Level = domain.quantities['level']
714    Elevation = domain.quantities['elevation']   
715    h = Level.edge_values - Elevation.edge_values
716    v = Elevation.vertex_values
717   
718    x = domain.get_vertex_coordinates()
719    g = domain.g
720   
721    for k in range(domain.number_of_elements):   
722        avg_h = sum( h[k,:] )/3
723       
724        #Compute bed slope
725        x0, y0, x1, y1, x2, y2 = x[k,:]   
726        z0, z1, z2 = v[k,:]
727
728        zx, zy = gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2)
729
730        #Update momentum
731        xmom[k] += -g*zx*avg_h
732        ymom[k] += -g*zy*avg_h       
733
734
735def gravity_c(domain):
736    """Wrapper calling C version
737    """   
738
739    xmom = domain.quantities['xmomentum'].explicit_update
740    ymom = domain.quantities['ymomentum'].explicit_update
741
742    Level = domain.quantities['level']   
743    Elevation = domain.quantities['elevation']   
744    h = Level.edge_values - Elevation.edge_values
745    v = Elevation.vertex_values
746   
747    x = domain.get_vertex_coordinates()
748    g = domain.g
749   
750   
751    from shallow_water_ext import gravity
752    gravity(g, h, v, x, xmom, ymom)
753   
754       
755def manning_friction(domain):
756    """Apply (Manning) friction to water momentum
757    """
758
759    from math import sqrt
760
761    w = domain.quantities['level'].centroid_values
762    uh = domain.quantities['xmomentum'].centroid_values
763    vh = domain.quantities['ymomentum'].centroid_values
764    eta = domain.quantities['friction'].centroid_values   
765   
766    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
767    ymom_update = domain.quantities['ymomentum'].semi_implicit_update   
768   
769    N = domain.number_of_elements
770    eps = domain.minimum_allowed_height
771    g = domain.g
772   
773    for k in range(N):
774        if w[k] >= eps:
775            S = -g * eta[k]**2 * sqrt((uh[k]**2 + vh[k]**2))
776            S /= w[k]**(7.0/3)
777
778            #Update momentum
779            xmom_update[k] += S*uh[k]
780            ymom_update[k] += S*vh[k]
781           
782       
783def manning_friction_c(domain):
784    """Wrapper for c version
785    """
786
787
788    xmom = domain.quantities['xmomentum']
789    ymom = domain.quantities['ymomentum']   
790       
791    w = domain.quantities['level'].centroid_values
792    uh = xmom.centroid_values
793    vh = ymom.centroid_values
794    eta = domain.quantities['friction'].centroid_values   
795   
796    xmom_update = xmom.semi_implicit_update
797    ymom_update = ymom.semi_implicit_update   
798   
799    N = domain.number_of_elements
800    eps = domain.minimum_allowed_height
801    g = domain.g
802
803    from shallow_water_ext import manning_friction
804    manning_friction(g, eps, w, uh, vh, eta, xmom_update, ymom_update)
805       
806
807###########################
808###########################
809#Geometries
810
811
812#FIXME: Rethink this way of creating values.
813
814
815class Weir:
816    """Set a bathymetry for weir with a hole and a downstream gutter
817    x,y are assumed to be in the unit square
818    """
819   
820    def __init__(self, stage):
821        self.inflow_stage = stage
822
823    def __call__(self, x, y):   
824        from Numeric import zeros, Float
825        from math import sqrt
826   
827        N = len(x)
828        assert N == len(y)   
829
830        z = zeros(N, Float)
831        for i in range(N):
832            z[i] = -x[i]/2  #General slope
833
834            #Flattish bit to the left
835            if x[i] < 0.3:
836                z[i] = -x[i]/10 
837           
838            #Weir
839            if x[i] >= 0.3 and x[i] < 0.4:
840                z[i] = -x[i]+0.9
841
842            #Dip
843            x0 = 0.6
844            #depth = -1.3
845            depth = -1.0
846            #plateaux = -0.9
847            plateaux = -0.6           
848            if y[i] < 0.7:
849                if x[i] > x0 and x[i] < 0.9:
850                    z[i] = depth
851
852                #RHS plateaux
853                if x[i] >= 0.9:
854                    z[i] = plateaux
855                   
856                   
857            elif y[i] >= 0.7 and y[i] < 1.5:
858                #Restrict and deepen
859                if x[i] >= x0 and x[i] < 0.8:
860                    z[i] = depth-(y[i]/3-0.3)
861                    #z[i] = depth-y[i]/5
862                    #z[i] = depth
863                elif x[i] >= 0.8:   
864                    #RHS plateaux
865                    z[i] = plateaux
866                   
867            elif y[i] >= 1.5:
868                if x[i] >= x0 and x[i] < 0.8 + (y[i]-1.5)/1.2:
869                    #Widen up and stay at constant depth
870                    z[i] = depth-1.5/5
871                elif x[i] >= 0.8 + (y[i]-1.5)/1.2:                       
872                    #RHS plateaux
873                    z[i] = plateaux                                   
874                   
875
876            #Hole in weir (slightly higher than inflow condition)
877            if x[i] >= 0.3 and x[i] < 0.4 and y[i] > 0.2 and y[i] < 0.4:
878                z[i] = -x[i]+self.inflow_stage + 0.02
879
880            #Channel behind weir
881            x0 = 0.5
882            if x[i] >= 0.4 and x[i] < x0 and y[i] > 0.2 and y[i] < 0.4:
883                z[i] = -x[i]+self.inflow_stage + 0.02
884               
885            if x[i] >= x0 and x[i] < 0.6 and y[i] > 0.2 and y[i] < 0.4:
886                #Flatten it out towards the end
887                z[i] = -x0+self.inflow_stage + 0.02 + (x0-x[i])/5
888
889            #Hole to the east
890            x0 = 1.1; y0 = 0.35
891            #if x[i] < -0.2 and y < 0.5:
892            if sqrt((2*(x[i]-x0))**2 + (2*(y[i]-y0))**2) < 0.2:
893                z[i] = sqrt(((x[i]-x0))**2 + ((y[i]-y0))**2)-1.0
894
895            #Tiny channel draining hole
896            if x[i] >= 1.14 and x[i] < 1.2 and y[i] >= 0.4 and y[i] < 0.6:
897                z[i] = -0.9 #North south
898               
899            if x[i] >= 0.9 and x[i] < 1.18 and y[i] >= 0.58 and y[i] < 0.65:
900                z[i] = -1.0 + (x[i]-0.9)/3 #East west
901           
902           
903
904            #Stuff not in use
905           
906            #Upward slope at inlet to the north west
907            #if x[i] < 0.0: # and y[i] > 0.5:
908            #    #z[i] = -y[i]+0.5  #-x[i]/2
909            #    z[i] = x[i]/4 - y[i]**2 + 0.5
910
911            #Hole to the west
912            #x0 = -0.4; y0 = 0.35 # center
913            #if sqrt((2*(x[i]-x0))**2 + (2*(y[i]-y0))**2) < 0.2:
914            #    z[i] = sqrt(((x[i]-x0))**2 + ((y[i]-y0))**2)-0.2
915
916
917
918
919
920        return z/2
921
922class Weir_simple:
923    """Set a bathymetry for weir with a hole and a downstream gutter
924    x,y are assumed to be in the unit square
925    """
926   
927    def __init__(self, stage):
928        self.inflow_stage = stage
929
930    def __call__(self, x, y):   
931        from Numeric import zeros, Float
932   
933        N = len(x)
934        assert N == len(y)   
935
936        z = zeros(N, Float)
937        for i in range(N):
938            z[i] = -x[i]  #General slope
939
940            #Flat bit to the left
941            if x[i] < 0.3:
942                z[i] = -x[i]/10  #General slope
943           
944            #Weir
945            if x[i] > 0.3 and x[i] < 0.4:
946                z[i] = -x[i]+0.9
947
948            #Dip
949            if x[i] > 0.6 and x[i] < 0.9:
950                z[i] = -x[i]-0.5  #-y[i]/5
951
952            #Hole in weir (slightly higher than inflow condition)
953            if x[i] > 0.3 and x[i] < 0.4 and y[i] > 0.2 and y[i] < 0.4:
954                z[i] = -x[i]+self.inflow_stage + 0.05       
955           
956
957        return z/2
958       
959
960           
961class Constant_height:
962    """Set an initial condition with constant water height, e.g
963    stage s = z+h
964    """
965    def __init__(self, W, h):
966        self.W = W
967        self.h = h
968
969    def __call__(self, x, y):
970        if self.W is None:
971            from Numeric import ones, Float
972            return self.h*ones(len(x), Float)
973        else:
974            return self.W(x,y) + self.h
975
976
977
978##############################################
979#Initialise module
980
981
982import compile
983if compile.can_use_C_extension('shallow_water_ext.c'):
984    #Replace python version with c implementations
985   
986    from shallow_water_ext import rotate
987    compute_fluxes = compute_fluxes_c
988    gravity = gravity_c
989    manning_friction = manning_friction_c
990    balance_deep_and_shallow = balance_deep_and_shallow_c
991    protect_against_infinitesimal_and_negative_heights = protect_against_infinitesimal_and_negative_heights_c
992
993   
994    #distribute_to_vertices_and_edges = distribute_to_vertices_and_edges_c
995
996
997#Optimisation with psyco
998from config import use_psyco
999if use_psyco:
1000    try:
1001        import psyco
1002    except:
1003        msg = 'WARNING: psyco (speedup) could not import'+\
1004              ', you may want to consider installing it'
1005        print msg
1006    else:
1007        psyco.bind(Domain.distribute_to_vertices_and_edges)
1008        psyco.bind(Domain.compute_fluxes)
1009       
1010if __name__ == "__main__":
1011    pass
Note: See TracBrowser for help on using the repository browser.