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

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

Updates to gd experimental

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