source: trunk/anuga_work/development/2010-projects/anuga_1d/pipe/pipe_domain.py @ 8177

Last change on this file since 8177 was 8177, checked in by paul, 14 years ago

First implementation of pressurised flow

File size: 15.2 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]
34eta                        mannings friction coefficient [to appear]
35
36The conserved quantities are A, Q
37--------------------------------------------------------------------------
38For details see e.g.
39Christopher Zoppou and Stephen Roberts,
40Catastrophic Collapse of Water Supply Reservoirs in Urban Areas,
41Journal of Hydraulic Engineering, vol. 127, No. 7 July 1999
42
43Add paper details here
44
45John Jakeman, Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou,
46Padarn Wilson, Geoscience Australia, 2008
47"""
48
49
50from anuga_1d.base.generic_domain import *
51import numpy
52
53
54#Shallow water pipe domain
55class Domain(Generic_domain):
56
57    def __init__(self, coordinates, boundary = None, tagged_elements = None):
58
59        conserved_quantities = ['area', 'discharge']
60        evolved_quantities = ['area', 'discharge', 'elevation', 'height', 'velocity','width','top','stage']
61        other_quantities = ['friction']
62        Generic_domain.__init__(self,
63                                coordinates = coordinates,
64                                boundary = boundary,
65                                conserved_quantities = conserved_quantities,
66                                evolved_quantities = evolved_quantities,
67                                other_quantities = other_quantities,
68                                tagged_elements = tagged_elements)
69       
70        from anuga_1d.config import minimum_allowed_height, g, h0
71        self.minimum_allowed_height = minimum_allowed_height
72        self.g = g
73        self.h0 = h0
74        self.setstageflag = False
75        self.discontinousb = False
76
77
78        # forcing terms gravity and boundary stress are included in the flux calculation
79        #self.forcing_terms.append(gravity)
80        #self.forcing_terms.append(boundary_stress)
81        #self.forcing_terms.append(manning_friction)
82       
83
84       
85        #Stored output
86        self.store = True
87        self.format = 'sww'
88        self.smooth = True
89
90       
91        #Reduction operation for get_vertex_values
92        from anuga_1d.base.util import mean
93        self.reduction = mean
94        #self.reduction = min  #Looks better near steep slopes
95
96        self.set_quantities_to_be_stored(['area','discharge'])
97
98        self.__doc__ = 'channel_domain'
99
100        self.check_integrity()
101
102
103    def check_integrity(self):
104
105        #Check that we are solving the shallow water wave equation
106
107        msg = 'First conserved quantity must be "area"'
108        assert self.conserved_quantities[0] == 'area', msg
109        msg = 'Second conserved quantity must be "discharge"'
110        assert self.conserved_quantities[1] == 'discharge', msg
111
112        msg = 'First evolved quantity must be "area"'
113        assert self.evolved_quantities[0] == 'area', msg
114        msg = 'Second evolved quantity must be "discharge"'
115        assert self.evolved_quantities[1] == 'discharge', msg
116        msg = 'Third evolved quantity must be "elevation"'
117        assert self.evolved_quantities[2] == 'elevation', msg
118        msg = 'Fourth evolved quantity must be "height"'
119        assert self.evolved_quantities[3] == 'height', msg
120        msg = 'Fifth evolved quantity must be "velocity"'
121        assert self.evolved_quantities[4] == 'velocity', msg
122        msg = 'Sixth evolved quantity must be "width"'
123        assert self.evolved_quantities[5] == 'width', msg
124        msg = 'Seventh evolved quantity must be "top"'
125        assert self.evolved_quantities[6] == 'top', msg
126        msg = 'Eighth evolved quantity must be "stage"'
127        assert self.evolved_quantities[7] == 'stage', msg
128
129        Generic_domain.check_integrity(self)
130
131    def compute_fluxes(self):
132        #Call correct module function
133        #(either from this module or C-extension)
134        compute_fluxes_pipe(self)
135
136    def distribute_to_vertices_and_edges(self):
137        #Call correct module function
138        #(either from this module or C-extension)
139        distribute_to_vertices_and_edges_limit_a_d(self)
140       
141
142#=============== End of Pipe Domain ===============================
143
144#-----------------------------------
145# Compute flux definition with pipe
146#-----------------------------------
147def compute_fluxes_pipe(domain):
148    import sys
149    timestep = float(sys.maxint)
150
151    area       = domain.quantities['area']
152    discharge  = domain.quantities['discharge']
153    bed        = domain.quantities['elevation']
154    height     = domain.quantities['height']
155    velocity   = domain.quantities['velocity']
156    width      = domain.quantities['width']
157    top        = domain.quantities['top']
158
159
160    from anuga_1d.pipe.pipe_domain_ext import compute_fluxes_pipe_ext
161    domain.flux_timestep = compute_fluxes_pipe_ext(timestep,domain,area,discharge,bed,height,velocity,width,top)
162
163#-----------------------------------------------------------------------
164# Distribute to verticies with stage reconstructed and then extrapolated
165#-----------------------------------------------------------------------
166def distribute_to_vertices_and_edges_limit_a_d(domain):
167   
168    #Remove very thin layers of water
169    #protect_against_infinitesimal_and_negative_heights(domain) 
170
171
172
173    import sys
174    from anuga_1d.config import epsilon, h0
175
176   
177    N = domain.number_of_elements
178
179    #Shortcuts
180    area      = domain.quantities['area']
181    discharge = domain.quantities['discharge']
182    bed       = domain.quantities['elevation']
183    height    = domain.quantities['height']
184    velocity  = domain.quantities['velocity']
185    width     = domain.quantities['width']
186    top       = domain.quantities['top']
187    stage     = domain.quantities['stage']
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
199    if domain.setstageflag:
200        a_C[:,] = (w_C-z_C)*b_C
201        domain.setstageflag = False
202
203    # Paul: Same for top?
204    if domain.discontinousb:
205        width.extrapolate_second_order()
206       
207
208    h0 = 1.0e-12
209
210    # Paul: How does top (i.e. t_C) affect these?
211    h_C[:] = numpy.where( (a_C>h0) | (b_C>h0), a_C/(b_C+h0/b_C), 0.0 )
212    u_C[:] = numpy.where( (a_C>h0) | (b_C>h0), d_C/(a_C+h0/a_C), 0.0 )
213
214    a_C[:] = numpy.where( (a_C>h0) | (b_C>h0), a_C, 0.0 )
215    b_C[:] = numpy.where( (a_C>h0) | (b_C>h0), b_C, 0.0 )
216    t_C[:] = numpy.where( (a_C>h0) | (b_C>h0), t_C, 0.0 )
217    d_C[:] = numpy.where( (a_C>h0) | (b_C>h0), d_C, 0.0 )
218
219    w_C[:] = h_C + z_C
220   
221               
222    # Paul: same for top?
223    bed.extrapolate_second_order()
224   
225    for name in ['velocity','stage']:
226        Q = domain.quantities[name]
227        if domain.order == 1:
228            Q.extrapolate_first_order()
229        elif domain.order == 2:
230            Q.extrapolate_second_order()
231        else:
232            raise 'Unknown order'
233
234
235    a_V  = area.vertex_values
236    w_V  = stage.vertex_values
237    z_V  = bed.vertex_values
238    h_V  = height.vertex_values
239    u_V  = velocity.vertex_values
240    d_V  = discharge.vertex_values
241    b_V  = width.vertex_values
242    t_V  = top.vertex_values
243
244
245    h_V[:] = w_V-z_V
246
247    w_V[:] = numpy.where(h_V > h0, w_V, z_V)
248    h_V[:] = numpy.where(h_V > h0, h_V, 0.0)
249    a_V[:] = b_V*h_V
250    d_V[:] = u_V*a_V
251
252   
253    return
254
255
256#--------------------------------------------------------
257#Boundaries - specific to the pipe_domain
258#--------------------------------------------------------
259class Reflective_boundary(Boundary):
260    """Reflective boundary returns same conserved quantities as
261    those present in its neighbour volume but reflected.
262
263    This class is specific to the shallow water equation as it
264    works with the momentum quantities assumed to be the second
265    and third conserved quantities.
266    """
267
268    def __init__(self, domain = None):
269        Boundary.__init__(self)
270
271        if domain is None:
272            msg = 'Domain must be specified for reflective boundary'
273            raise msg
274
275        #Handy shorthands
276        self.normals  = domain.normals
277        self.area     = domain.quantities['area'].vertex_values
278        self.discharge     = domain.quantities['discharge'].vertex_values
279        self.bed      = domain.quantities['elevation'].vertex_values
280        self.height   = domain.quantities['height'].vertex_values
281        self.velocity = domain.quantities['velocity'].vertex_values
282        self.width    = domain.quantities['width'].vertex_values
283        self.top    = domain.quantities['top'].vertex_values
284        self.stage    = domain.quantities['stage'].vertex_values
285
286        self.evolved_quantities = numpy.zeros(8, numpy.float)
287
288    def __repr__(self):
289        return 'Reflective_boundary'
290
291
292    def evaluate(self, vol_id, edge_id):
293        """Reflective boundaries reverses the outward momentum
294        of the volume they serve.
295        """
296
297        q = self.evolved_quantities
298        q[0] =  self.area[vol_id, edge_id]
299        q[1] = -self.discharge[vol_id, edge_id]
300        q[2] =  self.bed[vol_id, edge_id]
301        q[3] =  self.height[vol_id, edge_id]
302        q[4] = -self.velocity[vol_id, edge_id]
303        q[5] =  self.width[vol_id,edge_id]
304        q[6] =  self.top[vol_id,edge_id]
305        q[7] =  self.stage[vol_id,edge_id]
306
307        return q
308
309class Dirichlet_boundary(Boundary):
310    """Dirichlet boundary returns constant values for the
311    conserved quantities
312if k>5 and k<15:
313            print discharge_ud[k],-g*zx*avg_h*avg_b
314        discharge_ud[k] +=-g*zx*avg_h*avg_b    """
315
316
317    def __init__(self, evolved_quantities=None):
318        Boundary.__init__(self)
319
320        if evolved_quantities is None:
321            msg = 'Must specify one value for each evolved quantity'
322            raise msg
323
324        assert len(evolved_quantities) == 8
325
326        self.evolved_quantities=numpy.array(evolved_quantities,numpy.float)
327
328    def __repr__(self):
329        return 'Dirichlet boundary (%s)' %self.evolved_quantities
330
331    def evaluate(self, vol_id=None, edge_id=None):
332        return self.evolved_quantities
333
334
335#----------------------------
336#Standard forcing terms:
337#---------------------------
338def gravity(domain):
339    """Apply gravitational pull in the presence of bed slope
340    """
341
342    from util import gradient
343    from Numeric import zeros, Float, array, sum
344
345
346
347    Area      = domain.quantities['area']
348    Discharge  = domain.quantities['discharge']
349    Elevation = domain.quantities['elevation']
350    Height    = domain.quantities['height']
351    Width     = domain.quantities['width']
352    Top       = domain.quantities['top']
353
354    discharge_ud  = Discharge.explicit_update
355
356
357       
358    h = Height.vertex_values
359    b = Width.vertex_values
360    t = Top.vertex_values
361    a = Area.vertex_values
362    z = Elevation.vertex_values
363
364    x = domain.get_vertex_coordinates()
365    g = domain.g
366    for k in range(domain.number_of_elements):
367        avg_h = 0.5*(h[k,0] + h[k,1])
368        avg_b = 0.5*(b[k,0] + b[k,1])
369        avg_t = 0.5*(t[k,0] + t[k,1])
370       
371        #Compute bed slope
372        x0, x1 = x[k,:]
373        z0, z1 = z[k,:]
374        zx = gradient(x0, x1, z0, z1)
375       
376        #Update momentum (explicit update is reset to source values)
377        discharge_ud[k]+= -g*zx*avg_h*avg_b
378     
379
380def boundary_stress(domain):
381
382    from util import gradient
383    from Numeric import zeros, Float, array, sum
384
385    Area     = domain.quantities['area']
386    Discharge  = domain.quantities['discharge']
387    Elevation = domain.quantities['elevation']
388    Height    = domain.quantities['height']
389    Width     = domain.quantities['width']
390    Top       = domain.quantities['top']
391
392    discharge_ud  = Discharge.explicit_update
393 
394    h = Height.vertex_values
395    b = Width.vertex_values
396    t = Top.vertex_values
397    a = Area.vertex_values
398    z = Elevation.vertex_values
399
400    x = domain.get_vertex_coordinates()
401    g = domain.g
402
403    for k in range(domain.number_of_elements):
404        avg_h = 0.5*(h[k,0] + h[k,1])
405       
406
407        #Compute bed slope
408        x0, x1 = x[k,:]
409        b0, b1 = b[k,:]
410        bx = gradient(x0, x1, b0, b1)
411       
412        #Update momentum (explicit update is reset to source values)
413        discharge_ud[k] += 0.5*g*bx*avg_h*avg_h
414        #stage_ud[k] = 0.0
415 
416 
417def manning_friction(domain):
418    """Apply (Manning) friction to water momentum
419    """
420
421    from math import sqrt
422
423    w = domain.quantities['stage'].centroid_values
424    z = domain.quantities['elevation'].centroid_values
425    h = w-z
426
427    uh = domain.quantities['xmomentum'].centroid_values
428    eta = domain.quantities['friction'].centroid_values
429
430    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
431
432    N = domain.number_of_elements
433    eps = domain.minimum_allowed_height
434    g = domain.g
435
436    for k in range(N):
437        if eta[k] >= eps:
438            if h[k] >= eps:
439                #S = -g * eta[k]**2 * sqrt((uh[k]**2 + vh[k]**2))
440                S = -g * eta[k]**2 * uh[k]
441                S /= h[k]**(7.0/3)
442
443                #Update momentum
444                xmom_update[k] += S*uh[k]
445                #ymom_update[k] += S*vh[k]
446
447def linear_friction(domain):
448    """Apply linear friction to water momentum
449
450    Assumes quantity: 'linear_friction' to be present
451    """
452
453    from math import sqrt
454
455    w = domain.quantities['stage'].centroid_values
456    z = domain.quantities['elevation'].centroid_values
457    h = w-z
458
459    uh = domain.quantities['xmomentum'].centroid_values
460    tau = domain.quantities['linear_friction'].centroid_values
461
462    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
463
464    N = domain.number_of_elements
465    eps = domain.minimum_allowed_height
466
467    for k in range(N):
468        if tau[k] >= eps:
469            if h[k] >= eps:
470                S = -tau[k]/h[k]
471
472                #Update momentum
473                xmom_update[k] += S*uh[k]
474
475
476
477
478def linearb(domain):
479
480    bC = domain.quantities['width'].vertex_values
481   
482    for i in range(len(bC)-1):
483        temp= 0.5*(bC[i,1]+bC[i+1,0])
484        bC[i,1]=temp
485        bC[i+1,0]=temp
486
487
488
Note: See TracBrowser for help on using the repository browser.