source: trunk/anuga_work/development/2010-projects/anuga_1d/channel/channel_domain.py @ 8209

Last change on this file since 8209 was 8209, checked in by steve, 13 years ago

Commiting quite a few changes to operators and parallel stuff

File size: 17.5 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 Generic_domain from module
6generic_domain.py
7consisting of methods specific to Channel flow using the Shallow Water Wave Equation
8
9This particular modification of the Domain class implements the ability to
10vary the width of the 1D channel that the water flows in. As a result the
11conserved variables are different than previous implementations and so are the
12equations.
13
14U_t + E_x = S
15
16where
17
18U = [A, Q] = [b*h, u*b*h]
19E = [Q, Q^2/A + g*b*h^2/2]
20S represents source terms forcing the system
21(e.g. gravity, boundary_stree, friction, wind stress, ...)
22gravity = -g*b*h*z_x
23boundary_stress = 1/2*g*b_x*h^2
24
25and _t, _x, _y denote the derivative with respect to t, x and y respectiely.
26
27The quantities are
28
29symbol    variable name    explanation
30A         area             Wetted area = b*h
31Q         discharge        flux of water = u*b*h
32x         x                horizontal distance from origin [m]
33z         elevation        elevation of bed on which flow is modelled [m]
34h         height           water height above z [m]
35w         stage            absolute water level, w = z+h [m]
36u                          speed in the x direction [m/s]
37uh        xmomentum        momentum in the x direction [m^2/s]
38b         width            width of channel
39eta                        mannings friction coefficient [to appear]
40nu                         wind stress coefficient [to appear]
41
42The conserved quantities are A, Q
43--------------------------------------------------------------------------
44For details see e.g.
45Christopher Zoppou and Stephen Roberts,
46Catastrophic Collapse of Water Supply Reservoirs in Urban Areas,
47Journal of Hydraulic Engineering, vol. 127, No. 7 July 1999
48
49
50John Jakeman, Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou,
51Padarn Wilson, Geoscience Australia, 2008
52"""
53
54
55from anuga_1d.base.generic_domain import *
56import numpy
57
58
59#Shallow water domain
60class Domain(Generic_domain):
61
62    def __init__(self, coordinates, boundary = None, tagged_elements = None):
63
64        conserved_quantities = ['area', 'discharge']
65        evolved_quantities = ['area', 'discharge', 'elevation', 'height', 'velocity','width','stage']
66        other_quantities = ['friction']
67        Generic_domain.__init__(self,
68                                coordinates = coordinates,
69                                boundary = boundary,
70                                conserved_quantities = conserved_quantities,
71                                evolved_quantities = evolved_quantities,
72                                other_quantities = other_quantities,
73                                tagged_elements = tagged_elements)
74       
75        from anuga_1d.config import minimum_allowed_height, g, h0
76        self.minimum_allowed_height = minimum_allowed_height
77        self.g = g
78        self.h0 = h0
79        self.setstageflag = False
80        self.discontinousb = False
81
82
83        # forcing terms gravity and boundary stress are included in the flux calculation
84        #self.forcing_terms.append(gravity)
85        #self.forcing_terms.append(boundary_stress)
86        #self.forcing_terms.append(manning_friction)
87       
88
89       
90        #Stored output
91        self.store = True
92        self.format = 'sww'
93        self.smooth = True
94
95       
96        #Reduction operation for get_vertex_values
97        from anuga_1d.base.util import mean
98        self.reduction = mean
99        #self.reduction = min  #Looks better near steep slopes
100
101        self.set_quantities_to_be_stored(['area','discharge'])
102
103        self.__doc__ = 'channel_domain'
104
105        self.check_integrity()
106
107
108    def check_integrity(self):
109
110        #Check that we are solving the shallow water wave equation
111
112        msg = 'First conserved quantity must be "area"'
113        assert self.conserved_quantities[0] == 'area', msg
114        msg = 'Second conserved quantity must be "discharge"'
115        assert self.conserved_quantities[1] == 'discharge', msg
116
117        msg = 'First evolved quantity must be "area"'
118        assert self.evolved_quantities[0] == 'area', msg
119        msg = 'Second evolved quantity must be "discharge"'
120        assert self.evolved_quantities[1] == 'discharge', msg
121        msg = 'Third evolved quantity must be "elevation"'
122        assert self.evolved_quantities[2] == 'elevation', msg
123        msg = 'Fourth evolved quantity must be "height"'
124        assert self.evolved_quantities[3] == 'height', msg
125        msg = 'Fifth evolved quantity must be "velocity"'
126        assert self.evolved_quantities[4] == 'velocity', msg
127        msg = 'Sixth evolved quantity must be "width"'
128        assert self.evolved_quantities[5] == 'width', msg
129        msg = 'Seventh evolved quantity must be "stage"'
130        assert self.evolved_quantities[6] == 'stage', msg
131
132        Generic_domain.check_integrity(self)
133
134    def compute_fluxes(self):
135        #Call correct module function
136        #(either from this module or C-extension)
137        compute_fluxes_channel(self)
138
139    def distribute_to_vertices_and_edges(self):
140        #Call correct module function
141        #(either from this module or C-extension)
142        #distribute_to_vertices_and_edges_limit_s_v_h(self)
143        distribute_to_vertices_and_edges_limit_s_v(self)
144       
145
146#=============== End of Channel Domain ===============================
147
148#-----------------------------------
149# Compute flux definition with channel
150#-----------------------------------
151def compute_fluxes_channel(domain):
152    import sys
153    timestep = float(sys.maxint)
154
155    area       = domain.quantities['area']
156    discharge  = domain.quantities['discharge']
157    bed        = domain.quantities['elevation']
158    height     = domain.quantities['height']
159    velocity   = domain.quantities['velocity']
160    width      = domain.quantities['width']
161
162
163    from anuga_1d.channel.channel_domain_ext import compute_fluxes_channel_ext
164    domain.flux_timestep = compute_fluxes_channel_ext(timestep,domain,area,discharge,bed,height,velocity,width)
165
166#-----------------------------------------------------------------------
167# Distribute to verticies with stage, velocity and channel geometry
168# reconstructed and then extrapolated.
169#-----------------------------------------------------------------------
170def distribute_to_vertices_and_edges_limit_s_v(domain):
171    import sys
172    from anuga_1d.config import epsilon, h0
173
174    N = domain.number_of_elements
175
176    #Shortcuts
177    area      = domain.quantities['area']
178    discharge = domain.quantities['discharge']
179    bed       = domain.quantities['elevation']
180    height    = domain.quantities['height']
181    velocity  = domain.quantities['velocity']
182    width     = domain.quantities['width']
183    stage     = domain.quantities['stage']
184
185    #Arrays   
186    a_C   = area.centroid_values
187    d_C   = discharge.centroid_values
188    z_C   = bed.centroid_values
189    h_C   = height.centroid_values
190    u_C   = velocity.centroid_values
191    b_C   = width.centroid_values
192    w_C   = stage.centroid_values
193
194    # Calculate height, velocity and stage.
195    # Here we assume the conserved quantities and the channel geometry
196    # (i.e. bed and width) have been accurately computed in the previous
197    # timestep.
198    h_C[:] = numpy.where(a_C > 0.0, a_C/(b_C + h0/b_C), 0.0)
199    u_C[:] = numpy.where(a_C > 0.0, d_C/(a_C + h0/a_C), 0.0)
200
201    w_C[:] = h_C + z_C
202
203    # Extrapolate velocity and stage as well as channel geometry.
204    for name in ['velocity', 'stage', 'elevation', 'width']:
205        Q = domain.quantities[name]
206        if domain.order == 1:
207            Q.extrapolate_first_order()
208        elif domain.order == 2:
209            Q.extrapolate_second_order()
210        else:
211            raise 'Unknown order'
212
213    # Stage, bed, width and velocity have been extrapolated
214    w_V  = stage.vertex_values
215    u_V  = velocity.vertex_values
216    z_V  = bed.vertex_values
217    b_V  = width.vertex_values
218
219    # These quantites need to update vertex_values
220    a_V  = area.vertex_values
221    h_V  = height.vertex_values
222    d_V  = discharge.vertex_values
223
224    # Calculate height and fix up negatives. The main idea here is
225    # fix up the wet/dry interface.
226    h_V[:,:] = w_V - z_V
227
228    h_0 = numpy.where(h_V[:,0] < 0.0, 0.0, h_V[:,0])
229    h_1 = numpy.where(h_V[:,0] < 0.0, h_V[:,1]+h_V[:,0], h_V[:,1])
230
231    h_V[:,0] = h_0
232    h_V[:,1] = h_1
233
234
235    h_0 = numpy.where(h_V[:,1] < 0.0, h_V[:,1]+h_V[:,0], h_V[:,0])
236    h_1 = numpy.where(h_V[:,1] < 0.0, 0.0, h_V[:,1])
237
238    h_V[:,0] = h_0
239    h_V[:,1] = h_1
240
241
242    # Protect against negative and small heights. If we set h to zero
243    # we better do the same with velocity (i.e. no water, no velocity).
244    h_V[:,:] = numpy.where (h_V <= h0, 0.0, h_V)
245    u_V[:,:] = numpy.where (h_V <= h0, 0.0, u_V)
246
247
248    # Clean up conserved quantities
249    w_V[:] = z_V + h_V
250    a_V[:] = b_V * h_V
251    d_V[:] = u_V * a_V
252
253   
254    return
255
256#-----------------------------------------------------------------------
257# Distribute to verticies with stage, height and velocity reconstructed
258# and then extrapolated.
259# In this method, we extrapolate the stage and height out to the vertices.
260# The bed, although given as initial data to the problem, is reconstructed
261# from the stage and height. This ensures consistency of the reconstructed
262# quantities (i.e. w = z + h) as well as protecting against negative
263# heights.
264#-----------------------------------------------------------------------
265def distribute_to_vertices_and_edges_limit_s_v_h(domain):
266    import sys
267    from anuga_1d.config import epsilon, h0
268
269    N = domain.number_of_elements
270
271    #Shortcuts
272    area      = domain.quantities['area']
273    discharge = domain.quantities['discharge']
274    bed       = domain.quantities['elevation']
275    height    = domain.quantities['height']
276    velocity  = domain.quantities['velocity']
277    width     = domain.quantities['width']
278    stage     = domain.quantities['stage']
279
280    #Arrays   
281    a_C   = area.centroid_values
282    d_C   = discharge.centroid_values
283    z_C   = bed.centroid_values
284    h_C   = height.centroid_values
285    u_C   = velocity.centroid_values
286    b_C   = width.centroid_values
287    w_C   = stage.centroid_values
288
289    # Construct h,u,w from the conserved quantities after protecting
290    # conserved quantities from becoming too small.
291    a_C[:] = numpy.where( (a_C>h0), a_C, 0.0 )
292    d_C[:] = numpy.where( (a_C>h0), d_C, 0.0 )
293    h_C[:] = numpy.where( (b_C>h0), a_C/(b_C + h0/b_C), 0.0 )
294    u_C[:] = numpy.where( (a_C>h0), d_C/(a_C + h0/a_C), 0.0 )   
295
296    # Set the stage
297    w_C[:] = h_C + z_C         
298
299    # Extrapolate "fundamental" quantities.
300    # All other quantities will be reconstructed from these.
301    for name in ['velocity', 'stage',  'height', 'width']:
302        Q = domain.quantities[name]
303        if domain.order == 1:
304            Q.extrapolate_first_order()
305        elif domain.order == 2:
306            Q.extrapolate_second_order()
307        else:
308            raise 'Unknown order'
309
310    # These quantities have been extrapolated.
311    u_V  = velocity.vertex_values
312    w_V  = stage.vertex_values
313    h_V  = height.vertex_values
314    b_V  = width.vertex_values
315
316    # These need to be reconstructed
317    a_V  = area.vertex_values
318    d_V  = discharge.vertex_values
319    z_V  = bed.vertex_values
320
321    # Reconstruct bed from stage and height.
322    z_V[:] = w_V-h_V
323
324    # Now reconstruct our conserved quantities from the above
325    # reconstructed quantities.
326    a_V[:] = b_V*h_V
327    d_V[:] = u_V*a_V
328
329    return
330
331
332#--------------------------------------------------------
333#Boundaries - specific to the channel_domain
334#--------------------------------------------------------
335class Reflective_boundary(Boundary):
336    """Reflective boundary returns same conserved quantities as
337    those present in its neighbour volume but reflected.
338
339    This class is specific to the shallow water equation as it
340    works with the momentum quantities assumed to be the second
341    and third conserved quantities.
342    """
343
344    def __init__(self, domain = None):
345        Boundary.__init__(self)
346
347        if domain is None:
348            msg = 'Domain must be specified for reflective boundary'
349            raise msg
350
351        #Handy shorthands
352        self.normals  = domain.normals
353        self.area     = domain.quantities['area'].vertex_values
354        self.discharge     = domain.quantities['discharge'].vertex_values
355        self.bed      = domain.quantities['elevation'].vertex_values
356        self.height   = domain.quantities['height'].vertex_values
357        self.velocity = domain.quantities['velocity'].vertex_values
358        self.width    = domain.quantities['width'].vertex_values
359        self.stage    = domain.quantities['stage'].vertex_values
360
361        self.evolved_quantities = numpy.zeros(7, numpy.float)
362
363    def __repr__(self):
364        return 'Reflective_boundary'
365
366
367    def evaluate(self, vol_id, edge_id):
368        """Reflective boundaries reverses the outward momentum
369        of the volume they serve.
370        """
371
372        q = self.evolved_quantities
373        q[0] =  self.area[vol_id, edge_id]
374        q[1] = -self.discharge[vol_id, edge_id]
375        q[2] =  self.bed[vol_id, edge_id]
376        q[3] =  self.height[vol_id, edge_id]
377        q[4] = -self.velocity[vol_id, edge_id]
378        q[5] =  self.width[vol_id,edge_id]
379        q[6] =  self.stage[vol_id,edge_id]
380
381        return q
382
383class Dirichlet_boundary(Boundary):
384    """Dirichlet boundary returns constant values for the
385    conserved quantities
386if k>5 and k<15:
387            print discharge_ud[k],-g*zx*avg_h*avg_b
388        discharge_ud[k] +=-g*zx*avg_h*avg_b    """
389
390
391    def __init__(self, evolved_quantities=None):
392        Boundary.__init__(self)
393
394        if evolved_quantities is None:
395            msg = 'Must specify one value for each evolved quantity'
396            raise msg
397
398        assert len(evolved_quantities) == 7
399
400        self.evolved_quantities=numpy.array(evolved_quantities,numpy.float)
401
402    def __repr__(self):
403        return 'Dirichlet boundary (%s)' %self.evolved_quantities
404
405    def evaluate(self, vol_id=None, edge_id=None):
406        return self.evolved_quantities
407
408
409#----------------------------
410#Standard forcing terms:
411#---------------------------
412def gravity(domain):
413    """Apply gravitational pull in the presence of bed slope
414    """
415
416    from util import gradient
417    from Numeric import zeros, Float, array, sum
418
419
420
421    Area      = domain.quantities['area']
422    Discharge  = domain.quantities['discharge']
423    Elevation = domain.quantities['elevation']
424    Height    = domain.quantities['height']
425    Width     = domain.quantities['width']
426
427    discharge_ud  = Discharge.explicit_update
428
429
430       
431    h = Height.vertex_values
432    b = Width.vertex_values
433    a = Area.vertex_values
434    z = Elevation.vertex_values
435
436    x = domain.get_vertex_coordinates()
437    g = domain.g
438    for k in range(domain.number_of_elements):
439        avg_h = 0.5*(h[k,0] + h[k,1])
440        avg_b = 0.5*(b[k,0] + b[k,1])
441       
442        #Compute bed slope
443        x0, x1 = x[k,:]
444        z0, z1 = z[k,:]
445        zx = gradient(x0, x1, z0, z1)
446       
447        #Update momentum (explicit update is reset to source values)
448        discharge_ud[k]+= -g*zx*avg_h*avg_b
449     
450
451def boundary_stress(domain):
452
453    from util import gradient
454    from Numeric import zeros, Float, array, sum
455
456    Area     = domain.quantities['area']
457    Discharge  = domain.quantities['discharge']
458    Elevation = domain.quantities['elevation']
459    Height    = domain.quantities['height']
460    Width     = domain.quantities['width']
461
462    discharge_ud  = Discharge.explicit_update
463 
464    h = Height.vertex_values
465    b = Width.vertex_values
466    a = Area.vertex_values
467    z = Elevation.vertex_values
468
469    x = domain.get_vertex_coordinates()
470    g = domain.g
471
472    for k in range(domain.number_of_elements):
473        avg_h = 0.5*(h[k,0] + h[k,1])
474       
475
476        #Compute bed slope
477        x0, x1 = x[k,:]
478        b0, b1 = b[k,:]
479        bx = gradient(x0, x1, b0, b1)
480       
481        #Update momentum (explicit update is reset to source values)
482        discharge_ud[k] += 0.5*g*bx*avg_h*avg_h
483        #stage_ud[k] = 0.0
484 
485 
486def manning_friction(domain):
487    """Apply (Manning) friction to water momentum
488    """
489
490    from math import sqrt
491
492    w = domain.quantities['stage'].centroid_values
493    z = domain.quantities['elevation'].centroid_values
494    h = w-z
495
496    uh = domain.quantities['xmomentum'].centroid_values
497    eta = domain.quantities['friction'].centroid_values
498
499    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
500
501    N = domain.number_of_elements
502    eps = domain.minimum_allowed_height
503    g = domain.g
504
505    for k in range(N):
506        if eta[k] >= eps:
507            if h[k] >= eps:
508                #S = -g * eta[k]**2 * sqrt((uh[k]**2 + vh[k]**2))
509                S = -g * eta[k]**2 * uh[k]
510                S /= h[k]**(7.0/3)
511
512                #Update momentum
513                xmom_update[k] += S*uh[k]
514                #ymom_update[k] += S*vh[k]
515
516def linear_friction(domain):
517    """Apply linear friction to water momentum
518
519    Assumes quantity: 'linear_friction' to be present
520    """
521
522    from math import sqrt
523
524    w = domain.quantities['stage'].centroid_values
525    z = domain.quantities['elevation'].centroid_values
526    h = w-z
527
528    uh = domain.quantities['xmomentum'].centroid_values
529    tau = domain.quantities['linear_friction'].centroid_values
530
531    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
532
533    N = domain.number_of_elements
534    eps = domain.minimum_allowed_height
535
536    for k in range(N):
537        if tau[k] >= eps:
538            if h[k] >= eps:
539                S = -tau[k]/h[k]
540
541                #Update momentum
542                xmom_update[k] += S*uh[k]
543
544
545
546
547def linearb(domain):
548
549    bC = domain.quantities['width'].vertex_values
550   
551    for i in range(len(bC)-1):
552        temp= 0.5*(bC[i,1]+bC[i+1,0])
553        bC[i,1]=temp
554        bC[i+1,0]=temp
555
556
557
Note: See TracBrowser for help on using the repository browser.