source: anuga_work/development/pipeflow/sww_vel_domain.py @ 7840

Last change on this file since 7840 was 7829, checked in by steve, 15 years ago

Adding extended sww domain

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