source: trunk/anuga_work/development/gareth/experimental/bal_and/swb2_domain.py @ 9016

Last change on this file since 9016 was 9016, checked in by davies, 11 years ago

Cleaning dev_audusse -- some problems remain

File size: 26.9 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        #self.forcing_terms.append(manning_friction_implicit)
62        #------------------------------------------------
63        # set some defaults
64        # Most of these override the options in config.py
65        #------------------------------------------------
66        self.set_CFL(1.00)
67        self.set_use_kinematic_viscosity(False)
68        self.timestepping_method='rk2'#'rk2'#'rk3'#'euler'#'rk2'
69        # The following allows storage of the negative depths associated with this method
70        self.minimum_storable_height=-99999999999.0 
71        self.minimum_allowed_height=1.0e-03
72
73        self.use_edge_limiter=True
74        self.default_order=2
75        self.extrapolate_velocity_second_order=True
76
77        self.beta_w=1.0
78        self.beta_w_dry=0.0
79        self.beta_uh=1.0
80        self.beta_uh_dry=0.0
81        self.beta_vh=1.0
82        self.beta_vh_dry=0.0
83       
84
85        self.set_quantities_to_be_stored({'stage': 2, 'xmomentum': 2, 
86                 'ymomentum': 2, 'elevation': 2, 'height':2})
87
88        #self.epsilon=1.0e-100
89
90        #self.optimise_dry_cells=True
91        # "self.optimise_dry_cells=False" presently ensures that the stage is
92        # always >= minimum_bed_edge value.  Actually, we should only need to
93        # apply 'False' on the very first time step (to deal with stage values
94        # that were initialised below the bed by the user). After this, the
95        # algorithm should take care of itself, and 'True' should be okay.
96        self.optimise_dry_cells=False 
97
98        # Because gravity is treated within the flux function,
99        # we remove it from the forcing terms.
100        #self.forcing_terms.remove(gravity)
101
102        # We need the edge_coordinates for the extrapolation
103        self.edge_coordinates=self.get_edge_midpoint_coordinates()
104
105        # We demand that vertex values are stored uniquely
106        self.set_store_vertices_smoothly(False)
107
108        self.maximum_allowed_speed=0.0
109        #self.forcing_terms.append(manning_friction_explicit)
110        #self.forcing_terms.remove(manning_friction_implicit)
111
112        # Keep track of the fluxes through the boundaries
113        self.boundary_flux_integral=numpy.ndarray(1)
114        self.boundary_flux_integral[0]=0. 
115        self.boundary_flux_sum=numpy.ndarray(1)
116        self.boundary_flux_sum[0]=0.
117
118        self.call=1 # Integer counting how many times we call compute_fluxes_central
119
120        # Integer recording the order of the time-stepping scheme
121        if(self.timestepping_method=='rk2'):
122          self.timestep_order=2
123        elif(self.timestepping_method=='euler'):
124          self.timestep_order=1
125        elif(self.timestepping_method=='rk3'):
126          self.timestep_order=3
127        else:
128          err_mess='ERROR: timestepping_method= ' + self.timestepping_method +' not supported in this solver'
129          raise Exception, err_mess
130
131        print '##########################################################################'
132        print '#'
133        print '# Using experimental audusse type solver in anuga_work/development/gareth/experimental/bal_and/'
134        print "#"
135        print "# This solver is experimental. Here are some tips on using it"
136        print "#"
137        print "# 1) When plotting outputs, I strongly suggest you examine centroid values, not vertex values"
138        print "# , as the latter can be completely misleading near strong gradients in the flow. "
139        print "# ALSO, THIS SOLVER HAS VARIABLE BED EXTRAPOLATION THROUGH TIME -- REQUIRES A DIFFERENT APPROACH TO THE OTHER SOLVERS"
140        print '#'
141        print "# There is a util.py script in anuga_work/development/gareth/experimental/bal_and which might help you extract"
142        print "# quantities at centroid values from sww files."
143        print "# Note that to accuractely compute centroid values from sww files, the files need to store "
144        print "# vertices uniquely. This makes for large sww files (3x), but is the price to pay for the right answer"
145        print "# (unless we alter IO to allow centroids to be written to sww files, which would then affect"
146        print "# ANUGA viewer as well -- I expect this would be lots of work)"
147        print "#"
148        print '# 2) Note that many options in config.py have been overridden by the solver -- I have '
149        print '# deliberately attempted to get the solver to perform well with consistent values of '
150        print '# these parameters -- so I would advise against changing them unless you at least check that '
151        print '# it really does improve things'
152        print '#'
153        print '##########################################################################'
154
155    #-------------------------------
156    # Specialisation of Evolve
157    #-------------------------------
158    # This alters the 'evolve' method from shallow_water_domain.py so that just
159    # before the file-output step, the vertex values are replaced with the
160    # centroid values.
161    # This is useful so that we can unambigously know what the centroid values
162    # of various parameters are
163
164    def evolve(self,
165               yieldstep=None,
166               finaltime=None,
167               duration=None,
168               skip_initial_step=False):
169        """Specialisation of basic evolve method from parent class.
170       
171            Evolve the model by 1 step.
172        """
173
174        # Call check integrity here rather than from user scripts
175        # self.check_integrity()
176
177        #print 'Into Evolve'
178
179        msg = 'Attribute self.beta_w must be in the interval [0, 2]'
180        assert 0 <= self.beta_w <= 2.0, msg
181
182        # Initial update of vertex and edge values before any STORAGE
183        # and or visualisation.
184        # This is done again in the initialisation of the Generic_Domain
185        # evolve loop but we do it here to ensure the values are ok for storage.
186        self.distribute_to_vertices_and_edges()
187
188        if self.store is True and self.get_time() == self.get_starttime():
189            self.initialise_storage()
190        #print 'Into Generic_Domain Evolve'
191        # Call basic machinery from parent class
192        for t in Generic_Domain.evolve(self, yieldstep=yieldstep,
193                                       finaltime=finaltime, duration=duration,
194                                       skip_initial_step=skip_initial_step):
195            #print 'Out of Generic_Domain Evolve'
196            # Store model data, e.g. for subsequent visualisation
197            if self.store is True:
198                # FIXME: Extrapolation is done before writing, because I had
199                # trouble correctly computing the centroid values from the
200                # vertex values (an 'average' did not seem to work correctly in
201                # very shallow cells -- there was a discrepency between
202                # domain.quantity['blah'].centroid_values and the values
203                # computed from the sww using the vertex averge). There should
204                # be a much much more disk-efficient way to store the centroid
205                # values than this
206                self.extrapolate_second_order_edge_sw()
207
208                # Store the timestep
209                self.store_timestep()
210
211            # Pass control on to outer loop for more specific actions
212            yield(t)
213   
214    #-----------------
215    # Flux computation
216    #-----------------
217
218    ## @brief Compute fluxes and timestep suitable for all volumes in domain.
219    # @param domain The domain to calculate fluxes for.
220    def compute_fluxes(domain):
221        """Compute fluxes and timestep suitable for all volumes in domain.
222
223        Compute total flux for each conserved quantity using "flux_function"
224
225        Fluxes across each edge are scaled by edgelengths and summed up
226        Resulting flux is then scaled by area and stored in
227        explicit_update for each of the three conserved quantities
228        stage, xmomentum and ymomentum
229
230        The maximal allowable speed computed by the flux_function for each volume
231        is converted to a timestep that must not be exceeded. The minimum of
232        those is computed as the next overall timestep.
233
234        Post conditions:
235          domain.explicit_update is reset to computed flux values
236          domain.timestep is set to the largest step satisfying all volumes.
237
238        This wrapper calls the underlying C version of compute fluxes
239        """
240
241        import sys
242        from swb2_domain_ext import compute_fluxes_ext_central \
243                                      as compute_fluxes_ext
244
245        #print "."
246
247        # Shortcuts
248        #if(domain.timestepping_method!='rk2'):
249        #    err_mess='ERROR: Timestepping method is ' + domain.timestepping_method +'. '+\
250        #             'You need to use rk2 timestepping with this solver, ' +\
251        #             ' because there is presently a hack in compute fluxes central. \n'+\
252        #             ' The HACK: The timestep will only ' +\
253        #             'be recomputed on every 2nd call to compute_fluxes_central, to support'+\
254        #             ' correct treatment of wetting and drying'
255
256        #    raise Exception, err_mess
257
258        #if(domain.timestepping_method=='rk2'):
259        #  timestep_order=2
260        #elif(domain.timestepping_method=='euler'):
261        #  timestep_order=1
262        #elif(domain.timestepping_method=='rk3'):
263        #  timestep_order=3
264        #else:
265        #  err_mess='ERROR: domain.timestepping_method= ' + domain.timestepping_method +' not supported in this solver'
266        #  raise Exception, err_mess
267
268        ##print 'timestep_order=', timestep_order
269
270        Stage = domain.quantities['stage']
271        Xmom = domain.quantities['xmomentum']
272        Ymom = domain.quantities['ymomentum']
273        Bed = domain.quantities['elevation']
274        height = domain.quantities['height']
275
276        timestep = float(sys.maxint)
277
278        domain.call+=1
279        flux_timestep = compute_fluxes_ext(timestep,
280                                           domain.epsilon,
281                                           domain.minimum_allowed_height,
282                                           domain.g,
283                                           domain.boundary_flux_sum,
284                                           domain.neighbours,
285                                           domain.neighbour_edges,
286                                           domain.normals,
287                                           domain.edgelengths,
288                                           domain.radii,
289                                           domain.areas,
290                                           domain.tri_full_flag,
291                                           Stage.edge_values,
292                                           Xmom.edge_values,
293                                           Ymom.edge_values,
294                                           Bed.edge_values,
295                                           height.edge_values,
296                                           Stage.boundary_values,
297                                           Xmom.boundary_values,
298                                           Ymom.boundary_values,
299                                           domain.boundary_flux_type,
300                                           Stage.explicit_update,
301                                           Xmom.explicit_update,
302                                           Ymom.explicit_update,
303                                           domain.already_computed_flux,
304                                           domain.max_speed,
305                                           int(domain.optimise_dry_cells),
306                                           domain.timestep_order,
307                                           Stage.centroid_values,
308                                           Xmom.centroid_values,
309                                           Ymom.centroid_values,
310                                           Bed.centroid_values, 
311                                           height.centroid_values,
312                                           Bed.vertex_values)
313
314
315        # Update the boundary flux integral
316        if(domain.timestepping_method=='rk2'):
317            if(domain.call%2==1):
318              domain.boundary_flux_integral[0]= domain.boundary_flux_integral[0] +\
319                                                domain.boundary_flux_sum[0]*domain.timestep*0.5
320              #print 'dbfi ', domain.boundary_flux_integral, domain.boundary_flux_sum
321              domain.boundary_flux_sum[0]=0.
322
323        elif(domain.timestepping_method=='euler'):
324            domain.boundary_flux_integral=0.
325            # This presently doesn't work -- this section of code may need to go elsewhere
326            #domain.boundary_flux_integral[0]= domain.boundary_flux_integral[0] +\
327            #                                  domain.boundary_flux_sum[0]*domain.timestep
328            #domain.boundary_flux_sum[0]=0.
329
330        elif(domain.timestepping_method=='rk3'):
331            domain.boundary_flux_integral=0.
332            # This needs to be designed
333        else:
334            mess='ERROR: domain.timestepping_method', domain.timestepping_method,' method not recognised'
335            raise Exception, mess
336
337        domain.flux_timestep = flux_timestep
338
339
340
341    def protect_against_infinitesimal_and_negative_heights(domain):
342        """protect against infinitesimal heights and associated high velocities"""
343
344        from swb2_domain_ext import protect
345        #print'using swb2_protect_against ..'
346
347        # shortcuts
348        wc = domain.quantities['stage'].centroid_values
349        wv = domain.quantities['stage'].vertex_values
350        zc = domain.quantities['elevation'].centroid_values
351        zv = domain.quantities['elevation'].vertex_values
352        xmomc = domain.quantities['xmomentum'].centroid_values
353        ymomc = domain.quantities['ymomentum'].centroid_values
354        areas = domain.areas
355        xc = domain.centroid_coordinates[:,0]
356        yc = domain.centroid_coordinates[:,1] 
357
358        protect(domain.minimum_allowed_height, domain.maximum_allowed_speed,
359                domain.epsilon, wc, wv, zc,zv, xmomc, ymomc, areas, xc, yc)
360
361    def conserved_values_to_evolved_values(self, q_cons, q_evol):
362        """Mapping between conserved quantities and the evolved quantities.
363        Used where we have a boundary condition which works with conserved
364        quantities and we now want to use them for the new well balanced
365        code using the evolved quantities
366
367        Typically the old boundary condition will set the values in q_cons,
368
369        q_evol on input will have the values of the evolved quantities at the
370        edge point (useful as it provides values for evlevation).
371
372        """
373
374        wc  = q_cons[0]
375        uhc = q_cons[1]
376        vhc = q_cons[2]
377
378
379        q_evol[0]  = wc
380        q_evol[1]  = uhc
381        q_evol[2]  = vhc
382
383        return q_evol
384
385    def distribute_to_vertices_and_edges(self):
386        """ Call correct module function """
387
388        if self.use_edge_limiter:
389            #distribute_using_edge_limiter(self)
390            self.distribute_using_edge_limiter()
391        else:
392            #distribute_using_vertex_limiter(self)
393            self.distribute_using_vertex_limiter()
394
395
396    def distribute_using_vertex_limiter(domain):
397        """Distribution from centroids to vertices specific to the SWW equation.
398
399        Precondition:
400          All quantities defined at centroids and bed elevation defined at
401          vertices.
402
403        Postcondition
404          Conserved quantities defined at vertices
405        """
406        # Remove very thin layers of water
407        domain.protect_against_infinitesimal_and_negative_heights()
408
409        # Extrapolate all conserved quantities
410        if domain.optimised_gradient_limiter:
411            # MH090605 if second order,
412            # perform the extrapolation and limiting on
413            # all of the conserved quantities
414
415            if (domain._order_ == 1):
416                for name in domain.conserved_quantities:
417                    Q = domain.quantities[name]
418                    Q.extrapolate_first_order()
419            elif domain._order_ == 2:
420                domain.extrapolate_second_order_sw()
421            else:
422                raise Exception('Unknown order')
423        else:
424            # Old code:
425            for name in domain.conserved_quantities:
426                Q = domain.quantities[name]
427
428                if domain._order_ == 1:
429                    Q.extrapolate_first_order()
430                elif domain._order_ == 2:
431                    Q.extrapolate_second_order_and_limit_by_vertex()
432                else:
433                    raise Exception('Unknown order')
434
435        # Take bed elevation into account when water heights are small
436        #balance_deep_and_shallow(domain)
437
438        # Compute edge values by interpolation
439        for name in domain.conserved_quantities:
440            Q = domain.quantities[name]
441            Q.interpolate_from_vertices_to_edges()
442
443
444    def distribute_using_edge_limiter(domain):
445        """Distribution from centroids to edges specific to the SWW eqn.
446
447        Precondition:
448          All quantities defined at centroids and bed elevation defined at
449          vertices.
450
451        Postcondition
452          Conserved quantities defined at vertices
453        """
454        #from swb2_domain import protect_against_infinitesimal_and_negative_heights
455
456        # Remove very thin layers of water
457        #print 'Before protect'
458        domain.protect_against_infinitesimal_and_negative_heights()
459        #print 'After protect'
460
461        #for name in domain.conserved_quantities:
462        #    Q = domain.quantities[name]
463        #    if domain._order_ == 1:
464        #        Q.extrapolate_first_order()
465        #    elif domain._order_ == 2:
466        #        #Q.extrapolate_second_order_and_limit_by_edge()
467        #        # FIXME: This use of 'break' is hacky
468        #        domain.extrapolate_second_order_edge_sw()
469        #        break
470        #    else:
471        #        raise Exception('Unknown order')
472       
473        #print 'Before extrapolate'
474        domain.extrapolate_second_order_edge_sw()
475        #print 'After extrapolate'
476
477        #balance_deep_and_shallow(domain)
478
479        # Compute vertex values by interpolation
480        #for name in domain.conserved_quantities:
481        #    Q = domain.quantities[name]
482        #    Q.interpolate_from_edges_to_vertices()
483        #    Q.interpolate_from_vertices_to_edges()
484
485
486    def update_other_quantities(self):
487        """ There may be a need to calculates some of the other quantities
488        based on the new values of conserved quantities
489
490        But that is not needed in this version of the solver.
491        """
492        pass
493        # The centroid values of height and x and y velocity
494        # might not have been setup
495       
496        #self.update_centroids_of_velocities_and_height()
497        #
498        #for name in ['height', 'xvelocity', 'yvelocity']:
499        #    Q = self.quantities[name]
500        #    if self._order_ == 1:
501        #        Q.extrapolate_first_order()
502        #    elif self._order_ == 2:
503        #        Q.extrapolate_second_order_and_limit_by_edge()
504        #    else:
505        #        raise Exception('Unknown order')
506
507
508    def update_centroids_of_velocities_and_height(self):
509        """Calculate the centroid values of velocities and height based
510        on the values of the quantities stage and x and y momentum
511
512        Assumes that stage and momentum are up to date
513
514        Useful for kinematic viscosity calculations
515        """
516        pass
517        ## For shallow water we need to update height xvelocity and yvelocity
518
519        ##Shortcuts
520        #W  = self.quantities['stage']
521        #UH = self.quantities['xmomentum']
522        #VH = self.quantities['ymomentum']
523        #H  = self.quantities['height']
524        #Z  = self.quantities['elevation']
525        #U  = self.quantities['xvelocity']
526        #V  = self.quantities['yvelocity']
527
528        ##print num.min(W.centroid_values)
529
530        ## Make sure boundary values of conserved quantites
531        ## are consistent with value of functions at centroids
532        ##self.distribute_to_vertices_and_edges()
533        #Z.set_boundary_values_from_edges()
534
535        ##W.set_boundary_values_from_edges()
536        ##UH.set_boundary_values_from_edges()
537        ##VH.set_boundary_values_from_edges()
538
539        ## Update height values
540        #H.set_values(W.centroid_values-Z.centroid_values, location='centroids')
541        #H.set_boundary_values( num.where(W.boundary_values-Z.boundary_values>=0,
542        #                                 W.boundary_values-Z.boundary_values, 0.0))
543
544        ##assert num.min(H.centroid_values) >= 0
545        ##assert num.min(H.boundary_values) >= 0
546
547        ##Aliases
548        #uh_C  = UH.centroid_values
549        #vh_C  = VH.centroid_values
550        #h_C   = H.centroid_values
551
552        #uh_B  = UH.boundary_values
553        #vh_B  = VH.boundary_values
554        #h_B   = H.boundary_values
555
556        #H0 = 1.0e-8
557        #
558        #U.set_values(uh_C/(h_C + H0/h_C), location='centroids')
559        #V.set_values(vh_C/(h_C + H0/h_C), location='centroids')
560
561        #U.set_boundary_values(uh_B/(h_B + H0/h_B))
562        #V.set_boundary_values(vh_B/(h_B + H0/h_B))
563
564
565
566    def extrapolate_second_order_edge_sw(self):
567        """Wrapper calling C version of extrapolate_second_order_sw, using
568            edges instead of vertices in the extrapolation step. The routine
569            then interpolates from edges to vertices.
570            The momentum extrapolation / interpolation is based on either
571            momentum or velocity, depending on the choice of
572            extrapolate_velocity_second_order.
573
574        self  the domain to operate on
575
576        """
577
578        from swb2_domain_ext import extrapolate_second_order_edge_sw as extrapol2
579
580        # Shortcuts
581        Stage = self.quantities['stage']
582        Xmom = self.quantities['xmomentum']
583        Ymom = self.quantities['ymomentum']
584        Elevation = self.quantities['elevation']
585        height=self.quantities['height']
586
587        extrapol2(self,
588                  self.surrogate_neighbours,
589                  self.neighbour_edges,
590                  self.number_of_boundaries,
591                  self.centroid_coordinates,
592                  Stage.centroid_values,
593                  Xmom.centroid_values,
594                  Ymom.centroid_values,
595                  Elevation.centroid_values,
596                  height.centroid_values,
597                  self.edge_coordinates,
598                  Stage.edge_values,
599                  Xmom.edge_values,
600                  Ymom.edge_values,
601                  Elevation.edge_values,
602                  height.edge_values,
603                  Stage.vertex_values,
604                  Xmom.vertex_values,
605                  Ymom.vertex_values,
606                  Elevation.vertex_values,
607                  height.vertex_values,
608                  int(self.optimise_dry_cells),
609                  int(self.extrapolate_velocity_second_order))
610
611#    def set_boundary(self, boundary_map):
612#        ## Specialisation of set_boundary, which also updates the 'boundary_flux_type'
613#        Sww_domain.set_boundary(self,boundary_map)
614#       
615#        # Add a flag which can be used to distinguish flux boundaries within
616#        # compute_fluxes_central
617#        # Initialise to zero (which means 'not a flux_boundary')
618#        self.boundary_flux_type = self.boundary_edges*0
619#       
620#        # HACK to set the values of domain.boundary_flux
621#        for i in range(len(self.boundary_objects)):
622#            # Record the first 10 characters of the name of the boundary.
623#            # FIXME: There must be a better way than this!
624#            bndry_name = self.boundary_objects[i][1].__repr__()[0:10]
625#
626#            if(bndry_name=='zero_mass_'):
627#                # Create flag 'boundary_flux_type' that can be read in
628#                # compute_fluxes_central
629#                self.boundary_flux_type[i]=1
630#
631#
632#def manning_friction_implicit(domain):
633#    """Apply (Manning) friction to water momentum
634#    Wrapper for c version
635#    """
636#
637#    from shallow_water_ext import manning_friction_flat
638#    from shallow_water_ext import manning_friction_sloped
639#
640#    xmom = domain.quantities['xmomentum']
641#    ymom = domain.quantities['ymomentum']
642#
643#    x = domain.get_vertex_coordinates()
644#   
645#    w = domain.quantities['stage'].centroid_values
646#    z = domain.quantities['elevation'].vertex_values
647#
648#    uh = xmom.centroid_values
649#    vh = ymom.centroid_values
650#    eta = domain.quantities['friction'].centroid_values
651#
652#    xmom_update = xmom.semi_implicit_update
653#    ymom_update = ymom.semi_implicit_update
654#
655#    eps = domain.epsilon
656#    g = domain.g
657#
658#    if domain.use_sloped_mannings:
659#        manning_friction_sloped(g, eps, x, w, uh, vh, z, eta, xmom_update, \
660#                                ymom_update)
661#    else:
662#        manning_friction_flat(g, eps, w, uh, vh, z, eta, xmom_update, \
663#                                #ymom_update)
Note: See TracBrowser for help on using the repository browser.