source: anuga_work/development/anuga_1d/channel_domain.py @ 6038

Last change on this file since 6038 was 6038, checked in by wilson, 14 years ago

Adding files for Padarn to play with.

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