source: trunk/anuga_work/development/2010-projects/anuga_1d/sww/sww_domain.py @ 8192

Last change on this file since 8192 was 8192, checked in by steve, 14 years ago

Channel flow reconstruction fixed up (width was being cutoff)

File size: 15.2 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
45import numpy
46
47from anuga_1d.base.generic_domain import Generic_domain
48from sww_boundary_conditions import *
49from sww_forcing_terms import *
50
51#Shallow water domain
52class Domain(Generic_domain):
53
54    def __init__(self, coordinates, boundary = None, tagged_elements = None):
55
56        conserved_quantities = ['stage', 'xmomentum']
57        evolved_quantities   = ['stage', 'xmomentum', 'elevation', 'height', 'velocity']
58        other_quantities     = ['friction']
59        Generic_domain.__init__(self,
60                                coordinates          = coordinates,
61                                boundary             = boundary,
62                                conserved_quantities = conserved_quantities,
63                                evolved_quantities   = evolved_quantities,
64                                other_quantities     = other_quantities,
65                                tagged_elements      = tagged_elements)
66       
67        from anuga_1d.config import minimum_allowed_height, g, h0
68        self.minimum_allowed_height = minimum_allowed_height
69        self.g = g
70        self.h0 = h0
71
72        #forcing terms
73        self.forcing_terms.append(gravity)
74        #self.forcing_terms.append(manning_friction)
75       
76       
77        #Stored output
78        self.store = True
79        self.format = 'sww'
80        self.smooth = True
81
82        #Evolve parametrs
83        self.cfl = 1.0
84       
85        #Reduction operation for get_vertex_values
86        from anuga_1d.base.util import mean
87        self.reduction = mean
88        #self.reduction = min  #Looks better near steep slopes
89
90        self.quantities_to_be_stored = ['stage','xmomentum']
91
92        self.__doc__ = 'sww_domain'
93
94
95    def set_quantities_to_be_stored(self, q):
96        """Specify which quantities will be stored in the sww file.
97
98        q must be either:
99          - the name of a quantity
100          - a list of quantity names
101          - None
102
103        In the two first cases, the named quantities will be stored at each
104        yieldstep
105        (This is in addition to the quantities elevation and friction) 
106        If q is None, storage will be switched off altogether.
107        """
108
109
110        if q is None:
111            self.quantities_to_be_stored = []           
112            self.store = False
113            return
114
115        if isinstance(q, basestring):
116            q = [q] # Turn argument into a list
117
118        #Check correcness   
119        for quantity_name in q:
120            msg = 'Quantity %s is not a valid conserved quantity' %quantity_name
121            assert quantity_name in self.conserved_quantities, msg
122       
123        self.quantities_to_be_stored = q
124       
125
126
127    def check_integrity(self):
128        Generic_Domain.check_integrity(self)
129        #Check that we are solving the shallow water wave equation
130
131        msg = 'First conserved quantity must be "stage"'
132        assert self.conserved_quantities[0] == 'stage', msg
133        msg = 'Second conserved quantity must be "xmomentum"'
134        assert self.conserved_quantities[1] == 'xmomentum', msg
135
136
137
138    def compute_fluxes(self):
139        #Call correct module function
140        #(either from this module or C-extension)
141
142        import sys
143
144
145        timestep = float(sys.maxint)
146
147        stage    = self.quantities['stage']
148        xmom     = self.quantities['xmomentum']
149        bed      = self.quantities['elevation']
150        height   = self.quantities['height']
151        velocity = self.quantities['velocity']
152
153
154        from anuga_1d.sww.sww_comp_flux_ext import compute_fluxes_ext_short
155
156        #self.flux_timestep = compute_fluxes_ext(timestep,self,stage,xmom,bed,height,velocity)
157
158   
159        self.flux_timestep = compute_fluxes_ext_short(timestep,self,stage,xmom,bed)
160
161
162
163    def distribute_to_vertices_and_edges(self):
164        #Call correct module function
165        #(either from this module or C-extension)
166        distribute_to_vertices_and_edges(self)
167       
168    def evolve(self, yieldstep = None, finaltime = None, duration = None,
169               skip_initial_step = False):
170        """Specialisation of basic evolve method from parent class
171        """
172
173        #Call basic machinery from parent class
174        for t in Generic_domain.evolve(self, yieldstep, finaltime,duration,
175                                       skip_initial_step):
176
177            #Pass control on to outer loop for more specific actions
178            yield(t)
179
180
181
182
183
184#=============== End of Shallow Water Domain ===============================
185
186
187# Module functions for gradient limiting (distribute_to_vertices_and_edges)
188
189def distribute_to_vertices_and_edges(domain):
190    """Distribution from centroids to vertices specific to the
191    shallow water wave
192    equation.
193
194    It will ensure that h (w-z) is always non-negative even in the
195    presence of steep bed-slopes by taking a weighted average between shallow
196    and deep cases.
197
198    In addition, all conserved quantities get distributed as per either a
199    constant (order==1) or a piecewise linear function (order==2).
200
201    FIXME: more explanation about removal of artificial variability etc
202
203    Precondition:
204      All quantities defined at centroids and bed elevation defined at
205      vertices.
206
207    Postcondition
208      Conserved quantities defined at vertices
209
210    """
211
212    #from config import optimised_gradient_limiter
213
214    #Remove very thin layers of water
215    #protect_against_infinitesimal_and_negative_heights(domain) 
216
217    import sys
218    from anuga_1d.config import epsilon, h0
219       
220    N = domain.number_of_elements
221
222    #Shortcuts
223    Stage      = domain.quantities['stage']
224    Xmom       = domain.quantities['xmomentum']
225    Bed        = domain.quantities['elevation']
226    Height     = domain.quantities['height']
227    Velocity   = domain.quantities['velocity']
228
229    #Arrays   
230    w_C   = Stage.centroid_values   
231    uh_C  = Xmom.centroid_values   
232    z_C   = Bed.centroid_values
233    h_C   = Height.centroid_values
234    u_C   = Velocity.centroid_values
235       
236    #Calculate height (and fix negatives)better be non-negative!
237    h_C[:] = w_C - z_C
238    u_C[:]  = uh_C/(h_C + h0/h_C)
239
240    #print 'domain.order', domain.order
241   
242    for name in [ 'velocity', 'stage' ]:
243        Q = domain.quantities[name]
244        if domain.order == 1:
245            Q.extrapolate_first_order()
246        elif domain.order == 2:
247            #print "add extrapolate second order to shallow water"
248            #if name != 'height':
249            Q.extrapolate_second_order()
250            #Q.limit()
251        else:
252            raise 'Unknown order'
253
254
255    stage_V = domain.quantities['stage'].vertex_values                 
256    bed_V   = domain.quantities['elevation'].vertex_values     
257    h_V     = domain.quantities['height'].vertex_values
258    u_V     = domain.quantities['velocity'].vertex_values
259    xmom_V  = domain.quantities['xmomentum'].vertex_values
260
261    h_V[:,:]    = stage_V - bed_V
262
263
264    #print 'any' ,numpy.any( h_V[:,0] < 0.0)
265    #print 'any' ,numpy.any( h_V[:,1] < 0.0)
266
267
268    h_0 = numpy.where(h_V[:,0] < 0.0, 0.0, h_V[:,0])
269    h_1 = numpy.where(h_V[:,0] < 0.0, h_V[:,1]+h_V[:,0], h_V[:,1])
270
271    h_V[:,0] = h_0
272    h_V[:,1] = h_1
273
274
275    h_0 = numpy.where(h_V[:,1] < 0.0, h_V[:,1]+h_V[:,0], h_V[:,0])
276    h_1 = numpy.where(h_V[:,1] < 0.0, 0.0, h_V[:,1])
277
278    h_V[:,0] = h_0
279    h_V[:,1] = h_1
280   
281    #print 'any' ,numpy.any( h_V[:,0] < 0.0)
282    #print 'any' ,numpy.any( h_V[:,1] < 0.0)
283
284    #h00 = 1e-12
285    #print h00
286
287    h_V[:,:] = numpy.where (h_V <= h0, 0.0, h_V)
288    u_V[:,:] = numpy.where (h_V <= h0, 0.0, u_V)
289
290#    # protect from edge values going negative
291#    h_V[:,1] = numpy.where(h_V[:,0] < 0.0 , h_V[:,1]-h_V[:,0], h_V[:,1])
292#    h_V[:,0] = numpy.where(h_V[:,0] < 0.0 , 0.0, h_V[:,0])
293#
294#    h_V[:,0] = numpy.where(h_V[:,1] < 0.0 , h_V[:,0]-h_V[:,1], h_V[:,0])
295#    h_V[:,1] = numpy.where(h_V[:,1] < 0.0 , 0.0, h_V[:,1])
296#
297#
298    #stage_V[:,:] = bed_V + h_V
299    bed_V[:,:] = stage_V - h_V
300    xmom_V[:,:] = u_V * h_V
301   
302    return
303#
304
305
306
307
308
309
310
311#
312def protect_against_infinitesimal_and_negative_heights(domain):
313    """Protect against infinitesimal heights and associated high velocities
314    """
315
316    #Shortcuts
317    wc    = domain.quantities['stage'].centroid_values
318    zc    = domain.quantities['elevation'].centroid_values
319    xmomc = domain.quantities['xmomentum'].centroid_values
320    hc = wc - zc  #Water depths at centroids
321
322    zv = domain.quantities['elevation'].vertex_values
323    wv = domain.quantities['stage'].vertex_values
324    hv = wv-zv
325    xmomv = domain.quantities['xmomentum'].vertex_values
326    #remove the above two lines and corresponding code below
327
328    #Update
329    #FIXME replace with numpy.where
330    for k in range(domain.number_of_elements):
331
332        if hc[k] < domain.minimum_allowed_height:
333            #Control stage
334            if hc[k] < domain.epsilon:
335                wc[k] = zc[k] # Contain 'lost mass' error
336                wv[k,0] = zv[k,0]
337                wv[k,1] = zv[k,1]
338               
339            xmomc[k] = 0.0
340           
341        #N = domain.number_of_elements
342        #if (k == 0) | (k==N-1):
343        #    wc[k] = zc[k] # Contain 'lost mass' error
344        #    wv[k,0] = zv[k,0]
345        #    wv[k,1] = zv[k,1]
346   
347def h_limiter(domain):
348    """Limit slopes for each volume to eliminate artificial variance
349    introduced by e.g. second order extrapolator
350
351    limit on h = w-z
352
353    This limiter depends on two quantities (w,z) so it resides within
354    this module rather than within quantity.py
355    """
356
357    N = domain.number_of_elements
358    beta_h = domain.beta_h
359
360    #Shortcuts
361    wc = domain.quantities['stage'].centroid_values
362    zc = domain.quantities['elevation'].centroid_values
363    hc = wc - zc
364
365    wv = domain.quantities['stage'].vertex_values
366    zv = domain.quantities['elevation'].vertex_values
367    hv = wv-zv
368
369    hvbar = zeros(hv.shape, numpy.float) #h-limited values
370
371    #Find min and max of this and neighbour's centroid values
372    hmax = zeros(hc.shape, numpy.float)
373    hmin = zeros(hc.shape, numpy.float)
374
375    for k in range(N):
376        hmax[k] = hmin[k] = hc[k]
377        #for i in range(3):
378        for i in range(2):   
379            n = domain.neighbours[k,i]
380            if n >= 0:
381                hn = hc[n] #Neighbour's centroid value
382
383                hmin[k] = min(hmin[k], hn)
384                hmax[k] = max(hmax[k], hn)
385
386
387    #Diffences between centroids and maxima/minima
388    dhmax = hmax - hc
389    dhmin = hmin - hc
390
391    #Deltas between vertex and centroid values
392    dh = zeros(hv.shape, numpy.float)
393    #for i in range(3):
394    for i in range(2):
395        dh[:,i] = hv[:,i] - hc
396
397    #Phi limiter
398    for k in range(N):
399
400        #Find the gradient limiter (phi) across vertices
401        phi = 1.0
402        #for i in range(3):
403        for i in range(2):
404            r = 1.0
405            if (dh[k,i] > 0): r = dhmax[k]/dh[k,i]
406            if (dh[k,i] < 0): r = dhmin[k]/dh[k,i]
407
408            phi = min( min(r*beta_h, 1), phi )
409
410        #Then update using phi limiter
411        #for i in range(3):
412        for i in range(2):
413            hvbar[k,i] = hc[k] + phi*dh[k,i]
414
415    return hvbar
416
417def balance_deep_and_shallow(domain):
418    """Compute linear combination between stage as computed by
419    gradient-limiters limiting using w, and stage computed by
420    gradient-limiters limiting using h (h-limiter).
421    The former takes precedence when heights are large compared to the
422    bed slope while the latter takes precedence when heights are
423    relatively small.  Anything in between is computed as a balanced
424    linear combination in order to avoid numpyal disturbances which
425    would otherwise appear as a result of hard switching between
426    modes.
427
428    The h-limiter is always applied irrespective of the order.
429    """
430
431    #Shortcuts
432    wc = domain.quantities['stage'].centroid_values
433    zc = domain.quantities['elevation'].centroid_values
434    hc = wc - zc
435
436    wv = domain.quantities['stage'].vertex_values
437    zv = domain.quantities['elevation'].vertex_values
438    hv = wv-zv
439
440    #Limit h
441    hvbar = h_limiter(domain)
442
443    for k in range(domain.number_of_elements):
444        #Compute maximal variation in bed elevation
445        #  This quantitiy is
446        #    dz = max_i abs(z_i - z_c)
447        #  and it is independent of dimension
448        #  In the 1d case zc = (z0+z1)/2
449        #  In the 2d case zc = (z0+z1+z2)/3
450
451        dz = max(abs(zv[k,0]-zc[k]),
452                 abs(zv[k,1]-zc[k]))#,
453#                 abs(zv[k,2]-zc[k]))
454
455
456        hmin = min( hv[k,:] )
457
458        #Create alpha in [0,1], where alpha==0 means using the h-limited
459        #stage and alpha==1 means using the w-limited stage as
460        #computed by the gradient limiter (both 1st or 2nd order)
461
462        #If hmin > dz/2 then alpha = 1 and the bed will have no effect
463        #If hmin < 0 then alpha = 0 reverting to constant height above bed.
464
465        if dz > 0.0:
466            alpha = max( min( 2*hmin/dz, 1.0), 0.0 )
467        else:
468            #Flat bed
469            alpha = 1.0
470
471        alpha  = 0.0
472        #Let
473        #
474        #  wvi be the w-limited stage (wvi = zvi + hvi)
475        #  wvi- be the h-limited state (wvi- = zvi + hvi-)
476        #
477        #
478        #where i=0,1,2 denotes the vertex ids
479        #
480        #Weighted balance between w-limited and h-limited stage is
481        #
482        #  wvi := (1-alpha)*(zvi+hvi-) + alpha*(zvi+hvi)
483        #
484        #It follows that the updated wvi is
485        #  wvi := zvi + (1-alpha)*hvi- + alpha*hvi
486        #
487        # Momentum is balanced between constant and limited
488
489
490        #for i in range(3):
491        #    wv[k,i] = zv[k,i] + hvbar[k,i]
492
493        #return
494
495        if alpha < 1:
496
497            #for i in range(3):
498            for i in range(2):
499                wv[k,i] = zv[k,i] + (1.0-alpha)*hvbar[k,i] + alpha*hv[k,i]
500
501            #Momentums at centroids
502            xmomc = domain.quantities['xmomentum'].centroid_values
503#            ymomc = domain.quantities['ymomentum'].centroid_values
504
505            #Momentums at vertices
506            xmomv = domain.quantities['xmomentum'].vertex_values
507#           ymomv = domain.quantities['ymomentum'].vertex_values
508
509            # Update momentum as a linear combination of
510            # xmomc and ymomc (shallow) and momentum
511            # from extrapolator xmomv and ymomv (deep).
512            xmomv[k,:] = (1.0-alpha)*xmomc[k] + alpha*xmomv[k,:]
513#            ymomv[k,:] = (1-alpha)*ymomc[k] + alpha*ymomv[k,:]
514
515
516
Note: See TracBrowser for help on using the repository browser.