source: anuga_work/development/anuga_1d/shallow_water_vel_domain.py @ 7818

Last change on this file since 7818 was 6042, checked in by steve, 15 years ago

Found bug associated with changing max_speed_array to an integer

File size: 17.3 KB
RevLine 
[5845]1"""Class Domain -
21D interval domains for finite-volume computations of
3the shallow water wave equation.
4
5This module contains a specialisation of class Domain from module domain.py
6consisting of methods specific to the Shallow Water Wave Equation
7
8
9U_t + E_x = S
10
11where
12
13U = [w, uh]
14E = [uh, u^2h + gh^2/2]
15S represents source terms forcing the system
16(e.g. gravity, friction, wind stress, ...)
17
18and _t, _x, _y denote the derivative with respect to t, x and y respectiely.
19
20The quantities are
21
22symbol    variable name    explanation
23x         x                horizontal distance from origin [m]
24z         elevation        elevation of bed on which flow is modelled [m]
25h         height           water height above z [m]
26w         stage            absolute water level, w = z+h [m]
27u                          speed in the x direction [m/s]
28uh        xmomentum        momentum in the x direction [m^2/s]
29
30eta                        mannings friction coefficient [to appear]
31nu                         wind stress coefficient [to appear]
32
33The conserved quantities are w, uh
34
35For details see e.g.
36Christopher Zoppou and Stephen Roberts,
37Catastrophic Collapse of Water Supply Reservoirs in Urban Areas,
38Journal of Hydraulic Engineering, vol. 127, No. 7 July 1999
39
40
41John Jakeman, Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou
42Geoscience Australia, 2006
43"""
44
45
46from domain import *
47Generic_Domain = Domain #Rename
48
49#Shallow water domain
50class Domain(Generic_Domain):
51
52    def __init__(self, coordinates, boundary = None, tagged_elements = None):
53
54        conserved_quantities = ['stage', 'xmomentum']
55        evolved_quantities = ['stage', 'xmomentum', 'elevation', 'height', 'velocity']
56        other_quantities = ['friction']
57        Generic_Domain.__init__(self,
58                                coordinates = coordinates,
59                                boundary = boundary,
60                                conserved_quantities = conserved_quantities,
61                                evolved_quantities = evolved_quantities,
62                                other_quantities = other_quantities,
63                                tagged_elements = tagged_elements)
64       
65        from config import minimum_allowed_height, g, h0
66        self.minimum_allowed_height = minimum_allowed_height
67        self.g = g
68        self.h0 = h0
69
70        #forcing terms not included in 1d domain ?WHy?
71        self.forcing_terms.append(gravity)
72        #self.forcing_terms.append(manning_friction)
73        #print "\nI have Removed forcing terms line 64 1dsw"
74
75       
76        #Stored output
77        self.store = True
78        self.format = 'sww'
79        self.smooth = True
80
81       
82        #Reduction operation for get_vertex_values
83        from util import mean
84        self.reduction = mean
85        #self.reduction = min  #Looks better near steep slopes
86
87        self.set_quantities_to_be_stored(['stage','xmomentum'])
88
89        self.__doc__ = 'shallow_water_domain'
90
91        self.check_integrity()
92
93
94
95    def check_integrity(self):
96
97        #Check that we are solving the shallow water wave equation
98
99        msg = 'First conserved quantity must be "stage"'
100        assert self.conserved_quantities[0] == 'stage', msg
101        msg = 'Second conserved quantity must be "xmomentum"'
102        assert self.conserved_quantities[1] == 'xmomentum', msg
103
104        msg = 'First evolved quantity must be "stage"'
105        assert self.evolved_quantities[0] == 'stage', msg
106        msg = 'Second evolved quantity must be "xmomentum"'
107        assert self.evolved_quantities[1] == 'xmomentum', msg
108        msg = 'Third evolved quantity must be "elevation"'
109        assert self.evolved_quantities[2] == 'elevation', msg
110        msg = 'Fourth evolved quantity must be "height"'
111        assert self.evolved_quantities[3] == 'height', msg
112        msg = 'Fifth evolved quantity must be "velocity"'
113        assert self.evolved_quantities[4] == 'velocity', msg
114
115        Generic_Domain.check_integrity(self)
116
117    def compute_fluxes(self):
118        #Call correct module function
119        #(either from this module or C-extension)
120        compute_fluxes_vel(self)
121
122    def distribute_to_vertices_and_edges(self):
123        #Call correct module function
124        #(either from this module or C-extension)
125        distribute_to_vertices_and_edges_limit_w_u(self)
126       
127
128#=============== End of Shallow Water Domain ===============================
129#-----------------------------------
130# Compute fluxes interface
131#-----------------------------------
132def compute_fluxes(domain):
133    """
134    Python version of compute fluxes (local_compute_fluxes)
135    is available in test_shallow_water_vel_domain.py
136    """
137 
138
139    from Numeric import zeros, Float
140    import sys
141
142       
143    timestep = float(sys.maxint)
144
145    stage = domain.quantities['stage']
146    xmom = domain.quantities['xmomentum']
147    bed = domain.quantities['elevation']
148
149
150    from comp_flux_vel_ext import compute_fluxes_ext
151
152    domain.flux_timestep = compute_fluxes_ext(timestep,domain,stage,xmom,bed)
153
154
155#-----------------------------------
156# Compute flux definition with vel
157#-----------------------------------
158def compute_fluxes_vel(domain):
159    from Numeric import zeros, Float
160    import sys
161
162       
163    timestep = float(sys.maxint)
164
165    stage    = domain.quantities['stage']
166    xmom     = domain.quantities['xmomentum']
167    bed      = domain.quantities['elevation']
168    height   = domain.quantities['height']
169    velocity = domain.quantities['velocity']
170
171
172    from comp_flux_vel_ext import compute_fluxes_vel_ext
173
174    domain.flux_timestep = compute_fluxes_vel_ext(timestep,domain,stage,xmom,bed,height,velocity)
175
176
[6042]177
[5845]178#--------------------------------------------------------------------------
179def distribute_to_vertices_and_edges_limit_w_u(domain):
180    """Distribution from centroids to vertices specific to the
181    shallow water wave
182    equation.
183
184    It will ensure that h (w-z) is always non-negative even in the
185    presence of steep bed-slopes by taking a weighted average between shallow
186    and deep cases.
187
188    In addition, all conserved quantities get distributed as per either a
189    constant (order==1) or a piecewise linear function (order==2).
190
191    FIXME: more explanation about removal of artificial variability etc
192
193    Precondition:
194      All quantities defined at centroids and bed elevation defined at
195      vertices.
196
197    Postcondition
198      Conserved quantities defined at vertices
199
200    """
201
202    #from config import optimised_gradient_limiter
203
204    #Remove very thin layers of water
205    #protect_against_infinitesimal_and_negative_heights(domain) 
206
207    import sys
208    from Numeric import zeros, Float
209    from config import epsilon, h0
210       
211    N = domain.number_of_elements
212
213    #Shortcuts
214    Stage = domain.quantities['stage']
215    Xmom = domain.quantities['xmomentum']
216    Bed = domain.quantities['elevation']
217    Height = domain.quantities['height']
218    Velocity = domain.quantities['velocity']
219
220    #Arrays   
221    w_C   = Stage.centroid_values   
222    uh_C  = Xmom.centroid_values   
223    z_C   = Bed.centroid_values
224    h_C   = Height.centroid_values
225    u_C   = Velocity.centroid_values
226       
227    #print id(h_C)
[6042]228##     for i in range(N):
229##      h_C[i] = w_C[i] - z_C[i]                                               
230##         if h_C[i] <= 1.0e-12:
231##          #print 'h_C[%d]= %15.5e\n' % (i,h_C[i])
232##          h_C[i] = 0.0
233##          w_C[i] = z_C[i]
234##          #uh_C[i] = 0.0
[5845]235           
[6042]236##  #           u_C[i] = 0.0
237##  #       else:
238##  #           u_C[i] = uh_C[i]/h_C[i]
[5845]239               
240    h0 = 1.0e-12   
[6042]241    for i in range(N):
242        h_C[i] = w_C[i] - z_C[i]                                               
[5845]243        if h_C[i] < 1.0e-12:
244            u_C[i] = 0.0  #Could have been negative
245            h_C[i] = 0.0
[6042]246            w_C[i] = z_C[i]
[5845]247        else:
[6042]248            #u_C[i]  = uh_C[i]/(h_C[i] + h0/h_C[i])
249            u_C[i]  = uh_C[i]/h_C[i]
[5845]250       
251    for name in [ 'velocity', 'stage' ]:
252        Q = domain.quantities[name]
253        if domain.order == 1:
254            Q.extrapolate_first_order()
255        elif domain.order == 2:
256            Q.extrapolate_second_order()
257        else:
258            raise 'Unknown order'
259
260    w_V  = domain.quantities['stage'].vertex_values                     
261    z_V  = domain.quantities['elevation'].vertex_values
262    h_V  = domain.quantities['height'].vertex_values
263    u_V  = domain.quantities['velocity'].vertex_values         
264    uh_V = domain.quantities['xmomentum'].vertex_values
265
266    h_V[:]    = w_V - z_V
267    for i in range(len(h_C)):
268        for j in range(2):
269            if h_V[i,j] < 0.0 :
270                #print 'h_V[%d,%d] = %f \n' % (i,j,h_V[i,j])                 
271                dh = h_V[i,j]
272                h_V[i,j] = 0.0
273                w_V[i,j] = z_V[i,j]
274                h_V[i,(j+1)%2] = h_V[i,(j+1)%2] + dh
275                w_V[i,(j+1)%2] = w_V[i,(j+1)%2] + dh
276               
277    uh_V[:] = u_V * h_V
278
279   
280    return
281
282#---------------------------------------------------------------------------
283def distribute_to_vertices_and_edges_limit_w_uh(domain):
284    """Distribution from centroids to vertices specific to the
285    shallow water wave equation.
286
287    In addition, all conserved quantities get distributed as per either a
288    constant (order==1) or a piecewise linear function (order==2).
289
290    Precondition:
291      All quantities defined at centroids and bed elevation defined at
292      vertices.
293
294    Postcondition
295      Conserved quantities defined at vertices
296
297    """
298
299    import sys
300    from Numeric import zeros, Float
301    from config import epsilon, h0
302       
303    N = domain.number_of_elements
304
305    #Shortcuts
306    Stage = domain.quantities['stage']
307    Xmom = domain.quantities['xmomentum']
308    Bed = domain.quantities['elevation']
309    Height = domain.quantities['height']
310    Velocity = domain.quantities['velocity']
311
312    #Arrays   
313    w_C   = Stage.centroid_values   
314    uh_C  = Xmom.centroid_values   
315    z_C   = Bed.centroid_values
316    h_C   = Height.centroid_values
317    u_C   = Velocity.centroid_values
318       
319
320    for i in range(N):
321        h_C[i] = w_C[i] - z_C[i]                                               
322        if h_C[i] <= 1.0e-6:
323            #print 'h_C[%d]= %15.5e\n' % (i,h_C[i])
324            h_C[i] = 0.0
325            w_C[i] = z_C[i]
326            uh_C[i] = 0.0
327           
328    for name in [ 'stage', 'xmomentum']:
329        Q = domain.quantities[name]
330        if domain.order == 1:
331            Q.extrapolate_first_order()
332        elif domain.order == 2:
333            Q.extrapolate_second_order()
334        else:
335            raise 'Unknown order'
336
337    w_V  = domain.quantities['stage'].vertex_values                     
338    z_V  = domain.quantities['elevation'].vertex_values
339    h_V  = domain.quantities['height'].vertex_values
340    u_V  = domain.quantities['velocity'].vertex_values         
341    uh_V = domain.quantities['xmomentum'].vertex_values
342
343    h_V[:]    = w_V - z_V
344
345    for i in range(len(h_C)):
346        for j in range(2):
347            if h_V[i,j] < 0.0 :
348                #print 'h_V[%d,%d] = %f \n' % (i,j,h_V[i,j])                 
349                dh = h_V[i,j]
350                h_V[i,j] = 0.0
351                w_V[i,j] = z_V[i,j]
352                h_V[i,(j+1)%2] = h_V[i,(j+1)%2] + dh
353                w_V[i,(j+1)%2] = w_V[i,(j+1)%2] + dh
354                u_V[i,j] = 0.0
355            if h_V[i,j] < h0:
356                u_V[i,j]
357            else:
358                u_V[i,j] = uh_V[i,j]/(h_V[i,j] + h0/h_V[i,j])
359
360
361#--------------------------------------------------------
362#Boundaries - specific to the shallow_water_vel_domain
363#--------------------------------------------------------
364class Reflective_boundary(Boundary):
365    """Reflective boundary returns same conserved quantities as
366    those present in its neighbour volume but reflected.
367
368    This class is specific to the shallow water equation as it
369    works with the momentum quantities assumed to be the second
370    and third conserved quantities.
371    """
372
373    def __init__(self, domain = None):
374        Boundary.__init__(self)
375
376        if domain is None:
377            msg = 'Domain must be specified for reflective boundary'
378            raise msg
379
380        #Handy shorthands
381        self.normals  = domain.normals
382        self.stage    = domain.quantities['stage'].vertex_values
383        self.xmom     = domain.quantities['xmomentum'].vertex_values
384        self.bed      = domain.quantities['elevation'].vertex_values
385        self.height   = domain.quantities['height'].vertex_values
386        self.velocity = domain.quantities['velocity'].vertex_values
387
388        from Numeric import zeros, Float
389        #self.conserved_quantities = zeros(3, Float)
390        self.evolved_quantities = zeros(5, Float)
391
392    def __repr__(self):
393        return 'Reflective_boundary'
394
395
396    def evaluate(self, vol_id, edge_id):
397        """Reflective boundaries reverses the outward momentum
398        of the volume they serve.
399        """
400
401        q = self.evolved_quantities
402        q[0] = self.stage[vol_id, edge_id]
403        q[1] = -self.xmom[vol_id, edge_id]
404        q[2] = self.bed[vol_id, edge_id]
405        q[3] = self.height[vol_id, edge_id]
406        q[4] = -self.velocity[vol_id, edge_id]
407
408        #print "In Reflective q ",q
409
410
411        return q
412
413class Dirichlet_boundary(Boundary):
414    """Dirichlet boundary returns constant values for the
415    conserved quantities
416    """
417
418
419    def __init__(self, evolved_quantities=None):
420        Boundary.__init__(self)
421
422        if evolved_quantities is None:
423            msg = 'Must specify one value for each evolved quantity'
424            raise msg
425
426        from Numeric import array, Float
427        self.evolved_quantities=array(evolved_quantities).astype(Float)
428
429    def __repr__(self):
430        return 'Dirichlet boundary (%s)' %self.evolved_quantities
431
432    def evaluate(self, vol_id=None, edge_id=None):
433        return self.evolved_quantities
434
435
436#----------------------------
437#Standard forcing terms:
438#---------------------------
439def gravity(domain):
440    """Apply gravitational pull in the presence of bed slope
441    """
442
443    from util import gradient
444    from Numeric import zeros, Float, array, sum
445
446
447
448    Stage     = domain.quantities['stage']
449    Xmom      = domain.quantities['xmomentum']
450    Elevation = domain.quantities['elevation']
451    Height    = domain.quantities['height']
452
453    xmom_ud  = Xmom.explicit_update
454    #stage_ud = Stage.explicit_update
455
456
457    #h = Stage.vertex_values - Elevation.vertex_values
458    h = Height.vertex_values
459    b = Elevation.vertex_values
460    w = Stage.vertex_values
461
462    x = domain.get_vertex_coordinates()
463    g = domain.g
464
465    for k in range(domain.number_of_elements):
466        avg_h = 0.5*(h[k,0] + h[k,1])
467
468        #Compute bed slope
469        x0, x1 = x[k,:]
470        b0, b1 = b[k,:]
471        bx = gradient(x0, x1, b0, b1)
472       
473        #Update momentum (explicit update is reset to source values)
474        xmom_ud[k] += -g*bx*avg_h
475        #stage_ud[k] = 0.0
476 
477 
478def manning_friction(domain):
479    """Apply (Manning) friction to water momentum
480    """
481
482    from math import sqrt
483
484    w = domain.quantities['stage'].centroid_values
485    z = domain.quantities['elevation'].centroid_values
486    h = w-z
487
488    uh = domain.quantities['xmomentum'].centroid_values
489    #vh = domain.quantities['ymomentum'].centroid_values
490    eta = domain.quantities['friction'].centroid_values
491
492    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
493    #ymom_update = domain.quantities['ymomentum'].semi_implicit_update
494
495    N = domain.number_of_elements
496    eps = domain.minimum_allowed_height
497    g = domain.g
498
499    for k in range(N):
500        if eta[k] >= eps:
501            if h[k] >= eps:
502                #S = -g * eta[k]**2 * sqrt((uh[k]**2 + vh[k]**2))
503                S = -g * eta[k]**2 * uh[k]
504                S /= h[k]**(7.0/3)
505
506                #Update momentum
507                xmom_update[k] += S*uh[k]
508                #ymom_update[k] += S*vh[k]
509
510def linear_friction(domain):
511    """Apply linear friction to water momentum
512
513    Assumes quantity: 'linear_friction' to be present
514    """
515
516    from math import sqrt
517
518    w = domain.quantities['stage'].centroid_values
519    z = domain.quantities['elevation'].centroid_values
520    h = w-z
521
522    uh = domain.quantities['xmomentum'].centroid_values
523    tau = domain.quantities['linear_friction'].centroid_values
524
525    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
526
527    N = domain.number_of_elements
528    eps = domain.minimum_allowed_height
529
530    for k in range(N):
531        if tau[k] >= eps:
532            if h[k] >= eps:
533                S = -tau[k]/h[k]
534
535                #Update momentum
536                xmom_update[k] += S*uh[k]
537
538
539
540def check_forcefield(f):
541    """Check that f is either
542    1: a callable object f(t,x,y), where x and y are vectors
543       and that it returns an array or a list of same length
544       as x and y
545    2: a scalar
546    """
547
548    from Numeric import ones, Float, array
549
550
551    if callable(f):
552        #N = 3
553        N = 2
554        #x = ones(3, Float)
555        #y = ones(3, Float)
556        x = ones(2, Float)
557        #y = ones(2, Float)
558       
559        try:
560            #q = f(1.0, x=x, y=y)
561            q = f(1.0, x=x)
562        except Exception, e:
563            msg = 'Function %s could not be executed:\n%s' %(f, e)
564            #FIXME: Reconsider this semantics
565            raise msg
566
567        try:
568            q = array(q).astype(Float)
569        except:
570            msg = 'Return value from vector function %s could ' %f
571            msg += 'not be converted into a Numeric array of floats.\n'
572            msg += 'Specified function should return either list or array.'
573            raise msg
574
575        #Is this really what we want?
576        msg = 'Return vector from function %s ' %f
577        msg += 'must have same lenght as input vectors'
578        assert len(q) == N, msg
579
580    else:
581        try:
582            f = float(f)
583        except:
584            msg = 'Force field %s must be either a scalar' %f
585            msg += ' or a vector function'
586            raise msg
587    return f
588
Note: See TracBrowser for help on using the repository browser.