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