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

Last change on this file was 8245, checked in by steve, 12 years ago

Moved shallow_water_balanced to anuga_work directory

File size: 19.9 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    def update_derived_quantites(self):
146
147        pass
148
149
150
151    def initialize_plotting(self,
152                            stage_lim = [-1.0, 40.0],
153                            width_lim = [-1.0, 10.0],
154                            velocity_lim = [-5.0, 5.0]):
155
156        import pylab
157        pylab.ion()
158
159
160        x = self.get_vertices().flatten()
161
162        z = self.quantities['elevation'].vertex_values.flatten()
163        w = self.quantities['stage'].vertex_values.flatten()
164        h = self.quantities['height'].vertex_values.flatten()
165        v = self.quantities['velocity'].vertex_values.flatten()
166        b = self.quantities['width'].vertex_values.flatten()
167
168        print x.shape
169        print z.shape
170
171        #-------------------------------
172        # Top plot
173        #-------------------------------
174        self.plot1 = pylab.subplot(311)
175
176        self.zplot, = pylab.plot(x, z, 'k')
177        self.wplot, = pylab.plot(x, w, 'k')
178
179        self.plot1.set_ylim(stage_lim)
180        #pylab.xlabel('Position')
181        pylab.ylabel('Bed/Stage')
182
183        #-------------------------------
184        # Middle Plot
185        #-------------------------------
186        self.plot2 = pylab.subplot(312)
187
188        self.bplot, = pylab.plot(x, b, 'k')
189
190        self.plot2.set_ylim(width_lim)
191        #pylab.xlabel('Position')
192        pylab.ylabel('Width')
193
194        #-------------------------------
195        # Bottom Plot
196        #-------------------------------
197        self.plot3 = pylab.subplot(313)
198
199        self.vplot, = pylab.plot(x, v, 'k')
200
201        self.plot3.set_ylim(velocity_lim)
202
203        pylab.xlabel('Position')
204        pylab.ylabel('Velocity')
205
206
207    def update_plotting(self):
208
209        import pylab
210
211        #x = self.get_vertices().flatten()
212        z = self.quantities['elevation'].vertex_values.flatten()
213        w = self.quantities['stage'].vertex_values.flatten()
214        h = self.quantities['height'].vertex_values.flatten()
215        v = self.quantities['velocity'].vertex_values.flatten()
216        b = self.quantities['width'].vertex_values.flatten()
217
218
219        self.zplot.set_ydata(z)
220        self.wplot.set_ydata(w)
221        self.bplot.set_ydata(b)
222        self.vplot.set_ydata(v)
223
224        pylab.draw()
225
226
227    def hold_plotting(self,save=None):
228
229        self.update_plotting()
230        import pylab
231       
232        pylab.ioff()
233
234        if save != None:
235            file = save+".pdf"
236            pylab.savefig(file)
237
238        pylab.show()
239
240
241
242    def finalize_plotting(self):
243
244        pass
245
246
247
248#=============== End of Channel Domain ===============================
249
250#-----------------------------------
251# Compute flux definition with channel
252#-----------------------------------
253def compute_fluxes_channel(domain):
254    import sys
255    timestep = float(sys.maxint)
256
257    area       = domain.quantities['area']
258    discharge  = domain.quantities['discharge']
259    bed        = domain.quantities['elevation']
260    height     = domain.quantities['height']
261    velocity   = domain.quantities['velocity']
262    width      = domain.quantities['width']
263
264
265    from anuga_1d.channel.channel_domain_ext import compute_fluxes_channel_ext
266    domain.flux_timestep = compute_fluxes_channel_ext(timestep,domain,area,discharge,bed,height,velocity,width)
267
268#-----------------------------------------------------------------------
269# Distribute to verticies with stage, velocity and channel geometry
270# reconstructed and then extrapolated.
271#-----------------------------------------------------------------------
272def distribute_to_vertices_and_edges_limit_s_v(domain):
273    import sys
274    from anuga_1d.config import epsilon, h0
275
276    N = domain.number_of_elements
277
278    #Shortcuts
279    area      = domain.quantities['area']
280    discharge = domain.quantities['discharge']
281    bed       = domain.quantities['elevation']
282    height    = domain.quantities['height']
283    velocity  = domain.quantities['velocity']
284    width     = domain.quantities['width']
285    stage     = domain.quantities['stage']
286
287    #Arrays   
288    a_C   = area.centroid_values
289    d_C   = discharge.centroid_values
290    z_C   = bed.centroid_values
291    h_C   = height.centroid_values
292    u_C   = velocity.centroid_values
293    b_C   = width.centroid_values
294    w_C   = stage.centroid_values
295
296    # Calculate height, velocity and stage.
297    # Here we assume the conserved quantities and the channel geometry
298    # (i.e. bed and width) have been accurately computed in the previous
299    # timestep.
300    h_C[:] = numpy.where(a_C > 0.0, a_C/b_C, 0.0)
301    u_C[:] = numpy.where(a_C > 0.0, d_C/a_C, 0.0)
302
303    w_C[:] = h_C + z_C
304
305    #print w_C
306
307    # Extrapolate velocity and stage as well as channel geometry.
308    for name in ['velocity', 'stage', 'elevation', 'width']:
309        Q = domain.quantities[name]
310        if domain.order == 1:
311            Q.extrapolate_first_order()
312        elif domain.order == 2:
313            Q.extrapolate_second_order()
314        else:
315            raise 'Unknown order'
316
317    # Stage, bed, width and velocity have been extrapolated
318    w_V  = stage.vertex_values
319    u_V  = velocity.vertex_values
320    z_V  = bed.vertex_values
321    b_V  = width.vertex_values
322
323    # Need to update these vertex_values
324    a_V  = area.vertex_values
325    h_V  = height.vertex_values
326    d_V  = discharge.vertex_values
327
328    # Calculate height and fix up negatives. The main idea here is
329    # fix up the wet/dry interface.
330    h_V[:,:] = w_V - z_V
331
332    h_0 = numpy.where(h_V[:,0] < 0.0, 0.0, h_V[:,0])
333    h_1 = numpy.where(h_V[:,0] < 0.0, h_V[:,1]+h_V[:,0], h_V[:,1])
334
335    h_V[:,0] = h_0
336    h_V[:,1] = h_1
337
338
339    h_0 = numpy.where(h_V[:,1] < 0.0, h_V[:,1]+h_V[:,0], h_V[:,0])
340    h_1 = numpy.where(h_V[:,1] < 0.0, 0.0, h_V[:,1])
341
342    h_V[:,0] = h_0
343    h_V[:,1] = h_1
344
345
346    # Protect against negative and small heights. If we set h to zero
347    # we better do the same with velocity (i.e. no water, no velocity).
348    h_V[:,:] = numpy.where (h_V <= h0, 0.0, h_V)
349    u_V[:,:] = numpy.where (h_V <= h0, 0.0, u_V)
350
351
352    # Clean up conserved quantities
353    w_V[:] = z_V + h_V
354    a_V[:] = b_V * h_V
355    d_V[:] = u_V * a_V
356
357   
358    return
359
360#-----------------------------------------------------------------------
361# Distribute to verticies with stage, height and velocity reconstructed
362# and then extrapolated.
363# In this method, we extrapolate the stage and height out to the vertices.
364# The bed, although given as initial data to the problem, is reconstructed
365# from the stage and height. This ensures consistency of the reconstructed
366# quantities (i.e. w = z + h) as well as protecting against negative
367# heights.
368#-----------------------------------------------------------------------
369def distribute_to_vertices_and_edges_limit_s_v_h(domain):
370    import sys
371    from anuga_1d.config import epsilon, h0
372
373    N = domain.number_of_elements
374
375    #Shortcuts
376    area      = domain.quantities['area']
377    discharge = domain.quantities['discharge']
378    bed       = domain.quantities['elevation']
379    height    = domain.quantities['height']
380    velocity  = domain.quantities['velocity']
381    width     = domain.quantities['width']
382    stage     = domain.quantities['stage']
383
384    #Arrays   
385    a_C   = area.centroid_values
386    d_C   = discharge.centroid_values
387    z_C   = bed.centroid_values
388    h_C   = height.centroid_values
389    u_C   = velocity.centroid_values
390    b_C   = width.centroid_values
391    w_C   = stage.centroid_values
392
393    # Construct h,u,w from the conserved quantities after protecting
394    # conserved quantities from becoming too small.
395    a_C[:] = numpy.where( (a_C>h0), a_C, 0.0 )
396    d_C[:] = numpy.where( (a_C>h0), d_C, 0.0 )
397    h_C[:] = numpy.where( (b_C>h0), a_C/(b_C + h0/b_C), 0.0 )
398    u_C[:] = numpy.where( (a_C>h0), d_C/(a_C + h0/a_C), 0.0 )   
399
400    # Set the stage
401    w_C[:] = h_C + z_C         
402
403    # Extrapolate "fundamental" quantities.
404    # All other quantities will be reconstructed from these.
405    for name in ['velocity', 'stage',  'height', 'width']:
406        Q = domain.quantities[name]
407        if domain.order == 1:
408            Q.extrapolate_first_order()
409        elif domain.order == 2:
410            Q.extrapolate_second_order()
411        else:
412            raise 'Unknown order'
413
414    # These quantities have been extrapolated.
415    u_V  = velocity.vertex_values
416    w_V  = stage.vertex_values
417    h_V  = height.vertex_values
418    b_V  = width.vertex_values
419
420    # These need to be reconstructed
421    a_V  = area.vertex_values
422    d_V  = discharge.vertex_values
423    z_V  = bed.vertex_values
424
425    # Reconstruct bed from stage and height.
426    z_V[:] = w_V-h_V
427
428    # Now reconstruct our conserved quantities from the above
429    # reconstructed quantities.
430    a_V[:] = b_V*h_V
431    d_V[:] = u_V*a_V
432
433    return
434
435
436#--------------------------------------------------------
437#Boundaries - specific to the channel_domain
438#--------------------------------------------------------
439class Reflective_boundary(Boundary):
440    """Reflective boundary returns same conserved quantities as
441    those present in its neighbour volume but reflected.
442
443    This class is specific to the shallow water equation as it
444    works with the momentum quantities assumed to be the second
445    and third conserved quantities.
446    """
447
448    def __init__(self, domain = None):
449        Boundary.__init__(self)
450
451        if domain is None:
452            msg = 'Domain must be specified for reflective boundary'
453            raise msg
454
455        #Handy shorthands
456        self.normals  = domain.normals
457        self.area     = domain.quantities['area'].vertex_values
458        self.discharge     = domain.quantities['discharge'].vertex_values
459        self.bed      = domain.quantities['elevation'].vertex_values
460        self.height   = domain.quantities['height'].vertex_values
461        self.velocity = domain.quantities['velocity'].vertex_values
462        self.width    = domain.quantities['width'].vertex_values
463        self.stage    = domain.quantities['stage'].vertex_values
464
465        self.evolved_quantities = numpy.zeros(7, numpy.float)
466
467    def __repr__(self):
468        return 'Reflective_boundary'
469
470
471    def evaluate(self, vol_id, edge_id):
472        """Reflective boundaries reverses the outward momentum
473        of the volume they serve.
474        """
475
476        q = self.evolved_quantities
477        q[0] =  self.area[vol_id, edge_id]
478        q[1] = -self.discharge[vol_id, edge_id]
479        q[2] =  self.bed[vol_id, edge_id]
480        q[3] =  self.height[vol_id, edge_id]
481        q[4] = -self.velocity[vol_id, edge_id]
482        q[5] =  self.width[vol_id,edge_id]
483        q[6] =  self.stage[vol_id,edge_id]
484
485        return q
486
487class Dirichlet_boundary(Boundary):
488    """Dirichlet boundary returns constant values for the
489    conserved quantities
490if k>5 and k<15:
491            print discharge_ud[k],-g*zx*avg_h*avg_b
492        discharge_ud[k] +=-g*zx*avg_h*avg_b    """
493
494
495    def __init__(self, evolved_quantities=None):
496        Boundary.__init__(self)
497
498        if evolved_quantities is None:
499            msg = 'Must specify one value for each evolved quantity'
500            raise msg
501
502        assert len(evolved_quantities) == 7
503
504        self.evolved_quantities=numpy.array(evolved_quantities,numpy.float)
505
506    def __repr__(self):
507        return 'Dirichlet boundary (%s)' %self.evolved_quantities
508
509    def evaluate(self, vol_id=None, edge_id=None):
510        return self.evolved_quantities
511
512
513#----------------------------
514#Standard forcing terms:
515#---------------------------
516def gravity(domain):
517    """Apply gravitational pull in the presence of bed slope
518    """
519
520    from util import gradient
521    from Numeric import zeros, Float, array, sum
522
523
524
525    Area      = domain.quantities['area']
526    Discharge  = domain.quantities['discharge']
527    Elevation = domain.quantities['elevation']
528    Height    = domain.quantities['height']
529    Width     = domain.quantities['width']
530
531    discharge_ud  = Discharge.explicit_update
532
533
534       
535    h = Height.vertex_values
536    b = Width.vertex_values
537    a = Area.vertex_values
538    z = Elevation.vertex_values
539
540    x = domain.get_vertex_coordinates()
541    g = domain.g
542    for k in range(domain.number_of_elements):
543        avg_h = 0.5*(h[k,0] + h[k,1])
544        avg_b = 0.5*(b[k,0] + b[k,1])
545       
546        #Compute bed slope
547        x0, x1 = x[k,:]
548        z0, z1 = z[k,:]
549        zx = gradient(x0, x1, z0, z1)
550       
551        #Update momentum (explicit update is reset to source values)
552        discharge_ud[k]+= -g*zx*avg_h*avg_b
553     
554
555def boundary_stress(domain):
556
557    from util import gradient
558    from Numeric import zeros, Float, array, sum
559
560    Area     = domain.quantities['area']
561    Discharge  = domain.quantities['discharge']
562    Elevation = domain.quantities['elevation']
563    Height    = domain.quantities['height']
564    Width     = domain.quantities['width']
565
566    discharge_ud  = Discharge.explicit_update
567 
568    h = Height.vertex_values
569    b = Width.vertex_values
570    a = Area.vertex_values
571    z = Elevation.vertex_values
572
573    x = domain.get_vertex_coordinates()
574    g = domain.g
575
576    for k in range(domain.number_of_elements):
577        avg_h = 0.5*(h[k,0] + h[k,1])
578       
579
580        #Compute bed slope
581        x0, x1 = x[k,:]
582        b0, b1 = b[k,:]
583        bx = gradient(x0, x1, b0, b1)
584       
585        #Update momentum (explicit update is reset to source values)
586        discharge_ud[k] += 0.5*g*bx*avg_h*avg_h
587        #stage_ud[k] = 0.0
588 
589 
590def manning_friction(domain):
591    """Apply (Manning) friction to water momentum
592    """
593
594    from math import sqrt
595
596    w = domain.quantities['stage'].centroid_values
597    z = domain.quantities['elevation'].centroid_values
598    h = w-z
599
600    uh = domain.quantities['xmomentum'].centroid_values
601    eta = domain.quantities['friction'].centroid_values
602
603    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
604
605    N = domain.number_of_elements
606    eps = domain.minimum_allowed_height
607    g = domain.g
608
609    for k in range(N):
610        if eta[k] >= eps:
611            if h[k] >= eps:
612                #S = -g * eta[k]**2 * sqrt((uh[k]**2 + vh[k]**2))
613                S = -g * eta[k]**2 * uh[k]
614                S /= h[k]**(7.0/3)
615
616                #Update momentum
617                xmom_update[k] += S*uh[k]
618                #ymom_update[k] += S*vh[k]
619
620def linear_friction(domain):
621    """Apply linear friction to water momentum
622
623    Assumes quantity: 'linear_friction' to be present
624    """
625
626    from math import sqrt
627
628    w = domain.quantities['stage'].centroid_values
629    z = domain.quantities['elevation'].centroid_values
630    h = w-z
631
632    uh = domain.quantities['xmomentum'].centroid_values
633    tau = domain.quantities['linear_friction'].centroid_values
634
635    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
636
637    N = domain.number_of_elements
638    eps = domain.minimum_allowed_height
639
640    for k in range(N):
641        if tau[k] >= eps:
642            if h[k] >= eps:
643                S = -tau[k]/h[k]
644
645                #Update momentum
646                xmom_update[k] += S*uh[k]
647
648
649
650
651def linearb(domain):
652
653    bC = domain.quantities['width'].vertex_values
654   
655    for i in range(len(bC)-1):
656        temp= 0.5*(bC[i,1]+bC[i+1,0])
657        bC[i,1]=temp
658        bC[i+1,0]=temp
659
660
661
Note: See TracBrowser for help on using the repository browser.