source: trunk/anuga_core/source/anuga/advection/advection.py @ 7998

Last change on this file since 7998 was 7737, checked in by James Hudson, 15 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.