source: trunk/anuga_work/development/gareth/experimental/balanced_dev/swb2_domain.py @ 8751

Last change on this file since 8751 was 8751, checked in by davies, 12 years ago

Updates to gd in-development algorithms

File size: 23.9 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.00)
66        self.set_use_kinematic_viscosity(False)
67        self.timestepping_method='rk2' #'euler'#'rk2'#'euler'#'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 shallow_water_balanced2 solver in anuga_work/development/gareth/experimental/balanced_dev/'
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 util.py script in anuga_work/development/gareth/tests 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 the minimum bed_edge_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 '##########################################################################'
143
144    #-------------------------------
145    # Specialisation of Evolve
146    #-------------------------------
147    # This alters the 'evolve' method from shallow_water_domain.py so that just
148    # before the file-output step, the vertex values are replaced with the
149    # centroid values.
150    # This is useful so that we can unambigously know what the centroid values
151    # of various parameters are
152
153    def evolve(self,
154               yieldstep=None,
155               finaltime=None,
156               duration=None,
157               skip_initial_step=False):
158        """Specialisation of basic evolve method from parent class.
159       
160            Evolve the model by 1 step.
161        """
162
163        # Call check integrity here rather than from user scripts
164        # self.check_integrity()
165
166        #print 'Into Evolve'
167
168        msg = 'Attribute self.beta_w must be in the interval [0, 2]'
169        assert 0 <= self.beta_w <= 2.0, msg
170
171        # Initial update of vertex and edge values before any STORAGE
172        # and or visualisation.
173        # This is done again in the initialisation of the Generic_Domain
174        # evolve loop but we do it here to ensure the values are ok for storage.
175        self.distribute_to_vertices_and_edges()
176
177        if self.store is True and self.get_time() == self.get_starttime():
178            self.initialise_storage()
179        #print 'Into Generic_Domain Evolve'
180        # Call basic machinery from parent class
181        for t in Generic_Domain.evolve(self, yieldstep=yieldstep,
182                                       finaltime=finaltime, duration=duration,
183                                       skip_initial_step=skip_initial_step):
184            #print 'Out of Generic_Domain Evolve'
185            # Store model data, e.g. for subsequent visualisation
186            if self.store is True:
187                # FIXME: Extrapolation is done before writing, because I had
188                # trouble correctly computing the centroid values from the
189                # vertex values (an 'average' did not seem to work correctly in
190                # very shallow cells -- there was a discrepency between
191                # domain.quantity['blah'].centroid_values and the values
192                # computed from the sww using the vertex averge). There should
193                # be a much much more disk-efficient way to store the centroid
194                # values than this
195                self.extrapolate_second_order_edge_sw()
196
197                # Store the timestep
198                self.store_timestep()
199
200            # Pass control on to outer loop for more specific actions
201            yield(t)
202   
203    #-----------------
204    # Flux computation
205    #-----------------
206
207    ## @brief Compute fluxes and timestep suitable for all volumes in domain.
208    # @param domain The domain to calculate fluxes for.
209    def compute_fluxes(domain):
210        """Compute fluxes and timestep suitable for all volumes in domain.
211
212        Compute total flux for each conserved quantity using "flux_function"
213
214        Fluxes across each edge are scaled by edgelengths and summed up
215        Resulting flux is then scaled by area and stored in
216        explicit_update for each of the three conserved quantities
217        stage, xmomentum and ymomentum
218
219        The maximal allowable speed computed by the flux_function for each volume
220        is converted to a timestep that must not be exceeded. The minimum of
221        those is computed as the next overall timestep.
222
223        Post conditions:
224          domain.explicit_update is reset to computed flux values
225          domain.timestep is set to the largest step satisfying all volumes.
226
227        This wrapper calls the underlying C version of compute fluxes
228        """
229
230        import sys
231        from swb2_domain_ext import compute_fluxes_ext_central \
232                                      as compute_fluxes_ext
233
234        #print "."
235
236        # Shortcuts
237        if(domain.timestepping_method!='rk2'):
238            err_mess='ERROR: Timestepping method is ' + domain.timestepping_method +'. '+\
239                     'You need to use rk2 timestepping with this solver, ' +\
240                     ' because there is presently a hack in compute fluxes central. \n'+\
241                     ' The HACK: The timestep will only ' +\
242                     'be recomputed on every 2nd call to compute_fluxes_central, to support'+\
243                     ' correct treatment of wetting and drying'
244
245            raise Exception, err_mess
246
247        Stage = domain.quantities['stage']
248        Xmom = domain.quantities['xmomentum']
249        Ymom = domain.quantities['ymomentum']
250        Bed = domain.quantities['elevation']
251
252        timestep = float(sys.maxint)
253
254        flux_timestep = compute_fluxes_ext(timestep,
255                                           domain.epsilon,
256                                           domain.H0,
257                                           domain.g,
258                                           domain.neighbours,
259                                           domain.neighbour_edges,
260                                           domain.normals,
261                                           domain.edgelengths,
262                                           domain.radii,
263                                           domain.areas,
264                                           domain.tri_full_flag,
265                                           Stage.edge_values,
266                                           Xmom.edge_values,
267                                           Ymom.edge_values,
268                                           Bed.edge_values,
269                                           Stage.boundary_values,
270                                           Xmom.boundary_values,
271                                           Ymom.boundary_values,
272                                           domain.boundary_flux_type,
273                                           Stage.explicit_update,
274                                           Xmom.explicit_update,
275                                           Ymom.explicit_update,
276                                           domain.already_computed_flux,
277                                           domain.max_speed,
278                                           int(domain.optimise_dry_cells),
279                                           Stage.centroid_values,
280                                           Xmom.centroid_values,
281                                           Ymom.centroid_values,
282                                           Bed.centroid_values, 
283                                           Bed.vertex_values)
284
285        #import pdb
286        #pdb.set_trace()
287
288        domain.flux_timestep = flux_timestep
289
290
291    def protect_against_infinitesimal_and_negative_heights(domain):
292        """protect against infinitesimal heights and associated high velocities"""
293
294        from swb2_domain_ext import protect
295        #print'using swb2_protect_against ..'
296
297        # shortcuts
298        wc = domain.quantities['stage'].centroid_values
299        wv = domain.quantities['stage'].vertex_values
300        zc = domain.quantities['elevation'].centroid_values
301        zv = domain.quantities['elevation'].vertex_values
302        xmomc = domain.quantities['xmomentum'].centroid_values
303        ymomc = domain.quantities['ymomentum'].centroid_values
304        areas = domain.areas
305        xc = domain.centroid_coordinates[:,0]
306        yc = domain.centroid_coordinates[:,1] 
307
308        protect(domain.minimum_allowed_height, domain.maximum_allowed_speed,
309                domain.epsilon, wc, wv, zc,zv, xmomc, ymomc, areas, xc, yc)
310
311    def conserved_values_to_evolved_values(self, q_cons, q_evol):
312        """Mapping between conserved quantities and the evolved quantities.
313        Used where we have a boundary condition which works with conserved
314        quantities and we now want to use them for the new well balanced
315        code using the evolved quantities
316
317        Typically the old boundary condition will set the values in q_cons,
318
319        q_evol on input will have the values of the evolved quantities at the
320        edge point (useful as it provides values for evlevation).
321
322        """
323
324        wc  = q_cons[0]
325        uhc = q_cons[1]
326        vhc = q_cons[2]
327
328
329        q_evol[0]  = wc
330        q_evol[1]  = uhc
331        q_evol[2]  = vhc
332
333        return q_evol
334
335    def distribute_to_vertices_and_edges(self):
336        """ Call correct module function """
337
338        if self.use_edge_limiter:
339            #distribute_using_edge_limiter(self)
340            self.distribute_using_edge_limiter()
341        else:
342            #distribute_using_vertex_limiter(self)
343            self.distribute_using_vertex_limiter()
344
345
346    def distribute_using_vertex_limiter(domain):
347        """Distribution from centroids to vertices specific to the SWW equation.
348
349        Precondition:
350          All quantities defined at centroids and bed elevation defined at
351          vertices.
352
353        Postcondition
354          Conserved quantities defined at vertices
355        """
356        # Remove very thin layers of water
357        domain.protect_against_infinitesimal_and_negative_heights()
358
359        # Extrapolate all conserved quantities
360        if domain.optimised_gradient_limiter:
361            # MH090605 if second order,
362            # perform the extrapolation and limiting on
363            # all of the conserved quantities
364
365            if (domain._order_ == 1):
366                for name in domain.conserved_quantities:
367                    Q = domain.quantities[name]
368                    Q.extrapolate_first_order()
369            elif domain._order_ == 2:
370                domain.extrapolate_second_order_sw()
371            else:
372                raise Exception('Unknown order')
373        else:
374            # Old code:
375            for name in domain.conserved_quantities:
376                Q = domain.quantities[name]
377
378                if domain._order_ == 1:
379                    Q.extrapolate_first_order()
380                elif domain._order_ == 2:
381                    Q.extrapolate_second_order_and_limit_by_vertex()
382                else:
383                    raise Exception('Unknown order')
384
385        # Take bed elevation into account when water heights are small
386        #balance_deep_and_shallow(domain)
387
388        # Compute edge values by interpolation
389        for name in domain.conserved_quantities:
390            Q = domain.quantities[name]
391            Q.interpolate_from_vertices_to_edges()
392
393
394    def distribute_using_edge_limiter(domain):
395        """Distribution from centroids to edges specific to the SWW eqn.
396
397        Precondition:
398          All quantities defined at centroids and bed elevation defined at
399          vertices.
400
401        Postcondition
402          Conserved quantities defined at vertices
403        """
404        #from swb2_domain import protect_against_infinitesimal_and_negative_heights
405
406        # Remove very thin layers of water
407        #print 'Before protect'
408        domain.protect_against_infinitesimal_and_negative_heights()
409        #print 'After protect'
410
411        #for name in domain.conserved_quantities:
412        #    Q = domain.quantities[name]
413        #    if domain._order_ == 1:
414        #        Q.extrapolate_first_order()
415        #    elif domain._order_ == 2:
416        #        #Q.extrapolate_second_order_and_limit_by_edge()
417        #        # FIXME: This use of 'break' is hacky
418        #        domain.extrapolate_second_order_edge_sw()
419        #        break
420        #    else:
421        #        raise Exception('Unknown order')
422       
423        #print 'Before extrapolate'
424        domain.extrapolate_second_order_edge_sw()
425        #print 'After extrapolate'
426
427        #balance_deep_and_shallow(domain)
428
429        # Compute vertex values by interpolation
430        #for name in domain.conserved_quantities:
431        #    Q = domain.quantities[name]
432        #    Q.interpolate_from_edges_to_vertices()
433        #    Q.interpolate_from_vertices_to_edges()
434
435
436    def update_other_quantities(self):
437        """ There may be a need to calculates some of the other quantities
438        based on the new values of conserved quantities
439
440        But that is not needed in this version of the solver.
441        """
442        pass
443        # The centroid values of height and x and y velocity
444        # might not have been setup
445       
446        #self.update_centroids_of_velocities_and_height()
447        #
448        #for name in ['height', 'xvelocity', 'yvelocity']:
449        #    Q = self.quantities[name]
450        #    if self._order_ == 1:
451        #        Q.extrapolate_first_order()
452        #    elif self._order_ == 2:
453        #        Q.extrapolate_second_order_and_limit_by_edge()
454        #    else:
455        #        raise Exception('Unknown order')
456
457
458    def update_centroids_of_velocities_and_height(self):
459        """Calculate the centroid values of velocities and height based
460        on the values of the quantities stage and x and y momentum
461
462        Assumes that stage and momentum are up to date
463
464        Useful for kinematic viscosity calculations
465        """
466        pass
467        ## For shallow water we need to update height xvelocity and yvelocity
468
469        ##Shortcuts
470        #W  = self.quantities['stage']
471        #UH = self.quantities['xmomentum']
472        #VH = self.quantities['ymomentum']
473        #H  = self.quantities['height']
474        #Z  = self.quantities['elevation']
475        #U  = self.quantities['xvelocity']
476        #V  = self.quantities['yvelocity']
477
478        ##print num.min(W.centroid_values)
479
480        ## Make sure boundary values of conserved quantites
481        ## are consistent with value of functions at centroids
482        ##self.distribute_to_vertices_and_edges()
483        #Z.set_boundary_values_from_edges()
484
485        ##W.set_boundary_values_from_edges()
486        ##UH.set_boundary_values_from_edges()
487        ##VH.set_boundary_values_from_edges()
488
489        ## Update height values
490        #H.set_values(W.centroid_values-Z.centroid_values, location='centroids')
491        #H.set_boundary_values( num.where(W.boundary_values-Z.boundary_values>=0,
492        #                                 W.boundary_values-Z.boundary_values, 0.0))
493
494        ##assert num.min(H.centroid_values) >= 0
495        ##assert num.min(H.boundary_values) >= 0
496
497        ##Aliases
498        #uh_C  = UH.centroid_values
499        #vh_C  = VH.centroid_values
500        #h_C   = H.centroid_values
501
502        #uh_B  = UH.boundary_values
503        #vh_B  = VH.boundary_values
504        #h_B   = H.boundary_values
505
506        #H0 = 1.0e-8
507        #
508        #U.set_values(uh_C/(h_C + H0/h_C), location='centroids')
509        #V.set_values(vh_C/(h_C + H0/h_C), location='centroids')
510
511        #U.set_boundary_values(uh_B/(h_B + H0/h_B))
512        #V.set_boundary_values(vh_B/(h_B + H0/h_B))
513
514
515
516    def extrapolate_second_order_edge_sw(self):
517        """Wrapper calling C version of extrapolate_second_order_sw, using
518            edges instead of vertices in the extrapolation step. The routine
519            then interpolates from edges to vertices.
520            The momentum extrapolation / interpolation is based on either
521            momentum or velocity, depending on the choice of
522            extrapolate_velocity_second_order.
523
524        self  the domain to operate on
525
526        """
527
528        from swb2_domain_ext import extrapolate_second_order_edge_sw as extrapol2
529
530        # Shortcuts
531        Stage = self.quantities['stage']
532        Xmom = self.quantities['xmomentum']
533        Ymom = self.quantities['ymomentum']
534        Elevation = self.quantities['elevation']
535
536        extrapol2(self,
537                  self.surrogate_neighbours,
538                  self.neighbour_edges,
539                  self.number_of_boundaries,
540                  self.centroid_coordinates,
541                  Stage.centroid_values,
542                  Xmom.centroid_values,
543                  Ymom.centroid_values,
544                  Elevation.centroid_values,
545                  self.edge_coordinates,
546                  Stage.edge_values,
547                  Xmom.edge_values,
548                  Ymom.edge_values,
549                  Elevation.edge_values,
550                  Stage.vertex_values,
551                  Xmom.vertex_values,
552                  Ymom.vertex_values,
553                  Elevation.vertex_values,
554                  int(self.optimise_dry_cells),
555                  int(self.extrapolate_velocity_second_order))
556
557    def set_boundary(self, boundary_map):
558        ## Specialisation of set_boundary, which also updates the 'boundary_flux_type'
559        Sww_domain.set_boundary(self,boundary_map)
560       
561        # Add a flag which can be used to distinguish flux boundaries within
562        # compute_fluxes_central
563        # Initialise to zero (which means 'not a flux_boundary')
564        self.boundary_flux_type = self.boundary_edges*0
565       
566        # HACK to set the values of domain.boundary_flux
567        for i in range(len(self.boundary_objects)):
568            # Record the first 10 characters of the name of the boundary.
569            # FIXME: There must be a better way than this!
570            bndry_name = self.boundary_objects[i][1].__repr__()[0:10]
571
572            if(bndry_name=='zero_mass_'):
573                # Create flag 'boundary_flux_type' that can be read in
574                # compute_fluxes_central
575                self.boundary_flux_type[i]=1
576
577
Note: See TracBrowser for help on using the repository browser.