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

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

Fixed flux_function to match c-version
More testing

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