source: trunk/anuga_work/development/gareth/experimental/anuga_tsunami/swb2_domain.py @ 8932

Last change on this file since 8932 was 8607, checked in by steve, 12 years ago

Added sww_merge dummy to shallow_water_domain

File size: 23.1 KB
Line 
1
2from anuga.shallow_water.shallow_water_domain import *
3from anuga.shallow_water.shallow_water_domain import Domain as Sww_domain
4
5
6##############################################################################
7# Shallow Water Balanced Domain -- alternative implementation
8#
9# FIXME: Following the methods in CITE MODSIM PAPER
10#
11##############################################################################
12
13class Domain(Sww_domain):
14
15    def __init__(self,
16                 coordinates=None,
17                 vertices=None,
18                 boundary=None,
19                 tagged_elements=None,
20                 geo_reference=None,
21                 use_inscribed_circle=False,
22                 mesh_filename=None,
23                 use_cache=False,
24                 verbose=False,
25                 full_send_dict=None,
26                 ghost_recv_dict=None,
27                 starttime=0.0,
28                 processor=0,
29                 numproc=1,
30                 number_of_full_nodes=None,
31                 number_of_full_triangles=None):
32
33        conserved_quantities = [ 'stage', 'xmomentum', 'ymomentum']
34
35        evolved_quantities = [ 'stage', 'xmomentum', 'ymomentum']         
36        other_quantities   = [ 'elevation', 'friction', 'height', 
37                               'xvelocity', 'yvelocity', 'x', 'y' ]
38
39       
40        Sww_domain.__init__(self,
41                            coordinates = coordinates,
42                            vertices = vertices,
43                            boundary = boundary,
44                            tagged_elements = tagged_elements,
45                            geo_reference = geo_reference,
46                            use_inscribed_circle = use_inscribed_circle,
47                            mesh_filename = mesh_filename,
48                            use_cache = use_cache,
49                            verbose = verbose,
50                            conserved_quantities = conserved_quantities,
51                            evolved_quantities = evolved_quantities,
52                            other_quantities = other_quantities,
53                            full_send_dict = full_send_dict,
54                            ghost_recv_dict = ghost_recv_dict,
55                            starttime = starttime,
56                            processor = processor,
57                            numproc = numproc,
58                            number_of_full_nodes = number_of_full_nodes,
59                            number_of_full_triangles = number_of_full_triangles)
60       
61        #------------------------------------------------
62        # set some defaults
63        # Most of these override the options in config.py
64        #------------------------------------------------
65        self.set_CFL(1.0)
66        self.set_use_kinematic_viscosity(False)
67        self.timestepping_method='rk2' 
68        # The following allows storage of the negative depths associated with this method
69        self.minimum_storable_height=-99999999999.0 
70
71        self.use_edge_limiter=True
72        self.default_order=2
73        self.extrapolate_velocity_second_order=True
74
75        # Note that the extrapolation method used in quantity_ext.c (e.g.
76        # extrapolate_second_order_and_limit_by_edge) uses a constant value for
77        # all the betas. 
78        self.beta_w=1.0
79        self.beta_w_dry=1.0
80        self.beta_uh=1.0
81        self.beta_uh_dry=1.0
82        self.beta_vh=1.0
83        self.beta_vh_dry=1.0
84
85        #self.optimise_dry_cells=True
86        # "self.optimise_dry_cells=False" presently ensures that the stage is
87        # always >= minimum_bed_edge value.  Actually, we should only need to
88        # apply 'False' on the very first time step (to deal with stage values
89        # that were initialised below the bed by the user). After this, the
90        # algorithm should take care of itself, and 'True' should be okay.
91        self.optimise_dry_cells=False 
92
93        # Because gravity is treated within the flux function,
94        # we remove it from the forcing terms.
95        #self.forcing_terms.remove(gravity)
96
97        # We need the edge_coordinates for the extrapolation
98        self.edge_coordinates=self.get_edge_midpoint_coordinates()
99
100        # We demand that vertex values are stored uniquely
101        self.set_store_vertices_smoothly(False)
102
103        self.maximum_allowed_speed=0.0
104        #self.forcing_terms.append(manning_friction_explicit)
105        #self.forcing_terms.remove(manning_friction_implicit)
106
107        print '##########################################################################'
108        print '#'
109        print '# Using anuga_tsunami solver in anuga_work/development/gareth/experimental/anuga_tsunami/'
110        print "#"
111        print "# This solver is experimental. Here are some tips on using it"
112        print "#"
113        print "# 1) When plotting outputs, I strongly suggest you examine centroid values, not vertex values"
114        print "# , as the latter can be completely misleading near strong gradients in the flow. "
115        print "# There is a plot_util.py script in anuga_core/utilities/ which might help you extract"
116        print "# quantities at centroid values from sww files."
117        print "# Note that to accuractely compute centroid values from sww files, the files need to store "
118        print "# vertices uniquely. This makes for large sww files (3x), but is the price to pay for the right answer"
119        print "# (unless we alter IO to allow centroids to be written to sww files, which would then affect"
120        print "# ANUGA viewer as well -- I expect this would be lots of work)"
121        print "#"
122        print "# 2) In field scale applications (where the depth is typically > 1m), I suggest you set"
123        print "# domain.minimum_allowed_height=0.01 (the default is 1.0e-3). "
124        print "#"
125        print "# 3) This solver is not expected to perform well in problems with very"
126        print "# shallow water flowing down steep slopes (such that the stage_centroid_value "
127        print "# is less than the maximum bed_edge_value on a given triangle). However, analytical tests"
128        print "# suggest it can do typical wetting/drying situations very well (parabolic oscillations test case) "
129        print "#"
130        print "# 4) This solver allows the stage_centroid_value to drop to slightly below the minimum bed_vertex_value"
131        print "# on it's triangle. In other ANUGA versions (e.g. 1.2.1), the limit would be the"
132        print "# bed_centroid_value. This means that triangles store slightly more water than they are"
133        print "# typically interpreted to store, which might have significance in some applications."
134        print "#"
135        print "# You will probably be able to tell this is causing you problems by convergence testing"
136        print "#"
137        print '# 5) Note that many options in config.py have been overridden by the solver -- I have '
138        print '# deliberately attempted to get the solver to perform well with consistent values of '
139        print '# these parameters -- so I would advise against changing them unless you at least check that '
140        print '# it really does improve things'
141        print '#'
142        print '# Gareth Davies'
143        print '#'
144        print '##########################################################################'
145
146    #-------------------------------
147    # Specialisation of Evolve
148    #-------------------------------
149    # This alters the 'evolve' method from shallow_water_domain.py so that just
150    # before the file-output step, the vertex values are replaced with the
151    # centroid values.
152    # This is useful so that we can unambigously know what the centroid values
153    # of various parameters are
154
155    def evolve(self,
156               yieldstep=None,
157               finaltime=None,
158               duration=None,
159               skip_initial_step=False):
160        """Specialisation of basic evolve method from parent class.
161       
162            Evolve the model by 1 step.
163        """
164
165        # Call check integrity here rather than from user scripts
166        # self.check_integrity()
167
168        #print 'Into Evolve'
169
170        msg = 'Attribute self.beta_w must be in the interval [0, 2]'
171        assert 0 <= self.beta_w <= 2.0, msg
172
173        # Initial update of vertex and edge values before any STORAGE
174        # and or visualisation.
175        # This is done again in the initialisation of the Generic_Domain
176        # evolve loop but we do it here to ensure the values are ok for storage.
177        self.distribute_to_vertices_and_edges()
178
179        if self.store is True and self.get_time() == self.get_starttime():
180            self.initialise_storage()
181        #print 'Into Generic_Domain Evolve'
182        # Call basic machinery from parent class
183        for t in Generic_Domain.evolve(self, yieldstep=yieldstep,
184                                       finaltime=finaltime, duration=duration,
185                                       skip_initial_step=skip_initial_step):
186            #print 'Out of Generic_Domain Evolve'
187            # Store model data, e.g. for subsequent visualisation
188            if self.store is True:
189                # FIXME: Extrapolation is done before writing, because I had
190                # trouble correctly computing the centroid values from the
191                # vertex values (an 'average' did not seem to work correctly in
192                # very shallow cells -- there was a discrepency between
193                # domain.quantity['blah'].centroid_values and the values
194                # computed from the sww using the vertex averge). There should
195                # be a much much more disk-efficient way to store the centroid
196                # values than this
197                self.extrapolate_second_order_edge_sw()
198
199                # Store the timestep
200                self.store_timestep()
201
202            # Pass control on to outer loop for more specific actions
203            yield(t)
204   
205    #-----------------
206    # Flux computation
207    #-----------------
208
209    ## @brief Compute fluxes and timestep suitable for all volumes in domain.
210    # @param domain The domain to calculate fluxes for.
211    def compute_fluxes(domain):
212        """Compute fluxes and timestep suitable for all volumes in domain.
213
214        Compute total flux for each conserved quantity using "flux_function"
215
216        Fluxes across each edge are scaled by edgelengths and summed up
217        Resulting flux is then scaled by area and stored in
218        explicit_update for each of the three conserved quantities
219        stage, xmomentum and ymomentum
220
221        The maximal allowable speed computed by the flux_function for each volume
222        is converted to a timestep that must not be exceeded. The minimum of
223        those is computed as the next overall timestep.
224
225        Post conditions:
226          domain.explicit_update is reset to computed flux values
227          domain.timestep is set to the largest step satisfying all volumes.
228
229        This wrapper calls the underlying C version of compute fluxes
230        """
231
232        import sys
233        from swb2_domain_ext import compute_fluxes_ext_central \
234                                      as compute_fluxes_ext
235
236        #print "."
237
238        # Shortcuts
239        Stage = domain.quantities['stage']
240        Xmom = domain.quantities['xmomentum']
241        Ymom = domain.quantities['ymomentum']
242        Bed = domain.quantities['elevation']
243
244        timestep = float(sys.maxint)
245
246        flux_timestep = compute_fluxes_ext(timestep,
247                                           domain.epsilon,
248                                           domain.H0,
249                                           domain.g,
250                                           domain.neighbours,
251                                           domain.neighbour_edges,
252                                           domain.normals,
253                                           domain.edgelengths,
254                                           domain.radii,
255                                           domain.areas,
256                                           domain.tri_full_flag,
257                                           Stage.edge_values,
258                                           Xmom.edge_values,
259                                           Ymom.edge_values,
260                                           Bed.edge_values,
261                                           Stage.boundary_values,
262                                           Xmom.boundary_values,
263                                           Ymom.boundary_values,
264                                           domain.boundary_flux_type,
265                                           Stage.explicit_update,
266                                           Xmom.explicit_update,
267                                           Ymom.explicit_update,
268                                           domain.already_computed_flux,
269                                           domain.max_speed,
270                                           int(domain.optimise_dry_cells),
271                                           Stage.centroid_values,
272                                           Bed.centroid_values, 
273                                           Bed.vertex_values)
274
275        #import pdb
276        #pdb.set_trace()
277
278        domain.flux_timestep = flux_timestep
279
280
281    def protect_against_infinitesimal_and_negative_heights(domain):
282        """protect against infinitesimal heights and associated high velocities"""
283
284        from swb2_domain_ext import protect
285        #print'using swb2_protect_against ..'
286
287        # shortcuts
288        wc = domain.quantities['stage'].centroid_values
289        wv = domain.quantities['stage'].vertex_values
290        zc = domain.quantities['elevation'].centroid_values
291        zv = domain.quantities['elevation'].vertex_values
292        xmomc = domain.quantities['xmomentum'].centroid_values
293        ymomc = domain.quantities['ymomentum'].centroid_values
294        areas = domain.areas
295
296        protect(domain.minimum_allowed_height, domain.maximum_allowed_speed,
297                domain.epsilon, wc, wv, zc,zv, xmomc, ymomc, areas)
298
299    def conserved_values_to_evolved_values(self, q_cons, q_evol):
300        """Mapping between conserved quantities and the evolved quantities.
301        Used where we have a boundary condition which works with conserved
302        quantities and we now want to use them for the new well balanced
303        code using the evolved quantities
304
305        Typically the old boundary condition will set the values in q_cons,
306
307        q_evol on input will have the values of the evolved quantities at the
308        edge point (useful as it provides values for evlevation).
309
310        """
311
312        wc  = q_cons[0]
313        uhc = q_cons[1]
314        vhc = q_cons[2]
315
316
317        q_evol[0]  = wc
318        q_evol[1]  = uhc
319        q_evol[2]  = vhc
320
321        return q_evol
322
323    def distribute_to_vertices_and_edges(self):
324        """ Call correct module function """
325
326        if self.use_edge_limiter:
327            #distribute_using_edge_limiter(self)
328            self.distribute_using_edge_limiter()
329        else:
330            #distribute_using_vertex_limiter(self)
331            self.distribute_using_vertex_limiter()
332
333
334    def distribute_using_vertex_limiter(domain):
335        """Distribution from centroids to vertices specific to the SWW equation.
336
337        Precondition:
338          All quantities defined at centroids and bed elevation defined at
339          vertices.
340
341        Postcondition
342          Conserved quantities defined at vertices
343        """
344        # Remove very thin layers of water
345        domain.protect_against_infinitesimal_and_negative_heights()
346
347        # Extrapolate all conserved quantities
348        if domain.optimised_gradient_limiter:
349            # MH090605 if second order,
350            # perform the extrapolation and limiting on
351            # all of the conserved quantities
352
353            if (domain._order_ == 1):
354                for name in domain.conserved_quantities:
355                    Q = domain.quantities[name]
356                    Q.extrapolate_first_order()
357            elif domain._order_ == 2:
358                domain.extrapolate_second_order_sw()
359            else:
360                raise Exception('Unknown order')
361        else:
362            # Old code:
363            for name in domain.conserved_quantities:
364                Q = domain.quantities[name]
365
366                if domain._order_ == 1:
367                    Q.extrapolate_first_order()
368                elif domain._order_ == 2:
369                    Q.extrapolate_second_order_and_limit_by_vertex()
370                else:
371                    raise Exception('Unknown order')
372
373        # Take bed elevation into account when water heights are small
374        #balance_deep_and_shallow(domain)
375
376        # Compute edge values by interpolation
377        for name in domain.conserved_quantities:
378            Q = domain.quantities[name]
379            Q.interpolate_from_vertices_to_edges()
380
381
382    def distribute_using_edge_limiter(domain):
383        """Distribution from centroids to edges specific to the SWW eqn.
384
385        Precondition:
386          All quantities defined at centroids and bed elevation defined at
387          vertices.
388
389        Postcondition
390          Conserved quantities defined at vertices
391        """
392        #from swb2_domain import protect_against_infinitesimal_and_negative_heights
393
394        # Remove very thin layers of water
395        #print 'Before protect'
396        domain.protect_against_infinitesimal_and_negative_heights()
397        #print 'After protect'
398
399        #for name in domain.conserved_quantities:
400        #    Q = domain.quantities[name]
401        #    if domain._order_ == 1:
402        #        Q.extrapolate_first_order()
403        #    elif domain._order_ == 2:
404        #        #Q.extrapolate_second_order_and_limit_by_edge()
405        #        # FIXME: This use of 'break' is hacky
406        #        domain.extrapolate_second_order_edge_sw()
407        #        break
408        #    else:
409        #        raise Exception('Unknown order')
410       
411        #print 'Before extrapolate'
412        domain.extrapolate_second_order_edge_sw()
413        #print 'After extrapolate'
414
415        #balance_deep_and_shallow(domain)
416
417        # Compute vertex values by interpolation
418        #for name in domain.conserved_quantities:
419        #    Q = domain.quantities[name]
420        #    Q.interpolate_from_edges_to_vertices()
421        #    Q.interpolate_from_vertices_to_edges()
422
423
424    def update_other_quantities(self):
425        """ There may be a need to calculates some of the other quantities
426        based on the new values of conserved quantities
427
428        But that is not needed in this version of the solver.
429        """
430        pass
431        # The centroid values of height and x and y velocity
432        # might not have been setup
433       
434        #self.update_centroids_of_velocities_and_height()
435        #
436        #for name in ['height', 'xvelocity', 'yvelocity']:
437        #    Q = self.quantities[name]
438        #    if self._order_ == 1:
439        #        Q.extrapolate_first_order()
440        #    elif self._order_ == 2:
441        #        Q.extrapolate_second_order_and_limit_by_edge()
442        #    else:
443        #        raise Exception('Unknown order')
444
445
446    def update_centroids_of_velocities_and_height(self):
447        """Calculate the centroid values of velocities and height based
448        on the values of the quantities stage and x and y momentum
449
450        Assumes that stage and momentum are up to date
451
452        Useful for kinematic viscosity calculations
453        """
454        pass
455        ## For shallow water we need to update height xvelocity and yvelocity
456
457        ##Shortcuts
458        #W  = self.quantities['stage']
459        #UH = self.quantities['xmomentum']
460        #VH = self.quantities['ymomentum']
461        #H  = self.quantities['height']
462        #Z  = self.quantities['elevation']
463        #U  = self.quantities['xvelocity']
464        #V  = self.quantities['yvelocity']
465
466        ##print num.min(W.centroid_values)
467
468        ## Make sure boundary values of conserved quantites
469        ## are consistent with value of functions at centroids
470        ##self.distribute_to_vertices_and_edges()
471        #Z.set_boundary_values_from_edges()
472
473        ##W.set_boundary_values_from_edges()
474        ##UH.set_boundary_values_from_edges()
475        ##VH.set_boundary_values_from_edges()
476
477        ## Update height values
478        #H.set_values(W.centroid_values-Z.centroid_values, location='centroids')
479        #H.set_boundary_values( num.where(W.boundary_values-Z.boundary_values>=0,
480        #                                 W.boundary_values-Z.boundary_values, 0.0))
481
482        ##assert num.min(H.centroid_values) >= 0
483        ##assert num.min(H.boundary_values) >= 0
484
485        ##Aliases
486        #uh_C  = UH.centroid_values
487        #vh_C  = VH.centroid_values
488        #h_C   = H.centroid_values
489
490        #uh_B  = UH.boundary_values
491        #vh_B  = VH.boundary_values
492        #h_B   = H.boundary_values
493
494        #H0 = 1.0e-8
495        #
496        #U.set_values(uh_C/(h_C + H0/h_C), location='centroids')
497        #V.set_values(vh_C/(h_C + H0/h_C), location='centroids')
498
499        #U.set_boundary_values(uh_B/(h_B + H0/h_B))
500        #V.set_boundary_values(vh_B/(h_B + H0/h_B))
501
502
503
504    def extrapolate_second_order_edge_sw(self):
505        """Wrapper calling C version of extrapolate_second_order_sw, using
506            edges instead of vertices in the extrapolation step. The routine
507            then interpolates from edges to vertices.
508            The momentum extrapolation / interpolation is based on either
509            momentum or velocity, depending on the choice of
510            extrapolate_velocity_second_order.
511
512        self  the domain to operate on
513
514        """
515
516        from swb2_domain_ext import extrapolate_second_order_edge_sw as extrapol2
517
518        # Shortcuts
519        Stage = self.quantities['stage']
520        Xmom = self.quantities['xmomentum']
521        Ymom = self.quantities['ymomentum']
522        Elevation = self.quantities['elevation']
523
524        extrapol2(self,
525                  self.surrogate_neighbours,
526                  self.number_of_boundaries,
527                  self.centroid_coordinates,
528                  Stage.centroid_values,
529                  Xmom.centroid_values,
530                  Ymom.centroid_values,
531                  Elevation.centroid_values,
532                  self.edge_coordinates,
533                  Stage.edge_values,
534                  Xmom.edge_values,
535                  Ymom.edge_values,
536                  Elevation.edge_values,
537                  Stage.vertex_values,
538                  Xmom.vertex_values,
539                  Ymom.vertex_values,
540                  Elevation.vertex_values,
541                  int(self.optimise_dry_cells),
542                  int(self.extrapolate_velocity_second_order))
543
544    def set_boundary(self, boundary_map):
545        ## Specialisation of set_boundary, which also updates the 'boundary_flux_type'
546        Sww_domain.set_boundary(self,boundary_map)
547       
548        # Add a flag which can be used to distinguish flux boundaries within
549        # compute_fluxes_central
550        # Initialise to zero (which means 'not a flux_boundary')
551        self.boundary_flux_type = self.boundary_edges*0
552       
553        # HACK to set the values of domain.boundary_flux
554        for i in range(len(self.boundary_objects)):
555            # Record the first 10 characters of the name of the boundary.
556            # FIXME: There must be a better way than this!
557            bndry_name = self.boundary_objects[i][1].__repr__()[0:10]
558
559            if(bndry_name=='zero_mass_'):
560                # Create flag 'boundary_flux_type' that can be read in
561                # compute_fluxes_central
562                self.boundary_flux_type[i]=1
563
564
Note: See TracBrowser for help on using the repository browser.