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

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

Cleaned up compute_gradients. Prepared for c extension

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