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

Last change on this file since 7839 was 7825, checked in by steve, 15 years ago

anuga_1d works with numeric on 64bit machine

File size: 17.3 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 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
177
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)
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
235           
236##  #           u_C[i] = 0.0
237##  #       else:
238##  #           u_C[i] = uh_C[i]/h_C[i]
239               
240    h0 = 1.0e-12   
241    for i in range(N):
242        h_C[i] = w_C[i] - z_C[i]                                               
243        if h_C[i] < 1.0e-12:
244            u_C[i] = 0.0  #Could have been negative
245            h_C[i] = 0.0
246            w_C[i] = z_C[i]
247        else:
248            #u_C[i]  = uh_C[i]/(h_C[i] + h0/h_C[i])
249            u_C[i]  = uh_C[i]/h_C[i]
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    #print w_V
267    #print z_V
268   
269    h_V[:,:]    = w_V - z_V
270    for i in range(len(h_C)):
271        for j in range(2):
272            if h_V[i,j] < 0.0 :
273                #print 'h_V[%d,%d] = %f \n' % (i,j,h_V[i,j])                 
274                dh = h_V[i,j]
275                h_V[i,j] = 0.0
276                w_V[i,j] = z_V[i,j]
277                h_V[i,(j+1)%2] = h_V[i,(j+1)%2] + dh
278                w_V[i,(j+1)%2] = w_V[i,(j+1)%2] + dh
279               
280    uh_V[:,:] = u_V * h_V
281
282   
283    return
284
285#---------------------------------------------------------------------------
286def distribute_to_vertices_and_edges_limit_w_uh(domain):
287    """Distribution from centroids to vertices specific to the
288    shallow water wave equation.
289
290    In addition, all conserved quantities get distributed as per either a
291    constant (order==1) or a piecewise linear function (order==2).
292
293    Precondition:
294      All quantities defined at centroids and bed elevation defined at
295      vertices.
296
297    Postcondition
298      Conserved quantities defined at vertices
299
300    """
301
302    import sys
303    from Numeric import zeros, Float
304    from config import epsilon, h0
305       
306    N = domain.number_of_elements
307
308    #Shortcuts
309    Stage = domain.quantities['stage']
310    Xmom = domain.quantities['xmomentum']
311    Bed = domain.quantities['elevation']
312    Height = domain.quantities['height']
313    Velocity = domain.quantities['velocity']
314
315    #Arrays   
316    w_C   = Stage.centroid_values   
317    uh_C  = Xmom.centroid_values   
318    z_C   = Bed.centroid_values
319    h_C   = Height.centroid_values
320    u_C   = Velocity.centroid_values
321       
322
323    for i in range(N):
324        h_C[i] = w_C[i] - z_C[i]                                               
325        if h_C[i] <= 1.0e-6:
326            #print 'h_C[%d]= %15.5e\n' % (i,h_C[i])
327            h_C[i] = 0.0
328            w_C[i] = z_C[i]
329            uh_C[i] = 0.0
330           
331    for name in [ 'stage', 'xmomentum']:
332        Q = domain.quantities[name]
333        if domain.order == 1:
334            Q.extrapolate_first_order()
335        elif domain.order == 2:
336            Q.extrapolate_second_order()
337        else:
338            raise 'Unknown order'
339
340    w_V  = domain.quantities['stage'].vertex_values                     
341    z_V  = domain.quantities['elevation'].vertex_values
342    h_V  = domain.quantities['height'].vertex_values
343    u_V  = domain.quantities['velocity'].vertex_values         
344    uh_V = domain.quantities['xmomentum'].vertex_values
345
346    h_V[:,:]    = w_V - z_V
347
348    for i in range(len(h_C)):
349        for j in range(2):
350            if h_V[i,j] < 0.0 :
351                #print 'h_V[%d,%d] = %f \n' % (i,j,h_V[i,j])                 
352                dh = h_V[i,j]
353                h_V[i,j] = 0.0
354                w_V[i,j] = z_V[i,j]
355                h_V[i,(j+1)%2] = h_V[i,(j+1)%2] + dh
356                w_V[i,(j+1)%2] = w_V[i,(j+1)%2] + dh
357                u_V[i,j] = 0.0
358            if h_V[i,j] < h0:
359                u_V[i,j]
360            else:
361                u_V[i,j] = uh_V[i,j]/(h_V[i,j] + h0/h_V[i,j])
362
363
364#--------------------------------------------------------
365#Boundaries - specific to the shallow_water_vel_domain
366#--------------------------------------------------------
367class Reflective_boundary(Boundary):
368    """Reflective boundary returns same conserved quantities as
369    those present in its neighbour volume but reflected.
370
371    This class is specific to the shallow water equation as it
372    works with the momentum quantities assumed to be the second
373    and third conserved quantities.
374    """
375
376    def __init__(self, domain = None):
377        Boundary.__init__(self)
378
379        if domain is None:
380            msg = 'Domain must be specified for reflective boundary'
381            raise msg
382
383        #Handy shorthands
384        self.normals  = domain.normals
385        self.stage    = domain.quantities['stage'].vertex_values
386        self.xmom     = domain.quantities['xmomentum'].vertex_values
387        self.bed      = domain.quantities['elevation'].vertex_values
388        self.height   = domain.quantities['height'].vertex_values
389        self.velocity = domain.quantities['velocity'].vertex_values
390
391        from Numeric import zeros, Float
392        #self.conserved_quantities = zeros(3, Float)
393        self.evolved_quantities = zeros(5, Float)
394
395    def __repr__(self):
396        return 'Reflective_boundary'
397
398
399    def evaluate(self, vol_id, edge_id):
400        """Reflective boundaries reverses the outward momentum
401        of the volume they serve.
402        """
403
404        q = self.evolved_quantities
405        q[0] = self.stage[vol_id, edge_id]
406        q[1] = -self.xmom[vol_id, edge_id]
407        q[2] = self.bed[vol_id, edge_id]
408        q[3] = self.height[vol_id, edge_id]
409        q[4] = -self.velocity[vol_id, edge_id]
410
411        #print "In Reflective q ",q
412
413
414        return q
415
416class Dirichlet_boundary(Boundary):
417    """Dirichlet boundary returns constant values for the
418    conserved quantities
419    """
420
421
422    def __init__(self, evolved_quantities=None):
423        Boundary.__init__(self)
424
425        if evolved_quantities is None:
426            msg = 'Must specify one value for each evolved quantity'
427            raise msg
428
429        from Numeric import array, Float
430        self.evolved_quantities=array(evolved_quantities).astype(Float)
431
432    def __repr__(self):
433        return 'Dirichlet boundary (%s)' %self.evolved_quantities
434
435    def evaluate(self, vol_id=None, edge_id=None):
436        return self.evolved_quantities
437
438
439#----------------------------
440#Standard forcing terms:
441#---------------------------
442def gravity(domain):
443    """Apply gravitational pull in the presence of bed slope
444    """
445
446    from util import gradient
447    from Numeric import zeros, Float, array, sum
448
449
450
451    Stage     = domain.quantities['stage']
452    Xmom      = domain.quantities['xmomentum']
453    Elevation = domain.quantities['elevation']
454    Height    = domain.quantities['height']
455
456    xmom_ud  = Xmom.explicit_update
457    #stage_ud = Stage.explicit_update
458
459
460    #h = Stage.vertex_values - Elevation.vertex_values
461    h = Height.vertex_values
462    b = Elevation.vertex_values
463    w = Stage.vertex_values
464
465    x = domain.get_vertex_coordinates()
466    g = domain.g
467
468    for k in range(domain.number_of_elements):
469        avg_h = 0.5*(h[k,0] + h[k,1])
470
471        #Compute bed slope
472        x0, x1 = x[k,:]
473        b0, b1 = b[k,:]
474        bx = gradient(x0, x1, b0, b1)
475       
476        #Update momentum (explicit update is reset to source values)
477        xmom_ud[k] += -g*bx*avg_h
478        #stage_ud[k] = 0.0
479 
480 
481def manning_friction(domain):
482    """Apply (Manning) friction to water momentum
483    """
484
485    from math import sqrt
486
487    w = domain.quantities['stage'].centroid_values
488    z = domain.quantities['elevation'].centroid_values
489    h = w-z
490
491    uh = domain.quantities['xmomentum'].centroid_values
492    #vh = domain.quantities['ymomentum'].centroid_values
493    eta = domain.quantities['friction'].centroid_values
494
495    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
496    #ymom_update = domain.quantities['ymomentum'].semi_implicit_update
497
498    N = domain.number_of_elements
499    eps = domain.minimum_allowed_height
500    g = domain.g
501
502    for k in range(N):
503        if eta[k] >= eps:
504            if h[k] >= eps:
505                #S = -g * eta[k]**2 * sqrt((uh[k]**2 + vh[k]**2))
506                S = -g * eta[k]**2 * uh[k]
507                S /= h[k]**(7.0/3)
508
509                #Update momentum
510                xmom_update[k] += S*uh[k]
511                #ymom_update[k] += S*vh[k]
512
513def linear_friction(domain):
514    """Apply linear friction to water momentum
515
516    Assumes quantity: 'linear_friction' to be present
517    """
518
519    from math import sqrt
520
521    w = domain.quantities['stage'].centroid_values
522    z = domain.quantities['elevation'].centroid_values
523    h = w-z
524
525    uh = domain.quantities['xmomentum'].centroid_values
526    tau = domain.quantities['linear_friction'].centroid_values
527
528    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
529
530    N = domain.number_of_elements
531    eps = domain.minimum_allowed_height
532
533    for k in range(N):
534        if tau[k] >= eps:
535            if h[k] >= eps:
536                S = -tau[k]/h[k]
537
538                #Update momentum
539                xmom_update[k] += S*uh[k]
540
541
542
543def check_forcefield(f):
544    """Check that f is either
545    1: a callable object f(t,x,y), where x and y are vectors
546       and that it returns an array or a list of same length
547       as x and y
548    2: a scalar
549    """
550
551    from Numeric import ones, Float, array
552
553
554    if callable(f):
555        #N = 3
556        N = 2
557        #x = ones(3, Float)
558        #y = ones(3, Float)
559        x = ones(2, Float)
560        #y = ones(2, Float)
561       
562        try:
563            #q = f(1.0, x=x, y=y)
564            q = f(1.0, x=x)
565        except Exception, e:
566            msg = 'Function %s could not be executed:\n%s' %(f, e)
567            #FIXME: Reconsider this semantics
568            raise msg
569
570        try:
571            q = array(q).astype(Float)
572        except:
573            msg = 'Return value from vector function %s could ' %f
574            msg += 'not be converted into a Numeric array of floats.\n'
575            msg += 'Specified function should return either list or array.'
576            raise msg
577
578        #Is this really what we want?
579        msg = 'Return vector from function %s ' %f
580        msg += 'must have same lenght as input vectors'
581        assert len(q) == N, msg
582
583    else:
584        try:
585            f = float(f)
586        except:
587            msg = 'Force field %s must be either a scalar' %f
588            msg += ' or a vector function'
589            raise msg
590    return f
591
Note: See TracBrowser for help on using the repository browser.