source: inundation/ga/storm_surge/pyvolution/shallow_water.py @ 1507

Last change on this file since 1507 was 1507, checked in by matthew, 19 years ago

The new method extrapolate_second_order_sw belongs in the shallow_water domain class. If domain.order==2 then extrapolate_second_order_sw performs the second order extrapolation and subsequent limiting for all conserved quantities. This avoids the recalculation of geometric information for each conserved quantity. See comments in the method PyObject?* extrapolate_second_order_sw in shallow_water_ext.c. The notion of the 'auxiliary triangle' for computing gradients is explained in the comments.

For the problem of run_profile.py, with N=128, the combined extrapolation and limit procedures used to consume 13.92 seconds. With the all-in-one method extrapolate_second_order_sw, the extrapolation and limiting procedure takes 6.72 seconds (on hypathia, at the ANU).

  • Property svn:keywords set to LastChangedDate LastChangedRevision LastChangedBy HeadURL Id
File size: 52.0 KB
Line 
1"""Class Domain -
22D triangular domains for finite-volume computations of
3the shallow water wave equation.
4
5This module contains a specialisation of class Domain from module domain.py
6consisting of methods specific to the Shallow Water Wave Equation
7
8
9U_t + E_x + G_y = S
10
11where
12
13U = [w, uh, vh]
14E = [uh, u^2h + gh^2/2, uvh]
15G = [vh, uvh, v^2h + gh^2/2]
16S represents source terms forcing the system
17(e.g. gravity, friction, wind stress, ...)
18
19and _t, _x, _y denote the derivative with respect to t, x and y respectively.
20
21The quantities are
22
23symbol    variable name    explanation
24x         x                horizontal distance from origin [m]
25y         y                vertical distance from origin [m]
26z         elevation        elevation of bed on which flow is modelled [m]
27h         height           water height above z [m]
28w         stage            absolute water level, w = z+h [m]
29u                          speed in the x direction [m/s]
30v                          speed in the y direction [m/s]
31uh        xmomentum        momentum in the x direction [m^2/s]
32vh        ymomentum        momentum in the y direction [m^2/s]
33
34eta                        mannings friction coefficient [to appear]
35nu                         wind stress coefficient [to appear]
36
37The conserved quantities are w, uh, vh
38
39
40For details see e.g.
41Christopher Zoppou and Stephen Roberts,
42Catastrophic Collapse of Water Supply Reservoirs in Urban Areas,
43Journal of Hydraulic Engineering, vol. 127, No. 7 July 1999
44
45
46Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou
47Geoscience Australia, 2004
48"""
49
50#Subversion keywords:
51#
52#$LastChangedDate: 2005-06-09 04:49:17 +0000 (Thu, 09 Jun 2005) $
53#$LastChangedRevision: 1507 $
54#$LastChangedBy: matthew $
55
56
57from domain import *
58from region import *#
59
60Generic_domain = Domain #Rename
61
62#Shalow water domain
63class Domain(Generic_domain):
64
65    def __init__(self, coordinates, vertices, boundary = None,
66                 tagged_elements = None, geo_reference = None,
67                 use_inscribed_circle=False):
68
69        conserved_quantities = ['stage', 'xmomentum', 'ymomentum']
70        other_quantities = ['elevation', 'friction']
71        Generic_domain.__init__(self, coordinates, vertices, boundary,
72                                conserved_quantities, other_quantities,
73                                tagged_elements, geo_reference, use_inscribed_circle)
74
75        from config import minimum_allowed_height, g
76        self.minimum_allowed_height = minimum_allowed_height
77        self.g = g
78
79        self.forcing_terms.append(gravity)
80        self.forcing_terms.append(manning_friction)
81
82        #Realtime visualisation
83        self.visualiser = None
84        self.visualise  = False
85        self.visualise_color_stage = False
86        self.visualise_stage_range = 1.0
87        self.visualise_timer = True
88        self.visualise_range_z = None
89
90        #Stored output
91        self.store = False
92        self.format = 'sww'
93        self.smooth = True
94
95        #Reduction operation for get_vertex_values
96        #from util import mean
97        #self.reduction = mean
98        self.reduction = min  #Looks better near steep slopes
99
100        self.quantities_to_be_stored = ['stage']
101
102
103        #Establish shortcuts to relevant quantities (for efficiency)
104        #self.w = self.quantities['stage']
105        #self.uh = self.quantities['xmomentum']
106        #self.vh = self.quantities['ymomentum']
107        #self.z = self.quantities['elevation']
108        #self.eta = self.quantities['friction']
109
110    def initialise_visualiser(self,scale_z=1.0):
111        #Realtime visualisation
112        if self.visualiser is None:
113            from realtime_visualisation_new import Visualiser           
114            self.visualiser = Visualiser(self,scale_z)
115        self.visualise = True
116
117    def check_integrity(self):
118        Generic_domain.check_integrity(self)
119
120        #Check that we are solving the shallow water wave equation
121
122        msg = 'First conserved quantity must be "stage"'
123        assert self.conserved_quantities[0] == 'stage', msg
124        msg = 'Second conserved quantity must be "xmomentum"'
125        assert self.conserved_quantities[1] == 'xmomentum', msg
126        msg = 'Third conserved quantity must be "ymomentum"'
127        assert self.conserved_quantities[2] == 'ymomentum', msg
128       
129    def extrapolate_second_order_sw(self):
130        #Call correct module function
131        #(either from this module or C-extension)
132        extrapolate_second_order_sw(self)
133
134    def compute_fluxes(self):
135        #Call correct module function
136        #(either from this module or C-extension)
137        compute_fluxes(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(self)
143
144
145    #FIXME: Under construction
146#     def set_defaults(self):
147#         """Set default values for uninitialised quantities.
148#         This is specific to the shallow water wave equation
149#         Defaults for 'elevation', 'friction', 'xmomentum' and 'ymomentum'
150#         are 0.0. Default for 'stage' is whatever the value of 'elevation'.
151#         """
152
153#         for name in self.other_quantities + self.conserved_quantities:
154#             print name
155#             print self.quantities.keys()
156#             if not self.quantities.has_key(name):
157#                 if name == 'stage':
158
159#                     if self.quantities.has_key('elevation'):
160#                         z = self.quantities['elevation'].vertex_values
161#                         self.set_quantity(name, z)
162#                     else:
163#                         self.set_quantity(name, 0.0)
164#                 else:
165#                     self.set_quantity(name, 0.0)
166
167
168
169#         #Lift negative heights up
170#         #z = self.quantities['elevation'].vertex_values
171#         #w = self.quantities['stage'].vertex_values
172
173#         #h = w-z
174
175#         #for k in range(h.shape[0]):
176#         #    for i in range(3):
177#         #        if h[k, i] < 0.0:
178#         #            w[k, i] = z[k, i]
179
180
181#         #self.quantities['stage'].interpolate()
182
183
184
185    def evolve(self, yieldstep = None, finaltime = None):
186        """Specialisation of basic evolve method from parent class
187        """
188
189        #Call check integrity here rather than from user scripts
190        #self.check_integrity()
191
192        msg = 'Parameter beta_h must be in the interval [0, 1['
193        assert 0 <= self.beta_h < 1.0, msg
194        msg = 'Parameter beta_w must be in the interval [0, 1['
195        assert 0 <= self.beta_w < 1.0, msg
196
197
198        #Initial update of vertex and edge values before any storage
199        #and or visualisation
200        self.distribute_to_vertices_and_edges()
201
202
203        #Initialise real time viz if requested
204        if self.visualise is True and self.time == 0.0:
205            import realtime_visualisation_new as visualise
206            #import realtime_visualisation as visualise           
207            visualise.create_surface(self)
208
209        #Store model data, e.g. for visualisation
210        if self.store is True and self.time == 0.0:
211            self.initialise_storage()
212            #print 'Storing results in ' + self.writer.filename
213        else:
214            #print 'Results will not be stored.'
215            #print 'To store results set domain.store = True'
216            pass
217            #FIXME: Diagnostic output should be controlled by
218            # a 'verbose' flag living in domain (or in a parent class)
219
220        #Call basic machinery from parent class
221        for t in Generic_domain.evolve(self, yieldstep, finaltime):
222            #Real time viz
223            if self.visualise is True:
224                visualise.update(self)
225
226            #Store model data, e.g. for subsequent visualisation
227            if self.store is True:
228                self.store_timestep(self.quantities_to_be_stored)
229
230                #FIXME: Could maybe be taken from specified list
231                #of 'store every step' quantities
232
233            #Pass control on to outer loop for more specific actions
234            yield(t)
235
236
237    def initialise_storage(self):
238        """Create and initialise self.writer object for storing data.
239        Also, save x,y and bed elevation
240        """
241
242        import data_manager
243
244        #Initialise writer
245        self.writer = data_manager.get_dataobject(self, mode = 'w')
246
247        #Store vertices and connectivity
248        self.writer.store_connectivity()
249
250
251    def store_timestep(self, name):
252        """Store named quantity and time.
253
254        Precondition:
255           self.write has been initialised
256        """
257        self.writer.store_timestep(name)
258
259####################MH 090605 new extrapolation function belonging to domain class
260def extrapolate_second_order_sw(domain):
261    """extrapolate conserved quntities to the vertices of the triangles
262    Python version to be written after the C version
263    """
264    msg = 'Method extrapolate_second_order_sw should be implemented in C'
265    raise msg
266####################MH 090605 ###########################################
267
268#Rotation of momentum vector
269def rotate(q, normal, direction = 1):
270    """Rotate the momentum component q (q[1], q[2])
271    from x,y coordinates to coordinates based on normal vector.
272
273    If direction is negative the rotation is inverted.
274
275    Input vector is preserved
276
277    This function is specific to the shallow water wave equation
278    """
279
280    from Numeric import zeros, Float
281
282    assert len(q) == 3,\
283           'Vector of conserved quantities must have length 3'\
284           'for 2D shallow water equation'
285
286    try:
287        l = len(normal)
288    except:
289        raise 'Normal vector must be an Numeric array'
290
291    assert l == 2, 'Normal vector must have 2 components'
292
293
294    n1 = normal[0]
295    n2 = normal[1]
296
297    r = zeros(len(q), Float) #Rotated quantities
298    r[0] = q[0]              #First quantity, height, is not rotated
299
300    if direction == -1:
301        n2 = -n2
302
303
304    r[1] =  n1*q[1] + n2*q[2]
305    r[2] = -n2*q[1] + n1*q[2]
306
307    return r
308
309
310
311####################################
312# Flux computation
313def flux_function(normal, ql, qr, zl, zr):
314    """Compute fluxes between volumes for the shallow water wave equation
315    cast in terms of w = h+z using the 'central scheme' as described in
316
317    Kurganov, Noelle, Petrova. 'Semidiscrete Central-Upwind Schemes For
318    Hyperbolic Conservation Laws and Hamilton-Jacobi Equations'.
319    Siam J. Sci. Comput. Vol. 23, No. 3, pp. 707-740.
320
321    The implemented formula is given in equation (3.15) on page 714
322
323    Conserved quantities w, uh, vh are stored as elements 0, 1 and 2
324    in the numerical vectors ql an qr.
325
326    Bed elevations zl and zr.
327    """
328
329    from config import g, epsilon
330    from math import sqrt
331    from Numeric import array
332
333    #Align momentums with x-axis
334    q_left  = rotate(ql, normal, direction = 1)
335    q_right = rotate(qr, normal, direction = 1)
336
337    z = (zl+zr)/2 #Take average of field values
338
339    w_left  = q_left[0]   #w=h+z
340    h_left  = w_left-z
341    uh_left = q_left[1]
342
343    if h_left < epsilon:
344        u_left = 0.0  #Could have been negative
345        h_left = 0.0
346    else:
347        u_left  = uh_left/h_left
348
349
350    w_right  = q_right[0]  #w=h+z
351    h_right  = w_right-z
352    uh_right = q_right[1]
353
354
355    if h_right < epsilon:
356        u_right = 0.0  #Could have been negative
357        h_right = 0.0
358    else:
359        u_right  = uh_right/h_right
360
361    vh_left  = q_left[2]
362    vh_right = q_right[2]
363
364    soundspeed_left  = sqrt(g*h_left)
365    soundspeed_right = sqrt(g*h_right)
366
367    #Maximal wave speed
368    s_max = max(u_left+soundspeed_left, u_right+soundspeed_right, 0)
369
370    #Minimal wave speed
371    s_min = min(u_left-soundspeed_left, u_right-soundspeed_right, 0)
372
373    #Flux computation
374
375    #FIXME(Ole): Why is it again that we don't
376    #use uh_left and uh_right directly in the first entries?
377    flux_left  = array([u_left*h_left,
378                        u_left*uh_left + 0.5*g*h_left**2,
379                        u_left*vh_left])
380    flux_right = array([u_right*h_right,
381                        u_right*uh_right + 0.5*g*h_right**2,
382                        u_right*vh_right])
383
384    denom = s_max-s_min
385    if denom == 0.0:
386        edgeflux = array([0.0, 0.0, 0.0])
387        max_speed = 0.0
388    else:
389        edgeflux = (s_max*flux_left - s_min*flux_right)/denom
390        edgeflux += s_max*s_min*(q_right-q_left)/denom
391
392        edgeflux = rotate(edgeflux, normal, direction=-1)
393        max_speed = max(abs(s_max), abs(s_min))
394
395    return edgeflux, max_speed
396
397
398def compute_fluxes(domain):
399    """Compute all fluxes and the timestep suitable for all volumes
400    in domain.
401
402    Compute total flux for each conserved quantity using "flux_function"
403
404    Fluxes across each edge are scaled by edgelengths and summed up
405    Resulting flux is then scaled by area and stored in
406    explicit_update for each of the three conserved quantities
407    stage, xmomentum and ymomentum
408
409    The maximal allowable speed computed by the flux_function for each volume
410    is converted to a timestep that must not be exceeded. The minimum of
411    those is computed as the next overall timestep.
412
413    Post conditions:
414      domain.explicit_update is reset to computed flux values
415      domain.timestep is set to the largest step satisfying all volumes.
416    """
417
418    import sys
419    from Numeric import zeros, Float
420
421    N = domain.number_of_elements
422
423    #Shortcuts
424    Stage = domain.quantities['stage']
425    Xmom = domain.quantities['xmomentum']
426    Ymom = domain.quantities['ymomentum']
427    Bed = domain.quantities['elevation']
428
429    #Arrays
430    stage = Stage.edge_values
431    xmom =  Xmom.edge_values
432    ymom =  Ymom.edge_values
433    bed =   Bed.edge_values
434
435    stage_bdry = Stage.boundary_values
436    xmom_bdry =  Xmom.boundary_values
437    ymom_bdry =  Ymom.boundary_values
438
439    flux = zeros(3, Float) #Work array for summing up fluxes
440
441    #Loop
442    timestep = float(sys.maxint)
443    for k in range(N):
444
445        flux[:] = 0.  #Reset work array
446        for i in range(3):
447            #Quantities inside volume facing neighbour i
448            ql = [stage[k, i], xmom[k, i], ymom[k, i]]
449            zl = bed[k, i]
450
451            #Quantities at neighbour on nearest face
452            n = domain.neighbours[k,i]
453            if n < 0:
454                m = -n-1 #Convert negative flag to index
455                qr = [stage_bdry[m], xmom_bdry[m], ymom_bdry[m]]
456                zr = zl #Extend bed elevation to boundary
457            else:
458                m = domain.neighbour_edges[k,i]
459                qr = [stage[n, m], xmom[n, m], ymom[n, m]]
460                zr = bed[n, m]
461
462
463            #Outward pointing normal vector
464            normal = domain.normals[k, 2*i:2*i+2]
465
466            #Flux computation using provided function
467            edgeflux, max_speed = flux_function(normal, ql, qr, zl, zr)
468            flux -= edgeflux * domain.edgelengths[k,i]
469
470            #Update optimal_timestep
471            try:
472                timestep = min(timestep, 0.5*domain.radii[k]/max_speed)
473            except ZeroDivisionError:
474                pass
475
476        #Normalise by area and store for when all conserved
477        #quantities get updated
478        flux /= domain.areas[k]
479        Stage.explicit_update[k] = flux[0]
480        Xmom.explicit_update[k] = flux[1]
481        Ymom.explicit_update[k] = flux[2]
482
483
484    domain.timestep = timestep
485
486#MH090605 The following method belongs to the shallow_water domain class
487#see comments in the corresponding method in shallow_water_ext.c
488def extrapolate_second_order_sw_c(domain):
489    """Wrapper calling C version of extrapolate_second_order_sw
490    """
491    import sys
492    from Numeric import zeros, Float
493
494    N = domain.number_of_elements
495
496    #Shortcuts
497    Stage = domain.quantities['stage']
498    Xmom = domain.quantities['xmomentum']
499    Ymom = domain.quantities['ymomentum']
500    from shallow_water_ext import extrapolate_second_order_sw
501    extrapolate_second_order_sw(domain,domain.surrogate_neighbours,
502                                domain.number_of_boundaries,
503                                domain.centroid_coordinates,
504                                Stage.centroid_values,
505                                Xmom.centroid_values,
506                                Ymom.centroid_values,
507                                domain.vertex_coordinates,
508                                Stage.vertex_values,
509                                Xmom.vertex_values,
510                                Ymom.vertex_values)
511
512def compute_fluxes_c(domain):
513    """Wrapper calling C version of compute fluxes
514    """
515
516    import sys
517    from Numeric import zeros, Float
518
519    N = domain.number_of_elements
520
521    #Shortcuts
522    Stage = domain.quantities['stage']
523    Xmom = domain.quantities['xmomentum']
524    Ymom = domain.quantities['ymomentum']
525    Bed = domain.quantities['elevation']
526
527    timestep = float(sys.maxint)
528    from shallow_water_ext import compute_fluxes
529    domain.timestep = compute_fluxes(timestep, domain.epsilon, domain.g,
530                                     domain.neighbours,
531                                     domain.neighbour_edges,
532                                     domain.normals,
533                                     domain.edgelengths,
534                                     domain.radii,
535                                     domain.areas,
536                                     Stage.edge_values,
537                                     Xmom.edge_values,
538                                     Ymom.edge_values,
539                                     Bed.edge_values,
540                                     Stage.boundary_values,
541                                     Xmom.boundary_values,
542                                     Ymom.boundary_values,
543                                     Stage.explicit_update,
544                                     Xmom.explicit_update,
545                                     Ymom.explicit_update)
546
547
548####################################
549# Module functions for gradient limiting (distribute_to_vertices_and_edges)
550
551def distribute_to_vertices_and_edges(domain):
552    """Distribution from centroids to vertices specific to the
553    shallow water wave
554    equation.
555
556    It will ensure that h (w-z) is always non-negative even in the
557    presence of steep bed-slopes by taking a weighted average between shallow
558    and deep cases.
559
560    In addition, all conserved quantities get distributed as per either a
561    constant (order==1) or a piecewise linear function (order==2).
562
563    FIXME: more explanation about removal of artificial variability etc
564
565    Precondition:
566      All quantities defined at centroids and bed elevation defined at
567      vertices.
568
569    Postcondition
570      Conserved quantities defined at vertices
571
572    """
573
574    #Remove very thin layers of water
575    protect_against_infinitesimal_and_negative_heights(domain)
576
577    #Extrapolate all conserved quantities
578    #MH090605 if second order, perform the extrapolation and limiting on all of the conserved quantities
579    if (domain.order == 1):
580        for name in domain.conserved_quantities:
581            Q = domain.quantities[name]
582            Q.extrapolate_first_order()
583    elif domain.order == 2:
584        domain.extrapolate_second_order_sw()
585    else:
586        raise 'Unknown order'
587
588    #old code:
589    #for name in domain.conserved_quantities:
590    #    Q = domain.quantities[name]
591    #    if domain.order == 1:
592    #        Q.extrapolate_first_order()
593    #    elif domain.order == 2:
594    #        #Q.extrapolate_second_order()
595    #        Q.limit()
596    #    else:
597    #        raise 'Unknown order'
598
599    #Take bed elevation into account when water heights are small
600    balance_deep_and_shallow(domain)
601
602    #Compute edge values by interpolation
603    for name in domain.conserved_quantities:
604        Q = domain.quantities[name]
605        Q.interpolate_from_vertices_to_edges()
606
607
608
609def dry(domain):
610    """Protect against infinitesimal heights and associated high velocities
611    at vertices
612    """
613
614    #FIXME: Experimental (from old version). Not in use at the moment
615
616    #Shortcuts
617    wv = domain.quantities['stage'].vertex_values
618    zv = domain.quantities['elevation'].vertex_values
619    xmomv = domain.quantities['xmomentum'].vertex_values
620    ymomv = domain.quantities['ymomentum'].vertex_values
621    hv = wv - zv  #Water depths at vertices
622
623    #Update
624    for k in range(domain.number_of_elements):
625        hmax = max(hv[k, :])
626
627        if hmax < domain.minimum_allowed_height:
628            #Control stage
629            wv[k, :] = zv[k, :]
630
631            #Control momentum
632            xmomv[k,:] = ymomv[k,:] = 0.0
633
634
635def protect_against_infinitesimal_and_negative_heights(domain):
636    """Protect against infinitesimal heights and associated high velocities
637    """
638
639    #FIXME: Look here for error
640
641    #Shortcuts
642    wc = domain.quantities['stage'].centroid_values
643    zc = domain.quantities['elevation'].centroid_values
644    xmomc = domain.quantities['xmomentum'].centroid_values
645    ymomc = domain.quantities['ymomentum'].centroid_values
646    hc = wc - zc  #Water depths at centroids
647
648    #print zc
649    #print '1', wc
650    #Update
651    for k in range(domain.number_of_elements):
652
653        if hc[k] < domain.minimum_allowed_height:
654            #Control stage
655            wc[k] = zc[k]
656
657            #Control momentum
658            xmomc[k] = ymomc[k] = 0.0
659
660    #print '2', wc
661
662
663def protect_against_infinitesimal_and_negative_heights_c(domain):
664    """Protect against infinitesimal heights and associated high velocities
665    """
666
667    #Shortcuts
668    wc = domain.quantities['stage'].centroid_values
669    zc = domain.quantities['elevation'].centroid_values
670    xmomc = domain.quantities['xmomentum'].centroid_values
671    ymomc = domain.quantities['ymomentum'].centroid_values
672
673    from shallow_water_ext import protect
674
675    protect(domain.minimum_allowed_height, wc, zc, xmomc, ymomc)
676
677
678
679def h_limiter(domain):
680    """Limit slopes for each volume to eliminate artificial variance
681    introduced by e.g. second order extrapolator
682
683    limit on h = w-z
684
685    This limiter depends on two quantities (w,z) so it resides within
686    this module rather than within quantity.py
687    """
688
689    from Numeric import zeros, Float
690
691    N = domain.number_of_elements
692    beta_h = domain.beta_h
693
694    #Shortcuts
695    wc = domain.quantities['stage'].centroid_values
696    zc = domain.quantities['elevation'].centroid_values
697    hc = wc - zc
698
699    wv = domain.quantities['stage'].vertex_values
700    zv = domain.quantities['elevation'].vertex_values
701    hv = wv-zv
702
703    hvbar = zeros(hv.shape, Float) #h-limited values
704
705    #Find min and max of this and neighbour's centroid values
706    hmax = zeros(hc.shape, Float)
707    hmin = zeros(hc.shape, Float)
708
709    for k in range(N):
710        hmax[k] = hmin[k] = hc[k]
711        for i in range(3):
712            n = domain.neighbours[k,i]
713            if n >= 0:
714                hn = hc[n] #Neighbour's centroid value
715
716                hmin[k] = min(hmin[k], hn)
717                hmax[k] = max(hmax[k], hn)
718
719
720    #Diffences between centroids and maxima/minima
721    dhmax = hmax - hc
722    dhmin = hmin - hc
723
724    #Deltas between vertex and centroid values
725    dh = zeros(hv.shape, Float)
726    for i in range(3):
727        dh[:,i] = hv[:,i] - hc
728
729    #Phi limiter
730    for k in range(N):
731
732        #Find the gradient limiter (phi) across vertices
733        phi = 1.0
734        for i in range(3):
735            r = 1.0
736            if (dh[k,i] > 0): r = dhmax[k]/dh[k,i]
737            if (dh[k,i] < 0): r = dhmin[k]/dh[k,i]
738
739            phi = min( min(r*beta_h, 1), phi )
740
741        #Then update using phi limiter
742        for i in range(3):
743            hvbar[k,i] = hc[k] + phi*dh[k,i]
744
745    return hvbar
746
747
748
749def h_limiter_c(domain):
750    """Limit slopes for each volume to eliminate artificial variance
751    introduced by e.g. second order extrapolator
752
753    limit on h = w-z
754
755    This limiter depends on two quantities (w,z) so it resides within
756    this module rather than within quantity.py
757
758    Wrapper for c-extension
759    """
760
761    from Numeric import zeros, Float
762
763    N = domain.number_of_elements
764    beta_h = domain.beta_h
765
766    #Shortcuts
767    wc = domain.quantities['stage'].centroid_values
768    zc = domain.quantities['elevation'].centroid_values
769    hc = wc - zc
770
771    wv = domain.quantities['stage'].vertex_values
772    zv = domain.quantities['elevation'].vertex_values
773    hv = wv - zv
774
775    #Call C-extension
776    from shallow_water_ext import h_limiter
777    hvbar = h_limiter(domain, hc, hv)
778
779    return hvbar
780
781
782def balance_deep_and_shallow(domain):
783    """Compute linear combination between stage as computed by
784    gradient-limiters limiting using w, and stage computed as
785    constant height above bed and limited using h.
786    The former takes precedence when heights are large compared to the
787    bed slope while the latter takes precedence when heights are
788    relatively small.  Anything in between is computed as a balanced
789    linear combination in order to avoid numerical disturbances which
790    would otherwise appear as a result of hard switching between
791    modes.
792
793    The h-limiter is always applied irrespective of the order.
794    """
795
796    #New idea.
797    #
798    # In the presence and near of bedslope it is necessary to
799    # limit slopes based on differences in h rather than w
800    # (which is what is needed away from the bed).
801    #
802    # So whether extrapolation was first order or second order,
803    # it will need to be balanced with a h-limited gradient.
804    #
805    # For this we will use the quantity alpha as before
806    #
807
808    #Shortcuts
809    wc = domain.quantities['stage'].centroid_values
810    zc = domain.quantities['elevation'].centroid_values
811    hc = wc - zc
812
813    wv = domain.quantities['stage'].vertex_values
814    zv = domain.quantities['elevation'].vertex_values
815    hv = wv-zv
816
817    #Limit h
818    hvbar = h_limiter(domain)
819
820    for k in range(domain.number_of_elements):
821        #Compute maximal variation in bed elevation
822        #  This quantitiy is
823        #    dz = max_i abs(z_i - z_c)
824        #  and it is independent of dimension
825        #  In the 1d case zc = (z0+z1)/2
826        #  In the 2d case zc = (z0+z1+z2)/3
827
828        dz = max(abs(zv[k,0]-zc[k]),
829                 abs(zv[k,1]-zc[k]),
830                 abs(zv[k,2]-zc[k]))
831
832
833        hmin = min( hv[k,:] )
834
835        #Create alpha in [0,1], where alpha==0 means using the h-limited
836        #stage and alpha==1 means using the w-limited stage as
837        #computed by the gradient limiter (both 1st or 2nd order)
838
839        #If hmin > dz/2 then alpha = 1 and the bed will have no effect
840        #If hmin < 0 then alpha = 0 reverting to constant height above bed.
841
842        if dz > 0.0:
843            alpha = max( min( 2*hmin/dz, 1.0), 0.0 )
844        else:
845            #Flat bed
846            alpha = 1.0
847
848        #Let
849        #
850        #  wvi be the w-limited stage (wvi = zvi + hvi)
851        #  wvi- be the h-limited state (wvi- = zvi + hvi-)
852        #
853        #
854        #where i=0,1,2 denotes the vertex ids
855        #
856        #Weighted balance between w-limited and h-limited stage is
857        #
858        #  wvi := (1-alpha)*(zvi+hvi-) + alpha*(zvi+hvi)
859        #
860        #It follows that the updated wvi is
861        #  wvi := zvi + (1-alpha)*hvi- + alpha*hvi
862        #
863        # Momentum is balanced between constant and limited
864
865
866        #for i in range(3):
867        #    wv[k,i] = zv[k,i] + hvbar[k,i]
868
869        #return
870
871        if alpha < 1:
872
873            for i in range(3):
874                wv[k,i] = zv[k,i] + (1-alpha)*hvbar[k,i] + alpha*hv[k,i]
875
876            #Momentums at centroids
877            xmomc = domain.quantities['xmomentum'].centroid_values
878            ymomc = domain.quantities['ymomentum'].centroid_values
879
880            #Momentums at vertices
881            xmomv = domain.quantities['xmomentum'].vertex_values
882            ymomv = domain.quantities['ymomentum'].vertex_values
883
884            # Update momentum as a linear combination of
885            # xmomc and ymomc (shallow) and momentum
886            # from extrapolator xmomv and ymomv (deep).
887            xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:]
888            ymomv[k,:] = (1-alpha)*ymomc[k] + alpha*ymomv[k,:]
889
890
891def balance_deep_and_shallow_c(domain):
892    """Wrapper for C implementation
893    """
894
895    #Shortcuts
896    wc = domain.quantities['stage'].centroid_values
897    zc = domain.quantities['elevation'].centroid_values
898    hc = wc - zc
899
900    wv = domain.quantities['stage'].vertex_values
901    zv = domain.quantities['elevation'].vertex_values
902    hv = wv - zv
903
904    #Momentums at centroids
905    xmomc = domain.quantities['xmomentum'].centroid_values
906    ymomc = domain.quantities['ymomentum'].centroid_values
907
908    #Momentums at vertices
909    xmomv = domain.quantities['xmomentum'].vertex_values
910    ymomv = domain.quantities['ymomentum'].vertex_values
911
912    #Limit h
913    hvbar = h_limiter(domain)
914
915    #This is how one would make a first order h_limited value
916    #as in the old balancer (pre 17 Feb 2005):
917    #from Numeric import zeros, Float
918    #hvbar = zeros( (len(hc), 3), Float)
919    #for i in range(3):
920    #    hvbar[:,i] = hc[:]
921
922    from shallow_water_ext import balance_deep_and_shallow
923    balance_deep_and_shallow(wc, zc, hc, wv, zv, hv, hvbar,
924                             xmomc, ymomc, xmomv, ymomv)
925
926
927
928
929###############################################
930#Boundaries - specific to the shallow water wave equation
931class Reflective_boundary(Boundary):
932    """Reflective boundary returns same conserved quantities as
933    those present in its neighbour volume but reflected.
934
935    This class is specific to the shallow water equation as it
936    works with the momentum quantities assumed to be the second
937    and third conserved quantities.
938    """
939
940    def __init__(self, domain = None):
941        Boundary.__init__(self)
942
943        if domain is None:
944            msg = 'Domain must be specified for reflective boundary'
945            raise msg
946
947        #Handy shorthands
948        self.stage = domain.quantities['stage'].edge_values
949        self.xmom = domain.quantities['xmomentum'].edge_values
950        self.ymom = domain.quantities['ymomentum'].edge_values
951        self.normals = domain.normals
952
953        from Numeric import zeros, Float
954        self.conserved_quantities = zeros(3, Float)
955
956    def __repr__(self):
957        return 'Reflective_boundary'
958
959
960    def evaluate(self, vol_id, edge_id):
961        """Reflective boundaries reverses the outward momentum
962        of the volume they serve.
963        """
964
965        q = self.conserved_quantities
966        q[0] = self.stage[vol_id, edge_id]
967        q[1] = self.xmom[vol_id, edge_id]
968        q[2] = self.ymom[vol_id, edge_id]
969
970        normal = self.normals[vol_id, 2*edge_id:2*edge_id+2]
971
972
973        r = rotate(q, normal, direction = 1)
974        r[1] = -r[1]
975        q = rotate(r, normal, direction = -1)
976
977        return q
978
979
980#class Spatio_temporal_boundary(Boundary):
981#    """The spatio-temporal boundary, reads values for the conserved
982#    quantities from an sww NetCDF file, and returns interpolated values
983#    at the midpoints of each associated boundaty segment.
984#    Time dependency is interpolated linearly as in util.File_function.#
985#
986#    Example:
987#    Bf = Spatio_temporal_boundary('source_file.sww', domain)
988#
989#    """
990Spatio_temporal_boundary = File_boundary
991
992
993
994
995#########################
996#Standard forcing terms:
997#
998def gravity(domain):
999    """Apply gravitational pull in the presence of bed slope
1000    """
1001
1002    from util import gradient
1003    from Numeric import zeros, Float, array, sum
1004
1005    xmom = domain.quantities['xmomentum'].explicit_update
1006    ymom = domain.quantities['ymomentum'].explicit_update
1007
1008    Stage = domain.quantities['stage']
1009    Elevation = domain.quantities['elevation']
1010    h = Stage.edge_values - Elevation.edge_values
1011    v = Elevation.vertex_values
1012
1013    x = domain.get_vertex_coordinates()
1014    g = domain.g
1015
1016    for k in range(domain.number_of_elements):
1017        avg_h = sum( h[k,:] )/3
1018
1019        #Compute bed slope
1020        x0, y0, x1, y1, x2, y2 = x[k,:]
1021        z0, z1, z2 = v[k,:]
1022
1023        zx, zy = gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2)
1024
1025        #Update momentum
1026        xmom[k] += -g*zx*avg_h
1027        ymom[k] += -g*zy*avg_h
1028
1029
1030def gravity_c(domain):
1031    """Wrapper calling C version
1032    """
1033
1034    xmom = domain.quantities['xmomentum'].explicit_update
1035    ymom = domain.quantities['ymomentum'].explicit_update
1036
1037    Stage = domain.quantities['stage']
1038    Elevation = domain.quantities['elevation']
1039    h = Stage.edge_values - Elevation.edge_values
1040    v = Elevation.vertex_values
1041
1042    x = domain.get_vertex_coordinates()
1043    g = domain.g
1044
1045
1046    from shallow_water_ext import gravity
1047    gravity(g, h, v, x, xmom, ymom)
1048
1049
1050def manning_friction(domain):
1051    """Apply (Manning) friction to water momentum
1052    """
1053
1054    from math import sqrt
1055
1056    w = domain.quantities['stage'].centroid_values
1057    z = domain.quantities['elevation'].centroid_values
1058    h = w-z
1059
1060    uh = domain.quantities['xmomentum'].centroid_values
1061    vh = domain.quantities['ymomentum'].centroid_values
1062    eta = domain.quantities['friction'].centroid_values
1063
1064    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
1065    ymom_update = domain.quantities['ymomentum'].semi_implicit_update
1066
1067    N = domain.number_of_elements
1068    eps = domain.minimum_allowed_height
1069    g = domain.g
1070
1071    for k in range(N):
1072        if eta[k] >= eps:
1073            if h[k] >= eps:
1074                S = -g * eta[k]**2 * sqrt((uh[k]**2 + vh[k]**2))
1075                S /= h[k]**(7.0/3)
1076
1077                #Update momentum
1078                xmom_update[k] += S*uh[k]
1079                ymom_update[k] += S*vh[k]
1080
1081
1082def manning_friction_c(domain):
1083    """Wrapper for c version
1084    """
1085
1086
1087    xmom = domain.quantities['xmomentum']
1088    ymom = domain.quantities['ymomentum']
1089
1090    w = domain.quantities['stage'].centroid_values
1091    z = domain.quantities['elevation'].centroid_values
1092
1093    uh = xmom.centroid_values
1094    vh = ymom.centroid_values
1095    eta = domain.quantities['friction'].centroid_values
1096
1097    xmom_update = xmom.semi_implicit_update
1098    ymom_update = ymom.semi_implicit_update
1099
1100    N = domain.number_of_elements
1101    eps = domain.minimum_allowed_height
1102    g = domain.g
1103
1104    from shallow_water_ext import manning_friction
1105    manning_friction(g, eps, w, z, uh, vh, eta, xmom_update, ymom_update)
1106
1107
1108def linear_friction(domain):
1109    """Apply linear friction to water momentum
1110
1111    Assumes quantity: 'linear_friction' to be present
1112    """
1113
1114    from math import sqrt
1115
1116    w = domain.quantities['stage'].centroid_values
1117    z = domain.quantities['elevation'].centroid_values
1118    h = w-z
1119
1120    uh = domain.quantities['xmomentum'].centroid_values
1121    vh = domain.quantities['ymomentum'].centroid_values
1122    tau = domain.quantities['linear_friction'].centroid_values
1123
1124    xmom_update = domain.quantities['xmomentum'].semi_implicit_update
1125    ymom_update = domain.quantities['ymomentum'].semi_implicit_update
1126
1127    N = domain.number_of_elements
1128    eps = domain.minimum_allowed_height
1129    g = domain.g #Not necessary? Why was this added?
1130
1131    for k in range(N):
1132        if tau[k] >= eps:
1133            if h[k] >= eps:
1134                S = -tau[k]/h[k]
1135
1136                #Update momentum
1137                xmom_update[k] += S*uh[k]
1138                ymom_update[k] += S*vh[k]
1139
1140
1141
1142def check_forcefield(f):
1143    """Check that f is either
1144    1: a callable object f(t,x,y), where x and y are vectors
1145       and that it returns an array or a list of same length
1146       as x and y
1147    2: a scalar
1148    """
1149
1150    from Numeric import ones, Float, array
1151
1152
1153    if callable(f):
1154        N = 3
1155        x = ones(3, Float)
1156        y = ones(3, Float)
1157        try:
1158            q = f(1.0, x, y)
1159        except Exception, e:
1160            msg = 'Function %s could not be executed:\n%s' %(f, e)
1161            #FIXME: Reconsider this semantics
1162            raise msg
1163
1164        try:
1165            q = array(q).astype(Float)
1166        except:
1167            msg = 'Return value from vector function %s could ' %f
1168            msg += 'not be converted into a Numeric array of floats.\n'
1169            msg += 'Specified function should return either list or array.'
1170            raise msg
1171
1172        msg = 'Return vector from function %s' %f
1173        msg += 'must have same lenght as input vectors'
1174        assert len(q) == N, msg
1175
1176    else:
1177        try:
1178            f = float(f)
1179        except:
1180            msg = 'Force field %s must be either a scalar' %f
1181            msg += ' or a vector function'
1182            raise msg
1183    return f
1184
1185
1186class Wind_stress:
1187    """Apply wind stress to water momentum in terms of
1188    wind speed [m/s] and wind direction [degrees]
1189    """
1190
1191    def __init__(self, *args, **kwargs):
1192        """Initialise windfield from wind speed s [m/s]
1193        and wind direction phi [degrees]
1194
1195        Inputs v and phi can be either scalars or Python functions, e.g.
1196
1197        W = Wind_stress(10, 178)
1198
1199        #FIXME - 'normal' degrees are assumed for now, i.e. the
1200        vector (1,0) has zero degrees.
1201        We may need to convert from 'compass' degrees later on and also
1202        map from True north to grid north.
1203
1204        Arguments can also be Python functions of t,x,y as in
1205
1206        def speed(t,x,y):
1207            ...
1208            return s
1209
1210        def angle(t,x,y):
1211            ...
1212            return phi
1213
1214        where x and y are vectors.
1215
1216        and then pass the functions in
1217
1218        W = Wind_stress(speed, angle)
1219
1220        The instantiated object W can be appended to the list of
1221        forcing_terms as in
1222
1223        Alternatively, one vector valued function for (speed, angle)
1224        can be applied, providing both quantities simultaneously.
1225        As in
1226        W = Wind_stress(F), where returns (speed, angle) for each t.
1227
1228        domain.forcing_terms.append(W)
1229        """
1230
1231        from config import rho_a, rho_w, eta_w
1232        from Numeric import array, Float
1233
1234        if len(args) == 2:
1235            s = args[0]
1236            phi = args[1]
1237        elif len(args) == 1:
1238            #Assume vector function returning (s, phi)(t,x,y)
1239            vector_function = args[0]
1240            s = lambda t,x,y: vector_function(t,x,y)[0]
1241            phi = lambda t,x,y: vector_function(t,x,y)[1]
1242        else:
1243           #Assume info is in 2 keyword arguments
1244
1245           if len(kwargs) == 2:
1246               s = kwargs['s']
1247               phi = kwargs['phi']
1248           else:
1249               raise 'Assumes two keyword arguments: s=..., phi=....'
1250
1251        self.speed = check_forcefield(s)
1252        self.phi = check_forcefield(phi)
1253
1254        self.const = eta_w*rho_a/rho_w
1255
1256
1257    def __call__(self, domain):
1258        """Evaluate windfield based on values found in domain
1259        """
1260
1261        from math import pi, cos, sin, sqrt
1262        from Numeric import Float, ones, ArrayType
1263
1264        xmom_update = domain.quantities['xmomentum'].explicit_update
1265        ymom_update = domain.quantities['ymomentum'].explicit_update
1266
1267        N = domain.number_of_elements
1268        t = domain.time
1269
1270        if callable(self.speed):
1271            xc = domain.get_centroid_coordinates()
1272            s_vec = self.speed(t, xc[:,0], xc[:,1])
1273        else:
1274            #Assume s is a scalar
1275
1276            try:
1277                s_vec = self.speed * ones(N, Float)
1278            except:
1279                msg = 'Speed must be either callable or a scalar: %s' %self.s
1280                raise msg
1281
1282
1283        if callable(self.phi):
1284            xc = domain.get_centroid_coordinates()
1285            phi_vec = self.phi(t, xc[:,0], xc[:,1])
1286        else:
1287            #Assume phi is a scalar
1288
1289            try:
1290                phi_vec = self.phi * ones(N, Float)
1291            except:
1292                msg = 'Angle must be either callable or a scalar: %s' %self.phi
1293                raise msg
1294
1295        assign_windfield_values(xmom_update, ymom_update,
1296                                s_vec, phi_vec, self.const)
1297
1298
1299def assign_windfield_values(xmom_update, ymom_update,
1300                            s_vec, phi_vec, const):
1301    """Python version of assigning wind field to update vectors.
1302    A c version also exists (for speed)
1303    """
1304    from math import pi, cos, sin, sqrt
1305
1306    N = len(s_vec)
1307    for k in range(N):
1308        s = s_vec[k]
1309        phi = phi_vec[k]
1310
1311        #Convert to radians
1312        phi = phi*pi/180
1313
1314        #Compute velocity vector (u, v)
1315        u = s*cos(phi)
1316        v = s*sin(phi)
1317
1318        #Compute wind stress
1319        S = const * sqrt(u**2 + v**2)
1320        xmom_update[k] += S*u
1321        ymom_update[k] += S*v
1322
1323
1324
1325##############################
1326#OBSOLETE STUFF
1327
1328def balance_deep_and_shallow_old(domain):
1329    """Compute linear combination between stage as computed by
1330    gradient-limiters and stage computed as constant height above bed.
1331    The former takes precedence when heights are large compared to the
1332    bed slope while the latter takes precedence when heights are
1333    relatively small.  Anything in between is computed as a balanced
1334    linear combination in order to avoid numerical disturbances which
1335    would otherwise appear as a result of hard switching between
1336    modes.
1337    """
1338
1339    #OBSOLETE
1340
1341    #Shortcuts
1342    wc = domain.quantities['stage'].centroid_values
1343    zc = domain.quantities['elevation'].centroid_values
1344    hc = wc - zc
1345
1346    wv = domain.quantities['stage'].vertex_values
1347    zv = domain.quantities['elevation'].vertex_values
1348    hv = wv-zv
1349
1350
1351    #Computed linear combination between constant stages and and
1352    #stages parallel to the bed elevation.
1353    for k in range(domain.number_of_elements):
1354        #Compute maximal variation in bed elevation
1355        #  This quantitiy is
1356        #    dz = max_i abs(z_i - z_c)
1357        #  and it is independent of dimension
1358        #  In the 1d case zc = (z0+z1)/2
1359        #  In the 2d case zc = (z0+z1+z2)/3
1360
1361        dz = max(abs(zv[k,0]-zc[k]),
1362                 abs(zv[k,1]-zc[k]),
1363                 abs(zv[k,2]-zc[k]))
1364
1365
1366        hmin = min( hv[k,:] )
1367
1368        #Create alpha in [0,1], where alpha==0 means using shallow
1369        #first order scheme and alpha==1 means using the stage w as
1370        #computed by the gradient limiter (1st or 2nd order)
1371        #
1372        #If hmin > dz/2 then alpha = 1 and the bed will have no effect
1373        #If hmin < 0 then alpha = 0 reverting to constant height above bed.
1374
1375        if dz > 0.0:
1376            alpha = max( min( 2*hmin/dz, 1.0), 0.0 )
1377        else:
1378            #Flat bed
1379            alpha = 1.0
1380
1381        #Weighted balance between stage parallel to bed elevation
1382        #(wvi = zvi + hc) and stage as computed by 1st or 2nd
1383        #order gradient limiter
1384        #(wvi = zvi + hvi) where i=0,1,2 denotes the vertex ids
1385        #
1386        #It follows that the updated wvi is
1387        #  wvi := (1-alpha)*(zvi+hc) + alpha*(zvi+hvi) =
1388        #  zvi + hc + alpha*(hvi - hc)
1389        #
1390        #Note that hvi = zc+hc-zvi in the first order case (constant).
1391
1392        if alpha < 1:
1393            for i in range(3):
1394                wv[k,i] = zv[k,i] + hc[k] + alpha*(hv[k,i]-hc[k])
1395
1396
1397            #Momentums at centroids
1398            xmomc = domain.quantities['xmomentum'].centroid_values
1399            ymomc = domain.quantities['ymomentum'].centroid_values
1400
1401            #Momentums at vertices
1402            xmomv = domain.quantities['xmomentum'].vertex_values
1403            ymomv = domain.quantities['ymomentum'].vertex_values
1404
1405            # Update momentum as a linear combination of
1406            # xmomc and ymomc (shallow) and momentum
1407            # from extrapolator xmomv and ymomv (deep).
1408            xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:]
1409            ymomv[k,:] = (1-alpha)*ymomc[k] + alpha*ymomv[k,:]
1410
1411
1412
1413###########################
1414###########################
1415#Geometries
1416
1417
1418#FIXME: Rethink this way of creating values.
1419
1420
1421class Weir:
1422    """Set a bathymetry for weir with a hole and a downstream gutter
1423    x,y are assumed to be in the unit square
1424    """
1425
1426    def __init__(self, stage):
1427        self.inflow_stage = stage
1428
1429    def __call__(self, x, y):
1430        from Numeric import zeros, Float
1431        from math import sqrt
1432
1433        N = len(x)
1434        assert N == len(y)
1435
1436        z = zeros(N, Float)
1437        for i in range(N):
1438            z[i] = -x[i]/2  #General slope
1439
1440            #Flattish bit to the left
1441            if x[i] < 0.3:
1442                z[i] = -x[i]/10
1443
1444            #Weir
1445            if x[i] >= 0.3 and x[i] < 0.4:
1446                z[i] = -x[i]+0.9
1447
1448            #Dip
1449            x0 = 0.6
1450            #depth = -1.3
1451            depth = -1.0
1452            #plateaux = -0.9
1453            plateaux = -0.6
1454            if y[i] < 0.7:
1455                if x[i] > x0 and x[i] < 0.9:
1456                    z[i] = depth
1457
1458                #RHS plateaux
1459                if x[i] >= 0.9:
1460                    z[i] = plateaux
1461
1462
1463            elif y[i] >= 0.7 and y[i] < 1.5:
1464                #Restrict and deepen
1465                if x[i] >= x0 and x[i] < 0.8:
1466                    z[i] = depth-(y[i]/3-0.3)
1467                    #z[i] = depth-y[i]/5
1468                    #z[i] = depth
1469                elif x[i] >= 0.8:
1470                    #RHS plateaux
1471                    z[i] = plateaux
1472
1473            elif y[i] >= 1.5:
1474                if x[i] >= x0 and x[i] < 0.8 + (y[i]-1.5)/1.2:
1475                    #Widen up and stay at constant depth
1476                    z[i] = depth-1.5/5
1477                elif x[i] >= 0.8 + (y[i]-1.5)/1.2:
1478                    #RHS plateaux
1479                    z[i] = plateaux
1480
1481
1482            #Hole in weir (slightly higher than inflow condition)
1483            if x[i] >= 0.3 and x[i] < 0.4 and y[i] > 0.2 and y[i] < 0.4:
1484                z[i] = -x[i]+self.inflow_stage + 0.02
1485
1486            #Channel behind weir
1487            x0 = 0.5
1488            if x[i] >= 0.4 and x[i] < x0 and y[i] > 0.2 and y[i] < 0.4:
1489                z[i] = -x[i]+self.inflow_stage + 0.02
1490
1491            if x[i] >= x0 and x[i] < 0.6 and y[i] > 0.2 and y[i] < 0.4:
1492                #Flatten it out towards the end
1493                z[i] = -x0+self.inflow_stage + 0.02 + (x0-x[i])/5
1494
1495            #Hole to the east
1496            x0 = 1.1; y0 = 0.35
1497            #if x[i] < -0.2 and y < 0.5:
1498            if sqrt((2*(x[i]-x0))**2 + (2*(y[i]-y0))**2) < 0.2:
1499                z[i] = sqrt(((x[i]-x0))**2 + ((y[i]-y0))**2)-1.0
1500
1501            #Tiny channel draining hole
1502            if x[i] >= 1.14 and x[i] < 1.2 and y[i] >= 0.4 and y[i] < 0.6:
1503                z[i] = -0.9 #North south
1504
1505            if x[i] >= 0.9 and x[i] < 1.18 and y[i] >= 0.58 and y[i] < 0.65:
1506                z[i] = -1.0 + (x[i]-0.9)/3 #East west
1507
1508
1509
1510            #Stuff not in use
1511
1512            #Upward slope at inlet to the north west
1513            #if x[i] < 0.0: # and y[i] > 0.5:
1514            #    #z[i] = -y[i]+0.5  #-x[i]/2
1515            #    z[i] = x[i]/4 - y[i]**2 + 0.5
1516
1517            #Hole to the west
1518            #x0 = -0.4; y0 = 0.35 # center
1519            #if sqrt((2*(x[i]-x0))**2 + (2*(y[i]-y0))**2) < 0.2:
1520            #    z[i] = sqrt(((x[i]-x0))**2 + ((y[i]-y0))**2)-0.2
1521
1522
1523
1524
1525
1526        return z/2
1527
1528class Weir_simple:
1529    """Set a bathymetry for weir with a hole and a downstream gutter
1530    x,y are assumed to be in the unit square
1531    """
1532
1533    def __init__(self, stage):
1534        self.inflow_stage = stage
1535
1536    def __call__(self, x, y):
1537        from Numeric import zeros, Float
1538
1539        N = len(x)
1540        assert N == len(y)
1541
1542        z = zeros(N, Float)
1543        for i in range(N):
1544            z[i] = -x[i]  #General slope
1545
1546            #Flat bit to the left
1547            if x[i] < 0.3:
1548                z[i] = -x[i]/10  #General slope
1549
1550            #Weir
1551            if x[i] > 0.3 and x[i] < 0.4:
1552                z[i] = -x[i]+0.9
1553
1554            #Dip
1555            if x[i] > 0.6 and x[i] < 0.9:
1556                z[i] = -x[i]-0.5  #-y[i]/5
1557
1558            #Hole in weir (slightly higher than inflow condition)
1559            if x[i] > 0.3 and x[i] < 0.4 and y[i] > 0.2 and y[i] < 0.4:
1560                z[i] = -x[i]+self.inflow_stage + 0.05
1561
1562
1563        return z/2
1564
1565
1566
1567class Constant_stage:
1568    """Set an initial condition with constant stage
1569    """
1570    def __init__(self, s):
1571        self.s = s
1572
1573    def __call__(self, x, y):
1574        return self.s
1575
1576class Constant_height:
1577    """Set an initial condition with constant water height, e.g
1578    stage s = z+h
1579    """
1580
1581    def __init__(self, W, h):
1582        self.W = W
1583        self.h = h
1584
1585    def __call__(self, x, y):
1586        if self.W is None:
1587            from Numeric import ones, Float
1588            return self.h*ones(len(x), Float)
1589        else:
1590            return self.W(x,y) + self.h
1591
1592
1593
1594
1595def compute_fluxes_python(domain):
1596    """Compute all fluxes and the timestep suitable for all volumes
1597    in domain.
1598
1599    Compute total flux for each conserved quantity using "flux_function"
1600
1601    Fluxes across each edge are scaled by edgelengths and summed up
1602    Resulting flux is then scaled by area and stored in
1603    explicit_update for each of the three conserved quantities
1604    stage, xmomentum and ymomentum
1605
1606    The maximal allowable speed computed by the flux_function for each volume
1607    is converted to a timestep that must not be exceeded. The minimum of
1608    those is computed as the next overall timestep.
1609
1610    Post conditions:
1611      domain.explicit_update is reset to computed flux values
1612      domain.timestep is set to the largest step satisfying all volumes.
1613    """
1614
1615    import sys
1616    from Numeric import zeros, Float
1617
1618    N = domain.number_of_elements
1619
1620    #Shortcuts
1621    Stage = domain.quantities['stage']
1622    Xmom = domain.quantities['xmomentum']
1623    Ymom = domain.quantities['ymomentum']
1624    Bed = domain.quantities['elevation']
1625
1626    #Arrays
1627    stage = Stage.edge_values
1628    xmom =  Xmom.edge_values
1629    ymom =  Ymom.edge_values
1630    bed =   Bed.edge_values
1631
1632    stage_bdry = Stage.boundary_values
1633    xmom_bdry =  Xmom.boundary_values
1634    ymom_bdry =  Ymom.boundary_values
1635
1636    flux = zeros((N,3), Float) #Work array for summing up fluxes
1637
1638    #Loop
1639    timestep = float(sys.maxint)
1640    for k in range(N):
1641
1642        for i in range(3):
1643            #Quantities inside volume facing neighbour i
1644            ql = [stage[k, i], xmom[k, i], ymom[k, i]]
1645            zl = bed[k, i]
1646
1647            #Quantities at neighbour on nearest face
1648            n = domain.neighbours[k,i]
1649            if n < 0:
1650                m = -n-1 #Convert negative flag to index
1651                qr = [stage_bdry[m], xmom_bdry[m], ymom_bdry[m]]
1652                zr = zl #Extend bed elevation to boundary
1653            else:
1654                m = domain.neighbour_edges[k,i]
1655                qr = [stage[n, m], xmom[n, m], ymom[n, m]]
1656                zr = bed[n, m]
1657
1658
1659            #Outward pointing normal vector
1660            normal = domain.normals[k, 2*i:2*i+2]
1661
1662            #Flux computation using provided function
1663            edgeflux, max_speed = flux_function(normal, ql, qr, zl, zr)
1664
1665            flux[k,:] = edgeflux
1666
1667    return flux
1668
1669
1670
1671
1672
1673
1674
1675##############################################
1676#Initialise module
1677
1678
1679import compile
1680if compile.can_use_C_extension('shallow_water_ext.c'):
1681    #Replace python version with c implementations
1682
1683    from shallow_water_ext import rotate, assign_windfield_values
1684    compute_fluxes = compute_fluxes_c
1685    extrapolate_second_order_sw=extrapolate_second_order_sw_c
1686    gravity = gravity_c
1687    manning_friction = manning_friction_c
1688    h_limiter = h_limiter_c
1689    balance_deep_and_shallow = balance_deep_and_shallow_c
1690    protect_against_infinitesimal_and_negative_heights = protect_against_infinitesimal_and_negative_heights_c
1691
1692
1693    #distribute_to_vertices_and_edges = distribute_to_vertices_and_edges_c
1694
1695
1696
1697#Optimisation with psyco
1698from config import use_psyco
1699if use_psyco:
1700    try:
1701        import psyco
1702    except:
1703        import os
1704        if os.name == 'posix' and os.uname()[4] == 'x86_64':
1705            pass
1706            #Psyco isn't supported on 64 bit systems, but it doesn't matter
1707        else:
1708            msg = 'WARNING: psyco (speedup) could not import'+\
1709                  ', you may want to consider installing it'
1710            print msg
1711    else:
1712        psyco.bind(Domain.distribute_to_vertices_and_edges)
1713        psyco.bind(Domain.compute_fluxes)
1714
1715if __name__ == "__main__":
1716    pass
Note: See TracBrowser for help on using the repository browser.