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

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

Padarn's code

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