source: anuga_core/source/anuga/advection/advection.py @ 7737

Last change on this file since 7737 was 7737, checked in by hudson, 14 years ago

Various refactorings, all unit tests pass.
Domain renamed to generic domain.

File size: 9.5 KB
Line 
1"""Class Domain -
22D triangular domains for finite-volume computations of
3the advection equation.
4
5This module contains a specialisation of class Domain from module domain.py
6consisting of methods specific to the advection equantion
7
8The equation is
9
10  u_t + (v_1 u)_x + (v_2 u)_y = 0
11
12There is only one conserved quantity, the stage u
13
14The advection equation is a very simple specialisation of the generic
15domain and may serve as an instructive example or a test of other
16components such as visualisation.
17
18Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou
19Geoscience Australia, 2004
20"""
21
22
23#import logging, logging.config
24#logger = logging.getLogger('advection')
25#logger.setLevel(logging.WARNING)
26#
27#try:
28#    logging.config.fileConfig('log.ini')
29#except:
30#    pass
31
32
33from anuga.abstract_2d_finite_volumes.generic_domain \
34                import Generic_Domain
35import anuga.utilities.log as log
36
37import numpy as num
38
39
40class Advection_Domain(Generic_Domain):
41
42    def __init__(self,
43                 coordinates,
44                 vertices,
45                 boundary = None,
46                 tagged_elements = None,
47                 geo_reference = None,
48                 use_inscribed_circle=False,
49                 velocity = None,
50                 full_send_dict=None,
51                 ghost_recv_dict=None,
52                 processor=0,
53                 numproc=1
54                 ):
55
56        conserved_quantities = ['stage']
57        other_quantities = []
58        Generic_Domain.__init__(self,
59                                source=coordinates,
60                                triangles=vertices,
61                                boundary=boundary,
62                                conserved_quantities=conserved_quantities,
63                                other_quantities=other_quantities,
64                                tagged_elements=tagged_elements,
65                                geo_reference=geo_reference,
66                                use_inscribed_circle=use_inscribed_circle,
67                                full_send_dict=full_send_dict,
68                                ghost_recv_dict=ghost_recv_dict,
69                                processor=processor,
70                                numproc=numproc)
71
72        if velocity is None:
73            self.velocity = num.array([1,0],'d')
74        else:
75            self.velocity = num.array(velocity,'d')
76
77        #Only first is implemented for advection
78        self.set_default_order(1)
79        self.set_beta(1.0)
80       
81        self.smooth = True
82
83    def check_integrity(self):
84        Generic_Domain.check_integrity(self)
85
86        msg = 'Conserved quantity must be "stage"'
87        assert self.conserved_quantities[0] == 'stage', msg
88
89
90    def distribute_to_vertices_and_edges(self):
91        """Extrapolate conserved quantities from centroid to
92        vertices and edge-midpoints for each volume
93
94        Default implementation is straight first order,
95        i.e. constant values throughout each element and
96        no reference to non-conserved quantities.
97        """
98
99        for name in self.conserved_quantities:
100            Q = self.quantities[name]
101            if self._order_ == 1:
102                Q.extrapolate_first_order()
103            elif self._order_ == 2:
104                Q.extrapolate_second_order_and_limit_by_edge()
105                #Q.limit()
106            else:
107                raise 'Unknown order'
108            #Q.interpolate_from_vertices_to_edges()
109
110
111
112
113    def flux_function(self, normal, ql, qr, zl=None, zr=None):
114        """Compute outward flux as inner product between velocity
115        vector v=(v_1, v_2) and normal vector n.
116
117        if <n,v> > 0 flux direction is outward bound and its magnitude is
118        determined by the quantity inside volume: ql.
119        Otherwise it is inbound and magnitude is determined by the
120        quantity outside the volume: qr.
121        """
122
123        v1 = self.velocity[0]
124        v2 = self.velocity[1]
125
126
127        normal_velocity = v1*normal[0] + v2*normal[1]
128
129        if normal_velocity < 0:
130            flux = qr * normal_velocity
131        else:
132            flux = ql * normal_velocity
133
134        max_speed = abs(normal_velocity)
135        return flux, max_speed
136
137
138
139    def compute_fluxes(self):
140        """Compute all fluxes and the timestep suitable for all volumes
141        in domain.
142
143        Compute total flux for each conserved quantity using "flux_function"
144
145        Fluxes across each edge are scaled by edgelengths and summed up
146        Resulting flux is then scaled by area and stored in
147        domain.explicit_update
148
149        The maximal allowable speed computed by the flux_function
150        for each volume
151        is converted to a timestep that must not be exceeded. The minimum of
152        those is computed as the next overall timestep.
153
154        Post conditions:
155        domain.explicit_update is reset to computed flux values
156        domain.timestep is set to the largest step satisfying all volumes.
157        """
158
159        import sys
160        from anuga.config import max_timestep
161
162
163        huge_timestep = float(sys.maxint)
164        Stage = self.quantities['stage']
165
166        """
167        log.critical("======================================")
168        log.critical("BEFORE compute_fluxes")
169        log.critical("stage_update=%s" % str(Stage.explicit_update))
170        log.critical("stage_edge=%s" % str(Stage.edge_values))
171        log.critical("stage_bdry=%s" % str(Stage.boundary_values))
172        log.critical("neighbours=%s" % str(self.neighbours))
173        log.critical("neighbour_edges=%s" % str(self.neighbour_edges))
174        log.critical("normals=%s" % str(self.normals))
175        log.critical("areas=%s" % str(self.areas))
176        log.critical("radii=%s" % str(self.radii))
177        log.critical("edgelengths=%s" % str(self.edgelengths))
178        log.critical("tri_full_flag=%s" % str(self.tri_full_flag))
179        log.critical("huge_timestep=%s" % str(huge_timestep))
180        log.critical("max_timestep=%s" % str(max_timestep))
181        log.critical("velocity=%s" % str(self.velocity))
182        """
183
184        import advection_ext           
185        self.flux_timestep = advection_ext.compute_fluxes(self, Stage, huge_timestep, max_timestep)
186
187
188
189##     def evolve(self,
190##                yieldstep = None,
191##                finaltime = None,
192##                duration = None,
193##                skip_initial_step = False):
194
195##         """Specialisation of basic evolve method from parent class
196##         """
197
198##         #Call basic machinery from parent class
199##         for t in Generic_domain.evolve(self,
200##                                        yieldstep=yieldstep,
201##                                        finaltime=finaltime,
202##                                        duration=duration,
203##                                        skip_initial_step=skip_initial_step):
204
205##             #Pass control on to outer loop for more specific actions
206##             yield(t)
207
208
209
210
211    def compute_fluxes_python(self):
212        """Compute all fluxes and the timestep suitable for all volumes
213        in domain.
214
215        Compute total flux for each conserved quantity using "flux_function"
216
217        Fluxes across each edge are scaled by edgelengths and summed up
218        Resulting flux is then scaled by area and stored in
219        domain.explicit_update
220
221        The maximal allowable speed computed by the flux_function
222        for each volume
223        is converted to a timestep that must not be exceeded. The minimum of
224        those is computed as the next overall timestep.
225
226        Post conditions:
227        domain.explicit_update is reset to computed flux values
228        domain.timestep is set to the largest step satisfying all volumes.
229        """
230
231        import sys
232        from anuga.config import max_timestep
233
234        N = len(self)
235
236        neighbours = self.neighbours
237        neighbour_edges = self.neighbour_edges
238        normals = self.normals
239
240        areas = self.areas
241        radii = self.radii
242        edgelengths = self.edgelengths
243
244        timestep = max_timestep #FIXME: Get rid of this
245
246        #Shortcuts
247        Stage = self.quantities['stage']
248
249        #Arrays
250        stage = Stage.edge_values
251
252        stage_bdry = Stage.boundary_values
253
254        flux = num.zeros(1, num.float) #Work array for summing up fluxes
255
256        #Loop
257        for k in range(N):
258            optimal_timestep = float(sys.maxint)
259
260            flux[:] = 0.  #Reset work array
261            for i in range(3):
262                #Quantities inside volume facing neighbour i
263                ql = stage[k, i]
264
265                #Quantities at neighbour on nearest face
266                n = neighbours[k,i]
267                if n < 0:
268                    m = -n-1 #Convert neg flag to index
269                    qr = stage_bdry[m]
270                else:
271                    m = neighbour_edges[k,i]
272                    qr = stage[n, m]
273
274
275                #Outward pointing normal vector
276                normal = normals[k, 2*i:2*i+2]
277
278                #Flux computation using provided function
279                edgeflux, max_speed = self.flux_function(normal, ql, qr)
280                flux -= edgeflux * edgelengths[k,i]
281
282                #Update optimal_timestep
283                if  self.tri_full_flag[k] == 1 :
284                    try:
285                        optimal_timestep = min(optimal_timestep, radii[k]/max_speed)
286                    except ZeroDivisionError:
287                        pass
288
289            #Normalise by area and store for when all conserved
290            #quantities get updated
291            flux /= areas[k]
292            Stage.explicit_update[k] = flux[0]
293
294            timestep = min(timestep, optimal_timestep)
295
296        self.timestep = timestep
297
Note: See TracBrowser for help on using the repository browser.